😄

[Astar]Swankyで作成するpsp22コントラクトを読んでみよう!

2023/02/09に公開

こんにちは、CryptoGamesのユウキと申します。

今回は、swankyで作成する、「psp22」のコントラクトを見ていきたいと思います。

swankyを使うまでの過程については、こちらの記事をご確認ください。
https://zenn.dev/yuki2020/articles/605b2d31d085dd#3.テストを行う

1 テストプロジェクトを作成しよう

では、テストプロジェクトを作っていきたいと思います。

swanky init test_project

このように、今回見ていきたいプロジェクトができました。

こちらを一つ一つ見ていきましょう。

2 psp22のコードを確認しよう

1 #![cfg_attr(not(~))]について

こちらについては、下の記事の第1章の1をご参照ください。
https://zenn.dev/yuki2020/articles/7ec60d8e4d155b

2 #![feature(min_specialization)]について

では、こちらの#![feature(min_specialization)]について見ていきましょう。

min_specializationは「特殊化」最小限に制限することのようです。

特殊化を必要以上に行わないことを保証することでコードの信頼性が向上します。


chatGPTからの引用

3 SpreadAllocateについて

次はこちらの「ink_storage::traits::SpreadAllocate」についてです。

具体的な中身はこのようになっています。

allocate_spreadというメソッドが存在しています。

https://docs.rs/ink_storage/3.3.1/ink_storage/traits/trait.SpreadAllocate.html

また、このように、structの前で使われています。

下のように、スマートコントラクトのステートをSubstrateのステートストレージに格納する際に使われるようです。

chatGPTからの引用

ちなみに、[#derive]アトリビュートは、このように継承を表しています。

https://doc.rust-jp.rs/rust-by-example-ja/trait/derive.html

4 ink_lang::codegen::Envについて

では、こちらのEnvを見ていきましょう。

実装はこのようになっています。

https://paritytech.github.io/ink/ink/codegen/trait.Env.html

下のように、「EnvAccess型」を定義し、env関数で、その「EnvAccess型」を返しています。

chatGPTからの引用

5 ink_lang::codegen::EmitEventについて

では、こちらのEmitEventを見てみましょう。

このように定義されています。

https://paritytech.github.io/ink/ink/codegen/trait.EmitEvent.html

下のように、「ContractEventBase」というトレイトを実装しているかを確認しているようです。

chatGPTからの引用

6 ContractEventBaseについて

「ContractEventBase」については、こちらです。

このように、型パラメータの「Type」をもっているようです。


https://paritytech.github.io/ink/ink/reflect/trait.ContractEventBase.html

7 #[ink(event)]について

では、こちらの#[ink(event)]を見ていきましょう。

下のように、ink!イベントを定義しているようです。

「TransferEvent」や「ApprovalEvent」を定義していたようです。

https://use.ink/macros-attributes/event

8 #[ink(topic)]について

次は、こちらの#[ink(topic)]についてです。

イベントの構造体の中の一部についていますね。

このように書かれています。

solidityにおける、イベント時のインデックスのようなもののようです。

https://use.ink/macros-attributes/topic

下のように、トピックをつけることにより、トランザクションやブロックの検索時に用いることができるようです。

chatGPTからの引用

9 psp22::Dataについて

次は、こちらのpsp22::Dataを見ていきましょう。

下のように、モジュールのデータ構造をコントラクトに埋め込む必要があります。

そして、それぞれのモジュールのデータ構造の名前は「Data」となっているようです。


https://docs.openbrush.io/smart-contracts/overview

Githubを見ると、Dataの中身がこのようになっていることが確認できます。


https://github.dev/Supercolony-net/openbrush-contracts

10 AccountIdについて

では、こちらのAccountIdを見てみましょう。

このように、ink!の環境変数として、AccountIdが設定されています。

https://use.ink/basics/chain-environment-types

11 Transferについて

下のように、「_before_token_transfer」という関数が定義されています。

4つの引数をとり、to(宛先)が「hated_account」に一致するとエラーとなっています。

Githubを確認すると、このようになっています。

下のように、「_before_token_transfer」と「_after_token_transfer」の2つがあることが確認できました。

https://github.dev/Supercolony-net/openbrush-contracts

なお、OpenBrushのGithubを見ると、PSP22Errorは下のようになっています。

https://github.dev/Supercolony-net/openbrush-contracts

ちなみに、OptionとはNoneとSome(T)を持つ列挙型です。


https://doc.rust-jp.rs/rust-by-example-ja/std/option.html

12 psp22::Internalについて

では、次に、こちらを見てみましょう。

中に二つの関数があり、4章と5章で出てきた、envとemit_eventが出てきます。

イベントを発火する時に使う関数のようですね。

そして、下にあるように、内部でのみ使うときは、「○○::Internal」とするようです。

https://docs.openbrush.io/smart-contracts/overview

下のように、「Internal」というトレイトには9個のメソッドが入っており、


https://github.dev/Supercolony-net/openbrush-contracts

このように、それぞれ実装されています。

https://github.dev/Supercolony-net/openbrush-contracts

13 PSP22について

では、こちらのPSP22を見てみましょう。

このように、{}の中に記載がないので、デフォルトの実装をそのまま利用しています。

では、そのデフォルトの実装をGithubで見てみましょう。


https://github.dev/Supercolony-net/openbrush-contracts

14 constructor,messageなど

constructor,messageなどにつきましては、こちらの記事をご確認ください。(第3章と第6章)
https://zenn.dev/yuki2020/articles/7ec60d8e4d155b

その上で、こちらは引数に渡した値をミントし、3つの関数を用意しているようです。

15 テストについて

今回、chatGPTで作ったコードをこちらに貼っておきます。

#[cfg(test)]
    mod tests {
        use super::*;
        use ink_lang as ink;

        #[ink::test]
        fn new_method_test() {
            let psp22 = Psp22::new(100);
            assert_eq!(psp22.get_total_supply(), 100);
        }

        #[ink::test]
        fn set_hated_account_test() {
            let mut psp22 = Psp22::new(100);
            let hated_account = AccountId::from([0x1; 32]);
            psp22.set_hated_account(hated_account.clone());
            assert_eq!(psp22.get_hated_account(), hated_account);
        }
        #[ink::test]
        fn get_hated_account_test() {
            let mut psp22 = Psp22::new(100);
            let hated_account = AccountId::from([0x1; 32]);
            psp22.set_hated_account(hated_account.clone());
            let result = psp22.get_hated_account();
            assert_eq!(result, hated_account);
        }
    } 

#[cfg(test)]などにつきましては、こちらの記事の第7章をご確認ください。

https://zenn.dev/yuki2020/articles/7ec60d8e4d155b#7-%23[ink(test)]について

今回は、下のようなテストを行っています。

初期化時にミントを行った値はきちんと反映されているか、hated_accountにセットし、ゲットできるかを確かめています。

ちなみに、AccountId::from([0x01; 32])の部分は、下のように、任意のAccountIdを作成しています。

https://use.ink/ink-vs-solidity

なお、下のように、テストを実施すると、このように、うまく行っているようです。

今回は以上です。

最後まで、ありがとうございました!

Discussion