Introspection

To smooth interoperability, often standards require smart contracts to implement introspection mechanisms.

In Ethereum, the EIP165 standard defines how contracts should declare their support for a given interface, and how other contracts may query this support.

Starknet offers a similar mechanism for interface introspection defined by the SRC5 standard.

SRC5

Similar to its Ethereum counterpart, the SRC5 standard requires contracts to implement the supports_interface function, which can be used by others to query if a given interface is supported:

trait ISRC5 {
    /// Query if a contract implements an interface.
    /// Receives the interface identifier as specified in SRC-5.
    /// Returns `true` if the contract implements `interface_id`, `false` otherwise.
    fn supports_interface(interface_id: felt252) -> bool;
}

Computing the interface ID

The interface ID, as specified in the standard, is the XOR of all the Extended Function Selectors of the interface. We strongly advise reading the SNIP to understand the specifics of computing these extended function selectors. There are tools such as src5-rs that can help with this process.

Registering interfaces

For a contract to declare its support for a given interface, the contract should import the SRC5 module and register its support. It’s recommended to register interface support upon contract deployment through a constructor either directly or indirectly (as an initializer) like this:

#[starknet::contract]
mod MyContract {
    use openzeppelin::account::interface;
    use openzeppelin::introspection::src5::SRC5;

    #[storage]
    struct Storage {}

    #[constructor]
    fn constructor(ref self: ContractState) {
        let mut unsafe_state = SRC5::unsafe_new_contract_state();
        SRC5::InternalImpl::register_interface(ref unsafe_state, interface::ISRC6_ID);
    }

    (...)
}

Querying interfaces

Use the supports_interface function to query a contract’s support for a given interface.

#[starknet::contract]
mod MyContract {
    use openzeppelin::account::interface;
    use openzeppelin::introspection::interface::ISRC5DispatcherTrait;
    use openzeppelin::introspection::interface::ISRC5Dispatcher;
    use openzeppelin::introspection::src5::SRC5;
    use starknet::ContractAddress;

    #[storage]
    struct Storage {}

    #[external(v0)]
    fn query_is_account(self: @ContractState, target: ContractAddress) -> bool {
        let dispatcher = ISRC5Dispatcher { contract_address: target };
        dispatcher.supports_interface(interface::ISRC6_ID)
    }
}
If you are unsure whether a contract implements SRC5 or not, you can follow the process described in here.