Cairo 最近リリースされたバージョン 2.0.0-rc0 による文法の変更#
一部関連情報のチャンネル#
GitHub の Cairo プロジェクトタスク進捗ボード:https://github.com/orgs/starkware-libs/projects/1/views/1
GitHub リリース情報リスト:https://github.com/starkware-libs/cairo/releases
一般的な変更#
(1). 整数リテラルはもはやその型の変数を指定する必要がない
// 以前
let n:u8 = 8_u8;
// 現在は接尾辞を省略でき、付けてもエラーにならない
let n:u8 = 8;
(2). 辞書の作成
以前
use dict::Felt252DictTrait;
fn main(){
let mut dict = Felt252DictTrait::new();
}
現在は Default trait を使用
use dict::Felt252DictTrait;
use traits::Default;
fn main(){
let mut map : Felt252Dict<felt252> = Default::default();
}
コントラクトの文法変更#
まず旧バージョンのコントラクトコードを示します:
#[abi]
trait IOtherContract {
fn decrease_allowed() -> bool;
}
#[contract]
mod CounterContract {
use starknet::ContractAddress;
use super::{
IOtherContractDispatcher,
IOtherContractDispatcherTrait,
IOtherContractLibraryDispatcher
};
struct Storage {
counter: u128,
other_contract: IOtherContractDispatcher
}
#[event]
fn counter_increased(amount: u128) {}
#[event]
fn counter_decreased(amount: u128) {}
#[constructor]
fn constructor(initial_counter: u128, other_contract_addr: ContractAddress) {
counter::write(initial_counter);
other_contract::write(IOtherContractDispatcher { contract_address: other_contract_addr });
}
#[external]
fn increase_counter(amount: u128) {
let current = counter::read();
counter::write(current + amount);
counter_increased(amount);
}
#[external]
fn decrease_counter(amount: u128) {
let allowed = other_contract::read().decrease_allowed();
if allowed {
let current = counter::read();
counter::write(current - amount);
counter_decreased(amount);
}
}
#[view]
fn get_counter() -> u128 {
counter::read()
}
}
以下のコードは新文法のコントラクトコードです#
(1). external 関数は特定の trait と対応する impl に集中
上記のコントラクトには 3 つの公開関数があります:increase_counter
、decrease_counter
、および get_counter
。
- まずこれらの公開関数は、
#[starknet::interface]
で識別された trait の中で関数シグネチャ(または関数セレクタ)を定義します。 - この trait にはジェネリック変数
TContractState
が含まれており、コントラクトのストレージ構造体を表します。 - これらの公開関数はすべて
TContractState
のメソッドです。 - メソッドの最初の引数は
self
です;もし view メソッドであれば、self はTContractState
のスナップショットself: @TContractState
です;状態を変更するメソッドであれば、self はTContractState
の参照ref self: TContractState
です。その後にメソッドの他の引数が続きます。 - 公開関数のロジックは、
#[external(v0)]
で識別された impl に書かれています。
以上が公開関数に関する新しい文法ルールで、変更はかなり大きいです。コード内のコメントにはさらに詳細があります:
/// @notice 現在のコントラクトの外部インターフェースを定義し、すべてのexternal関数はこのtraitのimplに定義されます
#[starknet::interface]
trait ICounterContract<TContractState> {
fn increase_counter(ref self: TContractState, amount: u128);
fn decrease_counter(ref self: TContractState, amount: u128);
fn get_counter(self: @TContractState) -> u128;
}
#[starknet::contract]
mod CounterContract {
...
/// @notice ここで全てのexternal関数を定義し、ContractStateはコントラクトのストレージ状態を表します
/// @dev スナップショットを渡すか参照を渡すかでview関数かどうかを区別します。スナップショットならview関数です
/// @dev コントラクトの文法はまだ更新中で、v0は現在のコントラクトが将来のアップグレード後の新しいバージョンのコンパイラと互換性を持つためのものです
#[external(v0)]
impl CounterContract of super::ICounterContract<ContractState> {
// 渡されるのはスナップショットなのでview関数です
fn get_counter(self: @ContractState) -> u128 {
self.counter.read()
}
// 渡されるのは参照なのでコントラクトのストレージ状態を変更します
fn increase_counter(ref self: ContractState, amount: u128) {
// コントラクトの状態変数を読み取ります
let current = self.counter.read();
// コントラクトの状態変数を変更します
self.counter.write(current + amount);
// ContractStateは同時にイベントをemitする能力も提供します
self.emit(Event::CounterIncreased(CounterIncreased { amount }));
}
fn decrease_counter(ref self: ContractState, amount: u128) {
let allowed = self.other_contract.read().decrease_allowed();
if allowed {
let current = self.counter.read();
self.counter.write(current - amount);
self.emit(Event::CounterDecreased(CounterDecreased { amount }));
}
}
}
...
}
(2). コントラクトの外部呼び出し
公開関数の書き方が変更されたため、コントラクトの外部呼び出しも自然に変更されます。
- 元々
#[abi]
で識別されていた部分は、#[starknet::interface]
で識別されるようになりました。 - trait はジェネリック trait を使用し、使い方は上記の内容と一致します。
/// @notice 外部コントラクトインターフェースの定義
/// @dev #[abi] を #[starknet::interface] に置き換え
/// @dev ジェネリックtraitを使用し、TContractStateはコントラクト状態を表すジェネリック名です
#[starknet::interface]
trait IOtherContract<TContractState> {
fn decrease_allowed(self: @TContractState) -> bool;
}
(3). Event の変更
Event の変更もかなり大きく、現在は Enum と struct を使用して表現されています。
- すべての event は
#[event]
と#[derive(Drop, starknet::Event)]
で識別された enum の中で定義されています。 - 各 event は、渡されるフィールドと型を表すために個別の構造体で表現され、
#[derive(Drop, starknet::Event)]
で識別される必要があります。 - event の呼び出しには
ContractState
を使用します:self.emit(Event::CounterDecreased(CounterDecreased { amount }));
/// @notice コントラクトのeventも非常に大きな変更を受けました
/// @dev すべてのeventは #[event] で識別され、名前が Event の enum の中に定義されています
/// @dev 各eventの定義構造は: event_name: event_type、event_type はイベント内のパラメータ構造を格納します
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
CounterIncreased: CounterIncreased,
CounterDecreased: CounterDecreased
}
#[derive(Drop, starknet::Event)]
struct CounterIncreased {
amount: u128
}
#[derive(Drop, starknet::Event)]
struct CounterDecreased {
amount: u128
}