Skip to content

Commit

Permalink
Merge pull request #724 from thurendous/add-japanese-lessons-part2
Browse files Browse the repository at this point in the history
  • Loading branch information
AmazingAng authored May 27, 2024
2 parents 4ae4375 + 455d6e7 commit 795fff4
Show file tree
Hide file tree
Showing 16 changed files with 386 additions and 0 deletions.
33 changes: 33 additions & 0 deletions Languages/ja/19_Fallback_ja/Fallback.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

contract Fallback {
/* fallback() or receive()?
ETHを受け取る
|
msg.dataがemptyか?
/ \
はい いいえ
/ \
receive()あるか? fallback()
/ \
はい いいえ
/ \
/ \
receive() fallback()
*/

// eventを定義
event receivedCalled(address Sender, uint256 Value);
event fallbackCalled(address Sender, uint256 Value, bytes Data);

// ETHを受け取るときにイベントを発生
receive() external payable {
emit receivedCalled(msg.sender, msg.value);
}

// fallback
fallback() external payable {
emit fallbackCalled(msg.sender, msg.value, msg.data);
}
}
Binary file added Languages/ja/19_Fallback_ja/img/19-1.jpg
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/19_Fallback_ja/img/19-2.jpg
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/19_Fallback_ja/img/19-3.jpg
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/19_Fallback_ja/img/19-4.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
126 changes: 126 additions & 0 deletions Languages/ja/19_Fallback_ja/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
---
title: 19. ETHを受け取る
tags:
- solidity
- advanced
- wtfacademy
- receive
- fallback
---

# WTF Solidity 超シンプル入門: 19. ETH の受け取り receive と fallback

最近、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)

---

`Solidity`は 2 つの特別なコールバック関数、`receive()``fallback()`をサポートしており、主に 2 つのケースで使用されます:

1. ETH を受け取る
2. コントラクト内に存在しない関数呼び出しを処理する(プロキシコントラクト proxy contract)

注 ⚠️:Solidity 0.6.x 以前のバージョンでは、構文上 `fallback()` 関数のみが存在し、ユーザーから送信された ETH を受け取るときに呼び出され、呼び出される関数のシグネチャがマッチする関数が存在しない場合に呼び出されます。0.6 以降、`fallback()`関数は`receive()``fallback()`に分割されました。

今回のレッスンは主に ETH の受け取りについてです。

## ETH を受け取る関数 receive

`receive()`関数は、コントラクトが ETH を受け取るときに呼び出されます。コントラクトには 多くて 1 つの `receive()` 関数しか存在できません。宣言方法は通常の関数とは異なり、`function` キーワードは不要です。

```solidity
receive() external payable { ... }
```

`receive()` 関数は引数を持つことができず、戻り値もありません。ただし、`external``payable` を含めなければなりません。

コントラクトが`ETH`を受け取ると、`receive()`関数が呼び出されます。`receive()`関数は、`send``transfer`メソッドで`ETH`を送信するときに、`gas``2300`に制限されるため、複雑なロジックを実行すると`Out of Gas`エラーが発生する可能性があるため、複雑なロジックを実行することは避けるべきです。`call`を使用すると、`gas`をカスタマイズしてより複雑なロジックを実行できます。

私たちは`receive()`関数内で`event`を送信することができます。例えば:

```solidity
// イベントを定義
event Received(address Sender, uint Value);
// ETHを受け取るとイベントを放出
receive() external payable {
emit Received(msg.sender, msg.value);
}
```

悪意のコントラクトの場合、`receive()`関数(古いバージョンの場合は`fallback()`関数)に、`gas`を消費する悪意のあるコードや意図的に失敗するコードが埋め込まれていることがあります。これにより、返金や送金ロジックを含む一部のコントラクトが正常に動作しなくなる可能性があります。そのため、返金などのロジックを含むコントラクトを書く場合は、このような状況に注意する必要があります。

## フォールバック関数`fallback()`

`fallback()`関数は、呼び出される関数が存在しない場合に呼び出されます。ETH を受け取るために使用することもできますし、プロキシコントラクト`proxy contract`に使うこともできます。

`fallback()`関数は、宣言時に`function`キーワードを必要とせず、`external`で修飾され、通常は`payable`で修飾されます。ETH の受取に使えます。

```solidity
fallback() external payable { ... }
```

