PR・広告

【Aleph Zero】スマートコントラクトガイド/日本語翻訳

本記事は、ガイドについての日本語翻訳です。

Aleph Zero スマートコントラクトの基礎

開発者の皆様には、Aleph Zero テストネットのスマート コントラクト機能をぜひお試しください。このガイドでは、開始方法について説明します。

この短いチュートリアルでは、Aleph Zero Testnet のスマート コントラクト機能の基本について説明します。

次のページでは、テストネット アカウントの設定方法、スマート コントラクト開発用にコンピューターを準備する方法、スマート コントラクトの作成、コンパイル、デプロイの方法、そして最後にデプロイしたスマート コントラクトとやり取りする方法について説明します。注: スマート コントラクトは現在、Aleph Zero メインネットで稼働しています。

テストネットアカウントの設定

テストネットの操作について知っておくべきことはすべてここにあります。ここでは、このテスト環境を活用するために必要な 2 つの重要なコンポーネントについて説明します。

Aleph Zero Testnet アカウントを設定するには、次の 2 つのコンポーネントを使用する必要があります。

必要な手順は簡単です

必要なツールのインストール

Aleph Zero で最初のスマート コントラクトを実行する前に、まず Rust と ink! での開発用にコンピューターを準備する必要があります。こちらに、開始するための便利なガイドがあります。

Aleph Zero テストネットに最初のスマート コントラクトをデプロイする前に最初に行う必要があるのは、Rust と ink! での開発用にコンピューターを準備することです。

Rust

このガイドでは、https://rustup.rsインストーラーとrustupRust ツールチェーンを管理するツールを使用します。これは Rust をインストールするデフォルトの方法であり、この方法を使用することを強くお勧めします。ただし、別の方法を希望する場合は、公式Rust Web サイトの「その他のインストール方法」セクションを確認してください。

インストールして設定するには、rustupシェルで次のコマンドを入力します。

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env

これで、最新の安定バージョンの Rust がコンピューターにインストールされました。スマート コントラクトの開発には、もう少し新しいnightlyバージョンと、いくつかの追加コンポーネントが必要になります。

rustup toolchain install nightly
rustup component add rust-src --toolchain nightly
rustup target add wasm32-unknown-unknown --toolchain nightly

Rust にはネイティブ パッケージ マネージャーが付属しておりcargo、Rust コードのコンパイルにも使用されます。cargo正しくインストールされ、シェル環境で表示されていることを確認してください。

cargo --help

ink!

ink! は、WASM スマート コントラクトを Rust で記述するために使用できる組み込みドメイン固有言語(EDSL)です。言い換えると、ink! は Rust 上の「アドオン」のコレクションであり、言語の動作を変更して、コンピューターで実行できる通常のバイナリ コードではなく、Substrate ベースのスマート コントラクト実行環境と互換性のある特別な WASM コードを生成します。結局のところ、すべての ink! スマート コントラクトは、通常の Rust プログラムに「マクロ」と呼ばれる ink! 固有のヘッダーが少し追加されただけです。

binaryenink! の使用を開始するには、まず、コントラクトの WebAssembly バイトコードを最適化するために使用されるパッケージをインストールする必要があります。ほとんどの場合、binaryenデフォルトのパッケージ マネージャーから入手できます。

# For Ubuntu or Debian users:
sudo apt install binaryen
# For MacOS users:
brew install binaryen
# For Arch or Manjaro users:
pacman -S binaryen

バイナリリリースを直接ダウンロードすることもできます。

現在binaryen、インストールできるもの cargo contract:

cargo install --force --locked cargo-contract

cargo contractは、スマート コントラクトの開発に役立つコマンドで を拡張するアドオンですcargo。 を呼び出すことで、何ができるかを確認できますcargo contract --help。すぐにこれを使用して、最初の ink! スマート コントラクトを作成します。

最初の契約書を作成する

これでマシンの開発準備が整いましたので、最初のスマート コントラクトを構築します。このチュートリアルで開発するサンプル コントラクトは、ERC20 トークンの簡易バージョンです。

このようにマークされたボックスには、Rust プログラミング言語に関する基本的な情報が含まれています。Rust に精通している場合は、読み飛ばしてください。

