Skip to content
This repository has been archived by the owner on Sep 25, 2024. It is now read-only.

Latest commit

 

History

History
110 lines (84 loc) · 4.37 KB

14. Calling other contracts.md

File metadata and controls

110 lines (84 loc) · 4.37 KB

Calling other contracts

One common pattern we find in smart contract development, is the ability of contracts to interact with other smart contracts on the blockchain using interfaces.

Now if you come from a Solidity background, you'd already know we could call functions of contracts we don't own using Interfaces, but doing this in Cairo 1, is a little bit different from what you're probably used to in Solidity or even Cairo 0.x

Interfaces in Cairo 1

Here's what a typical contract interface in Cairo 1.0 looks like:

#[starknet::interface]
trait IENS<TContractState> {
    fn store_name(ref self: TContractState, _name: felt252);
    fn get_name(self: @TContractState, _address: ContractAddress) -> felt252;
}

An interface in Cairo 1, is a trait with the [starknet::interface] attribute, which specifies the function declaration (name, parameters, visibility and return value) without including the function body.

The Contract Interface Dispatcher

For each contract interface you create in Cairo, two dispatchers are automatically created and exported; a contract-dispatcher and a library-dispatcher. In this section we are going to take a look at how you could make calls to other contracts using the contract-dispatcher.

Calling another contract using the Contract Interface Dispatcher, calls the contract's logic in its context, and may change its state in most cases.

Here's an example on how you can do this using the ENS interface we specified earlier:

use starknet::ContractAddress;

#[starknet::interface]
trait IENS<TContractState> {
    fn store_name(ref self: TContractState, _name: felt252);
    fn get_name(self: @TContractState, _address: ContractAddress) -> felt252;
}

#[starknet::contract]
mod ENSContract {
    use starknet::get_caller_address;
    use starknet::ContractAddress;
    use super::IENSDispatcherTrait;
    use super::IENSDispatcher;

    #[storage]
    struct Storage {
        ...
    }

    #[generate_trait]
    #[external(v0)]
    impl IENSContractImpl of IENSContract {
        fn set_username(ref self: ContractState, _contract_address: ContractAddress, user_address: ContractAddress, name: felt252) {
            IENSDispatcher {contract_address: _contract_address }.store_name(name);
        }

        fn get_username(self: @ContractState, _contract_address: ContractAddress, user_address: ContractAddress) -> felt252 {
            IENSDispatcher {contract_address: _contract_address }.get_name(user_address)
        }
    }
}

As you can see, we first import the IENSDispatcher and IENSDispatcherTrait, then we call the store_name function contained within the interface, passing in the contract address of the target contract:

   IENSDispatcher {contract_address: _contract_address }.store_name(name);

The Library Interface Dispatcher

Unlike the contract interface dispatcher, the library dispatcher calls the target contract's classhash, whilst executing the call in the caller contract's context:

use starknet::ContractAddress;

#[starknet::interface]
trait IENS<TContractState> {
    fn store_name(ref self: TContractState, _name: felt252);
    fn get_name(self: @TContractState, _address: ContractAddress) -> felt252;
}

#[starknet::contract]
mod ENSContract {
    use starknet::get_caller_address;
    use starknet::ContractAddress;
    use super::IENSLibraryDispatcher;
    use super::IENSDispatcherTrait;

    #[storage]
    struct Storage {

    }

    #[generate_trait]
    #[external(v0)]
    impl IENSContractImpl of IENSContract {
        fn set_username(ref self: ContractState, _contract_address: ContractAddress, user_address: ContractAddress, name: felt252) {
            IENSLibraryDispatcher { class_hash: starknet::class_hash_const::<0x1234>() }.store_name(name);
        }

        fn get_username(self: @ContractState, _contract_address: ContractAddress, user_address: ContractAddress) -> felt252 {
            IENSLibraryDispatcher { class_hash: starknet::class_hash_const::<0x1234>() }.get_name(user_address)
        }
    }
}

In here, we import the library dispatcher for our interface IENSLibraryDispatcher, then we call the store_name function, whilst passing the class hash of our target contract rather than the contract address. 0x1234 in this case, represents our target contract class hash.

    IENSLibraryDispatcher { class_hash: starknet::class_hash_const::<0x1234>() }.store_name(name);