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
Program6KZLGdp5…FczoUr13
Global state (PDA, seed "state")BFM88ZRx…q43wxyjT
Vault authority (PDA, seed "vault")B3Zz1LyU…t7F6bjfW
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#

AccountSeedsContents
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 VaultATA(mint, vault authority)The mining reserve.
ClaimStatus"claim", walletLifetime amount claimed, last claim time and epoch, claim count.
RootRecord
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 time

Instructions#

InstructionSignerEffect
initializeProgram upgrade authorityOne-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_configAdminBefore activation: any parameter. After: the settlement delay only.
set_oracle / set_guardianAdminRotate the oracle or guardian key.
propose_admin / accept_adminAdmin, then new adminTwo-step admin transfer.
bind_mintAdminValidates the mint and creates the vault token account. Repeatable until activation.
activateAnyoneRequires vault ≥ reserve, burns the excess, sets genesis, freezes the schedule. Once.
publish_rootOracleStores a pending root. See the checks below.
settle_rootAnyonePromotes a pending root whose delay has passed. Claims do this lazily too.
cancel_pending_rootGuardian or adminRemoves a pending root before it settles.
set_pausedGuardian or adminHalts publish_root, settle_root and claim.
claimAny payerVerifies 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.
  • epoch has ended: epoch < current_epoch.
  • epoch is greater than the settled root's epoch.
  • total_allocated is 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#

IDStatement
INV-01Vault tokens leave only through claim, to the associated token account of the wallet in a proven leaf.
INV-02Immediately after activation the vault holds exactly 500,000,000.000000 GOON.
INV-03For every root, total_allocated ≤ cumulative_emission(root.epoch) ≤ 500,000,000.
INV-04total_claimed ≤ active_root.total_allocated at all times.
INV-05For every wallet, claimed is non-decreasing and never exceeds its leaf in the active root.
INV-06Vault balance ≥ 500,000,000 − total_claimed.
INV-07A root cannot be claimed against before effective_at.
INV-08After activation the mint, epoch length, first-era emission and halving interval never change.

Errors#

ErrorRaised when
NotUpgradeAuthorityinitialize is signed by anyone but the program's upgrade authority.
UnauthorizedThe signer does not hold the role the instruction requires.
InvalidConfig / EmissionExceedsReserveParameters out of range, or a schedule that could release more than the reserve.
InvalidMint / UnsupportedMintExtensionThe mint fails validation.
VaultUnderfundedactivate is called with less than the reserve in the vault.
AlreadyActivated / NotActivatedThe instruction is not valid in the current lifecycle state.
Pausedpublish, settle or claim while paused.
EpochNotFinished / EpochNotIncreasingA root for an epoch that is still open, or not later than the settled one.
AllocationDecreased / AllocationExceedsScheduleA total that shrinks, or exceeds the emission schedule.
NoPendingRoot / RootNotSettled / RootAlreadySettledsettle or cancel in the wrong window.
NoActiveRootA claim before any root has settled.
InvalidProof / ProofTooLongThe proof does not resolve to the active root, or exceeds 32 levels.
NothingToClaimThe wallet has already claimed its full cumulative amount.
ClaimExceedsAllocationA payment would take total claimed past the root's declared total.

Client#

A typed client ships in the repository as @goon/shared/program.

claim.ts
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.