(이번에는 목차 아니고 내용)
1. TinyBank 구조 (인터페이스로 MyToken 사용 설정하기)
2. Stake 기능 구현하고, 작동 테스트
MyToken : token balance management
TinyBank : (Mytoken) ==> (deposit / withdraw) vault
DeFi(탈중앙화 금융)에서 예치한 토큰의 이자를 주기적으로 예금해주는 예치 시스템을 일컫는 표현인 것 같다
Vault 이해하기
스마트 컨트랙트 기반 예치 상품 -> staking 단어 사용
- staking 이란..?
Staking은 보상을 받기 위한 목적의 예치로, 그냥 입금(예치)만 하는 Deposit과는 다르다.
- 그럼 무슨 보상을 받는 걸까?
PoS구조에서는 자산을 증명해 블록을 만들게 되고,
이 때 블록이 만들어지면 보상을 받는다.
(플랫폼의 운영/검증에 참여하고, 암호화폐를 보상받는 구조)
|
애초에 PoS 이름의 의미도 Proof of "Stake"이다.
+기존 PoW(Proof of Work, 작업증명)방식은 컴퓨터의 연산력을 바탕으로 블록을 만들어서 에너지 소비가 크고, PoS에 비해 참여가 어려움.
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 기능 정의
stake 함수 정의
혹시 0원을 예치하는 건지 체크하고, 잔고 이동. +Staked 이벤트 발생
Staked 이벤트 정의
주소와 금액을 받아서 이벤트 기록
+ tranferFrom 사용이유?
stakingToken.transfer(msg.sender, address[this], _amount);
해당 코드만 있으면 msg.sender = TinyBank가 됨 (잔액 부족)
--> transfer가 아닌 'transferFrom' 필요
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)
스테이킹 기능 테스트 (Staking)
❯ git commit -m "tiny bank service"