추가 공부 [스테이킹]

Lumi·2022년 1월 30일
1

Block_Chain_Project

목록 보기
24/30
post-thumbnail
  • 단순히 컨트랙트만 작성을 하였습니다.

🔥 개요

원래는 Swap을 좀 유동적으로 하는 풀을 구성해 보고자 하였습니다.

그러기 위해서 Uniswap Github를 뒤적뒤적 거렸지만

생각보다 복잡하고.. 어떤식으로 작성을 해야할까 고민을 하던중에

좀더 내가 접근 가능하고 스스로 완전히 이해하고 있는 영역을 먼저 작성을 해보자는 생각을 하게 되었습니다.

그래서 그나마 쉽게 만들수 있다고 생각한 스테이킹 컨트랙트 코드를 작성을 하였습니다.

  • 완벽하다고 할수는 없지만 나름 만족스럽게 작성을 하였습니다.

🔨 Flow Chart

시각적으로 보면 이와 같습니다.

기본적으로 Staking은 특정 코인을 잠금처리하고 일정 기간마다 해당 코인을 받게 됩니다.

  • 예를들면 Klaytn을 스테이킹 하면 스테이킹한 비율에 비례하여 Klaytn토큰이 지급이 됩니다.

저같은 경우에는 이러한 방법과는 다르게 스테이블과 같은 코인을 Staking함으로써 보상으로 또다른 코인을 주는 형태를 생각하였습니다.

  • 이런 형태에는 정답이 없다고 생각을 합니다!

🔥 Solidity

🔨 TokenFarm.sol

Token컨트랙트는 일반적으로 코드이기 떄문에 다루지 않고 직접적으로 스테이킹을 하는 컨트랙트를 살펴보겠습니다.

pragma solidity 0.8.0;

import "./DappToken.sol";
import "./DaiToken.sol";

contract TokenFarm {
    string public name = "Dapp Token Farm";
    address public owner;
    DappToken public dappToken;
    DaiToken public daiToken;

    address[] public stakers;
    mapping(address => uint256) public stakingBalance;
    mapping(address => bool) public hasStaked;
    mapping(address => bool) public isStaking;

    constructor(DappToken _dappToken, DaiToken _daiToken)  {
        dappToken = _dappToken;
        daiToken = _daiToken;
        owner = msg.sender;
    }
    // 기본적으로 사용자가 실행을 주체
    // -> 중앙화되어있지 않음

    function stakeTokens(uint256 _amount) public {
        require(_amount > 0, "amount cannot be 0"); // 반드시 스테이킹 할 양은 0보다 커야한다.

        daiToken.transferFrom(msg.sender, address(this), _amount); // 사용자, CA, 금액을 전송

        // (bool success, bytes memory data) = address(daiToken).delegatecall(abi.encodeWithSignature("transferFrom(address,address,uint256)", msg.sender, address(this), _amount));
        // delegatecall도 활용해 보고자 하였지만 daiToken의 msg.sender를 굳이 사용자로 지정할 필요성을 느끼지 못하여 활용은 하지 않았습니다.

        stakingBalance[msg.sender] = stakingBalance[msg.sender] + _amount; // 스테이킹한 양만큼 증가시킨다.

        if (!hasStaked[msg.sender]) {
            stakers.push(msg.sender);
        }

        // 스테이킹을 하였다는것을 표시해주는 역할
        isStaking[msg.sender] = true;
        hasStaked[msg.sender] = true;
    }


    function unstakeTokens() public {

        uint256 balance = stakingBalance[msg.sender];


        require(balance > 0, "staking balance cannot be 0");


        daiToken.transfer(msg.sender, balance);

        stakingBalance[msg.sender] = 0;
        isStaking[msg.sender] = false;
    }

    // 스테이킹 보상을 주는 함수
    function issueTokens() public {
        require(msg.sender == owner, "caller must be the owner"); // 운영자만이 사용 가능합니다.

        for (uint256 i = 0; i < stakers.length; i++) {
            address recipient = stakers[i];
            uint256 balance = stakingBalance[recipient];
            if (balance > 0) {
                dappToken.transfer(recipient, balance);
            }
        }

    }
}

