WEB3DEV

Cover image for Rastreamento de chamadas de transações de contrato inteligente e eventos locais com o hardhat-tracer.
Fatima Lima
Fatima Lima

Posted on

Rastreamento de chamadas de transações de contrato inteligente e eventos locais com o hardhat-tracer.

Image description
Foto de Mitchell Luo no Unsplash

Você já se deu conta da necessidade de rastrear transações de contratos inteligentes enquanto testava ou aprendia com protocolos ou bifurcações de contratos inteligentes?

O hardhat-tracer pode ajudá-lo.

Encontre essa ferramenta aqui: https://github.com/zemse/hardhat-tracer

Do arquivo Readme, fica bastante claro como usar a ferramenta. Ainda assim, eu gostaria de fazer uma apresentação desta ferramenta usando alguns exemplos simples de contratos.

Para esse exemplo, nós veremos a bandeira trace, mas ele também fornece outras bandeiras como fulltrace e hash para rastreamento.

Estes são os contratos que estamos usando:

  1. Contrato ERC20 simples que cunha o suprimento de token dado
// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract ERC20Mock is ERC20, Ownable {
constructor(
   string memory name
   string memory symbol,
   uint256 supply)
   public ERC20(name, symbol) {
        _mint(msg.sender, supply);
   }
}
Enter fullscreen mode Exit fullscreen mode
  1. OtherContract (um contrato que chama contrato EventEmitter para emitir evento):
// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./EventEmitter.sol";
contract OtherContract{
address emitter;
function takeTokenAndCallOtherFunction(address _tokenAddr) public {
IERC20(_tokenAddr).transferFrom(msg.sender, address(this), 5 ether);
emitTokensTaken(msg.sender);
}
function setEmitterAddress(address _emitter) external {
emitter=_emitter;
}
function emitTokensTaken(address _from) private {
EventEmitter(emitter).emitTokenFrom(_from);
}
}
Enter fullscreen mode Exit fullscreen mode

EventEmitter (que realmente emite o evento)

pragma solidity 0.8.4;
contract EventEmitter{
event TokensFrom(address From);
function emitTokenFrom(address _from) public {
emit TokensFrom(_from);
}
}
Enter fullscreen mode Exit fullscreen mode

Um rápido fluxo de trabalho:

O chamador aprova o OtherContract para gastar 5 tokens.

O chamador define o endereço do contrato EventEmitter com a função setEmitterAddress que está no contrato OtherContract.

O chamador chama a função takeTokenAndCallOtherFunction **do contrato OtherContract que então, chama a função emitTokensTaken.

A função emitTokensTaken chama a função emitTokenFrom localizada no contrato EventEmitter.

A função emitTokenFrom simplesmente emite o evento TokensFrom.

Instale e defina o hardhat-tracer:

Instale-o com o npm i hardhat-tracer

Acrescente-o ao hardhat.config.js: require(“hardhat-tracer”);

Vamos escrever um caso de teste simples para nossos contratos inteligentes:

No seguinte teste você pode ver quatro chamadas de função:

it("test",async () => {
// Aprove o OtherContract.
console.log("------------------------------------ approve tokens ------------------------------------");
await token1Instance.approve(OtherContractInstance.address,ethers.utils.parseEther("5"));
console.log("----------------------------------------------------------------------------------------\n");
console.log("----------------------------------- set emitter addr -----------------------------------");
await OtherContractInstance.setEmitterAddress(EventEmitterInstance.address);
console.log("----------------------------------------------------------------------------------------\n");
console.log("-------------------------- call takeTokenAndCallOtherFunction --------------------------")
await OtherContractInstance.takeTokenAndCallOtherFunction(token1Instance.address);
console.log("----------------------------------------------------------------------------------------\n")
console.log("---------------------- failed takeTokenAndCallOtherFunction call -----------------------")
await expect(OtherContractInstance.takeTokenAndCallOtherFunction(token1Instance.address)).to.be.revertedWith("ERC20: insufficient allowance");
console.log("----------------------------------------------------------------------------------------\n")
});

