Skip to content

Commit

Permalink
Merge pull request #778 from thurendous/translating-lesson-33
Browse files Browse the repository at this point in the history
translated lesson 33 into ja
  • Loading branch information
AmazingAng authored Sep 19, 2024
2 parents fbf6b19 + 0d368da commit 6ce6c25
Show file tree
Hide file tree
Showing 9 changed files with 306 additions and 0 deletions.
121 changes: 121 additions & 0 deletions Languages/ja/33_Airdrop_ja/Airdrop.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// SPDX-License-Identifier: MIT
// By 0xAA
pragma solidity ^0.8.21;

import "./IERC20.sol"; //import IERC20

/// @notice 複数のアドレスに対してトークンをエアドロするコントラクト
contract Airdrop {
mapping(address => uint256) failTransferList;

/// @notice 複数のアドレスに対してERC20トークンをエアドロする。使用前に承認が必要
///
/// @param _token エアドロップするERC20トークンのアドレス
/// @param _addresses エアドロップするアドレスの配列
/// @param _amounts トークンの数量の配列(各アドレスにエアドロップするトークンの数量)
function multiTransferToken(address _token, address[] calldata _addresses, uint256[] calldata _amounts) external {
// チェック:_addressesと_amounts配列の長さが等しいこと
require(_addresses.length == _amounts.length, "Lengths of Addresses and Amounts NOT EQUAL");
IERC20 token = IERC20(_token); // IERC20コントラクトインスタンスを宣言
uint256 _amountSum = getSum(_amounts); // エアドロップするトークンの総量を計算
// チェック:承認されたトークン数 > エアドロップするトークンの総量
require(token.allowance(msg.sender, address(this)) > _amountSum, "Need Approve ERC20 token");

// forループを使用し、transferFrom関数でエアドロップを送信
for (uint256 i; i < _addresses.length; i++) {
token.transferFrom(msg.sender, _addresses[i], _amounts[i]);
}
}

/// 複数のアドレスにETHを送金する
function multiTransferETH(address payable[] calldata _addresses, uint256[] calldata _amounts) public payable {
// チェック:_addressesと_amounts配列の長さが等しいこと
require(_addresses.length == _amounts.length, "Lengths of Addresses and Amounts NOT EQUAL");
uint256 _amountSum = getSum(_amounts); // エアドロップするETHの総量を計算
// 送金されたETHがエアドロップの総量と等しいことをチェック
require(msg.value == _amountSum, "Transfer amount error");
// forループを使用し、call関数でETHを送信
for (uint256 i = 0; i < _addresses.length; i++) {
// コメントアウトされたコードにはDoS攻撃のリスクがあり、transferも推奨されない書き方です
// DoS攻撃については https://github.com/AmazingAng/WTF-Solidity/blob/main/S09_DoS/readme.md を参照してください
// _addresses[i].transfer(_amounts[i]);
(bool success,) = _addresses[i].call{value: _amounts[i]}("");
if (!success) {
failTransferList[_addresses[i]] = _amounts[i];
}
}
}

// エアドロップ失敗に対して能動的な操作の機会を提供
function withdrawFromFailList(address _to) public {
uint256 failAmount = failTransferList[msg.sender];
require(failAmount > 0, "You are not in failed list");
failTransferList[msg.sender] = 0;
(bool success,) = _to.call{value: failAmount}("");
require(success, "Fail withdraw");
}

// 配列の合計を計算する関数
function getSum(uint256[] calldata _arr) public pure returns (uint256 sum) {
for (uint256 i = 0; i < _arr.length; i++) {
sum = sum + _arr[i];
}
}
}

// ERC20トークンコントラクト
contract ERC20 is IERC20 {
mapping(address => uint256) public override balanceOf;

mapping(address => mapping(address => uint256)) public override allowance;

uint256 public override totalSupply; // トークンのトータルサプライ(総供給量)

string public name; // 名前
string public symbol; // シンボル

uint8 public decimals = 18; // 小数点以下の桁数

constructor(string memory name_, string memory symbol_) {
name = name_;
symbol = symbol_;
}

// @dev `transfer`関数を実装、トークン転送ロジック
function transfer(address recipient, uint256 amount) public override returns (bool) {
balanceOf[msg.sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(msg.sender, recipient, amount);
return true;
}

// @dev `approve`関数を実装、トークン承認ロジック
function approve(address spender, uint256 amount) public override returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}

// @dev `transferFrom`関数を実装、トークン承認転送ロジック
function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
allowance[sender][msg.sender] -= amount;
balanceOf[sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(sender, recipient, amount);
return true;
}

// @dev トークンをミントする。`0`アドレスから呼び出し元アドレスに転送
function mint(uint256 amount) external {
balanceOf[msg.sender] += amount;
totalSupply += amount;
emit Transfer(address(0), msg.sender, amount);
}

// @dev トークンをバーン。呼び出し元アドレスから`0`アドレスに転送
function burn(uint256 amount) external {
balanceOf[msg.sender] -= amount;
totalSupply -= amount;
emit Transfer(msg.sender, address(0), amount);
}
}
63 changes: 63 additions & 0 deletions Languages/ja/33_Airdrop_ja/IERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: MIT
// WTF Solidity by 0xAA

