토카막 네트워크 스테이킹 컨트랙트 살펴보기

Harvey Jo
Tokamak Network
Published in
16 min readJul 7, 2021

--

들어가며

토카막 네트워크 스테이킹 컨트렉트는 TON을 가지고 있는 홀더분들이 이용할 수 있는 컨트렉트 중 하나로서, 스테이킹을 하게 되면 시뇨리지(TON)를 얻을 수 있는 이점이 있습니다.

시뇨리지의 사전적 정의는 중앙은행이나 정부가 화폐를 발권함으로서 얻는 수익을 시뇨리지라고 합니다. 이를 토카막 네트워크 스테이킹 컨트렉트 관점에서 보면 스테이킹 컨트렉트를 이용하면 TON이 발권이 되는데 이 때 발권된 TON을 시뇨리지로 볼 수 있습니다. 스테이킹 컨트렉트에선 이 시뇨리지를 톤 스테이커등 컨트렉트에 설계된 내용에 따라 나누어 주게 됩니다.

이번 포스팅은 토카막 네트워크 스테이킹 컨트랙트가 어떻게 동작하고, 시뇨리지는 어떻게 계산이 되어서 톤 스테이커에게 주어지는지 설명합니다.

컨트랙트 개요

컨트랙트는 유저가 스테이킹 했을 때 스테이킹 값들을 어떻게 관리하고, 시뇨리지가 어떻게 계산되고, 그 시뇨리지를 어떻게 분배하며 관리하는지를 보여줍니다.

먼저 각 컨트랙트들이 어떠한 역할을 하는지 알아보겠습니다.

  1. TON : TON을 만들고 관리합니다.
  2. WTON : TON <-> WTON으로 스왑해주고 WTON을 DepositManager Contract에 Deposit합니다.
  3. DepositManager : WTON을 deposit, redeposit, requestWithdrawal, processRequest하는 것을 관리하고 withdrawDelay를 설정합니다.
  4. SeigManager : 시뇨리지를 관리하고 계산합니다.
  5. Layer2Registry : coinage를 등록합니다.
  6. CoinageFactory : coinage를 만들어줍니다.
  7. AutoRefactorCoinage : factor 값을 이용해 coinage의 시뇨리지를 계산해줍니다.
  8. PowerTON : 2주에 한번씩 당첨자를 추첨하여 powerTON에 쌓인 시뇨리지를 줍니다..

그리고 스테이킹 컨트랙트에서 현재의 총 톤발행량, 블록당 시뇨리지 양, 각 분배비율은

  1. 총 톤발행량 : 50,000,000 TON (TON Contract의 totalSupply()을 호출하여서 확인 할 수 있습니다. 추가 발행되는 시뇨리지에 따라 변경될 수 있습니다.)
  2. 블록당 시뇨리지 : 3.92 TON (SeigManager Contract의 seigPerBlock()을 호출하여서 확인 할 수 있습니다. 블록당 시뇨리지 값은 변경되지 않습니다.)
  3. 각 분배비율 : 시뇨리지는 크게 2가지로 나눌 수 있습니다.
    ⓵ staking seigniorage = 블록당 최대 시뇨리지 계산 값 * (톤 스테이킹양/총 톤발행량)
    ⓶ unstaking seigniorage = 블록당 최대 시뇨리지 계산 값 * [ (총 톤발행량-톤스테이킹 양)/총 톤발행량]
    여기서 staking seigniorage는 톤 스테이커들에게만 주어지고 unstaking seignoiorage는 톤 스테이커(40%), powerTON(5%), DAO(50%)로 각각의 비율로 주어지게 됩니다. (이 비율은 각각 링크된 함수를 통해서 변경을 할 수 있으며 SeigManager 컨트랙트의 powerTONSeigRate, daoSeigRate, relativeSeigRate함수 호출를 통해서 비율을 확인할 수 있습니다.)

아래에서 이 시뇨리지를 주는 스테이킹 컨트랙트가 어떻게 진행되는지, 시뇨리지는 어떻게 계산되며 분배되는지 알아보겠습니다.

스테이킹 컨트랙트

유저가 TON으로 스테이킹을 신청하게 되면 스테이킹 프로세스는 다음과 같습니다.

  1. TON의 단위를 WTON으로 바꾸고 WTON을 mint합니다.
  2. TON을 WTON CA에 저장 후 WTON은 DepositManager에 deposit합니다.
  3. WTON을 DepositManager Contract에 저장 및 기록 후 SeigManager에게 알립니다.
  4. SeigManager에서는 시뇨리지를 관리하기 위해서 저장된 WTON의 양만큼 tot와 coinage에 mint해줍니다.

