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
IMinigameToken0x39b3cf4989f3d1493c059433fb1d41a763c166ef2f8f6bd801823f27414bdbcToken contracts
IMinigame0x1050f9a792acfa175e26783e365e1b0b38ff3440b960d0ffdfc0ff9d7dc9f2aGame contracts
IMinigameTokenSettings0x229ba85053f3653daaa2e0d3a9f9296e6d3eae099557c7610822f5b556f1bc8Games with settings
IMinigameTokenObjectives0x2c9b37fb2982c9480e67f2da4c7730a8cde17b5fb021f3d530305f2f3a0b929Games with objectives
IMinigameTokenContext0x02b329e82f6f6b94f8949b36c5dc95acf86c6083b08d99bc81e399b4b0e8d19aTokens with context
IMinigameTokenMinter0x2198424b9ee68499f53f33ad952598acbd6d141af6c6863c9c56b117063accaToken minting
IMetagame0x7997c74299c045696726f0f7f0165f85817acbb0964e23ff77e11e34eff6f2Platform contracts
IMetagameCallback0x3b4312c1422de8c35936cc79948381ab8ef9fd083d8c8e20317164690aa1600Platforms that receive callbacks
IMetagameContext0x1633419b5abcc4c0bbed8bd37a363fbe6de5bd25908761ab6dcda6a9b598ca9Platforms with context data
IMinigameRegistry0x2ff8aa8dda405faf0eb17c5f806d7482b7352cf91fa9668e9ddf030f14b2ee9Registry 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 = 0x1050f9a792acfa175e26783e365e1b0b38ff3440b960d0ffdfc0ff9d7dc9f2a;

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