````solidity
私たちは`fallback()`関数を定義し、それがトリガーされると`fallbackCalled`イベントを発生させ、`msg.sender`、`msg.value`、`msg.data`を出力します:
```solidity
event fallbackCalled(address Sender, uint Value, bytes Data);
// fallback
fallback() external payable{
emit fallbackCalled(msg.sender, msg.value, msg.data);
}
````

## receive と fallback の違い

`receive``fallback`はどちらも`ETH`を受け取るために使用できますが、それらのトリガーされる規則は次のとおりです:

```text
fallback() or receive()?
ETHを受け取る
|
msg.dataがemptyか?
/ \
はい いいえ
/ \
receive()あるか? fallback()
/ \
はい いいえ
/ \
/ \
receive() fallback()
```

简单来说,合约接收`ETH`时,`msg.data`为空且存在`receive()`时,会触发`receive()``msg.data`不为空或不存在`receive()`时,会触发`fallback()`,此时`fallback()`必须为`payable`

`receive()``payable fallback()`均不存在的时候,向合约**直接**发送`ETH`将会报错(你仍可以通过带有`payable`的函数向合约发送`ETH`)。

## Remix 演示

1. まず、`Fallback.sol`をデプロイします。

2. "VALUE"欄に送信する金額(単位は Wei)を入力し、"Transact"をクリックします。

![19-1.jpg](img/19-1.jpg)

トランザクションが成功し、`receivedCalled`イベントが放出されました。

![19-2.jpg](img/19-2.jpg)

"VALUE"欄に送信する金額(単位は Wei)を入力し、"CALLDATA"欄に任意の`msg.data`を入力し、"Transact"をクリックします。

![19-3.jpg](img/19-3.jpg)

5. トランザクションが成功し、`fallbackCalled`イベントが放出されました。

![19-4.jpg](img/19-4.jpg)

## まとめ

今回は、`Solidity`にある二種類の特殊の関数について紹介しました。`receive()``fallback()`は、主に ETH の受け取りの処理やプロキシコントラクトに使用されます。
55 changes: 55 additions & 0 deletions Languages/ja/20_SendETH_ja/SendETH.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

// 3つの方法でETHを送金
// transfer: 2300 gas, revert
// send: 2300 gas, return bool
// call: all gas, return (bool, data)

error SendFailed(); // send関数の失敗error
error CallFailed(); // call関数の失敗error

contract SendETH {
// payableなコンストラクター、デプロイ時にETHを送金できる
constructor() payable {}
// receive()関数は、コントラクトにETHを送信するときに呼び出される
receive() external payable {}

// transfer関数でETHを送金
function transferETH(address payable _to, uint256 amount) external payable {
_to.transfer(amount);
}

// send関数でETHを送金
function sendETH(address payable _to, uint256 amount) external payable {
// send()関数が失敗した場合、返り値の処理をしてrevertさせて、errorを放出
bool success = _to.send(amount);
if (!success) {
revert SendFailed();
}
}

// call()関数でETHを送金
function callETH(address payable _to, uint256 amount) external payable {
// call関数の返り値を処理し、失敗した場合、revertしてerrorを放出
(bool success,) = _to.call{value: amount}("");
if (!success) {
revert CallFailed();
}
}
}

contract ReceiveETH {
// ETHを受け取るときにイベントを発生させ、amountとgasを記録
event Log(uint256 amount, uint256 gas);

// receive関数は、コントラクトにETHを送信するときに呼び出される
receive() external payable {
emit Log(msg.value, gasleft());
}

// この関数は、コントラクトのETH残高を返す
function getBalance() public view returns (uint256) {
return address(this).balance;
}
}
Binary file added Languages/ja/20_SendETH_ja/img/20-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/20_SendETH_ja/img/20-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/20_SendETH_ja/img/20-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/20_SendETH_ja/img/20-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/20_SendETH_ja/img/20-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/20_SendETH_ja/img/20-6.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/20_SendETH_ja/img/20-7.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/20_SendETH_ja/img/20-8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
172 changes: 172 additions & 0 deletions Languages/ja/20_SendETH_ja/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
---
title: 20. ETH 送金
tags:
- solidity
- advanced
- wtfacademy
- transfer/send/call
---

# WTF Solidity 超シンプル入門: 20. ETH の 送金

最近、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)

---

`Solidity`有三种方法向其他合约发送`ETH`,他们是:`transfer()``send()``call()`,其中`call()`是被鼓励的用法。

## ETH を受け取るコントラクト

我们先部署一个接收`ETH`合约`ReceiveETH``ReceiveETH`合约里有一个事件`Log`,记录收到的`ETH`数量和`gas`剩余。还有两个函数,一个是`receive()`函数,收到`ETH`被触发,并发送`Log`事件;另一个是查询合约`ETH`余额的`getBalance()`函数。
まず、私たちは ETH を受け取る用のコントラクトをデプロイします。
このコントラクトは以下となっています。

1. `ReceiveETH`コントラクトには、受け取った ETH の量と残りの gas を記録する`Log`イベントがあります。
2. また、2つの関数があります。
- `receive()`は、ETH を受け取ると呼び出され、`Log`イベントを放出します。
- もう一つの関数は、コントラクトの ETH 残高を取得する`getBalance()`です。

```solidity
contract ReceiveETH {
// ETHを受け取るイベントで、amountやgasを記録します
event Log(uint amount, uint gas);
// この関数はETHを受け取ると呼び出されます
receive() external payable{
emit Log(msg.value, gasleft());
}
// この関数はコントラクトのETH残高を返します
function getBalance() view public returns(uint) {
return address(this).balance;
}
}
```

`ReceiveETH`コントラクトをデプロイした後、`getBalance()`関数を実行すると、現在のコントラクトの ETH 残高が`0`であることがわかります。

![20-1](./img/20-1.png)

## ETH を送金するコントラクト

我们将实现三种方法向`ReceiveETH`合约发送`ETH`。首先,先在发送 ETH 合约`SendETH`中实现`payable``构造函数``receive()`,让我们能够在部署时和部署后向合约转账。
私たちは3つの方法を使って`ReceiveETH`コントラクトに ETH を送ります。まず、`SendETH`コントラクトをデプロイします。

```solidity
contract SendETH {
// コンストラクターです。
// payableを使ってデプロイ時にETHを送金できるようにします
constructor() payable{}
// receive関数、ETHを受け取ると呼び出されます
receive() external payable{}
}
```

### transfer

- 使い方は`受取アドレス.transfer(送るETHの量)`
- `transfer()``gas`の制限は`2300`で、送金には十分ですが、相手のコントラクトの`fallback()``receive()`関数には複雑なロジックを実装できません。
- もし`transfer()`が失敗すると、自動的に`revert`(ロールバック)します。

サンプルコードです。`_to`には`ReceiveETH`コントラクトのアドレスを入力し、`amount`には送金する`ETH`の量を入力します。

```solidity
// transfer関数を使ってETHを送る
function transferETH(address payable _to, uint256 amount) external payable{
_to.transfer(amount);
}
```

`SendEth`コントラクトをデプロイした後、`ReceiveETH`コントラクトに ETH を送ります。この時、`amount`は 10、`value`は 0、`amount`>`value`なので、送金は失敗して`revert`されます。

![20-2](./img/20-2.png)

ここでは、`amount`は 10,`value`は 10,`amount`<=`value`なので,送金が成功するでしょう。

![20-3](./img/20-3.png)

`ReceiveETH`コントラクトでは、`getBalance()`関数を実行すると、現在のコントラクトの ETH 残高が`10`であることがわかります。

![20-4](./img/20-4.png)

### send

- 使い方は`受取アドレス.send(送るETHの量)`
- `send()``gas`の制限は`2300`で、送金には十分ですが、相手のコントラクトの`fallback()``receive()`関数には複雑なロジックを実装できません。
- `send()`がもし失敗したら、`revert`されることはない。
- `send()`的返回值是`bool`,代表着转账成功或失败,需要额外代码处理一下。
- `send()`の返り値は`bool`で、送金が成功したあるいは失敗したを表します。送金が失敗した場合、処理するコードの追加が必要です。

サンプルコード:

```solidity
error SendFailed(); // sendでETHを送る際に失敗した場合のエラー
// send()関数を使ってETHを送ります
function sendETH(address payable _to, uint256 amount) external payable{
// send()でETHを送る時に、失敗した場合、revertしてerrorを放出します
bool success = _to.send(amount);
if(!success){
revert SendFailed();
}
}
```

`ReceiveETH`コントラクトに対して ETH を送ります。この時、`amount`は 10、`value`は 0、`amount`>`value`なので、送金は失敗して`revert`されます。

![20-5](./img/20-5.png)

ここでは、`amount`は 10,`value`は 10,`amount`<=`value`なので、送金は成功します。

![20-6](./img/20-6.png)

### call

- 使い方は`受取アドレス.call{value: 送るETHの量}("")`
- ` call()``gas`の制限がなく、相手のコントラクトの`fallback()``receive()`関数に複雑なロジックを実装できます。
- `call()`如果转账失败,不会`revert`
- `call()`がもし失敗したら、`revert`されることはない。
- `call()`の返り値は`(bool, bytes)`で、送金が成功したあるいは失敗したを表します。送金が失敗した場合、処理するコードの追加が必要です。

サンプルコード:

```solidity
error CallFailed(); // 用call发送ETH失败error
// call()関数を使ってETHを送ります
function callETH(address payable _to, uint256 amount) external payable{
// call()の返り値を処理し、失敗した場合、revertしてerrorを放出します
(bool success,) = _to.call{value: amount}("");
if(!success){
revert CallFailed();
}
}
```

`ReceiveETH`コントラクトに対して ETH を送ります。この時、`amount`は 10、`value`は 0、`amount`>`value`なので、送金は失敗して`revert`されます。

![20-7](./img/20-7.png)

ここでは、`amount`は 10,`value`は 10,`amount`<=`value`なので、送金は成功します。

![20-8](./img/20-8.png)

运行三种方法,可以看到,他们都可以成功地向`ReceiveETH`合约发送`ETH`
3つの方法を使って`ReceiveETH`コントラクトに ETH を送ります。

すべての方法で送金ができることがわかりましたね。

## まとめ

今回は、`Solidity`の3つの方法で`ETH`を送る方法を紹介しました:`transfer``send``call`

- `call``gas`の制限がなく、最も柔軟であり、一番推奨される方法です。
- `transfer``2300 gas`限制,但是发送失败会自动`revert`交易,是次优选择。
- `transfer``2300 gas`の制限があり、送金が失敗した場合、自動的に`revert`されるため、`call`に次ぐ選択肢です。
- `send``2300 gas`の制限があり、送金が失敗した場合、自動的に`revert`されないため、ほとんど使用されません。

0 comments on commit 795fff4

Please sign in to comment.