The oracle is the bridge between off-chain chess platforms (Lichess, Chess.com) and the on-chain escrow contract. It is the only address authorised to call submit_result on the escrow contract.
An on-chain Soroban contract that:
- Stores verified results keyed by
match_id - Accepts result submissions only from the registered admin (the oracle service key)
- Prevents duplicate submissions for the same
match_id - Emits an on-chain event for every accepted result
A backend process that:
- Monitors the escrow contract for
("match", "activated")events - Extracts
game_idandplatformfrom the match record - Polls the appropriate chess platform API until the game is finished
- Submits the result to the Oracle Contract using the admin key
- Calls
submit_resulton the Escrow Contract to trigger payout
Chess platform API
│ game finished
▼
Oracle Service
1. fetch game result
2. map to MatchResult enum
3. oracle_contract.submit_result(match_id, game_id, result)
4. escrow_contract.submit_result(match_id, winner, oracle_address)
│
▼
Escrow Contract
- verifies caller == stored oracle address
- verifies match state == Active
- executes token payout
- sets state = Completed
- emits ("match", "completed") event
Oracle MatchResult |
Escrow Winner |
Payout |
|---|---|---|
Player1Wins |
Player1 |
Full pot to player1 |
Player2Wins |
Player2 |
Full pot to player2 |
Draw |
Draw |
stake_amount returned to each player |
| Platform | Enum Variant | API |
|---|---|---|
| Lichess | Platform::Lichess |
https://lichess.org/api/game/{id} |
| Chess.com | Platform::ChessDotCom |
https://api.chess.com/pub/game/{id} |
initialize(admin: Address)
submit_result(match_id: u64, game_id: String, result: MatchResult) -> Result<(), Error>
get_result(match_id: u64) -> Result<ResultEntry, Error>
has_result(match_id: u64) -> bool
| Error | Code | Meaning |
|---|---|---|
Unauthorized |
1 | Caller is not the admin |
AlreadySubmitted |
2 | Result already exists for this match |
ResultNotFound |
3 | No result stored for this match |
AlreadyInitialized |
4 | Contract has already been initialized |
- The oracle admin key is the only address that can submit results; any other caller is rejected with
Error::Unauthorized. - Once a result is submitted it is immutable —
AlreadySubmittedprevents overwriting. - The escrow contract independently verifies the caller against its stored oracle address before executing any payout.
- The oracle contract and escrow contract are separate deployments; a compromised oracle contract does not grant direct access to escrow funds.
Set the oracle admin key in .env:
ORACLE_ADMIN_SECRET=<stellar-secret-key>The oracle address is registered in the escrow contract at deploy time:
stellar contract invoke --id $CONTRACT_ESCROW \
-- initialize \
--oracle $ORACLE_ADDRESS \
--admin $ADMIN_ADDRESS