This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
Starknet の改良された文法の完全な解釈
原文:Improved Starknet Syntax
翻訳・校正:「Starknet Chinese Community」
概要
Cairo コンパイラのバージョン 2 では、コードをより明示的かつ安全にするために Starknet 構文に変更が導入されています。スマート コントラクトのパブリック インターフェイスはトレイトを使用して定義され、ストレージへのアクセスは ContractState トレイトを通じて行われます。プライベート メソッドは、パブリック インターフェイスとは異なる実装で定義する必要があります。イベントは列挙型として定義されるようになり、各バリアントは同じ名前の構造体になります。
免責事項: ここで使用されている用語は、Cairo コンパイラのさまざまなバージョンを指しており、どの用語を使用するのが最適であるかについて Starknet コミュニティがまだ議論しているため、構文は暫定的なものです。決定したら、この記事もそれに応じて更新されます。
コンパイラ v2
つい先週、Cairo コンパイラーの新しいメジャー バージョン 2.0.0-rc0 が Github でリリースされました。新しいコンパイラは Starknet プラグインに大幅な改善をもたらし、コードをより安全、より明示的、より再利用可能にしました。この新しいバージョンのコンパイラはまだ統合環境で開発中であるため、Starknet テストネットまたはメインネット ではまだサポートされていないことに注意してください。
この記事の目的は、Cairo コンパイラー バージョン 1.x 用に作成された Starknet スマート コントラクトを、コンパイラー バージョン 2.x と互換性のあるスマート コントラクトに書き換える方法を示すことです。私たちの出発点は、Cario コンパイラー バージョン 1.x と互換性のある、前の記事で作成した Ownable スマート コントラクトです。
[contract] mod 所有可能 {starknet::ContractAddress を使用; starknet::get_caller_address を使用;
[event] fn OwnershipTransferred(previous_owner: ContractAddress, new_owner: ContractAddress) {}
struct Storage {所有者: ContractAddress,}
[constructor] fnconstructor() {letdeployer = get_caller_address();owner::write(deployer);}
[view] fn get_owner() -> ContractAddress {owner::read()}
[external] fn transfer_ownership(new_owner: ContractAddress) {only_owner();letPrevious_owner = owner::read();owner::write(new_owner);OwnershipTransferred(previous_owner, new_owner) ;}
fnonly_owner() {let caller = get_caller_address();assert(caller == owner::read(), '呼び出し者は所有者ではありません');
プロジェクト設定
Protostar はまだコンパイラー v2 をサポートしていないため、この記事はそれをサポートする Scarb プレリリース (バージョン 0.5.0-alpha.1) に依存します。 Scarb の特定のバージョンをインストールするには、次のコマンドを使用できます。
$ --proto '=https' --tlsv1.2 -sSf | bash -s -- -v 0.5.0-alpha.1
インストールが完了したら、正しいバージョンであることを確認してください。
$scarb --version>>>scarb 0.5.0-alpha.1 (546dad33d 2023-06-19)cairo:2.0.0-rc3()
Scarb プロジェクトを作成できるようになりました。
$ scarb 新しいカイロ 1_v2$cdcairo1_v2
以下のようなフォルダー構造が得られるはずです。
$ ツリー .>>>.っていつ── Scarb.toml── src──lib.cairo
Scarb が Starknet スマート コントラクトをコンパイルするには、Starknet プラグインを依存関係として有効にする必要があります。
// Scarb.toml... [dependencies] starknet="2.0.0-rc3"
セットアップが完了したら、src/lib.cairo に移動してスマート コントラクトの作成を開始できます。
ストレージとコンストラクター
Cairo コンパイラのバージョン 2 では、スマート コントラクトは依然としてコントラクト属性で注釈が付けられたモジュールによって定義されますが、今回のみ属性はそれを定義するプラグイン (この場合は starknet) にちなんで名付けられます。
#[starknet::contract]mod 所有可能 {}
内部ストレージは依然として Storage と呼ばれる構造として定義されていますが、今回のみ storage 属性で注釈を付ける必要があります。
#[starknet::contract]mod 所有可能 {use super::ContractAddress; # [storage] struct Storage {所有者: ContractAddress,
コンストラクターを定義するには、v1 で行ったように、関数にコンストラクター属性の注釈を付けます。利点は、関数に任意の名前を付けることができ、v1 のように「コンストラクター」と呼ぶ必要がないことです。必須ではありませんが、慣例に反してこの関数を「コンストラクター」と呼びますが、別の方法で呼び出すこともできます。
もう 1 つの重要な変更点は、コンストラクターが自動的に ContractState への参照を渡し、変数 (この場合は「オーナー」) を格納するための仲介者として機能するようになったことです。
#[starknet::contract]mod 所有可能 {use super::ContractAddress; # [storage] struct Storage {所有者: ContractAddress,} # [constructor] fn constructionor(ref self: ContractState) {letdeployer = get_caller_address();self.owner.write(deployer);
ストレージの書き込みと読み取りの構文は v1 から変更されていることに注意してください。 owner::write() を行う前は、self.owner.write() を行うようになりました。ストレージからの読み取りにも同じことが当てはまります。
ちなみに、ContractState 型は手動でスコープに組み込む必要はなく、プレリュードに含まれています。
パブリックメソッド
Cairo コンパイラのバージョン 1 との重要な違いは、starknet::interface 属性で注釈が付けられた特性を使用して、スマート コントラクトのパブリック インターフェイスを明示的に定義する必要があることです。
starknet::ContractAddress を使用します。
#[starknet::interface]trait OwnableTrait { fn transfer_ownership(ref self: T, new_owner: ContractAddress); fn get_owner(self: @T) -> ContractAddress;}
#[starknet::contract]mod 所有可能 { ...}
v1 の元のコードを覚えている場合、スマート コントラクトには 2 つの「パブリック」メソッド (get_owner および transfer_ownership) と 1 つの「プライベート」メソッド (only_owner) があります。この機能はパブリック メソッドのみを扱い、どのメソッドがコントラクトの状態を変更できるか、どのメソッドが変更できないかを示す「external」属性や「view」属性には依存しません。代わりに、これはパラメーター self の型によって明示的に行われるようになりました。
メソッドが ContractStorage への参照を必要とする場合 (これは、実装されると汎用 T が行います)、そのメソッドはスマート コントラクトの内部状態を変更できます。これは、以前は「外部」メソッドと呼ばれていました。一方、メソッドが ContractStorage のスナップショットを必要とする場合、メソッドは読み取りのみが可能で、変更はできません。これは、以前は「ビュー」メソッドと呼ばれていたものです。
これで、キーワード impl を使用して定義したばかりの特性の実装を作成できます。 Cairo は、実装に名前があるという点で Rust とは異なります。
starknet::ContractAddress を使用します。
#[starknet::interface]trait OwnableTrait { fn transfer_ownership(ref self: T, new_owner: ContractAddress); fn get_owner(self: @T) -> ContractAddress;}
#[starknet::contract]mod Ownable { ... #[external(v0)] impl OwnableImpl of super::OwnableTrait { fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { let prev_owner = self.owner.read(); self.owner.write(new_owner); }
fn get_owner(self: @ContractState) -> ContractAddress { self.owner.read() }
スマート コントラクトを定義するモジュール内にトレイトの実装を作成し、ContractState 型をジェネリック型 T として渡し、コンストラクターのようにストレージにアクセスできるようにします。
私たちの実装には属性 external(v0) の注釈が付けられています。属性のバージョン 0 は、以前と同様に、セレクターがメソッド名のみから派生することを意味します。欠点は、スマート コントラクトに別のトレイトの別の実装を定義し、2 つのトレイトが偶然そのメソッドの 1 つに同じ名前を使用した場合、セレクターの重複によりコンパイラがエラーをスローすることです。
このプロパティの将来のバージョンでは、競合を防ぐためにセレクターを評価する新しい方法が追加される可能性がありますが、まだ機能していません。現在、外部プロパティのバージョン 0 のみを使用できます。
プライベートメソッド
また、スマート コントラクトの別のメソッド (only_owner) を定義する必要もあります。このメソッドは、呼び出した人がスマート コントラクトの所有者であるべきかどうかを確認します。
これは外部からの呼び出しが許可されていないプライベート メソッドであるため、OwnableTrait (スマート コントラクトのパブリック インターフェイス) の一部として定義することはできません。代わりに、generate_trait 属性を使用して、自動生成された特性の新しい実装を作成します。
...#[starknet::contract]mod Ownable { ... #[generate_trait] impl PrivateMethodsTrait の PrivateMethods { fnonly_owner(self: @ContractState) { let caller = get_caller_address(); assert(caller == self.owner.read(), '呼び出し元は所有者ではありません'); }
必要に応じて self.only_owner() を呼び出すことで、only_owner メソッドを使用できるようになりました。
#[starknet::contract]mod Ownable { ... #[external(v0)] impl OwnableImpl of super::OwnableTrait { fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { self.only_owner (); ... } ... }
#[generate_trait] impl PrivateMethods of PrivateMethodsTrait { fnonly_owner(self: @ContractState) { ... }
### イベント
Cairo v1 では、イベントは本体を持たず、event 属性で注釈が付けられた単なる関数でしたが、v2 では、イベントは同じ属性で注釈が付けられた列挙型でしたが、いくつかの追加機能の派生を使用して実装されました。
...#[starknet::contract]mod 所有可能 { ... # [event] #[derive(Drop, starknet::Event)] enum イベント { OwnershipTransferred: OwnershipTransferred, }
#[derive(Drop, starknet::Event)] struct OwnershipTransferred { # [key] prev_owner: 契約アドレス、# [key] new_owner: 契約アドレス、
イベント列挙型の各バリアントは、同じ名前の構造体である必要があります。この構造では、オプションの key 属性を使用して、発行するすべての値を定義し、Starknet にインデックスを作成する値をシステムに通知して、インデクサーがより高速に検索および取得できるようにします。この場合、2 つの値 (prev_owner と new_owner) にインデックスを付けます。
ContractState トレイトは、イベントの発行に使用できる発行メソッドを定義します。
...#[starknet::contract]mod Ownable { ... #[external(v0)] impl OwnableImpl of super::OwnableTrait { fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { .. .self.emit(Event::OwnershipTransferred(OwnershipTransferred { prev_owner: prev_owner, new_owner: new_owner, })); } ... } ...}
この最後の機能により、所有可能なスマート コントラクトの v1 から v2 への移行が完了しました。完全なコードを以下に示します。
starknet::ContractAddress を使用します。
#[starknet::interface]trait OwnableTrait { fn transfer_ownership(ref self: T, new_owner: ContractAddress); fn get_owner(self: @T) -> ContractAddress;}
#[starknet::contract]mod 所有可能 { super::ContractAddress を使用します。 starknet::get_caller_address を使用します。
[event] #[derive(Drop, starknet::Event)] enum イベント { OwnershipTransferred: OwnershipTransferred, }
#[derive(Drop, starknet::Event)] struct OwnershipTransferred { # [key] prev_owner: 契約アドレス、# [key] new_owner: ContractAddress, }
[storage] struct Storage { 所有者: ContractAddress, }
[constructor] fnconstructor(ref self:ContractState) { letdeployer = get_caller_address(); self.owner.write(deployer); }
#[external(v0)] impl OwnableImpl of super::OwnableTrait { fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { self.only_owner(); let prev_owner = self.owner.read(); self.owner.write(new_owner); self.emit(Event::OwnershipTransferred(OwnershipTransferred { prev_owner: prev_owner, new_owner: new_owner, })); }
fn get_owner(self: @ContractState) -> ContractAddress { self.owner.read() } }
#[generate_trait] impl PrivateMethods of PrivateMethodsTrait { fnonly_owner(self: @ContractState) { let caller = get_caller_address(); assert(caller == self.owner.read(), '呼び出し元は所有者ではありません'); }
このコードは Github にもあります。
### 結論は
Cairo コンパイラのバージョン 2 は、スマート コントラクト コードを Cairo 自体とより一貫性を持たせ、ひいてはより Rust に似た新しい構文を Starknet に導入しました。より煩雑なコードを犠牲にしても、セキュリティ上の利点はトレードオフの価値があります。
この記事では、新しい構文、特に他のスマート コントラクトとの相互作用についてすべて触れたわけではありませんが、コンパイラーの変更ログを読んだり、フォーラムのこの投稿を読んだり、StarkWare の YouTube チャンネルのビデオを見て詳細を学ぶことができます。詳細については、こちらをご覧ください。
この新しいバージョンのコンパイラは、数週間以内に Starknet のテストネットで利用可能になり、数週間以内にメインネットで利用可能になる予定です。そのため、このコードはまだ動作しないため、まだデプロイしようとしないでください。
カイロはますます良くなっています。
リソース