체인의정석

Solidity) internal 트랜잭션에 대한 이벤트 로그 테스트하기 본문

블록체인/Solidity

Solidity) internal 트랜잭션에 대한 이벤트 로그 테스트하기

체인의정석 2022. 4. 20. 15:52
728x90
반응형

현재 hardhat + ether.js에서 인터널 트랜잭션에 대한 이벤트 로그 테스트가 이루어지고 있지 않은 상황이다.

완벽한 단위 테스트를 위하여서 인터널 트랜잭션에 대한 이벤트 로그를 업데이트 하기로 하였다.

 

https://ethereum.stackexchange.com/questions/71785/how-to-test-events-that-were-sent-by-inner-transaction-delegate-call

 

How to test events that were sent by inner transaction / delegate call?

Usually, using truffle I can check for events like this: let { logs } = await myContract.doSomethingImportant( xxx ); // 1500 tokens are expected to get expectEvent.inLogs(logs, '

ethereum.stackexchange.com

해당 케이스를 보니 오픈제플린의 모듈을 사용하면 된다고 한다.

예시 코드도 발견하였다.

 

https://forum.openzeppelin.com/t/how-to-test-for-events-that-are-dispatched-in-a-nested-operation/955/2

 

How to test for events that are dispatched in a nested operation?

Use openzeppelin-test-helpers expectEvent.inTransaction to test for events from other contracts https://github.com/OpenZeppelin/openzeppelin-test-helpers#async-intransaction-txhash-emitter-eventname-eventargs-- async inTransaction (txHash, emitter, eventNa

forum.openzeppelin.com

pragma solidity ^0.5.0;

contract A {

    function doAStuff() public {
        emit DidAStuff("A");
    }

    event DidAStuff(string message);
}

A 컨트렉트가 있어서 호출되는 상황이고

pragma solidity ^0.5.0;

import "./A.sol";

contract B {

    A _a;

    constructor(A a) public {
        _a = a;
    }

    function doBStuff() public {
        _a.doAStuff();
        emit DidBStuff("B");
    }

    event DidBStuff(string message);
}

B 컨트렉트의 경우는 A를 가져와서 호출하는 경우이다. 여기서도 이벤트가 발생한다.

const { expectEvent } = require('openzeppelin-test-helpers');

const A = artifacts.require('A');
const B = artifacts.require('B');

contract('B', function () {
  beforeEach(async function () {
    this.a = await A.new();
    this.b = await B.new(this.a.address);
  });

  it('does stuff', async function () {
    const receipt = await this.b.doBStuff();

    // Log-checking will not only look at the event name, but also the values, which can be addresses, strings, numbers, etc.
    await expectEvent.inLogs(receipt.logs, 'DidBStuff', { message: "B" });
    await expectEvent.inTransaction(receipt.tx, A, 'DidAStuff', { message: "A" });
  });
});

이러면 openzeppelin-test-helpers 모듈을 받아서 expectEvent를 불러온다.

그리고 expectEvent.inLogs라는 것을 사용해서 인터널 트랜잭션의 로그값을 받아올 수 있다.

 

근데 문서를 확인해 보니 아무래도 web3를 사용했을때만 이러한 오픈제플린 테스트 모듈을 사용할 수 있는거 같다.

 

https://ethereum-waffle.readthedocs.io/en/latest/matchers.html

 

Chai matchers — waffle documentation

The matcher can accept numbers, strings and BigNumbers as a balance change, while the account should be specified either as a Wallet or a Contract. Note changeTokenBalance calls should not be chained. If you need to check changes of the balance for multipl

ethereum-waffle.readthedocs.io

지금 사용중인건 waffle에서 예시를 찾아볼 수 있었다.

문제는 여기서는 아직 2개의 emit은 안된다고 되어 있엇는데, 실제로 해보니까 체크가 잘되었었다. internal Transaction도 잡아주고 다른 이벤트 로그로 했을때도 잘못된 경우 테스트 통과가 안되고 에러가 나는것을 확인할 수 있었다.

await expect(contractName1.functionName(input))
.to.emit(contractName1, "EventName1")
.withArgs(event_expect1,event_expect2)
.to.emit(contractName2, "EventName2")
.withArgs(event_expect1,event_expect2)

만약 2개의 다른 컨트렉트에 이벤트가 발생한다면 위와 같이 구문을 2번 반복하면 이벤트 로그 검사가 진행되는 것을 확인할 수 있었다.

 

 

+) 추가적으로 테스트 코드를 보완하다가 알게 된 점은 이벤트로그의 경우 발생하는 순서대로 체크하지 않으면 에러가 난다는 점이다. 따라서 다중 이벤트로그를 체크할 경우 실행 순서를 확인하고 하는 작업이 필요하다.

 

그리고 뒤에 이벤트가 남아있음에도 .withArgs 뒤에 ; 를 다는 경우 에러가 난다.

따라서 ;는 마지막 줄에만 작성한다.

728x90
반응형
Comments