このようにマークされたボックスには、ink でのスマート コントラクト開発に関する一般的なコメントが含まれています。このチュートリアルを完了するために厳密に必要というわけではありませんが、後々役に立つ可能性があります。

導入

重要なツールがすべて揃ったら、最初の ink! スマート コントラクトを開発する準備が整いました。

このチュートリアルで構築するサンプル コントラクトは、ERC20トークンの非常にシンプルなバージョンです。コントラクトをインスタンス化すると、アカウント間で転送できる新しいタイプの代替可能なトークンを含むプールが作成されます。コントラクトは、アカウントの残高のレジストリを保持し、残高を照会してトークンを転送するメソッドを提供します。

私たちが作成しているトークンは、チェーンのネイティブ通貨とは何の関係もないことに注意してください。そのため、チェーンの内部メカニズムはトランザクションの正確性を保証するものではありません。ロジックが理にかなっていることを確認するのは、すべて契約の作成者であるあなたの責任です。

契約書の作成

まず、次のものを使用して契約テンプレートを生成しますcargo contract

cargo contract new mytoken
cd  mytoken

mytokenこのコマンドは、次のファイルを含む新しいディレクトリを作成します。

  • lib.rs– 契約のコードを含むRustソースファイル
  • Cargo.tomlcargo-コントラクトの構築方法を説明するマニフェスト ファイル(このチュートリアルでは変更する必要はありません)
  • .gitignoregit-契約書のバージョン管理に使用する場合

のみを扱いますlib.rs。残りの 2 つのファイルはそのままにしておきます。中を見ると、lib.rs最も単純な hello-world コントラクトであるフリッパーが見つかります。これは、単一のブール値を保持し、それを反転できるものです。コードを確認することをお勧めしますが、一部が不可解に思えても心配しないでください。コードを段階的に変更し、その過程ですべてを説明します。

実装