pragma solidity ^0.8.21;

/**
* @dev ERC20 インターフェース契約.
*/
interface IERC20 {
/**
* @dev 発行条件:`value` 単位の通貨がアカウント (`from`) から別のアカウント (`to`) に転送されたとき.
*/
event Transfer(address indexed from, address indexed to, uint256 value);

/**
* @dev 発行条件:`value` 単位の通貨がアカウント (`owner`) から別のアカウント (`spender`) に承認されたとき.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);

/**
* @dev トークンの総供給量を返す.
*/
function totalSupply() external view returns (uint256);

/**
* @dev アカウント`account`が保有するトークン数を返す.
*/
function balanceOf(address account) external view returns (uint256);

/**
* @dev 呼び出し元のアカウントから別のアカウント `to` に `amount` 単位のトークンを転送する.
*
* 成功した場合は `true` を返す.
*
* {Transfer} イベントを発行する.
*/
function transfer(address to, uint256 amount) external returns (bool);

/**
* @dev `owner`アカウントが`spender`アカウントに承認した額を返す。デフォルトは0.
*
* {approve} または {transferFrom} が呼び出されると、`allowance`は変更される.
*/
function allowance(address owner, address spender) external view returns (uint256);

/**
* @dev 呼び出し元のアカウントが`spender`アカウントに `amount` 数量のトークンを承認する.
*
* 成功した場合は `true` を返す.
*
* {Approval} イベントを発行する.
*/
function approve(address spender, uint256 amount) external returns (bool);

/**
* @dev 承認メカニズムを通じて、`from`アカウントから`to`アカウントに`amount`数量のトークンを転送する。転送された部分は呼び出し元の`allowance`から差し引かれる.
*
* 成功した場合は `true` を返す.
*
* {Transfer} イベントを発行する.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
Binary file added Languages/ja/33_Airdrop_ja/img/33-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Languages/ja/33_Airdrop_ja/img/33-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Languages/ja/33_Airdrop_ja/img/33-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Languages/ja/33_Airdrop_ja/img/33-4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Languages/ja/33_Airdrop_ja/img/33-5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Languages/ja/33_Airdrop_ja/img/33-6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
122 changes: 122 additions & 0 deletions Languages/ja/33_Airdrop_ja/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
---
title: 33. エアドロコントラクト
tags:
- solidity
- application
- wtfacademy
- ERC20
- airdrop
---

# WTF Solidity 超シンプル入門: 33. エアドロップのコントラクト

最近、Solidity の学習を再開し、詳細を確認しながら「Solidity 超シンプル入門」を作っています。これは初心者向けのガイドで、プログラミングの達人向けの教材ではありません。毎週 1〜3 レッスンのペースで更新していきます。

僕のツイッター:[@0xAA_Science](https://twitter.com/0xAA_Science)[@WTFAcademy\_](https://twitter.com/WTFAcademy_)

コミュニティ:[Discord](https://discord.gg/5akcruXrsk)[Wechat](https://docs.google.com/forms/d/e/1FAIpQLSe4KGT8Sh6sJ7hedQRuIYirOoZK_85miz3dw7vA1-YjodgJ-A/viewform?usp=sf_link)[公式サイト wtf.academy](https://wtf.academy)

すべてのソースコードやレッスンは github にて公開: [github.com/AmazingAng/WTFSolidity](https://github.com/AmazingAng/WTFSolidity)

---

仮想通貨の世界で最も嬉しいことの一つは、エアドロップをもらうことです。タダでトークンを手に入れられるからです。このレッスンでは、スマートコントラクトを使用して`ERC20`トークンのエアドロップを送信する方法を学びます。

## エアドロップ Airdrop

エアドロップは仮想通貨界隈でのマーケティング戦略の一つで、プロジェクトチームが特定のユーザーグループに無料でトークンを配布します。エアドロップの資格を得るために、ユーザーは通常、製品のテスト、ニュースの共有、友人の紹介など、いくつかの簡単なタスクを完了する必要があります。プロジェクトチームはエアドロップを通じてシードユーザーを獲得し、ユーザーはトークン(価値)を得ることができるため、双方にとって利益があります。

エアドロップを受け取るユーザーが多いため、プロジェクトチームが一つ一つ取引を行うことは不可能です。スマートコントラクトを利用して`ERC20`トークンを一括で配布することで、エアドロップの効率を大幅に向上させることができます。

### エアドロップトークンコントラクト

`Airdrop`エアドロップコントラクトのロジックは非常にシンプルです:ループを使用して、1 回の取引で複数のアドレスに`ERC20`トークンを送信します。コントラクトには 2 つの関数が含まれています:

- `getSum()`関数:`uint`配列の合計を返します。

```solidity
// 配列の合計を計算する関数
function getSum(uint256[] calldata _arr) public pure returns(uint sum){
for(uint i = 0; i < _arr.length; i++)
sum = sum + _arr[i];
}
```

- `multiTransferToken()`関数:`ERC20`トークンのエアドロップを送信します。3 つのパラメータがあります:

- `_token`:トークンコントラクトアドレス(`address`型)
- `_addresses`:エアドロップを受け取るユーザーアドレスの配列(`address[]`型)
- `_amounts`:エアドロップ量の配列、`_addresses`の各アドレスに対応する量(`uint[]`型)

この関数には 2 つのチェックがあります:最初の`require``_addresses``_amounts`の 2 つの配列の長さが等しいかをチェックします。2 つ目の`require`はエアドロップコントラクトの承認額がエアドロップするトークンの総量よりも大きいかをチェックします。

```solidity
/// @notice 複数のアドレスにERC20トークンを転送します。使用前にapproveが必要です
///
/// @param _token 転送するERC20トークンのアドレス
/// @param _addresses エアドロップアドレスの配列
/// @param _amounts トークン量の配列(各アドレスのエアドロップ量)
function multiTransferToken(
address _token,
address[] calldata _addresses,
uint256[] calldata _amounts
) external {
// チェック:_addressesと_amountsの配列の長さが等しいこと
require(_addresses.length == _amounts.length, "Lengths of Addresses and Amounts NOT EQUAL");
IERC20 token = IERC20(_token); // IERCコントラクト変数を宣言
uint _amountSum = getSum(_amounts); // エアドロップするトークンの総量を計算
// チェック:承認されたトークン量 >= エアドロップするトークンの総量
require(token.allowance(msg.sender, address(this)) >= _amountSum, "Need Approve ERC20 token");
// forループを使用し、transferFrom関数でエアドロップを送信
for (uint8 i; i < _addresses.length; i++) {
token.transferFrom(msg.sender, _addresses[i], _amounts[i]);
}
}
```

- `multiTransferETH()`関数:`ETH`エアドロップを送信します。2 つのパラメータがあります:

- `_addresses`:エアドロップを受け取るユーザーアドレスの配列(`address[]`型)
- `_amounts`:エアドロップ量の配列、`_addresses`の各アドレスに対応する量(`uint[]`型)

```solidity
/// 複数のアドレスにETHを転送
function multiTransferETH(
address payable[] calldata _addresses,
uint256[] calldata _amounts
) public payable {
// チェック:_addressesと_amountsの配列の長さが等しいこと
require(_addresses.length == _amounts.length, "Lengths of Addresses and Amounts NOT EQUAL");
uint _amountSum = getSum(_amounts); // エアドロップするETHの総量を計算
// 送金されたETHがエアドロップの総量と等しいことをチェック
require(msg.value == _amountSum, "Transfer amount error");
// forループを使用し、call関数でETHを送信
for (uint256 i = 0; i < _addresses.length; i++) {
// コメントアウトされたコードにはDoS攻撃のリスクがあり、transferも推奨されない書き方です
// DoS攻撃については https://github.com/AmazingAng/WTF-Solidity/blob/main/S09_DoS/readme.md を参照してください
// _addresses[i].transfer(_amounts[i]);
(bool success, ) = _addresses[i].call{value: _amounts[i]}("");
if (!success) {
failTransferList[_addresses[i]] = _amounts[i];
}
}
}
```

### エアドロップの実践

1. `ERC20`トークンコントラクトをデプロイし、自分に 10000 トークンをミントします。
![`ERC20`をデプロイ](./img/33-1.png)
2. `Airdrop`エアドロップコントラクトをデプロイします。
![ミント](./img/33-2.png)
3. `ERC20`トークンコントラクトの`approve()`関数を使用して、`Airdrop`エアドロップコントラクトに 10000 単位のトークンを承認します。
![`Airdrop`コントラクトをデプロイ](./img/33-3.png)
4. `Airdrop`コントラクトの`multiTransferToken()`関数を実行してエアドロップを行います。`_token``ERC20`トークンアドレスを入力し、`_addresses``_amounts`は以下のように入力します:
![`Airdrop`コントラクトを承認](./img/33-4.png)
5. `ERC20`トークンコントラクトの`balanceOf()`関数を使用して、トークンの残高を確認します。100 から 200 になっていることを確認でき、エアドロが成功していることがわかります。
![`Airdrop`コントラクトを承認](./img/33-5.png)

## まとめ

今回は、`Solidity`を使用して`ERC20`トークンのエアドロップを送信する方法を学びました。これにより、エアドロップの効率を大幅に向上させることができます。私が一番最初にもらったエアドロップップは`ENS`でした。あなたたちはどうですか。

0 comments on commit 6ce6c25

Please sign in to comment.