@gabox-labs/sdk 0.2.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@gabox-labs/sdk",
3
- "version": "0.2.0",
3
+ "version": "0.7.0",
4
4
  "type": "module",
5
- "description": "The Solana Kit SDK for gabox-v2: generated instruction builders, PDAs, Pump/PumpSwap venue accounts, and the pack flow.",
5
+ "description": "The Solana Kit SDK for gabox: generated instruction builders, PDAs, Raydium LaunchLab and CPMM venue accounts, and the pack flow.",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
@@ -16,8 +16,9 @@
16
16
  "solana",
17
17
  "solana-kit",
18
18
  "gabox",
19
- "pump.fun",
20
- "pumpswap",
19
+ "raydium",
20
+ "launchlab",
21
+ "cpmm",
21
22
  "anchor"
22
23
  ],
23
24
  "engines": {
@@ -40,10 +41,10 @@
40
41
  "import": "./dist/generated/index.js",
41
42
  "default": "./dist/generated/index.js"
42
43
  },
43
- "./pump": {
44
- "types": "./dist/pump/index.d.ts",
45
- "import": "./dist/pump/index.js",
46
- "default": "./dist/pump/index.js"
44
+ "./raydium": {
45
+ "types": "./dist/raydium/index.d.ts",
46
+ "import": "./dist/raydium/index.js",
47
+ "default": "./dist/raydium/index.js"
47
48
  },
48
49
  "./package.json": "./package.json"
49
50
  },
@@ -68,15 +69,9 @@
68
69
  "prepublishOnly": "npm run typecheck && npm test && npm run check:surface && npm run build && npm run check:package"
69
70
  },
70
71
  "dependencies": {
71
- "@pump-fun/pump-sdk": "1.36.0",
72
- "@pump-fun/pump-swap-sdk": "1.19.0",
73
72
  "@solana-program/system": "^0.13.0",
74
73
  "@solana-program/token": "^0.15.0",
75
- "@solana/program-client-core": "^7.0.0",
76
- "@solana/spl-token": "^0.4.15",
77
- "@solana/web3.js": "^1.99.0",
78
- "bn.js": "^5.2.5",
79
- "buffer": "^6.0.3"
74
+ "@solana/program-client-core": "^7.0.0"
80
75
  },
