👾Transações de Tokens PSP22
Criação de Carteiras, Simulação de Taxas e Transações de Tokens
A Lunes é uma solochain desenvolvida com o framework Substrate, operando de forma independente, mas utilizando ferramentas e padrões comuns ao ecossistema Substrate. Esta documentação já abordou a criação de carteiras, simulação de taxas e transações básicas na rede Lunes. Agora, expandimos para incluir transações de tokens baseadas no padrão PSP22, que é o equivalente ao ERC-20 para blockchains Substrate, permitindo a interação com contratos inteligentes de tokens na rede Lunes.
Sumário da Seção de Transações de Tokens
- Introdução ao PSP22 e Contratos de Tokens na Lunes 
- Função - getQueryToken: Simulação de Transações no Contrato
- Função - sendTXToken: Execução de Transações no Contrato
- Função - transferToken: Transferência Simplificada de Tokens
- Detalhes Técnicos e Boas Práticas 
- Exemplo Completo de Código 
- Possíveis Melhorias 
10. Introdução ao PSP22 e Contratos de Tokens na Lunes
O padrão PSP22 é uma especificação para tokens fungíveis no ecossistema Substrate, inspirada no ERC-20 do Ethereum. Ele define métodos como transfer, balanceOf, approve, entre outros, permitindo a criação e manipulação de tokens em blockchains como a Lunes. Esta seção documenta como interagir com um contrato PSP22 na rede Lunes, utilizando a biblioteca @polkadot/api para consultar e executar transações de tokens.
Pré-requisitos
- Contrato PSP22 implantado: Você precisa do endereço do contrato e do arquivo ABI (Application Binary Interface) correspondente. 
- Dependências: Certifique-se de ter instalado - @polkadot/apie- @polkadot/api-contract:- npm install @polkadot/api @polkadot/api-contract
- Endpoints: Use os mesmos endpoints da Lunes mencionados anteriormente (ex.: - wss://ws-test.lunes.iopara testnet).
11. Função getQueryToken: Simulação de Transações no Contrato
getQueryToken: Simulação de Transações no ContratoObjetivo
A função getQueryToken realiza uma consulta (simulação) ao contrato PSP22 para verificar os resultados de uma transação sem executá-la na blockchain. Isso é útil para estimar custos (gas e taxas) e validar parâmetros antes de enviar a transação real.
Código
const { ContractPromise } = require('@polkadot/api-contract');
async function getQueryToken(address, router, param, api, apiReady, account) {
  if (!api || !apiReady || !account) {
    throw new Error("API não inicializada ou conta inválida.");
  }
  const contract = new ContractPromise(api, ABI_psp22, address);
  const gasLimit = api.registry.createType('WeightV2', {
    refTime: 1000000000n,
    proofSize: 100000n,
  });
  const { storageDeposit, result, gasRequired, output } = await contract.query[router](
    account.address,
    { gasLimit, storageDepositLimit: null },
    ...Object.values(param)
  );
  if (result.isErr) {
    const dispatchError = api.registry.findMetaError(result.asErr.asModule);
    const error = dispatchError.docs.length ? dispatchError.docs.concat().toString() : dispatchError.name;
    throw new Error(error);
  }
  return { contract, gasRequired, storageDeposit, output };
}Passos Detalhados
- Validação Inicial: - Verifica se a API está inicializada ( - apiReady), se o objeto- apiexiste e se há uma conta válida (- account).
- Caso contrário, lança um erro para evitar execuções inválidas. 
 
- Criação do Contrato: - Usa - ContractPromisepara instanciar o contrato com o endereço (- address) e o ABI (- ABI_psp22).
- O ABI define a interface do contrato (métodos, parâmetros, etc.). 
 
- Simulação da Chamada: - O método - contract.query[router]simula a execução do método especificado (ex.:- transfer).
- Parâmetros: - account.address: Endereço da conta que realiza a consulta.
- { gasLimit, storageDepositLimit: null }: Define limites de gás e depósito de armazenamento.
- ...Object.values(param): Passa os parâmetros do método (ex.:- { to: "endereço", value: "100" }).
 
- Retorna: - gasRequired: Quantidade de gás estimada.
- storageDeposit: Taxa de armazenamento (se aplicável).
- result: Resultado da simulação.
- output: Saída do método, se bem-sucedido.
 
 
- Tratamento de Erros: - Se - result.isErr, extrai o erro do registro da API e lança uma exceção detalhada.
 
- Retorno: - Retorna um objeto com o contrato, gás necessário e outros dados para uso na transação real. 
 
12. Função sendTXToken: Execução de Transações no Contrato
sendTXToken: Execução de Transações no ContratoObjetivo
A função sendTXToken executa a transação real no contrato PSP22, utilizando os dados obtidos pela simulação em getQueryToken.
Código
async function sendTXToken(address, router, param, api, apiReady, account) {
  const contracts = await getQueryToken(address, router, param, api, apiReady, account);
  return new Promise((resolve, reject) => {
    contracts.contract.tx[router](
      {
        gasLimit: contracts.gasRequired,
        storageDepositLimit: null,
      },
      ...Object.values(param)
    ).signAndSend(account, (res) => {
      if (res.status.isInBlock) {
        console.log(`Transação incluída no bloco: ${res.status.asInBlock.toString()}`);
      } else if (res.status.isFinalized) {
        console.log(`Transação finalizada com hash: ${res.txHash.toString()}`);
        resolve(res.txHash.toString());
      }
      if (res.isError) {
        reject(new Error("Erro na execução da transação"));
      }
    });
  });
}Passos Detalhados
- Obter Dados da Simulação: - Chama - getQueryTokenpara obter o contrato e o- gasRequired.
 
- Envio da Transação: - Usa - contract.tx[router]para preparar a transação real.
- Configura - gasLimitcom o valor retornado pela simulação.
- Assina e envia a transação com - signAndSend, usando a conta fornecida (- account).
 
- Monitoramento: - isInBlock: A transação foi incluída em um bloco. 
- isFinalized: A transação foi finalizada e é irreversível. 
- isError: Indica falha na execução, rejeitando a promessa com um erro. 
 
- Retorno: - Resolve com o hash da transação quando finalizada. 
 
13. Função transferToken: Transferência Simplificada de Tokens
transferToken: Transferência Simplificada de TokensObjetivo
A função transferToken simplifica a transferência de tokens PSP22 para um endereço específico.
Código
async function transferToken(value, to, api, apiReady, account) {
  const contractAddress = import.meta.env.VITE_CONTRACT_TOKEN;
  await sendTXToken(
    contractAddress,
    "psp22::transfer",
    { to, value: value.toString(), data: "" },
    api,
    apiReady,
    account
  );
}Passos Detalhados
- Parâmetros: - value: Quantidade de tokens a transferir (em Unis).
- to: Endereço de destino.
- api,- apiReady,- account: Dependências necessárias para a transação.
 
- Chamada ao - sendTXToken:- Usa o endereço do contrato armazenado em - VITE_CONTRACT_TOKEN.
- Chama o método - psp22::transfercom os parâmetros- { to, value, data }.
 
- Execução: - A função delega a execução para - sendTXToken, que lida com a simulação e envio.
 
14. Detalhes Técnicos e Boas Práticas
PSP22
- O padrão PSP22 define métodos como: - transfer(to, value, data): Transfere tokens.
- balanceOf(account): Consulta o saldo de uma conta.
- approve(spender, value): Autoriza um gasto.
 
Gas e Storage Deposit
- GasRequired: Estimativa de gás necessária para a transação, obtida na simulação. 
- StorageDeposit: Taxa para armazenar dados no contrato, geralmente nula para transferências simples. 
Segurança
- Verifique sempre - apiReadye a validade de- accountantes de executar operações.
- Use variáveis de ambiente (ex.: - VITE_CONTRACT_TOKEN) para evitar expor endereços sensíveis no código.
Endpoints
- Para transações de tokens, utilize os mesmos endpoints da Lunes (ex.: - wss://ws.lunes.compara mainnet).
15. Exemplo Completo de Código
Aqui está um exemplo completo que conecta à rede Lunes, cria uma carteira e transfere tokens PSP22:
const { ApiPromise, WsProvider } = require('@polkadot/api');
const { ContractPromise } = require('@polkadot/api-contract');
const { Keyring } = require('@polkadot/api');
const { mnemonicGenerate } = require('@polkadot/util-crypto');
// Substitua pelo ABI do seu contrato PSP22
const ABI_psp22 = require('./path/to/psp22_abi.json');
async function main() {
  // Conectar à rede de teste
  const provider = new WsProvider('wss://ws-test.lunes.io');
  const api = await ApiPromise.create({ provider });
  const apiReady = true;
  // Criar uma nova carteira
  const keyring = new Keyring({ type: 'sr25519' });
  const newSeed = mnemonicGenerate();
  const account = keyring.addFromUri(newSeed);
  console.log('Nova Seed Phrase:', newSeed);
  console.log('Endereço da carteira:', account.address);
  // Função getQueryToken
  async function getQueryToken(address, router, param) {
    if (!api || !apiReady || !account) throw new Error("API ou conta inválida");
    const contract = new ContractPromise(api, ABI_psp22, address);
    const gasLimit = api.registry.createType('WeightV2', { refTime: 1000000000n, proofSize: 100000n });
    const { storageDeposit, result, gasRequired, output } = await contract.query[router](
      account.address,
      { gasLimit, storageDepositLimit: null },
      ...Object.values(param)
    );
    if (result.isErr) {
      const dispatchError = api.registry.findMetaError(result.asErr.asModule);
      throw new Error(dispatchError.docs.length ? dispatchError.docs.concat().toString() : dispatchError.name);
    }
    return { contract, gasRequired, storageDeposit, output };
  }
  // Função sendTXToken
  async function sendTXToken(address, router, param) {
    const contracts = await getQueryToken(address, router, param);
    return new Promise((resolve, reject) => {
      contracts.contract.tx[router](
        { gasLimit: contracts.gasRequired, storageDepositLimit: null },
        ...Object.values(param)
      ).signAndSend(account, (res) => {
        if (res.status.isInBlock) console.log(`Incluída no bloco: ${res.status.asInBlock.toString()}`);
        else if (res.status.isFinalized) resolve(res.txHash.toString());
        if (res.isError) reject(new Error("Erro na transação"));
      });
    });
  }
  // Função transferToken
  async function transferToken(value, to) {
    const contractAddress = "ENDEREÇO_DO_CONTRATO_PSP22"; // Substitua pelo endereço real
    await sendTXToken(
      contractAddress,
      "psp22::transfer",
      { to, value: value.toString(), data: "" }
    );
  }
  // Transferir 100 tokens
  try {
    await transferToken(100, "endereco_destino_lunes_aqui");
    console.log("Transferência concluída!");
  } catch (error) {
    console.error("Erro ao transferir tokens:", error.message);
  }
  // Desconectar
  await api.disconnect();
}
main().catch(console.error);Notas:
- Substitua - "ENDEREÇO_DO_CONTRATO_PSP22"pelo endereço real do contrato.
- Ajuste o caminho do - ABI_psp22para o arquivo correto.
16. Possíveis Melhorias
- Validação de Parâmetros: - Adicione verificação para garantir que - toseja um endereço válido:- if (!api.isValidAddress(to)) throw new Error("Endereço de destino inválido");
 
- Estado de Carregamento: - Implemente um mecanismo de feedback (ex.: - setLoading) para desabilitar botões durante a transação:- setLoading(true); await transferToken(value, to); setLoading(false);
 
- Mensagens de Erro Amigáveis: - Melhore o feedback ao usuário: - if (error.message.includes("1010")) { throw new Error("Saldo insuficiente para completar a transação."); }
 
Conclusão
Esta seção expande a documentação da rede Lunes para incluir transações de tokens PSP22, oferecendo um guia completo para simular, executar e simplificar transferências de tokens. Com exemplos práticos e boas práticas, os desenvolvedores podem integrar contratos inteligentes à suas aplicações na Lunes de forma eficiente e segura.
Last updated
Was this helpful?