CA는 Contract Account(계약계정)으로 스마트컨트랙트 역할이 가능하고 ‘배포된 코드’와 ‘저장공간’이 추가로 존재합니다. CA는 메시지를 받은 후 내부 코드를 실행한 후 데이터를 내부 저장 공간에 저장하는 역할을 합니다.

스테이킹을 할때 TON이 아닌 WTON으로 스왑 후 토카막 네트워크 스테이킹 을 하는 이유는 시뇨리지를 계산할 때 소수점이 잘리는 것을 최소화하기 위해서 WTON을 이용하여서 시뇨리지를 계산하고 주게 됩니다.

시뇨리지 계산이 WTON이 더 정확한 이유는 TON의 decimal은 18이고, WTON의 decimal은 27으로 단위가 차이가 나서 WTON으로 주면 더 정확하게 시뇨리지를 줄 수 있습니다.

토카막 네트워크 스테이킹 컨트랙트에서는 시뇨리지를 tot, coinage와 operator를 이용해서 관리를 하고 있습니다. 아래에서 토카막 네트워크 스테이킹 컨트랙트에서의 tot, coinage와 operator의 개념에 대해서 설명하겠습니다.

위의 그림은 예시입니다. 여기서 coinage는 tokamak1, DXM, DSRV이고 tot는 모든 coinage를 모아서 시뇨리지를 관리하는 곳이고, delegator는 각 coinage에 톤 스테이커들이 됩니다.

실제 토카막 네트워크 웹서비스에 적용해서 설명하겠습니다.
위의 그림은 현재 서비스 중인 토카막 네트워크 스테이킹에서 staking 화면입니다. 여기서 보이는 Tokamak1, Level 19, DSRV, staked, Talken 가 coinage가 되고 이것을 종합해서 관리하는 곳이 tot이고 유저가 coinage 중 하나를 선택해 staking을 하면 그 유저는 coinage의 delegator가 되게 됩니다.

그리고 operator는 Layer2에서 생성된 블록 정보를 Layer1에 올리는 역할을 담당하고, Layer2의 정보를 Layer1에 올리는 동시에 Layer1의 tot, coinage에서 스테이킹 시뇨리지를 분배합니다.
Layer2 정보를 Layer1에 올릴 때 operator는 정보업데이트 transaction에 대한 gasFee를 내야 하는데, 이에 대한 보상으로 commissionRate값에 따라 추가 시뇨리지를 더 받습니다.

현재 토카막 네트워크 스테이킹 웹서비스 중 위의 3개의 Layer2는 DAO candidate로 만들어진 컨트랙트로 operator가 아닌 누구나 시뇨리지를 업데이트 할 수 있습니다.

시뇨리지 계산

시뇨리지는 위에서 언급했듯이 톤 스테이커들에게 주는 시뇨리지, powerTON에게 주는 시뇨리지, DAO에게 주는 시뇨리지로 나누어서 시뇨리지를 줍니다.

시뇨리지를 분배하는 비율은 SeigManager 컨트랙트의 owner가 관리하며 분배 비율을 다음과 같이 예를 들어보겠습니다.

  • relativeSeigRate(톤 스테이커들에게 주는 시뇨리지) = 0.4
  • powerTONSeigRate(powerTON에게 주는 시뇨리지) = 0.05
  • daoSeigRate(DAO에게 주는 시뇨리지) = 0.5

위의 비율에 포함되지 않는 비율 0.05는 버려지게 됩니다.

블록 하나당 주어지는 시뇨리지는 seigPerBlock 값이며 이 값은 약 3.92입니다. 이 값을 이용해서 operator가 updateSeigniorage를 할 때, maxSeig(최대로 줄 수 있는 시뇨리지)값을 구할 수 있는데 maxSeig = (현재 블록넘버-가장 최근 updateSeigniorage를 한 블록넘버) * seigPerBlock 입니다.

maxSeig값은 TON을 소유한 모든 홀더가 모든 TON을 스테이킹을 시킨다면, 톤 스테이커가 받을 수 있는 시뇨리지 값 입니다. 하지만 현실에선 TON홀더 모두가 스테이킹을 하지 않기 때문에 톤 스테이커스테이킹하지않은 홀더로 나눌 수 있습니다.