81
76
  "peerDependencies": {
82
77
  "@solana/kit": "^7.0.0"
@@ -86,7 +81,7 @@
86
81
  "@codama/nodes-from-anchor": "^1.5.3",
87
82
  "@codama/renderers-js": "^2.3.1",
88
83
  "@solana/kit": "^7.0.0",
89
- "@types/bn.js": "^5.2.0",
84
+ "@solana/web3.js": "^1.99.0",
90
85
  "@types/node": "^24.3.0",
91
86
  "codama": "^1.10.0",
92
87
  "publint": "0.3.24",
@@ -1,164 +1,75 @@
1
1
  ---
2
2
  name: gabox-sdk
3
- description: Integrate gabox machines on Solana with @gabox-labs/sdk (TypeScript, @solana/kit 7). Create a machine on a new Pump.fun coin, price and buy packs, watch a draw resolve, sell the prize, handle referrals and stuck draws. Use when a task mentions gabox, machines, packs, draws, prize pools on Pump.fun coins, or @gabox-labs/sdk.
3
+ description: Integrate Gabox machines on Solana with @gabox-labs/sdk and @solana/kit. Use for Gabox pools, packs, Raydium LaunchLab and CPMM venues, draws, prizes, creator fees, and SDK integration.
4
4
  ---
5
5
 
6
- # gabox SDK
6
+ # Gabox SDK
7
7
 
8
- `@gabox-labs/sdk` is the TypeScript client for gabox machines. A **machine** is a prize pool tied to a
9
- new Pump.fun coin. A **pack** is 1,000,000 tokens of that coin; its price follows the coin. Buying
10
- a pack creates a **draw** with a frozen prize table; verifiable randomness picks the prize and the
11
- tokens land in the buyer's wallet. Full docs: `README.md` in the package. Every exported
12
- function and type: `references/api.md`.
8
+ Gabox binds a prize machine to a brand-new Raydium LaunchLab coin. The coin trades on its LaunchLab
9
+ curve until it raises the pinned amount, then Raydium migrates it into a Raydium CPMM pool. Gabox
10
+ delivers awards and closes draw accounts itself; there is no Ready, claim or sell-prize flow, and no
11
+ referral program.
13
12
 
14
- ## Setup
13
+ Use only devnet clients today. Pass `GaboxClient` first to every chain function; all amounts are
14
+ `bigint`.
15
15
 
16
- ```sh
17
- npm install @gabox-labs/sdk @solana/kit
18
- ```
19
-
20
- ESM only. Node 24+. `@solana/kit` is a peer dependency. The program is on **devnet only** today.
21
-
22
- ```ts
23
- import { createClient } from '@gabox-labs/sdk';
24
- const gabox = createClient({ cluster: 'devnet' }); // public devnet RPC
25
- const gabox = createClient({ cluster: 'devnet', url: RPC }); // your provider; wsUrl follows
26
- ```
27
-
28
- Pass `gabox` as the first argument to every SDK function. `cluster` is required and the URL must
29
- agree with it: a devnet client refuses a URL without `devnet` in it. This throws at
30
- `createClient`, before any request.
16
+ Gabox charges no fee at all. Every price the SDK shows already includes Raydium's own fees: on the
17
+ curve, 0.5% to the Gabox platform wallet and 0.5% to the coin creator plus Raydium's trade fee; after
18
+ graduation, the CPMM pool fee and the pool creator fee.
31
19
 
32
- ## Conventions that trip people up
20
+ A machine is priced in one quote asset, fixed when the pool is created. It defaults to wrapped SOL,
21
+ and any quote Raydium enabled on LaunchLab works; `raydium.fetchQuoteConfig(client, mint)` says
22
+ whether a mint is one of them, and `raydium.isQuoteSupported` is the boolean version. The quote may
23
+ be classic SPL Token or Token-2022; the coin is always classic SPL Token.
33
24
 
34
- - **All amounts are `bigint`.** Lamports for SOL, base units for tokens (6 decimals: one pack is
35
- `1_000_000n * 1_000_000n`). Never mix `number` into arithmetic. Basis points are `number`.
36
- - **Builders return a message, not a signature.** Sign with kit's
37
- `signTransactionMessageWithSigners` and send with `sendAndConfirmTransactionFactory({ rpc:
38
- gabox.rpc, rpcSubscriptions: gabox.rpcSubscriptions })`. The SDK never signs or sends.
39
- - **Build right before signing.** The message carries a blockhash that expires in about a minute.
40
- - **Address derivation is async.** `await poolAddress(mint)`, `await drawAddress(pool, seq)`.
41
- - **Low-level `generated.*` functions take `gabox.rpc`**, not the client.
42
- - **Keep the surface kit-only.** Do not import `@solana/web3.js` to talk to this SDK.
43
-
44
- ## Flow 1: price and buy a pack
25
+ Buyers pay in SOL either way. On a WSOL pool every builder wraps the SOL its trade needs and closes
26
+ the WSOL account afterwards, so the wallet spends and receives plain SOL; that close also unwraps any
27
+ WSOL the wallet already held. On any other pool a route provider swaps SOL into the quote token **in
28
+ the same transaction** as the Gabox instruction, and back after a sale. `createClient` gives mainnet
29
+ `jupiterRoute()`; devnet has no router, so pass `route: raydiumCpmmRoute(pool)` with a CPMM pool for
30
+ the SOL pair. `payWith: 'quote'` and `receive: 'quote'` skip the swap.
45
31
 
46
32
  ```ts
47
- import { buyPack, drawAddress, fetchPoolByMint, getOffer, share, watchDraw } from '@gabox-labs/sdk';
33
+ import { buyPack, createClient, getOffer } from '@gabox-labs/sdk';
48
34
 
35
+ const gabox = createClient({ cluster: 'devnet' });
49
36
  const offer = await getOffer(gabox, mint, { user: purchaser.address });
50
- // offer.quoteLamports the pack price now (venue fees included)
51
- // offer.prizes [{ amount, tickets }], amounts in tokens, already capped by inventory
52
- // offer.maximum top prize; offer.isSeeded === false means the cap is biting: warn the user
53
-
54
- const pool = await fetchPoolByMint(gabox, mint);
55
- const seq = pool!.nextSeq; // pin the draw
56
- const maxQuoteIn = (offer.quoteLamports * 102n) / 100n; // 2% slippage
57
37
  const message = await buyPack(gabox, {
58
38
  mint,
59
- purchaser, // TransactionSigner, the only signer
60
- seq,
61
- maxQuoteIn,
62
- minMaximum: offer.maximum,
63
- maxTotalDebit: maxQuoteIn + share(maxQuoteIn, 200n) + 20_000_000n,
64
- });
65
- // ...sign and send...
66
- const draw = await drawAddress(offer.pool, seq);
67
- const resolved = await watchDraw(gabox, draw, { signal: AbortSignal.timeout(180_000) });
68
- // resolved.amount = tokens won, already in the purchaser's associated token account
69
- ```
70
-
71
- The three limits the buyer signs:
72
-
73
- | limit | caps | set it to |
74
- | --------------- | -------------------------------------------------------------------- | -------------------------------------- |
75
- | `maxQuoteIn` | the venue price in lamports | `quoteLamports` + your slippage |
76
- | `minMaximum` | the top prize in tokens; fails if inventory dropped | `offer.maximum` |
77
- | `maxTotalDebit` | price + creator fee + protocol fee + randomness fee (both fees ≈ 1%) | `maxQuoteIn + share(maxQuoteIn, 200n) + margin` |
78
-
79
- `maxTotalDebit` excludes rent and the transaction fee. Add ~0.02 SOL of margin. Always pass `seq`
80
- so a retry after a stale blockhash cannot buy a second pack.
81
-
82
- ## Flow 2: keep or sell
83
-
84
- Keeping needs no transaction. Selling routes through the SDK:
85
-
86
- ```ts
87
- import { pump, sellTokens } from '@gabox-labs/sdk';
88
- const gross = await pump.sellQuote(gabox, mint, amount, { user: seller.address });
89
- const message = await sellTokens(gabox, {
90
- mint, seller, amount,
91
- minQuoteOutput: (gross * 95n) / 100n, // must be > 0
39
+ purchaser,
40
+ maxQuoteIn: (offer.quoteAmount * 102n) / 100n,
41
+ minMaximum: (offer.maximum * 98n) / 100n,
42
+ maxNativeDebit: 100_000_000n,
92
43
  });
93
44
  ```
94
45
 
95
- On the Pump route the seller gets SOL. On PumpSwap (a graduated coin) the seller gets WSOL.
96
-
97
- ## Flow 3: create a machine
98
-
99
- ```ts
100
- import { createMachine, DEFAULT_TIERS, seedCostEstimate } from '@gabox-labs/sdk';
101
- import { generateKeyPairSigner } from '@solana/kit';
102
-
103
- const mintKeypair = await generateKeyPairSigner();
104
- const seed = await seedCostEstimate(gabox); // DEFAULT_TIERS
105
- const message = await createMachine(gabox, {
106
- creator, mintKeypair, // two signers
107
- name, symbol, uri, // Pump metadata
108
- feeBps: 100, // creator fee, 0..100 (max 1%)
109
- tiers: DEFAULT_TIERS, // optional; default top prize = 20 packs
110
- maxSeedLamports: (seed.lamports * 105n) / 100n,
111
- });
112
- ```
113
-
114
- The creator buys the **seed** that backs the top prize. `DEFAULT_TIERS` needs nineteen extra packs
115
- of tokens. Pass a custom eight-row `Tier[]` only after validating its odds and seed with
116
- `seedCostEstimate(gabox, tiers)`; the SDK and program both validate it.
117
-
118
- ## Flow 4: stuck draws (permissionless)
119
-
120
- ```ts
121
- import { drawAvailability, expireDraw, retryDraw } from '@gabox-labs/sdk';
122
- const s = await drawAvailability(gabox, draw);
123
- if (s?.canRetry) await retryDraw(gabox, { payer, pool, draw, maxVrfDebit: 5_000_000n });
124
- if (s?.canExpire) await expireDraw(gabox, { payer, pool, draw });
125
- ```
126
-
127
- Retry: 300 slots after the last attempt, 3 attempts total. Expire: after 216,000 slots (~1 day),
128
- only while the draw is `Pending`; it pays the **smallest** prize, not a refund. A `Ready` draw must
129
- go through normal delivery. Show `slotsUntilRetry` / `slotsUntilExpiry` as a countdown.
130
-
131
- ## Referrals
132
-
133
- ```ts
134
- await bindReferrer(gabox, referee, referrerAddress); // referee signs, permanent
135
- await buyPack(gabox, { ...input, referrer: referrerAddress }); // or bind + buy in one tx
136
- await claimReferral(gabox, referrer, poolAddress); // referrer collects
137
- ```
138
-
139
- The referrer earns 1% of the creator's fee per pack. The buyer pays nothing extra.
140
-
141
- ## Reading state
142
-
143
- `fetchPoolByMint`, `fetchPoolInventory`, `fetchDraw`, `listPools`, `listDraws({ pool?, purchaser? })`,
144
- `fetchEvents(gabox, signature)`. `list*` uses `getProgramAccounts`: cache it. Events:
145
- `PoolCreated`, `PackBought`, `DrawResolved`, `TokensSold`, `PrizesFunded`, `RandomnessRetried`.
146
-
147
- ## Errors
148
-
149
- ```ts
150
- import { generated } from '@gabox-labs/sdk';
151
- if (generated.isGaboxV2Error(error, message, generated.GABOX_V2_ERROR__SLIPPAGE_EXCEEDED)) { /* widen maxQuoteIn */ }
152
- if (generated.isGaboxV2Error(error, message)) console.log(generated.getGaboxV2ErrorMessage(error.context.code));
153
- ```
154
-
155
- Common: `SlippageExceeded`, `PrizeCapChanged` (refresh the offer), `RetryTooSoon`,
156
- `RetryUnavailable`, `NotExpired`, `NotPending`, `InvalidReferral`, `ZeroAmount`.
157
-
158
- ## Do not
159
-
160
- - Do not guess the draw address: derive it with `drawAddress(pool, seq)` from the `seq` you pinned.
161
- - Do not set `minQuoteOutput` or `maxQuoteIn` to `0n`. The program rejects them.
162
- - Do not call `getOffer` once and reuse it for minutes. Prices move with the coin.
163
- - Do not point a `'devnet'` client at any other URL. Use `cluster: 'localnet'` for a test validator.
164
- - Do not run against mainnet. The program is not deployed there.
46
+ `offer.quoteAmount` is the exact venue price for one pack, in the pool's quote token;
47
+ `offer.solAmount` is the same price in lamports, with `offer.quoteDecimals` and `offer.quoteSymbol`
48
+ for showing it. `maxQuoteIn` is the slippage cap in the quote token, and the amount put in the
49
+ buyer's quote account first. `maxNativeDebit` is a separate cap on the lamports the handler watches:
50
+ the VRF request and any venue account rent.
51
+
52
+ For creation use `createMachine` with `name`, `symbol`, `uri`, `maxSeedQuoteIn` and
53
+ `maxSeedNativeDebit`. Metaplex limits the three strings to 32, 10 and 200 UTF-8 bytes. Add
54
+ `quote: { mint }` and `raise` for a machine priced in another token: `raise` is
55
+ `total_quote_fund_raising` in that token's base units, and a WSOL pool uses the raise the program
56
+ pins instead. Everything else about the launch is pinned. It buys a mandatory seed: enough for a 3x
57
+ top prize on the first pack, or the table's full top prize when that pays less than 3x. A table's
58
+ top tier can be at most 20x. Pass `extraSeedTokens` to buy more seed in the same trade, and read
59
+ `seedCostEstimate` for the mandatory, extra and total amounts plus the exact cost.
60
+
61
+ `raydium.resolveVenue(client, { mint, user })` says which venue is live. It throws while a coin is
62
+ migrating, which on devnet lasts under a minute. A migrated CPMM pool can sit at any address, so the
63
+ SDK proves it from its own data and reads its fee tier, vaults and oracle out of the pool account.
64
+
65
+ Creators collect their share with `raydium.claimCreatorFee(client, { creator, quoteMint })`, which
66
+ sweeps every coin they launched against that quote, and
67
+ `raydium.collectCreatorFee(client, { mint, creator })` for one graduated coin.
68
+ `raydium.fetchCreatorFees` reads both, per quote asset.
69
+
70
+ `buyPack` keeps a per-wallet `WalletActivity` account (`fetchWalletActivity(client, wallet)`, `null`
71
+ before a wallet's first purchase); it resolves the account itself.
72
+
73
+ Use `fetchEvents` and `findResolvedDraw` for final delivery, and `retryDraw` or `expireDraw` for
74
+ permissionless recovery. `fundPrizes` moves coins a funder already holds into the irrevocable prize
75
+ vault. See `references/api.md` and the package README for complete signatures.
@@ -1,148 +1,158 @@
1
- # `@gabox-labs/sdk` API reference
2
-
3
- Every function that reads or writes the chain takes `client: GaboxClient` first. Every builder
4
- returns `Promise<GaboxTransactionMessage>`: a version 0 kit message with the fee payer and a fresh
5
- blockhash set, ready for `signTransactionMessageWithSigners`. All amounts are `bigint`.
6
-
7
- ## Client (`src/rpc.ts`)
8
-
9
- | function / type | signature | notes |
10
- | --- | --- | --- |
11
- | `createClient` | `(config: ClientConfig) => GaboxClient` | the init step. Throws if the URL contradicts the cluster |
12
- | `ClientConfig` | `{ cluster: Cluster; url?: string; wsUrl?: string; addressLookupTables?: AddressesByLookupTableAddress }` | `cluster` is required |
13
- | `GaboxClient` | `{ cluster; url; wsUrl; rpc: GaboxRpc; rpcSubscriptions: GaboxRpcSubscriptions; addressLookupTables }` | a plain object; spread to replace `rpc` |
14
- | `Cluster` | `'devnet' \| 'mainnet-beta' \| 'localnet'` | |
15
- | `CLUSTER_ENDPOINTS` | `Record<Cluster, { url; wsUrl }>` | the defaults |
16
- | `clusterNamedBy` | `(url: string) => Cluster \| 'testnet' \| null` | substring check |
17
- | `assertClusterUrl` | `(cluster: Cluster, url: string) => void` | the guard, exported for scripts |
18
- | `websocketUrlFor` | `(url: string) => string` | `https`→`wss`, `http`→`ws` |
19
- | `defaultAddressLookupTables` | `(cluster: Cluster) => AddressesByLookupTableAddress` | devnet's shared table; `{}` elsewhere |
20
- | `GaboxRpc`, `GaboxRpcSubscriptions` | kit `Rpc<SolanaRpcApi>`, `RpcSubscriptions<SolanaRpcSubscriptionsApi>` | |
21
-
22
- ## Offers and prices (`src/offer.ts`)
23
-
24
- | function | signature | notes |
25
- | --- | --- | --- |
26
- | `getOffer` | `(client, mint: Address, options?: { user?: Address; venue?: VenueKind }) => Promise<PackOffer>` | two reads: pool + vault, then the venue. Throws if the mint has no machine |
27
- | `offerFromState` | `(inventory: PoolInventory, venue: VenueKind, quoteLamports: bigint) => PackOffer` | pure |
28
- | `seedShortfall` | `(offer: PackOffer) => bigint` | tokens that would uncap the top prize |
29
-
30
- `PackOffer` fields: `mint`, `pool`, `packTokens`, `quoteLamports`, `feeBps`, `feeLamports`,
31
- `protocolLamports`, `seedLamports`, `seedTokens`, `venue`, `prizes: Prize[]`, `maximum`, `minimum`,
32
- `uncapped`, `inventory`, `reserved`, `free`, `isFirstPack`, `isSeeded`, `maxMultiplierBps`,
33
- `averageMultiplierBps`.
34
-
35
- ## Transaction builders (`src/tx/`, `src/referral.ts`)
36
-
37
- | function | input | signers |
38
- | --- | --- | --- |
39
- | `createMachine` | `{ creator; mintKeypair; name; symbol; uri; feeBps: number; tiers?: readonly Tier[]; maxSeedLamports: bigint; feeRecipientIndex?; buybackRecipientIndex? }` | creator, mintKeypair |
40
- | `seedCostEstimate` | `(client, tiers?: readonly Tier[]) => Promise<{ tiers: readonly Tier[]; seedTokens: bigint; lamports: bigint }>` | read only |
41
- | `buyPack` | `{ mint; purchaser; maxQuoteIn: bigint; minMaximum: bigint; maxTotalDebit: bigint; seq?: bigint; venue?; wrapLamports?; cashback?; referrer?: Address }` | purchaser |
42
- | `sellTokens` | `{ mint; seller; amount: bigint; minQuoteOutput: bigint; venue? }` | seller |
43
- | `fundPrizes` | `{ mint; funder; amount: bigint; source?: Address }` | funder |
44
- | `fundPrizesWithBuy` | `{ mint; funder; tokens: bigint; maxQuoteIn: bigint; venue?; wrapLamports? }` | funder |
45
- | `retryDraw` | `{ payer; pool; draw; maxVrfDebit: bigint }` | payer (anyone) |
46
- | `expireDraw` | `{ payer; pool; draw }` | payer (anyone); only for an expired `Pending` draw |
47
- | `bindReferrer` | `(client, referee: TransactionSigner, referrer: Address, options?) ` | referee |
48
- | `claimReferral` | `(client, referrer: TransactionSigner, pool: Address, options?)` | referrer |
49
- | `claimPrize`, `sellPrize`, `quoteSellPrize` | legacy draws only (created before automatic delivery) | purchaser |
50
- | `buildMessage` | `(client, feePayer, instructions: Instruction[], options: BuildOptions)` | assemble your own |
51
-
52
- Every input also accepts `BuildOptions`: `computeUnitLimit?: number`, `computeUnitPrice?: number | bigint`
53
- (micro-lamports per unit), `addressLookupTables?`.
54
-
55
- `drawAvailability(client, draw) => Promise<DrawAvailability | null>` returns
56
- `{ status, attempts, slotsUntilRetry, slotsUntilExpiry, canRetry, canExpire }`.
57
-
58
- ## Readers (`src/accounts.ts`, `src/events.ts`)
59
-
60
- | function | returns |
1
+ # Gabox SDK API
2
+
3
+ All chain-touching functions take `client: GaboxClient` first. Addresses use `Address`; all token
4
+ amounts use base-unit `bigint`. Every venue is Raydium: LaunchLab before a coin graduates, Raydium
5
+ CPMM after. Both settle in the pool's own quote asset, which defaults to wrapped SOL.
6
+
7
+ ## Client and reads
8
+
9
+ | Function | Result |
61
10
  | --- | --- |
11
+ | `createClient({ cluster: 'devnet', ... })` | `GaboxClient` |
62
12
  | `fetchPoolByMint(client, mint)` | `Pool \| null` |
63
- | `fetchPoolAt(client, poolAddress)` | `Pool \| null` |
64
- | `fetchPoolInventory(client, mint)` | `{ pool, poolAddress, vault, inventory, reserved, free } \| null` |
65
- | `fetchVaultBalance(client, mint)` | `bigint` |
66
- | `fetchDraw(client, drawAddress)` | `Draw \| null` (null once delivered and closed) |
67
- | `fetchReferralReward(client, pool, referrer)` | `Referral \| null` |
68
- | `listPools(client)` | `{ address, data: Pool }[]` |
69
- | `listDraws(client, { pool?, purchaser? })` | `{ address, data: Draw }[]` |
70
- | `listDrawsByPool(client, pool)`, `listDrawsByPurchaser(client, wallet)` | same |
71
- | `listReferralLinksByReferrer(client, referrer)` | `{ address, data: ReferralLink }[]` |
72
- | `resolveReferral(client, purchaser, pool)` | `{ link, referrer, referral } \| null` |
73
- | `fetchEvents(client, signature)` | `GaboxEvent[]` |
74
- | `decodeEvents(logs: string[])`, `decodeEvent(bytes)` | pure |
75
- | `watchDraw(client, drawAddress, { signal?, onChange? })` | `Promise<Draw>` resolves on delivery |
76
-
77
- `Pool` fields: `creator`, `mint`, `tokenProgram`, `vault`, `bump`, `packTokens`, `feeBps`,
78
- `seedLamports`, `seedTokens`, `tiers: Tier[]`, `nextSeq`, `reserved`.
79
-
80
- `Draw` fields: `pool`, `purchaser`, `seq`, `status: DrawStatus` (`Pending | Ready`), `requestSlot`,
81
- `lastAttemptSlot`, `attempts`, `maximum`, `minimum`, `prizes`, `amount`, `randomness`, `timedOut`.
82
-
83
- `GaboxEvent` is a union on `name`: `PoolCreated`, `PrizesFunded`, `PackBought`, `RandomnessRetried`,
84
- `DrawResolved`, `PrizeRedeemed`, `TokensSold`. `PackBought.data` has `pool`, `seq`, `purchaser`,
85
- `venue`, `tokensBought`, `venueDebit`, `feeLamports`, `referralLamports`, `protocolLamports`,
86
- `vrfDebit`, `prizes`. `DrawResolved.data` has `pool`, `seq`, `purchaser`, `amount`, `randomness`,
87
- `timedOut`.
88
-
89
- ## Addresses (`src/pdas.ts`), all async
90
-
91
- `poolAddress(mint)`, `drawAddress(pool, seq)`, `vaultAddress(mint, tokenProgram?)`,
92
- `associatedTokenAddress(owner, mint, tokenProgram?)`, `referralLinkAddress(referee)`,
93
- `referralAddress(pool, referrer)`, `feeCollectorWsolAddress()`, `vrfIdentityAddress()`.
94
- Also the generated `findPoolPda`, `findDrawPda`, `findReferralLinkPda`, `findIdentityPda`.
95
-
96
- ## Math (`src/math.ts`), pure
97
-
98
- | function | signature | notes |
99
- | --- | --- | --- |
100
- | `share` | `(amount: bigint, bps: bigint) => bigint` |
101
- | `DEFAULT_TIERS` | `readonly Tier[]` | 75% / 20% / 4% / 1% odds at 0.52× / 1.2× / 3× / 20× |
102
- | `seedTokens` | `(packTokens: bigint, tiers: Tier[]) => bigint` |
103
- | `quote` | `(base: bigint, tiers: Tier[], inventory: bigint, reserved: bigint) => Offer` |
104
- | `uncappedMaximum` | `(base: bigint, tiers: Tier[]) => bigint` |
105
- | `tierAmount` | `(base: bigint, multiplierBps: number) => bigint` |
106
- | `choose` | `(prizes: Prize[], ticket: number) => bigint` |
107
- | `maxMultiplierBps`, `averageMultiplierBps` | `(tiers: Tier[]) => number` |
108
- | `validateTiers`, `validatePack` | throw `GaboxMathError` on a bad table |
109
-
110
- Types: `Tier = { multiplierBps: number; tickets: number }`, `Prize = { amount: bigint; tickets: number }`,
111
- `Offer = { prizes: Prize[]; maximum: bigint; minimum: bigint }`. Constants `BPS = 10_000n`, `TICKETS = 65_536`,
112
- `TIERS = 8`.
113
-
114
- ## Constants (`src/ids.ts`)
115
-
116
- `GABOX_PROGRAM_ID`, `PACK_TOKENS`, `PROTOCOL_FEE_BPS` (`100n`), `MAX_FEE_BPS` (`100`),
117
- `REFERRAL_FEE_BPS` (`100n`), `RETRY_SLOTS` (`300n`), `MAX_ATTEMPTS` (`3`), `TIMEOUT_SLOTS`
118
- (`216_000n`), `WSOL_MINT`, `TOKEN_PROGRAM_ADDRESS`, `TOKEN_2022_PROGRAM_ADDRESS`,
119
- `ASSOCIATED_TOKEN_PROGRAM_ADDRESS`, `PUMP_PROGRAM_ADDRESS`, `PUMP_SWAP_PROGRAM_ADDRESS`,
120
- `VRF_PROGRAM_ADDRESS`, `VRF_DEFAULT_QUEUE`.
121
-
122
- ## Venue layer (`pump` namespace, `@gabox-labs/sdk/pump`)
123
-
124
- | function | signature |
13
+ | `fetchPoolInventory(client, mint)` | pool, vault inventory, reservations, free inventory |
14
+ | `getOffer(client, mint, options?)` | current `PackOffer` |
15
+ | `listPools(client)` / `listDrawsByPurchaser(client, wallet)` | current account scans |
16
+ | `fetchWalletActivity(client, wallet)` | `WalletActivity \| null`, a buyer's lifetime `packsBought`/`nativeSpent` |
17
+
18
+ `Pool` records `creator`, `mint`, `vault`, `quoteMint`, `packTokens`, `seedQuoteAmount`, `tiers`,
19
+ `nextSeq`, `reserved`, `quoteConfig` (LaunchLab's global config for the quote) and
20
+ `quoteTokenProgram` (classic SPL Token or Token-2022). It stores no base token program, because the
21
+ coin is always classic SPL Token, and no fee, because Gabox charges none.
22
+
23
+ `PackOffer` gives `quoteAmount` (the whole pack price in the quote token, the venue's fees
24
+ included), `quoteMint`, `quoteDecimals`, `quoteSymbol`, `solAmount` (the same price in lamports
25
+ through the route provider, or `null`), `venue` (`'launchlab'` or `'cpmm'`), the frozen prize table,
26
+ `maximum`, `minimum`, `uncapped`, the inventory numbers and `isSeeded`.
27
+
28
+ ## Builders
29
+
30
+ Each returns a kit transaction message. Nothing here signs or sends.
31
+
32
+ | Function | Input highlights |
33
+ | --- | --- |
34
+ | `createMachine` | `name`, `symbol`, `uri`, `tiers?`, `quote?`, `raise?`, `payWith?`, `maxSeedQuoteIn`, `maxSeedNativeDebit`, `extraSeedTokens?` |
35
+ | `buyPack` | `maxQuoteIn`, `minMaximum`, `maxNativeDebit`, `payWith?`, `venue?`, `seq?` |
36
+ | `fundPrizes` | donate coins the funder already holds into the prize vault |
37
+ | `sellTokens` | `amount`, `minQuoteOutput`, `maxNativeDebit`, `receive?`, `venue?` |
38
+ | `retryDraw` / `expireDraw` | permissionless recovery |
39
+ | `raydium.claimCreatorFee` | `creator`, `quoteMint?`; sweeps every coin that wallet launched against that quote |
40
+ | `raydium.collectCreatorFee` | `mint`, `creator`; one graduated coin's CPMM creator fee |
41
+
42
+ On a WSOL pool every builder wraps the SOL its trade needs into the wallet's WSOL account and closes
43
+ that account afterwards, so the wallet spends and receives plain SOL. Closing unwraps any WSOL the
44
+ wallet already held.
45
+
46
+ On any other pool the wallet's quote account is created if missing, and `payWith: 'sol'` (the
47
+ default) prepends a swap that turns SOL into the quote token. `receive: 'sol'` appends the reverse
48
+ after a sale. `payWith: 'quote'` and `receive: 'quote'` skip the swap. The swap and the Gabox
49
+ instruction always share one transaction.
50
+
51
+ `createMachine` builds one transaction with LaunchLab's `initialize_v2` first and `initialize_pool`
52
+ second. The mint keypair and the creator both sign. It buys a mandatory seed,
53
+ `seedTokens(PACK_TOKENS, tiers)`: enough for a 3x top prize on the first pack, or the table's full
54
+ top prize when that pays less than 3x. A table's top tier can be at most 20x. `extraSeedTokens`
55
+ (default `0n`) adds more in the same trade. `seedCostEstimate` returns `seedTokens`,
56
+ `extraSeedTokens`, `totalSeedTokens` and the exact `quoteAmount` a fresh curve would charge.
57
+
58
+ `buyPack` also resolves a per-wallet `WalletActivity` account (`findActivityPda({ purchaser })` or
59
+ `activityAddress(wallet)`); callers do not pass it. It does not create the purchaser's coin account
60
+ either: the program declares it `init_if_needed`, so Anchor does that and the purchaser pays its rent
61
+ once.
62
+
63
+ `maxQuoteIn` and `maxSeedQuoteIn` are venue slippage caps in the quote token, and each is also the
64
+ amount put in the wallet's quote account before the trade. `maxNativeDebit` and
65
+ `maxSeedNativeDebit` are separate lamport caps for what the handler watches: venue account rent, and
66
+ the VRF request on a pack buy.
67
+
68
+ `createMachine` takes `quote: { mint }` and `raise`. The quote defaults to wrapped SOL, whose raise
69
+ the program pins per cluster; any other quote needs a `raise` in its own base units, at or above
70
+ `min_quote_fund_raising` in its config. `seedCostEstimate(client, tiers?, { extraSeedTokens?,
71
+ quote?, raise? })` reports the seed cost in the quote token, its decimals and symbol, the raise, and
72
+ `solAmount` through the route provider.
73
+
74
+ ## The venue
75
+
76
+ | Function | Result |
77
+ | --- | --- |
78
+ | `raydium.resolveVenue(client, { mint, user, venue? })` | `ResolvedVenue`: `kind`, `program`, `poolState`, `status`, `creator`, `ammConfig`, `remainingCurveBase`, `buyAccounts`, `sellAccounts`, `quoteBuy`, `quoteSell` |
79
+ | `raydium.curveQuote(client, mint, tokens)` | the buy price, for a display |
80
+ | `raydium.sellQuote(client, mint, tokens)` | the sale proceeds |
81
+ | `raydium.newCurveBuyCost(client, tokens, { quoteConfig, raise })` | what a buy costs on a curve that does not exist yet |
82
+ | `raydium.findCpmmPool(client, { mint, creator, cpswapConfig, quoteMint? })` | the migrated CPMM pool, proved from its own data |
83
+ | `raydium.fetchCurveSettings(client, quoteConfig)` | the fee rates, the migrate fee and the CPMM fee tier, read from chain |
84
+ | `raydium.fetchCreatorFees(client, { creator, mint?, quoteMint? })` | `{ quoteMint, curveQuote, cpmmQuote, cpmmTokens }` |
85
+ | `raydium.fetchQuoteConfig(client, mint)` | LaunchLab's global config for a quote, or `null` |
86
+ | `raydium.isQuoteSupported(client, mint)` | can a machine be priced in this mint? |
87
+ | `raydium.fetchQuoteAsset(client, mint)` | the config, token program, decimals, symbol and minimum raise |
88
+ | `raydium.quoteAccountFor(user, quoteMint, quoteTokenProgram)` | the account both venues settle in |
89
+ | `raydium.wsolAccountFor(user)` | the same, for a WSOL pool |
90
+
91
+ `resolveVenue` takes an optional `quote: { mint, config, tokenProgram }`. Left out, it reads the
92
+ Gabox pool for it, so a caller that already holds a `Pool` saves a round trip by passing it.
93
+
94
+ `resolveVenue` follows the LaunchLab pool's `status`: `0` routes to the curve, `2` to the CPMM pool,
95
+ and `1` throws because the coin is migrating right now. A migrated CPMM pool does not have to sit at
96
+ a derived address, so it proves itself from its own data, and its fee tier, its two vaults and its
97
+ oracle are read out of the pool account. The program checks the same things.
98
+
99
+ `quoteBuy` on the curve throws when the amount is larger than the curve has left to sell. That is not
100
+ a price failure: the program requires an exact one-pack delta, so such a buy cannot succeed until the
101
+ coin graduates.
102
+
103
+ ## Math and low-level builders
104
+
105
+ `raydium.curve` exports exact bigint ports of Raydium's own math: `curveBuyExactOut`,
106
+ `curveBuyExactIn`, `curveSellExactIn`, `initialCurve`, `cpmmSwapBaseOutput`, `cpmmSwapBaseInput`, and
107
+ the `ceilDivRate` / `preFeeAmount` helpers they are built from. Every rounding step matches
108
+ Raydium's.
109
+
110
+ `raydium.getLaunchInstruction(input, ids)` builds LaunchLab's `initialize_v2` with the pinned launch
111
+ shape. `raydium.getCurveBuyExactInInstruction(client, input, ids?)` builds a plain curve buy that
112
+ Gabox never makes itself; the devnet end-to-end test uses it to buy a curve out and force graduation.
113
+
114
+ `initialize_pool`'s `venue` account has no generated default, because LaunchLab has a different
115
+ address on each cluster and a default would be wrong on one of them. Every builder here passes it
116
+ from `raydiumIds(client.cluster)`; a caller using the generated builder directly has to do the same.
117
+
118
+ `raydiumIds(cluster)` gives one cluster's Raydium deployment. Mainnet and devnet run different
119
+ programs and PDAs, and a WSOL launch raises 3 SOL on devnet where mainnet raises 85. `localnet` uses
120
+ the mainnet set, which is what the program's default build pins.
121
+
122
+ ## Routes
123
+
124
+ A route provider swaps SOL into a pool's quote token, and back. `createClient` gives mainnet
125
+ `jupiterRoute()` and every other cluster `null`; pass `route` to override, or `null` to switch the
126
+ swap leg off.
127
+
128
+ | Function | Result |
125
129
  | --- | --- |
126
- | `resolveVenue` | `(client, { mint; user; venue?; feeRecipientIndex?; buybackRecipientIndex? }) => Promise<ResolvedVenue>` |
127
- | `curveQuote` | `(client, mint, tokens: bigint, { user?, venue? }?) => Promise<bigint>` lamports to buy `tokens` |
128
- | `sellQuote` | `(client, mint, tokens: bigint, { user?, venue? }?) => Promise<bigint>` lamports a sale returns |
129
- | `newCurveBuyCost` | `(client, tokens: bigint) => Promise<bigint>` on a fresh curve |
130
- | `wsolAccountFor` | `(user: Address) => Address` |
131
- | `tokenAccountAmount` | `(data: Uint8Array) => bigint` |
132
-
133
- `ResolvedVenue`: `{ kind: 'pump' | 'pumpswap'; program; mint; tokenProgram; complete; creator;
134
- buyAccounts; sellAccounts; quoteBuy(tokens); quoteSell(tokens) }`. `VenueKind = 'pump' | 'pumpswap'`.
135
-
136
- ## Generated client (`generated` namespace, `@gabox-labs/sdk/generated`)
137
-
138
- Codama output for `@solana/kit`: `get*Instruction` builders, `fetch*` / `decode*` account readers
139
- (these take a kit `Rpc`, i.e. `gabox.rpc`), event codecs, PDAs, and errors:
140
-
141
- - `isGaboxV2Error(error, transactionMessage, code?)` narrows a kit `SolanaError`.
142
- - `getGaboxV2ErrorMessage(code)` readable outside `NODE_ENV=production`.
143
- - `GABOX_V2_ERROR__*` codes, from `6000`: `ARITHMETIC`, `INVALID_DISTRIBUTION`,
144
- `UNFUNDED_EXPECTATION`, `UNSUPPORTED_MINT`, `INSOLVENT_INVENTORY`, `PRIZE_CAP_CHANGED`,
145
- `PACK_TOO_SMALL`, `JACKPOT_BELOW_ONE_PACK`, `INVALID_VENUE`, `INCORRECT_TOKEN_DELTA`,
146
- `SLIPPAGE_EXCEEDED`, `NOT_PENDING`, `NOT_READY`, `RETRY_TOO_SOON`, `RETRY_UNAVAILABLE`,
147
- `NOT_EXPIRED`, `ZERO_AMOUNT`, `INVALID_FEE`, `COIN_NOT_CREATED_HERE`, `INVALID_REFERRAL`,
148
- `NOTHING_OWED_TO_REFERRER`, `INVALID_FEE_COLLECTOR`.
130
+ | `jupiterRoute({ url?, slippageBps? })` | a `RouteProvider` over Jupiter's swap API, for mainnet |
131
+ | `raydiumCpmmRoute(poolAddress)` | a `RouteProvider` over one Raydium CPMM pool, for devnet |
132
+ | `routeQuoteIn(client, provider, { quoteMint, amount, user })` | a swap that buys at least `amount`, exact-out first |
133
+ | `routeQuoteOut(client, provider, { quoteMint, amount, user })` | a swap that turns `amount` into SOL |
134
+ | `solPriceOf(client, quoteMint, amount)` | the exact-out SOL cost of `amount`, or `null` |
135
+ | `assertRouteIsSafe(route, { forbidden, settlesIn })` | refuse a route that touches Gabox state or settles elsewhere |
136
+
137
+ A `Route` is `{ instructions, lookupTables, inAmount, outAmount, mode, computeUnits }`. `mode` is
138
+ `'exactOut'` or `'exactIn'`: the amounts are bounds, so an exact-out route states the most it spends
139
+ and an exact-in route the least it pays out.
140
+
141
+ `computeUnits` is what the swap costs on top of the Gabox instruction, because the two share one
142
+ budget. A builder adds it to its own limit and caps the total at `MAX_COMPUTE_UNIT_LIMIT`; an
143
+ explicit `computeUnitLimit` still wins. Jupiter returns the number with the route, falling back to
144
+ `JUPITER_DEFAULT_COMPUTE_UNITS` when its response carries none; `raydiumCpmmRoute` uses
145
+ `CPMM_ROUTE_COMPUTE_UNITS`, measured on devnet, which holds because it is always one pool. Jupiter answers `NO_ROUTES_FOUND` for exact-out whenever the best route
146
+ has more than one hop, which is why `routeQuoteIn` falls back to exact-in and leaves the change in
147
+ the buyer's quote account.
148
+
149
+ ## Events
150
+
151
+ `decodeEvents` and `fetchEvents` return only `PoolCreated`, `PrizesFunded`, `PackBought`,
152
+ `RandomnessRetried`, `DrawResolved` and `TokensSold`. None carries a Gabox fee field any more.
153
+ `PackBought` reports `quoteMint`, `quoteDebit` (what the venue charged, in that token),
154
+ `nativeDebit` (venue account rent), `vrfNativeDebit` and `totalNativeDebit`.
155
+
156
+ Delivery closes a draw atomically; use `findResolvedDraw` rather than a Ready or claim flow. It
157
+ searches a bounded recent signature history, so production indexers should persist `DrawResolved`
158
+ events as the authoritative history.