ink! で書かれたスマート コントラクトは、実際には ink! マクロ ( のような行#[ink...]) を使用する通常の Rust コードです。これらのマクロの役割は、コンパイル プロセスを変更して、コンピューターで実行できる通常のプログラムではなく、Aleph Zero ブロックチェーンにデプロイできる WASM スマート コントラクトを生成することです。ファイルの上部に、構成マクロを含める必要があります。

#![cfg_attr(not(feature = "std"), no_std, no_main)]

この恐ろしそうなマクロは、基本的にコンパイラに標準ライブラリを含めないように指示します (ink! はスマート コントラクト開発に適した独自のプリミティブ セットを使用します)。また、mainシンボルの発行を無効にすることもできます。ファイルの残りの部分には、メインの ink! マクロがプレフィックスとして付いたモジュールの定義が含まれています。

#[ink::contract]
mod mytoken {
    //...
}

このマクロは、モジュールがmytoken実際にはスマート コントラクトの定義であり、ink! がそのモジュール内でコントラクトのさまざまなコンポーネントを調べる必要があることを ink! に伝えます。またBalance、 やなどの便利な型エイリアスも提供しますAccount。さらに、現時点では気にする必要のない不変条件をいくつか強制します。

  • 契約には、次のようにマークされた構造体が1つだけ必要です。#[ink::storage]
  • 契約には少なくとも1つの機能がマークされている必要があります。#[ink::constructor]

Rust モジュールは、型と関数のコレクションとして考えることができ、それらを単一の大きなスコープにグループ化します。

ストレージ

最初のコンポーネントは、コントラクト ストレージです。ブロックチェーンに保存され、コントラクトの状態を保持するデータが含まれています。この場合、これはユーザーと所有するトークンの数のマッピングと、総供給量または新しいトークンを保持する単一の数値になります。そのデータは、struct対応する ink! ストレージ マクロをプレフィックスとして付けた単一の Rust で囲む必要があります。

#[ink::contract]
mod mytoken {
    use ink::storage::Mapping;

    #[ink(storage)]
    #[derive(Default)]
    pub struct Mytoken {
        total_supply: Balance,
        balances: Mapping<AccountId, Balance>,
    }
}

structここでは、次のようにインスタンス化されるRust を宣言しています。

let my_token = Mytoken { total_supply: somevalue1, balances: somevalue2, }

そして、そのフィールドには次のようにアクセスされますmy_token.total_supply

便利なショートカットがあり、 と書く代わりに とMytoken { total_supply: total_supply, balances: balances } 書くことができます Mytoken { total_supply, balances }(total_supplyと はbalancesコード内に存在する変数であると仮定します)。

Mappingここでは、クレートによって提供されるデータ構造
を使用しています
ink::storage。ink! スマート コントラクトを作成する場合、Rust 標準ライブラリのデータ構造は使用できないことに注意してください。幸いなことに、ink! は、オンチェーンで保存できるように最適化されたキー値マップの形式で、
便利な代替手段を提供します。

この構造体に何を保存するかを選択するときは、手数料が発生するため、かなり慎重になる必要があります (Aleph Zero の場合、手数料は非常に小さいですが、割り当てる金額に注意する価値はあります)。たとえば、アドレスと残高のマッピングを保存することは問題ありません。ただし、コントラクトが画像を処理する場合は、これらをオフチェーンで保存し、ハッシュのみをコントラクトのストレージにコミットすることをお勧めします。

ink! に特性の実装を作成するように指示していることにも注意してくださいDefault。この場合、コンパイラはフィールドtotal_supplyを 0 ( Defaulta の値Balance) に初期化し、をbalancesMapping(偶然にも対応するDefault実装) に初期化できます。

コンストラクタ

次のステップは、コントラクトのコンストラクタを実装することです。これは、impl新しく定義されstruct Mytoken、適切な ink! マクロをプレフィックスとして付けたブロック内に配置する必要があり、

コントラクトには、それぞれがマクロでマークされている限り、任意の数の 0 以外のコンストラクタを含めることができます#[ink(constructor)]

#[ink::contract]
mod mytoken {   
    // ... (storage definition)

    impl Mytoken {
        #[ink(constructor)]
        pub fn new(total_supply: Balance) -> Self {
            let mut balances = Mapping::default();
            let caller = Self::env().caller();
            balances.insert(caller, &total_supply);
            Self {
                total_supply,
                balances,
            }
        }
    }
}

コンストラクターは、新しく作成されたトークンの初期供給量という単一の引数を受け取り、その供給量すべてをコントラクト作成者のアカウント (コンストラクターを呼び出すアカウント) に預け入れます。

コントラクトのコンストラクターは、通常の Rust コンストラクターと非常によく似ています。最初に関数を呼び出してstruct空を作成し、その後で最初のエントリを挿入して、すべての供給をコンストラクターの呼び出し元に割り当てることに注意してください。MappingDefault::default()

Self::env().caller()メソッドを使用して、コントラクトを呼び出したアカウントのアドレスにアクセスできます。コンストラクターのコンテキストでは、これはコントラクトの作成者/所有者になります。

ブロックimplには、同じ名前の構造体 (この場合はMytoken) を操作するメソッドが含まれます。言語によっては、これらのメソッドをクラス/構造体の本体に記述することもあります。Rust では、定義と実装を分離して柔軟性を高めています。

さらに、Rust では 1 行のコメントを二重スラッシュ ( //) で記述することに注意してください。

メッセージ

上記で定義したコントラクト のコンストラクターが実際には ink! マクロがプレフィックスとして付けられた通常の Rust コンストラクターであるのと同様に、コントラクトの呼び出し可能メソッド ( ink! によってメッセージと呼ばれる) は、別の ink! マクロで注釈が付けられた通常の Rust メソッドです。

#[ink::contract]
mod mytoken {  
    // ... (storage definition)
    
    impl Mytoken { 
        // ... (constructor definition)
    
        #[ink(message)]
        pub fn total_supply(&self) -> Balance {
            self.total_supply
        }
        
        #[ink(message)]
        pub fn balance_of(&self, account: AccountId) -> Balance {
            self.balances.get(&account).unwrap_or_default()
        }
    }
}

!でマークされたメソッドは、 public ()#[ink(message)]として宣言する必要があります。
pub fn

ここでは、契約のストレージにアクセスするための 2 つのメソッドを定義しました。合計供給量と特定のアカウントが保持するトークンの数を読み取るメソッドです。これらのメソッドは読み取り専用であり、契約ストレージを変更せず、ブロックチェーンにトランザクションを送信せずに呼び出すことができます。

メソッドは、まず指定された のマッピングbalance_ofから値を取得します。結果は構造体にラップされて返されます。メソッドを使用して、実際の値または型のデフォルト(便利なことに 0) を取得します。balancesaccountOptionunwrap_or_default()Balance

self特定のメソッドが呼び出される構造体のインスタンスにアクセスするには、キーワードを使用します(一部の言語では、this同じセマンティクスでキーワードを使用することを選択します)。

Rust の関数は、キーワードを使用して宣言されfn、その後に関数名、引数のリストとその型 ( arg: Type)、矢印の後の戻り値の型が続きます。最後の行を返すだけの場合は、キーワードを使用する必要がないことに注意してくださいreturn

注意: 短絡ステートメントを過度に使用しないと、コードはよりエレガントになると言えますreturn

キーワードpubは、特定の関数をパブリックとしてマークします。つまり、宣言されているモジュールの外部からアクセス可能になります。

エラー

関数を見る前にtransfer、Rust のエラー処理の慣用的な方法を学ぶ必要があります。一部の言語とは対照的に、Rust は例外の概念を捨てて代数的なエラー処理を採用しています。失敗する可能性のある各メソッドにはResult<T, E>型があり、次のように定義されます。コピー

pub enum Result<T, E> {
  Ok(val: T),    // T is the expected type of the computation
  Err(msg: E),   // E is an error type of your choice
}

Rust のジェネリック型に詳しくない方のために説明すると、ここでは は型パラメータと(基本的には型のプレースホルダ)Resultを持つ型であると言えます。コードの後半では、 をおよび などの任意の型でインスタンス化できます。たとえば、次のようになります。: この場合、 は符号なしの 128 ビット整数になり、 は文字列になります。インスタンス化ごとに、との選択に従う必要があることに注意してください。ただし、この型のインスタンスを作成するどの場所でも、任意の型を選択できます (使用例を見れば明らかです)。TEResultTEResult<u128, str>valmsgTE

私たちの契約では、の一部Errorとして使用するカスタム構造体を定義します。ErrResultコピー

#[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub enum Error {
    InsufficientBalance,
}

この入門チュートリアルでは、恐ろしいマクロについては心配しないでください。後のチュートリアルで詳しく説明します。今のところは、それらをコピーするだけで十分です😊以下の実装ではtransfer、実際にこのメソッドを使用する例がわかります。

Rust の に慣れていない場合、ここでは引数を取らない 1 つのバリアント (またはコンストラクター) だけを持つ型をenum定義しています: 。このエラーをインスタンス化するときは、 と記述し、その型は になります(たとえば、関数の型シグネチャでそれを指定する必要がある場合)。ErrorInsufficientBalanceError::InsufficientBalanceError

最後に必要なのは、アカウント間でトークンを転送する方法です。

mod mytoken {  
    // ...
    
    impl Mytoken { 
        // ... constructor definition
        // ... error definition
        
        #[ink(message)]
        pub fn transfer(&mut self, to: AccountId, value: Balance) -> Result<(), Error> {
            let from = self.env().caller();
            let from_balance = self.balance_of(from);
            if from_balance < value {
                return Err(Error::InsufficientBalance);
            }

            self.balances.insert(from, &(from_balance - value));
            let to_balance = self.balance_of(to);
            self.balances.insert(to, &(to_balance + value));
            Ok(())
        }
    }
}

このメソッドは、任意のユーザーが呼び出して、amount自分のトークンの一部を自分の選択した に転送することができますrecipient。ユーザーが所有するトークンよりも多くのトークンを転送しようとすると、メソッドは変更を実行せずに終了します。transferメソッド内では、以前に定義した メソッドを使用していることに注意してくださいbalance_of

transferこの方法と以前の方法の最も重要な違いは、 がコントラクト ストレージを変更するという事実です。 この事実は、最初の引数として&mut selfではなくを使用して示す必要があります&self。 この要件はコンパイラによって強制されます。 を忘れた場合mut、コントラクトはビルドされず、コンパイラは を使用するように提案しますmut。 したがって、バグのあるコントラクトをデプロイすることを心配する必要はありません。

sについてのこれまでの説明を踏まえてenum、戻り値の型を と宣言していることに注意してくださいResult<(), Error>

このOkバリアントには が含まれており、これはC(++) の()とほぼ同等であり、平易な英語では「値が返されない」ことを意味します。void

バリアントErrには、以前に定義した型が含まれている必要があるErrorため、それを返すときに、 としてインスタンス化しますErr(Error::InsufficientBalance)

このチュートリアルでは、参照 ( &) は C++ などの言語と非常によく似た動作をすると想定できます。つまり、値をコピーしたり譲渡したりするのではなく、他の関数に「貸し出す」だけです。後でわかるように、貸し借りの直感は、実際には Rust 言語で非常によく形式化されています。

借り手が一時的に取得した値に何らかの変更を加える可能性があることを示すには、参照を としてマークする必要があります&mut

幸いなことに、上で述べたように、Rust のコンパイラーはこの領域の問題を解決する方法について非常に役立つヒントを提供しています。正しくコンパイルされるコントラクトを作成するには、それらのヒントで十分でしょう。このトピックについてさらに詳しく知りたい場合は、『The Rust Book』を参照してください。

テスト

他のすべてのプログラムと同様に、スマート コントラクトもテストする必要があります。開発プロセスのこの部分も、通常の Rust で行われる方法と非常に似ています。テストはオフチェーンで実行され、ink! は、将来コントラクトが稼働するオンチェーン環境をシミュレートするのに役立つ便利なツールをいくつか提供します。

ここでは、実装された各メソッドの基本的な健全性チェックを含む非常に最小限のテスト スイートを示します。次のコードは、lib.rsコントラクトと同じファイルに配置する必要があります。

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

    #[ink::test]
    fn total_supply_works() {
        let mytoken = Mytoken::new(100);
        assert_eq!(mytoken.total_supply(), 100);
    }

    #[ink::test]
    fn balance_of_works() {
        let mytoken = Mytoken::new(100);
        let accounts =
            ink::env::test::default_accounts::<ink::env::DefaultEnvironment>();
        assert_eq!(mytoken.balance_of(accounts.alice), 100);
        assert_eq!(mytoken.balance_of(accounts.bob), 0);
    }

    #[ink::test]
    fn transfer_works() {
        let mut mytoken = Mytoken::new(100);
        let accounts =
            ink::env::test::default_accounts::<ink::env::DefaultEnvironment>();

        assert_eq!(mytoken.balance_of(accounts.bob), 0);
        assert_eq!(mytoken.transfer(accounts.bob, 10), Ok(()));
        assert_eq!(mytoken.balance_of(accounts.bob), 10);
    }
}

