ERC20開発ハンズオン3(コントラクトへの委任編)

# はじめに

https://qiita.com/shu-kob/items/3dab9f7ea642f3ef0d41

こちらの続きで、コントラクトへの委任をして、ICOのようにユーザがコントラクトアドレスへETHを送金したら、rateに応じてERC20トークンが送られる簡易的な仕組みを体験してみましょう。

※ 本記事はICOを推奨するものではありません。ICOは日本国内や海外においても規制上実施するのは困難です。ICOを行うと違法の可能性があります。本記事で扱うコントラクトは簡易的なものであり、mainnetで使用された場合に事故が発生しても責任は負いません。

# コントラクトへの委任

https://github.com/shu-kob/erc20/blob/master/TokenVendingMachine.sol

```TokenVendingMachine.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract TokenVendingContract {

    ERC20 public ERC20Token;
    address public TokenOwner;
    uint256 private weiAmount;
    uint256 private weiBalance;

    function TokenVending(address _ERC20Token, address payable _TokenOwner) public {
        ERC20Token = ERC20(_ERC20Token);
        TokenOwner = _TokenOwner;
    }

    function withdrawal(uint withdraw_amount) private {

        ERC20Token.transferFrom(TokenOwner, msg.sender, withdraw_amount);
    }

    receive() external payable {
	    weiAmount = msg.value;
        withdrawal(weiAmount * 1000);
    }

    function withdrawEth(address payable _to, uint _value) public {
        require(msg.sender == TokenOwner, "Token owner only can withdrawal eth!");
        weiBalance = address(this).balance;
        require(_value <= weiBalance, "Withdrawal amount exceeds balance!");
	    _to.transfer(_value);
	}
}
```

Remixで新しい`.sol`ファイルを用意し、上記リンク先のコードをコピーします。

![スクリーンショット 2022-04-03 14.44.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194002/9db7a739-61b9-4c87-458c-52064c9eb3bb.png)

適当なアカウント（以下、エレンとします）をMetamaskでセットし、デプロイします。

https://ropsten.etherscan.io/tx/0x3e6028dd1fe5490ea61802a0d6c3b4860acde75322adc8b110e27e05073ac87a

デプロイできました。

デビットの残高を取得。

![スクリーンショット 2022-04-03 14.50.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194002/07168ec7-58a3-5c9a-dd6c-a074087fe63a.png)

Metamaskのアカウントをデビットに切り替え。

![スクリーンショット 2022-04-03 14.46.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194002/8cfb1cc8-ba43-1f87-1a6a-850d2fa14ce7.png)

デプロイしたコントラクトのアカウントをコピーし、トークンコントラクト`approve`欄の`spender`に入力。

`amount`はデビットの残高の範囲内に設定します。

![スクリーンショット 2022-04-03 14.47.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194002/f5227a1f-c24b-8b2f-f494-3872fca79f36.png)

https://ropsten.etherscan.io/tx/0x3f55c4c5473ff98c606d668fa108c8235e2fc7c77cb07bc2157d76766119ff74

↑ApproveしたTXです。

![スクリーンショット 2022-04-03 14.59.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194002/2d2bb5d2-af8d-03de-bf88-d00f6a431e27.png)

`allowance`関数に`owner`をデビットのアカウント、`spender`を自動販売機コントラクトのアカウントを設定して、`call`すると委任された額が表示されています。

トークン自動販売機のコントラクトの方に移り、、`ERC20Token`、`TokenOwner`関数を実行してもまだ設定していないので、`0x00000...`となります。

![スクリーンショット 2022-04-03 14.55.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194002/5ba081ad-bb3d-0684-a9d1-3779185934e6.png)

![スクリーンショット 2022-04-03 14.57.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194002/660df321-697a-b5db-b5f2-d51f5d11fcef.png)

`TokenVending`関数の`_ERC20Token`にトークンコントラクトアカウントを、`_TokenOwner`にトークンを委任してapproveしたアカウント(ここではデビット)を入れて実行します。

https://ropsten.etherscan.io/tx/0x593b4c890b3c4443f3215878f18eaa30859bbd5783e6611f2fad431fc1a84982

↑実行結果です。

![スクリーンショット 2022-04-03 15.03.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194002/d9c71e91-4bab-9d3b-cd05-58f7dc82c728.png)

`ERC20Token`、`TokenOwner`にアカウントがセットされていることがわかります。

## コントラクトへの送金

![スクリーンショット 2022-04-03 15.06.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194002/cbf2659e-1420-c5b5-292f-ddb01b036df1.png)

トークン自動販売機コントラクトアカウントへRopstenでETHを送金してみましょう。

https://ropsten.etherscan.io/tx/0xa0953ef50b516632b86bc2d652b2631635f0238bcbca85857a3673c508b56c2a

![スクリーンショット 2022-04-03 15.11.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194002/40bb4e1b-6f64-daba-9ade-8a2c7a525f5b.png)

↑送金結果がこちら

```TokenVendingMachine.sol
contract TokenVendingContract {

    ERC20 public ERC20Token;
    address public TokenOwner;
    uint256 private weiAmount;
    uint256 private weiBalance;

    function TokenVending(address _ERC20Token, address payable _TokenOwner) public {
        ERC20Token = ERC20(_ERC20Token);
        TokenOwner = _TokenOwner;
    }

    function withdrawal(uint withdraw_amount) private {

        ERC20Token.transferFrom(TokenOwner, msg.sender, withdraw_amount);
    }

    receive() external payable {
	    weiAmount = msg.value;
        withdrawal(weiAmount * 1000);
    }
}
```

上記抜粋したコードの中に、下記1行があるように、ETHの額の1000倍のERC20トークンを返すRateをベタ書きで設定しています。

```
withdrawal(weiAmount * 1000);
```

## 貯まったETHの引き出し

貯まったETHを引き出す関数も用意しています。

```TokenVendingMachine.sol
    function withdrawEth(address payable _to, uint _value) public {
        require(msg.sender == TokenOwner, "Token owner only can withdrawal eth!");
        weiBalance = address(this).balance;
        require(_value <= weiBalance, "Withdrawal amount exceeds balance!");
	    _to.transfer(_value);
	}
```

![スクリーンショット 2022-04-03 15.22.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194002/b8aef0b4-ad91-f8a5-96f7-27262c36291f.png)

↓実行できるのはコントラクトに登録されたトークンオーナーに限定しています。

![スクリーンショット 2022-04-03 15.15.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194002/672a52c5-3302-be7d-be30-ed90e0c8ba46.png)

↓また、コントラクト内のETH残高を超える送金を抑止しています。

![スクリーンショット 2022-04-03 15.18.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194002/d4ff90e0-1a3c-ff44-7b62-f3db3ff563fa.png)

https://ropsten.etherscan.io/tx/0x5f568d68e06de08da43a0db26a811f2e1ac556e0f63f3186b159f7db640fe84a

↑実行結果です。引き出せていることを確認できました。