@gabox-labs/sdk 0.8.0 → 0.9.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/dist/index.js CHANGED
@@ -1,11 +1,19 @@
1
- import { Dt as POOL_DISCRIMINATOR, I as getFundPrizesInstruction, Ot as decodePool, Rt as DRAW_DISCRIMINATOR, W as getExpireDrawInstruction, _t as decodeWalletActivity, b as getRetryDrawInstruction, ft as findPoolPda, gt as WALLET_ACTIVITY_DISCRIMINATOR, ht as findActivityPda, k as getInitializePoolInstructionAsync, mt as findDrawPda, p as getSellTokensInstructionAsync, pt as findIdentityPda, st as getBuyPackInstructionAsync, zt as decodeDraw } from "./gabox-DGTCh34U.js";
2
- import { $ as readAccounts, $t as vrfIdentityAddress, A as wsolAccountFor, An as tierAmount, Bn as SLOT_HASHES_SYSVAR, Cn as TIERS, Ct as listDrawsByPurchaser, D as quoteAccountFor, Dn as quote, Dr as PLATFORM_ADMIN, E as newCurveReserves, En as maxMultiplierBps, Er as METAPLEX_PROGRAM_ADDRESS, Et as tiersOf, Fn as IDENTITY_SEED, Gt as tokenAccountAmount, Hn as VRF_DEFAULT_QUEUE, I as curveBuyExactOut, In as INSTRUCTIONS_SYSVAR, It as decodeCpmmAmmConfig, J as fetchQuoteAsset, Jt as associatedTokenAddress, Ln as MAX_ATTEMPTS, Lt as decodeCpmmPool, Mn as validatePack, N as cpmmSwapBaseInput, Nn as validateTiers, Nr as TOKEN_PROGRAM_ADDRESS, O as resolveVenue, On as resolveReservation, P as cpmmSwapBaseOutput, Pn as GABOX_PROGRAM_ID, Pr as WSOL_MINT, Qt as vaultAddress, Rn as PACK_TOKENS, S as fetchCurveSettings, Sn as TICKETS, St as listDrawsByPool, Tn as choose, Tr as LAUNCH_TOTAL_BASE_SELL, Tt as quotePool, Un as VRF_PROGRAM_ADDRESS, Vn as TIMEOUT_SLOTS, W as launchlabBuyAccounts, X as fetchQuoteDisplay, Xt as poolAddress, Yt as drawAddress, Zn as LAUNCH_DECIMALS, Zt as scopedVrfIdentityAddress, _ as buildMessage, _n as BPS, _t as fetchPoolByMint, at as HARVEST_COMPUTE_UNITS, b as creatorFeeOnInput, bn as MAX_MULTIPLIER_BPS, bt as fetchWalletActivity, ct as computeBudgetInstructions, dn as launchlabVaultAddress, dt as DRAW_POOL_OFFSET, en as ata, et as BUY_PACK_COMPUTE_UNITS, ft as DRAW_PURCHASER_OFFSET, g as getLaunchInstruction, gt as fetchPoolAt, hn as platformFeeVaultAddress, ht as fetchDraw, in as creatorFeeVaultAddress, it as DEFAULT_COMPUTE_UNIT_LIMIT, jn as uncappedMaximum, jr as SYSTEM_PROGRAM_ADDRESS, kn as seedTokens, lr as ASSOCIATED_TOKEN_PROGRAM_ADDRESS, lt as getSetComputeUnitLimitInstruction, mr as CPMM_SWAP_BASE_OUTPUT, mt as POOL_MINT_OFFSET, nt as COMPUTE_BUDGET_PROGRAM_ADDRESS, ot as MAX_COMPUTE_UNIT_LIMIT, pr as CPMM_SWAP_BASE_INPUT, pt as POOL_CREATOR_OFFSET, q as order, qt as activityAddress, rt as CREATE_MACHINE_COMPUTE_UNITS, sr as raydiumIds, st as REDEEM_COMPUTE_UNITS, t as raydium_exports, tt as CLAIM_COMPUTE_UNITS, un as launchlabPoolAddress, ut as getSetComputeUnitPriceInstruction, v as withRemainingAccounts, vn as DEFAULT_TIERS, vt as fetchPoolInventory, wn as averageMultiplierBps, wt as listPools, xn as MIN_SEED_MULTIPLIER_BPS, xt as listDraws, yn as GaboxMathError, yt as fetchVaultBalance, zn as RETRY_SLOTS } from "./raydium-DIF1WOvI.js";
3
- import { DRAW_RESOLVED_EVENT_DISCRIMINATOR, PACK_BOUGHT_EVENT_DISCRIMINATOR, POOL_CREATED_EVENT_DISCRIMINATOR, PRIZES_FUNDED_EVENT_DISCRIMINATOR, RANDOMNESS_RETRIED_EVENT_DISCRIMINATOR, TOKENS_SOLD_EVENT_DISCRIMINATOR, getDrawResolvedEventDecoder, getPackBoughtEventDecoder, getPoolCreatedEventDecoder, getPrizesFundedEventDecoder, getRandomnessRetriedEventDecoder, getTokensSoldEventDecoder, t as generated_exports } from "./generated/index.js";
4
- import { AccountRole, address, createNoopSigner, createSolanaRpc, createSolanaRpcSubscriptions, getAddressDecoder, getBase64Encoder, getU64Encoder } from "@solana/kit";
1
+ import { At as findDrawPda, Ct as getBuyPacksInstructionAsync, I as getFundPrizesInstruction, Mt as WALLET_ACTIVITY_DISCRIMINATOR, Nt as decodeWalletActivity, Ot as findPoolPda, Ut as POOL_DISCRIMINATOR, W as getExpireDrawInstruction, Wt as decodePool, b as getRetryDrawInstruction, en as DRAW_DISCRIMINATOR, ht as getClaimPrizeInstructionAsync, jt as findActivityPda, k as getInitializePoolInstructionAsync, kt as findIdentityPda, p as getSellTokensInstructionAsync, st as getClaimPrizeToInstructionAsync, tn as decodeDraw } from "./gabox-CuyJYmrS.js";
2
+ import { $ as readAccounts, $n as uncappedMaximum, $t as poolAddress, A as wsolAccountFor, An as VRF_PROGRAM_ADDRESS, Bn as checkedCount, Bt as decodeCpmmPool, Cn as INSTRUCTIONS_SYSVAR, Ct as fetchWalletActivity, D as quoteAccountFor, Dn as SLOT_HASHES_SYSVAR, Dt as listPools, E as newCurveReserves, En as RETRY_SLOTS, Er as CPMM_SWAP_BASE_OUTPUT, Et as listDrawsByPurchaser, Fn as MAX_MULTIPLIER_BPS, Gn as perPackCaps, Gr as TOKEN_PROGRAM_ADDRESS, Hn as chooseIndex, I as curveBuyExactOut, In as MIN_SEED_MULTIPLIER_BPS, J as fetchQuoteAsset, Jn as resolveReservation, Jt as tokenAccountAmount, Kn as quote, Kr as WSOL_MINT, Ln as TICKETS, Lr as LAUNCH_TOTAL_BASE_SELL, Mn as DEFAULT_TIERS, N as cpmmSwapBaseInput, Nn as GaboxMathError, O as resolveVenue, On as TIMEOUT_SLOTS, Ot as quotePool, P as cpmmSwapBaseOutput, Pn as MAX_BATCH_SIZE, Qn as uncappedAmounts, Qt as drawAddress, Rn as TIERS, Rr as METAPLEX_PROGRAM_ADDRESS, S as fetchCurveSettings, Sn as IDENTITY_SEED, St as fetchVaultBalance, Tn as PACK_TOKENS, Tr as CPMM_SWAP_BASE_INPUT, Tt as listDrawsByPool, Un as maxMultiplierBps, Ur as SYSTEM_PROGRAM_ADDRESS, Vn as choose, W as launchlabBuyAccounts, Wn as minTier, X as fetchQuoteDisplay, Xn as settleWith, Xt as activityAddress, Yn as seedTokens, Zn as tierAmount, Zt as associatedTokenAddress, _ as buildMessage, _t as fetchDraw, at as DEFAULT_COMPUTE_UNIT_LIMIT, b as creatorFeeOnInput, bt as fetchPoolByMint, cn as creatorFeeVaultAddress, ct as REDEEM_COMPUTE_UNITS, dt as getSetComputeUnitPriceInstruction, en as randomNonce, er as validatePack, et as BUY_PACK_COMPUTE_UNITS, ft as DRAW_POOL_OFFSET, g as getLaunchInstruction, gt as POOL_MINT_OFFSET, hn as launchlabVaultAddress, ht as POOL_CREATOR_OFFSET, in as ata, it as CREATE_MACHINE_COMPUTE_UNITS, jn as BPS, kn as VRF_DEFAULT_QUEUE, kt as tiersOf, lr as LAUNCH_DECIMALS, lt as computeBudgetInstructions, mn as launchlabPoolAddress, mt as DRAW_SETTLED_OFFSET, nn as vaultAddress, nt as CLAIM_PRIZE_COMPUTE_UNITS, ot as HARVEST_COMPUTE_UNITS, pt as DRAW_PURCHASER_OFFSET, q as order, qn as reserveFor, rn as vrfIdentityAddress, rt as COMPUTE_BUDGET_PROGRAM_ADDRESS, st as MAX_COMPUTE_UNIT_LIMIT, t as raydium_exports, tn as scopedVrfIdentityAddress, tr as validateTiers, tt as CLAIM_COMPUTE_UNITS, ut as getSetComputeUnitLimitInstruction, v as withRemainingAccounts, vt as fetchOwedDraws, wn as MAX_ATTEMPTS, wt as listDraws, xn as GABOX_PROGRAM_ID, xr as ASSOCIATED_TOKEN_PROGRAM_ADDRESS, xt as fetchPoolInventory, yn as platformFeeVaultAddress, yr as raydiumIds, yt as fetchPoolAt, zn as averageMultiplierBps, zr as PLATFORM_ADMIN, zt as decodeCpmmAmmConfig } from "./raydium-DXUJ-g1p.js";
3
+ import { DRAW_RESOLVED_EVENT_DISCRIMINATOR, PACKS_BOUGHT_EVENT_DISCRIMINATOR, POOL_CREATED_EVENT_DISCRIMINATOR, PRIZES_FUNDED_EVENT_DISCRIMINATOR, PRIZE_CLAIMED_EVENT_DISCRIMINATOR, RANDOMNESS_RETRIED_EVENT_DISCRIMINATOR, TOKENS_SOLD_EVENT_DISCRIMINATOR, getDrawResolvedEventDecoder, getPacksBoughtEventDecoder, getPoolCreatedEventDecoder, getPrizeClaimedEventDecoder, getPrizesFundedEventDecoder, getRandomnessRetriedEventDecoder, getTokensSoldEventDecoder, t as generated_exports } from "./generated/index.js";
4
+ import { AccountRole, address, createNoopSigner, createSolanaRpc, createSolanaRpcSubscriptions, getAddressDecoder, getAddressEncoder, getBase64Encoder, getU64Encoder } from "@solana/kit";
5
5
  import { getCloseAccountInstruction, getCreateAssociatedTokenIdempotentInstruction, getSyncNativeInstruction } from "@solana-program/token";
