@gabox-labs/sdk 0.1.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,37 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@gabox-labs/sdk`. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
4
+ and the project uses [Semantic Versioning](https://semver.org/) with the `0.x` rule from `CONTRIBUTING.md`.
5
+
6
+ ## [Unreleased]
7
+
8
+ ## [0.1.1] - 2026-09-11
9
+
10
+ Same content as 0.1.0. That version was published and then unpublished on the same day, and npm
11
+ does not allow a version number to be reused, so the first installable release is 0.1.1.
12
+
13
+ ### Added
14
+
15
+ - `npm run check:program` and `RELEASE.md`: the program → SDK → npm → app release workflow.
16
+
17
+ ## [0.1.0] - 2026-09-11
18
+
19
+ First public release.
20
+
21
+ ### Added
22
+
23
+ - `createClient({ cluster, url?, wsUrl?, addressLookupTables? })`: the one setup step. Every
24
+ chain-touching function takes the client it returns.
25
+ - Builders: `createMachine`, `buyPack`, `sellTokens`, `fundPrizes`, `fundPrizesWithBuy`,
26
+ `retryDraw`, `expireDraw`, `bindReferrer`, `claimReferral`.
27
+ - Readers: `getOffer`, `fetchPoolByMint`, `fetchPoolInventory`, `fetchDraw`, `listPools`,
28
+ `listDraws`, `fetchEvents`, `watchDraw`, `drawAvailability`, and the referral readers.
29
+ - The prize-table math as pure `bigint` functions: `jackpotTiers`, `quote`, `seedTokens`, `share`.
30
+ - The `generated` namespace (instruction builders, codecs, error codes) and the `pump` namespace
31
+ (venue routing and quotes).
32
+ - An agent skill at `skills/gabox-sdk/`.
33
+
34
+ ### Notes
35
+
36
+ - Devnet only. The devnet program upgrade to the build this SDK targets is pending; see the
37
+ README's Status section.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gabox Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,511 @@
1
+ # `@gabox-labs/sdk`
2
+
3
+ The TypeScript SDK for gabox machines on Solana. Built on [`@solana/kit`](https://github.com/anza-xyz/kit) 7.
4
+
5
+ With it you can create a machine, price a pack, buy one, wait for the draw, and sell the prize.
6
+ Every function returns plain kit values: `Address`, `bigint`, and transaction messages your wallet
7
+ signs. The SDK never signs or sends anything itself.
8
+
9
+ ```sh
10
+ npm install @gabox-labs/sdk @solana/kit
11
+ ```
12
+
13
+ ESM only. Node 24 or newer, or any bundler. `@solana/kit` is a peer dependency, so your app keeps
14
+ one copy of it.
15
+
16
+ > **Devnet only, for now.** The program runs on Solana devnet. A mainnet client is supported by the
17
+ > SDK, but there is nothing to talk to there yet. See [Status](#status).
18
+
19
+ **Using an AI coding agent?** This repo ships a skill. See [For AI agents](#for-ai-agents).
20
+
21
+ ---
22
+
23
+ ## What is a machine?
24
+
25
+ A **machine** is a prize pool tied to a brand-new [Pump.fun](https://pump.fun) coin. Creating a
26
+ machine launches the coin and the pool in one transaction.
27
+
28
+ A **pack** is a fixed number of tokens: 1,000,000, which is 0.1% of the coin's supply. The coin's
29
+ market decides what a pack costs, so the pack price follows the coin. A pack is bought at the
30
+ venue (Pump's bonding curve, or PumpSwap after graduation), and its tokens go into the pool's
31
+ vault.
32
+
33
+ Each pack creates a **draw**. The draw freezes a **prize table**: up to 8 prizes, each a multiple of
34
+ one pack, each with a number of tickets out of 65,536. Verifiable randomness (MagicBlock VRF) picks
35
+ one prize, and the tokens are sent straight to the buyer's wallet. The buyer signs once, at
36
+ purchase. Keeping the prize needs nothing else. Selling it later is one more transaction.
37
+
38
+ The **creator** chooses the jackpot size (for example 5×) and a risk profile. The creator buys the
39
+ tokens that back the jackpot at creation, and earns a fee on every pack.
40
+
41
+ ---
42
+
43
+ ## Quick start
44
+
45
+ ```ts
46
+ import {
47
+ createClient,
48
+ drawAddress,
49
+ fetchPoolByMint,
50
+ getOffer,
51
+ buyPack,
52
+ share,
53
+ watchDraw,
54
+ } from '@gabox-labs/sdk';
55
+ import {
56
+ address,
57
+ getSignatureFromTransaction,
58
+ sendAndConfirmTransactionFactory,
59
+ signTransactionMessageWithSigners,
60
+ } from '@solana/kit';
61
+
62
+ // The RPC endpoint goes here. Leave `url` out to use Solana's public devnet endpoint.
63
+ const gabox = createClient({
64
+ cluster: 'devnet',
65
+ url: 'https://devnet.helius-rpc.com/?api-key=…', // your provider; wsUrl is derived
66
+ });
67
+ const mint = address('…the coin…');
68
+ const purchaser = /* a TransactionSigner from your wallet */;
69
+
70
+ // 1. What does a pack cost, and what can it win?
71
+ const offer = await getOffer(gabox, mint, { user: purchaser.address });
72
+ console.log(offer.quoteLamports, offer.prizes, offer.maximum);
73
+
74
+ // 2. Build the purchase. Pin the draw's sequence number so you know its address.
75
+ const pool = await fetchPoolByMint(gabox, mint);
76
+ const seq = pool!.nextSeq;
77
+ const maxQuoteIn = (offer.quoteLamports * 102n) / 100n; // 2% slippage
78
+ const message = await buyPack(gabox, {
79
+ mint,
80
+ purchaser,
81
+ seq,
82
+ maxQuoteIn,
83
+ minMaximum: offer.maximum,
84
+ maxTotalDebit: maxQuoteIn + share(maxQuoteIn, 200n) + 20_000_000n,
85
+ });
86
+
87
+ // 3. Sign and send with kit.
88
+ const transaction = await signTransactionMessageWithSigners(message);
89
+ const send = sendAndConfirmTransactionFactory({
90
+ rpc: gabox.rpc,
91
+ rpcSubscriptions: gabox.rpcSubscriptions,
92
+ });
93
+ await send(transaction, { commitment: 'confirmed' });
94
+ console.log('bought:', getSignatureFromTransaction(transaction));
95
+
96
+ // 4. Wait for the prize.
97
+ const draw = await drawAddress(offer.pool, seq);
98
+ const resolved = await watchDraw(gabox, draw, {
99
+ onChange: (d) => console.log('status', d.status),
100
+ });
101
+ console.log('won', resolved.amount, 'tokens');
102
+ ```
103
+
104
+ ---
105
+
106
+ ## The client
107
+
108
+ `createClient` is the one setup step. Call it once, and pass the result to every SDK function that
109
+ reads or writes the chain.
110
+
111
+ ```ts
112
+ import { createClient } from '@gabox-labs/sdk';
113
+
114
+ const gabox = createClient({ cluster: 'devnet' });
115
+
116
+ // Your own RPC provider:
117
+ const gabox = createClient({
118
+ cluster: 'devnet',
119
+ url: 'https://devnet.helius-rpc.com/?api-key=…',
120
+ // wsUrl is optional. It follows `url` with `wss://` when left out.
121
+ });
122
+ ```
123
+
124
+ | field | required | default |
125
+ | --------------------- | -------- | ------------------------------------------------------------------------------------------------ |
126
+ | `cluster` | yes | none. One of `'devnet'`, `'mainnet-beta'`, `'localnet'` |
127
+ | `url` | no | the cluster's public endpoint. `http://127.0.0.1:8899` for `localnet` |
128
+ | `wsUrl` | no | `url` with `https` → `wss`. The cluster's default WebSocket endpoint when `url` is also left out |
129
+ | `addressLookupTables` | no | the shared lookup table on `devnet`; none on other clusters. Pass `{}` to turn compression off |
130
+
131
+ The client is a plain object with `cluster`, `url`, `wsUrl`, `rpc`, `rpcSubscriptions`, and
132
+ `addressLookupTables`. If you need a custom transport, spread it and replace `rpc`:
133
+
134
+ ```ts
135
+ const gabox = { ...createClient({ cluster: 'devnet' }), rpc: myRpc };
136
+ ```
137
+
138
+ ### The URL is checked against the cluster
139
+
140
+ `cluster` has no default, and the URL must agree with it. This is on purpose: every address the
141
+ SDK uses exists on every cluster, so a wrong URL does not fail. It sends a real transaction to the
142
+ wrong network.
143
+
144
+ - `devnet` needs a URL that contains `devnet`. A URL that names no cluster is refused too.
145
+ - `mainnet-beta` and `localnet` refuse a URL that names a different cluster. A URL that names no
146
+ cluster is fine, because private endpoints and local validators often do not say.
147
+
148
+ Both `url` and `wsUrl` are checked. A mismatch throws from `createClient`, before any request.
149
+
150
+ ---
151
+
152
+ ## Flows
153
+
154
+ Every builder returns a `GaboxTransactionMessage`: a version 0 message with the fee payer and a
155
+ fresh blockhash already set. Sign it with `signTransactionMessageWithSigners` and send it with kit.
156
+ A blockhash lasts about a minute, so build the message when the user is ready to sign, not when the
157
+ page loads.
158
+
159
+ Every builder also accepts `computeUnitLimit`, `computeUnitPrice` (a priority fee, in micro-lamports
160
+ per unit), and `addressLookupTables`. The defaults are generous.
161
+
162
+ ### Create a machine
163
+
164
+ ```ts
165
+ import { createMachine, seedCostEstimate } from '@gabox-labs/sdk';
166
+ import { generateKeyPairSigner } from '@solana/kit';
167
+
168
+ const mintKeypair = await generateKeyPairSigner();
169
+ const seed = await seedCostEstimate(gabox, 50_000, 'balanced');
170
+
171
+ const message = await createMachine(gabox, {
172
+ creator, // TransactionSigner. Pays for everything.
173
+ mintKeypair, // Signs once, for the new coin.
174
+ name: 'Lucky Cat',
175
+ symbol: 'CAT',
176
+ uri: 'https://…/metadata.json',
177
+ feeBps: 100, // the creator's fee per pack, 0–100 bps (max 1%)
178
+ riskProfile: 'balanced', // 'conservative' | 'balanced' | 'jackpot'
179
+ jackpotBps: 50_000, // the top prize: 5× one pack
180
+ maxSeedLamports: (seed.lamports * 105n) / 100n,
181
+ });
182
+ ```
183
+
184
+ One transaction, two signers: the creator and the new mint. The creator pays for the coin, the
185
+ pool, and the **seed**.
186
+
187
+ The seed is the inventory that backs the jackpot. The first pack must be able to pay the top prize
188
+ in full. A 5× jackpot needs four extra packs of tokens in the vault; a 10× jackpot needs nine. A 1×
189
+ jackpot needs no seed. `seedCostEstimate` tells you what that costs on a fresh Pump curve, in
190
+ lamports and tokens. `maxSeedLamports` is the creator's slippage cap on that buy.
191
+
192
+ - `jackpotBps` is the largest prize, in basis points of one pack. `10_000` is 1×. `50_000` is 5×.
193
+ - `riskProfile` decides how the odds are spread. Every profile returns 95% of a pack's value on
194
+ average. `conservative` keeps more of it in the common prize; `jackpot` moves more into the rare
195
+ ones. See `jackpotTiers(jackpotBps, profile)` to preview the table before creating.
196
+
197
+ ### Price a pack
198
+
199
+ ```ts
200
+ import { getOffer } from '@gabox-labs/sdk';
201
+
202
+ const offer = await getOffer(gabox, mint, { user: buyer.address });
203
+ ```
204
+
205
+ `getOffer` reads the pool, the vault, and the venue, and returns everything a buy screen needs:
206
+
207
+ | field | meaning |
208
+ | -------------------------------------- | ----------------------------------------------------------------------------- |
209
+ | `quoteLamports` | the pack price right now: what the venue charges for one pack, venue fees included |
210
+ | `feeLamports` | the creator's fee, paid on top |
211
+ | `protocolLamports` | the protocol's 1%, paid on top |
212
+ | `prizes` | the prize table this pack would get: `{ amount, tickets }`, amounts in tokens |
213
+ | `maximum`, `minimum` | the largest and smallest prize on that table |
214
+ | `uncapped` | the jackpot with no inventory cap. Equal to `maximum` when the pool is healthy |
215
+ | `isSeeded` | `maximum === uncapped`. Show a warning when this is `false` |
216
+ | `venue` | `'pump'` or `'pumpswap'`: where the buy would route right now |
217
+ | `inventory`, `reserved`, `free` | vault balance, tokens reserved by open draws, and the difference |
218
+ | `maxMultiplierBps`, `averageMultiplierBps` | the table's top and ticket-weighted average multipliers |
219
+
220
+ Prizes can be capped. The table is fixed, but a prize can never exceed what the vault holds beyond
221
+ what open draws already reserve. When the cap bites, `maximum < uncapped` and `isSeeded` is
222
+ `false`. The pack still sells. It just pays less than the table says, and a buyer should see that.
223
+
224
+ `offerFromState` does the same computation from values you already fetched, with no network call.
225
+
226
+ ### Buy a pack
227
+
228
+ ```ts
229
+ import { buyPack, share } from '@gabox-labs/sdk';
230
+
231
+ const maxQuoteIn = (offer.quoteLamports * 102n) / 100n;
232
+ const message = await buyPack(gabox, {
233
+ mint,
234
+ purchaser, // TransactionSigner. The only signer.
235
+ maxQuoteIn,
236
+ minMaximum: offer.maximum,
237
+ maxTotalDebit: maxQuoteIn + share(maxQuoteIn, 200n) + 20_000_000n,
238
+ seq: pool.nextSeq, // optional, see below
239
+ referrer, // optional, see Referrals
240
+ });
241
+ ```
242
+
243
+ The buyer signs three limits:
244
+
245
+ | limit | what it caps |
246
+ | --------------- | ----------------------------------------------------------------------------------------------------------------------- |
247
+ | `maxQuoteIn` | the venue price, in lamports. The token count is fixed, so the price is the only thing that moves. Add your slippage. |
248
+ | `minMaximum` | the top prize, in tokens. Fails if inventory dropped so far that the top prize is now smaller than this. |
249
+ | `maxTotalDebit` | venue price + creator fee + protocol fee + the randomness request fee, all together. Both fees are ~1% of the price. |
250
+
251
+ `maxTotalDebit` does not include account rent or the transaction fee. Budget a few million lamports
252
+ on top; the example above adds 0.02 SOL.
253
+
254
+ **`seq` pins the draw.** Every draw has a sequence number, and its address is derived from it. Pass
255
+ `seq: pool.nextSeq` so you know the draw's address before the transaction lands, and so that a
256
+ retry after a stale blockhash cannot buy a second pack by accident. Then `drawAddress(pool, seq)`
257
+ gives you the address to watch.
258
+
259
+ On the PumpSwap route, `buyPack` also adds the instructions that wrap SOL into WSOL for the buyer.
260
+ You do not need to handle that.
261
+
262
+ ### Wait for the draw
263
+
264
+ ```ts
265
+ import { watchDraw } from '@gabox-labs/sdk';
266
+
267
+ const resolved = await watchDraw(gabox, draw, {
268
+ signal: AbortSignal.timeout(180_000),
269
+ onChange: (d) => console.log(d.status, d.attempts),
270
+ });
271
+ // resolved.amount: tokens won, already in the buyer's wallet
272
+ ```
273
+
274
+ `watchDraw` subscribes to the draw account and resolves when the prize is delivered. A draw that
275
+ was already delivered resolves at once. There is no built-in timeout; pass a `signal` with the
276
+ patience your UI wants. Randomness usually arrives within a few seconds.
277
+
278
+ ### Keep or sell
279
+
280
+ Keeping the prize needs nothing. The tokens are already in the buyer's associated token account.
281
+
282
+ Selling goes through the SDK, so the sale routes to the right venue:
283
+
284
+ ```ts
285
+ import { sellTokens, pump } from '@gabox-labs/sdk';
286
+
287
+ const grossOutput = await pump.sellQuote(gabox, mint, resolved.amount, { user: purchaser.address });
288
+ const message = await sellTokens(gabox, {
289
+ mint,
290
+ seller: purchaser,
291
+ amount: resolved.amount,
292
+ minQuoteOutput: (grossOutput * 95n) / 100n, // 5% slippage. Must be > 0.
293
+ });
294
+ ```
295
+
296
+ `minQuoteOutput` is the floor on what the venue pays, before the protocol's 1% of the proceeds. On
297
+ the Pump route the seller receives SOL. On the PumpSwap route the seller receives WSOL in their
298
+ WSOL account; add an unwrap if they want lamports.
299
+
300
+ ### Fund a machine's prizes
301
+
302
+ Anyone can add tokens to a machine's vault. This raises the cap, which uncaps prizes.
303
+
304
+ ```ts
305
+ import { fundPrizes, fundPrizesWithBuy, seedShortfall } from '@gabox-labs/sdk';
306
+
307
+ // Tokens the donor already holds:
308
+ await fundPrizes(gabox, { mint, funder, amount });
309
+
310
+ // Or buy them and donate in one transaction. `seedShortfall(offer)` is the amount that
311
+ // uncaps the top prize right now.
312
+ await fundPrizesWithBuy(gabox, { mint, funder, tokens: seedShortfall(offer), maxQuoteIn });
313
+ ```
314
+
315
+ Donations cannot be withdrawn.
316
+
317
+ ### Referrals
318
+
319
+ A wallet can bind itself to a referrer, once and permanently. After that, every pack it buys pays
320
+ the referrer 1% of the creator's fee on that pack. The buyer pays nothing extra.
321
+
322
+ ```ts
323
+ import { bindReferrer, claimReferral, fetchReferralReward } from '@gabox-labs/sdk';
324
+
325
+ // The referred wallet signs the binding:
326
+ await bindReferrer(gabox, referee, referrerAddress);
327
+
328
+ // Or bind and buy in one transaction. Ignored when the wallet is already bound:
329
+ await buyPack(gabox, { ...input, referrer: referrerAddress });
330
+
331
+ // The referrer reads and claims what a pool owes them:
332
+ const owed = await fetchReferralReward(gabox, poolAddress, referrer.address);
333
+ await claimReferral(gabox, referrer, poolAddress);
334
+ ```
335
+
336
+ ### Stuck draws
337
+
338
+ Randomness can fail to arrive. Two permissionless transactions keep a draw moving. Anyone can send
339
+ them.
340
+
341
+ ```ts
342
+ import { drawAvailability, retryDraw, expireDraw } from '@gabox-labs/sdk';
343
+
344
+ const state = await drawAvailability(gabox, draw);
345
+ // { canRetry, canExpire, slotsUntilRetry, slotsUntilExpiry, attempts, status }
346
+
347
+ if (state?.canRetry) await retryDraw(gabox, { payer, pool, draw, maxVrfDebit: 5_000_000n });
348
+ if (state?.canExpire) await expireDraw(gabox, { payer, pool, draw });
349
+ ```
350
+
351
+ - `retryDraw` asks for randomness again. Allowed 300 slots after the last attempt, up to 3 attempts
352
+ in total. The payer covers the request fee, capped by `maxVrfDebit`.
353
+ - `expireDraw` settles a draw that never received randomness, 216,000 slots (about one day) after
354
+ the purchase. It pays the **smallest prize** on the draw's table. It is not a refund. Tell buyers
355
+ this before they pay.
356
+
357
+ Use `drawAvailability` to show a countdown instead of sending a transaction that fails.
358
+
359
+ ---
360
+
361
+ ## Reading state
362
+
363
+ | function | returns |
364
+ | ----------------------------------------- | ------------------------------------------------------------- |
365
+ | `fetchPoolByMint(gabox, mint)` | the machine's `Pool`, or `null` |
366
+ | `fetchPoolAt(gabox, poolAddress)` | the same, by pool address |
367
+ | `fetchPoolInventory(gabox, mint)` | the pool plus live `inventory`, `reserved`, `free` |
368
+ | `fetchDraw(gabox, drawAddress)` | a `Draw`, or `null` once it is delivered and closed |
369
+ | `listPools(gabox)` | every machine |
370
+ | `listDraws(gabox, { pool?, purchaser? })` | open draws, filtered by machine, by buyer, or both |
371
+ | `listDrawsByPurchaser(gabox, wallet)` | "what am I still waiting on" |
372
+ | `listReferralLinksByReferrer(gabox, ref)` | every wallet bound to a referrer |
373
+ | `fetchEvents(gabox, signature)` | the SDK events one transaction emitted |
374
+
375
+ The `list*` functions use `getProgramAccounts`. Public RPC endpoints rate-limit that call, so cache
376
+ the result in your app rather than calling it on every page view.
377
+
378
+ Addresses are derived, not looked up: `poolAddress(mint)`, `drawAddress(pool, seq)`,
379
+ `vaultAddress(mint)`, `referralLinkAddress(wallet)`, `referralAddress(pool, referrer)`, and
380
+ `associatedTokenAddress(owner, mint)`.
381
+
382
+ ## Events
383
+
384
+ Every transaction emits typed events. Decode them from a confirmed signature:
385
+
386
+ ```ts
387
+ import { fetchEvents } from '@gabox-labs/sdk';
388
+
389
+ for (const event of await fetchEvents(gabox, signature)) {
390
+ if (event.name === 'PackBought') console.log(event.data.venueDebit, event.data.prizes);
391
+ if (event.name === 'DrawResolved') console.log(event.data.amount, event.data.timedOut);
392
+ }
393
+ ```
394
+
395
+ Event names: `PoolCreated`, `PackBought`, `DrawResolved`, `TokensSold`, `PrizesFunded`,
396
+ `RandomnessRetried`, `PrizeRedeemed`. `decodeEvents(logs)` does the same from log lines you already
397
+ have.
398
+
399
+ ## Errors
400
+
401
+ A failed transaction carries a program error code. The generated namespace decodes it:
402
+
403
+ ```ts
404
+ import { generated } from '@gabox-labs/sdk';
405
+
406
+ try {
407
+ await send(transaction, { commitment: 'confirmed' });
408
+ } catch (error) {
409
+ // `message` is the GaboxTransactionMessage you built. It tells the check which instruction is ours.
410
+ if (generated.isGaboxV2Error(error, message, generated.GABOX_V2_ERROR__SLIPPAGE_EXCEEDED)) {
411
+ // ask the user to widen maxQuoteIn and rebuild
412
+ } else if (generated.isGaboxV2Error(error, message)) {
413
+ console.log(generated.getGaboxV2ErrorMessage(error.context.code));
414
+ } else {
415
+ throw error;
416
+ }
417
+ }
418
+ ```
419
+
420
+ `getGaboxV2ErrorMessage` returns a readable message in development builds and a placeholder when
421
+ `NODE_ENV` is `production`, to keep bundles small.
422
+
423
+ The ones a UI will meet:
424
+
425
+ | error | when |
426
+ | -------------------- | -------------------------------------------------------------------------- |
427
+ | `SlippageExceeded` | the venue price moved past `maxQuoteIn`, or the sale returned less than `minQuoteOutput` |
428
+ | `PrizeCapChanged` | the top prize fell below `minMaximum` between quote and send |
429
+ | `RetryTooSoon` | `retryDraw` before 300 slots passed |
430
+ | `RetryUnavailable` | `retryDraw` after 3 attempts, or on a draw that is no longer pending |
431
+ | `NotExpired` | `expireDraw` before the deadline |
432
+ | `InvalidReferral` | a self-referral, or a link that does not match |
433
+ | `ZeroAmount` | a zero `amount` or a zero `minQuoteOutput` |
434
+
435
+ The SDK also throws plain `Error`s before building, for example when a mint has no machine, when
436
+ `maxQuoteIn` is not positive, or when a message would exceed Solana's 1,232-byte limit.
437
+
438
+ ---
439
+
440
+ ## Units and conventions
441
+
442
+ - **Tokens** are in base units. Every Pump coin has 6 decimals, so `1_000_000n` is one whole token
443
+ and a pack (`PACK_TOKENS`) is `1_000_000n * 1_000_000n`.
444
+ - **SOL** is in lamports. WSOL is used on the PumpSwap route, and the SDK handles the wrapping.
445
+ - **Every amount is a `bigint`.** Basis points are `number`. `share(amount, bps)` computes a share.
446
+ - **Addresses** are kit `Address` strings. The SDK never exposes `PublicKey` or `BN`.
447
+ - **Async by default.** Address derivation is async, because kit's PDA derivation is.
448
+
449
+ ## Constants
450
+
451
+ | constant | value | meaning |
452
+ | ----------------------- | ---------------------------------- | --------------------------------------------------------- |
453
+ | `PACK_TOKENS` | `1_000_000n * 1_000_000n` | tokens in one pack. Prefer `pool.packTokens` when you have a pool |
454
+ | `PROTOCOL_FEE_BPS` | `100n` | the protocol's 1% on every purchase and every sale |
455
+ | `MAX_FEE_BPS` | `100` | the largest creator fee: 1% of the venue price |
456
+ | `REFERRAL_FEE_BPS` | `100n` | the referrer's 1% of the creator fee |
457
+ | `RETRY_SLOTS` | `300n` | slots between randomness attempts |
458
+ | `MAX_ATTEMPTS` | `3` | randomness attempts per draw, counting the first |
459
+ | `TIMEOUT_SLOTS` | `216_000n` | slots until a draw can be expired |
460
+ | `TIERS`, `TICKETS` | `8`, `65_536` | rows in a prize table, and tickets per table |
461
+ | `GABOX_PROGRAM_ID` | `GaBoxR9nYcK1zeu8EvSJVHV3SrYpCFmvbh2MLgobMcUA` | the program |
462
+
463
+ ## Entry points
464
+
465
+ | import | what |
466
+ | ------------------------- | ---------------------------------------------------------------------------------------- |
467
+ | `@gabox-labs/sdk` | everything above: the client, builders, readers, math, events, constants |
468
+ | `@gabox-labs/sdk/generated` | the low-level generated client: instruction builders, account and event codecs, error codes. Also available as the `generated` namespace |
469
+ | `@gabox-labs/sdk/pump` | the venue layer: `resolveVenue`, `curveQuote`, `sellQuote`, and the Pump/PumpSwap account lists. Also available as the `pump` namespace |
470
+
471
+ The `generated` functions take a kit `Rpc` (`gabox.rpc`), not the client.
472
+
473
+ ---
474
+
475
+ ## For AI agents
476
+
477
+ This repository is agent-ready.
478
+
479
+ - **[`skills/gabox-sdk/SKILL.md`](skills/gabox-sdk/SKILL.md)** teaches an agent how to integrate the
480
+ SDK: setup, the flows, the limits a buyer signs, and the mistakes to avoid. It follows the
481
+ [Agent Skills](https://agentskills.io) format, so it installs into Claude Code, Cursor, Codex,
482
+ and others:
483
+
484
+ ```sh
485
+ npx skills add gabox-labs/gabox-sdk
486
+ ```
487
+
488
+ - **[`skills/gabox-sdk/references/api.md`](skills/gabox-sdk/references/api.md)** is a compact
489
+ reference of every exported function and type.
490
+ - **[`llms.txt`](llms.txt)** points a model at the right files.
491
+ - **[`AGENTS.md`](AGENTS.md)** is for agents working on this repository itself.
492
+
493
+ ## Status
494
+
495
+ - **Devnet only.** Mainnet is not live.
496
+ - The devnet program is being upgraded to the build this SDK targets. Until that upgrade lands,
497
+ transactions built by this SDK do not match the deployed program, and `fetchPoolByMint` reports
498
+ machines from the previous build as an "older devnet account layout". Watch the
499
+ [changelog](CHANGELOG.md) for the release that marks the upgrade done.
500
+ - The Pump bonding-curve route has an end-to-end test on devnet (`GABOX_DEVNET_E2E=1`). The
501
+ PumpSwap route (coins that graduated off the curve) has unit coverage only.
502
+ - The API is `0.x`. Breaking changes bump the minor version and are listed in the changelog.
503
+
504
+ ## Contributing
505
+
506
+ See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the development setup and the checks that run before
507
+ a release.
508
+
509
+ ## License
510
+
511
+ [MIT](LICENSE)