上記のコントラクトを実装すると、このコードの詳細については説明を要しません。

テスト スイートは、フォルダーcargo test内でターミナルを呼び出すことによって実行できますmytoken

cargo test
   Compiling mytoken v0.1.0 (/home/user/ink/mytoken)
    Finished test [unoptimized + debuginfo] target(s) in 1.08s
     Running unittests lib.rs (target/debug/deps/mytoken-668aad4b5e4b8a01)

running 3 tests
test tests::balance_of_works ... ok
test tests::total_supply_works ... ok
test tests::transfer_works ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0

ink! のバージョン 4.0 では、スマート コントラクト開発フローにエンドツーエンドのテストが導入されました。これについては、コントラクトの正確性を確保するための推奨される追加の方法として、後のチュートリアルで説明します。

まとめ

最後に、契約のすべての部分を最終バージョンに組み合わせましょう。///...これらの部分の機能を説明するドキュメントコメント ( ) を追加することもできます。ここでは簡潔にするために省略していますが、含めることをお勧めします。この情報は、契約 UI を通じて契約を操作するユーザーに表示されます。mytoken/lib.rs

#![cfg_attr(not(feature = "std"), no_std, no_main)]

#[ink::contract]
mod mytoken {
    use ink::storage::Mapping;

    #[ink(storage)]
    #[derive(Default)]
    pub struct Mytoken {
        total_supply: Balance,
        balances: Mapping<AccountId, Balance>,
    }

