feat(gacha): add provably-fair gacha (pack-pull) example - #664
Merged
Conversation
Rebuild the gacha example as a Collector Crypt-style commit-reveal: alpha binds buyer entropy (SHA-256(pull || client_seed)), fixed tier weights make outcomes settle-order independent, and settle_pull anchors each reveal in the deployed cc-vrf registry by CPI — one commit per pull enforced by Light Protocol address uniqueness against a frozen operator record. Adds claim_prize (Token-2022 NFT carrying a rarity metadata field), refund_pull (post-deadline liveness escape), withdraw_fees (capped by pending-pull liabilities), and regenerated IDL/clients with cross-language alpha/tier parity fixtures.
36 tests covering init/buy/refund/withdraw with exact balance math, settle and claim negatives (all pre-CPI, so dummy Light accounts suffice), full claim Token-2022 TLV decode via fabricated settled pulls, and an IDL-driven demote-writable harness across all six instructions.
…path light-program-test suite (standalone workspace: litesvm 0.7 cannot share a graph with the 0.12 suite) running the full settle_pull -> cc-vrf -> Light CPI chain with genuine validity proofs: settle happy path with registry commit decode, replay rejected at both the program and registry layers, unfrozen and unregistered authorities rejected, claim after a real settle, and refund-after-settle rejected. just light-bootstrap provisions the environment (spl_noop restore, CDN proving-key download, pinned prover started directly — the CLI's start-prover points the binary at a dead key URL), and light-test joins the aggregate test recipe so CI runs it.
Rewrite CLAUDE.md around the detection-not-prevention randomness model and the on-chain-verified cc-vrf caveats, update the example READMEs, drop the inert nested workflows, and register the example in .ghaignore so the root justfile workflow drives CI.
Contributor
|
Too many files changed for review. ( Bypass the limit by tagging |
The gacha pinocchio example is a self-contained nested workspace, so its crates escape the root fmt/clippy coverage and fail the workspace-membership check. List them in .workspace-ignore, matching the world-cup precedent.
A prior prettier pass reformatted idl/gacha.json (2-space to 4-space), which the codama generator does not reproduce, so the check-generated CI job failed on IDL drift. Regenerate with the actual generator output.
The root prettier check reformats games/gacha/pinocchio/idl/gacha.json, but the codama generator emits it in a different style, so the gacha check-generated job and the root prettier check cannot both pass. Exclude the generated IDL from the root prettier check.
dev-jodee
previously approved these changes
Aug 4, 2026
- Drop the per-example LICENSE and SECURITY.md; both live at the repo root and the program's security_txt already points at the repo-level policy. - Source the Light system program, account-compression program and authority, registered-program PDA, and the v2 address tree from light-sdk-types rather than hand-copied base58 literals. cc-vrf publishes no crate, so its own program ID, CPI authority, and instruction discriminator stay local. - Split the Token-2022 mint and token-metadata CPIs out of claim_prize into instructions/helpers/prize_nft, and move the account guards and PDA creation into helpers/checks and helpers/account so helpers/mod.rs is declarations only. claim_prize drops from 279 to 142 lines. - Declare Pull's PDA seeds in Codama so both generated clients derive it, and delete the hand-written findPullPda from the TypeScript client. - Derive pool, vault, pull, and prize-mint addresses in the integration tests through gacha-client's generated find_pda helpers, so a seed the IDL gets wrong fails the suite instead of shipping. Take the system, Token-2022, and ATA program IDs from the crates that declare them. - Use solana_address::Address across the tests and drop solana-pubkey, whose 4.x Pubkey is a re-export of the same type. - Document that utils/idl.rs drives the account-meta demotion tests. - Bump @codama/renderers-rust to 3.1.3. 3.1.1 rendered numeric PDA seeds as decimal strings instead of to_le_bytes, so Pull::find_pda in the Rust client disagreed with both the program and the TypeScript client. - Ignore webapp/.next in eslint. AccountDiscriminator keeps its 0-based numbering. Renumbering closes the gap where a zeroed account passes the Pool check, but it would strand the live devnet pool and its pulls, whose PDAs cannot be re-initialized once allocated. Also lands the in-flight devnet burst-randomness script and its justfile recipes, which shared the working tree.
dev-jodee
approved these changes
Aug 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a complete provably-fair gacha (loot-box / pack-pull) game example — the on-chain mechanic behind Solana RWA pack platforms like Collector Crypt and Phygitals. An admin configures a pool of fixed-weight reward tiers and a fixed entry fee; buyers open pulls revealed via RFC 9381 ECVRF anchored in the deployed
cc-vrfregistry by CPI, and prizes are minted as Token-2022 NFTs carrying araritymetadata field.What's included
init_pool,buy_pull,settle_pull,refund_pull,claim_prize,withdraw_fees) with Anchor-compatible self-CPI events and a hand-built CPI into cc-vrf'scommit_proof_with_beta.@solana/gachakit plugin used by both scripts and the webapp.settle_pull→ cc-vrf → Light CPI chain with genuine validity proofs; Rust↔TS parity forselect_tier/derive_alphapinned by shared fixtures.Trust model
The program accepts the registered operator's signed
beta, while buyer entropy inalpha, fixed weights, one-reveal-per-pull enforcement via cc-vrf/Light, and a refund escape hatch close the gaps detection alone leaves open. Verification is fully reproducible off-chain from emitted events.Closes DEV-776