sSPELL은 SPELL을 스테이킹 해서 받을 수 있다. 현재 보유하고 있는sSPELL 토큰의 갯수에 따라서 사용자가 SPELL fee pool 내 자신의 지분이 어느 정도인지 알 수 있다. 현재 sSPELL과 SPELL의 비율은 1:1.3692다. 일정 비율이 지날 때 서로 스왑할 수 있도록 설계는 했지만 그 일정 비율이 어느 정도인지 항상 알고 있어야 한다.
이러한 정보는 SPELL을 스테이킹 했을 때 sSPELL을 민팅해주는 컨트랙트에서 가져와야 한다. 여기서 핵심은 (amount * totalSupply) / totalTokens;
이 부분이다. 현재 컨트랙트가 가지고 있는 SPELL 토큰의 양(totalTokens)에 비해 가지고 있는 sSPELL의 양(totalSupply)을 이용해 비율을 결정한다. 또 한가지 주목할 부분은 emit Transfer()
다. emit 된 빈도수를 가지고 비율을 결정할 수 있을 것이다.
function mint(uint256 amount) public returns (bool) {
require(msg.sender != address(0), "Zero address");
User memory user = users[msg.sender];
uint256 totalTokens = token.balanceOf(address(this));
uint256 shares = totalSupply == 0 ? amount : (amount * totalSupply) / totalTokens;
user.balance += shares.to128();
user.lockedUntil = (block.timestamp + LOCK_TIME).to128();
users[msg.sender] = user;
totalSupply += shares;
token.safeTransferFrom(msg.sender, address(this), amount);
emit Transfer(address(0), msg.sender, shares);
return true;
}
그렇다면 우리도 실시간으로 비율을 가져오기 위해서 노드를 돌릴 필요가 있다.
가볍게 사용하기 좋은 Infura에서 노드를 만들어보자. 홈페이지에 가입해서 CREATE NEW KEY
를 통해 api key를 만든다. 이제 brownie에 적용해보자. api key를 export하고 이더리움 메인넷으로 네트워크를 변경하면 된다.
$ source .venv/bin/activate
$ export WEB3_INFURA_PROJECT_ID="" <- Infura api key 넣으면 된다.
$ brownie console --network mainnet
이제 각 토큰 주소를 추가하고 얼마나 들어있는지 확인해보자.
>>> spell = Contract.from_explorer('0x090185f2135308bad17527004364ebcc2d37e5f6')
>>> sspell = Contract.from_explorer('0x26fa3fffb6efe8c1e69103acb4044c26b9a106a9')
>>> sspell.totalSupply()
21698335947692476238338032297
>>> spell.balanceOf(sspell.address)
29709488074582133650944681754
이제 비율을 계산해보면 29709488074582133650944681754/21698335947692476238338032297 = 1.3692
가 나오는 것을 알 수 있다..!! 이제 비율을 자동으로 가져오는 봇을 만들어보자.
def main():
try:
network.connect("mainnet")
except:
sys.exit(
"Could not connect to Ethereum! Verify that brownie lists the Ethereum (Infura) Mainnet using 'brownie networks list'"
)
print("\nContracts loaded:")
spell_contract = contract_load(SPELL_CONTRACT_ADDRESS, "Token: SPELL")
sspell_contract = contract_load(SSPELL_CONTRACT_ADDRESS, "Token: sSPELL")
# creates a blank file, writes "0.0" to force a refresh in the main loop
with open(FILENAME, "w") as file:
file.write(str(0.0) + "\n")
while True:
#읽기모드로 파일 오픈해서 변수에 저장
with open(FILENAME, "r") as file:
abra_rate = float(file.read().strip())
try:
result = round(
spell_contract.balanceOf(sspell_contract.address)
/ sspell_contract.totalSupply(),
4,
)
except Exception as e:
print(f"{e}")
continue
if abra_rate and result == abra_rate:
pass
else:
print(f"Updated rate found: {result}")
abra_rate = result
with open(FILENAME, "w") as file:
file.write(str(abra_rate) + "\n")
time.sleep(60)
# Only executes main loop if this file is called directly
if __name__ == "__main__":
main()
로직은 간단하다. Brownie로 이더리움 네트워크에 연결하고 각 컨트랙트를 불러온다. 이후 빈 파일을 만들고 일정 시간마다 SPELL/sSPELL 비율을 계산해서 파일에 업데이트 한다.
with..as
: with..as로 파일을 열면 close()를 따로 호출하지 않아도 된다.
strip()
: 공백을 제거한다.
round( ,4)
: 소수점 넷째자리까지 반올림한다.