    #[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)]
    #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
    pub enum Error {
        InsufficientBalance,
    }

    impl Mytoken {
        #[ink(constructor)]
        pub fn new(total_supply: Balance) -> Self {
            let mut balances = Mapping::default();
            let caller = Self::env().caller();
            balances.insert(caller, &total_supply);
            Self {
                total_supply,
                balances,
            }
        }

        #[ink(message)]
        pub fn total_supply(&self) -> Balance {
            self.total_supply
        }

        #[ink(message)]
        pub fn balance_of(&self, owner: AccountId) -> Balance {
            self.balances.get(owner).unwrap_or_default()
        }

        #[ink(message)]
        pub fn transfer(&mut self, to: AccountId, value: Balance) -> Result<(), Error> {
            let from = self.env().caller();
            let from_balance = self.balance_of(from);
            if from_balance < value {
                return Err(Error::InsufficientBalance);
            }

            self.balances.insert(from, &(from_balance - value));
            let to_balance = self.balance_of(to);
            self.balances.insert(to, &(to_balance + value));
            Ok(())
        }
    }

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

        #[ink::test]
        fn total_supply_works() {
            let mytoken = Mytoken::new(100);
            assert_eq!(mytoken.total_supply(), 100);
        }

        #[ink::test]
        fn balance_of_works() {
            let mytoken = Mytoken::new(100);
            let accounts =
                ink::env::test::default_accounts::<ink::env::DefaultEnvironment>();
            assert_eq!(mytoken.balance_of(accounts.alice), 100);
            assert_eq!(mytoken.balance_of(accounts.bob), 0);
        }

