@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.
- package/README.md +8 -9
- package/dist/index.cjs +777 -433
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +678 -217
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +678 -217
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +777 -433
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
- package/src/dex/generated/accounts/globalConfig.ts +39 -2
- package/src/dex/generated/accounts/pool.ts +60 -9
- package/src/dex/generated/accounts/rewardAccrual.ts +39 -2
- package/src/dex/generated/errors/sendDex.ts +16 -8
- package/src/dex/generated/events/creatorClaimEvent.ts +3 -2
- package/src/dex/generated/events/poolCreateEvent.ts +8 -2
- package/src/dex/generated/events/tokenCreateEvent.ts +8 -9
- package/src/dex/generated/events/tradeEvent.ts +9 -2
- package/src/dex/generated/index.ts +1 -0
- package/src/dex/generated/instructions/claimCreatorFees.ts +21 -5
- package/src/{nexus → dex}/generated/pdas/creatorFeeConfig.ts +6 -6
- package/src/dex/generated/pdas/index.ts +1 -0
- package/src/dex/generated/plugins/sendDex.ts +3 -0
- package/src/dex/generated/shared/index.ts +52 -0
- package/src/dex/trade.ts +12 -13
- package/src/launchpad/create.ts +13 -38
- package/src/launchpad/generated/accounts/bondingCurve.ts +62 -9
- package/src/launchpad/generated/accounts/globalConfig.ts +39 -2
- package/src/launchpad/generated/accounts/rewardAccrual.ts +39 -2
- package/src/launchpad/generated/errors/sendLaunchpad.ts +12 -4
- package/src/launchpad/generated/events/completeEvent.ts +3 -2
- package/src/launchpad/generated/events/creatorClaimEvent.ts +3 -2
- package/src/launchpad/generated/events/migrateEvent.ts +3 -2
- package/src/launchpad/generated/events/tokenCreateEvent.ts +10 -9
- package/src/launchpad/generated/events/tradeEvent.ts +9 -2
- package/src/launchpad/generated/index.ts +1 -0
- package/src/launchpad/generated/instructions/claimCreatorFees.ts +17 -4
- package/src/launchpad/generated/instructions/createToken.ts +32 -50
- package/src/launchpad/generated/instructions/migrate.ts +79 -12
- package/src/launchpad/generated/pdas/creatorFeeConfig.ts +39 -0
- package/src/launchpad/generated/pdas/index.ts +1 -0
- package/src/launchpad/generated/plugins/sendLaunchpad.ts +3 -0
- package/src/launchpad/generated/shared/index.ts +52 -0
- package/src/launchpad/migrate.ts +7 -4
- package/src/launchpad/trade.ts +15 -15
- package/src/math/amm.ts +40 -33
- package/src/math/fee-decay.ts +3 -2
- package/src/math/fees.ts +4 -2
- package/src/math/internal.ts +1 -1
- package/src/nexus/fee-helpers.ts +10 -5
- package/src/nexus/generated/accounts/altRegistry.ts +41 -2
- package/src/nexus/generated/accounts/feePreset.ts +43 -4
- package/src/nexus/generated/accounts/globalConfig.ts +39 -2
- package/src/nexus/generated/accounts/index.ts +0 -1
- package/src/nexus/generated/accounts/partnerConfig.ts +48 -5
- package/src/nexus/generated/accounts/partnerMetadata.ts +39 -2
- package/src/nexus/generated/accounts/rewardState.ts +41 -2
- package/src/nexus/generated/accounts/sendNexus.accounts.ts +0 -5
- package/src/nexus/generated/accounts/stakingConfig.ts +39 -2
- package/src/nexus/generated/accounts/userRewardDebt.ts +42 -5
- package/src/nexus/generated/accounts/userStakePosition.ts +42 -5
- package/src/nexus/generated/errors/sendNexus.ts +12 -4
- package/src/nexus/generated/index.ts +1 -0
- package/src/nexus/generated/pdas/index.ts +0 -1
- package/src/nexus/generated/plugins/sendNexus.ts +0 -35
- package/src/nexus/generated/shared/index.ts +52 -0
- package/src/nexus/generated/types/dexFees.ts +4 -4
- package/src/nexus/generated/types/index.ts +0 -1
- package/src/nexus/generated/types/launchpadFees.ts +4 -4
- package/src/nexus/staking.ts +45 -41
- package/src/platform.ts +2 -2
- package/src/transfer-fee.ts +19 -17
- package/src/utils/chunk.ts +1 -1
- package/src/utils/index.ts +2 -5
- package/src/utils/mint-info.ts +8 -7
- package/src/utils/partner.ts +1 -1
- package/src/utils/pda.ts +1 -1
- package/src/nexus/generated/accounts/creatorFeeConfig.ts +0 -200
- package/src/nexus/generated/types/callerType.ts +0 -38
- package/src/utils/creator-hash.ts +0 -57
|
@@ -98,12 +98,16 @@ export const SEND_NEXUS_ERROR__USER_TOKEN_ACCOUNT_WRONG_MINT = 0x1b61; // 7009
|
|
|
98
98
|
export const SEND_NEXUS_ERROR__USER_TOKEN_ACCOUNT_WRONG_AUTHORITY = 0x1b62; // 7010
|
|
99
99
|
/** NothingToClaim: Nothing to claim */
|
|
100
100
|
export const SEND_NEXUS_ERROR__NOTHING_TO_CLAIM = 0x1b63; // 7011
|
|
101
|
-
/**
|
|
102
|
-
export const
|
|
101
|
+
/** Unused7012: Unused */
|
|
102
|
+
export const SEND_NEXUS_ERROR__UNUSED7012 = 0x1b64; // 7012
|
|
103
103
|
/** QuoteMintPriceUnset: Quote mint has no USDC price on its nexus reward state */
|
|
104
104
|
export const SEND_NEXUS_ERROR__QUOTE_MINT_PRICE_UNSET = 0x1b65; // 7013
|
|
105
105
|
/** TransferFeeNotSettleable: Token-2022 transfer fee schedule cannot settle the exact amount */
|
|
106
106
|
export const SEND_NEXUS_ERROR__TRANSFER_FEE_NOT_SETTLEABLE = 0x1b66; // 7014
|
|
107
|
+
/** CreatorFeeTooHigh: Creator fee exceeds the partner's maximum */
|
|
108
|
+
export const SEND_NEXUS_ERROR__CREATOR_FEE_TOO_HIGH = 0x1b67; // 7015
|
|
109
|
+
/** CreatorFeeModeNotAllowed: Creator fee mode is not allowed by the partner config */
|
|
110
|
+
export const SEND_NEXUS_ERROR__CREATOR_FEE_MODE_NOT_ALLOWED = 0x1b68; // 7016
|
|
107
111
|
/** InsufficientLiquidity: Insufficient liquidity */
|
|
108
112
|
export const SEND_NEXUS_ERROR__INSUFFICIENT_LIQUIDITY = 0x1bbc; // 7100
|
|
109
113
|
/** InvalidAmount: Invalid amount */
|
|
@@ -112,6 +116,8 @@ export const SEND_NEXUS_ERROR__INVALID_AMOUNT = 0x1bbd; // 7101
|
|
|
112
116
|
export type SendNexusError =
|
|
113
117
|
| typeof SEND_NEXUS_ERROR__AMBIGUOUS_FEE_SOURCE
|
|
114
118
|
| typeof SEND_NEXUS_ERROR__ARITHMETIC_OVERFLOW
|
|
119
|
+
| typeof SEND_NEXUS_ERROR__CREATOR_FEE_MODE_NOT_ALLOWED
|
|
120
|
+
| typeof SEND_NEXUS_ERROR__CREATOR_FEE_TOO_HIGH
|
|
115
121
|
| typeof SEND_NEXUS_ERROR__DUPLICATE_AUTH_PLATFORM_NAME
|
|
116
122
|
| typeof SEND_NEXUS_ERROR__INSUFFICIENT_LIQUIDITY
|
|
117
123
|
| typeof SEND_NEXUS_ERROR__INSUFFICIENT_STAKE
|
|
@@ -119,7 +125,6 @@ export type SendNexusError =
|
|
|
119
125
|
| typeof SEND_NEXUS_ERROR__INVALID_AUTHORITY
|
|
120
126
|
| typeof SEND_NEXUS_ERROR__INVALID_AUTH_PLATFORM_INDEX
|
|
121
127
|
| typeof SEND_NEXUS_ERROR__INVALID_AUTH_PLATFORM_NAME
|
|
122
|
-
| typeof SEND_NEXUS_ERROR__INVALID_CREATOR_ID
|
|
123
128
|
| typeof SEND_NEXUS_ERROR__INVALID_DESTINATION
|
|
124
129
|
| typeof SEND_NEXUS_ERROR__INVALID_FEE_PRESET
|
|
125
130
|
| typeof SEND_NEXUS_ERROR__INVALID_MINT
|
|
@@ -153,6 +158,7 @@ export type SendNexusError =
|
|
|
153
158
|
| typeof SEND_NEXUS_ERROR__TOO_MANY_AUTH_PLATFORMS
|
|
154
159
|
| typeof SEND_NEXUS_ERROR__TRANSFER_FEE_NOT_SETTLEABLE
|
|
155
160
|
| typeof SEND_NEXUS_ERROR__UNAUTHORIZED
|
|
161
|
+
| typeof SEND_NEXUS_ERROR__UNUSED7012
|
|
156
162
|
| typeof SEND_NEXUS_ERROR__USER_TOKEN_ACCOUNT_INVALID
|
|
157
163
|
| typeof SEND_NEXUS_ERROR__USER_TOKEN_ACCOUNT_WRONG_AUTHORITY
|
|
158
164
|
| typeof SEND_NEXUS_ERROR__USER_TOKEN_ACCOUNT_WRONG_MINT
|
|
@@ -161,6 +167,8 @@ export type SendNexusError =
|
|
|
161
167
|
export const sendNexusErrorMessages: Record<SendNexusError, string> = {
|
|
162
168
|
[SEND_NEXUS_ERROR__AMBIGUOUS_FEE_SOURCE]: `Cannot specify both fee_preset and custom_fees`,
|
|
163
169
|
[SEND_NEXUS_ERROR__ARITHMETIC_OVERFLOW]: `Arithmetic overflow`,
|
|
170
|
+
[SEND_NEXUS_ERROR__CREATOR_FEE_MODE_NOT_ALLOWED]: `Creator fee mode is not allowed by the partner config`,
|
|
171
|
+
[SEND_NEXUS_ERROR__CREATOR_FEE_TOO_HIGH]: `Creator fee exceeds the partner's maximum`,
|
|
164
172
|
[SEND_NEXUS_ERROR__DUPLICATE_AUTH_PLATFORM_NAME]: `Duplicate auth platform name`,
|
|
165
173
|
[SEND_NEXUS_ERROR__INSUFFICIENT_LIQUIDITY]: `Insufficient liquidity`,
|
|
166
174
|
[SEND_NEXUS_ERROR__INSUFFICIENT_STAKE]: `Insufficient stake balance`,
|
|
@@ -168,7 +176,6 @@ export const sendNexusErrorMessages: Record<SendNexusError, string> = {
|
|
|
168
176
|
[SEND_NEXUS_ERROR__INVALID_AUTHORITY]: `Invalid authority`,
|
|
169
177
|
[SEND_NEXUS_ERROR__INVALID_AUTH_PLATFORM_INDEX]: `Invalid auth platform index`,
|
|
170
178
|
[SEND_NEXUS_ERROR__INVALID_AUTH_PLATFORM_NAME]: `Invalid auth platform name`,
|
|
171
|
-
[SEND_NEXUS_ERROR__INVALID_CREATOR_ID]: `Creator identity is not a decodable wallet address`,
|
|
172
179
|
[SEND_NEXUS_ERROR__INVALID_DESTINATION]: `Invalid destination account`,
|
|
173
180
|
[SEND_NEXUS_ERROR__INVALID_FEE_PRESET]: `Invalid fee configuration`,
|
|
174
181
|
[SEND_NEXUS_ERROR__INVALID_MINT]: `Invalid mint`,
|
|
@@ -202,6 +209,7 @@ export const sendNexusErrorMessages: Record<SendNexusError, string> = {
|
|
|
202
209
|
[SEND_NEXUS_ERROR__TOO_MANY_AUTH_PLATFORMS]: `Too many auth platforms (max 255)`,
|
|
203
210
|
[SEND_NEXUS_ERROR__TRANSFER_FEE_NOT_SETTLEABLE]: `Token-2022 transfer fee schedule cannot settle the exact amount`,
|
|
204
211
|
[SEND_NEXUS_ERROR__UNAUTHORIZED]: `Unauthorized`,
|
|
212
|
+
[SEND_NEXUS_ERROR__UNUSED7012]: `Unused`,
|
|
205
213
|
[SEND_NEXUS_ERROR__USER_TOKEN_ACCOUNT_INVALID]: `User token account is not an initialized token account`,
|
|
206
214
|
[SEND_NEXUS_ERROR__USER_TOKEN_ACCOUNT_WRONG_AUTHORITY]: `User token account is not owned by the trading user`,
|
|
207
215
|
[SEND_NEXUS_ERROR__USER_TOKEN_ACCOUNT_WRONG_MINT]: `User token account mint does not match the trade`,
|
|
@@ -23,11 +23,9 @@ import {
|
|
|
23
23
|
} from '@solana/kit/program-client-core';
|
|
24
24
|
import {
|
|
25
25
|
fetchAllAltRegistry,
|
|
26
|
-
fetchAllCreatorFeeConfig,
|
|
27
26
|
fetchAllFeePreset,
|
|
28
27
|
fetchAllGlobalConfig,
|
|
29
28
|
fetchAllMaybeAltRegistry,
|
|
30
|
-
fetchAllMaybeCreatorFeeConfig,
|
|
31
29
|
fetchAllMaybeFeePreset,
|
|
32
30
|
fetchAllMaybeGlobalConfig,
|
|
33
31
|
fetchAllMaybePartnerConfig,
|
|
@@ -43,11 +41,9 @@ import {
|
|
|
43
41
|
fetchAllUserRewardDebt,
|
|
44
42
|
fetchAllUserStakePosition,
|
|
45
43
|
fetchAltRegistry,
|
|
46
|
-
fetchCreatorFeeConfig,
|
|
47
44
|
fetchFeePreset,
|
|
48
45
|
fetchGlobalConfig,
|
|
49
46
|
fetchMaybeAltRegistry,
|
|
50
|
-
fetchMaybeCreatorFeeConfig,
|
|
51
47
|
fetchMaybeFeePreset,
|
|
52
48
|
fetchMaybeGlobalConfig,
|
|
53
49
|
fetchMaybePartnerConfig,
|
|
@@ -63,7 +59,6 @@ import {
|
|
|
63
59
|
fetchUserRewardDebt,
|
|
64
60
|
fetchUserStakePosition,
|
|
65
61
|
getAltRegistryCodec,
|
|
66
|
-
getCreatorFeeConfigCodec,
|
|
67
62
|
getFeePresetCodec,
|
|
68
63
|
getGlobalConfigCodec,
|
|
69
64
|
getPartnerConfigCodec,
|
|
@@ -75,8 +70,6 @@ import {
|
|
|
75
70
|
identifySendNexusAccount,
|
|
76
71
|
type AltRegistry,
|
|
77
72
|
type AltRegistryArgs,
|
|
78
|
-
type CreatorFeeConfig,
|
|
79
|
-
type CreatorFeeConfigArgs,
|
|
80
73
|
type FeePreset,
|
|
81
74
|
type FeePresetArgs,
|
|
82
75
|
type GlobalConfig,
|
|
@@ -115,7 +108,6 @@ import {
|
|
|
115
108
|
} from '../instructions/index.js';
|
|
116
109
|
import {
|
|
117
110
|
findAltRegistryPda,
|
|
118
|
-
findCreatorFeeConfigPda,
|
|
119
111
|
findDefaultFeePresetPda,
|
|
120
112
|
findDefaultPartnerMetadataPda,
|
|
121
113
|
findDefaultPartnerPda,
|
|
@@ -143,8 +135,6 @@ export type SendNexusPlugin = {
|
|
|
143
135
|
export type SendNexusPluginAccounts = {
|
|
144
136
|
altRegistry: ReturnType<typeof getAltRegistryCodec> &
|
|
145
137
|
SelfFetchFunctions<AltRegistryArgs, AltRegistry>;
|
|
146
|
-
creatorFeeConfig: ReturnType<typeof getCreatorFeeConfigCodec> &
|
|
147
|
-
SelfFetchFunctions<CreatorFeeConfigArgs, CreatorFeeConfig>;
|
|
148
138
|
feePreset: ReturnType<typeof getFeePresetCodec> &
|
|
149
139
|
SelfFetchFunctions<FeePresetArgs, FeePreset>;
|
|
150
140
|
globalConfig: ReturnType<typeof getGlobalConfigCodec> &
|
|
@@ -203,7 +193,6 @@ export type SendNexusPluginPdas = {
|
|
|
203
193
|
userStakePosition: typeof findUserStakePositionPda;
|
|
204
194
|
userRewardDebt: typeof findUserRewardDebtPda;
|
|
205
195
|
eventAuthority: typeof findEventAuthorityPda;
|
|
206
|
-
creatorFeeConfig: typeof findCreatorFeeConfigPda;
|
|
207
196
|
altRegistry: typeof findAltRegistryPda;
|
|
208
197
|
};
|
|
209
198
|
|
|
@@ -236,29 +225,6 @@ export function sendNexusProgram() {
|
|
|
236
225
|
fetchMaybe: (address, config) =>
|
|
237
226
|
fetchMaybeAltRegistry(client.rpc, address, config),
|
|
238
227
|
}),
|
|
239
|
-
creatorFeeConfig: Object.freeze({
|
|
240
|
-
...getCreatorFeeConfigCodec(),
|
|
241
|
-
fetch: (address, config) =>
|
|
242
|
-
fetchCreatorFeeConfig(client.rpc, address, config),
|
|
243
|
-
fetchAll: (addresses, config) =>
|
|
244
|
-
fetchAllCreatorFeeConfig(
|
|
245
|
-
client.rpc,
|
|
246
|
-
addresses,
|
|
247
|
-
config,
|
|
248
|
-
),
|
|
249
|
-
fetchAllMaybe: (addresses, config) =>
|
|
250
|
-
fetchAllMaybeCreatorFeeConfig(
|
|
251
|
-
client.rpc,
|
|
252
|
-
addresses,
|
|
253
|
-
config,
|
|
254
|
-
),
|
|
255
|
-
fetchMaybe: (address, config) =>
|
|
256
|
-
fetchMaybeCreatorFeeConfig(
|
|
257
|
-
client.rpc,
|
|
258
|
-
address,
|
|
259
|
-
config,
|
|
260
|
-
),
|
|
261
|
-
}),
|
|
262
228
|
feePreset: Object.freeze({
|
|
263
229
|
...getFeePresetCodec(),
|
|
264
230
|
fetch: (address, config) =>
|
|
@@ -480,7 +446,6 @@ export function sendNexusProgram() {
|
|
|
480
446
|
userStakePosition: findUserStakePositionPda,
|
|
481
447
|
userRewardDebt: findUserRewardDebtPda,
|
|
482
448
|
eventAuthority: findEventAuthorityPda,
|
|
483
|
-
creatorFeeConfig: findCreatorFeeConfigPda,
|
|
484
449
|
altRegistry: findAltRegistryPda,
|
|
485
450
|
},
|
|
486
451
|
identifyAccount: identifySendNexusAccount,
|
|
@@ -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
|
+
}
|
|
@@ -23,7 +23,7 @@ export type DexFees = {
|
|
|
23
23
|
creationFeeCents: bigint;
|
|
24
24
|
protocolFeeBps: number;
|
|
25
25
|
lpFeeBps: number;
|
|
26
|
-
|
|
26
|
+
maxCreatorFeeBps: number;
|
|
27
27
|
feeDecaySeconds: number;
|
|
28
28
|
feeDecayStartBps: number;
|
|
29
29
|
};
|
|
@@ -32,7 +32,7 @@ export type DexFeesArgs = {
|
|
|
32
32
|
creationFeeCents: number | bigint;
|
|
33
33
|
protocolFeeBps: number;
|
|
34
34
|
lpFeeBps: number;
|
|
35
|
-
|
|
35
|
+
maxCreatorFeeBps: number;
|
|
36
36
|
feeDecaySeconds: number;
|
|
37
37
|
feeDecayStartBps: number;
|
|
38
38
|
};
|
|
@@ -42,7 +42,7 @@ export function getDexFeesEncoder(): FixedSizeEncoder<DexFeesArgs> {
|
|
|
42
42
|
['creationFeeCents', getU64Encoder()],
|
|
43
43
|
['protocolFeeBps', getU16Encoder()],
|
|
44
44
|
['lpFeeBps', getU16Encoder()],
|
|
45
|
-
['
|
|
45
|
+
['maxCreatorFeeBps', getU16Encoder()],
|
|
46
46
|
['feeDecaySeconds', getU16Encoder()],
|
|
47
47
|
['feeDecayStartBps', getU16Encoder()],
|
|
48
48
|
]);
|
|
@@ -53,7 +53,7 @@ export function getDexFeesDecoder(): FixedSizeDecoder<DexFees> {
|
|
|
53
53
|
['creationFeeCents', getU64Decoder()],
|
|
54
54
|
['protocolFeeBps', getU16Decoder()],
|
|
55
55
|
['lpFeeBps', getU16Decoder()],
|
|
56
|
-
['
|
|
56
|
+
['maxCreatorFeeBps', getU16Decoder()],
|
|
57
57
|
['feeDecaySeconds', getU16Decoder()],
|
|
58
58
|
['feeDecayStartBps', getU16Decoder()],
|
|
59
59
|
]);
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
export type LaunchpadFees = {
|
|
23
23
|
creationFeeCents: bigint;
|
|
24
24
|
protocolFeeBps: number;
|
|
25
|
-
|
|
25
|
+
maxCreatorFeeBps: number;
|
|
26
26
|
feeDecaySeconds: number;
|
|
27
27
|
feeDecayStartBps: number;
|
|
28
28
|
};
|
|
@@ -30,7 +30,7 @@ export type LaunchpadFees = {
|
|
|
30
30
|
export type LaunchpadFeesArgs = {
|
|
31
31
|
creationFeeCents: number | bigint;
|
|
32
32
|
protocolFeeBps: number;
|
|
33
|
-
|
|
33
|
+
maxCreatorFeeBps: number;
|
|
34
34
|
feeDecaySeconds: number;
|
|
35
35
|
feeDecayStartBps: number;
|
|
36
36
|
};
|
|
@@ -39,7 +39,7 @@ export function getLaunchpadFeesEncoder(): FixedSizeEncoder<LaunchpadFeesArgs> {
|
|
|
39
39
|
return getStructEncoder([
|
|
40
40
|
['creationFeeCents', getU64Encoder()],
|
|
41
41
|
['protocolFeeBps', getU16Encoder()],
|
|
42
|
-
['
|
|
42
|
+
['maxCreatorFeeBps', getU16Encoder()],
|
|
43
43
|
['feeDecaySeconds', getU16Encoder()],
|
|
44
44
|
['feeDecayStartBps', getU16Encoder()],
|
|
45
45
|
]);
|
|
@@ -49,7 +49,7 @@ export function getLaunchpadFeesDecoder(): FixedSizeDecoder<LaunchpadFees> {
|
|
|
49
49
|
return getStructDecoder([
|
|
50
50
|
['creationFeeCents', getU64Decoder()],
|
|
51
51
|
['protocolFeeBps', getU16Decoder()],
|
|
52
|
-
['
|
|
52
|
+
['maxCreatorFeeBps', getU16Decoder()],
|
|
53
53
|
['feeDecaySeconds', getU16Decoder()],
|
|
54
54
|
['feeDecayStartBps', getU16Decoder()],
|
|
55
55
|
]);
|
package/src/nexus/staking.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
} from '../constants.js';
|
|
18
18
|
import { fetchInChunks } from '../utils/chunk.js';
|
|
19
19
|
import { findAssociatedTokenPda } from '../utils/pda.js';
|
|
20
|
+
import { accountIsCreated } from './generated/shared/index.js';
|
|
20
21
|
import { fetchStakingConfig } from './generated/accounts/stakingConfig.js';
|
|
21
22
|
import {
|
|
22
23
|
fetchMaybeUserStakePosition,
|
|
@@ -42,10 +43,10 @@ import { findRewardAccrualPda as findLaunchpadRewardAccrualPda } from '../launch
|
|
|
42
43
|
import { getRewardAccrualDecoder } from '../launchpad/generated/accounts/rewardAccrual.js';
|
|
43
44
|
import { findRewardAccrualPda as findDexRewardAccrualPda } from '../dex/generated/pdas/rewardAccrual.js';
|
|
44
45
|
|
|
45
|
-
//
|
|
46
|
+
// Fixed-point scale of `RewardAccrual.accPerToken` in both programs.
|
|
46
47
|
const PRECISION = 1_000_000_000_000n;
|
|
47
48
|
|
|
48
|
-
/**
|
|
49
|
+
/** An RPC with `getAccountInfo` and, optionally, `getProgramAccounts`. Without `getProgramAccounts`, pass `rewardMints`. */
|
|
49
50
|
export type RewardMintRpc = Rpc<GetAccountInfoApi> &
|
|
50
51
|
Partial<Rpc<GetProgramAccountsApi>>;
|
|
51
52
|
|
|
@@ -55,7 +56,7 @@ function hasProgramAccounts(
|
|
|
55
56
|
return typeof rpc.getProgramAccounts === 'function';
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
//
|
|
59
|
+
// Field offsets, including the 8-byte discriminator. tests/staking.test.ts pins them.
|
|
59
60
|
const REWARD_STATE_STAKING_CONFIG_OFFSET = 10n;
|
|
60
61
|
const REWARD_STATE_REWARD_MINT_OFFSET = 42;
|
|
61
62
|
|
|
@@ -65,9 +66,8 @@ function compareAddresses(a: Address, b: Address): number {
|
|
|
65
66
|
return 0;
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
/**
|
|
69
|
-
*
|
|
70
|
-
* `StakingConfig.rewardCount` rather than settle a short list. */
|
|
69
|
+
/** Returns every registered reward mint, disabled mints included, sorted by address.
|
|
70
|
+
* Throws if the count differs from `StakingConfig.rewardCount`. */
|
|
71
71
|
export async function getRewardMints(
|
|
72
72
|
rpc: Rpc<GetAccountInfoApi> & Rpc<GetProgramAccountsApi>,
|
|
73
73
|
): Promise<readonly Address[]> {
|
|
@@ -98,7 +98,7 @@ export async function getRewardMints(
|
|
|
98
98
|
const mints: Address[] = [];
|
|
99
99
|
for (const { account } of accounts) {
|
|
100
100
|
const data = base64.encode(account.data[0]);
|
|
101
|
-
// Another nexus account of the same size
|
|
101
|
+
// Another nexus account of the same size can pass both filters.
|
|
102
102
|
if (!containsBytes(data, REWARD_STATE_DISCRIMINATOR, 0)) continue;
|
|
103
103
|
mints.push(
|
|
104
104
|
addressDecoder.decode(data, REWARD_STATE_REWARD_MINT_OFFSET),
|
|
@@ -136,13 +136,14 @@ async function resolveRewardMints(
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
export interface SettleParams {
|
|
139
|
-
/** `settle` is permissionless
|
|
139
|
+
/** Does not sign. `settle` is permissionless. */
|
|
140
140
|
user: Address;
|
|
141
141
|
payer: TransactionSigner;
|
|
142
142
|
stakingMint: Address;
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
/**
|
|
145
|
+
/** Builds one `settle` per reward mint. `source` is the mint list, or an RPC to read it from.
|
|
146
|
+
* `settle` is idempotent. With a partial list, `stake` and `unstake` fail with `RewardsNotSettled`. */
|
|
146
147
|
export async function buildSettleInstructions(
|
|
147
148
|
source: RewardMintSource,
|
|
148
149
|
params: SettleParams,
|
|
@@ -187,7 +188,7 @@ export async function fetchMissingUserRewardDebts(
|
|
|
187
188
|
|
|
188
189
|
const missingMints: Address[] = [];
|
|
189
190
|
for (const [index, rewardMint] of rewardMints.entries()) {
|
|
190
|
-
if (!encodedAccounts[index]
|
|
191
|
+
if (!accountIsCreated(encodedAccounts[index])) {
|
|
191
192
|
missingMints.push(rewardMint);
|
|
192
193
|
}
|
|
193
194
|
}
|
|
@@ -199,16 +200,17 @@ export interface PrepareStakingParams {
|
|
|
199
200
|
user: Address;
|
|
200
201
|
payer: TransactionSigner;
|
|
201
202
|
stakingMint: Address;
|
|
202
|
-
/**
|
|
203
|
+
/** If given, must hold every registered mint. With a subset, `stake` and `unstake` fail. */
|
|
203
204
|
rewardMints?: readonly Address[];
|
|
204
205
|
}
|
|
205
206
|
|
|
206
|
-
/**
|
|
207
|
+
/** Builds a `create_user_reward_debt` for each missing `UserRewardDebt`, then a `settle` for every
|
|
208
|
+
* reward mint. The user does not sign these instructions. */
|
|
207
209
|
export async function buildStakingPreflightInstructions(
|
|
208
210
|
rpc: RewardMintRpc & Rpc<GetMultipleAccountsApi>,
|
|
209
211
|
params: PrepareStakingParams,
|
|
210
212
|
): Promise<Instruction[]> {
|
|
211
|
-
//
|
|
213
|
+
// Read the mints once. Two reads can return different lists.
|
|
212
214
|
const rewardMints = await resolveRewardMints(rpc, params.rewardMints);
|
|
213
215
|
|
|
214
216
|
const missingMints = await fetchMissingUserRewardDebts(
|
|
@@ -234,7 +236,7 @@ export async function buildStakingPreflightInstructions(
|
|
|
234
236
|
return [...createIxs, ...settleIxs];
|
|
235
237
|
}
|
|
236
238
|
|
|
237
|
-
/**
|
|
239
|
+
/** Returns true if `stake` and `unstake` pass the `settledCount == rewardCount` check. */
|
|
238
240
|
export async function isFullySettled(
|
|
239
241
|
rpc: Rpc<GetAccountInfoApi>,
|
|
240
242
|
user: Address,
|
|
@@ -255,7 +257,8 @@ export interface StakeParams {
|
|
|
255
257
|
amount: bigint;
|
|
256
258
|
}
|
|
257
259
|
|
|
258
|
-
/**
|
|
260
|
+
/** The instruction fails with `RewardsNotSettled` until every mint is settled at the current
|
|
261
|
+
* `stakeVersion`. Run `buildStakingPreflightInstructions` first. */
|
|
259
262
|
export async function buildStakeInstruction(
|
|
260
263
|
params: StakeParams,
|
|
261
264
|
): Promise<Instruction> {
|
|
@@ -274,8 +277,9 @@ export interface UnstakeParams {
|
|
|
274
277
|
amount: bigint;
|
|
275
278
|
}
|
|
276
279
|
|
|
277
|
-
/**
|
|
278
|
-
*
|
|
280
|
+
/** Run `buildStakingPreflightInstructions` immediately before. The check also passes on an older
|
|
281
|
+
* settle. Rewards since that settle then accrue on the smaller post-unstake amount, and the
|
|
282
|
+
* difference is lost. */
|
|
279
283
|
export async function buildUnstakeInstruction(
|
|
280
284
|
params: UnstakeParams,
|
|
281
285
|
): Promise<Instruction> {
|
|
@@ -291,12 +295,12 @@ export interface ClaimRewardsParams {
|
|
|
291
295
|
user: TransactionSigner;
|
|
292
296
|
payer?: TransactionSigner;
|
|
293
297
|
stakingMint: Address;
|
|
294
|
-
/** Defaults to every registered mint
|
|
298
|
+
/** Defaults to every registered mint. A subset needs no preflight: each `claim` settles its own mint. */
|
|
295
299
|
rewardMints?: readonly Address[];
|
|
296
300
|
}
|
|
297
301
|
|
|
298
|
-
/**
|
|
299
|
-
* `
|
|
302
|
+
/** Builds one `claim` per reward mint. A `create_user_reward_debt` comes before each `claim` whose
|
|
303
|
+
* `UserRewardDebt` is missing: `claim` fails without it. Throws if a mint has no `RewardState`. */
|
|
300
304
|
export async function buildClaimRewardsInstructions(
|
|
301
305
|
rpc: RewardMintRpc & Rpc<GetMultipleAccountsApi>,
|
|
302
306
|
params: ClaimRewardsParams,
|
|
@@ -337,7 +341,7 @@ export async function buildClaimRewardsInstructions(
|
|
|
337
341
|
const perMint = await Promise.all(
|
|
338
342
|
rewardMints.map(async (rewardMint, index) => {
|
|
339
343
|
const encodedRewardState = fetched[index];
|
|
340
|
-
if (!encodedRewardState
|
|
344
|
+
if (!accountIsCreated(encodedRewardState)) {
|
|
341
345
|
throw new Error(`RewardState not found for mint ${rewardMint}`);
|
|
342
346
|
}
|
|
343
347
|
const { vault } = rewardStateDecoder.decode(
|
|
@@ -356,8 +360,8 @@ export async function buildClaimRewardsInstructions(
|
|
|
356
360
|
|
|
357
361
|
const instructions: Instruction[] = [];
|
|
358
362
|
|
|
359
|
-
// Directly before its own `claim`, so
|
|
360
|
-
if (!fetched[rewardMints.length * 2 + index]
|
|
363
|
+
// Directly before its own `claim`, so a split of the list by mint keeps each pair together.
|
|
364
|
+
if (!accountIsCreated(fetched[rewardMints.length * 2 + index])) {
|
|
361
365
|
instructions.push(
|
|
362
366
|
await getCreateUserRewardDebtInstructionAsync({
|
|
363
367
|
user: params.user.address,
|
|
@@ -395,12 +399,13 @@ export interface WithdrawFeesAccounts {
|
|
|
395
399
|
tokenProgram: Address;
|
|
396
400
|
}
|
|
397
401
|
|
|
398
|
-
/**
|
|
402
|
+
/** Returns the accounts of `withdraw_fees` for one reward mint. Without `tokenProgram`, reads it
|
|
403
|
+
* from the mint account. Throws if that account does not exist. */
|
|
399
404
|
export async function buildWithdrawFeesAccounts(
|
|
400
405
|
rpc: Rpc<GetAccountInfoApi>,
|
|
401
406
|
params: {
|
|
402
407
|
rewardMint: Address;
|
|
403
|
-
/**
|
|
408
|
+
/** A wallet, not a token account. The SDK derives its ATA. */
|
|
404
409
|
destination: Address;
|
|
405
410
|
tokenProgram?: Address;
|
|
406
411
|
},
|
|
@@ -467,13 +472,12 @@ export async function fetchUserStakePositionData(
|
|
|
467
472
|
return maybePosition.exists ? maybePosition.data : null;
|
|
468
473
|
}
|
|
469
474
|
|
|
470
|
-
/**
|
|
471
|
-
* can never store: it requires a token mint. */
|
|
475
|
+
/** Returns false for the system program address, the value of an unbound staking mint. */
|
|
472
476
|
export function isStakingMintBound(stakingMint: Address): boolean {
|
|
473
477
|
return stakingMint !== SYSTEM_PROGRAM_ADDRESS;
|
|
474
478
|
}
|
|
475
479
|
|
|
476
|
-
/**
|
|
480
|
+
/** Returns the staking mint. Throws if no staking mint is bound. A bound staking mint cannot change. */
|
|
477
481
|
export async function fetchSendMint(
|
|
478
482
|
rpc: Rpc<GetAccountInfoApi>,
|
|
479
483
|
): Promise<Address> {
|
|
@@ -488,13 +492,14 @@ export async function fetchSendMint(
|
|
|
488
492
|
|
|
489
493
|
export interface PendingReward {
|
|
490
494
|
rewardMint: Address;
|
|
491
|
-
/**
|
|
492
|
-
*
|
|
495
|
+
/** Amount the vault sends, in raw units of `rewardMint`. Equals `ClaimEvent.amount`. A mint with a
|
|
496
|
+
* transfer fee delivers less. Get the fee with `transferFee.fetchMintFees`. */
|
|
493
497
|
pending: bigint;
|
|
494
498
|
}
|
|
495
499
|
|
|
496
|
-
/**
|
|
497
|
-
* its own mint
|
|
500
|
+
/** Returns one entry per reward mint, in `knownRewardMints` order, else sorted by address. Each amount
|
|
501
|
+
* is in the raw units of its own mint. Do not add them together. Each amount is what the vault sends,
|
|
502
|
+
* before the mint's transfer fee. */
|
|
498
503
|
export async function fetchPendingRewards(
|
|
499
504
|
rpc: RewardMintRpc & Rpc<GetMultipleAccountsApi>,
|
|
500
505
|
user: Address,
|
|
@@ -541,33 +546,32 @@ export async function fetchPendingRewards(
|
|
|
541
546
|
);
|
|
542
547
|
|
|
543
548
|
const userRewardDebtDecoder = getUserRewardDebtDecoder();
|
|
544
|
-
// One decoder for both: the two `RewardAccrual` types
|
|
549
|
+
// One decoder for both: the two `RewardAccrual` types have the same layout and discriminator.
|
|
545
550
|
const rewardAccrualDecoder = getRewardAccrualDecoder();
|
|
546
551
|
|
|
547
552
|
return pdas.map(({ rewardMint }, index) => {
|
|
548
553
|
const base = index * 3;
|
|
549
554
|
|
|
550
555
|
const encodedDebt = encodedAccounts[base];
|
|
551
|
-
// A missing debt is not zero owed
|
|
552
|
-
// `accSnapshot = 0
|
|
553
|
-
const debt = encodedDebt
|
|
556
|
+
// A missing debt is not zero owed. `create_user_reward_debt` opens it at
|
|
557
|
+
// `accSnapshot = 0` and `amountSnapshot = position.amount`, and the claim pays from there.
|
|
558
|
+
const debt = accountIsCreated(encodedDebt)
|
|
554
559
|
? userRewardDebtDecoder.decode(encodedDebt.data)
|
|
555
560
|
: { owed: 0n, accSnapshot: 0n, amountSnapshot: stakeAmount };
|
|
556
561
|
|
|
557
562
|
const encodedLaunchpadAccrual = encodedAccounts[base + 1];
|
|
558
|
-
const launchpadAcc = encodedLaunchpadAccrual
|
|
563
|
+
const launchpadAcc = accountIsCreated(encodedLaunchpadAccrual)
|
|
559
564
|
? rewardAccrualDecoder.decode(encodedLaunchpadAccrual.data)
|
|
560
565
|
.accPerToken
|
|
561
566
|
: 0n;
|
|
562
567
|
|
|
563
568
|
const encodedDexAccrual = encodedAccounts[base + 2];
|
|
564
|
-
const dexAcc = encodedDexAccrual
|
|
569
|
+
const dexAcc = accountIsCreated(encodedDexAccrual)
|
|
565
570
|
? rewardAccrualDecoder.decode(encodedDexAccrual.data).accPerToken
|
|
566
571
|
: 0n;
|
|
567
572
|
|
|
568
|
-
//
|
|
569
|
-
//
|
|
570
|
-
// where the program would fail.
|
|
573
|
+
// Matches `settle`: the basis is the smaller amount, floored once over the summed
|
|
574
|
+
// accumulators. A negative delta quotes 0, where the program fails.
|
|
571
575
|
const basis =
|
|
572
576
|
debt.amountSnapshot < stakeAmount
|
|
573
577
|
? debt.amountSnapshot
|
package/src/platform.ts
CHANGED
|
@@ -2,10 +2,10 @@ import { getProgramDerivedAddress } from '@solana/kit';
|
|
|
2
2
|
import type { ProgramDerivedAddress } from '@solana/kit';
|
|
3
3
|
import { SEND_NEXUS_PROGRAM_ADDRESS } from './nexus/generated/programs/index.js';
|
|
4
4
|
|
|
5
|
-
/**
|
|
5
|
+
/** Seed of the platform PDA. A platform has no on-chain account. */
|
|
6
6
|
export const PLATFORM_SEED = 'platform';
|
|
7
7
|
|
|
8
|
-
/**
|
|
8
|
+
/** Derives the platform key for `name` under the nexus program. Each name gives a different platform. */
|
|
9
9
|
export async function findPlatformAddress(
|
|
10
10
|
name: string,
|
|
11
11
|
): Promise<ProgramDerivedAddress> {
|