Migrating a contract from Cairo v1 to Cairo v2
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
-
The most recent version of the Cairo compiler
-
Change the contract annotation from
#[contract]
to#[starknet::contract]
. For example::-
Cairo v1
-
Cairo v2
#[contract] mod CounterContract { ... }
#[starknet::contract] mod CounterContract { ... }
-
-
Annotate the
Storage
struct with the#[storage]
attribute. For example:-
Cairo v1
-
Cairo v2
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:
-
Cairo v1
-
Cairo v2
#[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:
-
Cairo v1
-
Cairo v2
#[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:
-
Cairo v1
-
Cairo v2
#[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:
-
Cairo v1
-
Cairo v2
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:
-
Cairo v1
-
Cairo v2
#[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:-
Cairo v1
-
Cairo v2
fn increase_counter(amount: u128) { ... counter_increased(amount); }
fn increase_counter(ref self: ContractState, amount: u128) { ... self.emit(Event::CounterIncreased(CounterIncreased { amount })); }
-