        #[ink::test]
        fn transfer_works() {
            let mut mytoken = Mytoken::new(100);
            let accounts =
                ink::env::test::default_accounts::<ink::env::DefaultEnvironment>();

            assert_eq!(mytoken.balance_of(accounts.bob), 0);
            assert_eq!(mytoken.transfer(accounts.bob, 10), Ok(()));
            assert_eq!(mytoken.balance_of(accounts.bob), 10);
        }
    }
}

コンパイル

次は契約を構築します。

cargo +nightly contract build --release

結果のファイルはmytoken/target/ink/フォルダーに配置されます。コンパイルが成功すると、次の 3 つのファイルが見つかります。

  • mytoken.wasmコンパイルされた契約を含むバイナリWASMファイルです
  • metadata.json契約ABI(アプリケーションバイナリインターフェース)を含む
  • mytoken.contract上記2つをバンドルして、チェーンエクスプローラーとのより便利なやり取りを実現します。

mytokenこれで、 Aleph Zero Testnet に契約をデプロイする準備が整いました。

Aleph Zero テストネットへの契約のデプロイ

このチュートリアルでは、Aleph Zero ブロックチェーン上のスマート コントラクト環境と対話するいくつかの方法について説明します。

ここで、新しいスマート コントラクトを Aleph Zero テストネットにデプロイします。Aleph Zero ブロックチェーン上のスマート コントラクト環境とやり取りする方法は複数あります。ここでは、Contracts UI を使用する方法と、cargo contractツール経由でコマンド ラインを使用する方法の 2 つを紹介します。

契約UI

契約の展開

スマート コントラクトの展開と操作に必要なすべてのツールは、Contracts UIとして便利にパッケージ化されています。開発者 Web ウォレットを使用してアカウントを作成し、 Faucetから無料の TZERO を取得していることを確認してください。そうしないと、ウォレットの一部のタブとボタンが表示されない場合があります。

[開発者] タブに移動し、ポップアップ メニューの [コントラクト] をクリックして、スマート コントラクト コントラクト UI にアクセスします (上記のリンクに直接アクセスすることもできます)。

Contracts UI に入ったら、サイド メニューの [新しい契約を追加] をクリックしてデプロイメント ページに移動し、[新しい契約をアップロード] を選択します。

契約を展開するために使用するアカウントを選択します (複数ある場合)。このアカウントは、mytoken契約によって作成された新しいトークンの初期供給全体を保持します。次に、下の [契約バンドルのアップロード] フィールドをクリックし、mytoken.contract以前に生成したファイルの場所を選択しますcargo contract(フォルダー内にあるはずですmytoken/target/ink/)。すべてが正常であれば、[有効な契約バンドルです] というテキストが表示されます。

「次へ」をクリックします。インスタンス化するために、コントラクトのコンストラクタにパラメータを指定するよう求められます。この場合、これはトークンの初期供給量にすぎません (ここでは任意の数を選択できます。アカウントの TZERO 残高とはまったく関係ありません)。

