@send-fun/sdk 1.1.0 → 2.0.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.
Files changed (80) hide show
  1. package/README.md +8 -9
  2. package/dist/index.cjs +777 -433
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +678 -217
  5. package/dist/index.d.cts.map +1 -1
  6. package/dist/index.d.mts +678 -217
  7. package/dist/index.d.mts.map +1 -1
  8. package/dist/index.mjs +777 -433
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +7 -7
  11. package/src/dex/generated/accounts/globalConfig.ts +39 -2
  12. package/src/dex/generated/accounts/pool.ts +60 -9
  13. package/src/dex/generated/accounts/rewardAccrual.ts +39 -2
  14. package/src/dex/generated/errors/sendDex.ts +16 -8
  15. package/src/dex/generated/events/creatorClaimEvent.ts +3 -2
  16. package/src/dex/generated/events/poolCreateEvent.ts +8 -2
  17. package/src/dex/generated/events/tokenCreateEvent.ts +8 -9
  18. package/src/dex/generated/events/tradeEvent.ts +9 -2
  19. package/src/dex/generated/index.ts +1 -0
  20. package/src/dex/generated/instructions/claimCreatorFees.ts +21 -5
  21. package/src/{nexus → dex}/generated/pdas/creatorFeeConfig.ts +6 -6
  22. package/src/dex/generated/pdas/index.ts +1 -0
  23. package/src/dex/generated/plugins/sendDex.ts +3 -0
  24. package/src/dex/generated/shared/index.ts +52 -0
  25. package/src/dex/trade.ts +12 -13
  26. package/src/launchpad/create.ts +13 -38
  27. package/src/launchpad/generated/accounts/bondingCurve.ts +62 -9
  28. package/src/launchpad/generated/accounts/globalConfig.ts +39 -2
  29. package/src/launchpad/generated/accounts/rewardAccrual.ts +39 -2
  30. package/src/launchpad/generated/errors/sendLaunchpad.ts +12 -4
  31. package/src/launchpad/generated/events/completeEvent.ts +3 -2
  32. package/src/launchpad/generated/events/creatorClaimEvent.ts +3 -2
  33. package/src/launchpad/generated/events/migrateEvent.ts +3 -2
  34. package/src/launchpad/generated/events/tokenCreateEvent.ts +10 -9
  35. package/src/launchpad/generated/events/tradeEvent.ts +9 -2
  36. package/src/launchpad/generated/index.ts +1 -0
  37. package/src/launchpad/generated/instructions/claimCreatorFees.ts +17 -4
  38. package/src/launchpad/generated/instructions/createToken.ts +32 -50
  39. package/src/launchpad/generated/instructions/migrate.ts +79 -12
  40. package/src/launchpad/generated/pdas/creatorFeeConfig.ts +39 -0
  41. package/src/launchpad/generated/pdas/index.ts +1 -0
  42. package/src/launchpad/generated/plugins/sendLaunchpad.ts +3 -0
  43. package/src/launchpad/generated/shared/index.ts +52 -0
  44. package/src/launchpad/migrate.ts +7 -4
  45. package/src/launchpad/trade.ts +15 -15
  46. package/src/math/amm.ts +40 -33
  47. package/src/math/fee-decay.ts +3 -2
  48. package/src/math/fees.ts +4 -2
  49. package/src/math/internal.ts +1 -1
  50. package/src/nexus/fee-helpers.ts +10 -5
  51. package/src/nexus/generated/accounts/altRegistry.ts +41 -2
  52. package/src/nexus/generated/accounts/feePreset.ts +43 -4
  53. package/src/nexus/generated/accounts/globalConfig.ts +39 -2
  54. package/src/nexus/generated/accounts/index.ts +0 -1
  55. package/src/nexus/generated/accounts/partnerConfig.ts +48 -5
  56. package/src/nexus/generated/accounts/partnerMetadata.ts +39 -2
  57. package/src/nexus/generated/accounts/rewardState.ts +41 -2
  58. package/src/nexus/generated/accounts/sendNexus.accounts.ts +0 -5
  59. package/src/nexus/generated/accounts/stakingConfig.ts +39 -2
  60. package/src/nexus/generated/accounts/userRewardDebt.ts +42 -5
  61. package/src/nexus/generated/accounts/userStakePosition.ts +42 -5
  62. package/src/nexus/generated/errors/sendNexus.ts +12 -4
  63. package/src/nexus/generated/index.ts +1 -0
  64. package/src/nexus/generated/pdas/index.ts +0 -1
  65. package/src/nexus/generated/plugins/sendNexus.ts +0 -35
  66. package/src/nexus/generated/shared/index.ts +52 -0
  67. package/src/nexus/generated/types/dexFees.ts +4 -4
  68. package/src/nexus/generated/types/index.ts +0 -1
  69. package/src/nexus/generated/types/launchpadFees.ts +4 -4
  70. package/src/nexus/staking.ts +45 -41
  71. package/src/platform.ts +2 -2
  72. package/src/transfer-fee.ts +19 -17
  73. package/src/utils/chunk.ts +1 -1
  74. package/src/utils/index.ts +2 -5
  75. package/src/utils/mint-info.ts +8 -7
  76. package/src/utils/partner.ts +1 -1
  77. package/src/utils/pda.ts +1 -1
  78. package/src/nexus/generated/accounts/creatorFeeConfig.ts +0 -200
  79. package/src/nexus/generated/types/callerType.ts +0 -38
  80. package/src/utils/creator-hash.ts +0 -57
