Cairo 最近 released 的版本 2.0.0-rc0 產生的語法變化#
一些相關資訊的渠道#
GitHub 的 Cairo 項目任務進度版圖:https://github.com/orgs/starkware-libs/projects/1/views/1
GitHub release 資訊列表: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 中
以上合約中有三個公開的函數:increase_counter
、decrease_counter
和 get_counter
。
- 首先這些公開的函數,會在由
#[starknet::interface]
標識的 trait 中定義出函數簽名(或稱函數選擇器)。 - 這個 trait 中包含一個泛型變量
TContractState
,它代表合約的 storage 結構體。 - 這些公開的函數都是
TContractState
的方法(method)。 - 方法中,第一個參數是
self
;如果是 view 方法,self 就是TContractState
的 snapshotself: @TContractState
;如果是會更改狀態的方法,self 就是TContractState
的 referenceref 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代表合約的storage狀態
/// @dev 通過傳遞 snapshot 還是 reference 來區分是否是view函數,如果是snapshot,那麼就是view函數
/// @dev 合約語法還在更新中,v0是為了現在的合約可以兼容將來升級後的新版本編譯器
#[external(v0)]
impl CounterContract of super::ICounterContract<ContractState> {
// 傳入的是snapshot,因此是view函數
fn get_counter(self: @ContractState) -> u128 {
self.counter.read()
}
// 傳入的是reference,所以會更改合約 storage狀態
fn increase_counter(ref self: ContractState, amount: u128) {
// 讀取合約狀態變量
let current = self.counter.read();
// 更改合約狀態變量
self.counter.write(current + amount);
// ContractState 同時給出 emit event 的能力
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 使用 #[starknet::interface] 替換 #[abi]
/// @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
}