いくつかのカスタム オプションがありますが、これについては後のチュートリアルで説明します。右側のコスト見積もりに注目してください。この見積もりでは、とりわけ、アカウントにこの契約を作成するのに十分な残高があるかどうか (つまり、ガス料金をカバーできるかどうか) がわかります。

すべてがうまくいけば、次の画面が表示されます。

残っているのは、「アップロードしてインスタンス化」ボタンをクリックして契約をデプロイすることだけです。選択したアカウント マネージャーを使用してトランザクションに署名する必要があります (現在のデフォルトは Polkadot.js 拡張機能です)。その後、契約に関する情報と、契約を呼び出すメソッドを選択するためのドロップダウン リストを含む画面が表示されます。

「メタデータ」タブを選択して、コントラクトで呼び出し可能なメソッドを表示することもできます。

契約とのやり取り

いよいよ、新しいトークンを試してみましょう。左側の契約リストから「mytoken」を選択した場合、契約で呼び出すメソッドを選択するためのドロップダウンが表示されます。

読み取り専用メソッドはbalanceOftotalSupply右側の「結果」モーダルに結果をすぐに返すことがわかります。これは、読み取り専用であるため、呼び出しのトランザクションを作成する必要がないためです。

興味深いのはtransferメソッドを呼び出す部分です。

送金の有効な受取人を設定するには、2 番目のアカウントが必要になります (実際には、アカウント アドレスの形式が正しい限り、既存のアカウントに関連付ける必要はありません。ただし、テスト目的で有効なアドレスを取得するには、別のアカウントを作成するのが最も簡単な方法です)。

転送される金額を入力する必要があります (契約の作成時に選択した初期供給量よりも大きい値を入力した場合に何が起こるかを試すことができます)。ここでも、追加オプションはデフォルト値のままにしておきます。

契約のインスタンス化と同様に、画面の右側に便利なガス見積もりがあり、これを使用して、この呼び出しを実行するのに十分な資金があるかどうかを確認できます (ガス料金に関して)。もう一度トランザクションに署名すると、転送が完了します。 を使用してbalanceOf、転送が実際に行われたかどうかを確認できるようになりました。

コマンドライン

契約の展開

Aleph Zero ブロックチェーン上のスマート コントラクトをより自動化され、プログラム可能な方法で操作したい場合は、cargo contractコントラクトをコンパイルしたコマンド ライン ツールを使用して、上記のすべてのアクションを実行することもできます。 のすべての外部関連機能の簡単な概要については、こちらをcargo contractご覧ください。

ライブ チェーンと対話するすべてのcargo contractサブコマンドは、チェーンのエンドポイント アドレスとユーザーの秘密鍵 (シード フレーズ) を定義するフラグを使用して呼び出す必要があります。このセクションのコマンドをより簡潔にするために、まずこれらのフラグで使用される値を持つ環境変数をいくつか定義しましょう。

export SEED="[put your 12 words seed phrase here]"
export URL="wss://ws.test.azero.dev"

新しいコントラクトのデプロイはinstantiateサブコマンドで実行できます。コントラクトが存在するフォルダーにいることを確認しmytoken、次のコマンドを実行します。

cargo contract instantiate --suri "$SEED" --url "$URL" \
        --constructor new_token \
        --args 1000

このコマンドの出力には、デプロイメント トランザクションの結果としてチェーンによって生成されたさまざまなイベント (料金の支払い、連絡先のアカウントの作成など) のリストが含まれます。最後のイベントはSystem ➜ ExtrinsicSuccessデプロイメントが成功したことを示し、その後に作成したコントラクトのアドレスに関する情報が続きます。コントラクトとのやり取りをより便利にするために、このアドレスを別の環境変数に保存しましょう。

export CONTRACT="5GNruCfnGXjSPkW7LkRnB45MiHJJtvc6NBKZnDSnFh4So3ws

契約アドレスは、前のセクションで使用したウェブ ウォレットに既存の契約をインポートするためにも使用できます。
スマート コントラクト マネージャーmetadata.jsonで [既存の契約を追加] をクリックし、そこにアドレスを貼り付けます。コンパイル中に生成され、フォルダーに存在するはずの ABI を含むファイルをアップロードする必要もあります
mytoken/target/ink/。その後は、前述と同じ方法で契約を操作できます。

