Migrating a contract from Cairo 0 to Cairo
With the v2.0.0 release of the Cairo compiler, the Starknet contract syntax has evolved, affecting the organization of functions, storage, and events.
For more information on the latest syntax changes, see the Community Forum post Cairo 1: Contract Syntax is Evolving.
-
A contract written with the Cairo compiler v1.1.0
-
The most recent version of the Cairo compiler
-
Change the contract annotation from
#[contract]
to#[starknet::contract]
. For example::-
old
-
new
#[contract] mod CounterContract { ... }
#[starknet::contract] mod CounterContract { ... }
-
-
Annotate the
Storage
struct with the#[storage]
attribute. For example:-
old
-
new
struct Storage { counter: u128, other_contract: IOtherContractDispatcher }
#[storage] struct Storage { counter: u128, other_contract: IOtherContractDispatcher }
-
-
Gather your contract’s
external
andview
function signatures under a trait annotated with#[starknet::interface]
as follows:-
Add a generic parameter to the trait. In the following example, the name
TContractState
represents the state of your contract. -
For view functions, add the
self: @TContractState
argument. -
For external functions, add the
ref self: TContractState
argument. -
Static functions that do not touch storage or emit events do not require an additional argument.
For example:
-
old
-
new
#[contract] mod CounterContract { #[external] fn increase_counter(amount: u128) { ... } #[external] fn decrease_counter(amount: u128) { ... } #[view] fn get_counter() -> u128 { ... } }
#[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 { ... }
-
-
-
Add the external and view function bodies under an
impl
of the interface trait, and mark theimpl
with the[external(v0)]
attribute, which generates the type of dispatcher that is used to call the contract.For example:
-
old
-
new
#[contract] mod CounterContract { #[external] fn increase_counter(amount: u128) { ... } #[external] fn decrease_counter(amount: u128) { ... } #[view] fn get_counter() -> u128 { ... } }
#[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 { #[external(v0)] impl CounterContract of super::ICounterContract<ContractState> { fn increase_counter(ref self: ContractState, amount: u128) { ... } fn decrease_counter(ref self: ContractState, amount: u128) { ... } fn get_counter(self: @ContractState) -> u128 { ... } } }
-
-
Replace the
#[abi]
attribute with#[starknet::interface]
.While it doesn’t affect the generated code, adding to the trait a generic parameter
T
representing the contract’s state, and adding theref self: T
argument to external functions andself: @T
argument for view functions makes the implementation more complete.For example:
-
old
-
new
#[abi] trait IOtherContract { fn decrease_allowed() -> bool; }
#[starknet::interface] trait IOtherContract<TContractState> { fn decrease_allowed(self: @TContractState) -> bool; }
-
-
Modify storage accesses to happen through
ContractState
or@ContractState
.No external functions in the contract that access storage also need to get it as an argument.
For example:
-
old
-
new
let current = counter::read();
let current = self.counter.read();
-
-
Unify all the contract’s events under the
Event
enum, and add a corresponding struct for every variant.All the structs must derive the
Event
trait, and each member type must implement theSerde
trait.For example:
-
old
-
new
#[event] fn counter_increased(amount: u128) {} #[event] fn counter_decreased(amount: u128) {}
#[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 }
-
-
Emit events via the
ContractState
type. For example:-
old
-
new
fn increase_counter(amount: u128) { ... counter_increased(amount); }
fn increase_counter(ref self: ContractState, amount: u128) { ... self.emit(Event::CounterIncreased(CounterIncreased { amount })); }
-