Reference
On-chain program
proof_of_goon is an Anchor program. It holds the vault, stores one settled and one pending root, and enforces the rules that do not depend on anybody's good behaviour.
Clustermainnet
GOON token address (mainnet)Wn46fj2vUJwJtcu877TWX5XvXTmrEhvJfSSUM9vgoon
GOON mint bound to this deploymentnone yet
Goon Vault token accountcreated when the mint is bound
APIhttps://api-production-bffee4f7.up.railway.app/v1
Current period index…
Accounts#
| Account | Seeds | Contents |
|---|---|---|
| GlobalState | "state" | Roles; mint and vault; activation flags; genesis; epoch length; first-era emission; halving interval; settlement delay; total claimed; excess burned; the active and pending roots; counters. |
| Vault authority | "vault" | No data. A program-derived signer that owns the vault token account. |
| Goon Vault | ATA(mint, vault authority) | The mining reserve. |
| ClaimStatus | "claim", wallet | Lifetime amount claimed, last claim time and epoch, claim count. |
root: [u8; 32] // Merkle root of cumulative allocations
epoch: u64 // last epoch the root covers
total_allocated: u64 // sum of every leaf
data_hash: [u8; 32] // sha256 of the distribution file
published_at: i64
effective_at: i64 // claims allowed from this timeInstructions#
| Instruction | Signer | Effect |
|---|---|---|
| initialize | Program upgrade authority | One-time setup of roles, epoch length, emission schedule and settlement delay. Checked against the program-data account so a deployment cannot be front-run. |
| update_config | Admin | Before activation: any parameter. After: the settlement delay only. |
| set_oracle / set_guardian | Admin | Rotate the oracle or guardian key. |
| propose_admin / accept_admin | Admin, then new admin | Two-step admin transfer. |
| bind_mint | Admin | Validates the mint and creates the vault token account. Repeatable until activation. |
| activate | Anyone | Requires vault ≥ reserve, burns the excess, sets genesis, freezes the schedule. Once. |
| publish_root | Oracle | Stores a pending root. See the checks below. |
| settle_root | Anyone | Promotes a pending root whose delay has passed. Claims do this lazily too. |
| cancel_pending_root | Guardian or admin | Removes a pending root before it settles. |
| set_paused | Guardian or admin | Halts publish_root, settle_root and claim. |
| claim | Any payer | Verifies a proof and pays the wallet its unclaimed allocation. |
publish_root checks#
- Signer is the oracle; the program is activated and not paused.
- Any pending root that has matured is promoted first, so the comparisons use the latest settled root.
epochhas ended:epoch < current_epoch.epochis greater than the settled root's epoch.total_allocatedis not less than the settled root's total, and not less than total claimed.total_allocated ≤ cumulative_emission(epoch).- The new root replaces any pending root that has not matured, and its delay starts again.
Invariants#
| ID | Statement |
|---|---|
| INV-01 | Vault tokens leave only through claim, to the associated token account of the wallet in a proven leaf. |
| INV-02 | Immediately after activation the vault holds exactly 500,000,000.000000 GOON. |
| INV-03 | For every root, total_allocated ≤ cumulative_emission(root.epoch) ≤ 500,000,000. |
| INV-04 | total_claimed ≤ active_root.total_allocated at all times. |
| INV-05 | For every wallet, claimed is non-decreasing and never exceeds its leaf in the active root. |
| INV-06 | Vault balance ≥ 500,000,000 − total_claimed. |
| INV-07 | A root cannot be claimed against before effective_at. |
| INV-08 | After activation the mint, epoch length, first-era emission and halving interval never change. |
Errors#
| Error | Raised when |
|---|---|
| NotUpgradeAuthority | initialize is signed by anyone but the program's upgrade authority. |
| Unauthorized | The signer does not hold the role the instruction requires. |
| InvalidConfig / EmissionExceedsReserve | Parameters out of range, or a schedule that could release more than the reserve. |
| InvalidMint / UnsupportedMintExtension | The mint fails validation. |
| VaultUnderfunded | activate is called with less than the reserve in the vault. |
| AlreadyActivated / NotActivated | The instruction is not valid in the current lifecycle state. |
| Paused | publish, settle or claim while paused. |
| EpochNotFinished / EpochNotIncreasing | A root for an epoch that is still open, or not later than the settled one. |
| AllocationDecreased / AllocationExceedsSchedule | A total that shrinks, or exceeds the emission schedule. |
| NoPendingRoot / RootNotSettled / RootAlreadySettled | settle or cancel in the wrong window. |
| NoActiveRoot | A claim before any root has settled. |
| InvalidProof / ProofTooLong | The proof does not resolve to the active root, or exceeds 32 levels. |
| NothingToClaim | The wallet has already claimed its full cumulative amount. |
| ClaimExceedsAllocation | A payment would take total claimed past the root's declared total. |
Client#
A typed client ships in the repository as @goon/shared/program.
import { claimIx, getProgram } from "@goon/shared/program";
const info = await (await fetch(`${API}/v1/claims/${wallet.toBase58()}`)).json();
const program = getProgram(connection);
const ix = await claimIx(program, {
payer: wallet, // or a relayer
wallet, // the wallet in the leaf; tokens always go here
mint,
tokenProgram, // the owner of the mint account
cumulativeRaw: BigInt(info.cumulativeRaw),
proofHex: info.proof,
});Testing#
- 29 program tests cover initialization, mint validation, activation, publishing, settlement, cancellation, pausing and claims, including forged amounts, foreign proofs and a root that under-declares its total.
- End-to-end runs on a local validator loaded with Meteora's mainnet bytecode go from pool creation through migration, leftover withdrawal, activation, mining, settlement and claiming with real transactions.
- The program has not had an independent audit. Treat it accordingly.