package/src/math/amm.ts CHANGED
@@ -4,7 +4,6 @@ const BPS_DIVISOR = 10_000n;
4
4
 
5
5
  const U64_MAX = 18_446_744_073_709_551_615n;
6
6
 
7
- /** Each call mirrors a `u64::try_from` in the Rust twin; drop one and an oversized leg fails in the encoder. */
8
7
  function assertU64(label: string, value: bigint): bigint {
9
8
  if (value > U64_MAX) {
10
9
  throw new RangeError(`${label}: overflows u64`);
@@ -12,15 +11,15 @@ function assertU64(label: string, value: bigint): bigint {
12
11
  return value;
13
12
  }
14
13
 
15
- /** Token-2022 `TransferFeeConfig` for the epoch the trade lands in; a stale one misprices the trade. */
14
+ /** A Token-2022 transfer fee for one epoch. Use the fee for the epoch the trade lands in. */
16
15
  export interface MintFee {
17
16
  /** 0 to 10_000. */
18
17
  bps: number;
19
- /** Cap on the withheld amount, in the mint's raw units. */
18
+ /** Maximum fee per transfer, in the mint's raw units. */
20
19
  maximumFee: bigint;
21
20
  }
22
21
 
23
- /** No transfer lands exactly the requested amount; approximating one hands the program a bound it rejects. */
22
+ /** Thrown when no transfer delivers exactly `amount` after the transfer fee. */
24
23
  export class TransferFeeNotSettleableError extends RangeError {
25
24
  readonly amount: bigint;
26
25
  readonly mintFee: MintFee;
@@ -42,7 +41,8 @@ function assertMintFee(fee: MintFee): void {
42
41
  }
43
42
  }
44
43
 
45
- /** Rounds up, then caps (SPL's order); swapping them lets a split booking over-credit. */
44
+ /** Returns the transfer fee on `amount`. Rounds up, then caps at `maximumFee`, in SPL's order.
45
+ * Returns 0 without `fee`. Throws `RangeError` if `fee` is out of range. */
46
46
  export function feeOn(amount: bigint, fee?: MintFee): bigint {
47
47
  if (fee === undefined) return 0n;
48
48
  assertMintFee(fee);
@@ -52,18 +52,18 @@ export function feeOn(amount: bigint, fee?: MintFee): bigint {
52
52
  return raw < fee.maximumFee ? raw : fee.maximumFee;
53
53
  }
54
54
 
55
- /** What lands when `amount` is sent; use `grossUp` to land an exact amount. */
55
+ /** Returns what the recipient receives when `amount` is sent. `grossUp` is the inverse. */
56
56
  export function amountAfterFee(amount: bigint, fee?: MintFee): bigint {
57
57
  return amount - feeOn(amount, fee);
58
58
  }
59
59
 
60
- /** Line-for-line mirror of SPL's `TransferFee::calculate_pre_fee_amount`; `undefined` when no u64 answer exists. */
60
+ /** Matches SPL `TransferFee::calculate_pre_fee_amount`. `undefined` if the result does not fit a u64. */
61
61
  function preFeeAmount(amount: bigint, fee: MintFee): bigint | undefined {
62
62
  const bps = BigInt(fee.bps);
63
63
  if (bps === 0n) return amount;
64
- // Unreachable via `grossUp`; SPL has it, so the mirror keeps it.
64
+ // Unreachable from `grossUp`. Kept to match SPL.
65
65
  if (amount === 0n) return 0n;
66
- // 100%: only the cap can be settled.
66
+ // At 100%, the fee is always `maximumFee`.
67
67
  if (bps === BPS_DIVISOR) {
68
68
  const capped = amount + fee.maximumFee;
69
69
  return capped > U64_MAX ? undefined : capped;
@@ -77,7 +77,8 @@ function preFeeAmount(amount: bigint, fee: MintFee): bigint | undefined {
77
77
  return rawPreFee > U64_MAX ? undefined : rawPreFee;
78
78
  }
79
79
 
80
- /** What must be sent for exactly `amount` to land; throws {@link TransferFeeNotSettleableError} if none does. */
80
+ /** Returns the amount to send so that exactly `amount` arrives. Returns `amount` without `fee`.
81
+ * Throws {@link TransferFeeNotSettleableError} if no such amount exists. */
81
82
  export function grossUp(amount: bigint, fee?: MintFee): bigint {
82
83
  if (fee === undefined) return amount;
83
84
  assertMintFee(fee);
@@ -87,7 +88,7 @@ export function grossUp(amount: bigint, fee?: MintFee): bigint {
87
88
  if (preFee === undefined)
88
89
  throw new TransferFeeNotSettleableError(amount, fee);
89
90
 
90
- // SPL's inverse is inexact (`feeOn(x) >= inverse(x - feeOn(x))`): re-derive forward and reject a mismatch.
91
+ // SPL's inverse is inexact (`feeOn(x) >= inverse(x - feeOn(x))`). Check it forward and reject a mismatch.
91
92
  const impliedFee = feeOn(preFee, fee);
92
93
  const gross = amount + impliedFee;
93
94
  if (gross > U64_MAX || feeOn(gross, fee) !== impliedFee) {
@@ -96,8 +97,8 @@ export function grossUp(amount: bigint, fee?: MintFee): bigint {
96
97
  return gross;
97
98
  }
98
99
 
99
- /** `baseAmount`/`quoteAmount` are the user's transfers, as in on-chain `TradeResult`. Read the net
100
- * fields on {@link BuyQuote}/{@link SellQuote}; re-deriving them drifts from the program by a rounding step. */
100
+ /** `baseAmount` and `quoteAmount` are the user's transfers.
101
+ * Read the net amounts from the fields of {@link BuyQuote} and {@link SellQuote}. Do not calculate them again. */
101
102
  export interface TradeQuote {
102
103
  /** Base sent: vault to user on a buy, user to vault on a sell. */
103
104
  baseAmount: bigint;
@@ -110,20 +111,20 @@ export interface TradeQuote {
110
111
  }
111
112
 
112
113
  export interface BuyQuote extends TradeQuote {
113
- /** Priced on the quote reaching the vault. */
114
+ /** Priced on the quote that reaches the vault. */
114
115
  fee: bigint;
115
- /** Base credited to the buyer, after the base mint's cut. */
116
+ /** Base the buyer receives, after the base mint's transfer fee. */
116
117
  baseToUser: bigint;
117
118
  /** Equals `quoteAmount`. */
118
119
  quoteFromUser: bigint;
119
120
  }
120
121
 
121
122
  export interface SellQuote extends TradeQuote {
122
- /** Priced on the quote leaving the vault. */
123
+ /** Priced on the AMM's quote output, before this fee. */
123
124
  fee: bigint;
124
125
  /** Equals `baseAmount`. */
125
126
  baseFromUser: bigint;
126
- /** Quote credited to the seller, after the quote mint's cut. */
127
+ /** Quote the seller receives, after the quote mint's transfer fee. */
127
128
  quoteToUser: bigint;
128
129
  }
129
130
 
@@ -139,7 +140,8 @@ function sqrtBigInt(value: bigint): bigint {
139
140
  return x;
140
141
  }
141
142
 
142
- /** Output rounds down (the new reserve rounds up), so `k` never shrinks. */
143
+ /** Returns the constant-product output for `amountIn`, rounded down. Throws `RangeError` on a zero
144
+ * reserve, a zero `amountIn`, a zero output, or an output past u64. */
143
145
  export function calculateOutput(
144
146
  reserveIn: bigint,
145
147
  reserveOut: bigint,
@@ -169,7 +171,8 @@ export function calculateOutput(
169
171
  return assertU64('calculateOutput', amountOut);
170
172
  }
171
173
 
172
- /** Required input rounds up so the user pays enough. */
174
+ /** Returns the input needed for `amountOut`, rounded up. Throws `RangeError` on a zero reserve, a zero
175
+ * `amountOut`, an `amountOut` not below `reserveOut`, or an input past u64. */
173
176
  export function calculateInputForOutput(
174
177
  reserveIn: bigint,
175
178
  reserveOut: bigint,
@@ -207,7 +210,7 @@ interface BaseQuoteParams {
207
210
  baseFee?: MintFee;
208
211
  }
209
212
 
210
- /** Launchpad only: `bondingCurve.realBaseReserves`. A DEX pool has no cap. */
213
+ /** `baseReserveCap` is `bondingCurve.realBaseReserves`. A DEX pool has no cap. */
211
214
  interface BaseReserveCap {
212
215
  baseReserveCap?: bigint;
213
216
  }
@@ -234,7 +237,7 @@ function ammBuyExactOut(
234
237
  throw new RangeError('buyExactOut: invalid feeBps');
235
238
  }
236
239
 
237
- // A `feeBps` near 10_000 amplifies the leg up to 10_000x, so this can overflow u64.
240
+ // A `feeBps` near 10_000 multiplies the leg by up to 10_000, so this can pass u64.
238
241
  const totalQuote = assertU64(
239
242
  'buyExactOut',
240
243
  ceilDiv(quoteBeforeFee * BPS_DIVISOR, divisor),
@@ -251,7 +254,7 @@ function ammBuyExactIn(
251
254
  params: { reserveQuote: bigint; reserveBase: bigint; feeBps: number },
252
255
  quoteAmountIn: bigint,
253
256
  ): AmmLegs {
254
- // Fee comes off before the swap; the net rounds down, so the fee keeps the remainder.
257
+ // The fee comes off before the swap. The net rounds down, and the fee takes the remainder.
255
258
  const netFactor = BPS_DIVISOR - BigInt(params.feeBps);
256
259
  if (netFactor <= 0n) {
257
260
  throw new RangeError('buyExactIn: invalid feeBps');
@@ -337,7 +340,9 @@ function buyQuote(
337
340
  };
338
341
  }
339
342
 
340
- /** Guard with `calculateSlippageUp(quoteAmount, bps)`; it already carries the quote mint's cut. */
343
+ /** Quotes a buy where the buyer receives `baseAmountOut`. With `baseReserveCap`, the vault sends at
344
+ * most the cap. Guard with `calculateSlippageUp(quoteAmount, bps)`. `quoteAmount` includes the quote
345
+ * mint's transfer fee. */
341
346
  export function buyExactOut(
342
347
  params: BaseQuoteParams & BaseReserveCap & { baseAmountOut: bigint },
343
348
  ): BuyQuote {
@@ -352,7 +357,7 @@ export function buyExactOut(
352
357
  cap !== undefined && baseOutOfVault > cap ? cap : baseOutOfVault;
353
358
 
354
359
  const legs = ammBuyExactOut(params, baseAmount);
355
- // The priced total must land, so the user is debited more than it.
360
+ // The priced total must reach the vault, so the user sends it grossed up.
356
361
  const quoteFromUser = grossUp(legs.quoteAmount, params.quoteFee);
357
362
 
358
363
  return buyQuote(
@@ -362,7 +367,8 @@ export function buyExactOut(
362
367
  );
363
368
  }
364
369
 
365
- /** Guard with `calculateSlippageDown(baseToUser, bps)`, not `baseAmount`: the program bounds what the buyer nets. */
370
+ /** Quotes a buy that spends `quoteAmountIn`. Guard with `calculateSlippageDown(baseToUser, bps)`, not
371
+ * `baseAmount`. The program checks the minimum against what the buyer receives. */
366
372
  export function buyExactIn(
367
373
  params: BaseQuoteParams & BaseReserveCap & { quoteAmountIn: bigint },
368
374
  ): BuyQuote {
@@ -371,7 +377,7 @@ export function buyExactIn(
371
377
  throw new RangeError('buyExactIn: invalid amount');
372
378
  }
373
379
 
374
- // The AMM only ever prices what reaches the vault.
380
+ // The AMM prices only what reaches the vault.
375
381
  const quoteIntoVault = amountAfterFee(
376
382
  params.quoteAmountIn,
377
383
  params.quoteFee,
@@ -382,7 +388,7 @@ export function buyExactIn(
382
388
 
383
389
  const uncapped = ammBuyExactIn(params, quoteIntoVault);
384
390
 
385
- // Capped: re-price exact-out at the cap and gross up; clamping the base output overstates the quote leg.
391
+ // Over the cap: price exact-out at the cap, then gross up. Clamping the base output alone overstates the quote leg.
386
392
  const cap = params.baseReserveCap;
387
393
  if (cap === undefined || uncapped.baseAmount <= cap) {
388
394
  return buyQuote(
@@ -402,7 +408,7 @@ export function buyExactIn(
402
408
  );
403
409
  }
404
410
 
405
- /** Guard with `calculateSlippageDown(quoteToUser, bps)`, not `quoteAmount`. */
411
+ /** Quotes a sell of `baseAmountIn`. Guard with `calculateSlippageDown(quoteToUser, bps)`, not `quoteAmount`. */
406
412
  export function sellExactIn(
407
413
  params: BaseQuoteParams & { baseAmountIn: bigint },
408
414
  ): SellQuote {
@@ -417,7 +423,7 @@ export function sellExactIn(
417
423
  }
418
424
 
419
425
  const legs = ammSellExactIn(params, baseIntoVault);
420
- // `feeOn`, never `grossUp`: the AMM's output leaves the vault as priced.
426
+ // `feeOn`, not `grossUp`: the AMM's output leaves the vault as priced.
421
427
  const quoteTransferFee = feeOn(legs.quoteAmount, params.quoteFee);
422
428
 
423
429
  return {
@@ -431,7 +437,8 @@ export function sellExactIn(
431
437
  };
432
438
  }
433
439
 
434
- /** Guard with `calculateSlippageUp(baseFromUser, bps)`, which already carries the base mint's cut. */
440
+ /** Quotes a sell where the seller receives `quoteAmountOut`. Guard with
441
+ * `calculateSlippageUp(baseFromUser, bps)`. `baseFromUser` includes the base mint's transfer fee. */
435
442
  export function sellExactOut(
436
443
  params: BaseQuoteParams & { quoteAmountOut: bigint },
437
444
  ): SellQuote {
@@ -478,7 +485,7 @@ export function calculateSlippageDown(
478
485
  );
479
486
  }
480
487
 
481
- /** Floors, as the program's `isqrt` does. */
488
+ /** Returns `sqrt(quoteAmount * baseAmount)`, rounded down. */
482
489
  export function calculateInitialLp(
483
490
  quoteAmount: bigint,
484
491
  baseAmount: bigint,
@@ -486,7 +493,7 @@ export function calculateInitialLp(
486
493
  return sqrtBigInt(quoteAmount * baseAmount);
487
494
  }
488
495
 
489
- /** Floating-point quote per base, for display only. */
496
+ /** Returns the price of one base token in quote tokens, as a float. For display only. */
490
497
  export function calculatePrice(params: {
491
498
  quoteReserves: bigint;
492
499
  baseReserves: bigint;
@@ -499,7 +506,7 @@ export function calculatePrice(params: {
499
506
  return rawRatio * 10 ** (baseDecimals - quoteDecimals);
500
507
  }
501
508
 
502
- /** Market cap in raw quote-token units. */
509
+ /** Returns the market cap in raw quote units. */
503
510
  export function calculateMarketCap(params: {
504
511
  quoteReserves: bigint;
505
512
  baseReserves: bigint;
@@ -1,6 +1,7 @@
1
1
  import { ceilDiv } from './internal.js';
2
2
 
3
- /** Premium in bps, rounded up. */
3
+ /** Returns the fee decay premium in bps, rounded up. The premium falls quadratically from
4
+ * `decayStartBps - standardFeeBps` at creation to 0 after `decaySeconds`. */
4
5
  export function calculateFeeDecayPremium(params: {
5
6
  currentTimestamp: bigint;
6
7
  createdAtTimestamp: bigint;
@@ -23,7 +24,7 @@ export function calculateFeeDecayPremium(params: {
23
24
  const standardBps = BigInt(standardFeeBps);
24
25
  const decaySecondsBig = BigInt(decaySeconds);
25
26
 
26
- // Future creation timestamps pay the full premium, not a discount.
27
+ // A creation time in the future pays the full premium.
27
28
  if (createdAtTimestamp > currentTimestamp) {
28
29
  if (startBps <= standardBps) return 0n;
29
30
  return startBps - standardBps;
package/src/math/fees.ts CHANGED
@@ -10,11 +10,13 @@ export interface FeeSplit {
10
10
  protocol: bigint;
11
11
  lp: bigint;
12
12
  creator: bigint;
13
- /** Decay share already included in `protocol`, not a separate payout. */
13
+ /** The decay premium share. `protocol` includes it. It is not a separate payout. */
14
14
  sniper: bigint;
15
15
  }
16
16
 
17
- /** LP/creator round down; protocol absorbs the decay premium and all remainders. */
17
+ /** Splits `feeAmount` into protocol, LP and creator shares. LP and creator round down. Protocol gets
18
+ * the decay premium and all remainders. Throws `RangeError` on a negative or non-integer input,
19
+ * a zero total bps, or `protocolBps + lpBps` above `baseTotalBps`. */
18
20
  export function splitFeeAmount(args: FeeSplitArgs): FeeSplit {
19
21
  const { feeAmount } = args;
20
22
  const protocolBps = toBps(args.protocolBps, 'protocolBps');
@@ -2,7 +2,7 @@ export function floorDiv(a: bigint, b: bigint): bigint {
2
2
  return a / b;
3
3
  }
4
4
 
5
- // Assumes non-negative inputs.
5
+ // Inputs must be non-negative.
6
6
  export function ceilDiv(a: bigint, b: bigint): bigint {
7
7
  return a / b + (a % b > 0n ? 1n : 0n);
8
8
  }
@@ -8,7 +8,8 @@ import { findPartnerConfigPda } from './generated/pdas/partnerConfig.js';
8
8
  import type { DexFees } from './generated/types/dexFees.js';
9
9
  import type { LaunchpadFees } from './generated/types/launchpadFees.js';
10
10
 
11
- /** Throws if the pair is unregistered. Take `platformConfig` from the market being priced: a partner's fees differ per platform. */
11
+ /** Fetches the `PartnerConfig` of `partner` on `platformConfig`. Throws if it does not exist.
12
+ * Use the `platformConfig` of the market you price. A partner's fees differ per platform. */
12
13
  export async function fetchPartnerFees(
13
14
  rpc: Rpc<GetAccountInfoApi>,
14
15
  partner: Address,
@@ -27,17 +28,21 @@ export async function fetchPartnerFees(
27
28
  return maybeAccount.data;
28
29
  }
29
30
 
30
- /** The `feeBps` a trade pays: the standard rate plus the decay premium from the market's
31
- * `createdAt`. Pass `fees.launchpad` for a curve, `fees.dex` for a pool; times in unix seconds. */
31
+ /** Returns the `feeBps` a trade pays: the standard rate plus the decay premium. The standard rate
32
+ * is the protocol and LP rates of `schedule` plus `creatorFeeBps`. `schedule` is the
33
+ * `PartnerConfig`'s `launchpad` for a curve or `dex` for a pool. `creatorFeeBps` and `createdAt`
34
+ * are the curve's or pool's. `schedule.maxCreatorFeeBps` has no effect. `createdAt` and `now`
35
+ * are in unix seconds. */
32
36
  export function effectiveFeeBps(
33
37
  schedule: LaunchpadFees | DexFees,
34
38
  createdAt: bigint,
35
39
  now: bigint,
40
+ creatorFeeBps: number,
36
41
  ): number {
37
42
  const standardFeeBps =
38
43
  schedule.protocolFeeBps +
39
- schedule.creatorFeeBps +
40
- ('lpFeeBps' in schedule ? schedule.lpFeeBps : 0);
44
+ ('lpFeeBps' in schedule ? schedule.lpFeeBps : 0) +
45
+ creatorFeeBps;
41
46
  const premium = calculateFeeDecayPremium({
42
47
  currentTimestamp: now,
43
48
  createdAtTimestamp: createdAt,
@@ -38,6 +38,7 @@ import {
38
38
  type ReadonlyUint8Array,
39
39
  } from '@solana/kit';
40
40
  import { SEND_NEXUS_PROGRAM_ADDRESS } from '../programs/index.js';
41
+ import { accountIsCreated } from '../shared/index.js';
41
42
 
42
43
  export const ALT_REGISTRY_DISCRIMINATOR: ReadonlyUint8Array = new Uint8Array([
43
44
  184, 237, 52, 250, 77, 8, 59, 74,
@@ -97,9 +98,25 @@ export function getAltRegistryCodec(): FixedSizeCodec<
97
98
  return combineCodec(getAltRegistryEncoder(), getAltRegistryDecoder());
98
99
  }
99
100
 
101
+ /**
102
+ * Decodes a `AltRegistry` account, throwing when another program owns it or its discriminator
103
+ * does not match.
104
+ *
105
+ * Unlike {@link fetchMaybeAltRegistry}, this throws for an address that only holds lamports rather
106
+ * than returning the non-existing variant: an account passed as existing must never come back as that
107
+ * variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
108
+ */
100
109
  export function decodeAltRegistry<TAddress extends string = string>(
101
110
  encodedAccount: EncodedAccount<TAddress>,
102
111
  ): Account<AltRegistry, TAddress>;
112
+ /**
113
+ * Decodes a `AltRegistry` account, throwing when another program owns it or its discriminator
114
+ * does not match.
115
+ *
116
+ * Unlike {@link fetchMaybeAltRegistry}, this throws for an address that only holds lamports rather
117
+ * than returning the non-existing variant: an account passed as existing must never come back as that
118
+ * variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
119
+ */
103
120
  export function decodeAltRegistry<TAddress extends string = string>(
104
121
  encodedAccount: MaybeEncodedAccount<TAddress>,
105
122
  ): MaybeAccount<AltRegistry, TAddress>;
@@ -130,6 +147,7 @@ export function decodeAltRegistry<TAddress extends string = string>(
130
147
  );
131
148
  }
132
149
 
150
+ /** Fetches a `AltRegistry` account, throwing when it does not exist or only holds lamports. */
133
151
  export async function fetchAltRegistry<TAddress extends string = string>(
134
152
  rpc: Parameters<typeof fetchEncodedAccount>[0],
135
153
  address: Address<TAddress>,
@@ -140,15 +158,25 @@ export async function fetchAltRegistry<TAddress extends string = string>(
140
158
  return maybeAccount;
141
159
  }
142
160
 
161
+ /**
162
+ * Fetches a `AltRegistry` account, or the non-existing variant when the address holds no
163
+ * account or only lamports (see {@link accountIsCreated}).
164
+ * {@link decodeAltRegistry} throws for a lamports-only account instead.
165
+ */
143
166
  export async function fetchMaybeAltRegistry<TAddress extends string = string>(
144
167
  rpc: Parameters<typeof fetchEncodedAccount>[0],
145
168
  address: Address<TAddress>,
146
169
  config?: FetchAccountConfig,
147
170
  ): Promise<MaybeAccount<AltRegistry, TAddress>> {
148
171
  const maybeAccount = await fetchEncodedAccount(rpc, address, config);
149
- return decodeAltRegistry(maybeAccount);
172
+ return decodeAltRegistry(
173
+ accountIsCreated(maybeAccount)
174
+ ? maybeAccount
175
+ : { address, exists: false },
176
+ );
150
177
  }
151
178
 
179
+ /** Fetches `AltRegistry` accounts, throwing when any does not exist or only holds lamports. */
152
180
  export async function fetchAllAltRegistry(
153
181
  rpc: Parameters<typeof fetchEncodedAccounts>[0],
154
182
  addresses: Array<Address>,
@@ -163,13 +191,24 @@ export async function fetchAllAltRegistry(
163
191
  return maybeAccounts;
164
192
  }
165
193
 
194
+ /**
195
+ * Fetches `AltRegistry` accounts, with the non-existing variant for each address that holds
196
+ * no account or only lamports (see {@link accountIsCreated}).
197
+ * {@link decodeAltRegistry} throws for a lamports-only account instead.
198
+ */
166
199
  export async function fetchAllMaybeAltRegistry(
167
200
  rpc: Parameters<typeof fetchEncodedAccounts>[0],
168
201
  addresses: Array<Address>,
169
202
  config?: FetchAccountsConfig,
170
203
  ): Promise<MaybeAccount<AltRegistry>[]> {
171
204
  const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
172
- return maybeAccounts.map((maybeAccount) => decodeAltRegistry(maybeAccount));
205
+ return maybeAccounts.map((maybeAccount) =>
206
+ decodeAltRegistry(
207
+ accountIsCreated(maybeAccount)
208
+ ? maybeAccount
209
+ : { address: maybeAccount.address, exists: false },
210
+ ),
211
+ );
173
212
  }
174
213
 
175
214
  export function getAltRegistrySize(): number {
@@ -44,6 +44,7 @@ import {
44
44
  type ReadonlyUint8Array,
45
45
  } from '@solana/kit';
46
46
  import { SEND_NEXUS_PROGRAM_ADDRESS } from '../programs/index.js';
47
+ import { accountIsCreated } from '../shared/index.js';
47
48
  import {
48
49
  getDexFeesDecoder,
49
50
  getDexFeesEncoder,
@@ -100,7 +101,7 @@ export function getFeePresetEncoder(): Encoder<FeePresetArgs> {
100
101
  ['launchpad', getLaunchpadFeesEncoder()],
101
102
  ['dex', getDexFeesEncoder()],
102
103
  ['platformConfig', getAddressEncoder()],
103
- ['reserved', fixEncoderSize(getBytesEncoder(), 32)],
104
+ ['reserved', fixEncoderSize(getBytesEncoder(), 64)],
104
105
  ]),
105
106
  (value) => ({ ...value, discriminator: FEE_PRESET_DISCRIMINATOR }),
106
107
  );
@@ -117,7 +118,7 @@ export function getFeePresetDecoder(): Decoder<FeePreset> {
117
118
  ['launchpad', getLaunchpadFeesDecoder()],
118
119
  ['dex', getDexFeesDecoder()],
119
120
  ['platformConfig', getAddressDecoder()],
120
- ['reserved', fixDecoderSize(getBytesDecoder(), 32)],
121
+ ['reserved', fixDecoderSize(getBytesDecoder(), 64)],
121
122
  ]);
122
123
  }
123
124
 
@@ -126,9 +127,25 @@ export function getFeePresetCodec(): Codec<FeePresetArgs, FeePreset> {
126
127
  return combineCodec(getFeePresetEncoder(), getFeePresetDecoder());
127
128
  }
128
129
 
130
+ /**
131
+ * Decodes a `FeePreset` account, throwing when another program owns it or its discriminator
132
+ * does not match.
133
+ *
134
+ * Unlike {@link fetchMaybeFeePreset}, this throws for an address that only holds lamports rather
135
+ * than returning the non-existing variant: an account passed as existing must never come back as that
136
+ * variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
137
+ */
129
138
  export function decodeFeePreset<TAddress extends string = string>(
130
139
  encodedAccount: EncodedAccount<TAddress>,
131
140
  ): Account<FeePreset, TAddress>;
141
+ /**
142
+ * Decodes a `FeePreset` account, throwing when another program owns it or its discriminator
143
+ * does not match.
144
+ *
145
+ * Unlike {@link fetchMaybeFeePreset}, this throws for an address that only holds lamports rather
146
+ * than returning the non-existing variant: an account passed as existing must never come back as that
147
+ * variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
148
+ */
132
149
  export function decodeFeePreset<TAddress extends string = string>(
133
150
  encodedAccount: MaybeEncodedAccount<TAddress>,
134
151
  ): MaybeAccount<FeePreset, TAddress>;
@@ -157,6 +174,7 @@ export function decodeFeePreset<TAddress extends string = string>(
157
174
  );
158
175
  }
159
176
 
177
+ /** Fetches a `FeePreset` account, throwing when it does not exist or only holds lamports. */
160
178
  export async function fetchFeePreset<TAddress extends string = string>(
161
179
  rpc: Parameters<typeof fetchEncodedAccount>[0],
162
180
  address: Address<TAddress>,
@@ -167,15 +185,25 @@ export async function fetchFeePreset<TAddress extends string = string>(
167
185
  return maybeAccount;
168
186
  }
169
187
 
188
+ /**
189
+ * Fetches a `FeePreset` account, or the non-existing variant when the address holds no
190
+ * account or only lamports (see {@link accountIsCreated}).
191
+ * {@link decodeFeePreset} throws for a lamports-only account instead.
192
+ */
170
193
  export async function fetchMaybeFeePreset<TAddress extends string = string>(
171
194
  rpc: Parameters<typeof fetchEncodedAccount>[0],
172
195
  address: Address<TAddress>,
173
196
  config?: FetchAccountConfig,
174
197
  ): Promise<MaybeAccount<FeePreset, TAddress>> {
175
198
  const maybeAccount = await fetchEncodedAccount(rpc, address, config);
176
- return decodeFeePreset(maybeAccount);
199
+ return decodeFeePreset(
200
+ accountIsCreated(maybeAccount)
201
+ ? maybeAccount
202
+ : { address, exists: false },
203
+ );
177
204
  }
178
205
 
206
+ /** Fetches `FeePreset` accounts, throwing when any does not exist or only holds lamports. */
179
207
  export async function fetchAllFeePreset(
180
208
  rpc: Parameters<typeof fetchEncodedAccounts>[0],
181
209
  addresses: Array<Address>,
@@ -186,11 +214,22 @@ export async function fetchAllFeePreset(
186
214
  return maybeAccounts;
187
215
  }
188
216
 
217
+ /**
218
+ * Fetches `FeePreset` accounts, with the non-existing variant for each address that holds
219
+ * no account or only lamports (see {@link accountIsCreated}).
220
+ * {@link decodeFeePreset} throws for a lamports-only account instead.
221
+ */
189
222
  export async function fetchAllMaybeFeePreset(
190
223
  rpc: Parameters<typeof fetchEncodedAccounts>[0],
191
224
  addresses: Array<Address>,
192
225
  config?: FetchAccountsConfig,
193
226
  ): Promise<MaybeAccount<FeePreset>[]> {
194
227
  const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
195
- return maybeAccounts.map((maybeAccount) => decodeFeePreset(maybeAccount));
228
+ return maybeAccounts.map((maybeAccount) =>
229
+ decodeFeePreset(
230
+ accountIsCreated(maybeAccount)
231
+ ? maybeAccount
232
+ : { address: maybeAccount.address, exists: false },
233
+ ),
234
+ );
196
235
  }
@@ -40,6 +40,7 @@ import {
40
40
  type ReadonlyUint8Array,
41
41
  } from '@solana/kit';
42
42
  import { SEND_NEXUS_PROGRAM_ADDRESS } from '../programs/index.js';
43
+ import { accountIsCreated } from '../shared/index.js';
43
44
  import {
44
45
  getAuthPlatformEntryDecoder,
45
46
  getAuthPlatformEntryEncoder,
@@ -110,9 +111,25 @@ export function getGlobalConfigCodec(): Codec<GlobalConfigArgs, GlobalConfig> {
110
111
  return combineCodec(getGlobalConfigEncoder(), getGlobalConfigDecoder());
111
112
  }
112
113
 
114
+ /**
115
+ * Decodes a `GlobalConfig` account, throwing when another program owns it or its discriminator
116
+ * does not match.
117
+ *
118
+ * Unlike {@link fetchMaybeGlobalConfig}, this throws for an address that only holds lamports rather
119
+ * than returning the non-existing variant: an account passed as existing must never come back as that
120
+ * variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
121
+ */
113
122
  export function decodeGlobalConfig<TAddress extends string = string>(
114
123
  encodedAccount: EncodedAccount<TAddress>,
115
124
  ): Account<GlobalConfig, TAddress>;
125
+ /**
126
+ * Decodes a `GlobalConfig` account, throwing when another program owns it or its discriminator
127
+ * does not match.
128
+ *
129
+ * Unlike {@link fetchMaybeGlobalConfig}, this throws for an address that only holds lamports rather
130
+ * than returning the non-existing variant: an account passed as existing must never come back as that
131
+ * variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
132
+ */
116
133
  export function decodeGlobalConfig<TAddress extends string = string>(
117
134
  encodedAccount: MaybeEncodedAccount<TAddress>,
118
135
  ): MaybeAccount<GlobalConfig, TAddress>;
@@ -143,6 +160,7 @@ export function decodeGlobalConfig<TAddress extends string = string>(
143
160
  );
144
161
  }
145
162
 
163
+ /** Fetches a `GlobalConfig` account, throwing when it does not exist or only holds lamports. */
146
164
  export async function fetchGlobalConfig<TAddress extends string = string>(
147
165
  rpc: Parameters<typeof fetchEncodedAccount>[0],
148
166
  address: Address<TAddress>,
@@ -153,15 +171,25 @@ export async function fetchGlobalConfig<TAddress extends string = string>(
153
171
  return maybeAccount;
154
172
  }
155
173
 
174
+ /**
175
+ * Fetches a `GlobalConfig` account, or the non-existing variant when the address holds no
176
+ * account or only lamports (see {@link accountIsCreated}).
177
+ * {@link decodeGlobalConfig} throws for a lamports-only account instead.
178
+ */
156
179
  export async function fetchMaybeGlobalConfig<TAddress extends string = string>(
157
180
  rpc: Parameters<typeof fetchEncodedAccount>[0],
158
181
  address: Address<TAddress>,
159
182
  config?: FetchAccountConfig,
160
183
  ): Promise<MaybeAccount<GlobalConfig, TAddress>> {
161
184
  const maybeAccount = await fetchEncodedAccount(rpc, address, config);
162
- return decodeGlobalConfig(maybeAccount);
185
+ return decodeGlobalConfig(
186
+ accountIsCreated(maybeAccount)
187
+ ? maybeAccount
188
+ : { address, exists: false },
189
+ );
163
190
  }
164
191
 
192
+ /** Fetches `GlobalConfig` accounts, throwing when any does not exist or only holds lamports. */
165
193
  export async function fetchAllGlobalConfig(
166
194
  rpc: Parameters<typeof fetchEncodedAccounts>[0],
167
195
  addresses: Array<Address>,
@@ -176,6 +204,11 @@ export async function fetchAllGlobalConfig(
176
204
  return maybeAccounts;
177
205
  }
178
206
 
207
+ /**
208
+ * Fetches `GlobalConfig` accounts, with the non-existing variant for each address that holds
209
+ * no account or only lamports (see {@link accountIsCreated}).
210
+ * {@link decodeGlobalConfig} throws for a lamports-only account instead.
211
+ */
179
212
  export async function fetchAllMaybeGlobalConfig(
180
213
  rpc: Parameters<typeof fetchEncodedAccounts>[0],
181
214
  addresses: Array<Address>,
@@ -183,6 +216,10 @@ export async function fetchAllMaybeGlobalConfig(
183
216
  ): Promise<MaybeAccount<GlobalConfig>[]> {
184
217
  const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
185
218
  return maybeAccounts.map((maybeAccount) =>
186
- decodeGlobalConfig(maybeAccount),
219
+ decodeGlobalConfig(
220
+ accountIsCreated(maybeAccount)
221
+ ? maybeAccount
222
+ : { address: maybeAccount.address, exists: false },
223
+ ),
187
224
  );
188
225
  }
@@ -7,7 +7,6 @@
7
7
  */
8
8
 
9
9
  export * from './altRegistry.js';
10
- export * from './creatorFeeConfig.js';
11
10
  export * from './feePreset.js';
12
11
  export * from './globalConfig.js';
13
12
  export * from './partnerConfig.js';