@gabox-labs/sdk 0.2.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3282 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
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";
4
+ import { getCloseAccountInstruction, getCreateAssociatedTokenIdempotentInstruction } from "@solana-program/token";
5
+ //#region src/raydium/abi.ts
6
+ const RAYDIUM_MAINNET_IDS = {
7
+ launchlab: "LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj",
8
+ launchlabAuthority: "WLHv2UAZm6z4KyaaELi5pjdbJh6RESMva1Rnn8pJVVh",
9
+ launchlabEventAuthority: "2DPAtwB8L12vrMRExbLuyGnC7n2J5LNoZQSejeQGpwkr",
10
+ gaboxPlatform: "8tXUG97CEpSKTNXdGtanYyC33RWmTTuSin4VdaWwNj8q",
11
+ cpmm: "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C",
12
+ cpmmAuthority: "GpMZbSM2GgvTKHJirzeGfMFoaZ8UR2X7F4v8vHTvxFbL",
13
+ launchQuoteRaise: 85000000000n
14
+ };
15
+ const RAYDIUM_DEVNET_IDS = {
16
+ launchlab: "DRay6fNdQ5J82H7xV6uq2aV3mNrUZ1J4PgSKsWgptcm6",
17
+ launchlabAuthority: "5xqNaZXX5eUi4p5HU4oz9i5QnwRNT2y6oN7yyn4qENeq",
18
+ launchlabEventAuthority: "4uAB7seenFJKPUXqYewAdfra2u6baBgjiXU8x1SC7Ycz",
19
+ gaboxPlatform: "DdEeCPXbCAzHE2PZSoR3RZng4WA4bSztrezQznrJ4ooB",
20
+ cpmm: "DRaycpLY18LhpbydsBWbVJtxpNv9oXPgjRSfpF2bWpYb",
21
+ cpmmAuthority: "CXniRufdq5xL8t8jZAPxsPZDpuudwuJSPWnbcD5Y5Nxq",
22
+ launchQuoteRaise: 3000000000n
23
+ };
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
+ */
28
+ const WSOL_MINT = "So11111111111111111111111111111111111111112";
29
+ /** Metaplex Token Metadata, which LaunchLab's create instruction writes to. */
30
+ const METAPLEX_PROGRAM_ADDRESS = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s";
31
+ /**
32
+ * `PLATFORM_ADMIN` in the program's constants.rs. The Gabox platform config PDA derives from this
33
+ * wallet, and LaunchLab pays the platform share of every curve trade to it.
34
+ */
35
+ const PLATFORM_ADMIN = "5WcPTEQ59UqpQzjZUPbU8QRGCbj7NeQNLDa7DbsLkLKT";
36
+ /** The coin supply every Gabox launch mints: 1,000,000,000 coins at 6 decimals. */
37
+ const LAUNCH_SUPPLY = 1000000000000000n;
38
+ /** The part of the supply the LaunchLab curve sells: 793,100,000 coins. */
39
+ const LAUNCH_TOTAL_BASE_SELL = 793100000000000n;
40
+ /** Classic SPL Token. The base coin is always under it, on both venues. */
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";
47
+ const ASSOCIATED_TOKEN_PROGRAM_ADDRESS = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL";
48
+ const SYSTEM_PROGRAM_ADDRESS = "11111111111111111111111111111111";
49
+ /** The rent sysvar. LaunchLab's create instruction still takes it. */
50
+ const RENT_SYSVAR_ADDRESS = "SysvarRent111111111111111111111111111111111";
51
+ /**
52
+ * The first eight bytes of each Raydium account this SDK decodes. `adapter.ts` checks them before
53
+ * it reads a field, so an account of the wrong type fails loudly instead of decoding as rubbish.
54
+ *
55
+ * LaunchLab and CPMM give their pool account the same eight bytes, because Anchor derives a
56
+ * discriminator from the struct name alone and both are called `PoolState`. The two programs own
57
+ * different accounts, so the pair never collides in practice.
58
+ */
59
+ const LAUNCHLAB_POOL_STATE_DISCRIMINATOR = new Uint8Array([
60
+ 247,
61
+ 237,
62
+ 227,
63
+ 245,
64
+ 215,
65
+ 195,
66
+ 222,
67
+ 70
68
+ ]);
69
+ const LAUNCHLAB_GLOBAL_CONFIG_DISCRIMINATOR = new Uint8Array([
70
+ 149,
71
+ 8,
72
+ 156,
73
+ 202,
74
+ 160,
75
+ 252,
76
+ 176,
77
+ 217
78
+ ]);
79
+ const LAUNCHLAB_PLATFORM_CONFIG_DISCRIMINATOR = new Uint8Array([
80
+ 160,
81
+ 78,
82
+ 128,
83
+ 0,
84
+ 248,
85
+ 83,
86
+ 230,
87
+ 160
88
+ ]);
89
+ const CPMM_POOL_STATE_DISCRIMINATOR = new Uint8Array([
90
+ 247,
91
+ 237,
92
+ 227,
93
+ 245,
94
+ 215,
95
+ 195,
96
+ 222,
97
+ 70
98
+ ]);
99
+ const CPMM_AMM_CONFIG_DISCRIMINATOR = new Uint8Array([
100
+ 218,
101
+ 244,
102
+ 33,
103
+ 104,
104
+ 203,
105
+ 203,
106
+ 43,
107
+ 111
108
+ ]);
109
+ /**
110
+ * `initialize_v2` on `LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj` — cross-checked against interfaces.json.
111
+ */
112
+ const LAUNCHLAB_INITIALIZE = {
113
+ name: "initialize_v2",
114
+ discriminator: new Uint8Array([
115
+ 67,
116
+ 153,
117
+ 175,
118
+ 39,
119
+ 218,
120
+ 16,
121
+ 38,
122
+ 32
123
+ ]),
124
+ accounts: [
125
+ {
126
+ name: "payer",
127
+ writable: true,
128
+ signer: true
129
+ },
130
+ {
131
+ name: "creator",
132
+ writable: false,
133
+ signer: false
134
+ },
135
+ {
136
+ name: "global_config",
137
+ writable: false,
138
+ signer: false
139
+ },
140
+ {
141
+ name: "platform_config",
142
+ writable: false,
143
+ signer: false
144
+ },
145
+ {
146
+ name: "authority",
147
+ writable: false,
148
+ signer: false
149
+ },
150
+ {
151
+ name: "pool_state",
152
+ writable: true,
153
+ signer: false
154
+ },
155
+ {
156
+ name: "base_mint",
157
+ writable: true,
158
+ signer: true
159
+ },
160
+ {
161
+ name: "quote_mint",
162
+ writable: false,
163
+ signer: false
164
+ },
165
+ {
166
+ name: "base_vault",
167
+ writable: true,
168
+ signer: false
169
+ },
170
+ {
171
+ name: "quote_vault",
172
+ writable: true,
173
+ signer: false
174
+ },
175
+ {
176
+ name: "metadata_account",
177
+ writable: true,
178
+ signer: false
179
+ },
180
+ {
181
+ name: "base_token_program",
182
+ writable: false,
183
+ signer: false
184
+ },
185
+ {
186
+ name: "quote_token_program",
187
+ writable: false,
188
+ signer: false
189
+ },
190
+ {
191
+ name: "metadata_program",
192
+ writable: false,
193
+ signer: false
194
+ },
195
+ {
196
+ name: "system_program",
197
+ writable: false,
198
+ signer: false
199
+ },
200
+ {
201
+ name: "rent_program",
202
+ writable: false,
203
+ signer: false
204
+ },
205
+ {
206
+ name: "event_authority",
207
+ writable: false,
208
+ signer: false
209
+ },
210
+ {
211
+ name: "program",
212
+ writable: false,
213
+ signer: false
214
+ }
215
+ ]
216
+ };
217
+ /**
218
+ * `buy_exact_in` on `LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj` — not in interfaces.json (the program never builds it).
219
+ * The last three accounts are not in the IDL. LaunchLab reads them from the remaining
220
+ * accounts, in this order.
221
+ */
222
+ const LAUNCHLAB_BUY_EXACT_IN = {
223
+ name: "buy_exact_in",
224
+ discriminator: new Uint8Array([
225
+ 250,
226
+ 234,
227
+ 13,
228
+ 123,
229
+ 213,
230
+ 156,
231
+ 19,
232
+ 236
233
+ ]),
234
+ accounts: [
235
+ {
236
+ name: "payer",
237
+ writable: true,
238
+ signer: true
239
+ },
240
+ {
241
+ name: "authority",
242
+ writable: false,
243
+ signer: false
244
+ },
245
+ {
246
+ name: "global_config",
247
+ writable: false,
248
+ signer: false
249
+ },
250
+ {
251
+ name: "platform_config",
252
+ writable: false,
253
+ signer: false
254
+ },
255
+ {
256
+ name: "pool_state",
257
+ writable: true,
258
+ signer: false
259
+ },
260
+ {
261
+ name: "user_base_token",
262
+ writable: true,
263
+ signer: false
264
+ },
265
+ {
266
+ name: "user_quote_token",
267
+ writable: true,
268
+ signer: false
269
+ },
270
+ {
271
+ name: "base_vault",
272
+ writable: true,
273
+ signer: false
274
+ },
275
+ {
276
+ name: "quote_vault",
277
+ writable: true,
278
+ signer: false
279
+ },
280
+ {
281
+ name: "base_token_mint",
282
+ writable: false,
283
+ signer: false
284
+ },
285
+ {
286
+ name: "quote_token_mint",
287
+ writable: false,
288
+ signer: false
289
+ },
290
+ {
291
+ name: "base_token_program",
292
+ writable: false,
293
+ signer: false
294
+ },
295
+ {
296
+ name: "quote_token_program",
297
+ writable: false,
298
+ signer: false
299
+ },
300
+ {
301
+ name: "event_authority",
302
+ writable: false,
303
+ signer: false
304
+ },
305
+ {
306
+ name: "program",
307
+ writable: false,
308
+ signer: false
309
+ },
310
+ {
311
+ name: "system_program",
312
+ writable: false,
313
+ signer: false
314
+ },
315
+ {
316
+ name: "platform_fee_vault",
317
+ writable: true,
318
+ signer: false
319
+ },
320
+ {
321
+ name: "creator_fee_vault",
322
+ writable: true,
323
+ signer: false
324
+ }
325
+ ]
326
+ };
327
+ /**
328
+ * `buy_exact_out` on `LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj` — cross-checked against interfaces.json.
329
+ * The last three accounts are not in the IDL. LaunchLab reads them from the remaining
330
+ * accounts, in this order.
331
+ */
332
+ const LAUNCHLAB_BUY_EXACT_OUT = {
333
+ name: "buy_exact_out",
334
+ discriminator: new Uint8Array([
335
+ 24,
336
+ 211,
337
+ 116,
338
+ 40,
339
+ 105,
340
+ 3,
341
+ 153,
342
+ 56
343
+ ]),
344
+ accounts: [
345
+ {
346
+ name: "payer",
347
+ writable: true,
348
+ signer: true
349
+ },
350
+ {
351
+ name: "authority",
352
+ writable: false,
353
+ signer: false
354
+ },
355
+ {
356
+ name: "global_config",
357
+ writable: false,
358
+ signer: false
359
+ },
360
+ {
361
+ name: "platform_config",
362
+ writable: false,
363
+ signer: false
364
+ },
365
+ {
366
+ name: "pool_state",
367
+ writable: true,
368
+ signer: false
369
+ },
370
+ {
371
+ name: "user_base_token",
372
+ writable: true,
373
+ signer: false
374
+ },
375
+ {
376
+ name: "user_quote_token",
377
+ writable: true,
378
+ signer: false
379
+ },
380
+ {
381
+ name: "base_vault",
382
+ writable: true,
383
+ signer: false
384
+ },
385
+ {
386
+ name: "quote_vault",
387
+ writable: true,
388
+ signer: false
389
+ },
390
+ {
391
+ name: "base_token_mint",
392
+ writable: false,
393
+ signer: false
394
+ },
395
+ {
396
+ name: "quote_token_mint",
397
+ writable: false,
398
+ signer: false
399
+ },
400
+ {
401
+ name: "base_token_program",
402
+ writable: false,
403
+ signer: false
404
+ },
405
+ {
406
+ name: "quote_token_program",
407
+ writable: false,
408
+ signer: false
409
+ },
410
+ {
411
+ name: "event_authority",
412
+ writable: false,
413
+ signer: false
414
+ },
415
+ {
416
+ name: "program",
417
+ writable: false,
418
+ signer: false
419
+ },
420
+ {
421
+ name: "system_program",
422
+ writable: false,
423
+ signer: false
424
+ },
425
+ {
426
+ name: "platform_fee_vault",
427
+ writable: true,
428
+ signer: false
429
+ },
430
+ {
431
+ name: "creator_fee_vault",
432
+ writable: true,
433
+ signer: false
434
+ }
435
+ ]
436
+ };
437
+ /**
438
+ * `sell_exact_in` on `LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj` — cross-checked against interfaces.json.
439
+ * The last three accounts are not in the IDL. LaunchLab reads them from the remaining
440
+ * accounts, in this order.
441
+ */
442
+ const LAUNCHLAB_SELL_EXACT_IN = {
443
+ name: "sell_exact_in",
444
+ discriminator: new Uint8Array([
445
+ 149,
446
+ 39,
447
+ 222,
448
+ 155,
449
+ 211,
450
+ 124,
451
+ 152,
452
+ 26
453
+ ]),
454
+ accounts: [
455
+ {
456
+ name: "payer",
457
+ writable: true,
458
+ signer: true
459
+ },
460
+ {
461
+ name: "authority",
462
+ writable: false,
463
+ signer: false
464
+ },
465
+ {
466
+ name: "global_config",
467
+ writable: false,
468
+ signer: false
469
+ },
470
+ {
471
+ name: "platform_config",
472
+ writable: false,
473
+ signer: false
474
+ },
475
+ {
476
+ name: "pool_state",
477
+ writable: true,
478
+ signer: false
479
+ },
480
+ {
481
+ name: "user_base_token",
482
+ writable: true,
483
+ signer: false
484
+ },
485
+ {
486
+ name: "user_quote_token",
487
+ writable: true,
488
+ signer: false
489
+ },
490
+ {
491
+ name: "base_vault",
492
+ writable: true,
493
+ signer: false
494
+ },
495
+ {
496
+ name: "quote_vault",
497
+ writable: true,
498
+ signer: false
499
+ },
500
+ {
501
+ name: "base_token_mint",
502
+ writable: false,
503
+ signer: false
504
+ },
505
+ {
506
+ name: "quote_token_mint",
507
+ writable: false,
508
+ signer: false
509
+ },
510
+ {
511
+ name: "base_token_program",
512
+ writable: false,
513
+ signer: false
514
+ },
515
+ {
516
+ name: "quote_token_program",
517
+ writable: false,
518
+ signer: false
519
+ },
520
+ {
521
+ name: "event_authority",
522
+ writable: false,
523
+ signer: false
524
+ },
525
+ {
526
+ name: "program",
527
+ writable: false,
528
+ signer: false
529
+ },
530
+ {
531
+ name: "system_program",
532
+ writable: false,
533
+ signer: false
534
+ },
535
+ {
536
+ name: "platform_fee_vault",
537
+ writable: true,
538
+ signer: false
539
+ },
540
+ {
541
+ name: "creator_fee_vault",
542
+ writable: true,
543
+ signer: false
544
+ }
545
+ ]
546
+ };
547
+ /**
548
+ * `claim_creator_fee` on `LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj` — not in interfaces.json (the program never builds it).
549
+ */
550
+ const LAUNCHLAB_CLAIM_CREATOR_FEE = {
551
+ name: "claim_creator_fee",
552
+ discriminator: new Uint8Array([
553
+ 26,
554
+ 97,
555
+ 138,
556
+ 203,
557
+ 132,
558
+ 171,
559
+ 141,
560
+ 252
561
+ ]),
562
+ accounts: [
563
+ {
564
+ name: "creator",
565
+ writable: true,
566
+ signer: true
567
+ },
568
+ {
569
+ name: "fee_vault_authority",
570
+ writable: false,
571
+ signer: false
572
+ },
573
+ {
574
+ name: "creator_fee_vault",
575
+ writable: true,
576
+ signer: false
577
+ },
578
+ {
579
+ name: "recipient_token_account",
580
+ writable: true,
581
+ signer: false
582
+ },
583
+ {
584
+ name: "quote_mint",
585
+ writable: false,
586
+ signer: false
587
+ },
588
+ {
589
+ name: "token_program",
590
+ writable: false,
591
+ signer: false
592
+ },
593
+ {
594
+ name: "system_program",
595
+ writable: false,
596
+ signer: false
597
+ },
598
+ {
599
+ name: "associated_token_program",
600
+ writable: false,
601
+ signer: false
602
+ }
603
+ ]
604
+ };
605
+ /**
606
+ * `swap_base_output` on `CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C` — cross-checked against interfaces.json.
607
+ */
608
+ const CPMM_SWAP_BASE_OUTPUT = {
609
+ name: "swap_base_output",
610
+ discriminator: new Uint8Array([
611
+ 55,
612
+ 217,
613
+ 98,
614
+ 86,
615
+ 163,
616
+ 74,
617
+ 180,
618
+ 173
619
+ ]),
620
+ accounts: [
621
+ {
622
+ name: "payer",
623
+ writable: false,
624
+ signer: true
625
+ },
626
+ {
627
+ name: "authority",
628
+ writable: false,
629
+ signer: false
630
+ },
631
+ {
632
+ name: "amm_config",
633
+ writable: false,
634
+ signer: false
635
+ },
636
+ {
637
+ name: "pool_state",
638
+ writable: true,
639
+ signer: false
640
+ },
641
+ {
642
+ name: "input_token_account",
643
+ writable: true,
644
+ signer: false
645
+ },
646
+ {
647
+ name: "output_token_account",
648
+ writable: true,
649
+ signer: false
650
+ },
651
+ {
652
+ name: "input_vault",
653
+ writable: true,
654
+ signer: false
655
+ },
656
+ {
657
+ name: "output_vault",
658
+ writable: true,
659
+ signer: false
660
+ },
661
+ {
662
+ name: "input_token_program",
663
+ writable: false,
664
+ signer: false
665
+ },
666
+ {
667
+ name: "output_token_program",
668
+ writable: false,
669
+ signer: false
670
+ },
671
+ {
672
+ name: "input_token_mint",
673
+ writable: false,
674
+ signer: false
675
+ },
676
+ {
677
+ name: "output_token_mint",
678
+ writable: false,
679
+ signer: false
680
+ },
681
+ {
682
+ name: "observation_state",
683
+ writable: true,
684
+ signer: false
685
+ }
686
+ ]
687
+ };
688
+ /**
689
+ * `swap_base_input` on `CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C` — cross-checked against interfaces.json.
690
+ */
691
+ const CPMM_SWAP_BASE_INPUT = {
692
+ name: "swap_base_input",
693
+ discriminator: new Uint8Array([
694
+ 143,
695
+ 190,
696
+ 90,
697
+ 218,
698
+ 196,
699
+ 30,
700
+ 51,
701
+ 222
702
+ ]),
703
+ accounts: [
704
+ {
705
+ name: "payer",
706
+ writable: false,
707
+ signer: true
708
+ },
709
+ {
710
+ name: "authority",
711
+ writable: false,
712
+ signer: false
713
+ },
714
+ {
715
+ name: "amm_config",
716
+ writable: false,
717
+ signer: false
718
+ },
719
+ {
720
+ name: "pool_state",
721
+ writable: true,
722
+ signer: false
723
+ },
724
+ {
725
+ name: "input_token_account",
726
+ writable: true,
727
+ signer: false
728
+ },
729
+ {
730
+ name: "output_token_account",
731
+ writable: true,
732
+ signer: false
733
+ },
734
+ {
735
+ name: "input_vault",
736
+ writable: true,
737
+ signer: false
738
+ },
739
+ {
740
+ name: "output_vault",
741
+ writable: true,
742
+ signer: false
743
+ },
744
+ {
745
+ name: "input_token_program",
746
+ writable: false,
747
+ signer: false
748
+ },
749
+ {
750
+ name: "output_token_program",
751
+ writable: false,
752
+ signer: false
753
+ },
754
+ {
755
+ name: "input_token_mint",
756
+ writable: false,
757
+ signer: false
758
+ },
759
+ {
760
+ name: "output_token_mint",
761
+ writable: false,
762
+ signer: false
763
+ },
764
+ {
765
+ name: "observation_state",
766
+ writable: true,
767
+ signer: false
768
+ }
769
+ ]
770
+ };
771
+ /**
772
+ * `collect_creator_fee` on `CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C` — not in interfaces.json (the program never builds it).
773
+ * The last account is not in the IDL snapshot; see `CPMM_CREATOR_FEE_SHARE_ACCOUNT` in
774
+ * scripts/codegen.mjs. Derive it as PDA(["creator_fee_share", creator, amm_config]).
775
+ */
776
+ const CPMM_COLLECT_CREATOR_FEE = {
777
+ name: "collect_creator_fee",
778
+ discriminator: new Uint8Array([
779
+ 20,
780
+ 22,
781
+ 86,
782
+ 123,
783
+ 198,
784
+ 28,
785
+ 219,
786
+ 132
787
+ ]),
788
+ accounts: [
789
+ {
790
+ name: "creator",
791
+ writable: true,
792
+ signer: true
793
+ },
794
+ {
795
+ name: "authority",
796
+ writable: false,
797
+ signer: false
798
+ },
799
+ {
800
+ name: "pool_state",
801
+ writable: true,
802
+ signer: false
803
+ },
804
+ {
805
+ name: "amm_config",
806
+ writable: false,
807
+ signer: false
808
+ },
809
+ {
810
+ name: "token_0_vault",
811
+ writable: true,
812
+ signer: false
813
+ },
814
+ {
815
+ name: "token_1_vault",
816
+ writable: true,
817
+ signer: false
818
+ },
819
+ {
820
+ name: "vault_0_mint",
821
+ writable: false,
822
+ signer: false
823
+ },
824
+ {
825
+ name: "vault_1_mint",
826
+ writable: false,
827
+ signer: false
828
+ },
829
+ {
830
+ name: "creator_token_0",
831
+ writable: true,
832
+ signer: false
833
+ },
834
+ {
835
+ name: "creator_token_1",
836
+ writable: true,
837
+ signer: false
838
+ },
839
+ {
840
+ name: "token_0_program",
841
+ writable: false,
842
+ signer: false
843
+ },
844
+ {
845
+ name: "token_1_program",
846
+ writable: false,
847
+ signer: false
848
+ },
849
+ {
850
+ name: "associated_token_program",
851
+ writable: false,
852
+ signer: false
853
+ },
854
+ {
855
+ name: "system_program",
856
+ writable: false,
857
+ signer: false
858
+ },
859
+ {
860
+ name: "creator_fee_share",
861
+ writable: false,
862
+ signer: false
863
+ }
864
+ ]
865
+ };
866
+ //#endregion
867
+ //#region src/raydium/ids.ts
868
+ /**
869
+ * The Raydium addresses and launch numbers Gabox pins, per cluster.
870
+ *
871
+ * Mainnet and devnet run different Raydium deployments. Every program id and every PDA differs, and
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 })`.
874
+ *
875
+ * None of these strings is typed in this file. `scripts/codegen.mjs` reads them out of the Rust
876
+ * program's `programs/gabox/src/venue/ids.rs` and writes `abi.ts`. The Rust file is the source of
877
+ * truth, because the program checks every one of these addresses on chain.
878
+ * `test/raydium-pdas.test.ts` derives each PDA again and compares.
879
+ */
880
+ const ADDRESS_ENCODER = getAddressEncoder();
881
+ /**
882
+ * The decimals every Gabox coin is launched with. `venue/launch.rs` refuses any other value, and
883
+ * `tokens.rs` refuses a pool mint that does not have exactly this many.
884
+ */
885
+ const LAUNCH_DECIMALS = 6;
886
+ /**
887
+ * The Raydium deployment of each cluster.
888
+ *
889
+ * `localnet` uses the mainnet set. A local validator normally clones the mainnet Raydium programs,
890
+ * and the program's own default `anchor build` pins the mainnet addresses too; the `devnet` cargo
891
+ * feature is what swaps them. Pass your own ids to a builder if your validator clones devnet.
892
+ */
893
+ const RAYDIUM_IDS = {
894
+ "mainnet-beta": RAYDIUM_MAINNET_IDS,
895
+ devnet: RAYDIUM_DEVNET_IDS,
896
+ localnet: RAYDIUM_MAINNET_IDS
897
+ };
898
+ /** The Raydium deployment one cluster trades on. Throws for a cluster this SDK does not know. */
899
+ function raydiumIds(cluster) {
900
+ const ids = RAYDIUM_IDS[cluster];
901
+ if (!ids) throw new Error(`unknown cluster ${JSON.stringify(cluster)}; expected 'devnet', 'mainnet-beta' or 'localnet'`);
902
+ return ids;
903
+ }
904
+ /** LaunchLab's PDA seeds, and the two Gabox coins are always sorted against. */
905
+ const LAUNCHLAB_SEEDS = {
906
+ /** `["vault_auth_seed"]`. */
907
+ authority: "vault_auth_seed",
908
+ /** `["__event_authority"]`. */
909
+ eventAuthority: "__event_authority",
910
+ /** `["global_config", quote_mint, u8 curve_type, u16 big-endian index]`. */
911
+ globalConfig: "global_config",
912
+ /** `["platform_config", platform_admin]`. */
913
+ platformConfig: "platform_config",
914
+ /** `["pool", base_mint, quote_mint]`. */
915
+ pool: "pool",
916
+ /** `["pool_vault", pool_state, mint]`. */
917
+ vault: "pool_vault",
918
+ /** `["creator_fee_vault_auth_seed"]`, the authority over every creator fee vault. */
919
+ creatorFeeVaultAuthority: "creator_fee_vault_auth_seed"
920
+ };
921
+ /** CPMM's PDA seeds. */
922
+ const CPMM_SEEDS = {
923
+ /** `["vault_and_lp_mint_auth_seed"]`. */
924
+ authority: "vault_and_lp_mint_auth_seed",
925
+ /** `["pool", amm_config, token_0_mint, token_1_mint]`, the mints sorted by byte order. */
926
+ pool: "pool",
927
+ /** `["pool_vault", pool_state, mint]`. */
928
+ vault: "pool_vault",
929
+ /** `["observation", pool_state]`. */
930
+ observation: "observation",
931
+ /** `["creator_fee_share", creator, amm_config]`. */
932
+ creatorFeeShare: "creator_fee_share"
933
+ };
934
+ /** Metaplex's metadata PDA seed: `["metadata", metaplex, mint]`. */
935
+ const METADATA_SEED = "metadata";
936
+ /**
937
+ * Every rate LaunchLab and CPMM state is out of 1,000,000, not out of 10,000. A `trade_fee_rate`
938
+ * of 5,000 is 0.5%.
939
+ */
940
+ const RATE_DENOMINATOR = 1000000n;
941
+ /** The share-fee receiver Gabox never passes, so its rate is always zero on every trade. */
942
+ const SHARE_FEE_RATE = 0n;
943
+ /** `CurveParams::Constant`, the only curve shape Gabox launches. */
944
+ const CONSTANT_CURVE_TAG = 0;
945
+ /** `migrate_type` 1: the coin graduates into a CPMM pool, not into an AMM pool. */
946
+ const MIGRATE_TO_CPMM = 1;
947
+ /** `AmmCreatorFeeOn::QuoteToken`, so the CPMM creator fee is paid in the pool's quote asset. */
948
+ const CREATOR_FEE_ON_QUOTE = 0;
949
+ /** LaunchLab pool `status`: still selling on the curve. */
950
+ const LAUNCHLAB_STATUS_FUND = 0;
951
+ /** LaunchLab pool `status`: the raise is complete and Raydium's bot is migrating the coin. */
952
+ const LAUNCHLAB_STATUS_MIGRATE = 1;
953
+ /** LaunchLab pool `status`: the coin has graduated and trades on its CPMM pool. */
954
+ const LAUNCHLAB_STATUS_TRADE = 2;
955
+ /** Metaplex's own limits on the three strings a creator picks, in UTF-8 bytes. */
956
+ const METADATA_LIMITS = {
957
+ name: 32,
958
+ symbol: 10,
959
+ uri: 200
960
+ };
961
+ /** Raydium sorts a CPMM pair by raw address byte order. */
962
+ function sortedMints(a, b) {
963
+ return compareAddresses(a, b) <= 0 ? [a, b] : [b, a];
964
+ }
965
+ /** Compare two addresses the way the runtime does: by their 32 raw bytes, not by base58 text. */
966
+ function compareAddresses(a, b) {
967
+ const left = ADDRESS_ENCODER.encode(a);
968
+ const right = ADDRESS_ENCODER.encode(b);
969
+ for (let i = 0; i < 32; i++) {
970
+ const difference = (left[i] ?? 0) - (right[i] ?? 0);
971
+ if (difference !== 0) return difference;
972
+ }
973
+ return 0;
974
+ }
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
1277
+ //#region src/raydium/pdas.ts
1278
+ /**
1279
+ * Every Raydium address a Gabox transaction derives.
1280
+ *
1281
+ * All of them are program-derived, so each one takes the program it lives under. Mainnet and devnet
1282
+ * run different Raydium programs, so the same seeds give different addresses on the two clusters.
1283
+ * Pass `raydiumIds(client.cluster)` and read the program out of it.
1284
+ *
1285
+ * `venue/trade.rs` and `venue/launch.rs` derive the same addresses on chain and refuse any account
1286
+ * that does not match. `test/raydium-pdas.test.ts` checks every one of these against the pinned
1287
+ * table in `abi.ts`, for both clusters.
1288
+ */
1289
+ const address$1 = getAddressEncoder();
1290
+ const utf8 = getUtf8Encoder();
1291
+ const u8$1 = getU8Encoder();
1292
+ const u16be = getU16Encoder({ endian: Endian.Big });
1293
+ const derive = async (programAddress, seeds) => (await getProgramDerivedAddress({
1294
+ programAddress,
1295
+ seeds
1296
+ }))[0];
1297
+ /** `["vault_auth_seed"]`. LaunchLab signs its own vault transfers with this PDA. */
1298
+ const launchlabAuthority = async (launchlab) => await derive(launchlab, [utf8.encode(LAUNCHLAB_SEEDS.authority)]);
1299
+ /** `["__event_authority"]`. */
1300
+ const launchlabEventAuthority = async (launchlab) => await derive(launchlab, [utf8.encode(LAUNCHLAB_SEEDS.eventAuthority)]);
1301
+ /**
1302
+ * `["global_config", quote_mint, u8 curve_type, u16 big-endian index]`. The curve settings for one
1303
+ * quote asset. Gabox uses curve type 0 (constant product) and index 0.
1304
+ */
1305
+ const launchlabGlobalConfig = async (launchlab, quoteMint, curveType = 0, index = 0) => await derive(launchlab, [
1306
+ utf8.encode(LAUNCHLAB_SEEDS.globalConfig),
1307
+ address$1.encode(quoteMint),
1308
+ u8$1.encode(curveType),
1309
+ u16be.encode(index)
1310
+ ]);
1311
+ /** `["platform_config", platform_admin]`. The Gabox platform on LaunchLab. */
1312
+ const launchlabPlatformConfig = async (launchlab, platformAdmin) => await derive(launchlab, [utf8.encode(LAUNCHLAB_SEEDS.platformConfig), address$1.encode(platformAdmin)]);
1313
+ /** `["pool", base_mint, quote_mint]`. The curve pool of one coin. */
1314
+ const launchlabPoolAddress = async (launchlab, baseMint, quoteMint) => await derive(launchlab, [
1315
+ utf8.encode(LAUNCHLAB_SEEDS.pool),
1316
+ address$1.encode(baseMint),
1317
+ address$1.encode(quoteMint)
1318
+ ]);
1319
+ /** `["pool_vault", pool_state, mint]`. One side of a curve pool's reserves. */
1320
+ const launchlabVaultAddress = async (launchlab, poolState, mint) => await derive(launchlab, [
1321
+ utf8.encode(LAUNCHLAB_SEEDS.vault),
1322
+ address$1.encode(poolState),
1323
+ address$1.encode(mint)
1324
+ ]);
1325
+ /**
1326
+ * `[platform_config, quote_mint]`. LaunchLab pays the platform share of every curve trade here.
1327
+ *
1328
+ * The seeds carry no text prefix. That is Raydium's own derivation, not a mistake.
1329
+ */
1330
+ const platformFeeVaultAddress = async (launchlab, platformConfig, quoteMint) => await derive(launchlab, [address$1.encode(platformConfig), address$1.encode(quoteMint)]);
1331
+ /**
1332
+ * `[creator, quote_mint]`. LaunchLab pays the creator share of every curve trade here.
1333
+ *
1334
+ * One vault per wallet per quote asset, not one per coin. A creator with several coins collects
1335
+ * all of them with a single `claim_creator_fee`.
1336
+ */
1337
+ const creatorFeeVaultAddress = async (launchlab, creator, quoteMint) => await derive(launchlab, [address$1.encode(creator), address$1.encode(quoteMint)]);
1338
+ /** `["creator_fee_vault_auth_seed"]`. The authority `claim_creator_fee` signs the payout with. */
1339
+ const creatorFeeVaultAuthority = async (launchlab) => await derive(launchlab, [utf8.encode(LAUNCHLAB_SEEDS.creatorFeeVaultAuthority)]);
1340
+ /** `["metadata", metaplex, mint]` under Metaplex. LaunchLab's create instruction writes it. */
1341
+ const metadataAddress = async (mint) => await derive(METAPLEX_PROGRAM_ADDRESS, [
1342
+ utf8.encode(METADATA_SEED),
1343
+ address$1.encode(METAPLEX_PROGRAM_ADDRESS),
1344
+ address$1.encode(mint)
1345
+ ]);
1346
+ /** `["vault_and_lp_mint_auth_seed"]`. */
1347
+ const cpmmAuthority = async (cpmm) => await derive(cpmm, [utf8.encode(CPMM_SEEDS.authority)]);
1348
+ /**
1349
+ * `["pool", amm_config, token_0, token_1]`, with the two mints sorted by raw byte order.
1350
+ *
1351
+ * This is where Raydium migrates a coin when the address is free. It is only a first guess: when the
1352
+ * account is taken, the migration lands somewhere else entirely. And the address alone proves
1353
+ * nothing, because anyone can open a CPMM pool for any pair under any config. `findCpmmPool` tries
1354
+ * this address, checks the account data, and scans for the real pool when it does not match.
1355
+ *
1356
+ * There are no vault or observation derivations here on purpose. `venue/trade.rs` reads those three
1357
+ * addresses out of the pool account, and so does this SDK: a migrated pool names its own vaults and
1358
+ * its own oracle, wherever it sits.
1359
+ */
1360
+ const cpmmPoolAddress = async (cpmm, ammConfig, mintA, mintB) => {
1361
+ const [token0, token1] = sortedMints(mintA, mintB);
1362
+ return await derive(cpmm, [
1363
+ utf8.encode(CPMM_SEEDS.pool),
1364
+ address$1.encode(ammConfig),
1365
+ address$1.encode(token0),
1366
+ address$1.encode(token1)
1367
+ ]);
1368
+ };
1369
+ /**
1370
+ * `["creator_fee_share", creator, amm_config]`. Where CPMM tracks what one creator is owed under
1371
+ * one fee tier.
1372
+ *
1373
+ * `collect_creator_fee` takes this account after the fourteen the mainnet IDL lists. That snapshot
1374
+ * predates the account; Raydium's devnet build declares it and stops with `AccountNotEnoughKeys`
1375
+ * (3005) when it is missing. One account per creator per fee tier, not one per pool.
1376
+ */
1377
+ const cpmmCreatorFeeShare = async (cpmm, creator, ammConfig) => await derive(cpmm, [
1378
+ utf8.encode(CPMM_SEEDS.creatorFeeShare),
1379
+ address$1.encode(creator),
1380
+ address$1.encode(ammConfig)
1381
+ ]);
1382
+ /** An associated token account. Off-curve owners are allowed: pool PDAs are all off-curve. */
1383
+ const ata = async (owner, mint, tokenProgram = TOKEN_PROGRAM_ADDRESS) => await derive(ASSOCIATED_TOKEN_PROGRAM_ADDRESS, [
1384
+ address$1.encode(owner),
1385
+ address$1.encode(tokenProgram),
1386
+ address$1.encode(mint)
1387
+ ]);
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
1422
+ //#region src/raydium/adapter.ts
1423
+ /**
1424
+ * Reading Raydium's accounts.
1425
+ *
1426
+ * Every decoder here is written from the field list in the two IDL snapshots under `idl/`. Nothing
1427
+ * is guessed: each struct is laid out in Borsh order with no padding between fields, so the offsets
1428
+ * follow from the field sizes alone. Each decoder checks the account's eight-byte discriminator
1429
+ * first, so a wrong account fails loudly instead of decoding as plausible rubbish.
1430
+ *
1431
+ * `test/raydium-decode.test.ts` builds one byte array per decoder by hand and reads it back.
1432
+ *
1433
+ * Only the fields Gabox needs are decoded. The trailing padding of each account is skipped, which
1434
+ * is also why a longer account than the one recorded here still decodes: Raydium only ever appends
1435
+ * into its own padding.
1436
+ *
1437
+ * Kit's codecs, never `Buffer`. This module is vendored into a Next.js app, and `Buffer` does not
1438
+ * exist in a browser.
1439
+ */
1440
+ const DISCRIMINATOR$1 = 8;
1441
+ const u8 = getU8Decoder();
1442
+ const u16 = getU16Decoder();
1443
+ const u64$1 = getU64Decoder();
1444
+ const addressAt = getAddressDecoder();
1445
+ const bool = getBooleanDecoder();
1446
+ /** Refuse an account that is too short or carries another type's discriminator. */
1447
+ function checked(data, discriminator, what) {
1448
+ if (data.length < discriminator.length) throw new Error(`not a ${what}: ${data.length} bytes is shorter than a discriminator`);
1449
+ for (const [i, byte] of discriminator.entries()) if (data[i] !== byte) throw new Error(`not a ${what}: the discriminator does not match`);
1450
+ return data;
1451
+ }
1452
+ /** Refuse a truncated account before a decoder reads past its end. */
1453
+ function atLeast(data, size, what) {
1454
+ if (data.length < size) throw new Error(`not a ${what}: ${data.length} bytes, expected at least ${size}`);
1455
+ return data;
1456
+ }
1457
+ /**
1458
+ * The `amount` field of an SPL token account: bytes 64 to 72, little-endian.
1459
+ *
1460
+ * Both venues and every Gabox account use classic SPL Token, whose account is 165 bytes. The
1461
+ * Token-2022 layout shares those first 165 bytes, so this reader covers a Token-2022 account too.
1462
+ */
1463
+ function tokenAccountAmount(data) {
1464
+ if (data.length < 72) throw new Error("not a token account: fewer than 72 bytes");
1465
+ return u64$1.decode(data, 64);
1466
+ }
1467
+ /** The `mint` and `owner` of an SPL token account: bytes 0 to 32 and 32 to 64. */
1468
+ function tokenAccountOwnerAndMint(data) {
1469
+ if (data.length < 64) throw new Error("not a token account: fewer than 64 bytes");
1470
+ return {
1471
+ mint: addressAt.decode(data, 0),
1472
+ owner: addressAt.decode(data, 32)
1473
+ };
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
+ }
1543
+ /** Offsets follow the `PoolState` field list in `idl/raydium_launchpad.json`. */
1544
+ const LAUNCHLAB_POOL_HEAD = getStructDecoder([
1545
+ ["epoch", u64$1],
1546
+ ["authBump", u8],
1547
+ ["status", u8],
1548
+ ["baseDecimals", u8],
1549
+ ["quoteDecimals", u8],
1550
+ ["migrateType", u8],
1551
+ ["supply", u64$1],
1552
+ ["totalBaseSell", u64$1],
1553
+ ["virtualBase", u64$1],
1554
+ ["virtualQuote", u64$1],
1555
+ ["realBase", u64$1],
1556
+ ["realQuote", u64$1],
1557
+ ["totalQuoteFundRaising", u64$1],
1558
+ ["quoteProtocolFee", u64$1],
1559
+ ["platformFee", u64$1],
1560
+ ["migrateFee", u64$1],
1561
+ ["vestingTotalLockedAmount", u64$1],
1562
+ ["vestingCliffPeriod", u64$1],
1563
+ ["vestingUnlockPeriod", u64$1],
1564
+ ["vestingStartTime", u64$1],
1565
+ ["vestingAllocatedShareAmount", u64$1],
1566
+ ["globalConfig", addressAt],
1567
+ ["platformConfig", addressAt],
1568
+ ["baseMint", addressAt],
1569
+ ["quoteMint", addressAt],
1570
+ ["baseVault", addressAt],
1571
+ ["quoteVault", addressAt],
1572
+ ["creator", addressAt],
1573
+ ["tokenProgramFlag", u8],
1574
+ ["ammCreatorFeeOn", u8]
1575
+ ]);
1576
+ /** The bytes the decoder above reads, discriminator included. The account itself is longer. */
1577
+ const LAUNCHLAB_POOL_STATE_HEAD_SIZE = 367;
1578
+ function decodeLaunchlabPool(data) {
1579
+ checked(data, LAUNCHLAB_POOL_STATE_DISCRIMINATOR, "LaunchLab pool");
1580
+ atLeast(data, 367, "LaunchLab pool");
1581
+ const head = LAUNCHLAB_POOL_HEAD.decode(data, DISCRIMINATOR$1);
1582
+ return {
1583
+ status: head.status,
1584
+ baseDecimals: head.baseDecimals,
1585
+ quoteDecimals: head.quoteDecimals,
1586
+ migrateType: head.migrateType,
1587
+ supply: head.supply,
1588
+ totalBaseSell: head.totalBaseSell,
1589
+ virtualBase: head.virtualBase,
1590
+ virtualQuote: head.virtualQuote,
1591
+ realBase: head.realBase,
1592
+ realQuote: head.realQuote,
1593
+ totalQuoteFundRaising: head.totalQuoteFundRaising,
1594
+ migrateFee: head.migrateFee,
1595
+ globalConfig: head.globalConfig,
1596
+ platformConfig: head.platformConfig,
1597
+ baseMint: head.baseMint,
1598
+ quoteMint: head.quoteMint,
1599
+ baseVault: head.baseVault,
1600
+ quoteVault: head.quoteVault,
1601
+ creator: head.creator,
1602
+ ammCreatorFeeOn: head.ammCreatorFeeOn
1603
+ };
1604
+ }
1605
+ const LAUNCHLAB_GLOBAL_CONFIG_HEAD = getStructDecoder([
1606
+ ["epoch", u64$1],
1607
+ ["curveType", u8],
1608
+ ["index", u16],
1609
+ ["migrateFee", u64$1],
1610
+ ["tradeFeeRate", u64$1],
1611
+ ["maxShareFeeRate", u64$1],
1612
+ ["minBaseSupply", u64$1],
1613
+ ["maxLockRate", u64$1],
1614
+ ["minBaseSellRate", u64$1],
1615
+ ["minBaseMigrateRate", u64$1],
1616
+ ["minQuoteFundRaising", u64$1],
1617
+ ["quoteMint", addressAt]
1618
+ ]);
1619
+ const LAUNCHLAB_GLOBAL_CONFIG_HEAD_SIZE = 115;
1620
+ function decodeLaunchlabGlobalConfig(data) {
1621
+ checked(data, LAUNCHLAB_GLOBAL_CONFIG_DISCRIMINATOR, "LaunchLab global config");
1622
+ atLeast(data, 115, "LaunchLab global config");
1623
+ const head = LAUNCHLAB_GLOBAL_CONFIG_HEAD.decode(data, DISCRIMINATOR$1);
1624
+ return {
1625
+ curveType: head.curveType,
1626
+ index: head.index,
1627
+ migrateFee: head.migrateFee,
1628
+ tradeFeeRate: head.tradeFeeRate,
1629
+ minQuoteFundRaising: head.minQuoteFundRaising,
1630
+ quoteMint: head.quoteMint
1631
+ };
1632
+ }
1633
+ /**
1634
+ * The fixed-size head of `PlatformConfig`, before its `curve_params` vector. The three text fields
1635
+ * are fixed byte arrays, so this is the same length for every platform.
1636
+ */
1637
+ const LAUNCHLAB_PLATFORM_CONFIG_HEAD = getStructDecoder([
1638
+ ["epoch", u64$1],
1639
+ ["platformFeeWallet", addressAt],
1640
+ ["platformNftWallet", addressAt],
1641
+ ["platformScale", u64$1],
1642
+ ["creatorScale", u64$1],
1643
+ ["burnScale", u64$1],
1644
+ ["feeRate", u64$1]
1645
+ ]);
1646
+ const PLATFORM_CPSWAP_CONFIG_OFFSET = 688;
1647
+ const PLATFORM_CREATOR_FEE_RATE_OFFSET = 720;
1648
+ const PLATFORM_CP_CREATOR_OFFSET = 800;
1649
+ /** Everything before `curve_params`, discriminator included. */
1650
+ const LAUNCHLAB_PLATFORM_CONFIG_HEAD_SIZE = 940;
1651
+ function decodeLaunchlabPlatformConfig(data) {
1652
+ checked(data, LAUNCHLAB_PLATFORM_CONFIG_DISCRIMINATOR, "LaunchLab platform config");
1653
+ atLeast(data, 940, "LaunchLab platform config");
1654
+ const head = LAUNCHLAB_PLATFORM_CONFIG_HEAD.decode(data, DISCRIMINATOR$1);
1655
+ return {
1656
+ platformFeeWallet: head.platformFeeWallet,
1657
+ feeRate: head.feeRate,
1658
+ creatorFeeRate: u64$1.decode(data, PLATFORM_CREATOR_FEE_RATE_OFFSET),
1659
+ cpswapConfig: addressAt.decode(data, PLATFORM_CPSWAP_CONFIG_OFFSET),
1660
+ platformCpCreator: addressAt.decode(data, PLATFORM_CP_CREATOR_OFFSET)
1661
+ };
1662
+ }
1663
+ const CPMM_POOL_HEAD = getStructDecoder([
1664
+ ["ammConfig", addressAt],
1665
+ ["poolCreator", addressAt],
1666
+ ["token0Vault", addressAt],
1667
+ ["token1Vault", addressAt],
1668
+ ["lpMint", addressAt],
1669
+ ["token0Mint", addressAt],
1670
+ ["token1Mint", addressAt],
1671
+ ["token0Program", addressAt],
1672
+ ["token1Program", addressAt],
1673
+ ["observationKey", addressAt],
1674
+ ["authBump", u8],
1675
+ ["status", u8],
1676
+ ["lpMintDecimals", u8],
1677
+ ["mint0Decimals", u8],
1678
+ ["mint1Decimals", u8],
1679
+ ["lpSupply", u64$1],
1680
+ ["protocolFeesToken0", u64$1],
1681
+ ["protocolFeesToken1", u64$1],
1682
+ ["fundFeesToken0", u64$1],
1683
+ ["fundFeesToken1", u64$1],
1684
+ ["openTime", u64$1],
1685
+ ["recentEpoch", u64$1],
1686
+ ["creatorFeeOn", u8],
1687
+ ["enableCreatorFee", bool],
1688
+ ["padding1", fixDecoderSize(getBytesDecoder(), 6)],
1689
+ ["creatorFeesToken0", u64$1],
1690
+ ["creatorFeesToken1", u64$1]
1691
+ ]);
1692
+ const CPMM_POOL_STATE_HEAD_SIZE = 413;
1693
+ /**
1694
+ * The full byte length of a CPMM `PoolState`, trailing padding included. A `getProgramAccounts`
1695
+ * scan filters on it, so it has to be the whole account and not only the part decoded above.
1696
+ */
1697
+ const CPMM_POOL_STATE_SIZE = 637;
1698
+ /**
1699
+ * Byte offsets a `getProgramAccounts` scan matches on. They are the same offsets `venue/trade.rs`
1700
+ * reads the fields at, so a pool that passes the scan passes the program's own check.
1701
+ */
1702
+ const CPMM_POOL_OFFSETS = {
1703
+ ammConfig: 8,
1704
+ poolCreator: 40,
1705
+ token0Mint: 168,
1706
+ token1Mint: 200,
1707
+ enableCreatorFee: 390
1708
+ };
1709
+ function decodeCpmmPool(data) {
1710
+ checked(data, CPMM_POOL_STATE_DISCRIMINATOR, "CPMM pool");
1711
+ atLeast(data, 413, "CPMM pool");
1712
+ const head = CPMM_POOL_HEAD.decode(data, DISCRIMINATOR$1);
1713
+ return {
1714
+ ammConfig: head.ammConfig,
1715
+ poolCreator: head.poolCreator,
1716
+ token0Vault: head.token0Vault,
1717
+ token1Vault: head.token1Vault,
1718
+ token0Mint: head.token0Mint,
1719
+ token1Mint: head.token1Mint,
1720
+ token0Program: head.token0Program,
1721
+ token1Program: head.token1Program,
1722
+ observationKey: head.observationKey,
1723
+ status: head.status,
1724
+ mint0Decimals: head.mint0Decimals,
1725
+ mint1Decimals: head.mint1Decimals,
1726
+ protocolFeesToken0: head.protocolFeesToken0,
1727
+ protocolFeesToken1: head.protocolFeesToken1,
1728
+ fundFeesToken0: head.fundFeesToken0,
1729
+ fundFeesToken1: head.fundFeesToken1,
1730
+ creatorFeesToken0: head.creatorFeesToken0,
1731
+ creatorFeesToken1: head.creatorFeesToken1,
1732
+ openTime: head.openTime,
1733
+ creatorFeeOn: head.creatorFeeOn,
1734
+ enableCreatorFee: head.enableCreatorFee
1735
+ };
1736
+ }
1737
+ const CPMM_AMM_CONFIG_HEAD = getStructDecoder([
1738
+ ["bump", u8],
1739
+ ["disableCreatePool", bool],
1740
+ ["index", u16],
1741
+ ["tradeFeeRate", u64$1],
1742
+ ["protocolFeeRate", u64$1],
1743
+ ["fundFeeRate", u64$1],
1744
+ ["createPoolFee", u64$1],
1745
+ ["protocolOwner", addressAt],
1746
+ ["fundOwner", addressAt],
1747
+ ["creatorFeeRate", u64$1]
1748
+ ]);
1749
+ const CPMM_AMM_CONFIG_HEAD_SIZE = 116;
1750
+ function decodeCpmmAmmConfig(data) {
1751
+ checked(data, CPMM_AMM_CONFIG_DISCRIMINATOR, "CPMM amm config");
1752
+ atLeast(data, 116, "CPMM amm config");
1753
+ const head = CPMM_AMM_CONFIG_HEAD.decode(data, DISCRIMINATOR$1);
1754
+ return {
1755
+ index: head.index,
1756
+ disableCreatePool: head.disableCreatePool,
1757
+ tradeFeeRate: head.tradeFeeRate,
1758
+ protocolFeeRate: head.protocolFeeRate,
1759
+ fundFeeRate: head.fundFeeRate,
1760
+ creatorFeeRate: head.creatorFeeRate
1761
+ };
1762
+ }
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
1879
+ //#region src/compute.ts
1880
+ /**
1881
+ * The two `ComputeBudget` instructions the transaction builders prepend.
1882
+ *
1883
+ * # Why every gabox transaction needs one
1884
+ *
1885
+ * The default budget is 200,000 compute units for the whole transaction. `buy_pack` alone does a
1886
+ * CPI into Raydium (which does its own CPIs into the token program and may create two fee vaults),
1887
+ * a `transfer_checked`, an account init, and a CPI into MagicBlock's VRF program that allocates a
1888
+ * request account. `createMachine` adds LaunchLab's `initialize_v2` to that, which mints the coin
1889
+ * and writes its Metaplex metadata. Neither fits in the default, and nothing on chain can raise its
1890
+ * own budget.
1891
+ *
1892
+ * # The numbers are measured
1893
+ *
1894
+ * Each figure below is about 1.3x the largest `computeUnitsConsumed` the devnet end-to-end test saw
1895
+ * over three runs on 2026-09-18, across both venues. That matters because the prioritisation fee is
1896
+ * `price x requested limit`: a request three times too large costs three times too much on every
1897
+ * pack. A caller may still override any of them.
1898
+ *
1899
+ * The three runs did not agree. A curve pack cost 164,717, then 167,723, then 181,225 units for the
1900
+ * same work, so the venue's own cost moves with state this SDK does not control. The 1.3x margin is
1901
+ * there to absorb that, and the largest of the three is always the basis.
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
+ *
1906
+ * Re-measure after a change to a builder or to a Raydium program. `test/devnet.e2e.test.ts` prints
1907
+ * `CU <label>: <units> | <signature>` for every transaction it sends, which is where these came
1908
+ * from.
1909
+ */
1910
+ const COMPUTE_BUDGET_PROGRAM_ADDRESS = "ComputeBudget111111111111111111111111111111";
1911
+ /** The runtime's per-transaction ceiling. A larger request is rejected outright. */
1912
+ const MAX_COMPUTE_UNIT_LIMIT = 14e5;
1913
+ /** What an instruction gets when no `SetComputeUnitLimit` is present. */
1914
+ const DEFAULT_COMPUTE_UNIT_LIMIT = 2e5;
1915
+ /**
1916
+ * LaunchLab `initialize_v2`, the WSOL wrap, pool initialization with a seed buy, and the WSOL
1917
+ * close, in one transaction. Measured at `266,488`, `258,988` and `267,988` over three devnet runs
1918
+ * against the slot-500297753 build.
1919
+ */
1920
+ const CREATE_MACHINE_COMPUTE_UNITS = 35e4;
1921
+ /**
1922
+ * A WSOL wrap, a venue buy, an escrow transfer, a draw init, a VRF request and the WSOL close.
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
1925
+ * and creator fee vaults.
1926
+ */
1927
+ const BUY_PACK_COMPUTE_UNITS = 235e3;
1928
+ /**
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.
1931
+ */
1932
+ const REDEEM_COMPUTE_UNITS = 125e3;
1933
+ /**
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.
1937
+ */
1938
+ const CLAIM_COMPUTE_UNITS = 55e3;
1939
+ /** `ComputeBudgetInstruction`'s discriminants. Positional and append-only upstream. */
1940
+ const SET_COMPUTE_UNIT_LIMIT = 2;
1941
+ const SET_COMPUTE_UNIT_PRICE = 3;
1942
+ /** `[u8 discriminant, u32 units]` — five bytes. */
1943
+ const LIMIT_ENCODER = getStructEncoder([["discriminant", getU8Encoder()], ["units", getU32Encoder()]]);
1944
+ /** `[u8 discriminant, u64 microLamports]` — nine bytes. */
1945
+ const PRICE_ENCODER = getStructEncoder([["discriminant", getU8Encoder()], ["microLamports", getU64Encoder()]]);
1946
+ function getSetComputeUnitLimitInstruction(units) {
1947
+ if (!Number.isInteger(units) || units < 0 || units > 14e5) throw new Error(`compute unit limit must be an integer in 0..=${MAX_COMPUTE_UNIT_LIMIT}, got ${units}`);
1948
+ return {
1949
+ programAddress: COMPUTE_BUDGET_PROGRAM_ADDRESS,
1950
+ accounts: [],
1951
+ data: LIMIT_ENCODER.encode({
1952
+ discriminant: SET_COMPUTE_UNIT_LIMIT,
1953
+ units
1954
+ })
1955
+ };
1956
+ }
1957
+ /**
1958
+ * `SetComputeUnitPrice(microLamports)` — the priority fee, per compute unit.
1959
+ *
1960
+ * No default, deliberately. The fee that lands a transaction is a property of the network at the
1961
+ * moment you send it. A hardcoded price is either money burnt on an idle chain or a transaction
1962
+ * that quietly stops landing under load. Sample `getRecentPrioritizationFees`, or take it from
1963
+ * config.
1964
+ */
1965
+ function getSetComputeUnitPriceInstruction(microLamports) {
1966
+ const price = BigInt(microLamports);
1967
+ if (price < 0n) throw new Error(`compute unit price must not be negative, got ${price}`);
1968
+ return {
1969
+ programAddress: COMPUTE_BUDGET_PROGRAM_ADDRESS,
1970
+ accounts: [],
1971
+ data: PRICE_ENCODER.encode({
1972
+ discriminant: SET_COMPUTE_UNIT_PRICE,
1973
+ microLamports: price
1974
+ })
1975
+ };
1976
+ }
1977
+ /** The compute budget prefix a builder prepends: a limit, and a price only when one is asked for. */
1978
+ function computeBudgetInstructions(units, microLamports) {
1979
+ const instructions = [getSetComputeUnitLimitInstruction(units)];
1980
+ if (microLamports !== void 0) instructions.push(getSetComputeUnitPriceInstruction(microLamports));
1981
+ return instructions;
1982
+ }
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
2081
+ //#region src/raydium/accounts.ts
2082
+ /**
2083
+ * The ordered account list of every Raydium trade Gabox forwards.
2084
+ *
2085
+ * # Why these are built here
2086
+ *
2087
+ * `venue/trade.rs` accepts four trade instructions and no others: LaunchLab's `buy_exact_out` and
2088
+ * `sell_exact_in`, CPMM's `swap_base_output` and `swap_base_input`. It takes the venue's own
2089
+ * account list as `remaining_accounts` and walks it position by position. So the lists are
2090
+ * assembled here, from `abi.ts`, which codegen generated from the same Raydium IDLs the program's
2091
+ * `venue/abi.rs` came from.
2092
+ *
2093
+ * # How to read the output
2094
+ *
2095
+ * Each function returns `AccountMeta[]` in ABI order, with the role each slot's own ABI gives it.
2096
+ * That list goes into `initialize_pool`, `buy_pack` or `sell_tokens` as `remainingAccounts`,
2097
+ * unchanged. The program then requires every writable slot to be writable in the outer transaction,
2098
+ * requires the one signer slot to be the purchaser, and checks every address by name.
2099
+ *
2100
+ * # CPMM names its accounts by direction
2101
+ *
2102
+ * CPMM calls its four pairs `input_*` and `output_*`. A buy sends quote and receives the coin; a
2103
+ * sell is the other way round. So the two builders below fill the same four names with opposite
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.
2112
+ */
2113
+ /**
2114
+ * Turn `{name -> address}` into the ABI's ordered `AccountMeta[]`.
2115
+ *
2116
+ * The map is keyed by the IDL's own account names, so a missing entry names the slot it belongs to
2117
+ * rather than failing as an off-by-one further down. The role comes from the ABI table alone, never
2118
+ * from the caller, which is what keeps this list in step with `venue/abi.rs`.
2119
+ */
2120
+ function order(abi, byName) {
2121
+ return abi.accounts.map((spec) => {
2122
+ const address = byName[spec.name];
2123
+ if (address === void 0) throw new Error(`${abi.name}: no address given for the "${spec.name}" account`);
2124
+ return {
2125
+ address,
2126
+ role: spec.signer ? spec.writable ? AccountRole.WRITABLE_SIGNER : AccountRole.READONLY_SIGNER : spec.writable ? AccountRole.WRITABLE : AccountRole.READONLY
2127
+ };
2128
+ });
2129
+ }
2130
+ function launchlabTradeAccounts(abi, input) {
2131
+ return order(abi, {
2132
+ payer: input.user,
2133
+ authority: input.launchlabAuthority,
2134
+ global_config: input.globalConfig,
2135
+ platform_config: input.platformConfig,
2136
+ pool_state: input.poolState,
2137
+ user_base_token: input.userBaseToken,
2138
+ user_quote_token: input.userQuoteToken,
2139
+ base_vault: input.baseVault,
2140
+ quote_vault: input.quoteVault,
2141
+ base_token_mint: input.mint,
2142
+ quote_token_mint: input.quoteMint,
2143
+ base_token_program: TOKEN_PROGRAM_ADDRESS,
2144
+ quote_token_program: input.quoteTokenProgram,
2145
+ event_authority: input.launchlabEventAuthority,
2146
+ program: input.launchlab,
2147
+ system_program: SYSTEM_PROGRAM_ADDRESS,
2148
+ platform_fee_vault: input.platformFeeVault,
2149
+ creator_fee_vault: input.creatorFeeVault
2150
+ });
2151
+ }
2152
+ /** `buy_exact_out`: 18 accounts. The first argument is the coins wanted, the second the cost cap. */
2153
+ const launchlabBuyAccounts = (input) => launchlabTradeAccounts(LAUNCHLAB_BUY_EXACT_OUT, input);
2154
+ /** `sell_exact_in`: the same 18 accounts. `venue/trade.rs` uses one table for both sides. */
2155
+ const launchlabSellAccounts = (input) => launchlabTradeAccounts(LAUNCHLAB_SELL_EXACT_IN, input);
2156
+ /**
2157
+ * `buy_exact_in`: the same 18 accounts again. Gabox never forwards this instruction; a wallet sends
2158
+ * it on its own, to buy the curve out with a fixed spend.
2159
+ */
2160
+ const launchlabBuyExactInAccounts = (input) => launchlabTradeAccounts(LAUNCHLAB_BUY_EXACT_IN, input);
2161
+ /** A buy sends the quote and receives the coin. A sell is the other way round. */
2162
+ function cpmmTradeAccounts(abi, input, side) {
2163
+ const buying = side === "buy";
2164
+ return order(abi, {
2165
+ payer: input.user,
2166
+ authority: input.cpmmAuthority,
2167
+ amm_config: input.ammConfig,
2168
+ pool_state: input.poolState,
2169
+ input_token_account: buying ? input.userQuoteToken : input.userBaseToken,
2170
+ output_token_account: buying ? input.userBaseToken : input.userQuoteToken,
2171
+ input_vault: buying ? input.quoteVault : input.baseVault,
2172
+ output_vault: buying ? input.baseVault : input.quoteVault,
2173
+ input_token_program: buying ? input.quoteTokenProgram : input.baseTokenProgram,
2174
+ output_token_program: buying ? input.baseTokenProgram : input.quoteTokenProgram,
2175
+ input_token_mint: buying ? input.quoteMint : input.mint,
2176
+ output_token_mint: buying ? input.mint : input.quoteMint,
2177
+ observation_state: input.observationState
2178
+ });
2179
+ }
2180
+ /** `swap_base_output`: 13 accounts. Note the argument order is `(max_amount_in, amount_out)`. */
2181
+ const cpmmBuyAccounts = (input) => cpmmTradeAccounts(CPMM_SWAP_BASE_OUTPUT, input, "buy");
2182
+ /** `swap_base_input`: 13 accounts, with the four pairs swapped. */
2183
+ const cpmmSellAccounts = (input) => cpmmTradeAccounts(CPMM_SWAP_BASE_INPUT, input, "sell");
2184
+ //#endregion
2185
+ //#region src/raydium/curve.ts
2186
+ /**
2187
+ * Raydium's own price math, ported to bigint.
2188
+ *
2189
+ * Every function here is a line-by-line port of Raydium's TypeScript, which is itself the same math
2190
+ * their on-chain programs run. The point is that a buyer sees the exact number the venue will
2191
+ * charge, not an estimate: `buy_pack` passes the pack size as an exact-tokens-out amount, and the
2192
+ * program then requires the token delta to equal one pack.
2193
+ *
2194
+ * Sources, read on 2026-09-18:
2195
+ *
2196
+ * - `raydium/launchpad/curve/constantProductCurve.ts` and `curve.ts` (LaunchLab)
2197
+ * - `raydium/cpmm/curve/calculator.ts`, `constantProduct.ts` and `fee.ts` (CPMM)
2198
+ *
2199
+ * Raydium uses `BN`, which truncates toward zero on division. Every value here is a non-negative
2200
+ * bigint, where `/` truncates the same way, so a plain `/` is the faithful port of their `.div()`.
2201
+ * Where they round up they call a ceiling helper, and so does this file. Nothing is approximated
2202
+ * and no step is reordered: a division that happens before a subtraction there happens before it
2203
+ * here, because the two orders give different answers.
2204
+ *
2205
+ * Every rate is out of 1,000,000, not out of 10,000.
2206
+ */
2207
+ /** `ceilDiv(a, b, denominator)` in Raydium's code: `ceil(a * b / denominator)`. */
2208
+ function ceilDivRate(amount, rate, denominator = RATE_DENOMINATOR) {
2209
+ if (rate === 0n) return 0n;
2210
+ return ceilDiv(amount * rate, denominator);
2211
+ }
2212
+ /** `ceil(numerator / denominator)` for non-negative values. */
2213
+ function ceilDiv(numerator, denominator) {
2214
+ if (denominator === 0n) throw new Error("divide by zero");
2215
+ return (numerator + denominator - 1n) / denominator;
2216
+ }
2217
+ /**
2218
+ * The amount before a fee was taken, given the amount after it: `ceil(post * 1e6 / (1e6 - rate))`.
2219
+ *
2220
+ * Raydium calls this `calculatePreFee` on the curve and `calculatePreFeeAmount` on CPMM. Both are
2221
+ * the same expression.
2222
+ */
2223
+ function preFeeAmount(postFeeAmount, rate) {
2224
+ if (rate === 0n) return postFeeAmount;
2225
+ if (rate >= 1000000n) throw new Error("a fee rate of 100% or more has no pre-fee amount");
2226
+ return ceilDiv(postFeeAmount * RATE_DENOMINATOR, RATE_DENOMINATOR - rate);
2227
+ }
2228
+ /**
2229
+ * The whole fee a curve trade pays. Gabox never passes a share-fee receiver, so the fourth rate
2230
+ * Raydium supports is always zero and is left out here.
2231
+ */
2232
+ function totalCurveFeeRate(rates) {
2233
+ const total = rates.tradeFeeRate + rates.platformFeeRate + rates.creatorFeeRate;
2234
+ if (total > 1000000n) throw new Error("the total curve fee rate exceeds 1,000,000");
2235
+ return total;
2236
+ }
2237
+ /** Coins the curve still has to sell. A buy cannot take more than this. */
2238
+ function remainingBase(pool) {
2239
+ return pool.totalBaseSell > pool.realBase ? pool.totalBaseSell - pool.realBase : 0n;
2240
+ }
2241
+ /** The two sides of the constant product, as the curve sees them right now. */
2242
+ const quoteSide = (pool) => pool.virtualQuote + pool.realQuote;
2243
+ const baseSide = (pool) => pool.virtualBase - pool.realBase;
2244
+ /**
2245
+ * Quote needed to buy exactly `tokens` coins on the curve, fees included.
2246
+ *
2247
+ * This is `Curve.buyExactOut` with no token transfer fee, which is always the case for Gabox: both
2248
+ * mints are classic SPL Token and carry no transfer-fee extension.
2249
+ *
2250
+ * Raydium caps the amount at what the curve has left to sell and still returns a price, so this
2251
+ * does the same. A caller that needs the whole `tokens` must check `remainingBase` first;
2252
+ * `resolveVenue` does.
2253
+ */
2254
+ function curveBuyExactOut(pool, rates, tokens) {
2255
+ if (tokens <= 0n) throw new Error("tokens must be positive");
2256
+ const remaining = remainingBase(pool);
2257
+ const amount = tokens > remaining ? remaining : tokens;
2258
+ if (amount === 0n) throw new Error("the curve has no coins left to sell");
2259
+ return preFeeAmount(curveQuoteInForBaseOut(pool, amount), totalCurveFeeRate(rates));
2260
+ }
2261
+ /**
2262
+ * Coins `quote` buys on the curve right now, fees already taken off the spend.
2263
+ *
2264
+ * This is `Curve.buyExactIn`. It is what a raw `buy_exact_in` pays out, which the devnet end-to-end
2265
+ * test uses to push a curve to graduation.
2266
+ */
2267
+ function curveBuyExactIn(pool, rates, quote) {
2268
+ if (quote <= 0n) throw new Error("quote must be positive");
2269
+ const spend = quote - ceilDivRate(quote, totalCurveFeeRate(rates));
2270
+ if (spend <= 0n) throw new Error("the whole spend would go to fees");
2271
+ const tokens = curveBaseOutForQuoteIn(pool, spend);
2272
+ const remaining = remainingBase(pool);
2273
+ return tokens > remaining ? remaining : tokens;
2274
+ }
2275
+ /**
2276
+ * Quote a sale of exactly `tokens` coins returns, net of fees. This is `Curve.sellExactIn`.
2277
+ */
2278
+ function curveSellExactIn(pool, rates, tokens) {
2279
+ if (tokens <= 0n) throw new Error("tokens must be positive");
2280
+ const gross = curveQuoteOutForBaseIn(pool, tokens);
2281
+ const fee = ceilDivRate(gross, totalCurveFeeRate(rates));
2282
+ return gross > fee ? gross - fee : 0n;
2283
+ }
2284
+ /** `getAmountIn` on the buy side: `ceil(quoteSide * out / (baseSide - out))`. */
2285
+ function curveQuoteInForBaseOut(pool, tokens) {
2286
+ const output = baseSide(pool);
2287
+ if (tokens >= output) throw new Error("the curve does not hold that many coins");
2288
+ return ceilDiv(quoteSide(pool) * tokens, output - tokens);
2289
+ }
2290
+ /** `getAmountOut` on the buy side: `floor(in * baseSide / (quoteSide + in))`. */
2291
+ function curveBaseOutForQuoteIn(pool, quote) {
2292
+ return quote * baseSide(pool) / (quoteSide(pool) + quote);
2293
+ }
2294
+ /** `getAmountOut` on the sell side: `floor(in * quoteSide / (baseSide + in))`. */
2295
+ function curveQuoteOutForBaseIn(pool, tokens) {
2296
+ return tokens * quoteSide(pool) / (baseSide(pool) + tokens);
2297
+ }
2298
+ /**
2299
+ * The virtual reserves LaunchLab gives a coin that does not exist yet.
2300
+ *
2301
+ * This is `LaunchConstantProductCurve.getInitParam`. `createMachine` needs it because the seed buy
2302
+ * runs in the same transaction that creates the coin: there is no pool account to read yet, and the
2303
+ * reserves follow from the pinned launch shape alone.
2304
+ *
2305
+ * Checked against Raydium's own published numbers for the mainnet Gabox launch shape (supply
2306
+ * 1,000,000,000 coins, 793,100,000 sold on the curve, 85 SOL raised, no migrate fee):
2307
+ * `virtualBase` 1,073,025,605,596,382 and `virtualQuote` 30,000,852,951.
2308
+ */
2309
+ function initialCurve(params) {
2310
+ const { supply, totalBaseSell, quoteRaise } = params;
2311
+ const locked = params.totalLockedAmount ?? 0n;
2312
+ const migrateFee = params.migrateFee ?? 0n;
2313
+ if (supply <= totalBaseSell) throw new Error("the supply must be larger than the curve sells");
2314
+ const migrateBase = supply - totalBaseSell - locked;
2315
+ if (migrateBase <= 0n) throw new Error("nothing would be left to seed the pool at graduation");
2316
+ const raiseLessMigrateFee = quoteRaise - migrateFee;
2317
+ if (raiseLessMigrateFee <= 0n) throw new Error("the migrate fee eats the whole raise");
2318
+ const numerator = raiseLessMigrateFee * totalBaseSell * totalBaseSell / migrateBase;
2319
+ const denominator = raiseLessMigrateFee * totalBaseSell / migrateBase - quoteRaise;
2320
+ if (denominator <= 0n) throw new Error("these launch parameters have no valid starting price");
2321
+ return {
2322
+ virtualBase: numerator / denominator,
2323
+ virtualQuote: quoteRaise * quoteRaise / denominator,
2324
+ realBase: 0n,
2325
+ realQuote: 0n,
2326
+ totalBaseSell
2327
+ };
2328
+ }
2329
+ /**
2330
+ * Quote a CPMM buy of exactly `tokens` coins costs, both fees included.
2331
+ *
2332
+ * This is `CurveCalculator.swapBaseOutput`, which is what `swap_base_output` charges.
2333
+ */
2334
+ function cpmmSwapBaseOutput(sides, rates, amountOut) {
2335
+ if (amountOut <= 0n) throw new Error("amountOut must be positive");
2336
+ const actualOut = rates.creatorFeeOnInput ? amountOut : preFeeAmount(amountOut, rates.creatorFeeRate);
2337
+ if (actualOut >= sides.outputReserve) throw new Error("the pool does not hold that much of the output token");
2338
+ return preFeeAmount(ceilDiv(sides.inputReserve * actualOut, sides.outputReserve - actualOut), rates.creatorFeeOnInput ? rates.tradeFeeRate + rates.creatorFeeRate : rates.tradeFeeRate);
2339
+ }
2340
+ /**
2341
+ * What a CPMM swap of exactly `amountIn` returns, net of both fees.
2342
+ *
2343
+ * This is `CurveCalculator.swapBaseInput`, which is what `swap_base_input` pays out.
2344
+ */
2345
+ function cpmmSwapBaseInput(sides, rates, amountIn) {
2346
+ if (amountIn <= 0n) throw new Error("amountIn must be positive");
2347
+ const tradeFee = ceilDivRate(amountIn, rates.tradeFeeRate);
2348
+ const creatorFeeIn = rates.creatorFeeOnInput ? ceilDivRate(amountIn, rates.creatorFeeRate) : 0n;
2349
+ const lessFees = amountIn - tradeFee - creatorFeeIn;
2350
+ if (lessFees <= 0n) throw new Error("the whole input would go to fees");
2351
+ const swapped = lessFees * sides.outputReserve / (sides.inputReserve + lessFees);
2352
+ if (rates.creatorFeeOnInput) return swapped;
2353
+ const creatorFeeOut = ceilDivRate(swapped, rates.creatorFeeRate);
2354
+ return swapped > creatorFeeOut ? swapped - creatorFeeOut : 0n;
2355
+ }
2356
+ //#endregion
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
+ }
2368
+ /**
2369
+ * The user's quote associated token account, under the quote's own token program.
2370
+ *
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.
2374
+ */
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);
2381
+ }
2382
+ /**
2383
+ * Read the quote's global config and the Gabox platform config, and take the four numbers a quote
2384
+ * needs out of them.
2385
+ *
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.
2388
+ */
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}`);
2392
+ if (!platformAccount) throw new Error(`the Gabox platform config is missing at ${ids.gaboxPlatform}`);
2393
+ return settingsFrom(globalAccount.data, platformAccount.data);
2394
+ }
2395
+ function settingsFrom(globalData, platformData) {
2396
+ const global = decodeLaunchlabGlobalConfig(globalData);
2397
+ const platform = decodeLaunchlabPlatformConfig(platformData);
2398
+ return {
2399
+ rates: {
2400
+ tradeFeeRate: global.tradeFeeRate,
2401
+ platformFeeRate: platform.feeRate,
2402
+ creatorFeeRate: platform.creatorFeeRate
2403
+ },
2404
+ migrateFee: global.migrateFee,
2405
+ cpswapConfig: platform.cpswapConfig
2406
+ };
2407
+ }
2408
+ async function resolveVenue(client, options) {
2409
+ const ids = raydiumIds(client.cluster);
2410
+ const { mint, user } = options;
2411
+ const quote = options.quote ?? await fetchQuoteBinding(client, mint);
2412
+ const curvePool = await launchlabPoolAddress(ids.launchlab, mint, quote.mint);
2413
+ const [poolAccount, globalAccount, platformAccount] = await readAccounts(client.rpc, [
2414
+ curvePool,
2415
+ quote.config,
2416
+ ids.gaboxPlatform
2417
+ ]);
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}`);
2420
+ if (!platformAccount) throw new Error(`the Gabox platform config is missing at ${ids.gaboxPlatform}`);
2421
+ const pool = decodeLaunchlabPool(poolAccount.data);
2422
+ if (pool.baseMint !== mint || pool.quoteMint !== quote.mint) throw new Error(`the LaunchLab pool at ${curvePool} is not the pool of ${mint}`);
2423
+ const settings = settingsFrom(globalAccount.data, platformAccount.data);
2424
+ const kind = options.venue ?? (pool.status === 2 ? "cpmm" : "launchlab");
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.`);
2426
+ if (kind === "launchlab") {
2427
+ if (pool.status !== 0) throw new Error(`${mint} no longer trades on its LaunchLab curve (status ${pool.status}). Use the CPMM venue.`);
2428
+ return await launchlabVenue(ids, pool, settings.rates, {
2429
+ mint,
2430
+ quote,
2431
+ user,
2432
+ poolState: curvePool
2433
+ });
2434
+ }
2435
+ return await cpmmVenue(client, ids, pool, settings.cpswapConfig, {
2436
+ mint,
2437
+ quote,
2438
+ user
2439
+ });
2440
+ }
2441
+ async function launchlabVenue(ids, pool, rates, where) {
2442
+ const { mint, quote, user, poolState } = where;
2443
+ const userQuoteToken = await quoteAccountFor(user, quote.mint, quote.tokenProgram);
2444
+ const shared = {
2445
+ launchlab: ids.launchlab,
2446
+ launchlabAuthority: ids.launchlabAuthority,
2447
+ launchlabEventAuthority: ids.launchlabEventAuthority,
2448
+ globalConfig: quote.config,
2449
+ platformConfig: ids.gaboxPlatform,
2450
+ poolState,
2451
+ mint,
2452
+ quoteMint: quote.mint,
2453
+ baseVault: await launchlabVaultAddress(ids.launchlab, poolState, mint),
2454
+ quoteVault: await launchlabVaultAddress(ids.launchlab, poolState, quote.mint),
2455
+ user,
2456
+ userBaseToken: await ata(user, mint),
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)
2461
+ };
2462
+ const reserves = {
2463
+ virtualBase: pool.virtualBase,
2464
+ virtualQuote: pool.virtualQuote,
2465
+ realBase: pool.realBase,
2466
+ realQuote: pool.realQuote,
2467
+ totalBaseSell: pool.totalBaseSell
2468
+ };
2469
+ const remaining = remainingBase(reserves);
2470
+ return {
2471
+ kind: "launchlab",
2472
+ program: ids.launchlab,
2473
+ mint,
2474
+ quoteMint: quote.mint,
2475
+ quoteTokenProgram: quote.tokenProgram,
2476
+ userQuoteToken,
2477
+ poolState,
2478
+ status: pool.status,
2479
+ creator: pool.creator,
2480
+ ammConfig: null,
2481
+ remainingCurveBase: remaining,
2482
+ buyAccounts: launchlabBuyAccounts(shared),
2483
+ sellAccounts: launchlabSellAccounts(shared),
2484
+ quoteBuy: (tokens) => {
2485
+ if (tokens > remaining) throw new Error(`the curve has ${remaining} base units left and cannot sell ${tokens}. Wait for the coin to graduate, then buy on its CPMM pool.`);
2486
+ return curveBuyExactOut(reserves, rates, tokens);
2487
+ },
2488
+ quoteSell: (tokens) => curveSellExactIn(reserves, rates, tokens)
2489
+ };
2490
+ }
2491
+ /**
2492
+ * Every check the program makes on a CPMM pool account, in one place.
2493
+ *
2494
+ * `enable_creator_fee` is the one that identifies the migrated pool. Anyone can call CPMM's own
2495
+ * `initialize` and open a pool for the same pair with the same creator, but that pool always has
2496
+ * the flag false. Only a creator Raydium has permissioned can set it, and LaunchLab's migration is
2497
+ * one of those.
2498
+ */
2499
+ function cpmmPoolMatches(pool, expect) {
2500
+ return pool.enableCreatorFee && pool.poolCreator === expect.creator && pool.token0Mint === expect.token0 && pool.token1Mint === expect.token1;
2501
+ }
2502
+ /**
2503
+ * The CPMM pool LaunchLab migrated a coin into.
2504
+ *
2505
+ * Two steps, because the pool is not always at a derived address. Raydium migrates into
2506
+ * `["pool", cpswap_config, token_0, token_1]` when that account is free, and into a random account
2507
+ * when it is not. So: try the derived address, and scan for the pool when it does not hold one.
2508
+ *
2509
+ * The scan filters on the account length and on the three fields at their fixed offsets, so the
2510
+ * RPC does the work and returns at most a handful of accounts. Every candidate still has to pass
2511
+ * `cpmmPoolMatches`, and more than one match is an error rather than a guess.
2512
+ */
2513
+ async function findCpmmPool(client, options) {
2514
+ const ids = raydiumIds(client.cluster);
2515
+ const { mint, creator, cpswapConfig } = options;
2516
+ const quoteMint = options.quoteMint ?? "So11111111111111111111111111111111111111112";
2517
+ const [token0, token1] = sortedMints(mint, quoteMint);
2518
+ const expect = {
2519
+ creator,
2520
+ token0,
2521
+ token1
2522
+ };
2523
+ const derived = await cpmmPoolAddress(ids.cpmm, cpswapConfig, mint, quoteMint);
2524
+ const [account] = await readAccounts(client.rpc, [derived]);
2525
+ if (account && account.owner === ids.cpmm) {
2526
+ const pool = decodeCpmmPoolOrNull(account.data);
2527
+ if (pool && cpmmPoolMatches(pool, expect)) return {
2528
+ address: derived,
2529
+ pool
2530
+ };
2531
+ }
2532
+ const rows = await client.rpc.getProgramAccounts(ids.cpmm, {
2533
+ encoding: "base64",
2534
+ commitment: "confirmed",
2535
+ filters: [
2536
+ { dataSize: BigInt(637) },
2537
+ memcmp(CPMM_POOL_OFFSETS.poolCreator, creator),
2538
+ memcmp(CPMM_POOL_OFFSETS.token0Mint, token0),
2539
+ memcmp(CPMM_POOL_OFFSETS.token1Mint, token1)
2540
+ ]
2541
+ }).send();
2542
+ const matches = [];
2543
+ for (const { pubkey, account: row } of rows) {
2544
+ const pool = decodeCpmmPoolOrNull(decodeBase64(row.data[0]));
2545
+ if (pool && cpmmPoolMatches(pool, expect)) matches.push({
2546
+ address: pubkey,
2547
+ pool
2548
+ });
2549
+ }
2550
+ const [only] = matches;
2551
+ if (!only) throw new Error(`no migrated CPMM pool for ${mint}. The derived address ${derived} holds no matching pool, and no other CPMM pool has this coin, this creator and a creator fee enabled.`);
2552
+ if (matches.length > 1) throw new Error(`${matches.length} CPMM pools match ${mint}: ${matches.map((m) => m.address).join(", ")}. Pass the one you mean rather than letting this choose.`);
2553
+ return only;
2554
+ }
2555
+ /** A `getProgramAccounts` byte filter. The bytes of an address are its own base58 text. */
2556
+ function memcmp(offset, value) {
2557
+ return { memcmp: {
2558
+ offset: BigInt(offset),
2559
+ bytes: value,
2560
+ encoding: "base58"
2561
+ } };
2562
+ }
2563
+ /** Decode a candidate account, or give up quietly. A scan row that is not a pool is not an error. */
2564
+ function decodeCpmmPoolOrNull(data) {
2565
+ try {
2566
+ return decodeCpmmPool(data);
2567
+ } catch {
2568
+ return null;
2569
+ }
2570
+ }
2571
+ async function cpmmVenue(client, ids, curve, cpswapConfig, where) {
2572
+ const { mint, quote, user } = where;
2573
+ const { address: poolState, pool } = await findCpmmPool(client, {
2574
+ mint,
2575
+ quoteMint: quote.mint,
2576
+ creator: curve.creator,
2577
+ cpswapConfig
2578
+ });
2579
+ const baseIsToken0 = pool.token0Mint === mint;
2580
+ const baseVault = baseIsToken0 ? pool.token0Vault : pool.token1Vault;
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`);
2585
+ const [configAccount, baseVaultAccount, quoteVaultAccount] = await readAccounts(client.rpc, [
2586
+ pool.ammConfig,
2587
+ baseVault,
2588
+ quoteVault
2589
+ ]);
2590
+ if (!configAccount) throw new Error(`the CPMM pool at ${poolState} names a fee tier that is not on chain`);
2591
+ if (!baseVaultAccount || !quoteVaultAccount) throw new Error(`the CPMM pool at ${poolState} has no reserve accounts`);
2592
+ const config = decodeCpmmAmmConfig(configAccount.data);
2593
+ const owedBase = baseIsToken0 ? pool.protocolFeesToken0 + pool.fundFeesToken0 + pool.creatorFeesToken0 : pool.protocolFeesToken1 + pool.fundFeesToken1 + pool.creatorFeesToken1;
2594
+ const owedQuote = baseIsToken0 ? pool.protocolFeesToken1 + pool.fundFeesToken1 + pool.creatorFeesToken1 : pool.protocolFeesToken0 + pool.fundFeesToken0 + pool.creatorFeesToken0;
2595
+ const baseReserve = tokenAccountAmount(baseVaultAccount.data) - owedBase;
2596
+ const quoteReserve = tokenAccountAmount(quoteVaultAccount.data) - owedQuote;
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);
2599
+ const shared = {
2600
+ cpmmAuthority: ids.cpmmAuthority,
2601
+ ammConfig: pool.ammConfig,
2602
+ poolState,
2603
+ mint,
2604
+ quoteMint: quote.mint,
2605
+ baseVault,
2606
+ quoteVault,
2607
+ observationState: pool.observationKey,
2608
+ user,
2609
+ userBaseToken: await ata(user, mint),
2610
+ userQuoteToken,
2611
+ baseTokenProgram,
2612
+ quoteTokenProgram
2613
+ };
2614
+ const creatorFeeRate = pool.enableCreatorFee ? config.creatorFeeRate : 0n;
2615
+ const buySides = {
2616
+ inputReserve: quoteReserve,
2617
+ outputReserve: baseReserve
2618
+ };
2619
+ const sellSides = {
2620
+ inputReserve: baseReserve,
2621
+ outputReserve: quoteReserve
2622
+ };
2623
+ const buyRates = {
2624
+ tradeFeeRate: config.tradeFeeRate,
2625
+ creatorFeeRate,
2626
+ creatorFeeOnInput: creatorFeeOnInput(pool, quote.mint)
2627
+ };
2628
+ const sellRates = {
2629
+ tradeFeeRate: config.tradeFeeRate,
2630
+ creatorFeeRate,
2631
+ creatorFeeOnInput: creatorFeeOnInput(pool, mint)
2632
+ };
2633
+ return {
2634
+ kind: "cpmm",
2635
+ program: ids.cpmm,
2636
+ mint,
2637
+ quoteMint: quote.mint,
2638
+ quoteTokenProgram,
2639
+ userQuoteToken,
2640
+ poolState,
2641
+ status: curve.status,
2642
+ creator: pool.poolCreator,
2643
+ ammConfig: pool.ammConfig,
2644
+ remainingCurveBase: 0n,
2645
+ buyAccounts: cpmmBuyAccounts(shared),
2646
+ sellAccounts: cpmmSellAccounts(shared),
2647
+ quoteBuy: (tokens) => cpmmSwapBaseOutput(buySides, buyRates, tokens),
2648
+ quoteSell: (tokens) => cpmmSwapBaseInput(sellSides, sellRates, tokens)
2649
+ };
2650
+ }
2651
+ /**
2652
+ * Does this side's creator fee come off the token going in?
2653
+ *
2654
+ * `creator_fee_on` is 0 when the fee follows the input token, 1 when it is always token 0, and 2
2655
+ * when it is always token 1. LaunchLab migrates a Gabox coin with `AmmCreatorFeeOn::QuoteToken`, so
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.
2658
+ */
2659
+ function creatorFeeOnInput(pool, inputMint) {
2660
+ if (pool.creatorFeeOn === 0) return true;
2661
+ const inputIsToken0 = pool.token0Mint === inputMint;
2662
+ return pool.creatorFeeOn === 1 ? inputIsToken0 : !inputIsToken0;
2663
+ }
2664
+ /**
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.
2667
+ *
2668
+ * Use `resolveVenue` when you also need the account list, which every transaction builder does.
2669
+ * This is for a price display.
2670
+ */
2671
+ async function curveQuote(client, mint, tokens, options = {}) {
2672
+ return (await resolveVenue(client, {
2673
+ mint,
2674
+ user: options.user ?? mint,
2675
+ ...options.venue ? { venue: options.venue } : {},
2676
+ ...options.quote ? { quote: options.quote } : {}
2677
+ })).quoteBuy(tokens);
2678
+ }
2679
+ /** Quote token a sale of `tokens` returns right now, net of the venue's fees. */
2680
+ async function sellQuote(client, mint, tokens, options = {}) {
2681
+ return (await resolveVenue(client, {
2682
+ mint,
2683
+ user: options.user ?? mint,
2684
+ ...options.venue ? { venue: options.venue } : {},
2685
+ ...options.quote ? { quote: options.quote } : {}
2686
+ })).quoteSell(tokens);
2687
+ }
2688
+ /**
2689
+ * Quote token an exact-coins-out buy of `tokens` would cost on a curve that does not exist yet,
2690
+ * fees included.
2691
+ *
2692
+ * This is what the seed costs. `initialize_pool` buys it in the same transaction that creates the
2693
+ * coin, so the curve is a brand-new LaunchLab curve with the pinned Gabox launch shape at that
2694
+ * moment. Exact, unless Raydium changes a fee rate between this read and the send.
2695
+ */
2696
+ async function newCurveBuyCost(client, tokens, launch) {
2697
+ if (tokens <= 0n) return 0n;
2698
+ const ids = raydiumIds(client.cluster);
2699
+ const settings = await fetchCurveSettings(client, launch.quoteConfig, ids);
2700
+ return curveBuyExactOut(newCurveReserves(launch.raise, settings.migrateFee), settings.rates, tokens);
2701
+ }
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) {
2709
+ return initialCurve({
2710
+ supply: LAUNCH_SUPPLY,
2711
+ totalBaseSell: LAUNCH_TOTAL_BASE_SELL,
2712
+ quoteRaise: raise,
2713
+ migrateFee
2714
+ });
2715
+ }
2716
+ //#endregion
2717
+ //#region src/tx/message.ts
2718
+ /**
2719
+ * Assembling a transaction message.
2720
+ *
2721
+ * Every builder in this directory ends here: compute budget first, then the program instructions,
2722
+ * with a fee payer and a blockhash lifetime. The result is a message a wallet can sign and send —
2723
+ * nothing in this SDK signs or sends anything itself.
2724
+ */
2725
+ /**
2726
+ * Build the message. One RPC read, for the blockhash.
2727
+ *
2728
+ * The blockhash expires in about a minute, so build the message when the user is ready to sign
2729
+ * rather than when the page loads.
2730
+ */
2731
+ async function buildMessage(client, feePayer, instructions, options) {
2732
+ const { value: latestBlockhash } = await client.rpc.getLatestBlockhash({ commitment: "confirmed" }).send();
2733
+ const budget = computeBudgetInstructions(options.computeUnitLimit, options.computeUnitPrice);
2734
+ const message = pipe(createTransactionMessage({ version: 0 }), (m) => setTransactionMessageFeePayerSigner(feePayer, m), (m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m), (m) => appendTransactionMessageInstructions([...budget, ...instructions], m), (m) => compressTransactionMessageUsingAddressLookupTables(m, options.addressLookupTables ?? client.addressLookupTables));
2735
+ const size = getTransactionEncoder().encode(compileTransaction(message)).length;
2736
+ if (size > 1232) throw new Error(`Transaction is ${size} bytes; Solana allows 1232. Shorten metadata or supply additional address lookup tables.`);
2737
+ return message;
2738
+ }
2739
+ /** Append `remainingAccounts` to a generated instruction, which is how a venue's list is passed. */
2740
+ function withRemainingAccounts(instruction, remaining) {
2741
+ return {
2742
+ ...instruction,
2743
+ accounts: [...instruction.accounts ?? [], ...remaining]
2744
+ };
2745
+ }
2746
+ //#endregion
2747
+ //#region src/raydium/launch.ts
2748
+ /**
2749
+ * LaunchLab's `initialize_v2`, built by hand.
2750
+ *
2751
+ * Put it **before** `initialize_pool` in the transaction. `venue/launch.rs` reads the Instructions
2752
+ * sysvar and looks for this instruction by discriminator, then checks all 18 of its accounts and
2753
+ * every one of its arguments. The order matters for a second reason too: the mint account has to
2754
+ * exist and deserialize before Anchor validates the Gabox accounts.
2755
+ *
2756
+ * # The launch shape is pinned
2757
+ *
2758
+ * A creator picks the name, the symbol and the metadata URI. Nothing else. Every other argument is
2759
+ * fixed, and the program refuses any other value:
2760
+ *
2761
+ * | Argument | Value |
2762
+ * | --- | --- |
2763
+ * | `decimals` | 6 (the coin; the quote keeps its own) |
2764
+ * | curve | `Constant` |
2765
+ * | `supply` | 1,000,000,000 coins |
2766
+ * | `total_base_sell` | 793,100,000 coins |
2767
+ * | `total_quote_fund_raising` | the caller's `raise`, pinned to 85 SOL on mainnet and 3 SOL on devnet for a WSOL pool |
2768
+ * | `migrate_type` | 1, which means graduate into a CPMM pool |
2769
+ * | `VestingParams` | all three fields 0 |
2770
+ * | `AmmCreatorFeeOn` | `QuoteToken` |
2771
+ *
2772
+ * The instruction data must end exactly after the last argument. Trailing bytes are refused.
2773
+ *
2774
+ * # The argument layout
2775
+ *
2776
+ * Borsh, in IDL order: `MintParams { decimals, name, symbol, uri }`, then the `CurveParams` enum
2777
+ * tag and its `ConstantCurve` fields, then `VestingParams`, then the `AmmCreatorFeeOn` enum tag.
2778
+ * An Anchor enum tag is one byte.
2779
+ */
2780
+ const borshString = () => addEncoderSizePrefix(getUtf8Encoder(), getU32Encoder());
2781
+ const ARGS_ENCODER = getStructEncoder([
2782
+ ["decimals", getU8Encoder()],
2783
+ ["name", borshString()],
2784
+ ["symbol", borshString()],
2785
+ ["uri", borshString()],
2786
+ ["curveTag", getU8Encoder()],
2787
+ ["supply", getU64Encoder()],
2788
+ ["totalBaseSell", getU64Encoder()],
2789
+ ["totalQuoteFundRaising", getU64Encoder()],
2790
+ ["migrateType", getU8Encoder()],
2791
+ ["totalLockedAmount", getU64Encoder()],
2792
+ ["cliffPeriod", getU64Encoder()],
2793
+ ["unlockPeriod", getU64Encoder()],
2794
+ ["ammFeeOn", getU8Encoder()]
2795
+ ]);
2796
+ /** Metaplex refuses a longer string, so refuse it here rather than on chain. */
2797
+ function bounded(value, limit, what) {
2798
+ const bytes = new TextEncoder().encode(value).length;
2799
+ if (bytes === 0) throw new Error(`${what} must not be empty`);
2800
+ if (bytes > limit) throw new Error(`${what} is ${bytes} UTF-8 bytes; Metaplex allows at most ${limit}`);
2801
+ return value;
2802
+ }
2803
+ /**
2804
+ * Build `initialize_v2` for one cluster's LaunchLab. `ids` is `raydiumIds(client.cluster)`.
2805
+ *
2806
+ * Two slots sign: the mint keypair, which LaunchLab takes as a signer rather than deriving, and the
2807
+ * creator, who pays for everything.
2808
+ */
2809
+ async function getLaunchInstruction(input, ids) {
2810
+ const { mint, creator } = input;
2811
+ const name = bounded(input.name, METADATA_LIMITS.name, "name");
2812
+ const symbol = bounded(input.symbol, METADATA_LIMITS.symbol, "symbol");
2813
+ const uri = bounded(input.uri, METADATA_LIMITS.uri, "uri");
2814
+ const { quoteMint, quoteTokenProgram } = input;
2815
+ const poolState = await launchlabPoolAddress(ids.launchlab, mint.address, quoteMint);
2816
+ const metas = order(LAUNCHLAB_INITIALIZE, {
2817
+ payer: creator.address,
2818
+ creator: creator.address,
2819
+ global_config: input.quoteConfig,
2820
+ platform_config: ids.gaboxPlatform,
2821
+ authority: ids.launchlabAuthority,
2822
+ pool_state: poolState,
2823
+ base_mint: mint.address,
2824
+ quote_mint: quoteMint,
2825
+ base_vault: await launchlabVaultAddress(ids.launchlab, poolState, mint.address),
2826
+ quote_vault: await launchlabVaultAddress(ids.launchlab, poolState, quoteMint),
2827
+ metadata_account: await metadataAddress(mint.address),
2828
+ base_token_program: TOKEN_PROGRAM_ADDRESS,
2829
+ quote_token_program: quoteTokenProgram,
2830
+ metadata_program: METAPLEX_PROGRAM_ADDRESS,
2831
+ system_program: SYSTEM_PROGRAM_ADDRESS,
2832
+ rent_program: RENT_SYSVAR_ADDRESS,
2833
+ event_authority: ids.launchlabEventAuthority,
2834
+ program: ids.launchlab
2835
+ });
2836
+ const signerFor = /* @__PURE__ */ new Map([[mint.address, mint], [creator.address, creator]]);
2837
+ const accounts = metas.map((meta) => {
2838
+ const signs = meta.role === 2 || meta.role === 3;
2839
+ const signer = signerFor.get(meta.address);
2840
+ return signs && signer ? {
2841
+ ...meta,
2842
+ signer
2843
+ } : meta;
2844
+ });
2845
+ const data = new Uint8Array([...LAUNCHLAB_INITIALIZE.discriminator, ...ARGS_ENCODER.encode({
2846
+ decimals: 6,
2847
+ name,
2848
+ symbol,
2849
+ uri,
2850
+ curveTag: 0,
2851
+ supply: LAUNCH_SUPPLY,
2852
+ totalBaseSell: LAUNCH_TOTAL_BASE_SELL,
2853
+ totalQuoteFundRaising: input.raise,
2854
+ migrateType: 1,
2855
+ totalLockedAmount: 0n,
2856
+ cliffPeriod: 0n,
2857
+ unlockPeriod: 0n,
2858
+ ammFeeOn: 0
2859
+ })]);
2860
+ return {
2861
+ programAddress: ids.launchlab,
2862
+ accounts,
2863
+ data
2864
+ };
2865
+ }
2866
+ //#endregion
2867
+ //#region src/raydium/claim.ts
2868
+ /**
2869
+ * Collecting the creator's share of trading.
2870
+ *
2871
+ * Gabox charges no fee at all. The creator's income is the share Raydium pays out of its own
2872
+ * revenue, and it sits in two different places depending on where the coin trades:
2873
+ *
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.
2878
+ * - **After graduation.** The CPMM pool keeps the creator's fee inside the pool account, and
2879
+ * `collect_creator_fee` pays it out. That is per pool, so it takes one call per coin.
2880
+ *
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.
2885
+ *
2886
+ * **Closing unwraps everything.** If the creator already held WSOL in that account before the
2887
+ * claim, closing turns that into SOL too. Nothing is lost, but the balance moves.
2888
+ */
2889
+ /**
2890
+ * Create the creator's quote account, if it is missing, and close it again when it is WSOL.
2891
+ *
2892
+ * Raydium's own instructions create the account when they need to, but an idempotent create here
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.
2896
+ */
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];
2907
+ return [
2908
+ create,
2909
+ ...middle,
2910
+ getCloseAccountInstruction({
2911
+ account,
2912
+ destination: creator.address,
2913
+ owner: creator
2914
+ })
2915
+ ];
2916
+ }
2917
+ /** Attach a signer to the one slot the ABI marks as signing. Roles never change here. */
2918
+ function withSigner(accounts, signer) {
2919
+ return accounts.map((meta) => (meta.role === AccountRole.READONLY_SIGNER || meta.role === AccountRole.WRITABLE_SIGNER) && meta.address === signer.address ? {
2920
+ ...meta,
2921
+ signer
2922
+ } : meta);
2923
+ }
2924
+ /**
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.
2927
+ *
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.
2931
+ */
2932
+ async function getClaimCreatorFeeInstructions(creator, ids, quote) {
2933
+ return await wrapAround(creator, quote, [{
2934
+ programAddress: ids.launchlab,
2935
+ accounts: withSigner(order(LAUNCHLAB_CLAIM_CREATOR_FEE, {
2936
+ creator: creator.address,
2937
+ fee_vault_authority: await creatorFeeVaultAuthority(ids.launchlab),
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,
2942
+ system_program: SYSTEM_PROGRAM_ADDRESS,
2943
+ associated_token_program: ASSOCIATED_TOKEN_PROGRAM_ADDRESS
2944
+ }), creator),
2945
+ data: new Uint8Array(LAUNCHLAB_CLAIM_CREATOR_FEE.discriminator)
2946
+ }]);
2947
+ }
2948
+ /**
2949
+ * Claim the curve creator fee for one quote asset.
2950
+ *
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.
2953
+ */
2954
+ async function claimCreatorFee(client, input) {
2955
+ const ids = raydiumIds(client.cluster);
2956
+ const quote = await quoteOf(client, input.quoteMint ?? "So11111111111111111111111111111111111111112");
2957
+ const instructions = await getClaimCreatorFeeInstructions(input.creator, ids, quote);
2958
+ return await buildMessage(client, input.creator, instructions, {
2959
+ addressLookupTables: input.addressLookupTables,
2960
+ computeUnitLimit: input.computeUnitLimit ?? 55e3,
2961
+ ...input.computeUnitPrice === void 0 ? {} : { computeUnitPrice: input.computeUnitPrice }
2962
+ });
2963
+ }
2964
+ /**
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.
2981
+ *
2982
+ * This is per coin. `collect_creator_fee` pays out both sides of the pair, so anything owed in the
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.
2986
+ */
2987
+ async function getCollectCreatorFeeInstructions(client, input, ids = raydiumIds(client.cluster)) {
2988
+ const { mint, creator } = input;
2989
+ const quote = input.quote ?? await fetchQuoteBinding(client, mint);
2990
+ const settings = await fetchCurveSettings(client, quote.config, ids);
2991
+ const { address: poolState, pool } = await findCpmmPool(client, {
2992
+ mint,
2993
+ quoteMint: quote.mint,
2994
+ creator: creator.address,
2995
+ cpswapConfig: settings.cpswapConfig
2996
+ });
2997
+ const collect = {
2998
+ programAddress: ids.cpmm,
2999
+ accounts: withSigner(order(CPMM_COLLECT_CREATOR_FEE, {
3000
+ creator: creator.address,
3001
+ authority: ids.cpmmAuthority,
3002
+ pool_state: poolState,
3003
+ amm_config: pool.ammConfig,
3004
+ token_0_vault: pool.token0Vault,
3005
+ token_1_vault: pool.token1Vault,
3006
+ vault_0_mint: pool.token0Mint,
3007
+ vault_1_mint: pool.token1Mint,
3008
+ creator_token_0: await ata(creator.address, pool.token0Mint, pool.token0Program),
3009
+ creator_token_1: await ata(creator.address, pool.token1Mint, pool.token1Program),
3010
+ token_0_program: pool.token0Program,
3011
+ token_1_program: pool.token1Program,
3012
+ associated_token_program: ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
3013
+ system_program: SYSTEM_PROGRAM_ADDRESS,
3014
+ creator_fee_share: await cpmmCreatorFeeShare(ids.cpmm, creator.address, pool.ammConfig)
3015
+ }), creator),
3016
+ data: new Uint8Array(CPMM_COLLECT_CREATOR_FEE.discriminator)
3017
+ };
3018
+ return await wrapAround(creator, {
3019
+ mint: quote.mint,
3020
+ tokenProgram: quote.tokenProgram
3021
+ }, [collect]);
3022
+ }
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
+ */
3029
+ async function collectCreatorFee(client, input) {
3030
+ const instructions = await getCollectCreatorFeeInstructions(client, {
3031
+ mint: input.mint,
3032
+ creator: input.creator,
3033
+ ...input.quote ? { quote: input.quote } : {}
3034
+ });
3035
+ return await buildMessage(client, input.creator, instructions, {
3036
+ addressLookupTables: input.addressLookupTables,
3037
+ computeUnitLimit: input.computeUnitLimit ?? 55e3,
3038
+ ...input.computeUnitPrice === void 0 ? {} : { computeUnitPrice: input.computeUnitPrice }
3039
+ });
3040
+ }
3041
+ /**
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.
3046
+ *
3047
+ * `mint` is optional. Without it only the curve vault is read, which is the one number that covers
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.
3050
+ */
3051
+ async function fetchCreatorFees(client, input) {
3052
+ const ids = raydiumIds(client.cluster);
3053
+ const quoteMint = input.quoteMint ?? "So11111111111111111111111111111111111111112";
3054
+ const vault = await creatorFeeVaultAddress(ids.launchlab, input.creator, quoteMint);
3055
+ const [vaultAccount] = await readAccounts(client.rpc, [vault]);
3056
+ const curveQuote = vaultAccount ? tokenAccountAmount(vaultAccount.data) : 0n;
3057
+ if (!input.mint) return {
3058
+ quoteMint,
3059
+ curveQuote,
3060
+ cpmmQuote: 0n,
3061
+ cpmmTokens: 0n
3062
+ };
3063
+ const settings = await fetchCurveSettings(client, await launchlabGlobalConfig(ids.launchlab, quoteMint), ids);
3064
+ try {
3065
+ const { pool } = await findCpmmPool(client, {
3066
+ mint: input.mint,
3067
+ quoteMint,
3068
+ creator: input.creator,
3069
+ cpswapConfig: settings.cpswapConfig
3070
+ });
3071
+ const [token0] = sortedMints(input.mint, quoteMint);
3072
+ const baseIsToken0 = token0 === input.mint;
3073
+ return {
3074
+ quoteMint,
3075
+ curveQuote,
3076
+ cpmmQuote: baseIsToken0 ? pool.creatorFeesToken1 : pool.creatorFeesToken0,
3077
+ cpmmTokens: baseIsToken0 ? pool.creatorFeesToken0 : pool.creatorFeesToken1
3078
+ };
3079
+ } catch {
3080
+ return {
3081
+ quoteMint,
3082
+ curveQuote,
3083
+ cpmmQuote: 0n,
3084
+ cpmmTokens: 0n
3085
+ };
3086
+ }
3087
+ }
3088
+ //#endregion
3089
+ //#region src/raydium/trade.ts
3090
+ /**
3091
+ * A venue trade a wallet sends on its own, outside any Gabox instruction.
3092
+ *
3093
+ * Pack buys and token sales go through `buy_pack` and `sell_tokens`: they take the venue's account
3094
+ * list as `remainingAccounts` and the Gabox program does the call. This file is for the one trade
3095
+ * Gabox never makes, a plain `buy_exact_in` on the curve. Anyone may trade a Gabox coin directly,
3096
+ * and the devnet end-to-end test uses this to buy the rest of the curve and force graduation.
3097
+ *
3098
+ * The account list and the discriminator both come from `abi.ts`, which codegen took from
3099
+ * Raydium's IDL. Nothing here retypes either.
3100
+ */
3101
+ const u64 = getU64Encoder();
3102
+ /**
3103
+ * `buy_exact_in` on the curve: spend exactly `quoteIn` of the quote token and take whatever coins
3104
+ * it buys.
3105
+ *
3106
+ * LaunchLab caps the trade at what the curve has left to sell, so a spend larger than the rest of
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.
3110
+ */
3111
+ async function getCurveBuyExactInInstruction(client, input, ids = raydiumIds(client.cluster)) {
3112
+ if (input.quoteIn <= 0n) throw new Error("quoteIn must be positive");
3113
+ if (input.minTokensOut < 0n) throw new Error("minTokensOut must not be negative");
3114
+ const quoteMint = input.quoteMint ?? "So11111111111111111111111111111111111111112";
3115
+ const quoteTokenProgram = input.quoteTokenProgram ?? "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
3116
+ const poolState = await launchlabPoolAddress(ids.launchlab, input.mint, quoteMint);
3117
+ const accounts = launchlabBuyExactInAccounts({
3118
+ launchlab: ids.launchlab,
3119
+ launchlabAuthority: ids.launchlabAuthority,
3120
+ launchlabEventAuthority: ids.launchlabEventAuthority,
3121
+ globalConfig: await launchlabGlobalConfig(ids.launchlab, quoteMint),
3122
+ platformConfig: ids.gaboxPlatform,
3123
+ poolState,
3124
+ mint: input.mint,
3125
+ quoteMint,
3126
+ baseVault: await launchlabVaultAddress(ids.launchlab, poolState, input.mint),
3127
+ quoteVault: await launchlabVaultAddress(ids.launchlab, poolState, quoteMint),
3128
+ user: input.user.address,
3129
+ userBaseToken: await ata(input.user.address, input.mint),
3130
+ userQuoteToken: await ata(input.user.address, quoteMint, quoteTokenProgram),
3131
+ quoteTokenProgram,
3132
+ platformFeeVault: await platformFeeVaultAddress(ids.launchlab, ids.gaboxPlatform, quoteMint),
3133
+ creatorFeeVault: await creatorFeeVaultAddress(ids.launchlab, input.creator, quoteMint)
3134
+ });
3135
+ return {
3136
+ programAddress: ids.launchlab,
3137
+ accounts: attachSigner(accounts, input.user),
3138
+ data: new Uint8Array([
3139
+ ...LAUNCHLAB_BUY_EXACT_IN.discriminator,
3140
+ ...u64.encode(input.quoteIn),
3141
+ ...u64.encode(input.minTokensOut),
3142
+ ...u64.encode(SHARE_FEE_RATE)
3143
+ ])
3144
+ };
3145
+ }
3146
+ /** Give the signer to the slot the ABI already marks as signing. Roles never change here. */
3147
+ function attachSigner(accounts, signer) {
3148
+ let matched = false;
3149
+ const withSigner = accounts.map((meta) => {
3150
+ if (!(meta.role === 2 || meta.role === 3) || meta.address !== signer.address) return meta;
3151
+ matched = true;
3152
+ return {
3153
+ ...meta,
3154
+ signer
3155
+ };
3156
+ });
3157
+ if (!matched) throw new Error(`${signer.address} is not the signing account of this trade. Build the account list with the same wallet you are going to sign with.`);
3158
+ return withSigner;
3159
+ }
3160
+ //#endregion
3161
+ //#region src/raydium/index.ts
3162
+ var raydium_exports = /* @__PURE__ */ __exportAll({
3163
+ ASSOCIATED_TOKEN_PROGRAM_ADDRESS: () => ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
3164
+ CONSTANT_CURVE_TAG: () => 0,
3165
+ CPMM_AMM_CONFIG_DISCRIMINATOR: () => CPMM_AMM_CONFIG_DISCRIMINATOR,
3166
+ CPMM_AMM_CONFIG_HEAD_SIZE: () => 116,
3167
+ CPMM_COLLECT_CREATOR_FEE: () => CPMM_COLLECT_CREATOR_FEE,
3168
+ CPMM_POOL_OFFSETS: () => CPMM_POOL_OFFSETS,
3169
+ CPMM_POOL_STATE_DISCRIMINATOR: () => CPMM_POOL_STATE_DISCRIMINATOR,
3170
+ CPMM_POOL_STATE_HEAD_SIZE: () => 413,
3171
+ CPMM_POOL_STATE_SIZE: () => 637,
3172
+ CPMM_SEEDS: () => CPMM_SEEDS,
3173
+ CPMM_SWAP_BASE_INPUT: () => CPMM_SWAP_BASE_INPUT,
3174
+ CPMM_SWAP_BASE_OUTPUT: () => CPMM_SWAP_BASE_OUTPUT,
3175
+ CREATOR_FEE_ON_QUOTE: () => 0,
3176
+ LAUNCHLAB_BUY_EXACT_IN: () => LAUNCHLAB_BUY_EXACT_IN,
3177
+ LAUNCHLAB_BUY_EXACT_OUT: () => LAUNCHLAB_BUY_EXACT_OUT,
3178
+ LAUNCHLAB_CLAIM_CREATOR_FEE: () => LAUNCHLAB_CLAIM_CREATOR_FEE,
3179
+ LAUNCHLAB_GLOBAL_CONFIG_DISCRIMINATOR: () => LAUNCHLAB_GLOBAL_CONFIG_DISCRIMINATOR,
3180
+ LAUNCHLAB_GLOBAL_CONFIG_HEAD_SIZE: () => 115,
3181
+ LAUNCHLAB_INITIALIZE: () => LAUNCHLAB_INITIALIZE,
3182
+ LAUNCHLAB_PLATFORM_CONFIG_DISCRIMINATOR: () => LAUNCHLAB_PLATFORM_CONFIG_DISCRIMINATOR,
3183
+ LAUNCHLAB_PLATFORM_CONFIG_HEAD_SIZE: () => 940,
3184
+ LAUNCHLAB_POOL_STATE_DISCRIMINATOR: () => LAUNCHLAB_POOL_STATE_DISCRIMINATOR,
3185
+ LAUNCHLAB_POOL_STATE_HEAD_SIZE: () => 367,
3186
+ LAUNCHLAB_SEEDS: () => LAUNCHLAB_SEEDS,
3187
+ LAUNCHLAB_SELL_EXACT_IN: () => LAUNCHLAB_SELL_EXACT_IN,
3188
+ LAUNCHLAB_STATUS_FUND: () => 0,
3189
+ LAUNCHLAB_STATUS_MIGRATE: () => 1,
3190
+ LAUNCHLAB_STATUS_TRADE: () => 2,
3191
+ LAUNCH_DECIMALS: () => 6,
3192
+ LAUNCH_SUPPLY: () => LAUNCH_SUPPLY,
3193
+ LAUNCH_TOTAL_BASE_SELL: () => LAUNCH_TOTAL_BASE_SELL,
3194
+ METADATA_LIMITS: () => METADATA_LIMITS,
3195
+ METADATA_SEED: () => METADATA_SEED,
3196
+ METAPLEX_PROGRAM_ADDRESS: () => METAPLEX_PROGRAM_ADDRESS,
3197
+ MIGRATE_TO_CPMM: () => 1,
3198
+ PLATFORM_ADMIN: () => PLATFORM_ADMIN,
3199
+ RATE_DENOMINATOR: () => RATE_DENOMINATOR,
3200
+ RAYDIUM_DEVNET_IDS: () => RAYDIUM_DEVNET_IDS,
3201
+ RAYDIUM_IDS: () => RAYDIUM_IDS,
3202
+ RAYDIUM_MAINNET_IDS: () => RAYDIUM_MAINNET_IDS,
3203
+ RENT_SYSVAR_ADDRESS: () => RENT_SYSVAR_ADDRESS,
3204
+ SHARE_FEE_RATE: () => SHARE_FEE_RATE,
3205
+ SYSTEM_PROGRAM_ADDRESS: () => SYSTEM_PROGRAM_ADDRESS,
3206
+ TOKEN_2022_PROGRAM_ADDRESS: () => TOKEN_2022_PROGRAM_ADDRESS,
3207
+ TOKEN_PROGRAM_ADDRESS: () => TOKEN_PROGRAM_ADDRESS,
3208
+ WSOL_MINT: () => WSOL_MINT,
3209
+ ata: () => ata,
3210
+ ceilDiv: () => ceilDiv,
3211
+ ceilDivRate: () => ceilDivRate,
3212
+ claimCreatorFee: () => claimCreatorFee,
3213
+ collectCreatorFee: () => collectCreatorFee,
3214
+ compareAddresses: () => compareAddresses,
3215
+ cpmmAuthority: () => cpmmAuthority,
3216
+ cpmmBuyAccounts: () => cpmmBuyAccounts,
3217
+ cpmmCreatorFeeShare: () => cpmmCreatorFeeShare,
3218
+ cpmmPoolAddress: () => cpmmPoolAddress,
3219
+ cpmmPoolMatches: () => cpmmPoolMatches,
3220
+ cpmmSellAccounts: () => cpmmSellAccounts,
3221
+ cpmmSwapBaseInput: () => cpmmSwapBaseInput,
3222
+ cpmmSwapBaseOutput: () => cpmmSwapBaseOutput,
3223
+ creatorFeeOnInput: () => creatorFeeOnInput,
3224
+ creatorFeeVaultAddress: () => creatorFeeVaultAddress,
3225
+ creatorFeeVaultAuthority: () => creatorFeeVaultAuthority,
3226
+ curveBuyExactIn: () => curveBuyExactIn,
3227
+ curveBuyExactOut: () => curveBuyExactOut,
3228
+ curveQuote: () => curveQuote,
3229
+ curveSellExactIn: () => curveSellExactIn,
3230
+ decodeBase64: () => decodeBase64,
3231
+ decodeCpmmAmmConfig: () => decodeCpmmAmmConfig,
3232
+ decodeCpmmPool: () => decodeCpmmPool,
3233
+ decodeLaunchlabGlobalConfig: () => decodeLaunchlabGlobalConfig,
3234
+ decodeLaunchlabPlatformConfig: () => decodeLaunchlabPlatformConfig,
3235
+ decodeLaunchlabPool: () => decodeLaunchlabPool,
3236
+ fetchCreatorFees: () => fetchCreatorFees,
3237
+ fetchCurveSettings: () => fetchCurveSettings,
3238
+ fetchQuoteAsset: () => fetchQuoteAsset,
3239
+ fetchQuoteBinding: () => fetchQuoteBinding,
3240
+ fetchQuoteConfig: () => fetchQuoteConfig,
3241
+ fetchQuoteDisplay: () => fetchQuoteDisplay,
3242
+ findCpmmPool: () => findCpmmPool,
3243
+ getClaimCreatorFeeInstructions: () => getClaimCreatorFeeInstructions,
3244
+ getCollectCreatorFeeInstructions: () => getCollectCreatorFeeInstructions,
3245
+ getCurveBuyExactInInstruction: () => getCurveBuyExactInInstruction,
3246
+ getLaunchInstruction: () => getLaunchInstruction,
3247
+ initialCurve: () => initialCurve,
3248
+ isQuoteSupported: () => isQuoteSupported,
3249
+ launchlabAuthority: () => launchlabAuthority,
3250
+ launchlabBuyAccounts: () => launchlabBuyAccounts,
3251
+ launchlabBuyExactInAccounts: () => launchlabBuyExactInAccounts,
3252
+ launchlabEventAuthority: () => launchlabEventAuthority,
3253
+ launchlabGlobalConfig: () => launchlabGlobalConfig,
3254
+ launchlabPlatformConfig: () => launchlabPlatformConfig,
3255
+ launchlabPoolAddress: () => launchlabPoolAddress,
3256
+ launchlabSellAccounts: () => launchlabSellAccounts,
3257
+ launchlabVaultAddress: () => launchlabVaultAddress,
3258
+ metadataAddress: () => metadataAddress,
3259
+ metaplexSymbol: () => metaplexSymbol,
3260
+ mintDecimals: () => mintDecimals,
3261
+ newCurveBuyCost: () => newCurveBuyCost,
3262
+ newCurveReserves: () => newCurveReserves,
3263
+ order: () => order,
3264
+ platformFeeVaultAddress: () => platformFeeVaultAddress,
3265
+ preFeeAmount: () => preFeeAmount,
3266
+ quoteAccountFor: () => quoteAccountFor,
3267
+ raydiumIds: () => raydiumIds,
3268
+ readAccounts: () => readAccounts,
3269
+ remainingBase: () => remainingBase,
3270
+ resolveVenue: () => resolveVenue,
3271
+ sellQuote: () => sellQuote,
3272
+ sortedMints: () => sortedMints,
3273
+ token2022Symbol: () => token2022Symbol,
3274
+ tokenAccountAmount: () => tokenAccountAmount,
3275
+ tokenAccountOwnerAndMint: () => tokenAccountOwnerAndMint,
3276
+ totalCurveFeeRate: () => totalCurveFeeRate,
3277
+ wsolAccountFor: () => wsolAccountFor
3278
+ });
3279
+ //#endregion
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 };
3281
+
3282
+ //# sourceMappingURL=raydium-CU-tZzIk.js.map