diff --git a/Languages/ja/05_DataStorage_ja/readme.md b/Languages/ja/05_DataStorage_ja/readme.md index be645a5ef..8248b1108 100644 --- a/Languages/ja/05_DataStorage_ja/readme.md +++ b/Languages/ja/05_DataStorage_ja/readme.md @@ -1,4 +1,5 @@ -# WTF Solidity 超シンプル入門: 5. Data Storage and Scope(データ保存とスコープ) +# WTF Solidity 超シンプル入門: 5. Data Storage and Scope +#                         (データ保存とスコープ) 最近、Solidity の学習を再開し、詳細を確認しながら「Solidity 超シンプル入門」を作っています。これは初心者向けのガイドで、プログラミングの達人向けの教材ではありません。毎週 1〜3 レッスンのペースで更新していきます。 diff --git a/Languages/ja/06_ArrayAndStruct_ja/readme.md b/Languages/ja/06_ArrayAndStruct_ja/readme.md index 474dcec9e..de56491ac 100644 --- a/Languages/ja/06_ArrayAndStruct_ja/readme.md +++ b/Languages/ja/06_ArrayAndStruct_ja/readme.md @@ -132,7 +132,7 @@ Solidityにおいて、`struct`というフォーマットによって新しい ``` -## Summary +## まとめ このレクチャーでは、Solidityにおける`array`と`struct`のベーシックな使用方法を紹介しました。次のレクチャーでは、Solidityのハッシュテーブルを紹介します。- `mapping` diff --git a/Languages/ja/07_Mapping_ja/readme.md b/Languages/ja/07_Mapping_ja/readme.md index 9850352d1..037b329d5 100644 --- a/Languages/ja/07_Mapping_ja/readme.md +++ b/Languages/ja/07_Mapping_ja/readme.md @@ -73,6 +73,6 @@ -## Summary +## まとめ この章では、Solidityの`mapping`型を紹介しました。ここまでで、一般的な変数型の全ての種類について学んできましたよ。 diff --git a/Languages/ja/08_InitialValue_ja/readme.md b/Languages/ja/08_InitialValue_ja/readme.md index 19ce218a3..27e738749 100644 --- a/Languages/ja/08_InitialValue_ja/readme.md +++ b/Languages/ja/08_InitialValue_ja/readme.md @@ -90,6 +90,6 @@ Solidityにおいて、宣言されているが代入されていない変数に ![](./img/8-2_ja.png) -## Summary +## まとめ この章では、Solidityにおける変数の初期値を紹介しました。変数が宣言されたにも拘らず代入されていない場合には、その値はデフォルトで初期値に設定されており、そしてそれはその型で表される`0`に等しくなります。`delete`演算子は変数の値を初期値にリセット出来ます。 diff --git a/Languages/ja/09_Constant_ja/readme.md b/Languages/ja/09_Constant_ja/readme.md index 983677f99..f64dbb4fe 100644 --- a/Languages/ja/09_Constant_ja/readme.md +++ b/Languages/ja/09_Constant_ja/readme.md @@ -1,4 +1,5 @@ -# WTF Solidity 超シンプル入門: 9. Constant and Immutable (定数と不変) +# WTF Solidity 超シンプル入門: 9. Constant and Immutable +#                         (定数と不変) 最近、Solidity の学習を再開し、詳細を確認しながら「Solidity 超シンプル入門」を作っています。これは初心者向けのガイドで、プログラミングの達人向けの教材ではありません。毎週 1〜3 レッスンのペースで更新していきます。 @@ -74,7 +75,7 @@ ![9-3.png](./img/9-3.png) -## Summary +## まとめ この章では、Solidityにおいてその状態を編集することを制限する2つのキーワードを紹介しました: `constant`と`immutable`です。それらは変更されるべきではない変数を不変のままに維持します。コントラクトのセキュリティーを改善する一方で、`gas`を節約することに役立つでしょう。 diff --git a/Languages/ja/10_InsertionSort_ja/readme.md b/Languages/ja/10_InsertionSort_ja/readme.md index 7688ab7db..42736fe1f 100644 --- a/Languages/ja/10_InsertionSort_ja/readme.md +++ b/Languages/ja/10_InsertionSort_ja/readme.md @@ -170,6 +170,6 @@ Result: !["Input [2,5,3,1] Output[1,2,3,5]"](https://images.mirror-media.xyz/publication-images/S-i6rwCMeXoi8eNJ0fRdB.png?height=300&width=554) -## Summary +## まとめ このレクチャーでは、Solidityにおける制御フローを紹介し、シンプルでありながらバグが発生しやすいソートアルゴリズムを書きました。Solidityはシンプルに見えますが、沢山の罠を抱えています。毎月、スマートコントラクトにある小さなバグが故に、プロジェクトはハッキングされて、何百万ドルもの損失を生んでしまいます。安全なコントラクトを書く為には、Solidityの基礎をマスターし、訓練し続ける必要があるのです。 diff --git a/Languages/ja/11_Modifier_ja/readme.md b/Languages/ja/11_Modifier_ja/readme.md index 10241719d..5b1ef675e 100644 --- a/Languages/ja/11_Modifier_ja/readme.md +++ b/Languages/ja/11_Modifier_ja/readme.md @@ -8,7 +8,8 @@ tags: - modifier --- -# WTF Solidity 超シンプル入門: 11. Constructor & Modifier(コンストラクターと修飾子) +# WTF Solidity 超シンプル入門: 11. Constructor & Modifier +#                         (コンストラクターと修飾子) 最近、Solidity の学習を再開し、詳細を確認しながら「Solidity 超シンプル入門」を作っています。これは初心者向けのガイドで、プログラミングの達人向けの教材ではありません。毎週 1〜3 レッスンのペースで更新していきます。 @@ -84,5 +85,5 @@ contract Parents { ![](img/11-4_ja.png) -## Summary +## まとめ このレクチャーでは、Solidityの`constructor`と`modifier`を紹介し、スマートコントラクトへのアクセスを制御する`Ownable`スマートコントラクトを書きました。 diff --git a/Languages/ja/12_Event_ja/Event.sol b/Languages/ja/12_Event_ja/Event.sol new file mode 100644 index 000000000..bc59d7b35 --- /dev/null +++ b/Languages/ja/12_Event_ja/Event.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; +contract Events { + // define _balances mapping variable to record number of tokens held at each address + //(各アドレスで保有されているトークン数を記録するmapping変数として_balanceを定義します) + mapping(address => uint256) public _balances; + + // define Transfer event to record transfer address, receiving address and transfer number of a transfer transfaction + //(転送を行うトランザクションの送信元アドレスや受信アドレス、転送数を記録する為のTransferイベントを定義します) + event Transfer(address indexed from, address indexed to, uint256 value); + + + // define _transfer function,execute transfer logic + //(_transfer関数を定義して、転送ロジックを実行する) + function _transfer( + address from, + address to, + uint256 amount + ) external { + + _balances[from] = 10000000; // give some initial tokens to transfer address(送信元アドレスにいくつかの初期トークンを付与します) + + _balances[from] -= amount; // "from" address minus the number of transfer("from"アドレスから転送数を減算します) + _balances[to] += amount; // "to" address adds the number of transfer("to"アドレスに転送数を加算します) + + // emit event + emit Transfer(from, to, amount); + } +} \ No newline at end of file diff --git a/Languages/ja/12_Event_ja/img/12-1_ja.png b/Languages/ja/12_Event_ja/img/12-1_ja.png new file mode 100644 index 000000000..e26c9fc2c Binary files /dev/null and b/Languages/ja/12_Event_ja/img/12-1_ja.png differ diff --git a/Languages/ja/12_Event_ja/img/12-2_ja.png b/Languages/ja/12_Event_ja/img/12-2_ja.png new file mode 100644 index 000000000..b49ab3ed8 Binary files /dev/null and b/Languages/ja/12_Event_ja/img/12-2_ja.png differ diff --git a/Languages/ja/12_Event_ja/img/12-3_ja.png b/Languages/ja/12_Event_ja/img/12-3_ja.png new file mode 100644 index 000000000..6039eaeda Binary files /dev/null and b/Languages/ja/12_Event_ja/img/12-3_ja.png differ diff --git a/Languages/ja/12_Event_ja/readme.md b/Languages/ja/12_Event_ja/readme.md new file mode 100644 index 000000000..050a24996 --- /dev/null +++ b/Languages/ja/12_Event_ja/readme.md @@ -0,0 +1,106 @@ +--- +title: 12. Events(イベント) +tags: + - solidity + - basic + - wtfacademy + - event +--- + +# WTF Solidity 超シンプル入門: 12. Events(イベント) + +最近、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のトークンを例として使い、Solidityにおける`event`を紹介します。 + +## Events(イベント) +`solidity`におけるイベントは`EVM`(Ethereum Virtual Machine)に格納されるトランザクションログです。それらは関数が呼び出される際に発生し、コントラクトアドレスでアクセスできます。イベントは2つの性質を持っています : + +- 即応性がある: アプリケーション(例えば[`ether.js`](https://learnblockchain.cn/docs/ethers.js/api-contract.html#id18))は`RPC`インターフェースを通して、購読し(検知)し監視(追跡)することができ、フロントエンドで応答することができる。 +- 経済的である: イベントにデータを格納するのは安く、大体一度に2,000`gas`位掛かります。それに比べて、オンチェーンに新しい変数を格納するのは少なくとも20,000`gas`かかります。 + +### Declare events(イベントを宣言する) +イベントは`event`キーワードで宣言され、イベントの名前、そして記録されるそれぞれのパラメーターの型と名前が続きます。例として`ERC20`トークンのコントラクトから`Transfer`イベントを取り上げてみましょう。 +```solidity +event Transfer(address indexed from, address indexed to, uint256 value); +``` +`Transfer`イベントは3つのパラメーターを記録します: `from`と`to`、そして`value`です。それぞれ、それらはトークンが送信される元のアドレス、受信アドレス、そして送金されるトークンの数に相当しています。パラメーターの`from`と`to`は`indexed`キーワードで印が付けられており、`topics`として知られている特別なデータ構造に格納され、プログラムによって容易に照会されます。 + +### Emit events(イベントを発生させる) + +関数の中でイベントを発生させることが出来ます。次に続く例において、`_transfer()`関数が呼び出される度に、`Transfer`イベントは発生させられ、対応するパラメーターが記録されます。 +```solidity + // define _transfer function, execute transfer logic(_transfer関数を定義し、転送ロジックを実行する) + function _transfer( + address from, + address to, + uint256 amount + ) external { + + _balances[from] = 10000000; // give some initial tokens to transfer address(転送元アドレスに幾らかの初期トークンを付与する) + + _balances[from] -= amount; // "from" address minus the number of transfer("from"アドレスは転送させる数を減算する) + _balances[to] += amount; // "to" address adds the number of transfer("to"アドレスは転送させる数を加算する) + + // emit event(イベントを発生させる) + emit Transfer(from, to, amount); + } +``` + +## EVM Log + +EVMはSolidityのイベントを保管する為に`Log`を使用します。各ログは2つの部品を含んでいます: `topics`と`data`です。 + +![](img/12-3.png) + +### `Topics` + +`Topics`はイベントを記述する為に使用されます。各イベントは最大で4つのtopicsを含めることが可能です。典型的に、最初の`topic`はイベントハッシュです: イベントシグネチャのハッシュです。`Transfer`イベントのイベントハッシュは次のように計算されます: + +```solidity +keccak256("Transfer(addrses,address,uint256)") + +//0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef +``` + +イベントハッシュに加えて、`topics`は最大で3つのindex`indexed`パラメーターを含めることが可能です。例えば、`Transfer`イベントにおける`from`と`to`パラメーターのようなものです。匿名イベント(イベントの記述に"anonymous"を付与)は特別です: イベント名を持ちませんし、最大で4つの`indexed`パラメーターを持つことが出来ます。 + +`indexed`パラメーターはイベントの索引付けされた"key"として理解することが出来ます。そしてそれは容易にプログラムによって照会されることが出来ます。各`indexed`パラメーターのサイズは32バイトです。`array`や`string`のような32バイトよりも大きなパラメーターに関しては、基礎となるデータのハッシュが格納されます。(非基本型(例:配列や構造体)の場合、そのデータ自体ではなく、そのデータのKeccak-256ハッシュが格納される) + +### `Data` + +索引付けされていないパラメーターはログの`data`セクションに格納されます。それらはイベントの"value"として解釈され、直接取得することは出来ません。しかしそれらはより大きいサイズのデータを格納することが出来ます。それゆえに、`data`セクションは、`array`や`string`と言った複雑なデータ構造を格納する為に使用されます。これらのデータは256ビットを超えるため、イベントのtopics部分に格納される場合でも、ハッシュ化された形で保存されます。その上、`data`は`topic`に較べてより少ないガスを消費します。 + +## Remix Demo +`Event.sol`コントラクトを例として見てみましょう。 + +1. `Event`コントラクトをデプロイします。 + +2. `Transfer`イベントを発生させる為に、`_transfer`関数を呼び出します。 + +![](./img/12-1_ja.png) + +3. 発生させられたイベントをチェックする為に、トランザクションの詳細情報を確認します。 + +![](./img/12-2_ja.png) + +### Etherscanでイベントを照会する + +イーサスキャン(Etherscan)はイーサリアムのブロックチェーン上にあるトランザクションやスマートコントラクト、それ以上のことを見ることが出来るブロックエクスプローラーです。先ず、私はRinkebyやGoerliなどのイーサリアムテストネットにコントラクトをデプロイしました。次に、私は100トークン送金する為に`_transfer`関数を呼び出しました。その後で、トランザクションの詳細情報を`etherscan`で確認することが出来ます:[URL](https://rinkeby.etherscan.io/tx/0x8cf87215b23055896d93004112bbd8ab754f081b4491cb48c37592ca8f8a36c7) + +イベントの詳細情報を確認する為に`Logs`ボタンをクリックします: + +![details of event](./img/12-3_ja.png) + +`Topics`には3つの要素があります: `[0]`はイベントのハッシュであり、`[1]`と`[2]`は`Transfer`イベントで定義されている`indexed`パラメーターです(即ち、`from`と`to`です)。`Data`にある要素は索引付けされていないパラメーター`amount`です。 + +## まとめ +このレクチャーでは、`Solidity`におけるイベントを使用し、照会する方法について紹介しました。`Dune Analytics`のような、多くのオンチェーンの分析ツールは、solidityのイベントに基いています。 diff --git a/Languages/ja/13_Inheritance_ja/DiamondInheritance.sol b/Languages/ja/13_Inheritance_ja/DiamondInheritance.sol new file mode 100644 index 000000000..5eaa1fad2 --- /dev/null +++ b/Languages/ja/13_Inheritance_ja/DiamondInheritance.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +/* Inheritance tree(継承ツリー): + God + / \ +Adam Eve + \ / +people +*/ + +contract God { + event Log(string message); + + function foo() public virtual { + emit Log("God.foo called"); + } + + function bar() public virtual { + emit Log("God.bar called"); + } +} + +contract Adam is God { + function foo() public virtual override { + emit Log("Adam.foo called"); + } + + function bar() public virtual override { + emit Log("Adam.bar called"); + super.bar(); + } +} + +contract Eve is God { + function foo() public virtual override { + emit Log("Eve.foo called"); + } + + function bar() public virtual override { + emit Log("Eve.bar called"); + super.bar(); + } +} + +contract people is Adam, Eve { + function foo() public override(Adam, Eve) { + super.foo(); + } + + function bar() public override(Adam, Eve) { + super.bar(); + } +} diff --git a/Languages/ja/13_Inheritance_ja/Inheritance.sol b/Languages/ja/13_Inheritance_ja/Inheritance.sol new file mode 100644 index 000000000..5408f5c6c --- /dev/null +++ b/Languages/ja/13_Inheritance_ja/Inheritance.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; + +// Inheritance contract(継承コントラクト) +contract Grandfather { + event Log(string msg); + + // Apply inheritance to the following 3 functions: hip(), pop(), Grandfather(),then log "Grandfather". + //(継承を次の3つの関数に対して適用します: hip()、pop()、Grandfather()、そしてログは"Grandfather"です。) + function hip() public virtual{ + emit Log("Grandfather"); + } + + function pop() public virtual{ + emit Log("Grandfather"); + } + + function grandfather() public virtual { + emit Log("Grandfather"); + } +} + +contract Father is Grandfather{ + // Apply inheritance to the following 2 functions: hip() and pop(), then change the log value to "Father". + //(継承を次の2つの関数に対して適用します: hip()、pop()、そしてログの値を"Father"に変更します。) + function hip() public virtual override{ + emit Log("Father"); + } + + function pop() public virtual override{ + emit Log("Father"); + } + + function father() public virtual{ + emit Log("Father"); + } +} + +contract Son is Grandfather, Father{ + // Apply inheritance to the following 2 functions: hip() and pop(), then change the log value to "Son". + //(継承を次の2つの関数に対して適用します: hip()、pop()、そしてログの値を"Son"に変更します。) + function hip() public virtual override(Grandfather, Father){ + emit Log("Son"); + } + + function pop() public virtual override(Grandfather, Father) { + emit Log("Son"); + } + + function callParent() public{ + Grandfather.pop(); + } + + function callParentSuper() public{ + super.pop(); + } +} + +// Applying inheritance to the constructor functions +//(コンストラクター関数に対して継承を適用します) +abstract contract A { + uint public a; + + constructor(uint _a) { + a = _a; + } +} + +contract B is A(1) { +} + +contract C is A { + constructor(uint _c) A(_c * _c) {} +} diff --git a/Languages/ja/13_Inheritance_ja/ModifierInheritance.sol b/Languages/ja/13_Inheritance_ja/ModifierInheritance.sol new file mode 100644 index 000000000..4f9ec5746 --- /dev/null +++ b/Languages/ja/13_Inheritance_ja/ModifierInheritance.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; + +contract Base1 { + modifier exactDividedBy2And3(uint _a) virtual { + require(_a % 2 == 0 && _a % 3 == 0); + _; + } +} + +contract Identifier is Base1 { + + // Calculate the value of a number divided by 2 and divided by 3, respectively, but the parameters passed in must be multiples of 2 and 3 + //(2で除算された数値と3で除算された数値をそれぞれ計算しますが、渡される引数は2と3の倍数でなければなりません) + function getExactDividedBy2And3(uint _dividend) public exactDividedBy2And3(_dividend) pure returns(uint, uint) { + return getExactDividedBy2And3WithoutModifier(_dividend); + } + + // Calculate the value of a number divided by 2 and divided by 3, respectively + //(2で除算された数値と3で除算された数値をそれぞれ計算します) + function getExactDividedBy2And3WithoutModifier(uint _dividend) public pure returns(uint, uint){ + uint div2 = _dividend / 2; + uint div3 = _dividend / 3; + return (div2, div3); + } + + + // Rewrite the modifier: when not rewriting, enter 9 to call getExactDividedBy2And3, it will be reverted because it cannot pass the check + // Delete the following three lines of comments and rewrite the modifier function. At this time, enter 9 to call getExactDividedBy2And3, and the call will be successful. + //(修飾子を書き換える: 書き換えない時には、getExactDividedBy2And3を呼び指す際に9を入れます。そうすればチェックを通らないので、リバーとされます。) + //(次の3行のコメントを消して、修飾子関数を書き換えてください。この時にはgetExactDividedBy2And3に9を代入すれば、呼び出しは成功するでしょう。) + + // modifier exactDividedBy2And3(uint _a) override { + // _; + // } +} + diff --git a/Languages/ja/13_Inheritance_ja/img/13-10_ja.png b/Languages/ja/13_Inheritance_ja/img/13-10_ja.png new file mode 100644 index 000000000..2010cd845 Binary files /dev/null and b/Languages/ja/13_Inheritance_ja/img/13-10_ja.png differ diff --git a/Languages/ja/13_Inheritance_ja/img/13-1_ja.png b/Languages/ja/13_Inheritance_ja/img/13-1_ja.png new file mode 100644 index 000000000..7fd0738cc Binary files /dev/null and b/Languages/ja/13_Inheritance_ja/img/13-1_ja.png differ diff --git a/Languages/ja/13_Inheritance_ja/img/13-2_ja.png b/Languages/ja/13_Inheritance_ja/img/13-2_ja.png new file mode 100644 index 000000000..dfb181cbe Binary files /dev/null and b/Languages/ja/13_Inheritance_ja/img/13-2_ja.png differ diff --git a/Languages/ja/13_Inheritance_ja/img/13-3_ja.png b/Languages/ja/13_Inheritance_ja/img/13-3_ja.png new file mode 100644 index 000000000..6528e4f39 Binary files /dev/null and b/Languages/ja/13_Inheritance_ja/img/13-3_ja.png differ diff --git a/Languages/ja/13_Inheritance_ja/img/13-4_ja.png b/Languages/ja/13_Inheritance_ja/img/13-4_ja.png new file mode 100644 index 000000000..2aa5e8e42 Binary files /dev/null and b/Languages/ja/13_Inheritance_ja/img/13-4_ja.png differ diff --git a/Languages/ja/13_Inheritance_ja/img/13-5_ja.png b/Languages/ja/13_Inheritance_ja/img/13-5_ja.png new file mode 100644 index 000000000..bbba3a540 Binary files /dev/null and b/Languages/ja/13_Inheritance_ja/img/13-5_ja.png differ diff --git a/Languages/ja/13_Inheritance_ja/img/13-6_ja.png b/Languages/ja/13_Inheritance_ja/img/13-6_ja.png new file mode 100644 index 000000000..e22c9ad88 Binary files /dev/null and b/Languages/ja/13_Inheritance_ja/img/13-6_ja.png differ diff --git a/Languages/ja/13_Inheritance_ja/img/13-7_ja.png b/Languages/ja/13_Inheritance_ja/img/13-7_ja.png new file mode 100644 index 000000000..04efebd01 Binary files /dev/null and b/Languages/ja/13_Inheritance_ja/img/13-7_ja.png differ diff --git a/Languages/ja/13_Inheritance_ja/img/13-8_ja.png b/Languages/ja/13_Inheritance_ja/img/13-8_ja.png new file mode 100644 index 000000000..6de1c16db Binary files /dev/null and b/Languages/ja/13_Inheritance_ja/img/13-8_ja.png differ diff --git a/Languages/ja/13_Inheritance_ja/img/13-9_ja.png b/Languages/ja/13_Inheritance_ja/img/13-9_ja.png new file mode 100644 index 000000000..a34ac9dc3 Binary files /dev/null and b/Languages/ja/13_Inheritance_ja/img/13-9_ja.png differ diff --git a/Languages/ja/13_Inheritance_ja/readme.md b/Languages/ja/13_Inheritance_ja/readme.md new file mode 100644 index 000000000..e95b77f64 --- /dev/null +++ b/Languages/ja/13_Inheritance_ja/readme.md @@ -0,0 +1,302 @@ +--- +title: 13. Inheritance(インターフェース) +tags: + - solidity + - basic + - wtfacademy + - inheritance +--- + +# WTF Solidity 超シンプル入門: 13. Inheritance(インターフェース) + +最近、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の`inheritance`を紹介します。 + +## Inheritance(継承) +継承はオブジェクト指向プログラミングの核となるコンセプトの一つであり、コードの冗長性を大幅に削減することができます。属性とメソッドのセットを共有するクラスの階層によって、他のクラスからあるクラスを派生させることが出来る仕組みです。Solidityでは、スマートコントラクトは継承をサポートするオブジェクトとして見做すことが出来ます。 + +### Rules(決まり) + +Solidityでは、継承の為に2つの重要なキーワードがあります: + +- `virtual`: もし、親コントラクトにある関数がその子コントラクトでオーバーライドされることが予期されるのであれば、その関数は`virtual`として宣言されるべきである。 + +- `override`:もし、子コントラクトにある関数がその親コントラクトにある関数をオーバーライドするのであれば、`override`として宣言されるべきである。 + +**注記 1**: もし、関数が他の関数をオーバーライドし、またオーバーライドされることが予期される場合、`virtual override`としてラベル付けされるべきである。 + +**注記 2**: もし、`public`の状態変数が`override`としてラベル付けされている場合、その`getter`関数はオーバーライドされるでしょう。例えば次の通りです: + +```solidity +mapping(address => uint256) public override balanceOf; +``` + +### Simple inheritance(単純継承) + +1つの`Log`イベントと3つの関数を含む1つの単純な`Grandfather`コントラクトを書くことで始めてみましょう:string型の`"Grandfather"`を出力する`hip()`と`pop()`、そして`Grandfather()`です。 + +```solidity +contract Grandfather { + event Log(string msg); + + // Apply inheritance to the following 3 functions: hip(), pop(), Grandfather(),then log "Grandfather". + //(継承を次の3つの関数に対して適用します: hip()、pop()、Grandfather()、そしてログは"Grandfather"です。) + function hip() public virtual{ + emit Log("Grandfather"); + } + + function pop() public virtual{ + emit Log("Grandfather"); + } + + function Grandfather() public virtual { + emit Log("Grandfather"); + } +} +``` + +`Grandfather`コントラクトを継承する`Father`と呼ばれるもう1つのコントラクトを定義しましょう。継承の構文は`contract Father is Grandfather`であり、とても直感的です。`Father`コントラクトでは、`override`キーワードを用いて関数`hip()`と`pop()`を書き直します。その結果、それらの出力を`"Father"`へと変更します。string型の`"Father"`を出力する`father`と呼ばれる新しい関数も追加します。 + + +```solidity +contract Father is Grandfather{ + // Apply inheritance to the following 2 functions: hip() and pop(), then change the log value to "Father". + //(継承を次の2つの関数に対して適用します: hip()、pop()、そしてログの値を"Father"に変更します。) + function hip() public virtual override{ + emit Log("Father"); + } + + function pop() public virtual override{ + emit Log("Father"); + } + + function father() public virtual{ + emit Log("Father"); + } +} +``` + +コントラクトをデプロイした後で、`Father`コントラクトが4つの関数を含んでいることが分かります。継承された`grandfather()`関数の出力が`"Grandfather"`のままである一方で、`hip()`と`pop()`の出力は`"Father"`の出力で上手く書き換えられています。 + + +### 多重継承 + +Solidityのコントラクトは複数のコントラクトを継承することが出来ます。ルールは次の通りです: + +1. 多重継承の場合、親コントラクトは先行度の高いものから低いものへと順序付けられるべきです。例えば: `contract Son is Grandfather, Father`。順序が不正確の場合、エラーが出ます。 + +2. もし、多重の親コントラクトに関数が存在している場合、子コントラクトでオーバーライドされるべきです。さもなければ、エラーが発生してしまいます。 + +3. 多重の親コントラクトに関数が存在している時、`override`キーワードの後に全ての親コントラクトの名前を付加する必要があります。例えば: `override(Grandfather, Father)`。 + +Example: +```solidity +contract Son is Grandfather, Father{ + // Apply inheritance to the following 2 functions: hip() and pop(), then change the log value to "Son". + //(継承を次の2つの関数に対して適用します: hip()、pop()、そしてログの値を"Son"に変更します。) + function hip() public virtual override(Grandfather, Father){ + emit Log("Son"); + } + + function pop() public virtual override(Grandfather, Father) { + emit Log("Son"); + } +``` + +コントラクトをデプロイした後、`hip()`と`pop()`関数の出力が`"Son"`に変わっており、`Son`コントラクトのそれらの関数を上手く書き換えられていることが分かります。ところが一方で、その親コントラクトから継承された`Grandfather()`と`father()`関数は変更されないままです。 + +### 修飾子の継承 + +同じように、Solidityの修飾子もまた継承することが出来ます。`virtual`と`override`キーワードを使用しますので、修飾子の継承ルールは関数の継承に似ています。 + +```solidity +contract Base1 { + modifier exactDividedBy2And3(uint _a) virtual { + require(_a % 2 == 0 && _a % 3 == 0); + _; + } +} + +contract Identifier is Base1 { + // Calculate _dividend/2 and _dividend/3, but the _dividend must be a multiple of 2 and 3 + //(_dividend/2と_dividend/3を計算しますが、_dividendは2と3の倍数でなければなりません) + function getExactDividedBy2And3(uint _dividend) public exactDividedBy2And3(_dividend) pure returns(uint, uint) { + return getExactDividedBy2And3WithoutModifier(_dividend); + } + + // Calculate _dividend/2 and _dividend/3 + //(_dividend/2と_dividend/3を計算します) + function getExactDividedBy2And3WithoutModifier(uint _dividend) public pure returns(uint, uint){ + uint div2 = _dividend / 2; + uint div3 = _dividend / 3; + return (div2, div3); + } +} +``` + +`Base1`コントラクトを継承しているので、`Identifier`コントラクトは`exactDividedBy2And3`修飾子を直接使用することが出来ます。コントラクト内で修飾子を書き換えることも出来ます: + +```solidity + modifier exactDividedBy2And3(uint _a) override { + _; + require(_a % 2 == 0 && _a % 3 == 0); + } +``` + +### コンストラクターの継承 + +コンストラクターもまた継承されます。まず初めに、状態変数`a`を持つ親コントラクト`A`について考えてみましょう。そのコンストラクターにおいて初期化されています: + +```solidity +// Applying inheritance to the constructor functions +//(コンストラクター関数に対して継承を適用します) +abstract contract A { + uint public a; + + constructor(uint _a) { + a = _a; + } +} +``` + +子コントラクトにはその親コントラクト`A`からコンストラクターを継承する2つの方法があります。 +1. 継承時に親コンストラクターのパラメーターを宣言する: + + ```solidity + contract B is A(1){} + ``` + +2. 子コントラクタのコンストラクターにおいて親コンストラクターのパラメーターを宣言する: + + ```solidity + contract C is A { + constructor(uint _c) A(_c * _c) {} + } + ``` + +### 親コントラクトから関数を呼び出す + +子コントラクトには、親コントラクトの関数を呼び出す2つの方法があります。 + +1. 直接呼び出す:子コントラクトは`parentContractName.functionName()`によって直接親コントラクトの関数を呼び出すことが出来ます。例えば: + + ```solidity + function callParent() public{ + Grandfather.pop(); + } + ``` + +2. `super`キーワード: 子コントラクトは継承ヒエラルキーで最も近い親コントラクトの関数を呼び出すために`super.functionName()`を用いることが出来ます。Solidityの継承は右から左へという形で宣言されます: `contract Son is Grandfather, Father`という記述方法の場合、`Father`コントラクトは`Grandfather`コントラクトよりも近いです。このようにして、`Son`コントラクトにおける`super.pop()`は`GrandFather.pop()`ではなくて、`Father.pop()`を呼び出すことが出来ます。 + + ```solidity + function callParentSuper() public{ + // call the function one level higher up in the inheritance hierarchy + //(継承ヒエラルキーにおいて1階層上の関数を呼び出します) + super.pop(); + } + ``` + +### ダイアモンド継承 + +オブジェクト指向のプログラミングにおいて、ダイアモンド継承は派生クラスが2つかそれ以上の基底クラスを所有しているというシナリオを指します。 + +ダイアモンド継承チェーンにおいて`super`キーワードを使用する時、ただ単に最も近い親コントラクトだけではなくて、継承チェーンにある各コントラクトの関連性のある関数を呼び出すということに注意しなければなりません。 + +最初に、`God`と呼ばれる基底コントラクトを書きます。それから`God`コントラクトから継承している`Adam`と`Eve`という2つのコントラクトを書きます。最後に、`Adam`と`Eve`から継承しているもう一つのコントラクト`people`を書きます。それぞれのコントラクトは、`foo()`と`bar()`という2つの関数を持っています: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +/* Inheritance tree visualized(可視化された継承ツリー): + God + / \ +Adam Eve + \ / +people +*/ +contract God { + event Log(string message); + function foo() public virtual { + emit Log("God.foo called"); + } + function bar() public virtual { + emit Log("God.bar called"); + } +} +contract Adam is God { + function foo() public virtual override { + emit Log("Adam.foo called"); + Adam.foo(); + } + function bar() public virtual override { + emit Log("Adam.bar called"); + super.bar(); + } +} +contract Eve is God { + function foo() public virtual override { + emit Log("Eve.foo called"); + Eve.foo(); + } + function bar() public virtual override { + emit Log("Eve.bar called"); + super.bar(); + } +} +contract people is Adam, Eve { + function foo() public override(Adam, Eve) { + super.foo(); + } + function bar() public override(Adam, Eve) { + super.bar(); + } +} +``` + +この例では、`people`コントラクト内で`super.bar()`関数を呼ぶことは`Eve`そして`Adam`、`God`コントラクトの`bar()`関数を呼び出すことになります。そしてそれは普通の多重継承とは異なります。 + +`Eve`と`Adam`はどちらも`God`親コントラクトの子コントラクトですが、`God`コントラクトは全体のプロセスにおいて一度だけしか呼ばれません。これは何故ならば、SolidityがPythonからパラダイムを借りているからであり、基底クラスで構成されるDAG(有効非巡回グラフ)にC3 Linearizationに基づく特定の順序を保証させる為です。継承と線形化についてのより多くの情報が欲しければ、次のオフィシャルを読んでください[Solidity docs here](https://docs.soliditylang.org/en/v0.8.17/contracts.html#multiple-inheritance-and-linearization)。 + +## Verify on Remix(Remixによる検証) +1. 単純継承セッションにおける例となるコントラクトをデプロイした後、`Father`コントラクトが`Grandfather`関数を持っていることが分かります: + + ![13-1](./img/13-1_ja.png) + + ![13-2](./img/13-2_ja.png) + +2. 修飾子の継承の例: + + ![13-3](./img/13-3_ja.png) + + ![13-4](./img/13-4_ja.png) + + ![13-5](./img/13-5_ja.png) + +3. コンストラクターの継承: + + ![13-6](./img/13-6_ja.png) + + ![13-7](./img/13-7_ja.png) + +4. 親コントラクトから関数を呼ぶ: + + ![13-8](./img/13-8_ja.png) + + ![13-9](./img/13-9_ja.png) + +5. ダイアモンド継承: + + ![13-10](./img/13-10_ja.png) + +## まとめ +このチュートリアルでは、単純継承や多重継承、修飾子とコンストラクターの継承と言ったSolidityにおける継承の基礎と親コントラクトから関数の呼び出しについて紹介しました。 diff --git a/Languages/ja/14_Interface_ja/AbstractDemo.sol b/Languages/ja/14_Interface_ja/AbstractDemo.sol new file mode 100644 index 000000000..98cda59c8 --- /dev/null +++ b/Languages/ja/14_Interface_ja/AbstractDemo.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; +abstract contract Base{ + string public name = "Base"; + function getAlias() public pure virtual returns(string memory); +} + +contract BaseImpl is Base{ + function getAlias() public pure override returns(string memory){ + return "BaseImpl"; + } +} diff --git a/Languages/ja/14_Interface_ja/Interface.sol b/Languages/ja/14_Interface_ja/Interface.sol new file mode 100644 index 000000000..1d8b53bf2 --- /dev/null +++ b/Languages/ja/14_Interface_ja/Interface.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; + +abstract contract InsertionSort{ + function insertionSort(uint[] memory a) public pure virtual returns(uint[] memory); +} + +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; + +interface IERC721 is IERC165 { + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + function balanceOf(address owner) external view returns (uint256 balance); + + function ownerOf(uint256 tokenId) external view returns (address owner); + + function safeTransferFrom(address from, address to, uint256 tokenId) external; + + function transferFrom(address from, address to, uint256 tokenId) external; + + function approve(address to, uint256 tokenId) external; + + function getApproved(uint256 tokenId) external view returns (address operator); + + function setApprovalForAll(address operator, bool _approved) external; + + function isApprovedForAll(address owner, address operator) external view returns (bool); + + function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; +} + +contract interactBAYC { + // Use BAYC address to create interface contract variables (ETH Mainnet) + //(インターフェースコントラクト変数を作成する為にBAYCアドレスを使用します(ETH Mainnet)) + IERC721 BAYC = IERC721(0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D); + + // Call BAYC's balanceOf() to query the open interest through the interface + //(インターフェースを通してopen interest"所有者の残高(保有数)"を照会する為にBAYCのbalanceOf()を呼び出します) + function balanceOfBAYC(address owner) external view returns (uint256 balance){ + return BAYC.balanceOf(owner); + } + + // Safe transfer by calling BAYC's safeTransferFrom() through the interface + //(インターフェースを通してBAYCのsafeTransferFrom()を呼び出すことによって安全な転送を実現します) + function safeTransferFromBAYC(address from, address to, uint256 tokenId) external{ + BAYC.safeTransferFrom(from, to, tokenId); + } +} diff --git a/Languages/ja/14_Interface_ja/InterfaceDemo.sol b/Languages/ja/14_Interface_ja/InterfaceDemo.sol new file mode 100644 index 000000000..5ce97d21d --- /dev/null +++ b/Languages/ja/14_Interface_ja/InterfaceDemo.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; +interface Base { + function getFirstName() external pure returns(string memory); + function getLastName() external pure returns(string memory); +} +contract BaseImpl is Base{ + function getFirstName() external pure override returns(string memory){ + return "Amazing"; + } + function getLastName() external pure override returns(string memory){ + return "Ang"; + } +} diff --git a/Languages/ja/14_Interface_ja/img/14-1_ja.png b/Languages/ja/14_Interface_ja/img/14-1_ja.png new file mode 100644 index 000000000..0e179e934 Binary files /dev/null and b/Languages/ja/14_Interface_ja/img/14-1_ja.png differ diff --git a/Languages/ja/14_Interface_ja/img/14-2_ja.png b/Languages/ja/14_Interface_ja/img/14-2_ja.png new file mode 100644 index 000000000..863c3d0b0 Binary files /dev/null and b/Languages/ja/14_Interface_ja/img/14-2_ja.png differ diff --git a/Languages/ja/14_Interface_ja/readme.md b/Languages/ja/14_Interface_ja/readme.md new file mode 100644 index 000000000..72d4d8658 --- /dev/null +++ b/Languages/ja/14_Interface_ja/readme.md @@ -0,0 +1,136 @@ +--- +title: 14. Abstract and Interface(抽象コントラクトとインターフェース) +tags: + - solidity + - basic + - wtfacademy + - abstract + - interface +--- + +# WTF Solidity 超シンプル入門: 14. Abstract and Interface +#                     (抽象コントラクトとインターフェース) + +最近、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) + +----- + +このセクションでは、例として`ERC721`のインターフェースを用いて、Solidityにおける`abstract`と`interface`のコントラクトを紹介します。コントラクトのテンプレートを書き、コードの冗長性を減らす為に使用されます。 + + +## Abstract contract(抽象コントラクト) + +もし、コントラクトが少なくとも一つの未実装関数(関数本体`{}`に何も内容が無い)を含む場合、`abstract`としてラベルをされなければなりません; さもなければそのコントラクトはコンパイルされません。付け加えると、未実装関数は`virtual`としてラベルされる必要があります。 +例として我々の以前の[Insertion Sort Contract](https://github.com/AmazingAng/WTFSolidity/tree/main/07_InsertionSort)を取ってみると、挿入ソート関数の実装方法が未だ分かっていないのならば、コントラクトに`abstract`として印をつけることが出来、将来的に他のコントラクトに上書きさせることが出来ます。 + +```solidity +abstract contract InsertionSort{ + function insertionSort(uint[] memory a) public pure virtual returns(uint[] memory); +} +``` + +## Interface(インターフェース) + +`interface`コントラクトは`abstract`コントラクトに類似していますが、関数を何も実装しないようにする必要があります。インターフェースのルールは次のようなものです: + +1. 状態変数を含むことが出来ません。 +2. コンストラクターを含むことが出来ません。 +3. インターフェースでないコントラクトを継承することが出来ません。 +4. 全ての関数はexternalでなければならず、関数本体に内容を持っていてはいけません。 +5. インターフェースのコントラクトを継承するコントラクトは継承したコントラクト内で定義されている関数全てを実装しなければなりません。 + +インターフェースは何も実際の機能を実装していませんが、スマートコントラクトの骨格となっています。インターフェースはコントラクトが何をするのか、また如何にしてそれらと相互作用するのか?ということを定義します: もしスマートコントラクトがインターフェース(`ERC20`や`ERC721`のような)を実装していれば、他のDappsとスマートコントラクトは如何にしてそれと相互作用するのか?ということを知ることが出来ます。なぜならば2つの重要な情報を提供するからです: + +1. コントラクト内の各関数の`bytes4`セレクターと関数シグネチャ`function name (parameter type)`。 +2. Interface id (次を見てください [EIP165](https://eips.ethereum.org/EIPS/eip-165) for more information) + +付け加えて、インターフェースは`ABI` (Application Binary Interface)コントラクトと等しく、お互いに変換されることが出来ます: インターフェースコントラクトをコンパイルすると`ABI`コントラクトが得られ、[abi-to-sol tool](https://gnidan.github.io/abi-to-sol/)を使用すると`ABI`をインターフェースコントラクトに変換し戻すことが出来ます。 + +例として、`ERC721`トークンスタンダードのインターフェースである`IERC721`コントラクトを見てみましょう。3つのイベントと9つの関数を持っており、全ての`ERC721`コントラクトは実装する必要があります。インターフェース内では、各関数は関数本体の`{ }`の代わりに`;`で終了します。それに加えて、全てのインターフェースコントラクト内の関数はデフォルトで`virtual`であり、従って明示的に`virtual`としてラベル付けする必要はありません。 + +```solidity +interface IERC721 is IERC165 { + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + function balanceOf(address owner) external view returns (uint256 balance); + + function ownerOf(uint256 tokenId) external view returns (address owner); + + function safeTransferFrom(address from, address to, uint256 tokenId) external; + + function transferFrom(address from, address to, uint256 tokenId) external; + + function approve(address to, uint256 tokenId) external; + + function getApproved(uint256 tokenId) external view returns (address operator); + + function setApprovalForAll(address operator, bool _approved) external; + + function isApprovedForAll(address owner, address operator) external view returns (bool); + + function safeTransferFrom( address from, address to, uint256 tokenId, bytes calldata data) external; +} +``` + +### IERC721 Event(IERC721のイベント) +`IERC721`は3つのイベントを持っています。 +- `Transfer` event: 転送の最中に発生させられるものであり、送信元アドレス`from`と受信アドレス`to`、そして`tokenid`を記録します。 +- `Approval` event: 承認の最中に発生させられるものであり、トークン所有者のアドレス`owner`と承認されるアドレス`approved`、そして`tokenid`を記録します。 +- `ApprovalForAll` event: バッチ承認の最中に発生させられるものであり、バッチ承認の所有者アドレス`owner`と承認されるアドレス`operator`、そして承認が有効か否かを示す`approved`を記録します。 + +### IERC721 Function(IERC721の関数) +- `balanceOf`: 所有者によって保有されている全てのNFTをカウントする。 +- `ownerOf`: NFTの所有者を見つける(`tokenId`)。 +- `transferFrom`: `tokenId`を持つNFTの所有権を`from`から`to`に移す。 +- `safeTransferFrom`: `tokenId`を持つNFTの所有権を`from`から`to`に移す。追加チェック: もし受信者がコントラクトアドレスの場合、`ERC721Receiver`インターフェースを実装している必要があります。 +- `approve`: 他のアドレスにNFTを管理できるようにしたり、管理できないようにしたりする。 +- `getApproved`: 単一のNFTに対する承認されたアドレスを取得する。 +- `setApprovalForAll`: このコントラクト内の全てのNFTを管理する権限をサードパーティーに対して有効化したり無効化したりする。 +- `isApprovedForAll`: あるアドレスが他のアドレスの認可された操作者であるかどうかを照会する。 +- `safeTransferFrom`: パラメーターに`data`を含む、安全な転送の為のオーバーロードされた関数。 +- + +### When to use an interface?(いつインターフェースを使用するのか?) +もし、`IERC721`インターフェースをコントラクトが実装していることが分かっているならば、詳細な実装を知ることなく、そのコントラクトと相互作用することが出来ます。 + +「The Bored Ape Yacht Club」`BAYC`は`ERC721`のNFTですが、`IERC721`インターフェース内にあるすべての関数を実装しています。そのソースコードを知ることなく、`IERC721`インターフェースとそのコントラクトアドレスを用いることによって`BAYC`コントラクトと相互作用することが出来ます。 +例えば、`BAYC`のアドレス残高を照会する為に`balanceOf()`を使用することが出来たり、`BAYC`NFTを転送する為に`safeTransferFrom()`を使用することが出来たりします。 + + +```solidity +contract interactBAYC { + // Use BAYC address to create interface contract variables (ETH Mainnet) + //(インターフェースコントラクト変数を作成する為にBAYCアドレスを使用します(ETH Mainnet)) + IERC721 BAYC = IERC721(0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D); + + // Call BAYC's balanceOf() to query the open interest through the interface + //(インターフェースを通してopen interest"所有者の残高(保有数)"を照会する為にBAYCのbalanceOf()を呼び出します) + function balanceOfBAYC(address owner) external view returns (uint256 balance){ + return BAYC.balanceOf(owner); + } + + // Safe transfer by calling BAYC's safeTransferFrom() through the interface + //(インターフェースを通してBAYCのsafeTransferFrom()を呼び出すことによって安全な転送を実現します) + function safeTransferFromBAYC(address from, address to, uint256 tokenId) external{ + BAYC.safeTransferFrom(from, to, tokenId); + } +} +``` + +## Remix demo +1. Abstract example: + ![14-1](./img/14-1_ja.png) +2. Interface example: + ![14-2](./img/14-2_ja.png) + +## まとめ +この章では、Solidityの`abstract`と`interface`コントラクトを紹介しましたが、それらはコントラクトテンプレートを記述し、ソースコードの冗長性を低減する為に使用されています。 +`ERC721`トークンスタンダードのインターフェース、そしてインターフェースを使用して`BAYC`コントラクトと相互作用する方法についても学びました。 diff --git a/Languages/ja/15_Errors_ja/Error.sol b/Languages/ja/15_Errors_ja/Error.sol new file mode 100644 index 000000000..62af0ae38 --- /dev/null +++ b/Languages/ja/15_Errors_ja/Error.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; + +// カスタムerror +error TransferNotOwner(); + +contract Errors{ + // A set of mappings that record the Owner of each TokenId + //(それぞれのトークンIDの所有者を記録するマッピングのセット) + mapping(uint256 => address) private _owners; + + // Error : gas cost 24445 + function transferOwner1(uint256 tokenId, address newOwner) public { + if(_owners[tokenId] != msg.sender){ + revert TransferNotOwner(); + } + _owners[tokenId] = newOwner; + } + + // require : gas cost 24743 + function transferOwner2(uint256 tokenId, address newOwner) public { + require(_owners[tokenId] == msg.sender, "Transfer Not Owner"); + _owners[tokenId] = newOwner; + } + + // assert : gas cost 24446 + function transferOwner3(uint256 tokenId, address newOwner) public { + assert(_owners[tokenId] == msg.sender); + _owners[tokenId] = newOwner; + } +} diff --git a/Languages/ja/15_Errors_ja/img/15-1_ja.png b/Languages/ja/15_Errors_ja/img/15-1_ja.png new file mode 100644 index 000000000..fc9a53a98 Binary files /dev/null and b/Languages/ja/15_Errors_ja/img/15-1_ja.png differ diff --git a/Languages/ja/15_Errors_ja/img/15-2_ja.png b/Languages/ja/15_Errors_ja/img/15-2_ja.png new file mode 100644 index 000000000..beb3fec3b Binary files /dev/null and b/Languages/ja/15_Errors_ja/img/15-2_ja.png differ diff --git a/Languages/ja/15_Errors_ja/img/15-3_ja.png b/Languages/ja/15_Errors_ja/img/15-3_ja.png new file mode 100644 index 000000000..5c56bd471 Binary files /dev/null and b/Languages/ja/15_Errors_ja/img/15-3_ja.png differ diff --git a/Languages/ja/15_Errors_ja/readme.md b/Languages/ja/15_Errors_ja/readme.md new file mode 100644 index 000000000..7fe14ea9a --- /dev/null +++ b/Languages/ja/15_Errors_ja/readme.md @@ -0,0 +1,113 @@ +--- +title: 15. Errors +tags: + - solidity + - advanced + - wtfacademy + - error + - revert/assert/require +--- + +# WTF Solidity 超シンプル入門: 15. Errors + +最近、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において例外をスローする3つの方法を紹介します: `error`、`require`、そして`assert`です。 + +## Errors +Solidityはエラーハンドリングの為の多くの関数を持っています。エラーはコンパイル時やランタイム(実行時)に発生します。 + +### Error +`error`ステートメントはsolidity`0.8`における新機能です。gasを節約し、ユーザーに何故操作が失敗したのか?を教えてくれます。solidityにおいてエラーをスローする推奨方法です。 +カスタムエラーはエラーステートメントを用いることによって定義され、コントラクトの内側と外側で使用することが出来ます。以下の通り、我々は`TransferNotOwner`エラーを作成します。そうすることで、転送時に呼び出し元がトークン`owner`でない時にエラーをスローすることが出来ます。 + +```solidity +error TransferNotOwner(); // custom error +``` + +関数に於いて、`error`は`revert`ステートメントと一緒に使用されなければなりません。 + +```solidity +function transferOwner1(uint256 tokenId, address newOwner) public { + if(_owners[tokenId] != msg.sender){ + revert TransferNotOwner(); + } + _owners[tokenId] = newOwner; +} +``` +`transferOwner1()`関数は呼び出し元がトークンの所有者かどうかをチェックします; もしそうでなければ、`TransferNotOwner`エラーをスローし、トランザクションをリバート(取り消し)します。 + +### Require +`require`ステートメントはsolidity`0.8`以前でエラーハンドリングにおいて最も一般的に使用されたメソッドです。開発者の間で未だにポピュラーです。 + +`require`の文法: +``` +require(condition, "error message"); +``` + +条件が満たされない場合、例外がスローされます。 + +その簡潔さにも関わらず、gasの消費量は`error`ステートメントよりも高いものとなっています: gas消費量はエラーメッセージが増えるに従って線形的に増えていきます。 + +それでは、`require`ステートメントで上記の`transferOwner`関数を書き換えてみましょう: +```solidity +function transferOwner2(uint256 tokenId, address newOwner) public { + require(_owners[tokenId] == msg.sender, "Transfer Not Owner"); + _owners[tokenId] = newOwner; +} +``` + +### Assert +`assert`ステートメントはユーザーに知らせる為のエラーメッセージを含んでいないので、一般的にデバッグ目的で使用されます。 +`assert`の文法: +```solidity +assert(condition); +``` +条件が満たされていない場合、エラーがスローされます。 + +`assert`ステートメントを用いて`transferOwner`関数を書き換えてみましょう: +```solidity + function transferOwner3(uint256 tokenId, address newOwner) public { + assert(_owners[tokenId] == msg.sender); + _owners[tokenId] = newOwner; + } +``` + +## Remix Demo +`Error`コントラクトをデプロイした後。 + +1. `error`: `uint256`型の数値とゼロアドレスでないアドレスを入力し、`transferOwner1()`関数を呼び出します。コンソールがカスタムエラーである`TransferNotOwner`エラーをスローします。 + + ![15-1.png](./img/15-1_ja.png) + +2. `require`: `uint256`型の数値とゼロアドレスでないアドレスを入力し、`tranferOwner2()`関数を呼び出します。コンソールがエラーをスローしエラーメッセージ`"Transfer Not Owner"`というエラーメッセージを出力します。 + + ![15-2.png](./img/15-2_ja.png) + +3. `assert`: `uint256`型の数値とゼロアドレスでないアドレスを入力し、`transferOwner3()`関数を呼び出します。コンソールがエラーメッセージ無しでエラーをスローします。 + + ![15-3.png](./img/15-3_ja.png) + + +## ガス代比較 +`error`、`require`、`assert`のガス消費量を比較してみましょう。 +remixコンソールのDebugボタンを押すことで、各関数のガス消費量を確認することが出来ます。 + +1. **gas for `error`**:24457 `wei` +2. **gas for `require`**:24755 `wei` +3. **gas for `assert`**:24473 `wei` + +`require`が最も多くのガスを消費するのに対して、`error`が最も少ないガスを消費し、続いて`assert`がガスの消費量が少ないということが分かります。 +それ故に、`error`はエラーメッセージをユーザーに知らせてくれるだけでなく、ガスを節約することが出来るのです。 + +## まとめ +この章では、Solidityにおいてエラーを処理する3つのステートメントについて紹介しました: `error`、`require`、そして`assert`です。それらのガス消費量を比較した後で、`require`ステートメントが最も高いガスの消費量である一方で、`error`ステートメントが最もコストがかからないということが分かりました。 +