7-1. TinyBank) 기본구조 & Stake 기능

동동주·2025년 10월 18일


(이번에는 목차 아니고 내용)
1. TinyBank 구조 (인터페이스로 MyToken 사용 설정하기)
2. Stake 기능 구현하고, 작동 테스트

TinyBank의 구조와 관련 개념

MyToken과 TinyBank의 구조

MyToken : token balance management

  • the balabce of TinyBank address

TinyBank : (Mytoken) ==> (deposit / withdraw) vault

  • users token management
    user --> deposit --> TinyBank --> transfer(user --> Tiny)호출

- vault란..?

DeFi(탈중앙화 금융)에서 예치한 토큰의 이자를 주기적으로 예금해주는 예치 시스템을 일컫는 표현인 것 같다
Vault 이해하기


- staking

스마트 컨트랙트 기반 예치 상품 -> staking 단어 사용

  • staking 이란..?
    Staking은 보상을 받기 위한 목적의 예치로, 그냥 입금(예치)만 하는 Deposit과는 다르다.
  • 그럼 무슨 보상을 받는 걸까?
    PoS구조에서는 자산을 증명해 블록을 만들게 되고,
    이 때 블록이 만들어지면 보상을 받는다.
    (플랫폼의 운영/검증에 참여하고, 암호화폐를 보상받는 구조)
    |
    애초에 PoS 이름의 의미도 Proof of "Stake"이다.
    +기존 PoW(Proof of Work, 작업증명)방식은 컴퓨터의 연산력을 바탕으로 블록을 만들어서 에너지 소비가 크고, PoS에 비해 참여가 어려움.

참고:
쉽게 설명하는 블록체인 : 지분증명이란?
코인 스테이킹(Crypto Staking)


TinyBank 구조 & Stake기능 만들기

contract 폴더에 TinyBank.sol 파일 새로 제작.

contract/TinyBank.sol 코드

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

interface IMyToken {
    // MyToken.sol 파일에서 사용할 함수의 헤더 가져오기
    function transfer(uint256 amount, address to) external;
    function transferFrom(address from, address to, uint256 amount) external;
    
}



contract TinyBank {
    event Staked(address, uint256);

    IMyToken public stakingToken; //사용할 토큰 주소가 들어갈 변수 선언
    mapping(address => uint256) public staked; //balanceOf 와 유사
    uint256 public totalStaked; //totalsupply와 유사
    

    constructor(IMyToken _stakingToken) { //MyToken의 주소를 입력받아서 사용할 예정
        stakingToken = _stakingToken; //매개변수로 입력받은 주소 변수에 대입
    }


    function stake(uint256 _amount) external {
        require(_amount > 0, "cannot stake 0 amount");
        stakingToken.transferFrom(msg.sender, address(this), _amount);
        staked[msg.sender] += _amount;
        totalStaked += _amount;
        emit Staked(msg.sender, _amount);
    }
}

코드 흐름

1. TinyBank에서 MyToken 사용 설정
IMyToken 인터페이스* 타입 (사용할 함수 정의)
--> stakingToken 변수 선언
--> test파일에서 MyToken의 실제 주소가 TinyBank의 constructor로 전달됨.
=> stakingToken.transferFrom(~~) 처럼 사용
*인터페이스 vs 추상클래스 용도 차이점 - 완벽 이해 참고..! (글이 매우 좋았다)

2. Stake 기능 정의

  • staked 를 주소와 금액 mapping --> balanceOf 와 유사
  • totalStaked 정의 --> totalsupply와 유사
  • stake 함수 정의
    혹시 0원을 예치하는 건지 체크하고, 잔고 이동. +Staked 이벤트 발생

  • Staked 이벤트 정의
    주소와 금액을 받아서 이벤트 기록


+ tranferFrom 사용이유?
stakingToken.transfer(msg.sender, address[this], _amount);
해당 코드만 있으면 msg.sender = TinyBank가 됨 (잔액 부족)
--> transfer가 아닌 'transferFrom' 필요


test/TinyBank.ts 코드

import hre from "hardhat";
import { expect } from "chai";
import { DECIMALS, MINTING_AMOUNT } from "./constant";
import { MyToken, TinyBank } from "../typechain-types";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";

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

  beforeEach(async () => {
    signers = await hre.ethers.getSigners();
    myTokenC = await hre.ethers.deployContract("MyToken", [
      "MyToken",
      "MT",
      DECIMALS,
      MINTING_AMOUNT, //100MT 발행했었다
    ]);

// 아까 말한 MyToken주소 받아서 변수에 저장하는 부분.
    tinyBankC = await hre.ethers.deployContract("TinyBank", [
      await myTokenC.getAddress(),
    ]);
  });


  describe("Initialized state check", () => {
    it("should return totalStaked 0", async () => {
      expect(await tinyBankC.totalStaked()).equal(0);
    });
    it("should return staked 0 amount of signer0", async () => {
      const signer0 = signers[0];
      expect(await tinyBankC.staked(signer0.address)).equal(0);
    });
  });

  describe("Staking", async () => {
    it("should return staked amount", async () => {
      const signer0 = signers[0];
      const stakingAmount = hre.ethers.parseUnits("50", DECIMALS);
      
      await myTokenC.approve(await tinyBankC.getAddress(), stakingAmount);
      await tinyBankC.stake(stakingAmount);
      expect(await tinyBankC.staked(signer0.address)).equal(stakingAmount);
      expect(await tinyBankC.totalStaked()).equal(stakingAmount);
      expect(await myTokenC.balanceOf(tinyBankC)).equal(
        await tinyBankC.totalStaked(),
      );
    });
  });
}); //root grouping

코드 흐름

초기 상태 확인 (Initialized State Check)

  • totalStaked() 함수가 0을 반환하는지 테스트한다
  • 특정 사용자의 staked(address) 값이 0인지 확인한다 (예: signer0)

스테이킹 기능 테스트 (Staking)

git commit

❯ git commit -m "tiny bank service"


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

0개의 댓글