6
6
  import { getTransferSolInstruction } from "@solana-program/system";
7
7
  //#region src/events.ts
8
- /** Decode Gabox's Anchor events. Draw accounts are closed on delivery; use `DrawResolved` as final state. */
8
+ /**
9
+ * Decode Gabox's Anchor events.
10
+ *
11
+ * A paid delivery closes the draw account, so `DrawResolved` is its final state. An owed delivery
12
+ * (`paid: false`) leaves the draw open and settled until `claim_prize` pays it, and `PrizeClaimed`
13
+ * closes that story. Events are a convenience for indexers: what a pack won never depends on them.
14
+ * An owed draw carries everything on chain (`settleDraw`), and a paid draw's randomness is in the
15
+ * oracle's transaction on the ledger.
16
+ */
9
17
  const b64 = getBase64Encoder();
10
18
  const starts = (a, b) => a.length >= b.length && b.every((v, i) => a[i] === v);
11
19
  function decodeEvent(data) {
@@ -17,9 +25,9 @@ function decodeEvent(data) {
17
25
  name: "PrizesFunded",
18
26
  data: getPrizesFundedEventDecoder().decode(data)
19
27
  };
20
- if (starts(data, PACK_BOUGHT_EVENT_DISCRIMINATOR)) return {
21
- name: "PackBought",
22
- data: getPackBoughtEventDecoder().decode(data)
28
+ if (starts(data, PACKS_BOUGHT_EVENT_DISCRIMINATOR)) return {
29
+ name: "PacksBought",
30
+ data: getPacksBoughtEventDecoder().decode(data)
23
31
  };
24
32
  if (starts(data, RANDOMNESS_RETRIED_EVENT_DISCRIMINATOR)) return {
25
33
  name: "RandomnessRetried",
@@ -29,6 +37,10 @@ function decodeEvent(data) {
29
37
  name: "DrawResolved",
30
38
  data: getDrawResolvedEventDecoder().decode(data)
31
39
  };
40
+ if (starts(data, PRIZE_CLAIMED_EVENT_DISCRIMINATOR)) return {
41
+ name: "PrizeClaimed",
42
+ data: getPrizeClaimedEventDecoder().decode(data)
43
+ };
32
44
  if (starts(data, TOKENS_SOLD_EVENT_DISCRIMINATOR)) return {
33
45
  name: "TokensSold",
34
46
  data: getTokensSoldEventDecoder().decode(data)
@@ -107,7 +119,10 @@ async function readEvents(rpc, signature) {
107
119
  if (!tx || !meta || meta.err) return [];
108
120
  return decodeEvents(meta.logMessages ?? []);
109
121
  }
110
- /** Poll final resolution events for one closed draw address. There is no separate Ready/claim state to poll instead. */
122
+ /**
123
+ * The `DrawResolved` event of one draw, found through the draw address's own signature history.
124
+ * `paid` says whether the tokens reached the purchaser in the callback or wait for a claim.
125
+ */
111
126
  async function findResolvedDraw(client, address) {
112
127
  let before;
113
128
  for (let page = 0; page < 10; page++) {
@@ -120,7 +135,7 @@ async function findResolvedDraw(client, address) {
120
135
  if (row.err) continue;
121
136
  for (const event of await readEvents(client.rpc, row.signature)) {
122
137
  if (event.name !== "DrawResolved") continue;
123
- if (await drawAddress(event.data.pool, event.data.seq) === address) return {
138
+ if (event.data.draw === address) return {
124
139
  ...event.data,
125
140
  address
126
141
  };
@@ -439,9 +454,10 @@ const messageOf = (cause) => cause instanceof Error ? cause.message : String(cau
439
454
  * quote mint and its metadata. A non-SOL pool adds one HTTP call to the route provider for
440
455
  * `solAmount`.
441
456
  *
442
- * Throws when the coin has no pool.
457
+ * Throws when the coin has no pool, or when the curve has fewer whole packs left than `count`.
443
458
  */
444
459
  async function getOffer(client, mint, options = {}) {
460
+ const count = checkedCount(options.count ?? 1);
445
461
  const inventory = await fetchPoolInventory(client, mint);
446
462
  if (!inventory) throw new Error(`no gabox pool for mint ${mint}`);
447
463
  const { pool } = inventory;
@@ -455,18 +471,18 @@ async function getOffer(client, mint, options = {}) {
455
471
  },
456
472
  ...options.venue ? { venue: options.venue } : {}
457
473
  });
458
- const quoteAmount = venue.quoteBuy(pool.packTokens);
474
+ const quoteAmount = venue.quoteBuy(pool.packTokens * BigInt(count));
459
475
  const display = await fetchQuoteDisplay(client, pool.quoteMint);
460
476
  return offerFromState(inventory, venue.kind, quoteAmount, {
461
477
  quoteDecimals: display.decimals,
462
478
  quoteSymbol: display.symbol,
463
479
  solAmount: await solPriceOf(client, pool.quoteMint, quoteAmount)
464
- });
480
+ }, count);
465
481
  }
466
482
  /**
467
483
  * The same computation with the reads already done. Useful when a caller holds a `ResolvedVenue`
468
484
  * and wants to re-price without touching the network. `quoteAmount` is
469
- * `venue.quoteBuy(pool.packTokens)`.
485
+ * `venue.quoteBuy(pool.packTokens * count)`.
470
486
  *
471
487
  * `display` is optional: a caller that only wants the prize numbers can leave it out, and the three
472
488
  * display fields then report the quote's own base units with no symbol and no SOL price.
@@ -475,7 +491,8 @@ function offerFromState(inventory, venue, quoteAmount, display = {
475
491
  quoteDecimals: 0,
476
492
  quoteSymbol: null,
477
493
  solAmount: null
478
- }) {
494
+ }, count = 1) {
495
+ checkedCount(count);
479
496
  const { pool } = inventory;
480
497
  const tiers = tiersOf(pool);
481
498
  const offer = quote(pool.packTokens, tiers, inventory.inventory, inventory.reserved);
@@ -484,6 +501,7 @@ function offerFromState(inventory, venue, quoteAmount, display = {
484
501
  mint: pool.mint,
485
502
  pool: inventory.poolAddress,
486
503
  packTokens: pool.packTokens,
504
+ count,
487
505
  quoteAmount,
488
506
  quoteMint: pool.quoteMint,
489
507
  quoteDecimals: display.quoteDecimals,
@@ -496,9 +514,12 @@ function offerFromState(inventory, venue, quoteAmount, display = {
496
514
  maximum: offer.maximum,
497
515
  minimum: offer.minimum,
498
516
  uncapped,
517
+ perPackCaps: perPackCaps(tiers, pool.packTokens, inventory.free, count),
518
+ batchReserved: reserveFor(count, pool.packTokens, uncapped, inventory.free),
499
519
  inventory: inventory.inventory,
500
520
  reserved: inventory.reserved,
501
521
  free: inventory.free,
522
+ freeSnapshot: inventory.free,
502
523
  isFirstPack: pool.nextSeq === 0n,
503
524
  isSeeded: offer.maximum === uncapped,
504
525
  maxMultiplierBps: maxMultiplierBps(tiers),
@@ -988,6 +1009,57 @@ function defaultRoute(cluster) {
988
1009
  return cluster === "mainnet-beta" ? jupiterRoute() : null;
989
1010
  }
990
1011
  //#endregion
1012
+ //#region src/settle.ts
1013
+ /**
1014
+ * Reproduce a batch's settlement from the randomness the oracle published.
1015
+ *
1016
+ * The program settles every pack of a purchase inside one callback, from one 32-byte randomness:
1017
+ * pack k's ticket is the first two bytes of `sha256(randomness || pool || seqStart || k)`, read
1018
+ * little-endian, and its prize table is capped at what the vault can pay after the packs before it.
1019
+ * Everything that goes in is either on the `Draw` account or in the `DrawResolved` event, so anyone
1020
+ * can recompute what each pack won. `math.ts` holds the pure part; this file adds the hash, which
1021
+ * is asynchronous because it goes through WebCrypto.
1022
+ */
1023
+ /** `math::ticket`. Pack `k`'s 16-bit ticket for this pool and purchase. */
1024
+ async function ticketFor(randomness, pool, seqStart, k) {
1025
+ if (randomness.length !== 32) throw new Error("randomness is 32 bytes");
1026
+ const bytes = /* @__PURE__ */ new Uint8Array(73);
1027
+ bytes.set(randomness, 0);
1028
+ bytes.set(getAddressEncoder().encode(pool), 32);
1029
+ bytes.set(getU64Encoder().encode(seqStart), 64);
1030
+ bytes[72] = k;
1031
+ const hash = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes));
1032
+ return hash[0] | hash[1] << 8;
1033
+ }
1034
+ /** `math::settle`. What the callback paid, or owed, for this purchase. */
1035
+ async function settle(input) {
1036
+ const timedOut = input.timedOut ?? false;
1037
+ const tickets = timedOut ? [] : await Promise.all(Array.from({ length: input.count }, (_, k) => ticketFor(input.randomness, input.pool, input.seqStart, k)));
1038
+ return settleWith(input.tiers, input.packTokens, input.freeSnapshot, input.count, tickets, timedOut);
1039
+ }
1040
+ /**
1041
+ * The settlement of a draw that is still on chain: settled but not yet claimed. The account alone
1042
+ * carries everything, so this needs no event and no indexer. `total` equals `draw.owed`.
1043
+ *
1044
+ * A pending draw (`settled === false`) has no randomness yet; this throws for it.
1045
+ */
1046
+ async function settleDraw(draw) {
1047
+ if (!draw.settled) throw new Error("the draw is not settled yet; there is no randomness to settle from");
1048
+ return await settle({
1049
+ tiers: draw.tiers.map(({ multiplierBps, tickets }) => ({
1050
+ multiplierBps,
1051
+ tickets
1052
+ })),
1053
+ packTokens: draw.packTokens,
1054
+ freeSnapshot: draw.freeSnapshot,
1055
+ count: draw.count,
1056
+ pool: draw.pool,
1057
+ seqStart: draw.seqStart,
1058
+ randomness: draw.randomness,
1059
+ timedOut: draw.timedOut
1060
+ });
1061
+ }
1062
+ //#endregion
991
1063
  //#region src/tx/wsol.ts
992
1064
  /**
993
1065
  * Wrapping and unwrapping SOL around a venue trade.
@@ -1210,8 +1282,9 @@ function routeSizeHint(cause, leg) {
1210
1282
  return new Error(`${cause.message} The swap and the Gabox instruction share one transaction on purpose, so this SDK never splits them. Supply more address lookup tables, or pay in the quote token.`, { cause });
1211
1283
  }
1212
1284
  //#endregion
1213
- //#region src/tx/buyPack.ts
1214
- async function buyPack(client, input) {
1285
+ //#region src/tx/buyPacks.ts
1286
+ async function buyPacks(client, input) {
1287
+ const count = checkedCount(input.count);
1215
1288
  if (input.maxQuoteIn <= 0n) throw new Error("maxQuoteIn must be positive");
1216
1289
  if (input.maxNativeDebit < 0n) throw new Error("maxNativeDebit must not be negative");
1217
1290
  const inventory = await fetchPoolInventory(client, input.mint);
@@ -1228,8 +1301,14 @@ async function buyPack(client, input) {
1228
1301
  },
1229
1302
  ...input.venue ? { venue: input.venue } : {}
1230
1303
  });
1231
- const draw = await drawAddress(poolAddress, input.seq ?? pool.nextSeq);
1232
- const buy = await getBuyPackInstructionAsync({
1304
+ const tokens = pool.packTokens * BigInt(count);
1305
+ if (venue.kind === "launchlab" && venue.remainingCurveBase < tokens) {
1306
+ const left = venue.remainingCurveBase / pool.packTokens;
1307
+ throw new Error(`the curve has ${left} whole pack(s) left and this purchase asks for ${count}. The program needs the exact amount, so buy fewer packs or wait for the coin to graduate.`);
1308
+ }
1309
+ const nonce = input.nonce ?? randomNonce();
1310
+ const draw = await drawAddress(poolAddress, purchaser, nonce);
1311
+ const buy = await getBuyPacksInstructionAsync({
1233
1312
  purchaser: input.purchaser,
1234
1313
  pool: poolAddress,
1235
1314
  draw,
@@ -1238,8 +1317,10 @@ async function buyPack(client, input) {
1238
1317
  vault: pool.vault,
1239
1318
  venue: venue.program,
1240
1319
  quoteTokenProgram: pool.quoteTokenProgram,
1320
+ count,
1321
+ nonce,
1241
1322
  maxQuoteIn: input.maxQuoteIn,
1242
- minMaximum: input.minMaximum,
1323
+ minFirstMaximum: input.minFirstMaximum,
1243
1324
  maxNativeDebit: input.maxNativeDebit
1244
1325
  });
1245
1326
  const leg = await quoteLegIn(client, {
@@ -1261,18 +1342,33 @@ async function buyPack(client, input) {
1261
1342
  ...leg.after
1262
1343
  ];
1263
1344
  try {
1264
- return await buildMessage(client, input.purchaser, instructions, {
1265
- addressLookupTables: {
1266
- ...input.addressLookupTables ?? client.addressLookupTables,
1267
- ...leg.lookupTables
1268
- },
1269
- computeUnitLimit: input.computeUnitLimit ?? computeUnitsWithRoute(235e3, leg),
1270
- ...input.computeUnitPrice === void 0 ? {} : { computeUnitPrice: input.computeUnitPrice }
1271
- });
1345
+ return {
1346
+ message: await buildMessage(client, input.purchaser, instructions, {
1347
+ addressLookupTables: {
1348
+ ...input.addressLookupTables ?? client.addressLookupTables,
1349
+ ...leg.lookupTables
1350
+ },
1351
+ computeUnitLimit: input.computeUnitLimit ?? computeUnitsWithRoute(235e3, leg),
1352
+ ...input.computeUnitPrice === void 0 ? {} : { computeUnitPrice: input.computeUnitPrice }
1353
+ }),
1354
+ draw,
1355
+ nonce,
1356
+ seqStart: pool.nextSeq,
1357
+ count
1358
+ };
1272
1359
  } catch (cause) {
1273
1360
  throw routeSizeHint(cause, leg);
1274
1361
  }
1275
1362
  }
1363
+ /** One pack: `buyPacks` with `count: 1`. Same result shape, so the draw address is known. */
1364
+ async function buyPack(client, input) {
1365
+ const { minMaximum, ...rest } = input;
1366
+ return await buyPacks(client, {
1367
+ ...rest,
1368
+ count: 1,
1369
+ minFirstMaximum: minMaximum
1370
+ });
1371
+ }
1276
1372
  //#endregion
1277
1373
  //#region src/tx/createMachine.ts
1278
1374
  /**
@@ -1435,6 +1531,12 @@ function assertSeedFitsCurve(seed) {
1435
1531
  //#endregion
1436
1532
  //#region src/tx/draw.ts
1437
1533
  const DRAW_COMPUTE_UNITS = 2e5;
1534
+ const options = (input, computeUnitLimit) => ({
1535
+ ...input.addressLookupTables === void 0 ? {} : { addressLookupTables: input.addressLookupTables },
1536
+ computeUnitLimit: input.computeUnitLimit ?? computeUnitLimit,
1537
+ ...input.computeUnitPrice === void 0 ? {} : { computeUnitPrice: input.computeUnitPrice }
1538
+ });
1539
+ /** Another randomness request for a draw whose earlier attempts have not landed. Any signer. */
1438
1540
  async function retryDraw(client, input) {
1439
1541
  const ix = await getRetryDrawInstruction({
1440
1542
  payer: input.payer,
@@ -1443,15 +1545,17 @@ async function retryDraw(client, input) {
1443
1545
  identity: await vrfIdentityAddress(),
1444
1546
  maxVrfDebit: input.maxVrfDebit
1445
1547
  });
1446
- return await buildMessage(client, input.payer, [ix], {
1447
- addressLookupTables: input.addressLookupTables,
1448
- computeUnitLimit: input.computeUnitLimit ?? DRAW_COMPUTE_UNITS,
1449
- ...input.computeUnitPrice === void 0 ? {} : { computeUnitPrice: input.computeUnitPrice }
1450
- });
1548
+ return await buildMessage(client, input.payer, [ix], options(input, DRAW_COMPUTE_UNITS));
1451
1549
  }
1550
+ /**
1551
+ * Fix a timed-out purchase at every pack's smallest tier, from `TIMEOUT_SLOTS` after the purchase.
1552
+ * Any signer. The purchaser's ATA address is passed whether or not the account exists: the program
1553
+ * pays it if it can and owes the total otherwise.
1554
+ */
1452
1555
  async function expireDraw(client, input) {
1453
1556
  const draw = await fetchDraw(client, input.draw);
1454
1557
  if (!draw || draw.pool !== input.pool) throw new Error("draw does not belong to pool or was already delivered");
1558
+ if (draw.settled) throw new Error("the draw is settled; use claimPrize to pay what it owes");
1455
1559
  const pool = await fetchPoolAt(client, input.pool);
1456
1560
  if (!pool) throw new Error(`no pool at ${input.pool}`);
1457
1561
  const ix = getExpireDrawInstruction({
@@ -1462,12 +1566,45 @@ async function expireDraw(client, input) {
1462
1566
  vault: pool.vault,
1463
1567
  userTokens: await associatedTokenAddress(draw.purchaser, pool.mint)
1464
1568
  });
1465
- return await buildMessage(client, input.payer, [ix], {
1466
- addressLookupTables: input.addressLookupTables,
1467
- computeUnitLimit: input.computeUnitLimit ?? DRAW_COMPUTE_UNITS,
1468
- ...input.computeUnitPrice === void 0 ? {} : { computeUnitPrice: input.computeUnitPrice }
1569
+ return await buildMessage(client, input.payer, [ix], options(input, DRAW_COMPUTE_UNITS));
1570
+ }
1571
+ /** Pay an owed prize to the purchaser's coin ATA, creating it if needed. Permissionless. */
1572
+ async function claimPrize(client, input) {
1573
+ const draw = await fetchDraw(client, input.draw);
1574
+ if (!draw) throw new Error(`no draw at ${input.draw}; a paid draw is closed and owes nothing`);
1575
+ if (!draw.settled) throw new Error("the draw is still pending; nothing is owed yet");
1576
+ const pool = await fetchPoolAt(client, draw.pool);
1577
+ if (!pool) throw new Error(`no pool at ${draw.pool}`);
1578
+ const ix = await getClaimPrizeInstructionAsync({
1579
+ payer: input.payer,
1580
+ pool: draw.pool,
1581
+ draw: input.draw,
1582
+ purchaser: draw.purchaser,
1583
+ mint: pool.mint,
1584
+ vault: pool.vault,
1585
+ userTokens: await associatedTokenAddress(draw.purchaser, pool.mint)
1586
+ });
1587
+ return await buildMessage(client, input.payer, [ix], options(input, CLAIM_PRIZE_COMPUTE_UNITS));
1588
+ }
1589
+ /** The purchaser pays their own owed prize to a coin account they name. */
1590
+ async function claimPrizeTo(client, input) {
1591
+ const draw = await fetchDraw(client, input.draw);
1592
+ if (!draw) throw new Error(`no draw at ${input.draw}; a paid draw is closed and owes nothing`);
1593
+ if (!draw.settled) throw new Error("the draw is still pending; nothing is owed yet");
1594
+ if (draw.purchaser !== input.purchaser.address) throw new Error("only the purchaser can redirect a prize");
1595
+ const pool = await fetchPoolAt(client, draw.pool);
1596
+ if (!pool) throw new Error(`no pool at ${draw.pool}`);
1597
+ const ix = await getClaimPrizeToInstructionAsync({
1598
+ purchaser: input.purchaser,
1599
+ pool: draw.pool,
1600
+ draw: input.draw,
1601
+ mint: pool.mint,
1602
+ vault: pool.vault,
1603
+ destination: input.destination
1469
1604
  });
1605
+ return await buildMessage(client, input.purchaser, [ix], options(input, CLAIM_PRIZE_COMPUTE_UNITS));
1470
1606
  }
1607
+ /** What can still happen to a draw, or `null` when it is closed. */
1471
1608
  async function drawAvailability(client, address) {
1472
1609
  const draw = await fetchDraw(client, address);
1473
1610
  if (!draw) return null;
@@ -1476,12 +1613,17 @@ async function drawAvailability(client, address) {
1476
1613
  const expireAt = draw.requestSlot + TIMEOUT_SLOTS;
1477
1614
  const slotsUntilRetry = now >= retryAt ? 0n : retryAt - now;
1478
1615
  const slotsUntilExpiry = now >= expireAt ? 0n : expireAt - now;
1616
+ const pending = !draw.settled;
1479
1617
  return {
1480
1618
  attempts: draw.attempts,
1619
+ settled: draw.settled,
1620
+ owed: draw.owed,
1481
1621
  slotsUntilRetry,
1482
1622
  slotsUntilExpiry,
1483
- canRetry: draw.attempts < 3 && slotsUntilRetry === 0n && slotsUntilExpiry > 0n,
1484
- canExpire: slotsUntilExpiry === 0n
1623
+ canDeliver: pending && slotsUntilExpiry > 0n,
1624
+ canRetry: pending && draw.attempts < 3 && slotsUntilRetry === 0n && slotsUntilExpiry > 0n,
1625
+ canExpire: pending && slotsUntilExpiry === 0n,
1626
+ canClaim: draw.settled
1485
1627
  };
1486
1628
  }
1487
1629
  //#endregion
@@ -1581,6 +1723,6 @@ async function oracleAccounts() {
1581
1723
  };
1582
1724
  }
1583
1725
  //#endregion
1584
- export { ASSOCIATED_TOKEN_PROGRAM_ADDRESS, BPS, BUY_PACK_COMPUTE_UNITS, CLAIM_COMPUTE_UNITS, CLUSTER_ENDPOINTS, COMPUTE_BUDGET_PROGRAM_ADDRESS, CPMM_ROUTE_COMPUTE_UNITS, CPMM_ROUTE_SLIPPAGE_BPS, CREATE_MACHINE_COMPUTE_UNITS, DEFAULT_COMPUTE_UNIT_LIMIT, DEFAULT_TIERS, DEVNET_ADDRESS_LOOKUP_TABLES, DEVNET_HTTP, DEVNET_LOOKUP_TABLE_ADDRESS, DEVNET_LOOKUP_TABLE_ADDRESSES, DEVNET_WS, DRAW_DISCRIMINATOR, DRAW_POOL_OFFSET, DRAW_PURCHASER_OFFSET, EXACT_IN_MARGIN_BPS, GABOX_PROGRAM_ID, GaboxMathError, HARVEST_COMPUTE_UNITS, IDENTITY_SEED, INSTRUCTIONS_SYSVAR, JUPITER_DEFAULT_COMPUTE_UNITS, JUPITER_DEFAULT_SLIPPAGE_BPS, JUPITER_LITE_URL, LAUNCH_DECIMALS, MAX_ATTEMPTS, MAX_COMPUTE_UNIT_LIMIT, MAX_MULTIPLIER_BPS, METAPLEX_PROGRAM_ADDRESS, MIN_SEED_MULTIPLIER_BPS, PACK_TOKENS, PLATFORM_ADMIN, POOL_CREATOR_OFFSET, POOL_DISCRIMINATOR, POOL_MINT_OFFSET, REDEEM_COMPUTE_UNITS, RETRY_SLOTS, SLOT_HASHES_SYSVAR, SYSTEM_PROGRAM_ADDRESS, TICKETS, TIERS, TIMEOUT_SLOTS, TOKEN_PROGRAM_ADDRESS, VRF_DEFAULT_QUEUE, VRF_PROGRAM_ADDRESS, WALLET_ACTIVITY_DISCRIMINATOR, WSOL_MINT, activityAddress, assertClusterUrl, assertRouteIsSafe, associatedTokenAddress, averageMultiplierBps, buildMessage, buyPack, choose, clusterNamedBy, computeBudgetInstructions, computeUnitsOf, computeUnitsWithRoute, createClient, createMachine, createQuoteAccount, decodeDraw, decodeEvent, decodeEvents, decodePool, decodeWalletActivity, defaultAddressLookupTables, defaultRoute, drawAddress, drawAvailability, expireDraw, fetchAddressLookupTables, fetchDraw, fetchEvents, fetchPoolAt, fetchPoolByMint, fetchPoolInventory, fetchVaultBalance, fetchWalletActivity, findActivityPda, findDrawPda, findIdentityPda, findPoolPda, findResolvedDraw, fundPrizes, fundWsol, generated_exports as generated, getOffer, getSetComputeUnitLimitInstruction, getSetComputeUnitPriceInstruction, jupiterRoute, listDraws, listDrawsByPool, listDrawsByPurchaser, listPools, maxMultiplierBps, offerFromState, oracleAccounts, poolAddress, providerOf, quote, quoteLegFromWallet, quoteLegIn, quoteLegOut, quotePool, raydium_exports as raydium, raydiumCpmmRoute, resolveReservation, retryDraw, routeFrom, routeQuoteIn, routeQuoteOut, routeSizeHint, scopedVrfIdentityAddress, seedCostEstimate, seedShortfall, seedTokens, sellTokens, solPriceOf, tierAmount, tiersOf, uncappedMaximum, unwrapWsol, validatePack, validateTiers, vaultAddress, vrfIdentityAddress, websocketUrlFor, withRemainingAccounts };
1726
+ export { ASSOCIATED_TOKEN_PROGRAM_ADDRESS, BPS, BUY_PACK_COMPUTE_UNITS, CLAIM_COMPUTE_UNITS, CLAIM_PRIZE_COMPUTE_UNITS, CLUSTER_ENDPOINTS, COMPUTE_BUDGET_PROGRAM_ADDRESS, CPMM_ROUTE_COMPUTE_UNITS, CPMM_ROUTE_SLIPPAGE_BPS, CREATE_MACHINE_COMPUTE_UNITS, DEFAULT_COMPUTE_UNIT_LIMIT, DEFAULT_TIERS, DEVNET_ADDRESS_LOOKUP_TABLES, DEVNET_HTTP, DEVNET_LOOKUP_TABLE_ADDRESS, DEVNET_LOOKUP_TABLE_ADDRESSES, DEVNET_WS, DRAW_DISCRIMINATOR, DRAW_POOL_OFFSET, DRAW_PURCHASER_OFFSET, DRAW_SETTLED_OFFSET, EXACT_IN_MARGIN_BPS, GABOX_PROGRAM_ID, GaboxMathError, HARVEST_COMPUTE_UNITS, IDENTITY_SEED, INSTRUCTIONS_SYSVAR, JUPITER_DEFAULT_COMPUTE_UNITS, JUPITER_DEFAULT_SLIPPAGE_BPS, JUPITER_LITE_URL, LAUNCH_DECIMALS, MAX_ATTEMPTS, MAX_BATCH_SIZE, MAX_COMPUTE_UNIT_LIMIT, MAX_MULTIPLIER_BPS, METAPLEX_PROGRAM_ADDRESS, MIN_SEED_MULTIPLIER_BPS, PACK_TOKENS, PLATFORM_ADMIN, POOL_CREATOR_OFFSET, POOL_DISCRIMINATOR, POOL_MINT_OFFSET, REDEEM_COMPUTE_UNITS, RETRY_SLOTS, SLOT_HASHES_SYSVAR, SYSTEM_PROGRAM_ADDRESS, TICKETS, TIERS, TIMEOUT_SLOTS, TOKEN_PROGRAM_ADDRESS, VRF_DEFAULT_QUEUE, VRF_PROGRAM_ADDRESS, WALLET_ACTIVITY_DISCRIMINATOR, WSOL_MINT, activityAddress, assertClusterUrl, assertRouteIsSafe, associatedTokenAddress, averageMultiplierBps, buildMessage, buyPack, buyPacks, checkedCount, choose, chooseIndex, claimPrize, claimPrizeTo, clusterNamedBy, computeBudgetInstructions, computeUnitsOf, computeUnitsWithRoute, createClient, createMachine, createQuoteAccount, decodeDraw, decodeEvent, decodeEvents, decodePool, decodeWalletActivity, defaultAddressLookupTables, defaultRoute, drawAddress, drawAvailability, expireDraw, fetchAddressLookupTables, fetchDraw, fetchEvents, fetchOwedDraws, fetchPoolAt, fetchPoolByMint, fetchPoolInventory, fetchVaultBalance, fetchWalletActivity, findActivityPda, findDrawPda, findIdentityPda, findPoolPda, findResolvedDraw, fundPrizes, fundWsol, generated_exports as generated, getOffer, getSetComputeUnitLimitInstruction, getSetComputeUnitPriceInstruction, jupiterRoute, listDraws, listDrawsByPool, listDrawsByPurchaser, listPools, maxMultiplierBps, minTier, offerFromState, oracleAccounts, perPackCaps, poolAddress, providerOf, quote, quoteLegFromWallet, quoteLegIn, quoteLegOut, quotePool, randomNonce, raydium_exports as raydium, raydiumCpmmRoute, reserveFor, resolveReservation, retryDraw, routeFrom, routeQuoteIn, routeQuoteOut, routeSizeHint, scopedVrfIdentityAddress, seedCostEstimate, seedShortfall, seedTokens, sellTokens, settle, settleDraw, settleWith, solPriceOf, ticketFor, tierAmount, tiersOf, uncappedAmounts, uncappedMaximum, unwrapWsol, validatePack, validateTiers, vaultAddress, vrfIdentityAddress, websocketUrlFor, withRemainingAccounts };
1585
1727
 
1586
1728
  //# sourceMappingURL=index.js.map