@gabox-labs/sdk 0.6.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.
@@ -1,12 +1,12 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { AccountRole, Endian, addEncoderSizePrefix, appendTransactionMessageInstructions, compileTransaction, compressTransactionMessageUsingAddressLookupTables, createTransactionMessage, fixDecoderSize, getAddressDecoder, getAddressEncoder, getBase64Encoder, getBooleanDecoder, getBytesDecoder, getProgramDerivedAddress, getStructDecoder, getStructEncoder, getTransactionEncoder, getU16Decoder, getU16Encoder, getU32Encoder, getU64Decoder, getU64Encoder, getU8Decoder, getU8Encoder, getUtf8Encoder, pipe, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash } from "@solana/kit";
2
+ import { Dt as POOL_DISCRIMINATOR, Et as getWalletActivitySize, Jt as getDrawSize, Lt as getPoolSize, Ot as decodePool, Rt as DRAW_DISCRIMINATOR, _t as decodeWalletActivity, ft as findPoolPda, ht as findActivityPda, mt as findDrawPda, pt as findIdentityPda, t as GABOX_PROGRAM_ADDRESS, zt as decodeDraw } from "./gabox-DGTCh34U.js";
3
+ import { AccountRole, Endian, addEncoderSizePrefix, appendTransactionMessageInstructions, compileTransaction, compressTransactionMessageUsingAddressLookupTables, createTransactionMessage, fetchEncodedAccount, fixDecoderSize, getAddressDecoder, getAddressEncoder, getBase58Decoder, getBase64Encoder, getBooleanDecoder, getBytesDecoder, getBytesEncoder, getProgramDerivedAddress, getStructDecoder, getStructEncoder, getTransactionEncoder, getU16Decoder, getU16Encoder, getU32Encoder, getU64Decoder, getU64Encoder, getU8Decoder, getU8Encoder, getUtf8Encoder, pipe, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash } from "@solana/kit";
3
4
  import { getCloseAccountInstruction, getCreateAssociatedTokenIdempotentInstruction } from "@solana-program/token";
4
5
  //#region src/raydium/abi.ts
