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
| Interface | ID | Used By |
|---|---|---|
IMinigameToken | 0x39b3cf4989f3d1493c059433fb1d41a763c166ef2f8f6bd801823f27414bdbc | Token contracts |
IMinigame | 0x1050f9a792acfa175e26783e365e1b0b38ff3440b960d0ffdfc0ff9d7dc9f2a | Game contracts |
IMinigameTokenSettings | 0x229ba85053f3653daaa2e0d3a9f9296e6d3eae099557c7610822f5b556f1bc8 | Games with settings |
IMinigameTokenObjectives | 0x2c9b37fb2982c9480e67f2da4c7730a8cde17b5fb021f3d530305f2f3a0b929 | Games with objectives |
IMinigameTokenContext | 0x02b329e82f6f6b94f8949b36c5dc95acf86c6083b08d99bc81e399b4b0e8d19a | Tokens with context |
IMinigameTokenMinter | 0x2198424b9ee68499f53f33ad952598acbd6d141af6c6863c9c56b117063acca | Token minting |
IMetagame | 0x7997c74299c045696726f0f7f0165f85817acbb0964e23ff77e11e34eff6f2 | Platform contracts |
IMetagameCallback | 0x3b4312c1422de8c35936cc79948381ab8ef9fd083d8c8e20317164690aa1600 | Platforms that receive callbacks |
IMetagameContext | 0x1633419b5abcc4c0bbed8bd37a363fbe6de5bd25908761ab6dcda6a9b598ca9 | Platforms with context data |
IMinigameRegistry | 0x2ff8aa8dda405faf0eb17c5f806d7482b7352cf91fa9668e9ddf030f14b2ee9 | Registry contracts |
IMinigameSettings | 0x1a58ab3ee416cc018f93236fd0bb995de89ee536626c268491121e51a46a0f4 | Settings components |
IMinigameObjectives | 0xac0aaa451454d78741d9fafe803b69c8b31d073156020b08496104356db5e5 | Objectives components |
IMinigameTokenRenderer | 0x2899a752da88d6acf4ed54cc644238f3956b4db3c9885d3ad94f6149f0ec465 | Custom token renderer |
ISkills | 0x39fae678a19cd9b999da1d9ad54f00e686406974a4ced6f7eb51c8959aabd98 | Agent skills provider |
IMinigameTokenSkills | 0x33846532a9b9e859675aaa1a6c3ae6a45ccf1920c83e2d34898fa2f116201b3 | Per-token skills override |
ILeaderboard | 0x381684b... | Leaderboard components |
IEntryRequirement | 0x153355c... | Entry gating |
IEntryFee | 0x2386e28... | Entry fee handling |
IPrize | 0x2a7a3be... | 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