SolidityスマートコントラクトによるERC-20トークンの転送と承認
前回のチュートリアルでは、イーサリアムブロックチェーン上のSolidityで描かれたERC-20トークンの構造について学びました。 この記事では、Solidity言語で書かれたトークンとやり取りするためのスマートコントラクトの使い方について説明します。
このスマートコントラクトのために、新しくデプロイされたERC-20トークンでイーサを取引できる、ダミーの分散型取引所を実際に作成します。
このチュートリアルでは、前のチュートリアルで書いたコードをベースとして使います。 この分散型取引所(DEX)では、コントラクトのインスタンスをコンストラクタでインスタンス化し、以下の操作を実行します。
- トークンをイーサ(ETH)に交換
- イーサ(ETH)をトークンに交換
次のシンプルなERC20コードベースを追加することで、分散型取引所コードを開始します。
1pragma solidity ^0.8.0;23interface IERC20 {45 function totalSupply() external view returns (uint256);6 function balanceOf(address account) external view returns (uint256);7 function allowance(address owner, address spender) external view returns (uint256);89 function transfer(address recipient, uint256 amount) external returns (bool);10 function approve(address spender, uint256 amount) external returns (bool);11 function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);121314 event Transfer(address indexed from, address indexed to, uint256 value);15 event Approval(address indexed owner, address indexed spender, uint256 value);16}171819contract ERC20Basic is IERC20 {2021 string public constant name = "ERC20Basic";22 string public constant symbol = "ERC";23 uint8 public constant decimals = 18;242526 mapping(address => uint256) balances;2728 mapping(address => mapping (address => uint256)) allowed;2930 uint256 totalSupply_ = 10 ether;313233 constructor() {34 balances[msg.sender] = totalSupply_;35 }3637 function totalSupply() public override view returns (uint256) {38 return totalSupply_;39 }4041 function balanceOf(address tokenOwner) public override view returns (uint256) {42 return balances[tokenOwner];43 }4445 function transfer(address receiver, uint256 numTokens) public override returns (bool) {46 require(numTokens <= balances[msg.sender]);47 balances[msg.sender] = balances[msg.sender]-numTokens;48 balances[receiver] = balances[receiver]+numTokens;49 emit Transfer(msg.sender, receiver, numTokens);50 return true;51 }5253 function approve(address delegate, uint256 numTokens) public override returns (bool) {54 allowed[msg.sender][delegate] = numTokens;55 emit Approval(msg.sender, delegate, numTokens);56 return true;57 }5859 function allowance(address owner, address delegate) public override view returns (uint) {60 return allowed[owner][delegate];61 }6263 function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {64 require(numTokens <= balances[owner]);65 require(numTokens <= allowed[owner][msg.sender]);6667 balances[owner] = balances[owner]-numTokens;68 allowed[owner][msg.sender] = allowed[owner][msg.sender]-numTokens;69 balances[buyer] = balances[buyer]+numTokens;70 emit Transfer(owner, buyer, numTokens);71 return true;72 }73}7475すべて表示コピー
次の新しい分散型取引所(DEX)スマートコントラクトは 、ERC-20をデプロイし、供給されたすべてを取得します。
1contract DEX {23 IERC20 public token;45 event Bought(uint256 amount);6 event Sold(uint256 amount);78 constructor() {9 token = new ERC20Basic();10 }1112 function buy() payable public {13 // TODO14 }1516 function sell(uint256 amount) public {17 // TODO18 }1920}すべて表示コピー
これで分散型取引所(DEX)ができました。また、すべてのトークンリザーブが利用可能になりました。 コントラクトには、次の2つの関数があります。
buy
: ユーザーはイーサ(ETH)を送ってトークンに交換できますsell
: ユーザーはトークンを送信してイーサ(ETH)を取り戻すことができます
buy関数
buy関数をコーディングしてみましょう。 まず、メッセージに含まれるイーサ(ETH)の量を確認し、コントラクトが十分なトークンを所有していることと、そのメッセージにいくつかのイーサ(ETH)が含まれていることを検証する必要があります。 コントラクトが十分なトークンを所有している場合、ユーザーにその分のトークンを送信し、 Bought
イベントを発行します。
require関数の呼び出しがエラーだった場合に、送信されたイーサ(ETH)は直接元に戻され、ユーザーに返されることに注意してください。
ここではシンプルに、1トークンと1Weiを交換します。
1function buy() payable public {2 uint256 amountTobuy = msg.value;3 uint256 dexBalance = token.balanceOf(address(this));4 require(amountTobuy > 0, "You need to send some ether");5 require(amountTobuy <= dexBalance, "Not enough tokens in the reserve");6 token.transfer(msg.sender, amountTobuy);7 emit Bought(amountTobuy);8}コピー
購入が成功した場合、トランザクションにはTransfer
とBought
の2つのイベントが表示されます。
sell関数
売却を行う関数は事前にapprove関数を呼び出し、ユーザーがその金額を承認することを要求します。 転送を承認するには、分散型取引所(DEX)によってインスタンス化されたERC20Basicトークンがユーザーによって呼び出される必要があります。 これは、まず分散型取引所(DEX)コントラクトのtoken()
関数を呼び出し、分散型取引所(DEX)がtoken
というERC20Basicコントラクトをデプロイしたアドレスを取得することで実現できます。 次に、セッション内にそのコントラクトのインスタンスを作成し、そのapprove
関数を呼び出します。 次に、分散型取引所(DEX)のsell
関数を呼び出すことで、トークンをイーサ(ETH)に交換できます。 例えば、インタラクティブ・ブラウニー(interactive brownie)セッションでは、次のようになります。
1#### Python in interactive brownie console...23# deploy the DEX4dex = DEX.deploy({'from':account1})56# call the buy function to swap ether for token7# 1e18 is 1 ether denominated in wei8dex.buy({'from': account2, 1e18})910# get the deployment address for the ERC20 token11# that was deployed during DEX contract creation12# dex.token() returns the deployed address for token13token = ERC20Basic.at(dex.token())1415# call the token's approve function16# approve the dex address as spender17# and how many of your tokens it is allowed to spend18token.approve(dex.address, 3e18, {'from':account2})19すべて表示コピー
その後、sell関数が呼び出されたときに、呼び出し元のアドレスからコントラクトアドレスへの転送が成功したかどうかを確認し、その後イーサ(ETH)を呼び出し元のアドレスに送信します。
1function sell(uint256 amount) public {2 require(amount > 0, "You need to sell at least some tokens");3 uint256 allowance = token.allowance(msg.sender, address(this));4 require(allowance >= amount, "Check the token allowance");5 token.transferFrom(msg.sender, address(this), amount);6 payable(msg.sender).transfer(amount);7 emit Sold(amount);8}コピー
すべてがうまくいけば、トランザクションに2つのイベント(Transfer
と Sold
)が表示され、トークンの残高とイーサの残高が更新されるはずです。
このチュートリアルでは、残高とERC-20トークンの割当量を確認する方法と、インターフェースを使用してERC20スマートコントラクトのTransfer
とTransferFrom
を呼び出す方法について説明しました。
一度トランザクションが作成されると、コントラクト用に作成されている待機してトランザクションについての詳細を取得する(opens in a new tab)ためのJavaScriptチュートリアルや、アプリケーションバイナリインターフェース(ABI)があれば、トークン転送や他のイベントによって発行されるイベントをデコードするチュートリアル(opens in a new tab)を参照することで情報を取得できます。
チュートリアルの完全なコードは、次のようになります。
1pragma solidity ^0.8.0;23interface IERC20 {45 function totalSupply() external view returns (uint256);6 function balanceOf(address account) external view returns (uint256);7 function allowance(address owner, address spender) external view returns (uint256);89 function transfer(address recipient, uint256 amount) external returns (bool);10 function approve(address spender, uint256 amount) external returns (bool);11 function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);121314 event Transfer(address indexed from, address indexed to, uint256 value);15 event Approval(address indexed owner, address indexed spender, uint256 value);16}171819contract ERC20Basic is IERC20 {2021 string public constant name = "ERC20Basic";22 string public constant symbol = "ERC";23 uint8 public constant decimals = 18;242526 mapping(address => uint256) balances;2728 mapping(address => mapping (address => uint256)) allowed;2930 uint256 totalSupply_ = 10 ether;313233 constructor() {34 balances[msg.sender] = totalSupply_;35 }3637 function totalSupply() public override view returns (uint256) {38 return totalSupply_;39 }4041 function balanceOf(address tokenOwner) public override view returns (uint256) {42 return balances[tokenOwner];43 }4445 function transfer(address receiver, uint256 numTokens) public override returns (bool) {46 require(numTokens <= balances[msg.sender]);47 balances[msg.sender] = balances[msg.sender]-numTokens;48 balances[receiver] = balances[receiver]+numTokens;49 emit Transfer(msg.sender, receiver, numTokens);50 return true;51 }5253 function approve(address delegate, uint256 numTokens) public override returns (bool) {54 allowed[msg.sender][delegate] = numTokens;55 emit Approval(msg.sender, delegate, numTokens);56 return true;57 }5859 function allowance(address owner, address delegate) public override view returns (uint) {60 return allowed[owner][delegate];61 }6263 function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {64 require(numTokens <= balances[owner]);65 require(numTokens <= allowed[owner][msg.sender]);6667 balances[owner] = balances[owner]-numTokens;68 allowed[owner][msg.sender] = allowed[owner][msg.sender]-numTokens;69 balances[buyer] = balances[buyer]+numTokens;70 emit Transfer(owner, buyer, numTokens);71 return true;72 }73}747576contract DEX {7778 event Bought(uint256 amount);79 event Sold(uint256 amount);808182 IERC20 public token;8384 constructor() {85 token = new ERC20Basic();86 }8788 function buy() payable public {89 uint256 amountTobuy = msg.value;90 uint256 dexBalance = token.balanceOf(address(this));91 require(amountTobuy > 0, "You need to send some ether");92 require(amountTobuy <= dexBalance, "Not enough tokens in the reserve");93 token.transfer(msg.sender, amountTobuy);94 emit Bought(amountTobuy);95 }9697 function sell(uint256 amount) public {98 require(amount > 0, "You need to sell at least some tokens");99 uint256 allowance = token.allowance(msg.sender, address(this));100 require(allowance >= amount, "Check the token allowance");101 token.transferFrom(msg.sender, address(this), amount);102 payable(msg.sender).transfer(amount);103 emit Sold(amount);104 }105106}すべて表示コピー
最終編集者: @nhsz(opens in a new tab), 2023年8月15日