-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #724 from thurendous/add-japanese-lessons-part2
- Loading branch information
Showing
16 changed files
with
386 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 の受け取りの処理やプロキシコントラクトに使用されます。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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`されないため、ほとんど使用されません。 |