5-3. 토큰 전송 (transfer) 함수

동동주·2025년 10월 10일


1. 토큰 전송 (transfer) 함수
2. 테스트


1. 토큰 전송 (transfer) 함수

코드1

transfer 함수만 추가
보내는 사람의 잔고가 충분한지 확인하고,
(부족할 시 생기는 overflow를 보기 좋게 다른 오류로 보여줌)
보내는 사람 / 받는 사람 잔고를 각각 변화시켜준다

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

contract MyToken {
    string public name;
    string public symbol;
    uint8 public decimals;

    //토큰의 총 발행 개수
    uint256 public totalSupply;

    //누가 얼마나 가지고 있는지
    mapping(address => uint256) public balanceOf;
    // (key => value)

    constructor(string memory _name, string memory _symbol, uint8 _decimals) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;
        _mint(1*10**uint256(decimals), msg.sender); // 1MT
    }

    function _mint(uint256 amount, address owner) internal {
        totalSupply += amount;
        balanceOf[owner] += amount; 
    }

    function transfer(uint256 amount, address to) external {
    	require(balanceOf[msg.sender] >= amount, "insufficient balance");

        balanceOf[msg.sender] -= amount; 
        balanceOf[to] += amount; 
    }
 
}
  • contract를 배포할 때 Signer를 지정할 수 있음
    (msg.sender의 값이 지정한 주소로 나오겠죠)

  • transaction VS view function
    state를 보기만 하면 (view function) api call로 리턴해주고, transaction이 아님. 이 때 여러 개의 노드를 참고할 수 있다. (무결성, 탈중앙화..)
    하지만 state를 초기화하거나 변경하는 건 transaction이고, 수수료를 내야한다.

터미널1

추가한 함수를 test에서 사용하려면 컴파일을 해줘야 한다.

❯ npx hardhat compile                                         
Generating typings for: 1 artifacts in dir: typechain-types for target: ethers-v6
Successfully generated 6 typings!
Compiled 1 Solidity file successfully (evm target: paris).



2. 테스트

코드2

  • before에서 beforEach로 변경
    각각의 테스트를 독립적으로 유지 (신경 안쓰고

  • "should have 0.5MT"
    signer1을 가져왔고,
    hre.ethers.parseUnits (또는 그냥 parseUnits)를 이용해 잔고를 확인했다.
    (이전 mint 테스트에서 한 직접 숫자를 적는 형식 대신에 사용)

  • "should be reverted with insufficient balance error"
    잔고인 0.5보다 많이 보내서 오류가 나는지 확인하는 테스트.
    await의 위치 중요!

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에서 컴파일 할 때 필요한 타입 정의 파일을 여기에 보관함

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

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

  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(18);
  });

  it("should retrun 1MT totalSupply", async () => {
    expect(await myTokenC.totalSupply()).equal(1n * 10n ** 18n);
  });

  // 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(1n * 10n ** 18n);
  });

  it("should have 0.5MT", async () => {
    const signer1 = signers[1];
    await myTokenC.transfer(hre.ethers.parseUnits("0.5", 18), signer1.address);
    expect(await myTokenC.balanceOf(signer1.address)).equal(
      hre.ethers.parseUnits("0.5", 18),
    );
  });

// 일부러 0.5보다 많이 보내서 오류 만들기
  it("should be reverted with insufficient balance error", async () => {
    const signer1 = signers[1];
    // await 위치 중요!!
    await expect(
      myTokenC.transfer(hre.ethers.parseUnits("1.1", 18), signer1.address),
    ).to.be.revertedWith("insufficient balance");
  });
  • await
    에러가 생기는 코드를 테스트할 때 await가 실행하는 코드 앞이 아니라 expect 앞에 위치한다. 코드를 실행하고 그냥 기다리면 오류가 나서 멈추는 것이지만, 코드를 실행해두고 값이 예상한 것(특정에러)이랑 같은지 기다리는 차이가 있는 것 같다.

터미널2

의도한대로 전송이 잘 되고, 오류가 나는 모습도 볼 수 있다~

❯ npx hardhat test                                             

  mytoken deploy
    ✔ should return name (68ms)
    ✔ should return symbol (45ms)
    ✔ should return decimals
    ✔ should retrun 1MT totalSupply
    ✔ should retrun 1MT balance for signer 0
    ✔ should have 0.5MT
    1) should return insufficient balance

  6 passing (3s)
  1 failing

  1) mytoken deploy
       should return insufficient balance:
     Error: VM Exception while processing transaction: reverted with reason string 'insufficient balance'
profile
배운 내용 정리&기록, 스크랩

0개의 댓글