Skip to main content
These patterns help you adapt the minimal integration flow to real application state.

Pattern 1: Derive a bounded outcome in the callback

Use the delivered randomNumber to derive the game-specific result only after the callback arrives.
uint8 roll = uint8((randomNumber % 6) + 1);
bool won = roll == guess;
This keeps the outcome tied to the delivered value and avoids storing redundant derived state too early.

Pattern 2: Track requests per user or session

Map the returned requestId to the state you need for settlement.
mapping(address => uint256) public latestRequestIdByPlayer;
mapping(uint256 => Match) public matchesByRequestId;
Store this metadata immediately after requestRandomNumber returns.

Pattern 3: Keep callback checks at the top

Fail fast before you touch application logic.
if (msg.sender != address(coordinator)) revert Unauthorized();
if (matchData.player == address(0)) revert RequestNotFound(requestId);
if (matchData.settled) revert AlreadySettled(requestId);
Put these checks before outcome calculation or transfers.

Pattern 4: Separate pending state from settled state

Model the request as pending until the callback succeeds. Your app and frontend can then render a clear in-progress state instead of assuming synchronous completion. This is especially useful when:
  • a player can create multiple requests over time
  • you need to reconcile requests from emitted events
  • you want to retry UI polling or indexing until the callback lands

Choosing a pattern

Simple game outcome

Use a single mapping from requestId to a compact pending struct.

Session-based app

Track both requestId -> session and user -> latest requestId so your frontend can reconcile state quickly.

Integration pattern

Start from the minimal consumer before adding extra state.

Request lifecycle

Understand why pending state matters for asynchronous settlement.

Coordinator reference

Check the request interface and fee call while adapting these patterns.
Last modified on March 6, 2026