# 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

10. **Introdução ao PSP22 e Contratos de Tokens na Lunes**
11. **Função `getQueryToken`: Simulação de Transações no Contrato**
12. **Função `sendTXToken`: Execução de Transações no Contrato**
13. **Função `transferToken`: Transferência Simplificada de Tokens**
14. **Detalhes Técnicos e Boas Práticas**
15. **Exemplo Completo de Código**
16. **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`:

    ```bash
    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

#### Objetivo

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

```javascript
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

1. **Validação Inicial**:
   * Verifica se a API está inicializada (`apiReady`), se o objeto `api` existe e se há uma conta válida (`account`).
   * Caso contrário, lança um erro para evitar execuções inválidas.
2. **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.).
3. **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.
4. **Tratamento de Erros**:
   * Se `result.isErr`, extrai o erro do registro da API e lança uma exceção detalhada.
5. **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

#### Objetivo

A função `sendTXToken` executa a transação real no contrato PSP22, utilizando os dados obtidos pela simulação em `getQueryToken`.

#### Código

```javascript
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

1. **Obter Dados da Simulação**:
   * Chama `getQueryToken` para obter o contrato e o `gasRequired`.
2. **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`).
3. **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.
4. **Retorno**:
   * Resolve com o hash da transação quando finalizada.

***

### 13. Função `transferToken`: Transferência Simplificada de Tokens

#### Objetivo

A função `transferToken` simplifica a transferência de tokens PSP22 para um endereço específico.

#### Código

```javascript
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

1. **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.
2. **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 }`.
3. **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 de `account` 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:

```javascript
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

1. **Validação de Parâmetros**:
   *   Adicione verificação para garantir que `to` seja um endereço válido:

       ```javascript
       if (!api.isValidAddress(to)) throw new Error("Endereço de destino inválido");
       ```
2. **Estado de Carregamento**:
   *   Implemente um mecanismo de feedback (ex.: `setLoading`) para desabilitar botões durante a transação:

       ```javascript
       setLoading(true);
       await transferToken(value, to);
       setLoading(false);
       ```
3. **Mensagens de Erro Amigáveis**:
   *   Melhore o feedback ao usuário:

       ```javascript
       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.