톤 스테이커에 대한 시뇨리지를 stakedSeig라고 하고 스테이킹하지않은 홀더에 대한 시뇨리지를 unstakedSeig(maxSeig-stakedSeig)라고 하겠습니다.
stakedSeig는 100% 톤 스테이커에 대한 몫입니다.
하지만 unstakedSeig는 위의 시뇨리지 분배 비율에 따라서 톤 스테이커=40%, powerTON=5%, DAO=50%의 비율로 분배됩니다.

이 비율을 이용하여 최종적인 시뇨리지는

이렇게 주어지게 됩니다.

만약 operator가 block.number = 10일 때 updateSeigniorage한 후, block.number = 20일 때 updateSeigniorage를 하게 된다면 시뇨리지를 어떻게 받게 되는지 계산해보겠습니다.
(간단한 계산을 하기 위해서 실제값이 아닌 예시값입니다.)

  • 총 TON의 발행량 = 5,000
  • commissionRate= 0
  • tokamak1 operator = 1,000
  • A staking amount (tokamak1) = 100
  • B staking amount (tokamak1) = 200
  • C staking amount (tokamak1) = 500
  • total staking amount (tokamak1) = 1,800

maxSeig = (20–10) * 3.92 = 39.2
stakedSeig = 39.2 * (1,800/5,000) = 14.112
unstakedSeig = 39.2–14.112 = 25.088

1. 톤 스테이커들에게 주는 시뇨리지 = 14.112 + 25.088*0.4 = 24.147
1–1. A staking 시뇨리지 = 24.147*100/1,800 = 1.3415
1–2. B staking 시뇨리지 = 24.147*200/1,800 = 2.6830
1–3. C staking 시뇨리지 = 24.147*500/1,800 = 6.7075
1–4. operator staking 시뇨리지 = 24.147*1,000/1,800 = 13.4151

2. powerTON에게 주는 시뇨리지 = 25.088 * 0.05 = 1.2544

3. DAO에게 주는 시뇨리지 = 25.088 * 0.5 = 12.544

위와 같이 시뇨리지는 분배되게 됩니다.

시뇨리지 업데이트 코드

위는 SeigManager 컨트랙트의 updateSeigniorage function 입니다. 이 함수를 따라서 시뇨리지가 어떻게 분배되는지 살펴보겠습니다.

uint256 operatorAmount = getOperatorAmount(msg.sender);    require(operatorAmount >= minimumAmount);

operator의 조건을 검사하는 코드입니다. operator가 되기 위해서는 1000TON이상을 가지고 있어야하는데 이를 검사하여서 operator의 조건이 충족하는지 봅니다.

_increaseTot();

_increaseTot()의 전체코드는 위와 같습니다.

prevTotalSupply = _tot.totalSupply();uint256 maxSeig = _calcNumSeigBlocks().mul(_seigPerBlock);

시뇨리지가 추가되기전의 totalSupply를 prevTotalSupply에 넣습니다.
_calcNumSeigBlocks()를 이용하여서 시뇨리지를 발급하고 얼만큼의 블록이 지났는지 계산을하여서 maxSeig = (지난블록)*(_seigPerBlock)을 계산합니다.

uint256 tos = _ton.totalSupply()      .sub(_ton.balanceOf(address(_wton)))      
.mul(10 ** 9)
.add(_tot.totalSupply());
uint256 stakedSeig = rdiv(rmul(maxSeig,_tot.totalSupply()),tos);

tos(TON 스테이킹 양)와 maxSeig(블록당 최대 시뇨리지 값), totalSupply(총 톤발행량)값을 이용하여서 stakedSeig값을 구합니다. 이는 위에서 말한 stakedSieg값은 블록당 시뇨리지 계산 값 * (톤 스테이킹양/총 톤발행량)의 식과 같습니다.

uint256 totalPseig = rmul(maxSeig.sub(stakedSeig),relativeSeigRate); 

nextTotalSupply = prevTotalSupply.add(stakedSeig).add(totalPseig);
_lastSeigBlock = block.number;_tot.setFactor(_calcNewFactor(prevTotalSupply, nextTotalSupply, _tot.factor()));

totalPseig는 unstakedSeig 중 톤스테이커에게 주는 추가 시뇨리지입니다. 그래서 톤스테이커들에게 추가되는 시뇨리지는 stakedSeig와 totalPseig가 추가가 되고 이 값들을 prevTotalSupply에 더하게되면 nextTotalSupply값을 구할 수 있습니다.
prevTotalSupply와 nextTotalSupply이 값을 _tot.setFactor함수에 이용하면 톤스테이커에게 시뇨리지가 주어지게 됩니다.