5
6
  const RAYDIUM_MAINNET_IDS = {
6
7
  launchlab: "LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj",
7
8
  launchlabAuthority: "WLHv2UAZm6z4KyaaELi5pjdbJh6RESMva1Rnn8pJVVh",
8
9
  launchlabEventAuthority: "2DPAtwB8L12vrMRExbLuyGnC7n2J5LNoZQSejeQGpwkr",
9
- solGlobalConfig: "6s1xP3hpbAfFoNtUNF8mfHsjr2Bd97JxFJRWLbL6aHuX",
10
10
  gaboxPlatform: "8tXUG97CEpSKTNXdGtanYyC33RWmTTuSin4VdaWwNj8q",
11
11
  cpmm: "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C",
12
12
  cpmmAuthority: "GpMZbSM2GgvTKHJirzeGfMFoaZ8UR2X7F4v8vHTvxFbL",
@@ -16,13 +16,15 @@ const RAYDIUM_DEVNET_IDS = {
16
16
  launchlab: "DRay6fNdQ5J82H7xV6uq2aV3mNrUZ1J4PgSKsWgptcm6",
17
17
  launchlabAuthority: "5xqNaZXX5eUi4p5HU4oz9i5QnwRNT2y6oN7yyn4qENeq",
18
18
  launchlabEventAuthority: "4uAB7seenFJKPUXqYewAdfra2u6baBgjiXU8x1SC7Ycz",
19
- solGlobalConfig: "7ZR4zD7PYfY2XxoG1Gxcy2EgEeGYrpxrwzPuwdUBssEt",
20
19
  gaboxPlatform: "DdEeCPXbCAzHE2PZSoR3RZng4WA4bSztrezQznrJ4ooB",
21
20
  cpmm: "DRaycpLY18LhpbydsBWbVJtxpNv9oXPgjRSfpF2bWpYb",
22
21
  cpmmAuthority: "CXniRufdq5xL8t8jZAPxsPZDpuudwuJSPWnbcD5Y5Nxq",
23
22
  launchQuoteRaise: 3000000000n
24
23
  };
25
- /** Wrapped SOL. The only quote Gabox accepts today, on both venues. */
24
+ /**
25
+ * Wrapped SOL: the default quote, and the one every price in SOL is measured in. Gabox accepts any
26
+ * quote Raydium enabled on LaunchLab, so this is not the only one.
27
+ */
26
28
  const WSOL_MINT = "So11111111111111111111111111111111111111112";
27
29
  /** Metaplex Token Metadata, which LaunchLab's create instruction writes to. */
28
30
  const METAPLEX_PROGRAM_ADDRESS = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s";
@@ -35,8 +37,13 @@ const PLATFORM_ADMIN = "5WcPTEQ59UqpQzjZUPbU8QRGCbj7NeQNLDa7DbsLkLKT";
35
37
  const LAUNCH_SUPPLY = 1000000000000000n;
36
38
  /** The part of the supply the LaunchLab curve sells: 793,100,000 coins. */
37
39
  const LAUNCH_TOTAL_BASE_SELL = 793100000000000n;
38
- /** Classic SPL Token. LaunchLab pins it on both sides of every Gabox launch and trade. */
40
+ /** Classic SPL Token. The base coin is always under it, on both venues. */
39
41
  const TOKEN_PROGRAM_ADDRESS = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
42
+ /**
43
+ * Token-2022. A quote mint may be under it, inside the bounded profile the program's
44
+ * `tokens::validate_quote_mint` accepts. The base coin never is.
45
+ */
46
+ const TOKEN_2022_PROGRAM_ADDRESS = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb";
40
47
  const ASSOCIATED_TOKEN_PROGRAM_ADDRESS = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL";
41
48
  const SYSTEM_PROGRAM_ADDRESS = "11111111111111111111111111111111";
42
49
  /** The rent sysvar. LaunchLab's create instruction still takes it. */
@@ -862,8 +869,8 @@ const CPMM_COLLECT_CREATOR_FEE = {
862
869
  * The Raydium addresses and launch numbers Gabox pins, per cluster.
863
870
  *
864
871
  * Mainnet and devnet run different Raydium deployments. Every program id and every PDA differs, and
865
- * so does the quote the curve must raise before a coin graduates. So nothing here is a global: a
866
- * caller passes the cluster, which comes from `createClient({ cluster })`.
872
+ * so does the raise a WSOL-quoted curve must reach. So nothing here is a global: a caller passes the
873
+ * cluster, which comes from `createClient({ cluster })`.
867
874
  *
868
875
  * None of these strings is typed in this file. `scripts/codegen.mjs` reads them out of the Rust
869
876
  * program's `programs/gabox/src/venue/ids.rs` and writes `abi.ts`. The Rust file is the source of
@@ -937,7 +944,7 @@ const SHARE_FEE_RATE = 0n;
937
944
  const CONSTANT_CURVE_TAG = 0;
938
945
  /** `migrate_type` 1: the coin graduates into a CPMM pool, not into an AMM pool. */
939
946
  const MIGRATE_TO_CPMM = 1;
940
- /** `AmmCreatorFeeOn::QuoteToken`, so the CPMM creator fee is paid in WSOL. */
947
+ /** `AmmCreatorFeeOn::QuoteToken`, so the CPMM creator fee is paid in the pool's quote asset. */
941
948
  const CREATOR_FEE_ON_QUOTE = 0;
942
949
  /** LaunchLab pool `status`: still selling on the curve. */
943
950
  const LAUNCHLAB_STATUS_FUND = 0;
@@ -966,6 +973,307 @@ function compareAddresses(a, b) {
966
973
  return 0;
967
974
  }
968
975
  //#endregion
976
+ //#region src/ids.ts
977
+ /** `declare_id!` in `programs/gabox/src/lib.rs`. */
978
+ const GABOX_PROGRAM_ID = GABOX_PROGRAM_ADDRESS;
979
+ /** MagicBlock's ephemeral VRF program. `vrf.rs` pins it and refuses any other. */
980
+ const VRF_PROGRAM_ADDRESS = "Vrf1RNUjXmQGjmQrQLvJHs9SNkvDJEsRVFPkfSQUwGz";
981
+ /**
982
+ * MagicBlock's default oracle queue. The program pins this one address, so a pool creator cannot
983
+ * point a draw at an oracle they control.
984
+ */
985
+ const VRF_DEFAULT_QUEUE = "Cuj97ggrhhidhbu39TijNVqE74xvKJ69gDervRUXAxGh";
986
+ /** The seed of both identity PDAs. See `pdas.ts` — there are two, under different programs. */
987
+ const IDENTITY_SEED = new Uint8Array([
988
+ 105,
989
+ 100,
990
+ 101,
991
+ 110,
992
+ 116,
993
+ 105,
994
+ 116,
995
+ 121
996
+ ]);
997
+ const SLOT_HASHES_SYSVAR = "SysvarS1otHashes111111111111111111111111111";
998
+ const INSTRUCTIONS_SYSVAR = "Sysvar1nstructions1111111111111111111111111";
999
+ /** `constants.rs`. A retry may not run sooner than this after the last attempt. */
1000
+ const RETRY_SLOTS = 300n;
1001
+ /** `constants.rs`. After this many slots from the purchase, anyone may expire the draw. */
1002
+ const TIMEOUT_SLOTS = 216000n;
1003
+ /** `constants.rs`. Three requests in total, counting the one `buy_pack` makes. */
1004
+ const MAX_ATTEMPTS = 3;
1005
+ /**
1006
+ * `constants.rs`. Tokens in one pack, in base units: 1,000,000 tokens at the fixed 6 decimals, or
1007
+ * 0.1% of the 1,000,000,000 supply. Every pool sells packs of this size. The venue decides what a
1008
+ * pack costs, so the pack price follows the coin. Read `pool.packTokens` rather than this when a
1009
+ * pool is at hand: a later program version may change the constant.
1010
+ */
1011
+ const PACK_TOKENS = 1000000n * 1000000n;
1012
+ //#endregion
1013
+ //#region src/math.ts
1014
+ /**
1015
+ * A bigint port of `programs/gabox/src/math.rs`.
1016
+ *
1017
+ * The point is that a client can show a buyer the exact prize table the program will freeze into
1018
+ * their `Draw`, before they pay. Every rounding step here matches the Rust, including the direction
1019
+ * of each division. `test/math.test.ts` runs the same vectors as `programs/gabox/tests/math.rs`.
1020
+ *
1021
+ * All amounts are `bigint`, in the mint's smallest unit. `multiplierBps` and `tickets` are numbers
1022
+ * because both are `u32` in the program and both stay small.
1023
+ */
1024
+ /** Basis points. A multiplier of 10_000 pays back exactly one pack. */
1025
+ const BPS = 10000n;
1026
+ /**
1027
+ * `constants::MIN_SEED_MULTIPLIER_BPS`. The mandatory seed makes a 3x top prize payable on the
1028
+ * first pack, or the full top prize when the table's top tier is below 3x. A creator can add more
1029
+ * seed on top through `extraSeedTokens`.
1030
+ */
1031
+ const MIN_SEED_MULTIPLIER_BPS = 3e4;
1032
+ /**
1033
+ * `constants::MAX_MULTIPLIER_BPS`. A table's top tier can be at most 20x. Seed beyond what a 20x
1034
+ * prize needs stays in the vault as backup for the draws after a top-tier hit.
1035
+ */
1036
+ const MAX_MULTIPLIER_BPS = 2e5;
1037
+ /** Ticket counts must sum to this. A uniform 16-bit word then maps with no modulo bias. */
1038
+ const TICKETS = 65536;
1039
+ /** The prize table has exactly this many slots. Unused slots are all-zero. */
1040
+ const TIERS = 8;
1041
+ /** Thrown by every function here. `code` matches a `GaboxError` variant name. */
1042
+ var GaboxMathError = class extends Error {
1043
+ code;
1044
+ constructor(code, message) {
1045
+ super(`${code}: ${message}`);
1046
+ this.name = "GaboxMathError";
1047
+ this.code = code;
1048
+ }
1049
+ };
1050
+ const U64_MAX = (1n << 64n) - 1n;
1051
+ const U32_MAX = 4294967295;
1052
+ /**
1053
+ * The default prize table. The program does not enforce this table: it accepts any table that
1054
+ * passes `validateTiers`. This is only the table the Gabox app creates its pools with, and the
1055
+ * starting point for a client that has no reason to pick another one.
1056
+ *
1057
+ * Common 75% at 0.52x, Rare 20% at 1.2x, Epic 4% at 3x, Mythic 1% at 20x. Expected payout is
1058
+ * 0.9499x of a pack, so the seed is 19 packs.
1059
+ */
1060
+ const DEFAULT_TIERS = Object.freeze([
1061
+ Object.freeze({
1062
+ multiplierBps: 5201,
1063
+ tickets: 49153
1064
+ }),
1065
+ Object.freeze({
1066
+ multiplierBps: 12e3,
1067
+ tickets: 13107
1068
+ }),
1069
+ Object.freeze({
1070
+ multiplierBps: 3e4,
1071
+ tickets: 2621
1072
+ }),
1073
+ Object.freeze({
1074
+ multiplierBps: 2e5,
1075
+ tickets: 655
1076
+ }),
1077
+ Object.freeze({
1078
+ multiplierBps: 0,
1079
+ tickets: 0
1080
+ }),
1081
+ Object.freeze({
1082
+ multiplierBps: 0,
1083
+ tickets: 0
1084
+ }),
1085
+ Object.freeze({
1086
+ multiplierBps: 0,
1087
+ tickets: 0
1088
+ }),
1089
+ Object.freeze({
1090
+ multiplierBps: 0,
1091
+ tickets: 0
1092
+ })
1093
+ ]);
1094
+ function checkedU64(value, what) {
1095
+ if (value < 0n || value > U64_MAX) throw new GaboxMathError("Arithmetic", `${what} does not fit in u64`);
1096
+ return value;
1097
+ }
1098
+ /** Tier fields encode as program `u32`s. Reject JavaScript-only values before `BigInt` or codecs. */
1099
+ function checkedU32(value, what) {
1100
+ if (!Number.isInteger(value) || value < 0 || value > U32_MAX) throw new GaboxMathError("InvalidDistribution", `${what} must be an integer from 0 to ${U32_MAX}`);
1101
+ return value;
1102
+ }
1103
+ /** `math::tokens`. Floor division, and an overflow past u64 is an error, not a wrap. */
1104
+ function tierAmount(base, multiplierBps) {
1105
+ return checkedU64(base * BigInt(checkedU32(multiplierBps, "multiplierBps")) / BPS, "tier amount");
1106
+ }
1107
+ /**
1108
+ * `math::validate`. Checks the table alone, with no base.
1109
+ *
1110
+ * Four rules, and each one closes a way to sell a bad ticket:
1111
+ * - ticket counts sum to exactly 65,536, so the 16-bit draw is uniform;
1112
+ * - an unused row is all-zero, so a hidden multiplier cannot ride along;
1113
+ * - a ticketed tier's multiplier is from 1 to `MAX_MULTIPLIER_BPS` (20x);
1114
+ * - the expected multiplier over all tickets is at most 1x, so the table cannot promise more
1115
+ * tokens than a pack buys. This bounds tokens, not cash value.
1116
+ */
1117
+ function validateTiers(tiers) {
1118
+ if (tiers.length !== 8) throw new GaboxMathError("InvalidDistribution", `expected 8 tiers, got ${tiers.length}`);
1119
+ let count = 0n;
1120
+ let expected = 0n;
1121
+ for (const [index, tier] of tiers.entries()) {
1122
+ checkedU32(tier.tickets, `tier ${index} tickets`);
1123
+ checkedU32(tier.multiplierBps, `tier ${index} multiplierBps`);
1124
+ if (tier.tickets === 0) {
1125
+ if (tier.multiplierBps !== 0) throw new GaboxMathError("InvalidDistribution", "a tier with no tickets must have no multiplier");
1126
+ continue;
1127
+ }
1128
+ if (tier.multiplierBps <= 0) throw new GaboxMathError("InvalidDistribution", "a ticketed tier must pay something; it would sell a ticket that can only win zero");
1129
+ if (tier.multiplierBps > 2e5) throw new GaboxMathError("InvalidDistribution", `tier ${index} multiplierBps exceeds the ${MAX_MULTIPLIER_BPS} maximum (20x)`);
1130
+ count += BigInt(tier.tickets);
1131
+ expected += BigInt(tier.multiplierBps) * BigInt(tier.tickets);
1132
+ }
1133
+ if (count !== BigInt(65536)) throw new GaboxMathError("InvalidDistribution", `tickets must sum to ${TICKETS}, they sum to ${count}`);
1134
+ if (expected > 10000n * BigInt(65536)) throw new GaboxMathError("UnfundedExpectation", "the expected token award exceeds the tokens a pack buys");
1135
+ }
1136
+ /**
1137
+ * `math::validate_pack`. Every ticketed tier must pay at least one token for a pack of this size.
1138
+ *
1139
+ * The program checks this once at creation. At `PACK_TOKENS` no sane table fails it; it exists so
1140
+ * a table cannot sell a ticket that can only win zero.
1141
+ */
1142
+ function validatePack(packTokens, tiers) {
1143
+ if (packTokens <= 0n) throw new GaboxMathError("ZeroAmount", "packTokens must be positive");
1144
+ checkedU64(packTokens, "pack tokens");
1145
+ validateTiers(tiers);
1146
+ for (const [i, tier] of tiers.entries()) {
1147
+ if (tier.tickets === 0) continue;
1148
+ if (tierAmount(packTokens, tier.multiplierBps) === 0n) throw new GaboxMathError("PackTooSmall", `tier ${i} rounds to zero tokens at this pack size`);
1149
+ }
1150
+ }
1151
+ /**
1152
+ * `math::seed_tokens`. The mandatory seed a table needs, in tokens.
1153
+ *
1154
+ * The mandatory seed makes a 3x top prize payable on the first pack, or the full top prize when
1155
+ * the table's top tier is below 3x. A jackpot above 3x still shows the full table once later
1156
+ * packs grow the free vault; the mandatory seed alone does not have to fund it. The pack itself
1157
+ * brings `packTokens` into the vault, so the vault has to hold the rest beforehand:
1158
+ *
1159
+ * seedTokens = min(uncappedMaximum(packTokens, tiers), tierAmount(packTokens, MIN_SEED_MULTIPLIER_BPS)) - packTokens
1160
+ *
1161
+ * A 20x or a 3x top tier on a 1M-token pack both need a 2M-token seed, since the mandatory seed
1162
+ * stops at the 3x minimum. A 2x top tier needs a 1M-token seed, already below the minimum. A
1163
+ * table whose top tier pays exactly one pack needs no seed. A top tier below one pack is refused:
1164
+ * every ticket would lose. `createMachine` lets a creator add more seed on top of this mandatory
1165
+ * amount through `extraSeedTokens`, for example to fund a top tier above 3x from the first pack.
1166
+ *
1167
+ * Pool initialization computes this number itself and buys exactly that many tokens on the curve.
1168
+ * The creator only signs a maximum SOL cost.
1169
+ */
1170
+ function seedTokens(packTokens, tiers) {
1171
+ validateTiers(tiers);
1172
+ const largest = uncappedMaximum(packTokens, tiers);
1173
+ if (largest < packTokens) throw new GaboxMathError("JackpotBelowOnePack", "the largest tier must pay at least one pack");
1174
+ const minimum = tierAmount(packTokens, MIN_SEED_MULTIPLIER_BPS);
1175
+ return (largest < minimum ? largest : minimum) - packTokens;
1176
+ }
1177
+ /**
1178
+ * `math::uncapped_maximum`. The largest tier's award for this base, before any inventory cap.
1179
+ *
1180
+ * At `packTokens` this is the jackpot in tokens. `seedTokens` is this minus one pack.
1181
+ */
1182
+ function uncappedMaximum(base, tiers) {
1183
+ validateTiers(tiers);
1184
+ let largest = 0n;
1185
+ for (const tier of tiers) {
1186
+ if (tier.tickets === 0) continue;
1187
+ const amount = tierAmount(base, tier.multiplierBps);
1188
+ if (amount > largest) largest = amount;
1189
+ }
1190
+ return largest;
1191
+ }
1192
+ /**
1193
+ * `math::quote`. The offer a pack of `base` tokens would freeze right now. `base` is always
1194
+ * `pool.packTokens`; the parameter stays general so the vectors can use small numbers.
1195
+ *
1196
+ * `inventory` is the vault's token balance and `reserved` is `pool.reserved`. The difference is
1197
+ * free inventory. This pack's own `base` is added to it, because the purchase and the offer are
1198
+ * one transaction — the tokens are in the vault before the draw is written.
1199
+ *
1200
+ * Every amount is capped at what is actually available. A tier that rounds to zero tokens is an
1201
+ * error: the pool must not sell a ticket that can only win nothing.
1202
+ */
1203
+ function quote(base, tiers, inventory, reserved) {
1204
+ validateTiers(tiers);
1205
+ if (inventory < reserved) throw new GaboxMathError("InsolventInventory", "the vault holds less than the pool has already reserved");
1206
+ const available = checkedU64(inventory - reserved + base, "available inventory");
1207
+ const prizes = Array.from({ length: 8 }, () => ({
1208
+ amount: 0n,
1209
+ tickets: 0
1210
+ }));
1211
+ let maximum = 0n;
1212
+ let minimum = U64_MAX;
1213
+ for (const [i, tier] of tiers.entries()) {
1214
+ if (tier.tickets === 0) continue;
1215
+ const uncapped = tierAmount(base, tier.multiplierBps);
1216
+ if (uncapped === 0n) throw new GaboxMathError("PackTooSmall", `tier ${i} rounds to zero tokens at this pack size`);
1217
+ const amount = uncapped < available ? uncapped : available;
1218
+ prizes[i] = {
1219
+ amount,
1220
+ tickets: tier.tickets
1221
+ };
1222
+ if (amount > maximum) maximum = amount;
1223
+ if (amount < minimum) minimum = amount;
1224
+ }
1225
+ return {
1226
+ prizes,
1227
+ maximum,
1228
+ minimum
1229
+ };
1230
+ }
1231
+ /**
1232
+ * `math::choose`. Which prize a 16-bit ticket wins.
1233
+ *
1234
+ * The rows are consecutive ranges in table order, so this is a running total and a comparison.
1235
+ * A ticket past the last row returns `0`, which cannot happen for a validated table.
1236
+ */
1237
+ function choose(prizes, ticket) {
1238
+ let end = 0;
1239
+ for (const prize of prizes) {
1240
+ end += prize.tickets;
1241
+ if (ticket < end) return prize.amount;
1242
+ }
1243
+ return 0n;
1244
+ }
1245
+ /** `math::resolve_reservation`. Release the unwon part of a maximum, keep the award reserved. */
1246
+ function resolveReservation(reserved, maximum, award) {
1247
+ if (award > maximum) throw new GaboxMathError("InsolventInventory", "the award exceeds the reserved maximum");
1248
+ if (reserved < maximum) throw new GaboxMathError("Arithmetic", "the pool has reserved less than this draw holds");
1249
+ return checkedU64(reserved - maximum + award, "reserved");
1250
+ }
1251
+ /** The largest `multiplierBps` on any ticketed row. `0` for a table with no rows. */
1252
+ function maxMultiplierBps(tiers) {
1253
+ validateTiers(tiers);
1254
+ let largest = 0;
1255
+ for (const tier of tiers) {
1256
+ if (tier.tickets === 0) continue;
1257
+ if (tier.multiplierBps > largest) largest = tier.multiplierBps;
1258
+ }
1259
+ return largest;
1260
+ }
1261
+ /**
1262
+ * The expected multiplier over all 65,536 tickets, in basis points. Rounded down.
1263
+ *
1264
+ * `validateTiers` caps this at 10,000. A table at 9,800 keeps 2% of every pack's tokens in the
1265
+ * vault on average, which is what lets a pool survive a run of top-tier wins.
1266
+ */
1267
+ function averageMultiplierBps(tiers) {
1268
+ validateTiers(tiers);
1269
+ let weighted = 0n;
1270
+ for (const tier of tiers) {
1271
+ if (tier.tickets === 0) continue;
1272
+ weighted += BigInt(tier.multiplierBps) * BigInt(tier.tickets);
1273
+ }
1274
+ return Number(weighted / BigInt(TICKETS));
1275
+ }
1276
+ //#endregion
969
1277
  //#region src/raydium/pdas.ts
970
1278
  /**
971
1279
  * Every Raydium address a Gabox transaction derives.
@@ -1078,6 +1386,39 @@ const ata = async (owner, mint, tokenProgram = TOKEN_PROGRAM_ADDRESS) => await d
1078
1386
  address$1.encode(mint)
1079
1387
  ]);
1080
1388
  //#endregion
1389
+ //#region src/pdas.ts
1390
+ /**
1391
+ * Gabox's own program-derived addresses.
1392
+ *
1393
+ * Raydium's live in `src/raydium/pdas.ts`, because they depend on the cluster. These do not: the
1394
+ * Gabox program id is the same everywhere.
1395
+ *
1396
+ * Classic SPL Token only. Both venues use it, and the accepted LaunchLab create instruction mints
1397
+ * the coin with it, so no derivation here takes a token program any more.
1398
+ */
1399
+ /** `["pool", mint]`. One pool per coin. */
1400
+ const poolAddress = async (mint) => (await findPoolPda({ mint }))[0];
1401
+ /** `["draw", pool, seq]`, with the sequence as eight little-endian bytes. */
1402
+ const drawAddress = async (pool, seq) => (await findDrawPda({
1403
+ pool,
1404
+ seq
1405
+ }))[0];
1406
+ /** The per-wallet `WalletActivity` PDA. `buy_pack` derives it from the purchaser automatically. */
1407
+ const activityAddress = async (wallet) => (await findActivityPda({ purchaser: wallet }))[0];
1408
+ /** `["identity"]` under Gabox. The PDA the program signs its randomness request with. */
1409
+ const vrfIdentityAddress = async () => (await findIdentityPda())[0];
1410
+ /** `["identity", gabox_program_id]` under the VRF program. MagicBlock signs the callback with it. */
1411
+ async function scopedVrfIdentityAddress() {
1412
+ return await getProgramDerivedAddress({
1413
+ programAddress: VRF_PROGRAM_ADDRESS,
1414
+ seeds: [getBytesEncoder().encode(IDENTITY_SEED), getAddressEncoder().encode(GABOX_PROGRAM_ID)]
1415
+ });
1416
+ }
1417
+ /** An associated token account under classic SPL Token. */
1418
+ const associatedTokenAddress = async (owner, mint, tokenProgram = TOKEN_PROGRAM_ADDRESS) => await ata(owner, mint, tokenProgram);
1419
+ /** The prize vault: the pool PDA's own associated token account for the coin. */
1420
+ const vaultAddress = async (mint) => await associatedTokenAddress(await poolAddress(mint), mint);
1421
+ //#endregion
1081
1422
  //#region src/raydium/adapter.ts
1082
1423
  /**
1083
1424
  * Reading Raydium's accounts.
@@ -1096,7 +1437,7 @@ const ata = async (owner, mint, tokenProgram = TOKEN_PROGRAM_ADDRESS) => await d
1096
1437
  * Kit's codecs, never `Buffer`. This module is vendored into a Next.js app, and `Buffer` does not
1097
1438
  * exist in a browser.
1098
1439
  */
1099
- const DISCRIMINATOR = 8;
1440
+ const DISCRIMINATOR$1 = 8;
1100
1441
  const u8 = getU8Decoder();
1101
1442
  const u16 = getU16Decoder();
1102
1443
  const u64$1 = getU64Decoder();
@@ -1131,6 +1472,74 @@ function tokenAccountOwnerAndMint(data) {
1131
1472
  owner: addressAt.decode(data, 32)
1132
1473
  };
1133
1474
  }
1475
+ /** The base SPL mint is 82 bytes. Token-2022 repeats those 82 and appends its extensions. */
1476
+ const MINT_BASE_SIZE = 82;
1477
+ /** `decimals` sits after `mint_authority` (4 + 32) and `supply` (8). */
1478
+ const MINT_DECIMALS_AT = 44;
1479
+ /**
1480
+ * The `decimals` of a mint. Both token programs share the first 82 bytes, so one reader covers a
1481
+ * classic SPL mint and a Token-2022 one.
1482
+ */
1483
+ function mintDecimals(data) {
1484
+ if (data.length < MINT_BASE_SIZE) throw new Error(`not a mint: ${data.length} bytes, expected at least ${MINT_BASE_SIZE}`);
1485
+ return u8.decode(data, MINT_DECIMALS_AT);
1486
+ }
1487
+ /** A Borsh string: a u32 little-endian length, then that many UTF-8 bytes. */
1488
+ function borshString$1(data, at) {
1489
+ if (at + 4 > data.length) return null;
1490
+ const length = new DataView(data.buffer, data.byteOffset, data.byteLength).getUint32(at, true);
1491
+ const end = at + 4 + length;
1492
+ if (end > data.length) return null;
1493
+ return {
1494
+ text: new TextDecoder().decode(data.subarray(at + 4, end)),
1495
+ next: end
1496
+ };
1497
+ }
1498
+ /** Metaplex pads its three strings with NUL bytes up to their maximum length. */
1499
+ const trimmed = (text) => {
1500
+ const clean = text.replace(/\0+$/, "").trim();
1501
+ return clean.length > 0 ? clean : null;
1502
+ };
1503
+ /**
1504
+ * The `symbol` of a Metaplex token metadata account, or `null` when it carries none.
1505
+ *
1506
+ * Layout: a one-byte key, `update_authority` and `mint`, then the three Borsh strings `name`,
1507
+ * `symbol` and `uri`. Metaplex stores them at their maximum length, NUL padded.
1508
+ */
1509
+ function metaplexSymbol(data) {
1510
+ const name = borshString$1(data, 65);
1511
+ if (!name) return null;
1512
+ const symbol = borshString$1(data, name.next);
1513
+ return symbol ? trimmed(symbol.text) : null;
1514
+ }
1515
+ /** The Token-2022 extension type of `TokenMetadata`, from the SPL token metadata interface. */
1516
+ const TOKEN_METADATA_EXTENSION = 19;
1517
+ /** Token-2022 writes one account-type byte after the 82-byte mint, then its TLV extensions. */
1518
+ const EXTENSION_START = 166;
1519
+ /**
1520
+ * The `symbol` a Token-2022 mint stores in its own `TokenMetadata` extension, or `null`.
1521
+ *
1522
+ * The extensions are a TLV list: a u16 type, a u16 length, then the value. The metadata value is
1523
+ * `update_authority`, `mint`, then the Borsh strings `name`, `symbol` and `uri`.
1524
+ */
1525
+ function token2022Symbol(data) {
1526
+ let at = EXTENSION_START;
1527
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
1528
+ while (at + 4 <= data.length) {
1529
+ const type = view.getUint16(at, true);
1530
+ const length = view.getUint16(at + 2, true);
1531
+ const value = at + 4;
1532
+ if (value + length > data.length) return null;
1533
+ if (type === TOKEN_METADATA_EXTENSION) {
1534
+ const name = borshString$1(data.subarray(value, value + length), 64);
1535
+ if (!name) return null;
1536
+ const symbol = borshString$1(data.subarray(value, value + length), name.next);
1537
+ return symbol ? trimmed(symbol.text) : null;
1538
+ }
1539
+ at = value + length;
1540
+ }
1541
+ return null;
1542
+ }
1134
1543
  /** Offsets follow the `PoolState` field list in `idl/raydium_launchpad.json`. */
1135
1544
  const LAUNCHLAB_POOL_HEAD = getStructDecoder([
1136
1545
  ["epoch", u64$1],
@@ -1169,7 +1578,7 @@ const LAUNCHLAB_POOL_STATE_HEAD_SIZE = 367;
1169
1578
  function decodeLaunchlabPool(data) {
1170
1579
  checked(data, LAUNCHLAB_POOL_STATE_DISCRIMINATOR, "LaunchLab pool");
1171
1580
  atLeast(data, 367, "LaunchLab pool");
1172
- const head = LAUNCHLAB_POOL_HEAD.decode(data, DISCRIMINATOR);
1581
+ const head = LAUNCHLAB_POOL_HEAD.decode(data, DISCRIMINATOR$1);
1173
1582
  return {
1174
1583
  status: head.status,
1175
1584
  baseDecimals: head.baseDecimals,
@@ -1211,12 +1620,13 @@ const LAUNCHLAB_GLOBAL_CONFIG_HEAD_SIZE = 115;
1211
1620
  function decodeLaunchlabGlobalConfig(data) {
1212
1621
  checked(data, LAUNCHLAB_GLOBAL_CONFIG_DISCRIMINATOR, "LaunchLab global config");
1213
1622
  atLeast(data, 115, "LaunchLab global config");
1214
- const head = LAUNCHLAB_GLOBAL_CONFIG_HEAD.decode(data, DISCRIMINATOR);
1623
+ const head = LAUNCHLAB_GLOBAL_CONFIG_HEAD.decode(data, DISCRIMINATOR$1);
1215
1624
  return {
1216
1625
  curveType: head.curveType,
1217
1626
  index: head.index,
1218
1627
  migrateFee: head.migrateFee,
1219
1628
  tradeFeeRate: head.tradeFeeRate,
1629
+ minQuoteFundRaising: head.minQuoteFundRaising,
1220
1630
  quoteMint: head.quoteMint
1221
1631
  };
1222
1632
  }
@@ -1241,7 +1651,7 @@ const LAUNCHLAB_PLATFORM_CONFIG_HEAD_SIZE = 940;
1241
1651
  function decodeLaunchlabPlatformConfig(data) {
1242
1652
  checked(data, LAUNCHLAB_PLATFORM_CONFIG_DISCRIMINATOR, "LaunchLab platform config");
1243
1653
  atLeast(data, 940, "LaunchLab platform config");
1244
- const head = LAUNCHLAB_PLATFORM_CONFIG_HEAD.decode(data, DISCRIMINATOR);
1654
+ const head = LAUNCHLAB_PLATFORM_CONFIG_HEAD.decode(data, DISCRIMINATOR$1);
1245
1655
  return {
1246
1656
  platformFeeWallet: head.platformFeeWallet,
1247
1657
  feeRate: head.feeRate,
@@ -1299,7 +1709,7 @@ const CPMM_POOL_OFFSETS = {
1299
1709
  function decodeCpmmPool(data) {
1300
1710
  checked(data, CPMM_POOL_STATE_DISCRIMINATOR, "CPMM pool");
1301
1711
  atLeast(data, 413, "CPMM pool");
1302
- const head = CPMM_POOL_HEAD.decode(data, DISCRIMINATOR);
1712
+ const head = CPMM_POOL_HEAD.decode(data, DISCRIMINATOR$1);
1303
1713
  return {
1304
1714
  ammConfig: head.ammConfig,
1305
1715
  poolCreator: head.poolCreator,
@@ -1340,7 +1750,7 @@ const CPMM_AMM_CONFIG_HEAD_SIZE = 116;
1340
1750
  function decodeCpmmAmmConfig(data) {
1341
1751
  checked(data, CPMM_AMM_CONFIG_DISCRIMINATOR, "CPMM amm config");
1342
1752
  atLeast(data, 116, "CPMM amm config");
1343
- const head = CPMM_AMM_CONFIG_HEAD.decode(data, DISCRIMINATOR);
1753
+ const head = CPMM_AMM_CONFIG_HEAD.decode(data, DISCRIMINATOR$1);
1344
1754
  return {
1345
1755
  index: head.index,
1346
1756
  disableCreatePool: head.disableCreatePool,
@@ -1351,6 +1761,121 @@ function decodeCpmmAmmConfig(data) {
1351
1761
  };
1352
1762
  }
1353
1763
  //#endregion
1764
+ //#region src/accounts.ts
1765
+ /** Account readers and bounded discovery scans. Old pool layouts are intentionally unsupported. */
1766
+ const DISCRIMINATOR = 8;
1767
+ const DRAW_POOL_OFFSET = DISCRIMINATOR;
1768
+ const DRAW_PURCHASER_OFFSET = 40;
1769
+ const POOL_CREATOR_OFFSET = DISCRIMINATOR;
1770
+ const POOL_MINT_OFFSET = 40;
1771
+ const base58 = getBase58Decoder();
1772
+ const base64$1 = getBase64Encoder();
1773
+ const asBase58 = (bytes) => base58.decode(bytes);
1774
+ const memcmp$1 = (offset, bytes) => ({ memcmp: {
1775
+ offset: BigInt(offset),
1776
+ bytes,
1777
+ encoding: "base58"
1778
+ } });
1779
+ const encoded = (address, data) => ({
1780
+ address,
1781
+ data,
1782
+ exists: true,
1783
+ executable: false,
1784
+ lamports: 0n,
1785
+ programAddress: GABOX_PROGRAM_ID,
1786
+ space: BigInt(data.length)
1787
+ });
1788
+ async function fetchPoolByMint(client, mint) {
1789
+ return await fetchPoolAt(client, await poolAddress(mint));
1790
+ }
1791
+ async function fetchPoolAt(client, address) {
1792
+ return decodeCurrentPool(await fetchEncodedAccount(client.rpc, address, { commitment: "confirmed" }));
1793
+ }
1794
+ function decodeCurrentPool(account) {
1795
+ if (!account.exists) return null;
1796
+ if (account.data.length !== getPoolSize()) throw new Error(`pool ${account.address} is not a Gabox pool and is intentionally unsupported`);
1797
+ return decodePool(account).data;
1798
+ }
1799
+ function decodeCurrentDraw(account) {
1800
+ if (!account.exists) return null;
1801
+ if (account.data.length !== getDrawSize()) throw new Error(`draw ${account.address} is not a Gabox draw and is intentionally unsupported`);
1802
+ return decodeDraw(account).data;
1803
+ }
1804
+ async function fetchDraw(client, address) {
1805
+ return decodeCurrentDraw(await fetchEncodedAccount(client.rpc, address, { commitment: "confirmed" }));
1806
+ }
1807
+ function decodeCurrentWalletActivity(account) {
1808
+ if (!account.exists) return null;
1809
+ if (account.data.length !== getWalletActivitySize()) throw new Error(`activity account ${account.address} is not a Gabox WalletActivity and is intentionally unsupported`);
1810
+ return decodeWalletActivity(account).data;
1811
+ }
1812
+ /**
1813
+ * A wallet's lifetime pack-buying activity, or `null` before it has bought its first pack.
1814
+ * `buy_pack` creates this account `init_if_needed`, so a fresh wallet has none yet.
1815
+ */
1816
+ async function fetchWalletActivity(client, wallet) {
1817
+ return decodeCurrentWalletActivity(await fetchEncodedAccount(client.rpc, await activityAddress(wallet), { commitment: "confirmed" }));
1818
+ }
1819
+ async function scan(rpc, filters, decode) {
1820
+ return (await rpc.getProgramAccounts(GABOX_PROGRAM_ID, {
1821
+ encoding: "base64",
1822
+ commitment: "confirmed",
1823
+ filters
1824
+ }).send()).map(({ pubkey, account }) => decode(pubkey, new Uint8Array(base64$1.encode(account.data[0]))));
1825
+ }
1826
+ async function listPools(client) {
1827
+ return await scan(client.rpc, [memcmp$1(0, asBase58(POOL_DISCRIMINATOR)), { dataSize: BigInt(getPoolSize()) }], (address, data) => ({
1828
+ address,
1829
+ data: decodePool(encoded(address, data)).data
1830
+ }));
1831
+ }
1832
+ async function listDraws(client, query = {}) {
1833
+ const filters = [memcmp$1(0, asBase58(DRAW_DISCRIMINATOR)), { dataSize: BigInt(getDrawSize()) }];
1834
+ if (query.pool) filters.push(memcmp$1(8, query.pool));
1835
+ if (query.purchaser) filters.push(memcmp$1(40, query.purchaser));
1836
+ return await scan(client.rpc, filters, (address, data) => ({
1837
+ address,
1838
+ data: decodeDraw(encoded(address, data)).data
1839
+ }));
1840
+ }
1841
+ const listDrawsByPool = async (client, pool) => await listDraws(client, { pool });
1842
+ const listDrawsByPurchaser = async (client, purchaser) => await listDraws(client, { purchaser });
1843
+ async function fetchVaultBalance(client, mint) {
1844
+ const vault = await vaultAddress(mint);
1845
+ const { value } = await client.rpc.getAccountInfo(vault, {
1846
+ encoding: "base64",
1847
+ commitment: "confirmed"
1848
+ }).send();
1849
+ return value ? tokenAccountAmount(new Uint8Array(base64$1.encode(value.data[0]))) : 0n;
1850
+ }
1851
+ async function fetchPoolInventory(client, mint) {
1852
+ const poolPda = await poolAddress(mint);
1853
+ const vaultPda = await vaultAddress(mint);
1854
+ const { value } = await client.rpc.getMultipleAccounts([poolPda, vaultPda], {
1855
+ encoding: "base64",
1856
+ commitment: "confirmed"
1857
+ }).send();
1858
+ const poolAccount = value[0];
1859
+ if (!poolAccount) return null;
1860
+ const pool = decodeCurrentPool(encoded(poolPda, new Uint8Array(base64$1.encode(poolAccount.data[0]))));
1861
+ if (!pool) return null;
1862
+ if (pool.vault !== vaultPda) throw new Error(`pool ${poolPda} has an unexpected vault; it is not a supported Gabox pool`);
1863
+ const vaultAccount = value[1];
1864
+ const inventory = vaultAccount ? tokenAccountAmount(new Uint8Array(base64$1.encode(vaultAccount.data[0]))) : 0n;
1865
+ return {
1866
+ poolAddress: poolPda,
1867
+ pool,
1868
+ inventory,
1869
+ reserved: pool.reserved,
1870
+ free: inventory > pool.reserved ? inventory - pool.reserved : 0n
1871
+ };
1872
+ }
1873
+ const tiersOf = (pool) => pool.tiers.map(({ multiplierBps, tickets }) => ({
1874
+ multiplierBps,
1875
+ tickets
1876
+ }));
1877
+ const quotePool = (inventory) => quote(inventory.pool.packTokens, tiersOf(inventory.pool), inventory.inventory, inventory.reserved);
1878
+ //#endregion
1354
1879
  //#region src/compute.ts
1355
1880
  /**
1356
1881
  * The two `ComputeBudget` instructions the transaction builders prepend.
@@ -1375,6 +1900,9 @@ function decodeCpmmAmmConfig(data) {
1375
1900
  * same work, so the venue's own cost moves with state this SDK does not control. The 1.3x margin is
1376
1901
  * there to absorb that, and the largest of the three is always the basis.
1377
1902
  *
1903
+ * None of them covers a swap. When a builder puts a route in the same transaction, the route states
1904
+ * its own cost as `Route.computeUnits` and the builder adds it, capped at `MAX_COMPUTE_UNIT_LIMIT`.
1905
+ *
1378
1906
  * Re-measure after a change to a builder or to a Raydium program. `test/devnet.e2e.test.ts` prints
1379
1907
  * `CU <label>: <units> | <signature>` for every transaction it sends, which is where these came
1380
1908
  * from.
@@ -1386,25 +1914,26 @@ const MAX_COMPUTE_UNIT_LIMIT = 14e5;
1386
1914
  const DEFAULT_COMPUTE_UNIT_LIMIT = 2e5;
1387
1915
  /**
1388
1916
  * LaunchLab `initialize_v2`, the WSOL wrap, pool initialization with a seed buy, and the WSOL
1389
- * close, in one transaction. Measured at `293,004`, `264,249` and `268,749`. The first run is the
1390
- * worst case: that creator had no LaunchLab fee vault yet, so the transaction created one.
1917
+ * close, in one transaction. Measured at `266,488`, `258,988` and `267,988` over three devnet runs
1918
+ * against the slot-500297753 build.
1391
1919
  */
1392
- const CREATE_MACHINE_COMPUTE_UNITS = 385e3;
1920
+ const CREATE_MACHINE_COMPUTE_UNITS = 35e4;
1393
1921
  /**
1394
1922
  * A WSOL wrap, a venue buy, an escrow transfer, a draw init, a VRF request and the WSOL close.
1395
- * Measured at `164,717`, `167,723` and `181,225` on the curve, and `116,332`, `113,342` and
1396
- * `119,334` on CPMM. The curve buy is dearer because a coin's first trade also creates the platform
1923
+ * Measured at `178,690`, `163,686` and `157,692` on the curve, and `113,891`, `110,922` and
1924
+ * `106,387` on CPMM. The curve buy is dearer because a coin's first trade also creates the platform
1397
1925
  * and creator fee vaults.
1398
1926
  */
1399
- const BUY_PACK_COMPUTE_UNITS = 24e4;
1927
+ const BUY_PACK_COMPUTE_UNITS = 235e3;
1400
1928
  /**
1401
- * A WSOL wrap, a venue sale, and the WSOL close. Measured at `93,827`, `93,827` and `101,327` on
1402
- * the curve, and `61,041`, `64,041` and `67,041` on CPMM.
1929
+ * A WSOL wrap, a venue sale, and the WSOL close. Measured at `94,246`, `86,745` and `88,246` on the
1930
+ * curve, and `61,545`, `55,576` and `57,046` on CPMM.
1403
1931
  */
1404
- const REDEEM_COMPUTE_UNITS = 135e3;
1932
+ const REDEEM_COMPUTE_UNITS = 125e3;
1405
1933
  /**
1406
- * A creator fee claim: create the WSOL account, claim or collect, close it again. Measured at
1407
- * `31,319` every run for the LaunchLab claim, and `40,959` every run for the CPMM collect.
1934
+ * A creator fee claim: create the quote account, claim or collect, and close it when it is WSOL.
1935
+ * Measured at `31,319` every run for the LaunchLab claim and `40,959` for the CPMM collect. A claim
1936
+ * on a pool quoted in another token is cheaper, `17,635`, because nothing has to be unwrapped.
1408
1937
  */
1409
1938
  const CLAIM_COMPUTE_UNITS = 55e3;
1410
1939
  /** `ComputeBudgetInstruction`'s discriminants. Positional and append-only upstream. */
@@ -1452,6 +1981,103 @@ function computeBudgetInstructions(units, microLamports) {
1452
1981
  return instructions;
1453
1982
  }
1454
1983
  //#endregion
1984
+ //#region src/raydium/read.ts
1985
+ /**
1986
+ * One account reader, shared by every module that decodes a Raydium account.
1987
+ *
1988
+ * Kit's codecs, not `Buffer`. This SDK is vendored into a Next.js app and `Buffer` is not defined
1989
+ * in the browser.
1990
+ */
1991
+ const base64 = getBase64Encoder();
1992
+ /** Read several accounts in one call. A missing account comes back as `null`, not as an error. */
1993
+ async function readAccounts(rpc, addresses) {
1994
+ const { value } = await rpc.getMultipleAccounts(addresses, {
1995
+ encoding: "base64",
1996
+ commitment: "confirmed"
1997
+ }).send();
1998
+ return value.map((account) => {
1999
+ if (!account) return null;
2000
+ const [encoded] = account.data;
2001
+ return {
2002
+ data: new Uint8Array(base64.encode(encoded)),
2003
+ owner: account.owner
2004
+ };
2005
+ });
2006
+ }
2007
+ /** Decode one base64 account payload the RPC returned inside another shape. */
2008
+ const decodeBase64 = (encoded) => new Uint8Array(base64.encode(encoded));
2009
+ //#endregion
2010
+ //#region src/raydium/quote.ts
2011
+ /**
2012
+ * LaunchLab's global config for one quote mint, or `null` when Raydium enabled no such quote.
2013
+ *
2014
+ * Index 0 is the usual config, and the constant-product curve is the only shape Gabox launches, so
2015
+ * those two are fixed here. The decoded `quoteMint` has to be the mint asked for: a config for a
2016
+ * different quote would price the wrong asset.
2017
+ */
2018
+ async function fetchQuoteConfig(client, mint, ids = raydiumIds(client.cluster)) {
2019
+ const address = await launchlabGlobalConfig(ids.launchlab, mint, 0, 0);
2020
+ const [account] = await readAccounts(client.rpc, [address]);
2021
+ if (!account || account.owner !== ids.launchlab) return null;
2022
+ let config;
2023
+ try {
2024
+ config = decodeLaunchlabGlobalConfig(account.data);
2025
+ } catch {
2026
+ return null;
2027
+ }
2028
+ if (config.curveType !== 0 || config.quoteMint !== mint) return null;
2029
+ return {
2030
+ ...config,
2031
+ address
2032
+ };
2033
+ }
2034
+ /** Can a Gabox machine be quoted in this mint? One account read. */
2035
+ async function isQuoteSupported(client, mint) {
2036
+ return await fetchQuoteConfig(client, mint) !== null;
2037
+ }
2038
+ /**
2039
+ * The full quote asset: its LaunchLab config, its token program, its decimals and its symbol.
2040
+ *
2041
+ * Two round trips. The first reads the global config, because the config address is derived from
2042
+ * the mint. The second reads the mint and its Metaplex metadata account together.
2043
+ *
2044
+ * Throws when Raydium has no config for the mint, because no pool can be created or traded then.
2045
+ */
2046
+ async function fetchQuoteAsset(client, mint, ids = raydiumIds(client.cluster)) {
2047
+ const config = await fetchQuoteConfig(client, mint, ids);
2048
+ if (!config) throw new Error(`Raydium has no LaunchLab global config for ${mint}, so it is not a quote asset Gabox can use. Pick a quote Raydium enabled, such as wrapped SOL or USDC.`);
2049
+ const display = await fetchQuoteDisplay(client, mint);
2050
+ return {
2051
+ mint,
2052
+ config: config.address,
2053
+ tokenProgram: display.tokenProgram,
2054
+ decimals: display.decimals,
2055
+ symbol: display.symbol,
2056
+ tradeFeeRate: config.tradeFeeRate,
2057
+ migrateFee: config.migrateFee,
2058
+ minQuoteFundRaising: config.minQuoteFundRaising
2059
+ };
2060
+ }
2061
+ /**
2062
+ * Read a quote mint and its Metaplex metadata in one call.
2063
+ *
2064
+ * A Token-2022 mint may carry its own `TokenMetadata` extension, and that one wins: it is the
2065
+ * issuer's own record. A classic SPL mint keeps its symbol in a Metaplex account, and a mint with
2066
+ * neither reports `null`.
2067
+ */
2068
+ async function fetchQuoteDisplay(client, mint) {
2069
+ const [mintAccount, metadata] = await readAccounts(client.rpc, [mint, await metadataAddress(mint)]);
2070
+ if (!mintAccount) throw new Error(`the quote mint ${mint} is not on chain`);
2071
+ const tokenProgram = mintAccount.owner;
2072
+ if (tokenProgram !== "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" && tokenProgram !== "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb") throw new Error(`the quote mint ${mint} is owned by ${tokenProgram}, which is neither classic SPL Token nor Token-2022`);
2073
+ const own = tokenProgram === "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" ? token2022Symbol(mintAccount.data) : null;
2074
+ return {
2075
+ tokenProgram,
2076
+ decimals: mintDecimals(mintAccount.data),
2077
+ symbol: own ?? (metadata ? metaplexSymbol(metadata.data) : null)
2078
+ };
2079
+ }
2080
+ //#endregion
1455
2081
  //#region src/raydium/accounts.ts
1456
2082
  /**
1457
2083
  * The ordered account list of every Raydium trade Gabox forwards.
@@ -1475,7 +2101,14 @@ function computeBudgetInstructions(units, microLamports) {
1475
2101
  *
1476
2102
  * CPMM calls its four pairs `input_*` and `output_*`. A buy sends quote and receives the coin; a
1477
2103
  * sell is the other way round. So the two builders below fill the same four names with opposite
1478
- * addresses. LaunchLab names its accounts by role instead, and uses one list for both sides.
2104
+ * addresses, the token programs included. LaunchLab names its accounts by role instead, and uses
2105
+ * one list for both sides.
2106
+ *
2107
+ * # The token programs are inputs, not constants
2108
+ *
2109
+ * The base coin is always classic SPL Token. The quote may be Token-2022, so its program travels
2110
+ * with the pool: `pool.quoteTokenProgram` on the curve, and the two programs a migrated CPMM pool
2111
+ * records in its own data. `venue/trade.rs` checks both slots against the same values.
1479
2112
  */
1480
2113
  /**
1481
2114
  * Turn `{name -> address}` into the ABI's ordered `AccountMeta[]`.
@@ -1508,7 +2141,7 @@ function launchlabTradeAccounts(abi, input) {
1508
2141
  base_token_mint: input.mint,
1509
2142
  quote_token_mint: input.quoteMint,
1510
2143
  base_token_program: TOKEN_PROGRAM_ADDRESS,
1511
- quote_token_program: TOKEN_PROGRAM_ADDRESS,
2144
+ quote_token_program: input.quoteTokenProgram,
1512
2145
  event_authority: input.launchlabEventAuthority,
1513
2146
  program: input.launchlab,
1514
2147
  system_program: SYSTEM_PROGRAM_ADDRESS,
@@ -1537,8 +2170,8 @@ function cpmmTradeAccounts(abi, input, side) {
1537
2170
  output_token_account: buying ? input.userBaseToken : input.userQuoteToken,
1538
2171
  input_vault: buying ? input.quoteVault : input.baseVault,
1539
2172
  output_vault: buying ? input.baseVault : input.quoteVault,
1540
- input_token_program: TOKEN_PROGRAM_ADDRESS,
1541
- output_token_program: TOKEN_PROGRAM_ADDRESS,
2173
+ input_token_program: buying ? input.quoteTokenProgram : input.baseTokenProgram,
2174
+ output_token_program: buying ? input.baseTokenProgram : input.quoteTokenProgram,
1542
2175
  input_token_mint: buying ? input.quoteMint : input.mint,
1543
2176
  output_token_mint: buying ? input.mint : input.quoteMint,
1544
2177
  observation_state: input.observationState
@@ -1722,69 +2355,40 @@ function cpmmSwapBaseInput(sides, rates, amountIn) {
1722
2355
  }
1723
2356
  //#endregion
1724
2357
  //#region src/raydium/venue.ts
2358
+ /** The quote binding a Gabox pool records. One account read. */
2359
+ async function fetchQuoteBinding(client, mint) {
2360
+ const pool = await fetchPoolByMint(client, mint);
2361
+ if (!pool) throw new Error(`no Gabox pool for mint ${mint}, so its quote asset is unknown. Pass \`quote\` to price a coin that has no pool.`);
2362
+ return {
2363
+ mint: pool.quoteMint,
2364
+ config: pool.quoteConfig,
2365
+ tokenProgram: pool.quoteTokenProgram
2366
+ };
2367
+ }
1725
2368
  /**
1726
- * Reading the venue: which one a coin trades on right now, what a pack costs there, and the account
1727
- * list for the trade.
1728
- *
1729
- * `resolveVenue` reads every account the answer depends on, decodes them, and hands back a plain
1730
- * object whose methods are pure. A caller that wants to re-price a different token count calls
1731
- * `quoteBuy` again with no further reads.
2369
+ * The user's quote associated token account, under the quote's own token program.
1732
2370
  *
1733
- * # Choosing the venue
1734
- *
1735
- * The LaunchLab pool's `status` is the whole rule:
1736
- *
1737
- * - `0`: the curve is still selling, so trade LaunchLab.
1738
- * - `1`: the raise is finished and Raydium's own bot is moving the coin into a CPMM pool. Nothing
1739
- * trades. This throws, and the message says to retry in a moment. On devnet the move usually
1740
- * finishes within a minute.
1741
- * - `2`: the coin graduated, so trade its CPMM pool.
1742
- *
1743
- * A coin can graduate between the read and the send. A pack buy then fails and no draw is created,
1744
- * which is the intended outcome: refresh and retry.
1745
- *
1746
- * # Finding the migrated CPMM pool
1747
- *
1748
- * The pool is normally at `["pool", cpswap_config, token_0, token_1]` under CPMM, where
1749
- * `cpswap_config` is the fee tier the Gabox platform config names. It is not always: when that
1750
- * address is already taken, Raydium migrates into a random account instead. So this module tries
1751
- * the derived address first and falls back to a filtered `getProgramAccounts` scan.
1752
- *
1753
- * Either way the pool has to prove itself from its own data, which is what `venue/trade.rs` checks
1754
- * on chain as well: CPMM owns it, it carries the `PoolState` discriminator, its `pool_creator` is
1755
- * the coin creator, its two mints are the sorted pair, and `enable_creator_fee` is true. That last
1756
- * flag is the one that cannot be forged. A plain CPMM `initialize` always leaves it false; only a
1757
- * creator Raydium has permissioned, such as LaunchLab's migration, can set it.
2371
+ * Both venues settle in this account, never in native SOL. It must exist and hold enough before a
2372
+ * pack is bought, and sale proceeds land in it. For a WSOL-quoted pool every builder in `tx/` wraps
2373
+ * the SOL it needs into this account and closes it again in the same transaction.
1758
2374
  */
1759
- const base64$1 = getBase64Encoder();
1760
- async function readAccounts(rpc, addresses) {
1761
- const { value } = await rpc.getMultipleAccounts(addresses, {
1762
- encoding: "base64",
1763
- commitment: "confirmed"
1764
- }).send();
1765
- return value.map((account) => {
1766
- if (!account) return null;
1767
- const [encoded] = account.data;
1768
- return {
1769
- data: new Uint8Array(base64$1.encode(encoded)),
1770
- owner: account.owner
1771
- };
1772
- });
2375
+ async function quoteAccountFor(user, quoteMint, quoteTokenProgram = TOKEN_PROGRAM_ADDRESS) {
2376
+ return await ata(user, quoteMint, quoteTokenProgram);
2377
+ }
2378
+ /** The user's WSOL associated token account. WSOL is always classic SPL Token. */
2379
+ async function wsolAccountFor(user) {
2380
+ return await ata(user, WSOL_MINT, TOKEN_PROGRAM_ADDRESS);
1773
2381
  }
1774
2382
  /**
1775
- * The user's WSOL associated token account.
2383
+ * Read the quote's global config and the Gabox platform config, and take the four numbers a quote
2384
+ * needs out of them.
1776
2385
  *
1777
- * Both venues settle in WSOL, never in native SOL. This account must exist and hold enough before a
1778
- * pack is bought, and sale proceeds land in it. Every builder in `tx/` wraps the SOL it needs into
1779
- * this account and closes it again in the same transaction.
2386
+ * `quoteConfig` is the LaunchLab global config of the pool's quote asset: `pool.quoteConfig`, or
2387
+ * the address `raydium.fetchQuoteConfig` derives for a coin that has no pool yet.
1780
2388
  */
1781
- async function wsolAccountFor(user) {
1782
- return await ata(user, WSOL_MINT);
1783
- }
1784
- /** Read the two pinned config accounts and take the four numbers a quote needs out of them. */
1785
- async function fetchCurveSettings(client, ids = raydiumIds(client.cluster)) {
1786
- const [globalAccount, platformAccount] = await readAccounts(client.rpc, [ids.solGlobalConfig, ids.gaboxPlatform]);
1787
- if (!globalAccount) throw new Error(`LaunchLab's WSOL global config is missing at ${ids.solGlobalConfig}`);
2389
+ async function fetchCurveSettings(client, quoteConfig, ids = raydiumIds(client.cluster)) {
2390
+ const [globalAccount, platformAccount] = await readAccounts(client.rpc, [quoteConfig, ids.gaboxPlatform]);
2391
+ if (!globalAccount) throw new Error(`LaunchLab has no global config at ${quoteConfig}`);
1788
2392
  if (!platformAccount) throw new Error(`the Gabox platform config is missing at ${ids.gaboxPlatform}`);
1789
2393
  return settingsFrom(globalAccount.data, platformAccount.data);
1790
2394
  }
@@ -1804,18 +2408,18 @@ function settingsFrom(globalData, platformData) {
1804
2408
  async function resolveVenue(client, options) {
1805
2409
  const ids = raydiumIds(client.cluster);
1806
2410
  const { mint, user } = options;
1807
- const quoteMint = WSOL_MINT;
1808
- const curvePool = await launchlabPoolAddress(ids.launchlab, mint, quoteMint);
2411
+ const quote = options.quote ?? await fetchQuoteBinding(client, mint);
2412
+ const curvePool = await launchlabPoolAddress(ids.launchlab, mint, quote.mint);
1809
2413
  const [poolAccount, globalAccount, platformAccount] = await readAccounts(client.rpc, [
1810
2414
  curvePool,
1811
- ids.solGlobalConfig,
2415
+ quote.config,
1812
2416
  ids.gaboxPlatform
1813
2417
  ]);
1814
- if (!poolAccount) throw new Error(`no LaunchLab pool at ${curvePool}. The coin was not launched on LaunchLab with WSOL as its quote, so it has no Gabox venue.`);
1815
- if (!globalAccount) throw new Error(`LaunchLab's WSOL global config is missing at ${ids.solGlobalConfig}`);
2418
+ if (!poolAccount) throw new Error(`no LaunchLab pool at ${curvePool}. The coin was not launched on LaunchLab with ${quote.mint} as its quote, so it has no Gabox venue.`);
2419
+ if (!globalAccount) throw new Error(`LaunchLab has no global config at ${quote.config}`);
1816
2420
  if (!platformAccount) throw new Error(`the Gabox platform config is missing at ${ids.gaboxPlatform}`);
1817
2421
  const pool = decodeLaunchlabPool(poolAccount.data);
1818
- if (pool.baseMint !== mint || pool.quoteMint !== quoteMint) throw new Error(`the LaunchLab pool at ${curvePool} is not the pool of ${mint}`);
2422
+ if (pool.baseMint !== mint || pool.quoteMint !== quote.mint) throw new Error(`the LaunchLab pool at ${curvePool} is not the pool of ${mint}`);
1819
2423
  const settings = settingsFrom(globalAccount.data, platformAccount.data);
1820
2424
  const kind = options.venue ?? (pool.status === 2 ? "cpmm" : "launchlab");
1821
2425
  if (options.venue === void 0 && pool.status === 1) throw new Error(`${mint} is graduating: its LaunchLab curve is sold out and Raydium is moving it into a CPMM pool. Retry in a moment; the move usually finishes within a minute.`);
@@ -1823,35 +2427,37 @@ async function resolveVenue(client, options) {
1823
2427
  if (pool.status !== 0) throw new Error(`${mint} no longer trades on its LaunchLab curve (status ${pool.status}). Use the CPMM venue.`);
1824
2428
  return await launchlabVenue(ids, pool, settings.rates, {
1825
2429
  mint,
1826
- quoteMint,
2430
+ quote,
1827
2431
  user,
1828
2432
  poolState: curvePool
1829
2433
  });
1830
2434
  }
1831
2435
  return await cpmmVenue(client, ids, pool, settings.cpswapConfig, {
1832
2436
  mint,
1833
- quoteMint,
2437
+ quote,
1834
2438
  user
1835
2439
  });
1836
2440
  }
1837
2441
  async function launchlabVenue(ids, pool, rates, where) {
1838
- const { mint, quoteMint, user, poolState } = where;
2442
+ const { mint, quote, user, poolState } = where;
2443
+ const userQuoteToken = await quoteAccountFor(user, quote.mint, quote.tokenProgram);
1839
2444
  const shared = {
1840
2445
  launchlab: ids.launchlab,
1841
2446
  launchlabAuthority: ids.launchlabAuthority,
1842
2447
  launchlabEventAuthority: ids.launchlabEventAuthority,
1843
- globalConfig: ids.solGlobalConfig,
2448
+ globalConfig: quote.config,
1844
2449
  platformConfig: ids.gaboxPlatform,
1845
2450
  poolState,
1846
2451
  mint,
1847
- quoteMint,
2452
+ quoteMint: quote.mint,
1848
2453
  baseVault: await launchlabVaultAddress(ids.launchlab, poolState, mint),
1849
- quoteVault: await launchlabVaultAddress(ids.launchlab, poolState, quoteMint),
2454
+ quoteVault: await launchlabVaultAddress(ids.launchlab, poolState, quote.mint),
1850
2455
  user,
1851
2456
  userBaseToken: await ata(user, mint),
1852
- userQuoteToken: await ata(user, quoteMint),
1853
- platformFeeVault: await platformFeeVaultAddress(ids.launchlab, ids.gaboxPlatform, quoteMint),
1854
- creatorFeeVault: await creatorFeeVaultAddress(ids.launchlab, pool.creator, quoteMint)
2457
+ userQuoteToken,
2458
+ quoteTokenProgram: quote.tokenProgram,
2459
+ platformFeeVault: await platformFeeVaultAddress(ids.launchlab, ids.gaboxPlatform, quote.mint),
2460
+ creatorFeeVault: await creatorFeeVaultAddress(ids.launchlab, pool.creator, quote.mint)
1855
2461
  };
1856
2462
  const reserves = {
1857
2463
  virtualBase: pool.virtualBase,
@@ -1865,7 +2471,9 @@ async function launchlabVenue(ids, pool, rates, where) {
1865
2471
  kind: "launchlab",
1866
2472
  program: ids.launchlab,
1867
2473
  mint,
1868
- quoteMint,
2474
+ quoteMint: quote.mint,
2475
+ quoteTokenProgram: quote.tokenProgram,
2476
+ userQuoteToken,
1869
2477
  poolState,
1870
2478
  status: pool.status,
1871
2479
  creator: pool.creator,
@@ -1933,7 +2541,7 @@ async function findCpmmPool(client, options) {
1933
2541
  }).send();
1934
2542
  const matches = [];
1935
2543
  for (const { pubkey, account: row } of rows) {
1936
- const pool = decodeCpmmPoolOrNull(new Uint8Array(base64$1.encode(row.data[0])));
2544
+ const pool = decodeCpmmPoolOrNull(decodeBase64(row.data[0]));
1937
2545
  if (pool && cpmmPoolMatches(pool, expect)) matches.push({
1938
2546
  address: pubkey,
1939
2547
  pool
@@ -1961,16 +2569,19 @@ function decodeCpmmPoolOrNull(data) {
1961
2569
  }
1962
2570
  }
1963
2571
  async function cpmmVenue(client, ids, curve, cpswapConfig, where) {
1964
- const { mint, quoteMint, user } = where;
2572
+ const { mint, quote, user } = where;
1965
2573
  const { address: poolState, pool } = await findCpmmPool(client, {
1966
2574
  mint,
1967
- quoteMint,
2575
+ quoteMint: quote.mint,
1968
2576
  creator: curve.creator,
1969
2577
  cpswapConfig
1970
2578
  });
1971
2579
  const baseIsToken0 = pool.token0Mint === mint;
1972
2580
  const baseVault = baseIsToken0 ? pool.token0Vault : pool.token1Vault;
1973
2581
  const quoteVault = baseIsToken0 ? pool.token1Vault : pool.token0Vault;
2582
+ const baseTokenProgram = baseIsToken0 ? pool.token0Program : pool.token1Program;
2583
+ const quoteTokenProgram = baseIsToken0 ? pool.token1Program : pool.token0Program;
2584
+ if (baseTokenProgram !== "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" || quoteTokenProgram !== quote.tokenProgram) throw new Error(`the CPMM pool at ${poolState} records token programs ${baseTokenProgram} and ${quoteTokenProgram}, which are not the ones this coin trades under`);
1974
2585
  const [configAccount, baseVaultAccount, quoteVaultAccount] = await readAccounts(client.rpc, [
1975
2586
  pool.ammConfig,
1976
2587
  baseVault,
@@ -1984,18 +2595,21 @@ async function cpmmVenue(client, ids, curve, cpswapConfig, where) {
1984
2595
  const baseReserve = tokenAccountAmount(baseVaultAccount.data) - owedBase;
1985
2596
  const quoteReserve = tokenAccountAmount(quoteVaultAccount.data) - owedQuote;
1986
2597
  if (baseReserve <= 0n || quoteReserve <= 0n) throw new Error(`the CPMM pool at ${poolState} has no tradable reserves`);
2598
+ const userQuoteToken = await quoteAccountFor(user, quote.mint, quoteTokenProgram);
1987
2599
  const shared = {
1988
2600
  cpmmAuthority: ids.cpmmAuthority,
1989
2601
  ammConfig: pool.ammConfig,
1990
2602
  poolState,
1991
2603
  mint,
1992
- quoteMint,
2604
+ quoteMint: quote.mint,
1993
2605
  baseVault,
1994
2606
  quoteVault,
1995
2607
  observationState: pool.observationKey,
1996
2608
  user,
1997
2609
  userBaseToken: await ata(user, mint),
1998
- userQuoteToken: await ata(user, quoteMint)
2610
+ userQuoteToken,
2611
+ baseTokenProgram,
2612
+ quoteTokenProgram
1999
2613
  };
2000
2614
  const creatorFeeRate = pool.enableCreatorFee ? config.creatorFeeRate : 0n;
2001
2615
  const buySides = {
@@ -2009,7 +2623,7 @@ async function cpmmVenue(client, ids, curve, cpswapConfig, where) {
2009
2623
  const buyRates = {
2010
2624
  tradeFeeRate: config.tradeFeeRate,
2011
2625
  creatorFeeRate,
2012
- creatorFeeOnInput: creatorFeeOnInput(pool, quoteMint)
2626
+ creatorFeeOnInput: creatorFeeOnInput(pool, quote.mint)
2013
2627
  };
2014
2628
  const sellRates = {
2015
2629
  tradeFeeRate: config.tradeFeeRate,
@@ -2020,7 +2634,9 @@ async function cpmmVenue(client, ids, curve, cpswapConfig, where) {
2020
2634
  kind: "cpmm",
2021
2635
  program: ids.cpmm,
2022
2636
  mint,
2023
- quoteMint,
2637
+ quoteMint: quote.mint,
2638
+ quoteTokenProgram,
2639
+ userQuoteToken,
2024
2640
  poolState,
2025
2641
  status: curve.status,
2026
2642
  creator: pool.poolCreator,
@@ -2037,7 +2653,8 @@ async function cpmmVenue(client, ids, curve, cpswapConfig, where) {
2037
2653
  *
2038
2654
  * `creator_fee_on` is 0 when the fee follows the input token, 1 when it is always token 0, and 2
2039
2655
  * when it is always token 1. LaunchLab migrates a Gabox coin with `AmmCreatorFeeOn::QuoteToken`, so
2040
- * the pool charges the creator fee in WSOL only: on the input of a buy, on the output of a sale.
2656
+ * the pool charges the creator fee in the quote token only: on the input of a buy, on the output of
2657
+ * a sale.
2041
2658
  */
2042
2659
  function creatorFeeOnInput(pool, inputMint) {
2043
2660
  if (pool.creatorFeeOn === 0) return true;
@@ -2045,8 +2662,8 @@ function creatorFeeOnInput(pool, inputMint) {
2045
2662
  return pool.creatorFeeOn === 1 ? inputIsToken0 : !inputIsToken0;
2046
2663
  }
2047
2664
  /**
2048
- * WSOL an exact-coins-out buy of `tokens` costs right now, on whichever venue the coin trades.
2049
- * Pass `pool.packTokens` for the pack price.
2665
+ * Quote token an exact-coins-out buy of `tokens` costs right now, on whichever venue the coin
2666
+ * trades. Pass `pool.packTokens` for the pack price.
2050
2667
  *
2051
2668
  * Use `resolveVenue` when you also need the account list, which every transaction builder does.
2052
2669
  * This is for a price display.
@@ -2055,37 +2672,44 @@ async function curveQuote(client, mint, tokens, options = {}) {
2055
2672
  return (await resolveVenue(client, {
2056
2673
  mint,
2057
2674
  user: options.user ?? mint,
2058
- ...options.venue ? { venue: options.venue } : {}
2675
+ ...options.venue ? { venue: options.venue } : {},
2676
+ ...options.quote ? { quote: options.quote } : {}
2059
2677
  })).quoteBuy(tokens);
2060
2678
  }
2061
- /** WSOL a sale of `tokens` returns right now, net of the venue's fees. */
2679
+ /** Quote token a sale of `tokens` returns right now, net of the venue's fees. */
2062
2680
  async function sellQuote(client, mint, tokens, options = {}) {
2063
2681
  return (await resolveVenue(client, {
2064
2682
  mint,
2065
2683
  user: options.user ?? mint,
2066
- ...options.venue ? { venue: options.venue } : {}
2684
+ ...options.venue ? { venue: options.venue } : {},
2685
+ ...options.quote ? { quote: options.quote } : {}
2067
2686
  })).quoteSell(tokens);
2068
2687
  }
2069
2688
  /**
2070
- * WSOL an exact-coins-out buy of `tokens` would cost on a curve that does not exist yet, fees
2071
- * included.
2689
+ * Quote token an exact-coins-out buy of `tokens` would cost on a curve that does not exist yet,
2690
+ * fees included.
2072
2691
  *
2073
2692
  * This is what the seed costs. `initialize_pool` buys it in the same transaction that creates the
2074
2693
  * coin, so the curve is a brand-new LaunchLab curve with the pinned Gabox launch shape at that
2075
2694
  * moment. Exact, unless Raydium changes a fee rate between this read and the send.
2076
2695
  */
2077
- async function newCurveBuyCost(client, tokens) {
2696
+ async function newCurveBuyCost(client, tokens, launch) {
2078
2697
  if (tokens <= 0n) return 0n;
2079
2698
  const ids = raydiumIds(client.cluster);
2080
- const settings = await fetchCurveSettings(client, ids);
2081
- return curveBuyExactOut(newCurveReserves(ids, settings.migrateFee), settings.rates, tokens);
2699
+ const settings = await fetchCurveSettings(client, launch.quoteConfig, ids);
2700
+ return curveBuyExactOut(newCurveReserves(launch.raise, settings.migrateFee), settings.rates, tokens);
2082
2701
  }
2083
- /** The starting reserves of a brand-new Gabox curve on this cluster. */
2084
- function newCurveReserves(ids, migrateFee) {
2702
+ /**
2703
+ * The starting reserves of a brand-new Gabox curve.
2704
+ *
2705
+ * `raise` is `total_quote_fund_raising`, in the quote's own base units. It is the one launch number
2706
+ * the client picks, so the reserves follow it.
2707
+ */
2708
+ function newCurveReserves(raise, migrateFee) {
2085
2709
  return initialCurve({
2086
2710
  supply: LAUNCH_SUPPLY,
2087
2711
  totalBaseSell: LAUNCH_TOTAL_BASE_SELL,
2088
- quoteRaise: ids.launchQuoteRaise,
2712
+ quoteRaise: raise,
2089
2713
  migrateFee
2090
2714
  });
2091
2715
  }
@@ -2136,11 +2760,11 @@ function withRemainingAccounts(instruction, remaining) {
2136
2760
  *
2137
2761
  * | Argument | Value |
2138
2762
  * | --- | --- |
2139
- * | `decimals` | 6 |
2763
+ * | `decimals` | 6 (the coin; the quote keeps its own) |
2140
2764
  * | curve | `Constant` |
2141
2765
  * | `supply` | 1,000,000,000 coins |
2142
2766
  * | `total_base_sell` | 793,100,000 coins |
2143
- * | `total_quote_fund_raising` | 85 SOL on mainnet, 3 SOL on devnet |
2767
+ * | `total_quote_fund_raising` | the caller's `raise`, pinned to 85 SOL on mainnet and 3 SOL on devnet for a WSOL pool |
2144
2768
  * | `migrate_type` | 1, which means graduate into a CPMM pool |
2145
2769
  * | `VestingParams` | all three fields 0 |
2146
2770
  * | `AmmCreatorFeeOn` | `QuoteToken` |
@@ -2187,12 +2811,12 @@ async function getLaunchInstruction(input, ids) {
2187
2811
  const name = bounded(input.name, METADATA_LIMITS.name, "name");
2188
2812
  const symbol = bounded(input.symbol, METADATA_LIMITS.symbol, "symbol");
2189
2813
  const uri = bounded(input.uri, METADATA_LIMITS.uri, "uri");
2190
- const quoteMint = WSOL_MINT;
2814
+ const { quoteMint, quoteTokenProgram } = input;
2191
2815
  const poolState = await launchlabPoolAddress(ids.launchlab, mint.address, quoteMint);
2192
2816
  const metas = order(LAUNCHLAB_INITIALIZE, {
2193
2817
  payer: creator.address,
2194
2818
  creator: creator.address,
2195
- global_config: ids.solGlobalConfig,
2819
+ global_config: input.quoteConfig,
2196
2820
  platform_config: ids.gaboxPlatform,
2197
2821
  authority: ids.launchlabAuthority,
2198
2822
  pool_state: poolState,
@@ -2202,7 +2826,7 @@ async function getLaunchInstruction(input, ids) {
2202
2826
  quote_vault: await launchlabVaultAddress(ids.launchlab, poolState, quoteMint),
2203
2827
  metadata_account: await metadataAddress(mint.address),
2204
2828
  base_token_program: TOKEN_PROGRAM_ADDRESS,
2205
- quote_token_program: TOKEN_PROGRAM_ADDRESS,
2829
+ quote_token_program: quoteTokenProgram,
2206
2830
  metadata_program: METAPLEX_PROGRAM_ADDRESS,
2207
2831
  system_program: SYSTEM_PROGRAM_ADDRESS,
2208
2832
  rent_program: RENT_SYSVAR_ADDRESS,
@@ -2226,7 +2850,7 @@ async function getLaunchInstruction(input, ids) {
2226
2850
  curveTag: 0,
2227
2851
  supply: LAUNCH_SUPPLY,
2228
2852
  totalBaseSell: LAUNCH_TOTAL_BASE_SELL,
2229
- totalQuoteFundRaising: ids.launchQuoteRaise,
2853
+ totalQuoteFundRaising: input.raise,
2230
2854
  migrateType: 1,
2231
2855
  totalLockedAmount: 0n,
2232
2856
  cliffPeriod: 0n,
@@ -2247,36 +2871,41 @@ async function getLaunchInstruction(input, ids) {
2247
2871
  * Gabox charges no fee at all. The creator's income is the share Raydium pays out of its own
2248
2872
  * revenue, and it sits in two different places depending on where the coin trades:
2249
2873
  *
2250
- * - **On the curve.** LaunchLab pays 0.5% of every trade into `[creator, WSOL]` under LaunchLab.
2251
- * One vault per wallet, not one per coin, so a single `claim_creator_fee` sweeps every coin
2252
- * that creator launched.
2874
+ * - **On the curve.** LaunchLab pays 0.5% of every trade into `[creator, quote_mint]` under
2875
+ * LaunchLab. One vault per wallet **per quote asset**, not one per coin, so a single
2876
+ * `claim_creator_fee` sweeps every coin that creator launched against that quote. A creator
2877
+ * with a SOL machine and a USDC machine claims twice, once per quote.
2253
2878
  * - **After graduation.** The CPMM pool keeps the creator's fee inside the pool account, and
2254
2879
  * `collect_creator_fee` pays it out. That is per pool, so it takes one call per coin.
2255
2880
  *
2256
- * Both instructions pay into the creator's WSOL associated token account. Every builder here closes
2257
- * that account afterwards, so the creator ends up with plain SOL in their wallet.
2881
+ * Both instructions pay into the creator's associated token account for the quote mint, under that
2882
+ * mint's own token program. When the quote is WSOL, every builder here closes that account
2883
+ * afterwards, so the creator ends up with plain SOL. For any other quote the account is created if
2884
+ * missing and left alone: closing it would throw away a real balance.
2258
2885
  *
2259
2886
  * **Closing unwraps everything.** If the creator already held WSOL in that account before the
2260
2887
  * claim, closing turns that into SOL too. Nothing is lost, but the balance moves.
2261
2888
  */
2262
- const base64 = getBase64Encoder();
2263
2889
  /**
2264
- * Create the creator's WSOL account, if it is missing, and close it again at the end.
2890
+ * Create the creator's quote account, if it is missing, and close it again when it is WSOL.
2265
2891
  *
2266
2892
  * Raydium's own instructions create the account when they need to, but an idempotent create here
2267
- * costs nothing and makes the flow the same whichever instruction runs. The close is what turns the
2268
- * WSOL into SOL.
2893
+ * costs nothing and makes the flow the same whichever instruction runs. The close is what turns
2894
+ * WSOL into SOL, so it only belongs on a WSOL claim: closing a USDC account would throw the payout
2895
+ * away.
2269
2896
  */
2270
- async function wrapAround(creator, middle) {
2271
- const account = await ata(creator.address, WSOL_MINT);
2897
+ async function wrapAround(creator, quote, middle) {
2898
+ const account = await ata(creator.address, quote.mint, quote.tokenProgram);
2899
+ const create = getCreateAssociatedTokenIdempotentInstruction({
2900
+ payer: creator,
2901
+ ata: account,
2902
+ owner: creator.address,
2903
+ mint: quote.mint,
2904
+ tokenProgram: quote.tokenProgram
2905
+ });
2906
+ if (quote.mint !== "So11111111111111111111111111111111111111112") return [create, ...middle];
2272
2907
  return [
2273
- getCreateAssociatedTokenIdempotentInstruction({
2274
- payer: creator,
2275
- ata: account,
2276
- owner: creator.address,
2277
- mint: WSOL_MINT,
2278
- tokenProgram: TOKEN_PROGRAM_ADDRESS
2279
- }),
2908
+ create,
2280
2909
  ...middle,
2281
2910
  getCloseAccountInstruction({
2282
2911
  account,
@@ -2293,22 +2922,23 @@ function withSigner(accounts, signer) {
2293
2922
  } : meta);
2294
2923
  }
2295
2924
  /**
2296
- * The instructions of a curve fee claim: create the WSOL account, claim, close it.
2925
+ * The instructions of a curve fee claim, for one quote asset: create the quote account, claim, and
2926
+ * close it again when the quote is WSOL.
2297
2927
  *
2298
- * This sweeps **every** coin this wallet launched, because LaunchLab keeps one vault per wallet per
2299
- * quote asset. There is no per-coin version.
2928
+ * This sweeps **every** coin this wallet launched against that quote, because LaunchLab keeps one
2929
+ * vault per wallet per quote asset. There is no per-coin version, and a creator with machines in
2930
+ * two quote assets claims once per asset.
2300
2931
  */
2301
- async function getClaimCreatorFeeInstructions(creator, ids) {
2302
- const quoteMint = WSOL_MINT;
2303
- return await wrapAround(creator, [{
2932
+ async function getClaimCreatorFeeInstructions(creator, ids, quote) {
2933
+ return await wrapAround(creator, quote, [{
2304
2934
  programAddress: ids.launchlab,
2305
2935
  accounts: withSigner(order(LAUNCHLAB_CLAIM_CREATOR_FEE, {
2306
2936
  creator: creator.address,
2307
2937
  fee_vault_authority: await creatorFeeVaultAuthority(ids.launchlab),
2308
- creator_fee_vault: await creatorFeeVaultAddress(ids.launchlab, creator.address, quoteMint),
2309
- recipient_token_account: await ata(creator.address, quoteMint),
2310
- quote_mint: quoteMint,
2311
- token_program: TOKEN_PROGRAM_ADDRESS,
2938
+ creator_fee_vault: await creatorFeeVaultAddress(ids.launchlab, creator.address, quote.mint),
2939
+ recipient_token_account: await ata(creator.address, quote.mint, quote.tokenProgram),
2940
+ quote_mint: quote.mint,
2941
+ token_program: quote.tokenProgram,
2312
2942
  system_program: SYSTEM_PROGRAM_ADDRESS,
2313
2943
  associated_token_program: ASSOCIATED_TOKEN_PROGRAM_ADDRESS
2314
2944
  }), creator),
@@ -2316,13 +2946,15 @@ async function getClaimCreatorFeeInstructions(creator, ids) {
2316
2946
  }]);
2317
2947
  }
2318
2948
  /**
2319
- * Claim the curve creator fee and receive it as SOL.
2949
+ * Claim the curve creator fee for one quote asset.
2320
2950
  *
2321
- * One transaction, three instructions. It sweeps every coin this wallet launched on LaunchLab.
2951
+ * A WSOL claim arrives as SOL, because the builder closes the WSOL account. Any other quote arrives
2952
+ * as that token, in the creator's own account.
2322
2953
  */
2323
2954
  async function claimCreatorFee(client, input) {
2324
2955
  const ids = raydiumIds(client.cluster);
2325
- const instructions = await getClaimCreatorFeeInstructions(input.creator, ids);
2956
+ const quote = await quoteOf(client, input.quoteMint ?? "So11111111111111111111111111111111111111112");
2957
+ const instructions = await getClaimCreatorFeeInstructions(input.creator, ids, quote);
2326
2958
  return await buildMessage(client, input.creator, instructions, {
2327
2959
  addressLookupTables: input.addressLookupTables,
2328
2960
  computeUnitLimit: input.computeUnitLimit ?? 55e3,
@@ -2330,22 +2962,39 @@ async function claimCreatorFee(client, input) {
2330
2962
  });
2331
2963
  }
2332
2964
  /**
2333
- * The instructions of a graduated coin's fee collection: create the WSOL account, collect, close.
2965
+ * The token program a quote mint lives under. WSOL is always classic SPL Token, so that one needs
2966
+ * no read at all.
2967
+ */
2968
+ async function quoteOf(client, mint) {
2969
+ if (mint === "So11111111111111111111111111111111111111112") return {
2970
+ mint,
2971
+ tokenProgram: TOKEN_PROGRAM_ADDRESS
2972
+ };
2973
+ return {
2974
+ mint,
2975
+ tokenProgram: (await fetchQuoteDisplay(client, mint)).tokenProgram
2976
+ };
2977
+ }
2978
+ /**
2979
+ * The instructions of a graduated coin's fee collection: create the quote account, collect, and
2980
+ * close it again when the quote is WSOL.
2334
2981
  *
2335
2982
  * This is per coin. `collect_creator_fee` pays out both sides of the pair, so anything owed in the
2336
2983
  * coin itself lands in the creator's coin account and stays there.
2984
+ *
2985
+ * The coin's quote asset comes from its Gabox pool, so a caller never has to name it.
2337
2986
  */
2338
2987
  async function getCollectCreatorFeeInstructions(client, input, ids = raydiumIds(client.cluster)) {
2339
2988
  const { mint, creator } = input;
2340
- const quoteMint = WSOL_MINT;
2341
- const settings = await fetchCurveSettings(client, ids);
2989
+ const quote = input.quote ?? await fetchQuoteBinding(client, mint);
2990
+ const settings = await fetchCurveSettings(client, quote.config, ids);
2342
2991
  const { address: poolState, pool } = await findCpmmPool(client, {
2343
2992
  mint,
2344
- quoteMint,
2993
+ quoteMint: quote.mint,
2345
2994
  creator: creator.address,
2346
2995
  cpswapConfig: settings.cpswapConfig
2347
2996
  });
2348
- return await wrapAround(creator, [{
2997
+ const collect = {
2349
2998
  programAddress: ids.cpmm,
2350
2999
  accounts: withSigner(order(CPMM_COLLECT_CREATOR_FEE, {
2351
3000
  creator: creator.address,
@@ -2365,13 +3014,23 @@ async function getCollectCreatorFeeInstructions(client, input, ids = raydiumIds(
2365
3014
  creator_fee_share: await cpmmCreatorFeeShare(ids.cpmm, creator.address, pool.ammConfig)
2366
3015
  }), creator),
2367
3016
  data: new Uint8Array(CPMM_COLLECT_CREATOR_FEE.discriminator)
2368
- }]);
3017
+ };
3018
+ return await wrapAround(creator, {
3019
+ mint: quote.mint,
3020
+ tokenProgram: quote.tokenProgram
3021
+ }, [collect]);
2369
3022
  }
2370
- /** Collect one graduated coin's CPMM creator fee and receive the WSOL side as SOL. */
3023
+ /**
3024
+ * Collect one graduated coin's CPMM creator fee.
3025
+ *
3026
+ * A WSOL-quoted coin pays the quote side out as SOL, because the builder closes the WSOL account.
3027
+ * Any other quote arrives as that token.
3028
+ */
2371
3029
  async function collectCreatorFee(client, input) {
2372
3030
  const instructions = await getCollectCreatorFeeInstructions(client, {
2373
3031
  mint: input.mint,
2374
- creator: input.creator
3032
+ creator: input.creator,
3033
+ ...input.quote ? { quote: input.quote } : {}
2375
3034
  });
2376
3035
  return await buildMessage(client, input.creator, instructions, {
2377
3036
  addressLookupTables: input.addressLookupTables,
@@ -2380,27 +3039,28 @@ async function collectCreatorFee(client, input) {
2380
3039
  });
2381
3040
  }
2382
3041
  /**
2383
- * Read both places a creator's fees can sit.
3042
+ * Read both places a creator's fees can sit, for one quote asset.
3043
+ *
3044
+ * `quoteMint` defaults to wrapped SOL. LaunchLab keeps one vault per wallet per quote, so a creator
3045
+ * with machines in two quote assets reads this twice.
2384
3046
  *
2385
3047
  * `mint` is optional. Without it only the curve vault is read, which is the one number that covers
2386
- * every coin at once. With it the coin's CPMM pool is read too, and a coin that has not graduated
2387
- * reports zeros rather than failing.
3048
+ * every coin of that quote at once. With it the coin's CPMM pool is read too, and a coin that has
3049
+ * not graduated reports zeros rather than failing.
2388
3050
  */
2389
3051
  async function fetchCreatorFees(client, input) {
2390
3052
  const ids = raydiumIds(client.cluster);
2391
- const quoteMint = WSOL_MINT;
3053
+ const quoteMint = input.quoteMint ?? "So11111111111111111111111111111111111111112";
2392
3054
  const vault = await creatorFeeVaultAddress(ids.launchlab, input.creator, quoteMint);
2393
- const { value } = await client.rpc.getAccountInfo(vault, {
2394
- encoding: "base64",
2395
- commitment: "confirmed"
2396
- }).send();
2397
- const curveLamports = value ? tokenAccountAmount(new Uint8Array(base64.encode(value.data[0]))) : 0n;
3055
+ const [vaultAccount] = await readAccounts(client.rpc, [vault]);
3056
+ const curveQuote = vaultAccount ? tokenAccountAmount(vaultAccount.data) : 0n;
2398
3057
  if (!input.mint) return {
2399
- curveLamports,
2400
- cpmmLamports: 0n,
3058
+ quoteMint,
3059
+ curveQuote,
3060
+ cpmmQuote: 0n,
2401
3061
  cpmmTokens: 0n
2402
3062
  };
2403
- const settings = await fetchCurveSettings(client, ids);
3063
+ const settings = await fetchCurveSettings(client, await launchlabGlobalConfig(ids.launchlab, quoteMint), ids);
2404
3064
  try {
2405
3065
  const { pool } = await findCpmmPool(client, {
2406
3066
  mint: input.mint,
@@ -2411,14 +3071,16 @@ async function fetchCreatorFees(client, input) {
2411
3071
  const [token0] = sortedMints(input.mint, quoteMint);
2412
3072
  const baseIsToken0 = token0 === input.mint;
2413
3073
  return {
2414
- curveLamports,
2415
- cpmmLamports: baseIsToken0 ? pool.creatorFeesToken1 : pool.creatorFeesToken0,
3074
+ quoteMint,
3075
+ curveQuote,
3076
+ cpmmQuote: baseIsToken0 ? pool.creatorFeesToken1 : pool.creatorFeesToken0,
2416
3077
  cpmmTokens: baseIsToken0 ? pool.creatorFeesToken0 : pool.creatorFeesToken1
2417
3078
  };
2418
3079
  } catch {
2419
3080
  return {
2420
- curveLamports,
2421
- cpmmLamports: 0n,
3081
+ quoteMint,
3082
+ curveQuote,
3083
+ cpmmQuote: 0n,
2422
3084
  cpmmTokens: 0n
2423
3085
  };
2424
3086
  }
@@ -2438,22 +3100,25 @@ async function fetchCreatorFees(client, input) {
2438
3100
  */
2439
3101
  const u64 = getU64Encoder();
2440
3102
  /**
2441
- * `buy_exact_in` on the curve: spend exactly `quoteIn` WSOL and take whatever coins it buys.
3103
+ * `buy_exact_in` on the curve: spend exactly `quoteIn` of the quote token and take whatever coins
3104
+ * it buys.
2442
3105
  *
2443
3106
  * LaunchLab caps the trade at what the curve has left to sell, so a spend larger than the rest of
2444
- * the raise still succeeds and graduates the coin. Wrap the SOL into the user's WSOL ATA first and
2445
- * close it afterwards, the same way the Gabox builders do.
3107
+ * the raise still succeeds and graduates the coin. Fund the user's quote ATA first. On a WSOL pool
3108
+ * that means wrapping the SOL and closing the account afterwards, the same way the Gabox builders
3109
+ * do.
2446
3110
  */
2447
3111
  async function getCurveBuyExactInInstruction(client, input, ids = raydiumIds(client.cluster)) {
2448
3112
  if (input.quoteIn <= 0n) throw new Error("quoteIn must be positive");
2449
3113
  if (input.minTokensOut < 0n) throw new Error("minTokensOut must not be negative");
2450
- const quoteMint = WSOL_MINT;
3114
+ const quoteMint = input.quoteMint ?? "So11111111111111111111111111111111111111112";
3115
+ const quoteTokenProgram = input.quoteTokenProgram ?? "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
2451
3116
  const poolState = await launchlabPoolAddress(ids.launchlab, input.mint, quoteMint);
2452
3117
  const accounts = launchlabBuyExactInAccounts({
2453
3118
  launchlab: ids.launchlab,
2454
3119
  launchlabAuthority: ids.launchlabAuthority,
2455
3120
  launchlabEventAuthority: ids.launchlabEventAuthority,
2456
- globalConfig: ids.solGlobalConfig,
3121
+ globalConfig: await launchlabGlobalConfig(ids.launchlab, quoteMint),
2457
3122
  platformConfig: ids.gaboxPlatform,
2458
3123
  poolState,
2459
3124
  mint: input.mint,
@@ -2462,7 +3127,8 @@ async function getCurveBuyExactInInstruction(client, input, ids = raydiumIds(cli
2462
3127
  quoteVault: await launchlabVaultAddress(ids.launchlab, poolState, quoteMint),
2463
3128
  user: input.user.address,
2464
3129
  userBaseToken: await ata(input.user.address, input.mint),
2465
- userQuoteToken: await ata(input.user.address, quoteMint),
3130
+ userQuoteToken: await ata(input.user.address, quoteMint, quoteTokenProgram),
3131
+ quoteTokenProgram,
2466
3132
  platformFeeVault: await platformFeeVaultAddress(ids.launchlab, ids.gaboxPlatform, quoteMint),
2467
3133
  creatorFeeVault: await creatorFeeVaultAddress(ids.launchlab, input.creator, quoteMint)
2468
3134
  });
@@ -2537,6 +3203,7 @@ var raydium_exports = /* @__PURE__ */ __exportAll({
2537
3203
  RENT_SYSVAR_ADDRESS: () => RENT_SYSVAR_ADDRESS,
2538
3204
  SHARE_FEE_RATE: () => SHARE_FEE_RATE,
2539
3205
  SYSTEM_PROGRAM_ADDRESS: () => SYSTEM_PROGRAM_ADDRESS,
3206
+ TOKEN_2022_PROGRAM_ADDRESS: () => TOKEN_2022_PROGRAM_ADDRESS,
2540
3207
  TOKEN_PROGRAM_ADDRESS: () => TOKEN_PROGRAM_ADDRESS,
2541
3208
  WSOL_MINT: () => WSOL_MINT,
2542
3209
  ata: () => ata,
@@ -2560,6 +3227,7 @@ var raydium_exports = /* @__PURE__ */ __exportAll({
2560
3227
  curveBuyExactOut: () => curveBuyExactOut,
2561
3228
  curveQuote: () => curveQuote,
2562
3229
  curveSellExactIn: () => curveSellExactIn,
3230
+ decodeBase64: () => decodeBase64,
2563
3231
  decodeCpmmAmmConfig: () => decodeCpmmAmmConfig,
2564
3232
  decodeCpmmPool: () => decodeCpmmPool,
2565
3233
  decodeLaunchlabGlobalConfig: () => decodeLaunchlabGlobalConfig,
@@ -2567,12 +3235,17 @@ var raydium_exports = /* @__PURE__ */ __exportAll({
2567
3235
  decodeLaunchlabPool: () => decodeLaunchlabPool,
2568
3236
  fetchCreatorFees: () => fetchCreatorFees,
2569
3237
  fetchCurveSettings: () => fetchCurveSettings,
3238
+ fetchQuoteAsset: () => fetchQuoteAsset,
3239
+ fetchQuoteBinding: () => fetchQuoteBinding,
3240
+ fetchQuoteConfig: () => fetchQuoteConfig,
3241
+ fetchQuoteDisplay: () => fetchQuoteDisplay,
2570
3242
  findCpmmPool: () => findCpmmPool,
2571
3243
  getClaimCreatorFeeInstructions: () => getClaimCreatorFeeInstructions,
2572
3244
  getCollectCreatorFeeInstructions: () => getCollectCreatorFeeInstructions,
2573
3245
  getCurveBuyExactInInstruction: () => getCurveBuyExactInInstruction,
2574
3246
  getLaunchInstruction: () => getLaunchInstruction,
2575
3247
  initialCurve: () => initialCurve,
3248
+ isQuoteSupported: () => isQuoteSupported,
2576
3249
  launchlabAuthority: () => launchlabAuthority,
2577
3250
  launchlabBuyAccounts: () => launchlabBuyAccounts,
2578
3251
  launchlabBuyExactInAccounts: () => launchlabBuyExactInAccounts,
@@ -2583,22 +3256,27 @@ var raydium_exports = /* @__PURE__ */ __exportAll({
2583
3256
  launchlabSellAccounts: () => launchlabSellAccounts,
2584
3257
  launchlabVaultAddress: () => launchlabVaultAddress,
2585
3258
  metadataAddress: () => metadataAddress,
3259
+ metaplexSymbol: () => metaplexSymbol,
3260
+ mintDecimals: () => mintDecimals,
2586
3261
  newCurveBuyCost: () => newCurveBuyCost,
2587
3262
  newCurveReserves: () => newCurveReserves,
2588
3263
  order: () => order,
2589
3264
  platformFeeVaultAddress: () => platformFeeVaultAddress,
2590
3265
  preFeeAmount: () => preFeeAmount,
3266
+ quoteAccountFor: () => quoteAccountFor,
2591
3267
  raydiumIds: () => raydiumIds,
3268
+ readAccounts: () => readAccounts,
2592
3269
  remainingBase: () => remainingBase,
2593
3270
  resolveVenue: () => resolveVenue,
2594
3271
  sellQuote: () => sellQuote,
2595
3272
  sortedMints: () => sortedMints,
3273
+ token2022Symbol: () => token2022Symbol,
2596
3274
  tokenAccountAmount: () => tokenAccountAmount,
2597
3275
  tokenAccountOwnerAndMint: () => tokenAccountOwnerAndMint,
2598
3276
  totalCurveFeeRate: () => totalCurveFeeRate,
2599
3277
  wsolAccountFor: () => wsolAccountFor
2600
3278
  });
2601
3279
  //#endregion
2602
- export { LAUNCHLAB_PLATFORM_CONFIG_HEAD_SIZE as $, LAUNCH_SUPPLY as $t, remainingBase as A, METADATA_LIMITS as At, COMPUTE_BUDGET_PROGRAM_ADDRESS as B, CPMM_AMM_CONFIG_DISCRIMINATOR as Bt, cpmmSwapBaseInput as C, CPMM_SEEDS as Ct, curveSellExactIn as D, LAUNCHLAB_STATUS_MIGRATE as Dt, curveBuyExactOut as E, LAUNCHLAB_STATUS_FUND as Et, launchlabBuyExactInAccounts as F, SHARE_FEE_RATE as Ft, computeBudgetInstructions as G, LAUNCHLAB_BUY_EXACT_IN as Gt, DEFAULT_COMPUTE_UNIT_LIMIT as H, CPMM_POOL_STATE_DISCRIMINATOR as Ht, launchlabSellAccounts as I, compareAddresses as It, CPMM_AMM_CONFIG_HEAD_SIZE as J, LAUNCHLAB_GLOBAL_CONFIG_DISCRIMINATOR as Jt, getSetComputeUnitLimitInstruction as K, LAUNCHLAB_BUY_EXACT_OUT as Kt, order as L, raydiumIds as Lt, cpmmBuyAccounts as M, MIGRATE_TO_CPMM as Mt, cpmmSellAccounts as N, RATE_DENOMINATOR as Nt, initialCurve as O, LAUNCHLAB_STATUS_TRADE as Ot, launchlabBuyAccounts as P, RAYDIUM_IDS as Pt, LAUNCHLAB_GLOBAL_CONFIG_HEAD_SIZE as Q, LAUNCHLAB_SELL_EXACT_IN as Qt, BUY_PACK_COMPUTE_UNITS as R, sortedMints as Rt, ceilDivRate as S, CONSTANT_CURVE_TAG as St, curveBuyExactIn as T, LAUNCHLAB_SEEDS as Tt, MAX_COMPUTE_UNIT_LIMIT as U, CPMM_SWAP_BASE_INPUT as Ut, CREATE_MACHINE_COMPUTE_UNITS as V, CPMM_COLLECT_CREATOR_FEE as Vt, REDEEM_COMPUTE_UNITS as W, CPMM_SWAP_BASE_OUTPUT as Wt, CPMM_POOL_STATE_HEAD_SIZE as X, LAUNCHLAB_PLATFORM_CONFIG_DISCRIMINATOR as Xt, CPMM_POOL_OFFSETS as Y, LAUNCHLAB_INITIALIZE as Yt, CPMM_POOL_STATE_SIZE as Z, LAUNCHLAB_POOL_STATE_DISCRIMINATOR as Zt, newCurveReserves as _, launchlabPlatformConfig as _t, fetchCreatorFees as a, RENT_SYSVAR_ADDRESS as an, decodeLaunchlabPool as at, wsolAccountFor as b, metadataAddress as bt, getLaunchInstruction as c, WSOL_MINT as cn, ata as ct, cpmmPoolMatches as d, cpmmPoolAddress as dt, LAUNCH_TOTAL_BASE_SELL as en, LAUNCHLAB_POOL_STATE_HEAD_SIZE as et, creatorFeeOnInput as f, creatorFeeVaultAddress as ft, newCurveBuyCost as g, launchlabGlobalConfig as gt, findCpmmPool as h, launchlabEventAuthority as ht, collectCreatorFee as i, RAYDIUM_MAINNET_IDS as in, decodeLaunchlabPlatformConfig as it, totalCurveFeeRate as j, METADATA_SEED as jt, preFeeAmount as k, LAUNCH_DECIMALS as kt, buildMessage as l, cpmmAuthority as lt, fetchCurveSettings as m, launchlabAuthority as mt, getCurveBuyExactInInstruction as n, PLATFORM_ADMIN as nn, decodeCpmmPool as nt, getClaimCreatorFeeInstructions as o, SYSTEM_PROGRAM_ADDRESS as on, tokenAccountAmount as ot, curveQuote as p, creatorFeeVaultAuthority as pt, getSetComputeUnitPriceInstruction as q, LAUNCHLAB_CLAIM_CREATOR_FEE as qt, claimCreatorFee as r, RAYDIUM_DEVNET_IDS as rn, decodeLaunchlabGlobalConfig as rt, getCollectCreatorFeeInstructions as s, TOKEN_PROGRAM_ADDRESS as sn, tokenAccountOwnerAndMint as st, raydium_exports as t, METAPLEX_PROGRAM_ADDRESS as tn, decodeCpmmAmmConfig as tt, withRemainingAccounts as u, cpmmCreatorFeeShare as ut, resolveVenue as v, launchlabPoolAddress as vt, cpmmSwapBaseOutput as w, CREATOR_FEE_ON_QUOTE as wt, ceilDiv as x, platformFeeVaultAddress as xt, sellQuote as y, launchlabVaultAddress as yt, CLAIM_COMPUTE_UNITS as z, ASSOCIATED_TOKEN_PROGRAM_ADDRESS as zt };
3280
+ export { computeBudgetInstructions as $, LAUNCHLAB_BUY_EXACT_OUT as $n, launchlabVaultAddress as $t, initialCurve as A, CPMM_SEEDS as An, metaplexSymbol as At, fetchQuoteAsset as B, RATE_DENOMINATOR as Bn, vaultAddress as Bt, ceilDiv as C, PACK_TOKENS as Cn, LAUNCHLAB_PLATFORM_CONFIG_HEAD_SIZE as Ct, curveBuyExactIn as D, VRF_DEFAULT_QUEUE as Dn, decodeLaunchlabGlobalConfig as Dt, cpmmSwapBaseOutput as E, TIMEOUT_SLOTS as En, decodeCpmmPool as Et, cpmmSellAccounts as F, LAUNCHLAB_STATUS_TRADE as Fn, activityAddress as Ft, readAccounts as G, sortedMints as Gn, cpmmPoolAddress as Gt, fetchQuoteDisplay as H, SHARE_FEE_RATE as Hn, ata as Ht, launchlabBuyAccounts as I, LAUNCH_DECIMALS as In, associatedTokenAddress as It, COMPUTE_BUDGET_PROGRAM_ADDRESS as J, CPMM_COLLECT_CREATOR_FEE as Jn, launchlabAuthority as Jt, BUY_PACK_COMPUTE_UNITS as K, ASSOCIATED_TOKEN_PROGRAM_ADDRESS as Kn, creatorFeeVaultAddress as Kt, launchlabBuyExactInAccounts as L, METADATA_LIMITS as Ln, drawAddress as Lt, remainingBase as M, LAUNCHLAB_SEEDS as Mn, token2022Symbol as Mt, totalCurveFeeRate as N, LAUNCHLAB_STATUS_FUND as Nn, tokenAccountAmount as Nt, curveBuyExactOut as O, VRF_PROGRAM_ADDRESS as On, decodeLaunchlabPlatformConfig as Ot, cpmmBuyAccounts as P, LAUNCHLAB_STATUS_MIGRATE as Pn, tokenAccountOwnerAndMint as Pt, REDEEM_COMPUTE_UNITS as Q, LAUNCHLAB_BUY_EXACT_IN as Qn, launchlabPoolAddress as Qt, launchlabSellAccounts as R, METADATA_SEED as Rn, poolAddress as Rt, wsolAccountFor as S, MAX_ATTEMPTS as Sn, LAUNCHLAB_GLOBAL_CONFIG_HEAD_SIZE as St, cpmmSwapBaseInput as T, SLOT_HASHES_SYSVAR as Tn, decodeCpmmAmmConfig as Tt, isQuoteSupported as U, compareAddresses as Un, cpmmAuthority as Ut, fetchQuoteConfig as V, RAYDIUM_IDS as Vn, vrfIdentityAddress as Vt, decodeBase64 as W, raydiumIds as Wn, cpmmCreatorFeeShare as Wt, DEFAULT_COMPUTE_UNIT_LIMIT as X, CPMM_SWAP_BASE_INPUT as Xn, launchlabGlobalConfig as Xt, CREATE_MACHINE_COMPUTE_UNITS as Y, CPMM_POOL_STATE_DISCRIMINATOR as Yn, launchlabEventAuthority as Yt, MAX_COMPUTE_UNIT_LIMIT as Z, CPMM_SWAP_BASE_OUTPUT as Zn, launchlabPlatformConfig as Zt, newCurveBuyCost as _, validatePack as _n, tiersOf as _t, fetchCreatorFees as a, MAX_MULTIPLIER_BPS as an, LAUNCHLAB_SELL_EXACT_IN as ar, POOL_MINT_OFFSET as at, resolveVenue as b, IDENTITY_SEED as bn, CPMM_POOL_STATE_HEAD_SIZE as bt, getLaunchInstruction as c, TIERS as cn, METAPLEX_PROGRAM_ADDRESS as cr, fetchPoolByMint as ct, cpmmPoolMatches as d, maxMultiplierBps as dn, RAYDIUM_MAINNET_IDS as dr, fetchWalletActivity as dt, metadataAddress as en, LAUNCHLAB_CLAIM_CREATOR_FEE as er, getSetComputeUnitLimitInstruction as et, creatorFeeOnInput as f, quote as fn, RENT_SYSVAR_ADDRESS as fr, listDraws as ft, findCpmmPool as g, uncappedMaximum as gn, WSOL_MINT as gr, quotePool as gt, fetchQuoteBinding as h, tierAmount as hn, TOKEN_PROGRAM_ADDRESS as hr, listPools as ht, collectCreatorFee as i, GaboxMathError as in, LAUNCHLAB_POOL_STATE_DISCRIMINATOR as ir, POOL_CREATOR_OFFSET as it, preFeeAmount as j, CREATOR_FEE_ON_QUOTE as jn, mintDecimals as jt, curveSellExactIn as k, CONSTANT_CURVE_TAG as kn, decodeLaunchlabPool as kt, buildMessage as l, averageMultiplierBps as ln, PLATFORM_ADMIN as lr, fetchPoolInventory as lt, fetchCurveSettings as m, seedTokens as mn, TOKEN_2022_PROGRAM_ADDRESS as mr, listDrawsByPurchaser as mt, getCurveBuyExactInInstruction as n, BPS as nn, LAUNCHLAB_INITIALIZE as nr, DRAW_POOL_OFFSET as nt, getClaimCreatorFeeInstructions as o, MIN_SEED_MULTIPLIER_BPS as on, LAUNCH_SUPPLY as or, fetchDraw as ot, curveQuote as p, resolveReservation as pn, SYSTEM_PROGRAM_ADDRESS as pr, listDrawsByPool as pt, CLAIM_COMPUTE_UNITS as q, CPMM_AMM_CONFIG_DISCRIMINATOR as qn, creatorFeeVaultAuthority as qt, claimCreatorFee as r, DEFAULT_TIERS as rn, LAUNCHLAB_PLATFORM_CONFIG_DISCRIMINATOR as rr, DRAW_PURCHASER_OFFSET as rt, getCollectCreatorFeeInstructions as s, TICKETS as sn, LAUNCH_TOTAL_BASE_SELL as sr, fetchPoolAt as st, raydium_exports as t, platformFeeVaultAddress as tn, LAUNCHLAB_GLOBAL_CONFIG_DISCRIMINATOR as tr, getSetComputeUnitPriceInstruction as tt, withRemainingAccounts as u, choose as un, RAYDIUM_DEVNET_IDS as ur, fetchVaultBalance as ut, newCurveReserves as v, validateTiers as vn, CPMM_AMM_CONFIG_HEAD_SIZE as vt, ceilDivRate as w, RETRY_SLOTS as wn, LAUNCHLAB_POOL_STATE_HEAD_SIZE as wt, sellQuote as x, INSTRUCTIONS_SYSVAR as xn, CPMM_POOL_STATE_SIZE as xt, quoteAccountFor as y, GABOX_PROGRAM_ID as yn, CPMM_POOL_OFFSETS as yt, order as z, MIGRATE_TO_CPMM as zn, scopedVrfIdentityAddress as zt };
2603
3281
 
2604
- //# sourceMappingURL=raydium-B-l9V3O-.js.map
3282
+ //# sourceMappingURL=raydium-CU-tZzIk.js.map