契約とのやり取り

ウェブ ウォレットのセクションで前述したように、スマート コントラクトで実行できる呼び出しには 2 つの種類があります。コントラクトの状態を変更せずに問い合わせる状態クエリと、状態を変更し、署名済みトランザクションの送信と手数料の支払いを要求する実行可能呼び出しです。これら 2 種類のアクションは、フラグcargo contractによって区別されます--dry-run

まず、最も単純な引数なしの状態クエリを実行して、トークンの総供給量を調べてみましょう。コピー

cargo contract call --suri "$SEED" --url "$URL" \
        --contract "$CONTRACT" \
        --message total_supply \
        --dry-run

出力には、関数によって返されたデータなどが含まれますtotal_supply。コピー

          Result Success!
        Reverted false
            Data 1000
    Gas Consumed 248300975
    Gas Required 6815744000
 Storage Deposit StorageDeposit::Charge(0)

のような引数を使用して状態クエリを実行するには、連絡先とをデプロイしてそのコンストラクターを呼び出したときと同様に、フラグをbalance_of追加する必要があります。--args

cargo contract call --suri "$SEED" --url "$URL" \
        --contract "$CONTRACT" \
        --message balance_of \
        --args 5FWmHxBXH4WfrryA6xdbaQRJALJ549aL11HMyybqDy5iNRtE \
        --dry-run

Output:
          Result Success!
        Reverted false
            Data 1000
    Gas Consumed 322051074
    Gas Required 6815744000
 Storage Deposit StorageDeposit::Charge(0)

ここでの引数は、任意の有効なアカウント アドレスになります。もちろん、この時点では、コントラクト作成者 ( に入力したシード フレーズに関連付けられたアドレス$SEED) 以外のすべてのアカウントのトークンは 0 で、作成者は合計 1000 トークンの供給量を保有しています。そこで、トークンの一部を別のアカウントに転送して変更してみましょう。コピー

cargo contract call --suri "$SEED" --url "$URL" \
        --contract "$CONTRACT" \
        --message transfer \
        --args 5D853t8wQuHJpfWvtcB3VUyKo8Ki44HQwgTmynGT4i5UVhbr 100

今回はチェーンの状態を変更するトランザクションを送信したいので、--dry-runフラグは省略する必要があります。出力には、呼び出しが成功したことを示す familiar で終わるトランザクションによって生成されたイベントのリストが含まれますSystem ➜ ExtrinsicSuccess。これで、100 トークンの転送が実際に行われたことを確認できます。

cargo contract call --suri "$SEED" --url "$URL" \
        --contract "$CONTRACT" \
        --message balance_of \
        --args 5FWmHxBXH4WfrryA6xdbaQRJALJ549aL11HMyybqDy5iNRtE \
        --dry-run

Output:
          Result Success!
        Reverted false
            Data 900
    Gas Consumed 322051074
    Gas Required 6815744000
 Storage Deposit StorageDeposit::Charge(0)

コピー

cargo contract call --suri "$SEED" --url "$URL" \
        --contract "$CONTRACT" \
        --message balance_of \
        --args 5D853t8wQuHJpfWvtcB3VUyKo8Ki44HQwgTmynGT4i5UVhbr \
        --dry-run

Output:
          Result Success!
        Reverted false
            Data 100
    Gas Consumed 322051074
    Gas Required 6815744000
 Storage Deposit StorageDeposit::Charge(0)

次のステップ

おめでとうございます。これで、スマート コントラクト開発者になりました。ink! スマート コントラクトについてさらに詳しく知りたい場合は、優れたink! ドキュメント を詳しく読むことをお勧めします。

また、ここにある ink! コントラクトの例のコレクションを確認することもできます問題や質問がある場合は、いつでも喜んでお手伝いいたします。alephzero.org に記載されているチャネルのいずれかを使用してお問い合わせください。

監査および研究論文

Aleph Zero の安全なスマート コントラクト、zk 対応ブロックチェーン、プライバシー強化テクノロジーの詳細については、当社のホワイト ペーパーをご覧ください。

この記事は以下サイトの機械翻訳です。

BUILD | Aleph Zero
タイトルとURLをコピーしました