SwiftHTML & CSSSolidityDesenvolvimento de JogosSolana/Rust
28.11.2024

Aula 168: Funcionalidade de Permissão EIP-2612

A EIP-2612 introduz um novo recurso aos tokens ERC20 que permite aprovações sem a necessidade de taxa de gás. Esta especificação adiciona uma função permit que permite que um detentor de tokens aprove um gastador sem precisar enviar uma transação e pagar as taxas de gás. Em vez disso, a aprovação pode ser assinada off-chain e enviada para o contrato inteligente, permitindo que o gastador utilize os tokens em nome do detentor.

Nesta aula, discutiremos como a função permit funciona e forneceremos um exemplo de como implementá-la em um contrato inteligente em Solidity.

Entendendo a EIP-2612

A EIP-2612 inclui os seguintes recursos-chave:

  • Função Permit: Permite que aprovações sejam feitas usando uma assinatura pessoal, que o contrato verifica.
  • Nonces: Cada endereço recebe um nonce que deve ser utilizado para prevenir ataques de repetição.
  • Expiração: A permissão pode ser configurada para expirar, garantindo que permissões assinadas não possam ser usadas indefinidamente.

A assinatura da função permit é a seguinte:

function permit(
    address owner,
    address spender,
    uint256 value,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
) external;

Parâmetros da Função

  • owner: O endereço do detentor dos tokens.
  • spender: O endereço autorizado a gastar os tokens.
  • value: O número de tokens a serem aprovados.
  • deadline: O timestamp após o qual a permissão não é mais válida.
  • v, r, s: Componentes da assinatura gerada off-chain.

Modificando o Contrato ERC20

Para implementar a funcionalidade de permissão em um token ERC20 padrão, siga estes passos:

  1. Adicionar Variáveis de Estado: Precisamos de um mapeamento para armazenar nonces e uma variável para o separador de domínio.
  2. Implementar a Função Permit: Utilize o padrão EIP-712 para hash da mensagem de permissão.
  3. Atualizar a função approve: Para integrar a gestão de nonces.

Aqui está uma implementação de exemplo:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

contract MyToken is ERC20, EIP712 {
    mapping(address => uint256) public nonces;

    // O nome da aplicação para EIP-712
    string private constant SIGNING_DOMAIN = "MyToken";
    string private constant SIGNATURE_VERSION = "1";

    constructor() ERC20("MyToken", "MTK") EIP712(SIGNING_DOMAIN, SIGNATURE_VERSION) {}

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        require(block.timestamp <= deadline, "Permissão expirada");

        // Calcular o nonce atual e incrementar
        uint256 currentNonce = nonces[owner];
        nonces[owner]++;

        // Criar o digest para a permissão
        bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
            keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"),
            owner,
            spender,
            value,
            currentNonce,
            deadline
        )));

        // Recuperar o endereço do signatário
        address recoveredAddress = ECDSA.recover(digest, v, r, s);
        require(recoveredAddress == owner, "Assinatura inválida");

        // Aprovar o gastador
        _approve(owner, spender, value);
    }
}

Componentes Chave do Código

  • Estruturas EIP712: A função _hashTypedDataV4 gera o hash que corresponde à EIP-712.
  • Nonces: O mapeamento nonces é utilizado para acompanhar quantas permissões foram usadas, garantindo uso único.
  • Verificação de Assinatura: O uso do ECDSA nos permite recuperar o endereço do signatário e verificar se corresponde ao owner.

Como Usar a Função Permit

Após implantar seu contrato de token:

  1. O usuário (detentor dos tokens) gera uma assinatura para a permissão com seu provedor web3 ou qualquer biblioteca off-chain, como ethers.js ou web3.js.
  2. O usuário envia os parâmetros da assinatura para um método do contrato inteligente que pode chamar a função permit.

Exemplo de geração de assinatura usando ethers.js:

const { ethers } = require("ethers");

async function generatePermit(owner, spender, value, nonce, deadline) {
    const domain = {
        name: "MyToken",
        version: "1",
        chainId: 1, // chainId da Mainnet
        verifyingContract: "<SEU_ENDEREÇO_CONTRATO>"
    };

    const types = {
        Permit: [
            { name: "owner", type: "address" },
            { name: "spender", type: "address" },
            { name: "value", type: "uint256" },
            { name: "nonce", type: "uint256" },
            { name: "deadline", type: "uint256" }
        ]
    };

    const message = {
        owner: owner,
        spender: spender,
        value: value,
        nonce: nonce,
        deadline: deadline
    };

    const wallet = new ethers.Wallet("<SUA_CHAVE_PRIVADA>");
    const signature = await wallet._signTypedData(domain, types, message);

    return signature; // Separe isso em v, r, s para a chamada da transação
}

Conclusão

A funcionalidade de permissão da EIP-2612 melhora a usabilidade dos tokens ERC20 ao permitir aprovações sem custo de gás, o que pode melhorar significativamente a experiência do usuário. Seguindo os passos delineados nesta aula, você pode facilmente integrar a funcionalidade de permissão em seus contratos de token ERC20.

Video

Did you like this article? Rate it from 1 to 5:

Thank you for voting!