@send-fun/sdk 1.1.0 → 1.2.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 (50) hide show
  1. package/README.md +8 -9
  2. package/dist/index.cjs +526 -98
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +595 -93
  5. package/dist/index.d.cts.map +1 -1
  6. package/dist/index.d.mts +595 -93
  7. package/dist/index.d.mts.map +1 -1
  8. package/dist/index.mjs +526 -98
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +1 -1
  11. package/src/dex/generated/accounts/globalConfig.ts +39 -2
  12. package/src/dex/generated/accounts/pool.ts +41 -2
  13. package/src/dex/generated/accounts/rewardAccrual.ts +39 -2
  14. package/src/dex/generated/index.ts +1 -0
  15. package/src/dex/generated/shared/index.ts +52 -0
  16. package/src/dex/trade.ts +12 -13
  17. package/src/launchpad/create.ts +8 -8
  18. package/src/launchpad/generated/accounts/bondingCurve.ts +39 -2
  19. package/src/launchpad/generated/accounts/globalConfig.ts +39 -2
  20. package/src/launchpad/generated/accounts/rewardAccrual.ts +39 -2
  21. package/src/launchpad/generated/index.ts +1 -0
  22. package/src/launchpad/generated/shared/index.ts +52 -0
  23. package/src/launchpad/migrate.ts +1 -1
  24. package/src/launchpad/trade.ts +15 -15
  25. package/src/math/amm.ts +40 -33
  26. package/src/math/fee-decay.ts +3 -2
  27. package/src/math/fees.ts +4 -2
  28. package/src/math/internal.ts +1 -1
  29. package/src/nexus/fee-helpers.ts +5 -3
  30. package/src/nexus/generated/accounts/altRegistry.ts +41 -2
  31. package/src/nexus/generated/accounts/creatorFeeConfig.ts +39 -2
  32. package/src/nexus/generated/accounts/feePreset.ts +41 -2
  33. package/src/nexus/generated/accounts/globalConfig.ts +39 -2
  34. package/src/nexus/generated/accounts/partnerConfig.ts +39 -2
  35. package/src/nexus/generated/accounts/partnerMetadata.ts +39 -2
  36. package/src/nexus/generated/accounts/rewardState.ts +41 -2
  37. package/src/nexus/generated/accounts/stakingConfig.ts +39 -2
  38. package/src/nexus/generated/accounts/userRewardDebt.ts +39 -2
  39. package/src/nexus/generated/accounts/userStakePosition.ts +39 -2
  40. package/src/nexus/generated/index.ts +1 -0
  41. package/src/nexus/generated/shared/index.ts +52 -0
  42. package/src/nexus/staking.ts +45 -41
  43. package/src/platform.ts +2 -2
  44. package/src/transfer-fee.ts +19 -17
  45. package/src/utils/chunk.ts +1 -1
  46. package/src/utils/creator-hash.ts +6 -3
  47. package/src/utils/index.ts +2 -0
  48. package/src/utils/mint-info.ts +8 -7
  49. package/src/utils/partner.ts +1 -1
  50. package/src/utils/pda.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@send-fun/sdk",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Kit-native TypeScript SDK for send.fun",
