Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

SRC5 Interface Discovery

SRC5 is Starknet's standard for interface introspection (equivalent to Ethereum's ERC-165). EGS uses SRC5 extensively for safe, optional integration between contracts.

What SRC5 Does

Every SRC5-compliant contract implements:

#[starknet::interface]
pub trait ISRC5<TState> {
    fn supports_interface(self: @TState, interface_id: felt252) -> bool;
}

Before calling an interface method on another contract, you check if it supports that interface:

let src5 = ISRC5Dispatcher { contract_address: target };
if src5.supports_interface(IMETAGAME_CALLBACK_ID) {
    // Safe to dispatch callback
    let callback = IMetagameCallbackDispatcher { contract_address: target };
    callback.on_game_action(token_id, score);
}

How EGS Uses SRC5

Game Discovery

The token contract checks if a game implements IMinigameTokenData before reading scores:

if src5.supports_interface(IMINIGAME_ID) {
    let game = IMinigameTokenDataDispatcher { contract_address: game_addr };
    let score = game.score(token_id);
}

Optional Extensions

The token checks which extensions a game supports:

// Does this game have settings?
if src5.supports_interface(IMINIGAME_TOKEN_SETTINGS_ID) {
    // Safe to query settings
}
 
// Does this game have objectives?
if src5.supports_interface(IMINIGAME_TOKEN_OBJECTIVES_ID) {
    // Safe to query objectives
}

Callback Routing

When syncing game state, the token checks if the minter wants callbacks:

if src5.supports_interface(IMETAGAME_CALLBACK_ID) {
    // Dispatch score/game_over/objective callbacks
} else {
    // Skip callbacks - minter doesn't support them
}

Interface ID Table

InterfaceIDUsed By
IMinigameToken0x21c51b9820202309d87ff5d316b17b2d9280f2db9fd8fc2c6120c3a60869e49Token contracts
IMinigame0x3d1730c22937da340212dec5546ff5826895259966fa6a92d1191ab068cc2b4Game contracts
IMinigameTokenSettings0x3c6f5c714fef5141bb7edbbbf738c80782154e825a5675355c937aa9bc07baeGames with settings
IMinigameTokenObjectives0x1e9f4982a68b67ddda6e894e8e620fe12ae877cf303308fe16814ceb2706077Games with objectives
IMinigameTokenContext0x02b329e82f6f6b94f8949b36c5dc95acf86c6083b08d99bc81e399b4b0e8d19aTokens with context
IMinigameTokenMinter0x2198424b9ee68499f53f33ad952598acbd6d141af6c6863c9c56b117063accaToken minting
IMetagame0x7997c74299c045696726f0f7f0165f85817acbb0964e23ff77e11e34eff6f2Platform contracts
IMetagameCallback0x3b4312c1422de8c35936cc79948381ab8ef9fd083d8c8e20317164690aa1600Platforms that receive callbacks
IMetagameContext0x1633419b5abcc4c0bbed8bd37a363fbe6de5bd25908761ab6dcda6a9b598ca9Platforms with context data
IMinigameRegistry0x2d4d61e5a2e608d8adab5fc69e6d35baff40ba85dadbd5f4ff0be139d4b69b5Registry contracts
IMinigameSettings0x1a58ab3ee416cc018f93236fd0bb995de89ee536626c268491121e51a46a0f4Settings components
IMinigameObjectives0xac0aaa451454d78741d9fafe803b69c8b31d073156020b08496104356db5e5Objectives components
IMinigameTokenRenderer0x2899a752da88d6acf4ed54cc644238f3956b4db3c9885d3ad94f6149f0ec465Custom token renderer
ISkills0x39fae678a19cd9b999da1d9ad54f00e686406974a4ced6f7eb51c8959aabd98Agent skills provider
IMinigameTokenSkills0x33846532a9b9e859675aaa1a6c3ae6a45ccf1920c83e2d34898fa2f116201b3Per-token skills override
ILeaderboard0x381684b...Leaderboard components
IEntryRequirement0x153355c...Entry gating
IEntryFee0x2386e28...Entry fee handling
IPrize0x2a7a3be...Prize management

Computing Interface IDs

Interface IDs in Cairo are computed from the trait's selector. The constant is defined in the interface module:

// In game_components_embeddable_game_standard::minigame::interface
pub const IMINIGAME_ID: felt252 = 0x3d1730c22937da340212dec5546ff5826895259966fa6a92d1191ab068cc2b4;

Registering Interfaces

When your contract implements an EGS interface, register it in the constructor:

use openzeppelin_introspection::src5::SRC5Component;
use game_components_embeddable_game_standard::minigame::interface::IMINIGAME_ID;
 
component!(path: SRC5Component, storage: src5, event: SRC5Event);
 
#[constructor]
fn constructor(ref self: ContractState) {
    self.src5.register_interface(IMINIGAME_ID);
}

Components like MinigameComponent and MetagameComponent register their interfaces automatically during initialization.

Safe Dispatch Pattern

Always check before calling:

use openzeppelin_introspection::src5::{ISRC5Dispatcher, ISRC5DispatcherTrait};
 
fn safe_callback(
    minter_address: ContractAddress,
    token_id: u256,
    score: u64,
) {
    // Check if minter supports callbacks
    let src5 = ISRC5Dispatcher { contract_address: minter_address };
    if !src5.supports_interface(IMETAGAME_CALLBACK_ID) {
        return; // Skip - minter doesn't support callbacks
    }
 
    // Safe to dispatch
    let callback = IMetagameCallbackDispatcher { contract_address: minter_address };
    callback.on_game_action(token_id, score);
}

This pattern prevents:

  • Reverts when calling unsupported interfaces
  • Wasted gas on contracts that don't need callbacks
  • Breaking changes when new interfaces are added