1. 이벤트 발생&확인
2. 이벤트 활용


1. 이벤트 발생&확인

contracts 코드

  • event 만들기
    필드에 (event 이름 + 매개변수)로 정의하는 함수와 유사한 구조..

  • event 발생
    발생시키려는 곳에 (emit 이름 + 인자) 로 작성

contract MyToken {
    event Transfer(address from, address to, uint256 value);
    string public name;
    string public symbol;
    uint8 public decimals;
    
    .
    .
    .
    .
    
    function transfer(uint256 amount, address to) external {
        require(balanceOf[msg.sender] >= amount, "insufficient balance");

        balanceOf[msg.sender] -= amount; 
        balanceOf[to] += amount; 

        emit Transfer(msg.sender, to, amount);
    }



test 코드

이벤트가 일어나는 곳인 transfer는 transaction이니까 (5-3. 토큰 전송 (transfer) 함수 참고) const tx로 저장해준다.

tx가 완료될 때까지 기다린 뒤 receipt로 저장하고

console.log(receipt?.logs); 로 출력
(?는 null일 수도 있어서 넣는다고...하셨는데..)

import hre from "hardhat";
import { expect } from "chai";
import { MyToken } from "../typechain-types";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { parseUnits } from "ethers";
// hardhat에서 컴파일 할 때 필요한 타입 정의 파일을 여기에 보관함

const mintingAmount = 100n;
const decimals = 18n;

describe("My Token", () => {
  let myTokenC: MyToken;
  let signers: HardhatEthersSigner[];

  // before > describe, let myTokenC > myTokenC 사용ok
  beforeEach("should deploy", async () => {
    signers = await hre.ethers.getSigners();
    myTokenC = await hre.ethers.deployContract("MyToken", [
      "MyToken",
      "MT",
      decimals,
      mintingAmount, //100MT 발행
    ]);
  });

  describe("Basic state value check", () => {
    it("should return name", async () => {
      expect(await myTokenC.name()).equal("MyToken");
    });
    it("should return symbol", async () => {
      expect(await myTokenC.symbol()).equal("MT");
    });
    it("should return decimals", async () => {
      expect(await myTokenC.decimals()).equal(decimals);
    });

    it("should retrun 100 totalSupply", async () => {
      expect(await myTokenC.totalSupply()).equal(
        mintingAmount * 10n ** decimals,
      );
    });
  });

  describe("Mint", () => {
    // 1MT = 1*(10^18) = 1n*10n**18n = BigInt(1*10**18)
    it("should retrun 1MT balance for signer 0", async () => {
      expect(await myTokenC.balanceOf(signers[0].address)).equal(
        mintingAmount * 10n ** decimals,
      );
    });
  });

  describe("Transfer", () => {
    it("should have 0.5MT", async () => {
      const signer1 = signers[1];
      const tx = await myTokenC.transfer(
        hre.ethers.parseUnits("0.5", decimals),
        signer1.address,
      );
      const receipt = await tx.wait();
      console.log(receipt?.logs);
      expect(await myTokenC.balanceOf(signer1.address)).equal(
        hre.ethers.parseUnits("0.5", decimals),
      );
    });
    it("should be reverted with insufficient balance error", async () => {
      const signer1 = signers[1];
      await expect(
        myTokenC.transfer(
          hre.ethers.parseUnits((mintingAmount + 1n).toString(), decimals),
          signer1.address,
        ),
      ).to.be.revertedWith("insufficient balance");
    });
  });
});                                      

터미널

EventLog {
    provider: HardhatEthersProvider {
      _hardhatProvider: [LazyInitializationProviderAdapter],
      _networkName: 'hardhat',
      _blockListeners: [],
      _transactionHashListeners: Map(0) {},
      _eventListeners: []
    },
    transactionHash: '0x763070443c60586ba6913916a120fc47dc30d6831e823f429a8c743b4021d7e7',
    blockHash: '0x7aaaf79b7bd0ef94b76e6b47e36aa2dc372ee7a2179af50bc2e9833f59794e2f',
    blockNumber: 7,
    removed: undefined,
    address: '0x5FC8d32690cc91D4c39d9d3abcBD16989F875707',
    data: '0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c800000000000000000000000000000000000000000000000006f05b59d3b20000',
    topics: [
      '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
    ],
    index: 0,
    transactionIndex: 0,
    interface: Interface {
      fragments: [Array],
      deploy: [ConstructorFragment],
      fallback: null,
      receive: false
    },
    fragment: EventFragment {
      type: 'event',
      inputs: [Array],
      name: 'Transfer',
      anonymous: false
    },
    args: Result(3) [
      '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
      '0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
      500000000000000000n
    ]
  }
  • topic
    string을 해시함수를 통해 해시값으로 변경하고 topic값으로 나온다...

좀 더 찾아봐야 할 듯..
이벤트와 로그,




2. 테스트

코드2

contract

event Transfer(address indexed from, address indexed to, uint256 value);

...

 function _mint(uint256 amount, address owner) internal {
        totalSupply += amount;
        balanceOf[owner] += amount; 
                
        emit Transfer(address(0), owner, amount);
    }
  • indexed를 해줘야 쿼리 가능..???
  • mint 함수 에도 event 추가해줌

test (실제 사용하는 방식)

describe("Transfer", () => {
    it("should have 0.5MT", async () => {
      const signer0 = signers[0];
      const signer1 = signers[1];
      const tx = await myTokenC.transfer(
        hre.ethers.parseUnits("0.5", decimals),
        signer1.address,
      );
      const receipt = await tx.wait();
      console.log(receipt?.logs);
      expect(await myTokenC.balanceOf(signer1.address)).equal(
        hre.ethers.parseUnits("0.5", decimals),
      );

      const filter = myTokenC.filters.Transfer(signer0.address);
      const logs = await myTokenC.queryFilter(filter, 0, "latest"); // 실제는 latest - 10 이런식
      console.log(logs.length);

      console.log(logs[0].args.from);
      console.log(logs[0].args.to);
      console.log(logs[0].args.vlaue);

    });
    it("should be reverted with insufficient balance error", async () => {
      const signer1 = signers[1];
      await expect(
        myTokenC.transfer(
          hre.ethers.parseUnits((mintingAmount + 1n).toString(), decimals),
          signer1.address,
        ),
      ).to.be.revertedWith("insufficient balance");
    });
  });
  • queryFilter...
    SQL이나 queryFilter..? <<뭔지 모르겠다

  • 0에서 최신까지
    (현재는 테스트라서 할 때마다 첫 로그인거라 괜찮지만, 실제 환경에서는 너무 많으니까 주석처럼 사용..)

test

import hre from "hardhat";
import { expect } from "chai";
import { MyToken } from "../typechain-types";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { parseUnits } from "ethers";
// hardhat에서 컴파일 할 때 필요한 타입 정의 파일을 여기에 보관함

const mintingAmount = 100n;
const decimals = 18n;

describe("My Token", () => {
  let myTokenC: MyToken;
  let signers: HardhatEthersSigner[];

  // before > describe, let myTokenC > myTokenC 사용ok
  beforeEach("should deploy", async () => {
    signers = await hre.ethers.getSigners();
    myTokenC = await hre.ethers.deployContract("MyToken", [
      "MyToken",
      "MT",
      decimals,
      mintingAmount, //100MT 발행
    ]);
  });

  describe("Basic state value check", () => {
    it("should return name", async () => {
      expect(await myTokenC.name()).equal("MyToken");
    });
    it("should return symbol", async () => {
      expect(await myTokenC.symbol()).equal("MT");
    });
    it("should return decimals", async () => {
      expect(await myTokenC.decimals()).equal(decimals);
    });

    it("should retrun 100 totalSupply", async () => {
      expect(await myTokenC.totalSupply()).equal(
        mintingAmount * 10n ** decimals,
      );
    });
  });

  describe("Mint", () => {
    // 1MT = 1*(10^18) = 1n*10n**18n = BigInt(1*10**18)
    it("should retrun 1MT balance for signer 0", async () => {
      expect(await myTokenC.balanceOf(signers[0].address)).equal(
        mintingAmount * 10n ** decimals,
      );
    });
  });

  describe("Transfer", () => {
    it("should have 0.5MT", async () => {
      const signer0 = signers[0];
      const signer1 = signers[1];
      await expect(
        myTokenC.transfer(
          hre.ethers.parseUnits("0.5", decimals),
          signer1.address,
        ),
      )
        .to.emit(myTokenC, "Transfer")
        .withArgs(
          signer0.address,
          signer1.address,
          hre.ethers.parseEther("0.5", decimals),
        );
      // expect(1)
      //   .to.emit(myTokenC, "Transfer")
      //   .withArgs(
      //     signer0.address,
      //     signer1.address,
      //     hre.ethers.parseEther("0.5", decimals),
      //   );
      expect(await myTokenC.balanceOf(signer1.address)).equal(
        hre.ethers.parseUnits("0.5", decimals),
      );
    });
    it("should be reverted with insufficient balance error", async () => {
      const signer1 = signers[1];
      await expect(
        myTokenC.transfer(
          hre.ethers.parseUnits((mintingAmount + 1n).toString(), decimals),
          signer1.address,
        ),
      ).to.be.revertedWith("insufficient balance");
    });
  });
});

  • event 확인 시 주의점?
    expect 앞에 await를 써줘야 해당 contract가 실행하는 동안의 이벤트를 체크할 수 있다.. 아닐 경우 전부 참...?

단순 값 비교와 트랜잭션 검사의 차이?

  • balabceOf() 같은 부분의 expect()는 값을 기대함 --> promise가 끝나서 값이 나오도록 기다려줘야한다?
  • 그러나 transfer()는 트랜잭션을 발생시키고, .to.be.revertedWith()는 해당 트랜잭션의 실패 여부를 따짐 --> promise를 matcher?에게 바로 주고 그 실패 판단의 결과를 기다리는 형태?

  • matcher : 기대한 값이 실제 반환된 값과 일치하는 지를 확인하는 메서드
  • revert : 에러를 발생시키는 명령문/함수 (가스는 환불됨)

참고..


터미널2

❯ npx hardhat test                                             

  mytoken deploy
profile
배운 내용 정리&기록, 스크랩

0개의 댓글