Skip to content

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:

  1. Define the target contract's interface as a trait with #[starknet::interface] (IContract)
  2. Import the generated dispatcher types (IContractDispatcher and IContractDispatcherTrait)
  3. 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.

Powered By Nethermind