Gorilla Settlement · for developers
Settle your own markets against real sports results.
One call, no oracle to run. Pass a PredicateQuery — fixture_id, stat_key, period, predicate — and get a proof-verified bool back, or a revert. The fixture/stat/period binding is enforced insidethe engine, so you can't get it wrong.
Honest scope:the engine adds no cryptographic trust over CPIing TxLINE's oracle directly — the oracle is already the trust root. Its value is the forced binding every consumer is routed through, plus one audited deployment instead of each product re-implementing settlement.
One engine, two products.
Two structurally different consumers settle through the sameengine on devnet. Verify it on chain — each settle tx's logs show Program 9S6SwSp5…invoke.
Market
forge-marketsA pari-mutuel prop market. Reads its (fixture, stat, period, predicate), CPIs the engine, maps the bool to a Side. Escrows SOL.
Both CPI the same engine — 9S6S…jEjT. One deployment to audit; two products that share zero settlement code.
The integration, in ~10 lines
Consume it from your program.
This is the actual call — the shape lifted verbatim from forge-markets's settle.rs. It re-implements no CPI or binding logic: build the query from your market's tuple, thread the three accounts, read the result fail-closed.
use settlement_core::{client, PredicateQuery}; // 1. Declare what THIS market resolves. The engine binds the caller's oracle// args to exactly this tuple before it proves anything — you can't get it wrong.let query = PredicateQuery { fixture_id: market.fixture_id, // engine asserts fixture_summary.fixture_id == this stat_key: market.stat_key, // engine asserts stat_a.stat_to_prove.key == this period: market.period, // engine asserts stat_a.stat_to_prove.period == this predicate: market.predicate, // the YES condition, forwarded to txoracle}; // 2. One CPI. F1-binding + txoracle proof + fail-closed decode all live inside.let predicate_held: bool = client::resolve( &ctx.accounts.settlement_engine.to_account_info(), // engine, pinned by addr &ctx.accounts.daily_scores_merkle_roots.to_account_info(), // txoracle daily roots &ctx.accounts.txoracle_program.to_account_info(), // txoracle, pinned by addr &query, ts, &fixture_summary, &fixture_proof, &main_tree_proof, &stat_a, &stat_b, &op,)?; // Err = tampered/unbound proof -> your instruction reverts. Fail-closed. // 3. Apply the outcome. This is the only market-specific line you write.market.winner = if predicate_held { Side::Yes } else { Side::No };How it fails: a tampered proof makes the inner CPI Err, so resolve reverts and your instruction with it. A proof for the wrong fixture never even reaches the oracle — the engine rejects it with FixtureMismatch.
Reference, not a released package — there is no npm/crate to install. Copy the call; the engine is deployed.
Interface reference
client::resolve(query, proof) -> Result<bool>
PredicateQuery
What you declare you're resolving — the engine binds the proof to it.
- fixture_id: i64engine asserts fixture_summary.fixture_id == this
- stat_key: u32engine asserts stat_a.stat_to_prove.key == this
- period: i32engine asserts stat_a.stat_to_prove.period == this
- predicate: TraderPredicatethe YES condition, forwarded to txoracle
Accounts (in order)
- settlement_enginethe engine program — makes the resolve CPI callable
- daily_scores_merkle_rootstxoracle-owned daily roots; threaded straight through
- txoracle_programtxoracle, pinned by address at both hops
Returns
Ok(true)predicate heldOk(false)did not holdErrtampered / undecodable / unbound → the caller reverts
the one gotcha
Read the return data immediately after the engine CPI — no intervening CPI. The engine CPIs txoracle, then re-sets the return data to its own bool; client::resolvereads it right away and decodes fail-closed. Slip another CPI in between and you'd read the wrong program's bytes. This is the whole two-hop return-data contract.