👾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 ContratoFunção
sendTXToken
: Execução de Transações no ContratoFunção
transferToken
: Transferência Simplificada de TokensDetalhes 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/api
e@polkadot/api-contract
:npm install @polkadot/api @polkadot/api-contract
Endpoints: Use os mesmos endpoints da Lunes mencionados anteriormente (ex.:
wss://ws-test.lunes.io
para 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 objetoapi
existe 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
ContractPromise
para 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
getQueryToken
para obter o contrato e ogasRequired
.
Envio da Transação:
Usa
contract.tx[router]
para preparar a transação real.Configura
gasLimit
com 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::transfer
com 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
apiReady
e a validade deaccount
antes 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.com
para 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_psp22
para o arquivo correto.
16. Possíveis Melhorias
Validação de Parâmetros:
Adicione verificação para garantir que
to
seja 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?