Build an attested on-chain app
The starter templates show off-chain results and mobile signing. A real on-chain app that moves money usually needs:- a chain contract that holds value
- a way for users to register
- a Gora-attested decision that names a winner / recipient / outcome
- a single on-chain settlement transaction that pays out the winner
examples/apps/raffle-app.
One operator-signed hop
Minimum on-chain interface
Whatever chain you target, the contract should expose:| Function | Signer | What it does |
|---|---|---|
register() | participant | adds caller to the participant set |
fund() | anyone | deposits native token into the treasury |
settleWinner(round, winner, attestationHash) | operator | stores the result of one round and transfers payoutBps of the treasury to winner in the same call |
Off-chain app shape
The Gora app runs off-chain and returns JSON. For an on-chain app it should return:winneris the canonical attested result.settle_instructionis purely informational. The orchestrator script computes asha256over the deterministic parts of this output (app_id,request_id,round,winner,registry_hash) and passes that hash tosettleWinner. Any committee member can re-run the off-chain app on the same input and reproduce the same hash byte-for-byte.
Operator orchestration
A small script ties everything together:scripts/raffle.mjs draw in examples/apps/raffle-app.
Determinism
Validators reproduce non-deterministic AI outputs by re-running prompts (see MVP §10). For a deterministic app like a raffle, make the selection itself deterministic so a validator committee can verify exact byte-equality:Trust assumptions to call out in your policy
- The operator key can lie about the winner. Mitigate by running the off-chain app under a Gora committee quorum so multiple committee members independently sign the attestation before the operator broadcasts the settlement.
- The contract must not pay out more than the configured share. Cap by
payoutBpsand never accept an externally-supplied payout amount insettleWinner. - Compute the payout from the live treasury balance inside the contract, not from the off-chain output. The contract is the source of truth for how much money is currently held.
What to copy
Openexamples/apps/raffle-app and copy:
gora.app.jsonandpolicy.jsonfor the multi-chain manifest shapesrc/index.tsfor the deterministic winner-pickercontracts/{base,solana,algorand}/*for the single-settlement payout patternscripts/raffle.mjsfor the orchestration CLItest-wallets/for a safe way to keep devnet keys out of git