SettleKit docs

Building on SettleKit

Add proof-settled payouts to your own program — the goalshield walkthrough.

SettleKit is a general truth → money primitive. If your contract can name a predicate over TxODDS's signed stat feed, SettleKit can prove it, and your program can pay on it — without trusting us, the keeper, or any admin.

The whole integration, honestly

goalshield-mini is a complete parametric-insurance product in ~260 lines. This is its entire oracle integration:

// claim_payout: release escrow iff the proof exists
let v = &ctx.accounts.verdict; // Account<'info, fw_settle::state::Verdict>
require!(v.fixture_id == p.fixture_id, ShieldError::WrongVerdict);
require!(v.question_hash == p.question_hash, ShieldError::WrongVerdict);
require!(v.outcome, ShieldError::VerdictNotTrue);

Anchor enforces the account owner (fw-settle) and discriminator; the three lines pin which truth. A Verdict is an init-once PDA keyed by sha256(borsh(question)) — if it exists with outcome == true, the oracle proved exactly that predicate against an anchored Merkle root before the account could be created.

Depend on the engine (Rust)

Your program reads SettleKit accounts as first-class Anchor types. One dependency line:

# Cargo.toml — exactly how goalshield-mini and fw-markets do it
[dependencies]
fw-settle = { git = "https://gitlab.com/Bprime/finalwhistle", features = ["cpi", "no-entrypoint"] }
use fw_settle::state::{Verdict, MatchFacts};

#[derive(Accounts)]
pub struct PayOnTruth<'info> {
    // owner == fw-settle and the discriminator are checked by Anchor right here
    pub verdict: Account<'info, Verdict>,
    // …your accounts
}

No CPI into fw-settle is ever needed — truth is an account you read, not a call you make.

Deployed addresses (devnet)

ProgramAddress
fw-settle (the engine)HJR3owk8ARzqayhwELA72S38yome3X4HWvb7HMP9AA8K
fw-markets (LMSR consumer)3as8xHoWivvFTEcg1N6MCb81RANkahr9dt484vr1Camc
goalshield-mini (reference consumer)BaRqEqDuhqEJPsApRKgHtz3ozFtqAhEgPKLxPsipeDae
txoracle (TxODDS, devnet)6pW64gN1s2uqjHkn1unFeEjAwJkPGHoppGvS715wyP2J

PDA seeds you will read: Verdict = ["verdict", fixture_id_le_i64, question_hash], MatchFacts = ["facts", fixture_id_le_i64], BatchAnchor = ["batch_anchor", fixture_id_le_i64, batch_ts_le_i64] — derivation helpers for all of them ship in the SDK (verdictPda, factsPda, anchorPda).

Installing the SDK

@finalwhistle/settle is a workspace package today: clone the repo and pnpm install, then import it as the monorepo does (packages/web and packages/keeper are living examples). An npm publish is planned post-hackathon.

Naming a question (TypeScript SDK)

import {
  cornersSumQuestion, questionHash, postVerdictIx, verdictPda,
} from "@finalwhistle/settle";

// "total corners (home + away) > 10 at full time" — the classic parametric prop
const q = cornersSumQuestion(fixtureId, 10, 5 /* phase: full time */);
const hash = questionHash(q);

// escrow your product against that hash; anyone can later prove it:
const { ix, verdict } = postVerdictIx(keeper.address, q, payload, rootsPda(batchTs));

payload is the Merkle proof bundle from TxLINE's GET /api/scores/stat-validation?fixtureId=…&seq=…&statKeys=7,8, mapped by the SDK's buildPayload. Binary predicates (sums/differences of two stats) are first-class: the oracle evaluates stat_a op stat_b against your threshold inside the CPI.

The lifecycle

  1. Underwrite — escrow a payout against question_hash (your program's account).
  2. Sell / activate — your business logic; SettleKit does not care.
  3. Prove — anyone posts the verdict after the match. Validity comes from the proof, not the poster.
  4. Pay — your program reads the Verdict and releases funds. No claims process.

What SettleKit guarantees you

  • The verdict corresponds to exactly your predicate (G-HASH binds hash ↔ semantics).
  • The proven stats carry the phase you demanded (G-PHASE — no mid-match spoofs).
  • The oracle's answer could not be forged (G-ROOT + G-RETURN pins).
  • If the truth never arrives, nothing false ever gets written — pair your product with a time-based refund path, as goalshield's expire does.

Test your integration in ten minutes

The repo ships a LiteSVM harness that loads the dumped production oracle binary and a real anchored roots account, so your consumer runs against the same verifier that runs on devnet — no network, no faucet:

anchor build && ./scripts/sync-idl.sh
./scripts/dump-oracle.sh   # fetches txoracle + a real roots account (once)
pnpm test:svm              # the existing suites are your templates

Start from tests/litesvm/inplay-shield.test.ts — it posts a real golden-proof verdict and drives goalshield end-to-end. Load your program next to the others in tests/litesvm/util.ts (makeSvm), point a test at a Verdict you created, and you have a proof-settled integration test in one file.