1. 이벤트 발생&확인
2. 이벤트 활용
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);
}
이벤트가 일어나는 곳인 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
]
}
좀 더 찾아봐야 할 듯..
이벤트와 로그,
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);
}
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에서 최신까지
(현재는 테스트라서 할 때마다 첫 로그인거라 괜찮지만, 실제 환경에서는 너무 많으니까 주석처럼 사용..)
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");
});
});
});
단순 값 비교와 트랜잭션 검사의 차이?
- balabceOf() 같은 부분의 expect()는 값을 기대함 --> promise가 끝나서 값이 나오도록 기다려줘야한다?
- 그러나 transfer()는 트랜잭션을 발생시키고, .to.be.revertedWith()는 해당 트랜잭션의 실패 여부를 따짐 --> promise를 matcher?에게 바로 주고 그 실패 판단의 결과를 기다리는 형태?
- matcher : 기대한 값이 실제 반환된 값과 일치하는 지를 확인하는 메서드
- revert : 에러를 발생시키는 명령문/함수 (가스는 환불됨)
참고..
❯ npx hardhat test
mytoken deploy