Enter fullscreen mode Exit fullscreen mode
  1. A primeira chamada aprova o endereço OtherContract para gastar 5 Tokens.
  2. A segunda chamada define o endereço do contrato EventEmitter no OtherContract, usando setEmitterAddress
  3. A terceira chamada chama takeTokenAndCallOtherFunction com o endereço do token para o qual demos a nossa aprovação.
  4. E a quarta é para uma chamada malsucedida takeTokenAndCallOtherFunction em que não há tokens aprovados restantes para serem gastos pelo OtherContract.

Vamos rastrear essas chamadas com o hardhat-tracer:

agora basta executar o arquivo de teste com o comando test + a bandeira trace:

eg:     > npx hardhat test test/token-trace.js --trace
Enter fullscreen mode Exit fullscreen mode

Você pode ver o rastreamento da transação no terminal:

Image description

Test screenshot 1

O hardhat-tracer permite que você defina tags de nome Address para identificar endereços. Você pode adicioná-las antes das chamadas de teste ou após criações de instância de contrato para definir endereços como este:

hre.tracer.nameTags[OtherContractInstance.address] = "OtherContract";
hre.tracer.nameTags[EventEmitterInstance.address] = "EventEmitter";
hre.tracer.nameTags[token1Instance.address] = "TKN1";
hre.tracer.nameTags[alice.address] = "Alice";
Enter fullscreen mode Exit fullscreen mode

Uma vez que estas tags são adicionadas, você pode ver nomes/tags diretos para endereços no rastreamento:

Image description

rastros quando são adicionadas às tags de endereços

 
Na captura de tela acima, você pode ver rastreamentos para a criação de contratos com argumentos do construtor e propriedade sendo transferidos do endereço zero para o remetente msg.sender.

Chegando à primeira chamada de caso de teste, a chamada para aprovação de tokens:

Image description

A segunda chamada é OtherContract.setEmitterAddress() para definir o endereço do remetente.

Image description

A terceira é uma chamada interessante, que basicamente chama outra função que então, chama novamente a função no contrato Emitter. A captura de tela abaixo mostra os rastreamentos para a função OtherContract.takeTokenAndCallOtherFunction() que chama a função transferFrom(), que então estabelece a aprovação para zero (aprovação foi de 5 tokens) e transfere 5 tokens para o endereço OtherContract. Você pode ver os rastros do evento para Approval e Transfer.

Depois disso ele chama a função emitTokensTaken() que então, chama a função emitTokenFrom() no contrato do remetente que finalmente emite o evento TokensFrom. Você pode ver isso na captura de tela abaixo:

Image description

Finalmente, a última é para chamada takeTokenAndCallOtherFunction() malsucedida que será revertida. Aqui você poderá ver que a takeTokenAndCallOtherFunction() está chamando transferFrom() que está falhando por causa da permissão 0 (a função takeTokenAndCallOtherFunction precisa de pelo menos uma permissão de 5 tokens). E então você pode ver uma mensagem de retorno por insuficiência de permissão.

Image description

O exemplo acima foi bem pequeno e não tem chamadas externas de contrato complexas, mas ao passar por códigos complexos como bifurcações de grandes projetos e mesmo com uma rede bifurcada que contém muitas chamadas, o rastreamento é muito útil para entender o que está acontecendo em nível de código.

Sobre QuillAudits

Empresa líder em auditoria de contratos inteligentes, comprometida com a segurança de projetos de Blockchain com soluções de segurança Web3 de última geração.

É uma plataforma de auditoria que analisa e verifica rigorosamente contratos inteligentes para detectar vulnerabilidades de segurança por meio de revisão manual eficaz com ferramentas de análise estática e dinâmica, analisadores de gas, bem como simuladores. Além disso, o processo de auditoria também inclui testes unitários extensivos, bem como análise estrutural.

Realizamos tanto auditorias de contratos inteligentes quanto testes de invasão para encontrar potenciais vulnerabilidades de segurança que podem prejudicar a integridade da plataforma.

Para mais discussões e consultas sobre o mesmo tópico, junte-se à discussão em Grupo do Telegram QuillHash —

https://t.me/quillhash

Para estar em dia com o nosso trabalho, junte-se à nossa comunidade:-

Telegram | Twitter | Facebook | LinkedIn

Este artigo foi escrito por Prajwal More e traduzido por Fátima Lima. O original pode ser lido aqui.

Top comments (0)