StarknetAstro

StarknetAstro

Syntax changes introduced in the recently released version 2.0.0-rc0 of Cairo

Syntax changes introduced by the recently released version 2.0.0-rc0 of Cairo#

Channels for relevant information#

GitHub Cairo project task progress board: https://github.com/orgs/starkware-libs/projects/1/views/1

GitHub release information list: https://github.com/starkware-libs/cairo/releases

General changes#

(1). Integer literals no longer need to specify the variable type

// Before
let n:u8 = 8_u8;
// Now, the suffix can be omitted, but including it won't cause an error
let n:u8 = 8;

(2). Creating dictionaries

Before

use dict::Felt252DictTrait;
fn main(){
    let mut dict = Felt252DictTrait::new();
}

Now, use the Default trait

use dict::Felt252DictTrait;
use traits::Default;

fn main(){
    let mut map : Felt252Dict<felt252> = Default::default();
}

Syntax changes for contract writing#

First, here is the contract code for the old version:

#[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()
   }
}

The following code is for the new syntax of the contract#

(1). External functions are centralized in a specific trait and its corresponding impl

The above contract has three public functions: increase_counter, decrease_counter, and get_counter.

  • First, these public functions will be defined in a trait marked with #[starknet::interface], which specifies the function signature (or function selector).
  • This trait contains a generic variable TContractState, which represents the contract's storage struct.
  • These public functions are methods of TContractState.
  • In the methods, the first parameter is self. If it is a view method, self is the snapshot of TContractState (self: @TContractState). If it is a state-changing method, self is the reference of TContractState (ref self: TContractState). Other parameters follow.
  • The logic of the public functions is written in an impl marked with #[external(v0)].

The syntax changes regarding public functions are quite significant. The comments in the code provide more details:

/// @notice Defines the external interface of the current contract, all external functions will be defined in the impl of this trait
#[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 Define all external functions here, ContractState represents the contract's storage state
    /// @dev Distinguish between view functions and non-view functions by passing snapshot or reference as the first parameter
    /// @dev The contract syntax is still being updated, v0 is used to ensure compatibility of current contracts with future versions of the compiler
    #[external(v0)]
    impl CounterContract of super::ICounterContract<ContractState> {
        // Takes a snapshot, so it is a view function
        fn get_counter(self: @ContractState) -> u128 {
            self.counter.read()
        }

        // Takes a reference, so it modifies the contract's storage state
        fn increase_counter(ref self: ContractState, amount: u128) {
            let current = self.counter.read();
            self.counter.write(current + amount);
            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). External contract calls

Since the syntax for public functions has changed, external contract calls have naturally changed as well.

  • The part previously marked with #[abi] is now marked with #[starknet::interface].
  • The trait uses a generic trait, which is used in the same way as described above.
/// @notice Definition of the external contract interface
/// @dev Replace #[abi] with #[starknet::interface]
/// @dev Use a generic trait, where TContractState is the generic name representing the contract state
#[starknet::interface]
trait IOtherContract<TContractState> {
    fn decrease_allowed(self: @TContractState) -> bool;
}

(3). Changes to events

There are significant changes to events as well, now represented using enums and structs.

  • All events are defined in an enum marked with #[event] and #[derive(Drop, starknet::Event)].
  • Each event is represented by a separate struct that holds the fields and types of the event, also marked with #[derive(Drop, starknet::Event)].
  • Event calls use ContractState: self.emit(Event::CounterDecreased(CounterDecreased { amount }));
    /// @notice Events in the contract have also undergone significant changes
    /// @dev Define all events in an enum named Event, marked with #[event]
    /// @dev Each event is defined with a structure named event_type to hold the parameters of the event, also marked with #[derive(Drop, starknet::Event)]
    #[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
    }
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.