前提条件
開始前に、rustupと最新バージョンのrustcおよびcargoがインストールされていることを確認してください。現在、Rust v1.58.1以降でテストしています。
また、wasm32-unknown-unknownターゲットとcargo-generate Rustクレートもインストールする必要があります。
以下のコマンドでバージョンを確認できます:
目標
- カウンターを指定値まで増加およびリセットするスマートコントラクトの作成と操作
- CosmWasmスマートコントラクトの基礎を理解し、Injectiveにデプロイしてツールで操作する方法を習得
CosmWasmコントラクトの基礎
スマートコントラクトはシングルトンオブジェクトのインスタンスと考えることができます。その内部状態はブロックチェーン上に永続化されます。ユーザーはJSONメッセージを送信することで状態変更をトリガーでき、JSONメッセージ形式のリクエストを送信して状態をクエリすることもできます。これらのJSONメッセージはMsgSendやMsgExecuteContractなどのInjectiveブロックチェーンメッセージとは異なります。
スマートコントラクト開発者として、コントラクトのインターフェースを構成する3つの関数を定義する必要があります:
instantiate():コントラクトのインスタンス化時に呼び出されるコンストラクタ関数であり、初期状態を設定します。execute():ユーザーがスマートコントラクトのメソッドを呼び出す際に実行されますquery():ユーザーがスマートコントラクトからデータを取得する際に呼び出されます
instantiate、1つのquery、2つのexecuteメソッドを実装します。
テンプレートから始める
作業ディレクトリで以下のコマンドを実行すると、推奨されるフォルダ構成とビルド設定を備えたテンプレートから開発を開始できます:src/contract.rsファイルには、標準的なCosmWasmエントリポイントであるinstantiate()、execute()、query()が適切に公開されています。
State
CosmWasmのStateについて詳しくはドキュメントを参照してください。
Stateはスマートコントラクトのデータが保存・アクセスされるデータベースの状態を管理します。
スターティングテンプレートは以下の基本的な状態を持ちます。シングルトン構造体Stateは以下を含みます:
count:execute()メッセージが増加またはリセットを行う32ビット整数owner:MsgInstantiateContractの送信者addressで、特定のexecuteメッセージの実行可否を判断します
cosmwasm-storageなどのユーティリティクレートを提供しており、構造体やRustの数値型などの一般的に使用される型に対して自動的にシリアライズとデシリアライズを行う「singleton」や「bucket」などのデータコンテナの便利な高レベル抽象化を提供します。さらに、cw-storage-plusクレートを使用することで、より効率的なストレージメカニズムを利用できます。
State構造体がcountとownerの両方を保持していることに注目してください。derive属性は以下の便利なトレイトを自動実装します:
Serialize:シリアライズを提供Deserialize:デシリアライズを提供Clone:構造体のコピーを可能にするDebug:構造体の文字列出力を可能にするPartialEq:等価比較を提供JsonSchema:JSONスキーマを自動生成
AddrはInjectiveの人間が読めるアドレスを参照し、injがプレフィックスとして付きます。例:inj1clw20s2uxeyxtam6f7m84vgae92s9eh7vygagt
InstantiateMsg
CosmWasmのInstantiateMsgに関するドキュメントを参照してください。
InstantiateMsgは、ユーザーがMsgInstantiateContractを通じてブロックチェーン上でコントラクトをインスタンス化する際にコントラクトに提供されます。これにより、コントラクトの設定と初期状態が提供されます。
Injectiveブロックチェーンでは、コントラクトコードのアップロードとコントラクトのインスタンス化は別々のイベントとして扱われます(Ethereumとは異なります)。これにより、少数の検証済みコントラクトアーキタイプが同じベースコードを共有しながら異なるパラメータで設定された複数のインスタンスとして存在できます(標準的なERC20を想像し、そのコードを使用する複数のトークンがあるイメージです)。
例
このコントラクトでは、コントラクト作成者が初期状態をJSONメッセージとして提供することが期待されます。以下のメッセージ定義では、初期カウントを表すcountパラメータを1つ保持しています。
メッセージ定義
コントラクトロジック
contract.rsで、最初のエントリポイントinstantiate()を定義します。ここでコントラクトがインスタンス化され、InstantiateMsgが渡されます。メッセージからcountを取得し、初期状態を設定します:
countにメッセージのcountを割り当てownerにMsgInstantiateContractの送信者を割り当て
ExecuteMsg
CosmWasmのExecuteMsgについて詳しくはドキュメントを参照してください。
ExecuteMsgはMsgExecuteContractを通じてexecute()関数に渡されるJSONメッセージです。InstantiateMsgとは異なり、ExecuteMsgはスマートコントラクトがユーザーに公開できるさまざまな種類の関数に対応するため、複数の種類のメッセージとして存在できます。execute()関数は、これらの異なる種類のメッセージを適切なメッセージハンドラロジックにルーティングします。
2つのExecuteMsgがあります:IncrementとReset。
Incrementは入力パラメータがなく、countの値を1増加させます。Resetは32ビット整数をパラメータとして受け取り、countの値を入力パラメータにリセットします。
例
Increment すべてのユーザーが現在のカウントを1増加させることができます。Reset
オーナーのみがカウントを特定の数値にリセットできます。実装の詳細はロジックを参照してください。メッセージ定義
ExecuteMsgには、コントラクトが理解できるさまざまな種類のメッセージを多重化するためにenumを使用できます。serde属性は属性キーをスネークケースおよび小文字に書き換えるため、シリアライズおよびデシリアライズ時にIncrementとResetの代わりにincrementとresetになります。
ロジック
execute()メソッドで、Rustのパターンマッチングを使用して受信したExecuteMsgを適切な処理ロジックにルーティングします。受信したメッセージに応じてtry_increment()またはtry_reset()の呼び出しにディスパッチします。
stateにあるアイテムを更新するためにストレージへの可変参照を取得します。次に、新しい状態でOk結果を返すことでstateのcountを更新します。最後に、Responseと共にOk結果を返すことで、成功を確認してコントラクトの実行を終了します。
QueryMsg
CosmWasmのQueryMsgに関するドキュメントを参照してください。
GetCountクエリメッセージはパラメータがなく、count値を返します。
実装の詳細はロジックを参照してください。
例
テンプレートコントラクトは1種類のQueryMsgのみをサポートしています:
GetCount
リクエスト:
メッセージ定義
コントラクトでデータクエリをサポートするには、QueryMsgのフォーマット(リクエストを表す)とクエリの出力構造(この場合はCountResponse)の両方を定義する必要があります。query()は構造化されたJSONを通じてユーザーに情報を返送するため、レスポンスの形式を公開する必要があります。詳細はJSONスキーマの生成を参照してください。
src/msg.rsに以下を追加します:
ロジック
query()のロジックはexecute()と類似していますが、query()はエンドユーザーがトランザクションを作成せずに呼び出されるため、env引数は省略されます。
ユニットテスト
ユニットテストはチェーンにコードをデプロイする前の最初の保証として実行すべきです。実行が速く、RUST_BACKTRACE=1フラグを使用することで失敗時に有用なバックトレースを提供できます:
src/contract.rsにあります。
コントラクトのビルド
コントラクトを理解しテストしたので、以下のコマンドを実行してコントラクトをビルドできます。これにより、次のステップでコントラクトを最適化する前に予備的なエラーを確認します。本番用Wasmバイトコードの準備の詳細を参照してください。
/codeにマウントし出力を最適化します($(pwd)の代わりに絶対パスを使用することもできます):
CosmWasmはARM64版コンパイラの使用を推奨していません。Intel/AMD版とは異なるWasmアーティファクトが生成されるためです。リリース/本番環境では、Intel/AMDオプティマイザでビルドされたコントラクトのみの使用が推奨されます。詳細はこちらを参照してください。
コマンド実行時に詳細はThe Cargo Bookを参照してください。
Unable to update registry `crates-io`エラーが表示される場合があります。コントラクトディレクトリ内のCargo.tomlファイルに以下の行を追加して再実行してください:PROJECT_NAME.wasmとWasmファイルのSha256ハッシュを含むchecksums.txtが格納されたartifactsディレクトリが生成されます。Wasmファイルは決定論的にコンパイルされます(同じDockerで同じGitコミットを実行すると、同一のSha256ハッシュを持つ同一のファイルが得られます)。
injectivedのインストール
injectivedはInjectiveに接続してInjectiveブロックチェーンとの対話を可能にするコマンドラインインターフェースおよびデーモンです。
CLIを使用してスマートコントラクトとローカルで対話する場合は、injectivedをインストールする必要があります。Injectiveのインストールガイドの手順に従ってください。
または、このチュートリアルを簡単にするためにDockerイメージが用意されています。
バイナリから
injectivedをインストールした場合は、Dockerコマンドを無視してください。
パブリックエンドポイントセクションで、MainnetおよびTestnetと対話するための正しい—node情報を確認できます。directory_to_which_you_cloned_cw-templateは絶対パスである必要があります。絶対パスはCosmWasm/cw-counterディレクトリ内でpwdコマンドを実行することで簡単に取得できます。
新しいターミナルを開き、Dockerコンテナに入ってチェーンを初期化します:
jq依存パッケージを追加します:
testuserというテストユーザーを追加します(プロンプトが表示されたらパスワードとして12345678を使用)。テストユーザーは、テストネットでメッセージの署名に使用する新しいプライベートキーの生成にのみ使用します:
Injectiveテストfaucetを使用して、生成したテストアドレスにテストネット資金をリクエストできます。
testuserがInjective Testnetに正常に作成されました。faucetからテストネット資金をリクエストした後、残高も保有しているはずです。
確認するには、Injective Testnet Explorerでアドレスを検索して残高を確認してください。
または、Bank残高のクエリまたはcurlで確認することもできます:
Wasmコントラクトのアップロード
前のステップでコンパイルした.wasmファイルをInjective Testnetにアップロードします。メインネットの手順は異なり、ガバナンスプロポーザルが必要です。
txhashのトランザクションを探してください。トランザクションタイプはMsgStoreCodeのはずです。
Injective Testnet上のすべての保存コードはCodeで確認できます。
保存したコードを見つけるには複数の方法があります:
- Injective Explorerのコード一覧でTxHashを探します。最新のものである可能性が高いです。
injectivedを使用してトランザクション情報をクエリします。
txhashを使用してコントラクトがデプロイされたことを確認します。
code_idが290であることがわかります:
code_idを環境変数としてエクスポートしましょう。コントラクトのインスタンス化に必要です。このステップはスキップして後で手動で追加することもできますが、IDを記録しておいてください。
JSONスキーマの生成
Wasmのinstantiate、execute、queryはJSONを受け取りますが、これだけでは使用に十分な情報ではありません。期待されるメッセージのスキーマをクライアントに公開する必要があります。
JSONスキーマの自動生成を利用するには、スキーマが必要な各データ構造を登録する必要があります。
Stateに対応する5つのファイルが./schemaに出力されます。
これらのファイルは標準的なJSON Schemaフォーマットで、さまざまなクライアントサイドツールで使用できます。コーデックの自動生成や、定義されたスキーマに対する受信JSONの検証に利用できます。
スキーマを生成し(こちらで確認できます)、次のステップに必要なので内容を理解しておいてください。
コントラクトのインスタンス化
コードがInjective上にアップロードされたので、コントラクトをインスタンス化して対話できるようにします。CosmWasmでは、コントラクトコードのアップロードとコントラクトのインスタンス化は別々のイベントとして扱われます。
コントラクトのクエリ
先に説明した通り、唯一のQueryMsgはget_countです。
countは99です。
同じコントラクトをクエリすると、他のユーザーがカウントを増加またはリセットして操作している可能性があるため、異なるレスポンスが返される場合があります。
コントラクトの実行
カウンターを増加させてコントラクトと対話してみましょう。yes 12345678 | は、自動的にパスフレーズを injectived tx wasm execute の入力にパイプする(渡す)ため、手動で入力する必要はありません。
Cosmosメッセージ
カスタムスマートコントラクトロジックの定義に加えて、CosmWasmではコントラクトが基盤となるCosmos SDK機能と対話することもできます。一般的なユースケースの1つは、Cosmos SDKのBankモジュールを使用してコントラクトから指定アドレスにトークンを送信することです。例:Bank Send
BankMsg::Sendメッセージを使用すると、コントラクトが別のアドレスにトークンを転送できます。報酬の配布やユーザーへの返金など、さまざまなシナリオで役立ちます。
注意: 資金の送信と別のコントラクト上の関数の実行を同時に行いたい場合は、BankMsg::Sendを使用しないでください。代わりに、WasmMsg::Executeを使用して、それぞれのfundsフィールドを設定してください。
メッセージの構築
コントラクトのexecute関数内でBankMsg::Sendメッセージを構築できます。このメッセージでは受信者アドレスと送信額を指定する必要があります。構築方法の例を以下に示します:
スマートコントラクトでの使用
コントラクトでは、ExecuteMsg enumにBank送金機能を処理する新しいバリアントを追加できます:execute関数にこのメッセージを処理するケースを追加します:
