Contract migration guide
With the v2.0.0 release of the Cairo compiler, the Starknet contract syntax has evolved. This affects how external functions, storage, and events are organized inside the contract.
This page highlights the technical steps required to migrate from the old Starknet contract syntax to the new.
For a comprehensive breakdown of the changes, see the community forum post.
New contract syntax - concrete steps for migrating
Given a contract written with the previous compiler version (v1.1.0), you can follow the steps below in order to make it compatible with the new syntax.
Contract anotation
Outside the contract module, Starknet related attributes are expected to have the starknet::
prefix.
-
old
-
new
#[contract]
mod CounterContract {
...
}
#[starknet::contract]
mod CounterContract {
...
}
Storage anotation
Anotate the Storage
struct with the #[storage]
attribute
-
old
-
new
struct Storage {
counter: u128,
other_contract: IOtherContractDispatcher
}
#[storage]
struct Storage {
counter: u128,
other_contract: IOtherContractDispatcher
}
Contract interface
Gather your contract’s external and view function signatures under a trait annotated with #[starknet::interface]
:
-
Add a generic parameter to the trait, here we use the name
TContractState
as it stands for 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 addition argument
-
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 interface Impl
Add the external and view function bodies under an impl of the interface trait, and mark the impl with the [external(v0)]
attribute
-
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
These attributes are responsible for generating the dispatcher type, used to call the contract.
Replace the [abi]
attribute with [starknet::interface]
. While it doesn’t affect the generated code, we recommended adding to the trait a generic parameter T
representing the contract’s state,
and adding the ref self: T
argument to external functions and self: @T
argument for view functions.
-
old
-
new
#[abi]
trait IOtherContract {
fn decrease_allowed() -> bool;
}
#[starknet::interface]
trait IOtherContract<TContractState> {
fn decrease_allowed(self: @TContractState) -> bool;
}
Storage access
Modify storage access to happen through ContractState
or @ContractState
(none external
functions in the contract that access storage also need to get it as an argument).
-
old
-
new
let current = counter::read();
let current = self.counter.read();
Events definition
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 has to implement the Serde
trait)
-
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
}