5
5
  "keywords": [
6
6
  "dex",
@@ -40,6 +40,7 @@ import {
40
40
  type ReadonlyUint8Array,
41
41
  } from '@solana/kit';
42
42
  import { SEND_DEX_PROGRAM_ADDRESS } from '../programs/index.js';
43
+ import { accountIsCreated } from '../shared/index.js';
43
44
 
44
45
  export const GLOBAL_CONFIG_DISCRIMINATOR: ReadonlyUint8Array = new Uint8Array([
45
46
  149, 8, 156, 202, 160, 252, 176, 217,
@@ -107,9 +108,25 @@ export function getGlobalConfigCodec(): FixedSizeCodec<
107
108
  return combineCodec(getGlobalConfigEncoder(), getGlobalConfigDecoder());
108
109
  }
109
110
 
111
+ /**
112
+ * Decodes a `GlobalConfig` account, throwing when another program owns it or its discriminator
113
+ * does not match.
114
+ *
115
+ * Unlike {@link fetchMaybeGlobalConfig}, this throws for an address that only holds lamports rather
116
+ * than returning the non-existing variant: an account passed as existing must never come back as that
117
+ * variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
118
+ */
110
119
  export function decodeGlobalConfig<TAddress extends string = string>(
111
120
  encodedAccount: EncodedAccount<TAddress>,
112
121
  ): Account<GlobalConfig, TAddress>;
122
+ /**
123
+ * Decodes a `GlobalConfig` account, throwing when another program owns it or its discriminator
124
+ * does not match.
125
+ *
126
+ * Unlike {@link fetchMaybeGlobalConfig}, this throws for an address that only holds lamports rather
127
+ * than returning the non-existing variant: an account passed as existing must never come back as that
128
+ * variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
129
+ */
113
130
  export function decodeGlobalConfig<TAddress extends string = string>(
114
131
  encodedAccount: MaybeEncodedAccount<TAddress>,
115
132
  ): MaybeAccount<GlobalConfig, TAddress>;
@@ -140,6 +157,7 @@ export function decodeGlobalConfig<TAddress extends string = string>(
140
157
  );
141
158
  }
142
159
 
160
+ /** Fetches a `GlobalConfig` account, throwing when it does not exist or only holds lamports. */
143
161
  export async function fetchGlobalConfig<TAddress extends string = string>(
144
162
  rpc: Parameters<typeof fetchEncodedAccount>[0],
145
163
  address: Address<TAddress>,
@@ -150,15 +168,25 @@ export async function fetchGlobalConfig<TAddress extends string = string>(
150
168
  return maybeAccount;
151
169
  }
152
170
 
171
+ /**
172
+ * Fetches a `GlobalConfig` account, or the non-existing variant when the address holds no
173
+ * account or only lamports (see {@link accountIsCreated}).
174
+ * {@link decodeGlobalConfig} throws for a lamports-only account instead.
175
+ */
153
176
  export async function fetchMaybeGlobalConfig<TAddress extends string = string>(
154
177
  rpc: Parameters<typeof fetchEncodedAccount>[0],
155
178
  address: Address<TAddress>,
156
179
  config?: FetchAccountConfig,
157
180
  ): Promise<MaybeAccount<GlobalConfig, TAddress>> {
158
181
  const maybeAccount = await fetchEncodedAccount(rpc, address, config);
159
- return decodeGlobalConfig(maybeAccount);
182
+ return decodeGlobalConfig(
183
+ accountIsCreated(maybeAccount)
184
+ ? maybeAccount
185
+ : { address, exists: false },
186
+ );
160
187
  }
161
188
 
189
+ /** Fetches `GlobalConfig` accounts, throwing when any does not exist or only holds lamports. */
162
190
  export async function fetchAllGlobalConfig(
163
191
  rpc: Parameters<typeof fetchEncodedAccounts>[0],
164
192
  addresses: Array<Address>,
@@ -173,6 +201,11 @@ export async function fetchAllGlobalConfig(
173
201
  return maybeAccounts;
174
202
  }
175
203
 
204
+ /**
205
+ * Fetches `GlobalConfig` accounts, with the non-existing variant for each address that holds
206
+ * no account or only lamports (see {@link accountIsCreated}).
207
+ * {@link decodeGlobalConfig} throws for a lamports-only account instead.
208
+ */
176
209
  export async function fetchAllMaybeGlobalConfig(
177
210
  rpc: Parameters<typeof fetchEncodedAccounts>[0],
178
211
  addresses: Array<Address>,
@@ -180,7 +213,11 @@ export async function fetchAllMaybeGlobalConfig(
180
213
  ): Promise<MaybeAccount<GlobalConfig>[]> {
181
214
  const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
182
215
  return maybeAccounts.map((maybeAccount) =>
183
- decodeGlobalConfig(maybeAccount),
216
+ decodeGlobalConfig(
217
+ accountIsCreated(maybeAccount)
218
+ ? maybeAccount
219
+ : { address: maybeAccount.address, exists: false },
220
+ ),
184
221
  );
185
222
  }
186
223
 
@@ -42,6 +42,7 @@ import {
42
42
  type ReadonlyUint8Array,
43
43
  } from '@solana/kit';
44
44
  import { SEND_DEX_PROGRAM_ADDRESS } from '../programs/index.js';
45
+ import { accountIsCreated } from '../shared/index.js';
45
46
  import {
46
47
  getPoolStatusDecoder,
47
48
  getPoolStatusEncoder,
@@ -170,9 +171,25 @@ export function getPoolCodec(): FixedSizeCodec<PoolArgs, Pool> {
170
171
  return combineCodec(getPoolEncoder(), getPoolDecoder());
171
172
  }
172
173
 
174
+ /**
175
+ * Decodes a `Pool` account, throwing when another program owns it or its discriminator
176
+ * does not match.
177
+ *
178
+ * Unlike {@link fetchMaybePool}, this throws for an address that only holds lamports rather
179
+ * than returning the non-existing variant: an account passed as existing must never come back as that
180
+ * variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
181
+ */
173
182
  export function decodePool<TAddress extends string = string>(
174
183
  encodedAccount: EncodedAccount<TAddress>,
175
184
  ): Account<Pool, TAddress>;
185
+ /**
186
+ * Decodes a `Pool` account, throwing when another program owns it or its discriminator
187
+ * does not match.
188
+ *
189
+ * Unlike {@link fetchMaybePool}, this throws for an address that only holds lamports rather
190
+ * than returning the non-existing variant: an account passed as existing must never come back as that
191
+ * variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
192
+ */
176
193
  export function decodePool<TAddress extends string = string>(
177
194
  encodedAccount: MaybeEncodedAccount<TAddress>,
178
195
  ): MaybeAccount<Pool, TAddress>;
@@ -201,6 +218,7 @@ export function decodePool<TAddress extends string = string>(
201
218
  );
202
219
  }
203
220
 
221
+ /** Fetches a `Pool` account, throwing when it does not exist or only holds lamports. */
204
222
  export async function fetchPool<TAddress extends string = string>(
205
223
  rpc: Parameters<typeof fetchEncodedAccount>[0],
206
224
  address: Address<TAddress>,
@@ -211,15 +229,25 @@ export async function fetchPool<TAddress extends string = string>(
211
229
  return maybeAccount;
212
230
  }
213
231
 
232
+ /**
233
+ * Fetches a `Pool` account, or the non-existing variant when the address holds no
234
+ * account or only lamports (see {@link accountIsCreated}).
235
+ * {@link decodePool} throws for a lamports-only account instead.
236
+ */
214
237
  export async function fetchMaybePool<TAddress extends string = string>(
215
238
  rpc: Parameters<typeof fetchEncodedAccount>[0],
216
239
  address: Address<TAddress>,
217
240
  config?: FetchAccountConfig,
218
241
  ): Promise<MaybeAccount<Pool, TAddress>> {
219
242
  const maybeAccount = await fetchEncodedAccount(rpc, address, config);
220
- return decodePool(maybeAccount);
243
+ return decodePool(
244
+ accountIsCreated(maybeAccount)
245
+ ? maybeAccount
246
+ : { address, exists: false },
247
+ );
221
248
  }
222
249
 
250
+ /** Fetches `Pool` accounts, throwing when any does not exist or only holds lamports. */
223
251
  export async function fetchAllPool(
224
252
  rpc: Parameters<typeof fetchEncodedAccounts>[0],
225
253
  addresses: Array<Address>,
@@ -230,13 +258,24 @@ export async function fetchAllPool(
230
258
  return maybeAccounts;
231
259
  }
232
260
 
261
+ /**
262
+ * Fetches `Pool` accounts, with the non-existing variant for each address that holds
263
+ * no account or only lamports (see {@link accountIsCreated}).
264
+ * {@link decodePool} throws for a lamports-only account instead.
265
+ */
233
266
  export async function fetchAllMaybePool(
234
267
  rpc: Parameters<typeof fetchEncodedAccounts>[0],
235
268
  addresses: Array<Address>,
236
269
  config?: FetchAccountsConfig,
237
270
  ): Promise<MaybeAccount<Pool>[]> {
238
271
  const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
239
- return maybeAccounts.map((maybeAccount) => decodePool(maybeAccount));
272
+ return maybeAccounts.map((maybeAccount) =>
273
+ decodePool(
274
+ accountIsCreated(maybeAccount)
275
+ ? maybeAccount
276
+ : { address: maybeAccount.address, exists: false },
277
+ ),
278
+ );
240
279
  }
241
280
 
242
281
  export function getPoolSize(): number {
@@ -42,6 +42,7 @@ import {
42
42
  type ReadonlyUint8Array,
43
43
  } from '@solana/kit';
44
44
  import { SEND_DEX_PROGRAM_ADDRESS } from '../programs/index.js';
45
+ import { accountIsCreated } from '../shared/index.js';
45
46
 
46
47
  export const REWARD_ACCRUAL_DISCRIMINATOR: ReadonlyUint8Array = new Uint8Array([
47
48
  158, 244, 116, 30, 227, 132, 105, 182,
@@ -109,9 +110,25 @@ export function getRewardAccrualCodec(): FixedSizeCodec<
109
110
  return combineCodec(getRewardAccrualEncoder(), getRewardAccrualDecoder());
110
111
  }
111
112
 
113
+ /**
114
+ * Decodes a `RewardAccrual` account, throwing when another program owns it or its discriminator
115
+ * does not match.
116
+ *
117
+ * Unlike {@link fetchMaybeRewardAccrual}, this throws for an address that only holds lamports rather
118
+ * than returning the non-existing variant: an account passed as existing must never come back as that
119
+ * variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
120
+ */
112
121
  export function decodeRewardAccrual<TAddress extends string = string>(
113
122
  encodedAccount: EncodedAccount<TAddress>,
114
123
  ): Account<RewardAccrual, TAddress>;
124
+ /**
125
+ * Decodes a `RewardAccrual` account, throwing when another program owns it or its discriminator
126
+ * does not match.
127
+ *
128
+ * Unlike {@link fetchMaybeRewardAccrual}, this throws for an address that only holds lamports rather
129
+ * than returning the non-existing variant: an account passed as existing must never come back as that
130
+ * variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
131
+ */
115
132
  export function decodeRewardAccrual<TAddress extends string = string>(
116
133
  encodedAccount: MaybeEncodedAccount<TAddress>,
117
134
  ): MaybeAccount<RewardAccrual, TAddress>;
@@ -142,6 +159,7 @@ export function decodeRewardAccrual<TAddress extends string = string>(
142
159
  );
143
160
  }
144
161
 
162
+ /** Fetches a `RewardAccrual` account, throwing when it does not exist or only holds lamports. */
145
163
  export async function fetchRewardAccrual<TAddress extends string = string>(
146
164
  rpc: Parameters<typeof fetchEncodedAccount>[0],
147
165
  address: Address<TAddress>,
@@ -152,15 +170,25 @@ export async function fetchRewardAccrual<TAddress extends string = string>(
152
170
  return maybeAccount;
153
171
  }
154
172
 
173
+ /**
174
+ * Fetches a `RewardAccrual` account, or the non-existing variant when the address holds no
175
+ * account or only lamports (see {@link accountIsCreated}).
176
+ * {@link decodeRewardAccrual} throws for a lamports-only account instead.
177
+ */
155
178
  export async function fetchMaybeRewardAccrual<TAddress extends string = string>(
156
179
  rpc: Parameters<typeof fetchEncodedAccount>[0],
157
180
  address: Address<TAddress>,
158
181
  config?: FetchAccountConfig,
159
182
  ): Promise<MaybeAccount<RewardAccrual, TAddress>> {
160
183
  const maybeAccount = await fetchEncodedAccount(rpc, address, config);
161
- return decodeRewardAccrual(maybeAccount);
184
+ return decodeRewardAccrual(
185
+ accountIsCreated(maybeAccount)
186
+ ? maybeAccount
187
+ : { address, exists: false },
188
+ );
162
189
  }
163
190
 
191
+ /** Fetches `RewardAccrual` accounts, throwing when any does not exist or only holds lamports. */
164
192
  export async function fetchAllRewardAccrual(
165
193
  rpc: Parameters<typeof fetchEncodedAccounts>[0],
166
194
  addresses: Array<Address>,
@@ -175,6 +203,11 @@ export async function fetchAllRewardAccrual(
175
203
  return maybeAccounts;
176
204
  }
177
205
 
206
+ /**
207
+ * Fetches `RewardAccrual` accounts, with the non-existing variant for each address that holds
208
+ * no account or only lamports (see {@link accountIsCreated}).
209
+ * {@link decodeRewardAccrual} throws for a lamports-only account instead.
210
+ */
178
211
  export async function fetchAllMaybeRewardAccrual(
179
212
  rpc: Parameters<typeof fetchEncodedAccounts>[0],
180
213
  addresses: Array<Address>,
@@ -182,7 +215,11 @@ export async function fetchAllMaybeRewardAccrual(
182
215
  ): Promise<MaybeAccount<RewardAccrual>[]> {
183
216
  const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
184
217
  return maybeAccounts.map((maybeAccount) =>
185
- decodeRewardAccrual(maybeAccount),
218
+ decodeRewardAccrual(
219
+ accountIsCreated(maybeAccount)
220
+ ? maybeAccount
221
+ : { address: maybeAccount.address, exists: false },
222
+ ),
186
223
  );
187
224
  }
188
225
 
@@ -13,4 +13,5 @@ export * from './instructions/index.js';
13
13
  export * from './pdas/index.js';
14
14
  export * from './plugins/index.js';
15
15
  export * from './programs/index.js';
16
+ export * from './shared/index.js';
16
17
  export * from './types/index.js';
@@ -0,0 +1,52 @@
1
+ /**
2
+ * This code was AUTOGENERATED using the Codama library.
3
+ * Please DO NOT EDIT THIS FILE, instead use visitors
4
+ * to add features, then rerun Codama to update it.
5
+ *
6
+ * @see https://github.com/codama-idl/codama
7
+ */
8
+
9
+ import type { Address, EncodedAccount, MaybeEncodedAccount } from '@solana/kit';
10
+
11
+ /** The System Program, which owns every address nobody has created an account at yet. */
12
+ const SYSTEM_PROGRAM_ADDRESS =
13
+ '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>;
14
+
15
+ /**
16
+ * Whether an encoded account was created on-chain, rather than merely sent lamports.
17
+ *
18
+ * Anyone can send lamports to an address before its account is created, which leaves it on-chain,
19
+ * owned by the System Program, with no data. This returns `false` for that shape, as it does for an
20
+ * account that does not exist. Any other existing account counts as created, whoever owns it; the
21
+ * `decode*` functions still check the owner.
22
+ *
23
+ * The generated `fetchMaybe*` and `fetchAllMaybe*` helpers apply this rule themselves. Apply it to
24
+ * accounts you fetch or receive another way (subscriptions, batch loaders, caches) before passing
25
+ * them to a `decode*` function, which throws for a lamports-only account: an account passed to it as
26
+ * existing must never come back as the non-existing variant typed as an `Account`.
27
+ *
28
+ * Only a `true` result narrows the type. A lamports-only account is still `exists: true` at runtime,
29
+ * so a `false` result leaves the type unchanged instead of narrowing it to the non-existing variant.
30
+ * The `uncreated` property in the narrowed type is never present; it exists only to prevent that.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * const maybeAccount = decodeMyAccount(
35
+ * accountIsCreated(encodedAccount) ? encodedAccount : { address: encodedAccount.address, exists: false },
36
+ * );
37
+ * ```
38
+ */
39
+ export function accountIsCreated<
40
+ TAccount extends EncodedAccount | MaybeEncodedAccount,
41
+ >(
42
+ account: TAccount,
43
+ ): account is Exclude<TAccount, { readonly exists: false }> & {
44
+ readonly uncreated?: never;
45
+ } {
46
+ const maybeAccount: EncodedAccount | MaybeEncodedAccount = account;
47
+ if ('exists' in maybeAccount && !maybeAccount.exists) return false;
48
+ return !(
49
+ maybeAccount.programAddress === SYSTEM_PROGRAM_ADDRESS &&
50
+ maybeAccount.data.length === 0
51
+ );
52
+ }
package/src/dex/trade.ts CHANGED
@@ -19,7 +19,7 @@ export interface DexTradeParams {
19
19
  partner: PartnerInput;
20
20
  platformConfig: Address;
21
21
  quoteTokenProgram: Address;
22
- /** Token-2022 schedule for the epoch the trade lands in; stale or missing skews the slippage bounds. */
22
+ /** The quote mint's transfer fee for the epoch the trade lands in. A missing or old fee gives wrong slippage bounds. */
23
23
  quoteFee?: MintFee;
24
24
  baseFee?: MintFee;
25
25
  /** {@inheritDoc DexInstructionParams.userQuoteAccount} */
@@ -36,16 +36,15 @@ export interface DexInstructionParams {
36
36
  partner: PartnerInput;
37
37
  platformConfig: Address;
38
38
  quoteTokenProgram: Address;
39
- /** Any user-owned quote-mint account. Defaults to the ATA, created mid-trade
40
- * if missing (payer funds rent), which only rescues a sell. For WSOL,
41
- * a throwaway `createAccountWithSeed` account is cheaper. */
39
+ /** Any quote-mint token account that `user` owns. Defaults to the user's ATA.
40
+ * The program creates a missing ATA, and `payer` pays the rent. */
42
41
  userQuoteAccount?: Address;
43
- /** Any user-owned base-mint account. Defaults to the ATA, created mid-trade
44
- * if missing (payer funds rent), which only rescues a buy. */
42
+ /** Any base-mint token account that `user` owns. Defaults to the user's ATA.
43
+ * The program creates a missing ATA, and `payer` pays the rent. */
45
44
  userBaseAccount?: Address;
46
45
  }
47
46
 
48
- /** `baseAmountOut` is net to the buyer: the program reads `amount` as `base_to_user`. */
47
+ /** `baseAmountOut` is the base the buyer receives, after the base mint's transfer fee. */
49
48
  export async function buyExactOut(
50
49
  params: DexTradeParams & { baseAmountOut: bigint },
51
50
  ): Promise<{ instruction: Instruction; quote: BuyQuote }> {
@@ -57,7 +56,7 @@ export async function buyExactOut(
57
56
  quoteFee: params.quoteFee,
58
57
  baseFee: params.baseFee,
59
58
  });
60
- // The cap is measured on the gross the buyer sends, quote transfer fee included.
59
+ // The program checks `maxAmountIn` against the gross the buyer sends, quote transfer fee included.
61
60
  const maxQuoteIn = amm.calculateSlippageUp(
62
61
  quote.quoteFromUser,
63
62
  params.slippageBps,
@@ -83,7 +82,7 @@ export async function buyExactIn(
83
82
  quoteFee: params.quoteFee,
84
83
  baseFee: params.baseFee,
85
84
  });
86
- // The floor is measured on the buyer's credit, not the vault's debit.
85
+ // The program checks `minAmountOut` against what the buyer receives, not the vault's debit.
87
86
  const minBaseOut = amm.calculateSlippageDown(
88
87
  quote.baseToUser,
89
88
  params.slippageBps,
@@ -109,7 +108,7 @@ export async function sellExactIn(
109
108
  quoteFee: params.quoteFee,
110
109
  baseFee: params.baseFee,
111
110
  });
112
- // The floor is measured on the seller's credit, not what leaves the vault.
111
+ // The program checks `minAmountOut` against what the seller receives, not the vault's debit.
113
112
  const minQuoteOut = amm.calculateSlippageDown(
114
113
  quote.quoteToUser,
115
114
  params.slippageBps,
@@ -124,7 +123,7 @@ export async function sellExactIn(
124
123
  };
125
124
  }
126
125
 
127
- /** `quoteAmountOut` is net to the seller: the program reads `amount` as `quote_to_user`. */
126
+ /** `quoteAmountOut` is the quote the seller receives, after the quote mint's transfer fee. */
128
127
  export async function sellExactOut(
129
128
  params: DexTradeParams & { quoteAmountOut: bigint },
130
129
  ): Promise<{ instruction: Instruction; quote: SellQuote }> {
@@ -136,7 +135,7 @@ export async function sellExactOut(
136
135
  quoteFee: params.quoteFee,
137
136
  baseFee: params.baseFee,
138
137
  });
139
- // The cap is measured on the gross the seller sends, base transfer fee included.
138
+ // The program checks `maxAmountIn` against the gross the seller sends, base transfer fee included.
140
139
  const maxBaseIn = amm.calculateSlippageUp(
141
140
  quote.baseFromUser,
142
141
  params.slippageBps,
@@ -204,7 +203,7 @@ function resolveSharedAccounts(params: DexInstructionParams) {
204
203
  partner: params.partner,
205
204
  platformConfig: params.platformConfig,
206
205
  quoteTokenProgram: params.quoteTokenProgram,
207
- // Left undefined so the generated client derives the ATA itself.
206
+ // When omitted, the generated client derives the ATA.
208
207
  ...(params.userQuoteAccount !== undefined && {
209
208
  userQuoteAccount: params.userQuoteAccount,
210
209
  }),
@@ -15,8 +15,8 @@ export interface CreateTokenParams {
15
15
  name: string;
16
16
  symbol: string;
17
17
  uri: string;
18
- /** An enabled nexus auth platform (e.g. "wallet"), max 32 bytes; hashed with
19
- * `creatorId` into the creator fee identity. Unrelated to `platformConfig`. */
18
+ /** An enabled nexus auth platform, for example "wallet", of at most 32 bytes. It is hashed
19
+ * with `creatorId` into the creator fee identity. It is not `platformConfig`. */
20
20
  creatorPlatform: string;
21
21
  creatorId: string;
22
22
  partner: PartnerInput;
@@ -30,11 +30,11 @@ export interface CreateAndBuyParams extends CreateTokenParams {
30
30
  slippageBps: number;
31
31
  initialVirtualQuoteReserves: bigint;
32
32
  initialVirtualBaseReserves: bigint;
33
- /** `globalConfig.initialRealBaseReserves`: caps the appended buy. */
33
+ /** `globalConfig.initialRealBaseReserves`. Caps the buy. */
34
34
  initialRealBaseReserves: bigint;
35
- /** Quote mint's Token-2022 schedule for the launch epoch; without it `minAmountOut`
36
- * ignores the mint's cut, and a cut above `slippageBps` can revert the buy. No
37
- * `baseFee`: this transaction creates the base mint without the extension. */
35
+ /** The quote mint's transfer fee for the epoch the launch lands in. Without it, `minAmountOut`
36
+ * ignores the mint's cut, and a cut above `slippageBps` can make the buy fail. The new base
37
+ * mint has no transfer fee. */
38
38
  quoteFee?: MintFee;
39
39
  }
40
40
 
@@ -55,7 +55,7 @@ async function buildCreateTokenInstructionWithHash(
55
55
  creatorHash,
56
56
  quoteMint: params.quoteMint,
57
57
  });
58
- // The creation fee is native SOL from `user`; the client derives the WSOL staking vault.
58
+ // `user` pays the creation fee in native SOL. The generated client fills in the WSOL staking vault.
59
59
  return getCreateTokenInstructionAsync({
60
60
  user: params.user,
61
61
  payer: params.payer ?? params.user,
@@ -98,7 +98,7 @@ export async function buildCreateAndBuyInstructions(
98
98
  virtualQuoteReserves: params.initialVirtualQuoteReserves,
99
99
  virtualBaseReserves: params.initialVirtualBaseReserves,
100
100
  realBaseReserves: params.initialRealBaseReserves,
101
- // The curve was just stamped with this same key.
101
+ // `create_token` sets the curve's `platformConfig` to this key.
102
102
  platformConfig: params.platformConfig,
103
103
  feeBps: params.feeBps,
104
104
  slippageBps: params.slippageBps,
@@ -42,6 +42,7 @@ import {
42
42
  type ReadonlyUint8Array,
43
43
  } from '@solana/kit';
44
44
  import { SEND_LAUNCHPAD_PROGRAM_ADDRESS } from '../programs/index.js';
45
+ import { accountIsCreated } from '../shared/index.js';
45
46
  import {
46
47
  getBondingCurveStatusDecoder,
47
48
  getBondingCurveStatusEncoder,
@@ -191,9 +192,25 @@ export function getBondingCurveCodec(): FixedSizeCodec<
191
192
  return combineCodec(getBondingCurveEncoder(), getBondingCurveDecoder());
192
193
  }
193
194
 
195
+ /**
196
+ * Decodes a `BondingCurve` account, throwing when another program owns it or its discriminator
197
+ * does not match.
198
+ *
199
+ * Unlike {@link fetchMaybeBondingCurve}, this throws for an address that only holds lamports rather
200
+ * than returning the non-existing variant: an account passed as existing must never come back as that
201
+ * variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
202
+ */
194
203
  export function decodeBondingCurve<TAddress extends string = string>(
195
204
  encodedAccount: EncodedAccount<TAddress>,
196
205
  ): Account<BondingCurve, TAddress>;
206
+ /**
207
+ * Decodes a `BondingCurve` account, throwing when another program owns it or its discriminator
208
+ * does not match.
209
+ *
210
+ * Unlike {@link fetchMaybeBondingCurve}, this throws for an address that only holds lamports rather
211
+ * than returning the non-existing variant: an account passed as existing must never come back as that
212
+ * variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
213
+ */
197
214
  export function decodeBondingCurve<TAddress extends string = string>(
198
215
  encodedAccount: MaybeEncodedAccount<TAddress>,
199
216
  ): MaybeAccount<BondingCurve, TAddress>;
@@ -224,6 +241,7 @@ export function decodeBondingCurve<TAddress extends string = string>(
224
241
  );
225
242
  }
226
243
 
244
+ /** Fetches a `BondingCurve` account, throwing when it does not exist or only holds lamports. */
227
245
  export async function fetchBondingCurve<TAddress extends string = string>(
228
246
  rpc: Parameters<typeof fetchEncodedAccount>[0],
229
247
  address: Address<TAddress>,
@@ -234,15 +252,25 @@ export async function fetchBondingCurve<TAddress extends string = string>(
234
252
  return maybeAccount;
235
253
  }
236
254
 
255
+ /**
256
+ * Fetches a `BondingCurve` account, or the non-existing variant when the address holds no
257
+ * account or only lamports (see {@link accountIsCreated}).
258
+ * {@link decodeBondingCurve} throws for a lamports-only account instead.
259
+ */
237
260
  export async function fetchMaybeBondingCurve<TAddress extends string = string>(
238
261
  rpc: Parameters<typeof fetchEncodedAccount>[0],
239
262
  address: Address<TAddress>,
240
263
  config?: FetchAccountConfig,
241
264
  ): Promise<MaybeAccount<BondingCurve, TAddress>> {
242
265
  const maybeAccount = await fetchEncodedAccount(rpc, address, config);
243
- return decodeBondingCurve(maybeAccount);
266
+ return decodeBondingCurve(
267
+ accountIsCreated(maybeAccount)
268
+ ? maybeAccount
269
+ : { address, exists: false },
270
+ );
244
271
  }
245
272
 
273
+ /** Fetches `BondingCurve` accounts, throwing when any does not exist or only holds lamports. */
246
274
  export async function fetchAllBondingCurve(
247
275
  rpc: Parameters<typeof fetchEncodedAccounts>[0],
248
276
  addresses: Array<Address>,
@@ -257,6 +285,11 @@ export async function fetchAllBondingCurve(
257
285
  return maybeAccounts;
258
286
  }
259
287
 
288
+ /**
289
+ * Fetches `BondingCurve` accounts, with the non-existing variant for each address that holds
290
+ * no account or only lamports (see {@link accountIsCreated}).
291
+ * {@link decodeBondingCurve} throws for a lamports-only account instead.
292
+ */
260
293
  export async function fetchAllMaybeBondingCurve(
261
294
  rpc: Parameters<typeof fetchEncodedAccounts>[0],
262
295
  addresses: Array<Address>,
@@ -264,7 +297,11 @@ export async function fetchAllMaybeBondingCurve(
264
297
  ): Promise<MaybeAccount<BondingCurve>[]> {
265
298
  const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
266
299
  return maybeAccounts.map((maybeAccount) =>
267
- decodeBondingCurve(maybeAccount),
300
+ decodeBondingCurve(
301
+ accountIsCreated(maybeAccount)
302
+ ? maybeAccount
303
+ : { address: maybeAccount.address, exists: false },
304
+ ),
268
305
  );
269
306
  }
270
307
 
@@ -42,6 +42,7 @@ import {
42
42
  type ReadonlyUint8Array,
43
43
  } from '@solana/kit';
44
44
  import { SEND_LAUNCHPAD_PROGRAM_ADDRESS } from '../programs/index.js';
45
+ import { accountIsCreated } from '../shared/index.js';
45
46
 
46
47
  export const GLOBAL_CONFIG_DISCRIMINATOR: ReadonlyUint8Array = new Uint8Array([
47
48
  149, 8, 156, 202, 160, 252, 176, 217,
@@ -129,9 +130,25 @@ export function getGlobalConfigCodec(): FixedSizeCodec<
129
130
  return combineCodec(getGlobalConfigEncoder(), getGlobalConfigDecoder());
130
131
  }
131
132
 
133
+ /**
134
+ * Decodes a `GlobalConfig` account, throwing when another program owns it or its discriminator
135
+ * does not match.
136
+ *
137
+ * Unlike {@link fetchMaybeGlobalConfig}, this throws for an address that only holds lamports rather
138
+ * than returning the non-existing variant: an account passed as existing must never come back as that
139
+ * variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
140
+ */
132
141
  export function decodeGlobalConfig<TAddress extends string = string>(
133
142
  encodedAccount: EncodedAccount<TAddress>,
134
143
  ): Account<GlobalConfig, TAddress>;
144
+ /**
145
+ * Decodes a `GlobalConfig` account, throwing when another program owns it or its discriminator
146
+ * does not match.
147
+ *
148
+ * Unlike {@link fetchMaybeGlobalConfig}, this throws for an address that only holds lamports rather
149
+ * than returning the non-existing variant: an account passed as existing must never come back as that
150
+ * variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
151
+ */
135
152
  export function decodeGlobalConfig<TAddress extends string = string>(
136
153
  encodedAccount: MaybeEncodedAccount<TAddress>,
137
154
  ): MaybeAccount<GlobalConfig, TAddress>;
@@ -162,6 +179,7 @@ export function decodeGlobalConfig<TAddress extends string = string>(
162
179
  );
163
180
  }
164
181
 
182
+ /** Fetches a `GlobalConfig` account, throwing when it does not exist or only holds lamports. */
165
183
  export async function fetchGlobalConfig<TAddress extends string = string>(
166
184
  rpc: Parameters<typeof fetchEncodedAccount>[0],
167
185
  address: Address<TAddress>,
@@ -172,15 +190,25 @@ export async function fetchGlobalConfig<TAddress extends string = string>(
172
190
  return maybeAccount;
173
191
  }
174
192
 
193
+ /**
194
+ * Fetches a `GlobalConfig` account, or the non-existing variant when the address holds no
195
+ * account or only lamports (see {@link accountIsCreated}).
196
+ * {@link decodeGlobalConfig} throws for a lamports-only account instead.
197
+ */
175
198
  export async function fetchMaybeGlobalConfig<TAddress extends string = string>(
176
199
  rpc: Parameters<typeof fetchEncodedAccount>[0],
177
200
  address: Address<TAddress>,
178
201
  config?: FetchAccountConfig,
179
202
  ): Promise<MaybeAccount<GlobalConfig, TAddress>> {
180
203
  const maybeAccount = await fetchEncodedAccount(rpc, address, config);
181
- return decodeGlobalConfig(maybeAccount);
204
+ return decodeGlobalConfig(
205
+ accountIsCreated(maybeAccount)
206
+ ? maybeAccount
207
+ : { address, exists: false },
208
+ );
182
209
  }
183
210
 
211
+ /** Fetches `GlobalConfig` accounts, throwing when any does not exist or only holds lamports. */
184
212
  export async function fetchAllGlobalConfig(
185
213
  rpc: Parameters<typeof fetchEncodedAccounts>[0],
186
214
  addresses: Array<Address>,
@@ -195,6 +223,11 @@ export async function fetchAllGlobalConfig(
195
223
  return maybeAccounts;
196
224
  }
197
225
 
226
+ /**
227
+ * Fetches `GlobalConfig` accounts, with the non-existing variant for each address that holds
228
+ * no account or only lamports (see {@link accountIsCreated}).
229
+ * {@link decodeGlobalConfig} throws for a lamports-only account instead.
230
+ */
198
231
  export async function fetchAllMaybeGlobalConfig(
199
232
  rpc: Parameters<typeof fetchEncodedAccounts>[0],
200
233
  addresses: Array<Address>,
@@ -202,7 +235,11 @@ export async function fetchAllMaybeGlobalConfig(
202
235
  ): Promise<MaybeAccount<GlobalConfig>[]> {
203
236
  const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
204
237
  return maybeAccounts.map((maybeAccount) =>
205
- decodeGlobalConfig(maybeAccount),
238
+ decodeGlobalConfig(
239
+ accountIsCreated(maybeAccount)
240
+ ? maybeAccount
241
+ : { address: maybeAccount.address, exists: false },
242
+ ),
206
243
  );
207
244
  }
208
245