톤스테이커에게 시뇨리지를 주어지게되면 남은 시뇨리지분배는 powerTON과 DAO가 있는데 이는 다음의 코드로 실행되게 됩니다.

if (address(_powerton) != address(0)) {      
powertonSeig = rmul(unstakedSeig, powerTONSeigRate);
_wton.mint(address(_powerton), powertonSeig);
}
if (dao != address(0)) {
daoSeig = rmul(unstakedSeig, daoSeigRate);
_wton.mint(address(dao), daoSeig);
}

이렇게 __increaseTot() 함수에서 톤스테이커, powerTON, DAO에게 각각 시뇨리지가 분배되는게 끝이나고 updateSeigniorage()함수로 돌아가게 됩니다.

(nextTotalSupply, operatorSeigs) = _calcSeigsDistribution(            
msg.sender,
coinage,
prevTotalSupply,
seigs,
isCommissionRateNegative,
operator
);
// gives seigniorages to the layer2 as coinage
coinage.setFactor(
_calcNewFactor(
prevTotalSupply,
nextTotalSupply,
coinage.factor()
)
);
// give commission to operator or delegators
if (operatorSeigs != 0) {
if (isCommissionRateNegative) {
coinage.burnFrom(operator, operatorSeigs);
} else {
coinage.mint(operator, operatorSeigs);
}
}
_wton.mint(address(_depositManager), seigs);

이제 updateSeigniorage함수에서 남은 작업은 누가 언제 updateSeigniorage를 하였는지 저장하고, 변경된 값들을 최신화 시켜줍니다. 그리고 시뇨리지를 제대로 업데이트해준 operator에게 Seigniorage계산하여서 주게됩니다.

위의 내용은 코드를 통해서 시뇨리지가 어떻게 분배되는지 보았습니다. 아래에서는 시뇨리지 발급을 통해서 TON의 인플레가 어떻게 될지를 예상해보겠습니다.

명목 추가 시뇨리지

추가되는 시뇨리지는 만약 모든 톤 보유자가 스테이킹을 한다면 매년9,500,000TON으로 초기 발행량의 50,000,000TON에 대하여 초기 1년은 19%가 톤의 명목 인플레율이 됩니다.

위의 표는 초기 1년간 스테이킹 비율에 따라 각각의 시뇨리지가 어떻게 주어지고 TON의 총 발행량이 달라지는 것을 확인할 수 있습니다.

시뇨리지가 최대로 발급되기 위해서는 TON을 가진 모든 유저가 스테이킹하는 것으로 100%로 스테이킹한다면 9,500,000 TON이 매년 시뇨리지로 발급되고 이렇게 5년이 지나게되면 5년 후의 총 TON의 발급량은 97,500,000 TON이 됩니다.

위의 표를 보시면 현재는 톤스테이커 시뇨리지, powerTON 시뇨리지, DAO 시뇨리지로 시뇨리지가 분배가 되는데 이는 이후에 토카막 네트워크에서 제공하는 서비스가 추가되면서 항목이나 비율이 변경 될 수 있습니다.

결론

  • 유저가 TON을 스테이킹하게 되면 TON은 WTON으로 스왑되어서 스테이킹하게 되고 tot, coinage, operator에 의해서 시뇨리지 시스템이 관리가 됩니다.
  • tot : 총 coinage의 시뇨리지를 관리합니다.
  • coinage : 유저가 선택해서 스테이킹 할 수 있는 곳 입니다.
  • operator : coinage 관리자(Layer2에서 생성된 블록 정보를 Layer1에 올립니다.)
  • 시뇨리지는 톤 스테이커들에게 주는 시뇨리지, powerTON에게 주는 시뇨리지와 DAO에게 주는 시뇨리지가 있습니다.
  • 톤 스테이커들에게 주는 시뇨리지 : 톤 스테이커들이 받는 시뇨리지
  • powerTON에게 주는 시뇨리지 : 유저가 톤 스테이커가 되면 유저는 powerTON에 참여하게 되는데, powerTON에서는 2주에 한 번 추첨하여서 쌓인 시뇨리지를 유저에게 지급합니다.
  • DAO에게 주는 시뇨리지 : 토카막 다오 컨트렉트에 이용되는 시뇨리지입니다. DAO에 관한 내용은 토카막 다오의 모든 것에서 확인가능합니다.
  • TON스테이킹 시 수익을 계산하시고 싶은 분은 토카막 네트워크 스테이킹 간편 계산에서 간편하게 예상 수익을 계산하실 수 있습니다.

감사합니다.

--

--