Lição: 251: Trabalhando com MetaTransactions
MetaTransactions são um conceito poderoso no ecossistema Ethereum, permitindo que usuários interajam com contratos inteligentes sem precisar manter ETH para taxas de transação. Isso pode melhorar a experiência do usuário e possibilitar cenários como a delegação de assinatura de transações e o pagamento das taxas de transação por um terceiro. Nesta aula, vamos explorar como implementar MetaTransactions usando Solidity e demonstrar um exemplo simples.
O que são MetaTransactions?
Uma MetaTransaction permite que um usuário submeta uma transação que pode ser executada por outra conta. Em vez de enviar Ether diretamente, um relayer (terceiro) paga pela taxa da transação, permitindo assim que os usuários interajam com o contrato sem precisar ter Ether.
Conceitos-chave
- Relayers: Essas são contas ou serviços que submetem MetaTransactions em nome dos usuários. Eles frequentemente cobram uma taxa por esse serviço.
- Assinaturas: Os usuários assinam uma mensagem contendo os detalhes da transação, que o relayer pode usar para executar a transação.
- Nonces: Para prevenir ataques de replay, cada transação é atribuída a um nonce, que deve ser único para cada usuário.
Etapas de Implementação
Passo 1: Configurando o Contrato
Vamos começar criando um contrato simples de token ERC20 que dá suporte a MetaTransactions. Implementaremos uma função transfer
que pode ser chamada através de uma MetaTransaction.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
contract MetaToken is ERC20 {
using ECDSA for bytes32;
mapping(address => uint256) public nonces;
constructor(uint256 initialSupply) ERC20("MetaToken", "MTK") {
_mint(msg.sender, initialSupply);
}
function metaTransfer(
address from,
address to,
uint256 value,
uint256 nonce,
bytes memory signature
) public {
require(nonce == nonces[from], "Nonce inválido");
// Hash dos detalhes da transação
bytes32 messageHash = keccak256(abi.encodePacked(from, to, value, nonce));
// Recuperar o assinante
address signer = messageHash.toEthSignedMessageHash().recover(signature);
require(signer == from, "Assinatura inválida");
// Reiniciar o nonce e transferir tokens
nonces[from]++;
_transfer(from, to, value);
}
}
Passo 2: Gerando uma Assinatura
Em seguida, precisamos de uma forma de gerar uma assinatura para a MetaTransaction. Isso pode ser feito em um ambiente web3, como com JavaScript.
const Web3 = require('web3');
const web3 = new Web3();
async function signMetaTransaction(account, nonce, to, value) {
const messageHash = web3.utils.soliditySha3(
{ type: 'address', value: account },
{ type: 'address', value: to },
{ type: 'uint256', value: value },
{ type: 'uint256', value: nonce }
);
// Assinar a mensagem
const signature = await web3.eth.sign(messageHash, account);
return signature;
}
Passo 3: Encaminhando a MetaTransaction
O relayer pegará a mensagem assinada do usuário junto com o nonce e submeterá a transação usando a função metaTransfer
.
async function relayMetaTransaction(tokenContract, from, to, value, nonce, signature) {
const tx = await tokenContract.methods.metaTransfer(from, to, value, nonce, signature).send({ from: relayerAddress });
return tx;
}
Passo 4: Gerenciando Nonces
Nonces neste exemplo são gerenciados usando um mapeamento de endereços de usuários para seus nonce atuais. Cada vez que uma MetaTransaction é executada com sucesso, o nonce é incrementado para garantir sua unicidade.
Testando a MetaTransaction
Para testar nossa implementação, podemos seguir estas etapas:
- Mintar alguns tokens para o endereço do usuário.
- Assinar uma MetaTransaction usando o endereço do usuário.
- Encaminhar a transação através do relayer.
Essa abordagem permite que usuários que não possuem Ether interajam com o contrato inteligente de forma eficaz.
Conclusão
Nesta aula, exploramos o conceito de MetaTransactions, implementamos um contrato simples de token ERC20 com suporte a MetaTransactions e definimos como assinar e encaminhar mensagens. Este padrão pode melhorar significativamente a experiência do usuário, removendo a necessidade de os usuários manterem ETH. Agora, você pode começar a desenvolver aplicações mais complexas que utilizem MetaTransactions em seus projetos!