@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
@@ -1,5 +1,5 @@
1
- // Hand-parsed so consumers don't inherit `@solana-program/token-2022`. The programs
2
- // price every leg on what lands after the mint's cut; a quote without it fails slippage.
1
+ // Parses Token-2022 mint data by hand. The SDK does not depend on
2
+ // `@solana-program/token-2022`.
3
3
 
4
4
  import {
5
5
  getAddressDecoder,
@@ -20,8 +20,8 @@ import { fetchInChunks } from './utils/chunk.js';
20
20
 
21
21
  const MINT_BASE_LENGTH = 82;
22
22
 
23
- // Token-2022 pads the base mint to the 165-byte token-account length so the two
24
- // never share a prefix, then stamps the type byte; TLV entries follow it.
23
+ // Token-2022 pads the base mint to 165 bytes, the token-account length. The
24
+ // account-type byte follows, then the TLV entries.
25
25
  const ACCOUNT_TYPE_OFFSET = 165;
26
26
 
27
27
  const ACCOUNT_TYPE_MINT = 1;
@@ -58,7 +58,7 @@ export interface TransferFeeEntry {
58
58
  }
59
59
 
60
60
  export interface TransferFeeConfig {
61
- /** `undefined` when the authority is unset: the schedule is frozen forever. */
61
+ /** `undefined` if the config has no authority. Then the schedule cannot change. */
62
62
  readonly authority: Address | undefined;
63
63
  readonly older: TransferFeeEntry;
64
64
  readonly newer: TransferFeeEntry;
@@ -80,13 +80,14 @@ function readEntry(view: DataView, start: number): TransferFeeEntry {
80
80
  };
81
81
  }
82
82
 
83
- /** `owner` is the account's program. `undefined` means no fee extension; anything
84
- * unparseable throws `RangeError` rather than pricing a charging mint free. */
83
+ /** Decodes the `TransferFeeConfig` extension of a mint. `owner` is the program that owns the account.
84
+ * Returns `undefined` if the mint has no such extension. Throws `RangeError` if `owner` is not a
85
+ * token program or `data` is not a valid mint. */
85
86
  export function decodeTransferFeeConfig(
86
87
  data: Uint8Array,
87
88
  owner: Address,
88
89
  ): TransferFeeConfig | undefined {
89
- // Classic SPL can never gain an extension, so this result is permanent.
90
+ // Classic SPL mints have no extensions.
90
91
  if (owner === TOKEN_PROGRAM_ADDRESS) return undefined;
91
92
  if (owner !== TOKEN_2022_PROGRAM_ADDRESS) {
92
93
  throw new RangeError(
@@ -94,14 +95,14 @@ export function decodeTransferFeeConfig(
94
95
  );
95
96
  }
96
97
 
97
- // Token-2022 leaves a mint with no extensions unpadded at 82 bytes.
98
+ // A Token-2022 mint with no extensions is 82 bytes.
98
99
  if (data.length === MINT_BASE_LENGTH) return undefined;
99
100
  if (data.length < TLV_START) {
100
101
  throw new RangeError(
101
102
  `decodeTransferFeeConfig: a Token-2022 mint holds ${data.length} bytes, expected ${MINT_BASE_LENGTH} or at least ${TLV_START}`,
102
103
  );
103
104
  }
104
- // Token accounts share this TLV layout with different extension types.
105
+ // Token accounts use the same TLV layout.
105
106
  if (data[ACCOUNT_TYPE_OFFSET] !== ACCOUNT_TYPE_MINT) {
106
107
  throw new RangeError(
107
108
  `decodeTransferFeeConfig: account type ${data[ACCOUNT_TYPE_OFFSET]} is not a mint`,
@@ -112,7 +113,7 @@ export function decodeTransferFeeConfig(
112
113
 
113
114
  let offset = TLV_START;
114
115
  while (offset < data.length) {
115
- // Too short for a type, or a zero type: trailing slack, as SPL reads it.
116
+ // SPL reads a tail too short for a type, or a zero type, as the end of the list.
116
117
  if (offset + TLV_TYPE > data.length) return undefined;
117
118
  const extensionType = view.getUint16(offset, true);
118
119
  if (extensionType === UNINITIALIZED_TYPE) return undefined;
@@ -133,7 +134,7 @@ export function decodeTransferFeeConfig(
133
134
  }
134
135
 
135
136
  if (extensionType === TRANSFER_FEE_CONFIG_TYPE) {
136
- // Exactly 108 or throw: the extension never writes a wider config.
137
+ // The extension is always 108 bytes.
137
138
  if (length !== TRANSFER_FEE_CONFIG_LENGTH) {
138
139
  throw new RangeError(
139
140
  `decodeTransferFeeConfig: TransferFeeConfig holds ${length} bytes, expected ${TRANSFER_FEE_CONFIG_LENGTH}`,
@@ -152,7 +153,7 @@ export function decodeTransferFeeConfig(
152
153
  return undefined;
153
154
  }
154
155
 
155
- /** Mirrors SPL `get_epoch_fee`: the newer entry is live from its own epoch on. */
156
+ /** Returns the fee for `epoch`, as SPL `get_epoch_fee` does. The newer entry applies from its own epoch on. */
156
157
  export function transferFeeAtEpoch(
157
158
  config: TransferFeeConfig,
158
159
  epoch: bigint,
@@ -170,8 +171,8 @@ export function mintFeeAtEpoch(
170
171
  return config === undefined ? undefined : transferFeeAtEpoch(config, epoch);
171
172
  }
172
173
 
173
- // Omit the config, never `{ commitment: undefined }`: Kit strips the falsy key and
174
- // the server's `finalized` wins, where an absent key gets the client's default.
174
+ // Omit the config, not `{ commitment: undefined }`. Kit deletes an undefined key, so the
175
+ // server default `finalized` applies. With no config, the client default applies.
175
176
  function currentEpoch(
176
177
  rpc: Rpc<GetEpochInfoApi>,
177
178
  commitment: Commitment | undefined,
@@ -182,8 +183,9 @@ function currentEpoch(
182
183
  .then((info) => info.epoch);
183
184
  }
184
185
 
185
- /** A missing account throws, never reads as fee-free. Valid only for `epoch`, which defaults to
186
- * the cluster's (a second call). */
186
+ /** Reads the transfer fee of each mint for `epoch`. The result is valid only for that epoch.
187
+ * `epoch` defaults to the current cluster epoch, read with `getEpochInfo`. A mint without a
188
+ * transfer fee maps to `undefined`. Throws if a mint account is missing or does not decode. */
187
189
  export async function fetchMintFees(
188
190
  rpc: Rpc<GetMultipleAccountsApi & GetEpochInfoApi>,
189
191
  mints: readonly Address[],
@@ -3,7 +3,7 @@ import type { Address } from '@solana/kit';
3
3
  /** RPCs reject a `getMultipleAccounts` call past this many addresses. */
4
4
  const MAX_ACCOUNTS_PER_REQUEST = 100;
5
5
 
6
- /** Splits a multi-account read into 100-address calls; results follow `addresses` order. */
6
+ /** Calls `read` once per 100 addresses. Results follow `addresses` order. */
7
7
  export async function fetchInChunks<T>(
8
8
  addresses: readonly Address[],
9
9
  read: (chunk: Address[]) => Promise<readonly T[]>,
@@ -3,7 +3,8 @@ import type { Address, ReadonlyUint8Array } from '@solana/kit';
3
3
 
4
4
  const MAX_CREATOR_PLATFORM_LEN = 32;
5
5
 
6
- /** SHA-256 of the LE-u32-length-prefixed platform and id. Frozen: it seeds live PDAs. */
6
+ /** Returns the SHA-256 of `creatorPlatform` and `creatorId`, each prefixed with its byte length as a
7
+ * little-endian u32. Throws `RangeError` if `creatorPlatform` is more than 32 bytes. */
7
8
  export async function creatorHashFromId(
8
9
  creatorPlatform: string,
9
10
  creatorId: string,
@@ -34,7 +35,8 @@ export async function creatorHashFromId(
34
35
  return addressDecoder.decode(hash);
35
36
  }
36
37
 
37
- /** NUL-pads `text` to `length` bytes, the on-chain `CreatorFeeConfig.platformId` form; throws RangeError if longer. */
38
+ /** Pads `text` with NUL bytes to `length` bytes, the form of `CreatorFeeConfig.platformId`.
39
+ * Throws `RangeError` if `text` is longer. */
38
40
  export function encodeCreatorId(text: string, length: number): Uint8Array {
39
41
  const bytes = new TextEncoder().encode(text);
40
42
  if (bytes.length > length) {
@@ -47,7 +49,8 @@ export function encodeCreatorId(text: string, length: number): Uint8Array {
47
49
  return out;
48
50
  }
49
51
 
50
- /** Strips NUL padding. Pass this, never the padded array, to {@link creatorHashFromId}: the hash is length-prefixed. */
52
+ /** Removes the trailing NUL bytes and decodes the rest as UTF-8. Pass the result, not the padded
53
+ * bytes, to {@link creatorHashFromId}. */
51
54
  export function decodeCreatorId(bytes: ReadonlyUint8Array): string {
52
55
  let end = bytes.length;
53
56
  while (end > 0 && bytes[end - 1] === 0) {
@@ -1,3 +1,5 @@
1
+ // The three programs generate the same function.
2
+ export { accountIsCreated } from '../nexus/generated/shared/index.js';
1
3
  export {
2
4
  creatorHashFromId,
3
5
  decodeCreatorId,
@@ -1,5 +1,4 @@
1
- // Read off the mint, never tabulated: a stale `tokenProgram` derives an ATA the
2
- // transfer cannot reach.
1
+ // Reads the token program from the mint account. Do not hard-code it.
3
2
  import {
4
3
  getBase64Encoder,
5
4
  type Address,
@@ -15,14 +14,14 @@ import {
15
14
  } from '../constants.js';
16
15
  import { fetchInChunks } from './chunk.js';
17
16
 
18
- // Both token programs share the base mint: 36-byte `COption<Pubkey>` authority,
17
+ // Both token programs use the same base mint: 36-byte `COption<Pubkey>` authority,
19
18
  // u64 supply, then decimals.
20
19
  const MINT_BASE_LENGTH = 82;
21
20
  const MINT_DECIMALS_OFFSET = 44;
22
21
 
23
22
  export interface QuoteMintInfo {
24
23
  mint: Address;
25
- /** Immutable: safe to cache. */
24
+ /** Cannot change after the mint is created. */
26
25
  decimals: number;
27
26
  /** The account's owner: `TOKEN_PROGRAM_ADDRESS` or `TOKEN_2022_PROGRAM_ADDRESS`. */
28
27
  tokenProgram: Address;
@@ -41,7 +40,7 @@ function decodeMintInfo(
41
40
  `fetchQuoteMintInfo: ${mint} is owned by ${owner}, which is not a token program`,
42
41
  );
43
42
  }
44
- // A floor, not an equality: Token-2022 mints with extensions run longer.
43
+ // A minimum: Token-2022 mints with extensions are longer.
45
44
  if (data.length < MINT_BASE_LENGTH) {
46
45
  throw new Error(
47
46
  `fetchQuoteMintInfo: ${mint} holds ${data.length} bytes, too short for a mint`,
@@ -50,7 +49,8 @@ function decodeMintInfo(
50
49
  return { mint, decimals: data[MINT_DECIMALS_OFFSET], tokenProgram: owner };
51
50
  }
52
51
 
53
- /** Throws when the mint does not exist or is not owned by a token program. */
52
+ /** Reads the decimals and token program of `mint`. Throws if the account does not exist, a token
53
+ * program does not own it, or it is too short for a mint. */
54
54
  export async function fetchQuoteMintInfo(
55
55
  rpc: Rpc<GetAccountInfoApi>,
56
56
  mint: Address,
@@ -71,7 +71,8 @@ export async function fetchQuoteMintInfo(
71
71
  return decodeMintInfo(mint, data, value.owner);
72
72
  }
73
73
 
74
- /** One round trip per 100 mints; results follow `mints` order. */
74
+ /** Reads the decimals and token program of each mint. Results follow `mints` order. Throws as
75
+ * {@link fetchQuoteMintInfo} does. */
75
76
  export async function fetchQuoteMintInfos(
76
77
  rpc: Rpc<GetMultipleAccountsApi>,
77
78
  mints: readonly Address[],
@@ -1,5 +1,5 @@
1
1
  import type { TransactionSigner } from '@solana/kit';
2
2
  import type { DEFAULT_PARTNER } from '../constants.js';
3
3
 
4
- /** Non-default partners must sign; only `DEFAULT_PARTNER` is valid as a bare address. */
4
+ /** A partner signer, or `DEFAULT_PARTNER` as a bare address. Any other partner must sign. */
5
5
  export type PartnerInput = TransactionSigner | typeof DEFAULT_PARTNER;
package/src/utils/pda.ts CHANGED
@@ -7,7 +7,7 @@ import {
7
7
 
8
8
  const addressEncoder = getAddressEncoder();
9
9
 
10
- /** Seed order is [wallet, tokenProgram, mint], not the parameter order. */
10
+ /** Derives the associated token account of `wallet` for `mint` under `tokenProgram`. */
11
11
  export async function findAssociatedTokenPda(
12
12
  wallet: Address,
13
13
  mint: Address,