Calling Other Contracts
In Starknet, contracts can interact with each other through contract calls. The recommended way to make these calls is using the dispatcher pattern, which provides type safety and better error handling.
Understanding Dispatchers
A dispatcher is an automatically generated struct that handles the serialization and deserialization of contract calls. To use dispatchers:
- Define the target contract's interface as a trait with
#[starknet::interface]
(IContract
) - Import the generated dispatcher types (
IContractDispatcher
andIContractDispatcherTrait
) - Create a dispatcher instance with the target contract's address
Let's look at a practical example where one contract (Caller
) interacts with another (Callee
). The Callee
contract stores a value that can be set and retrieved:
// This will automatically generate ICalleeDispatcher and ICalleeDispatcherTrait
#[starknet::interface]
trait ICallee<TContractState> {
fn set_value(ref self: TContractState, value: u128);
fn get_value(self: @TContractState) -> u128;
}
#[starknet::contract]
mod Callee {
use starknet::storage::{StoragePointerWriteAccess, StoragePointerReadAccess};
use super::ICallee;
#[storage]
struct Storage {
value: u128,
}
#[abi(embed_v0)]
impl ICalleeImpl of ICallee<ContractState> {
fn set_value(ref self: ContractState, value: u128) {
self.value.write(value);
}
fn get_value(self: @ContractState) -> u128 {
self.value.read()
}
}
}
The Caller
contract demonstrates how to use the dispatcher to interact with Callee
:
#[starknet::contract]
mod Caller {
// Import the generated dispatcher types for the Callee contract
use super::{ICalleeDispatcher, ICalleeDispatcherTrait};
use super::ICaller;
use starknet::ContractAddress;
#[storage]
struct Storage {}
#[abi(embed_v0)]
impl ICallerImpl of ICaller<ContractState> {
fn set_value_from_address(ref self: ContractState, addr: ContractAddress, value: u128) {
// Create a dispatcher instance and call the target contract
ICalleeDispatcher { contract_address: addr }.set_value(value);
}
}
}
Key Points:
- The
#[starknet::interface]
attribute automatically generates the dispatcher types - Dispatchers handle all the low-level details of contract interaction
- Contract calls are type-safe and checked at compile time
- Each contract maintains its own storage and state
For more details about dispatchers, check out the Cairo Book.