스테이킹 서비스는 3가지의 함수로 작동이 이루어 집니다.

  1. stakeTokens
  • 원하는 금액만큼 스테이킹을 실행시키는 함수입니다.
  1. unstakeTokens
  • 자신이 스테이킹한 모든 금액을 취소하는 함수 입니다.
  1. issuetokens
  • 스테이키한 사용자에게 스테이킹한 토큰의 수만큼 다른 토큰을 지급하는 함수입니다.

일단 기본적으로 스테이킹하는 Token은 TokenFarm컨트랙트의 CA주소값에 할당이 됩니다.

  • CA주소를 가르키는 코드는 address(this)입니다.

transferFrom으로 작동이 이루어 지며

transferFrom내부에는 require(_value <= allowance[_from][msg.sender]);

와 같이 조건문이 있습니다.

즉 이전에 approve를 통해서 수량을 정하지 않는 사용자는 사용이 불가능합니다.

그러기 떄문에 반드시 Staking서비스를 이용하고자 하면 approve를 사용해야 하며

이러한 부분은 직접 구현되는 시스템에 따라서 입맛에 맞게 적용 가능하다고 생각합니다.

  • 굳이 조건문을 추가하지는 않아도 된다고는 생각합니다.

이런방식으로 작동이 이루어지며 unstakeTokens에는 별다른 제약을 걸지 않았습니다.

  • 왜냐하면 기본적으로 DAO형태인 사용자가 직접 실행시키는 구조이기 떄문입니다.
  • 그러기 떄문에 언스테이킹 서비스의 경우 굳이 제약을 걸 필요는 없고
  • 단순히 실제 스테이킹한 금액이 있는지만을 확인하면 됩니다.

추가 할수 있는 사항으로는 이 코드 같은 경우에는 모든 금액을 언스테이킹 하게 되지만 원하는 금액을 만큼 value를 받는 식으로도 하여 필요한 금액만 언스테이킹하게 만들수 있습니다.

issueTokens함수는 운영자가 직접 실행시키는 컨트랙트로써

특정시점에 실행시킴으로써 실제 스테이킹을 한 유저들에게 다른 토큰을 지급하는 함수 입니다.

🔥 의문점

예전부터 가지고 있던 의문점 이지만 transferFrom의 효율성을 제대로 이해를 하고 있지 못하는거 같습니다.

  • 개인적으로는 사용해야 하냐에 대한 의문이 있습니다.

allowance라는 변수에 거래하고자 하는 금액을 저장해둠으로써 거래가 이루어 지게 될텐데

이러한 코드에서는 사용자가 직접 거래를 원하기 떄문에 굳이 추가해야 하나 라는 의문이 있습니다.

  • 그래도 기본 형식을 따라가 보자는 의미에서 추가를 하였습니다.

이 부분에 대하여 그래도 좀더 제가 이해가 부족하다고 생각을 하기 떄문에 학습을 해본뒤에 다음 글에서 정리를 해보고자 합니다!

🔥 후기

이러한 틀을 짜고 작성을 한 것이지 아직 부족한 부분은 많다고 생각을 합니다.

  • 정상적인 작동이라든지 로직적인 부분이라던지

그러기 떄문에 좀더 수정을 거쳐볼 예정이고 그후에는 Swap에 좀더 집중을 해보고 싶습니다.

Swap마저도 해결이 된다면 예전에 너무나도 공부해 보고 싶었지만 JS를 공부하느라 하지 못했던 GoLang을 공부해 보고 싶습니다.

다음 글 주제

  • transferFrom이 존재해야 하는지에 대한 의문 해결
  • Swap에 대하여 좀더 부수적인 공부 및 코드 작성
  • Golang이전에 공부했던 부분 또는 그 이후로 공부

이 글을 누가 읽을지는 모르겠지만 모두 새해복 많이 받으시길!!

감사합니다!

profile
[기술 블로그가 아닌 하루하루 기록용 블로그]

0개의 댓글