@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.
- package/README.md +8 -9
- package/dist/index.cjs +526 -98
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +595 -93
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +595 -93
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +526 -98
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/dex/generated/accounts/globalConfig.ts +39 -2
- package/src/dex/generated/accounts/pool.ts +41 -2
- package/src/dex/generated/accounts/rewardAccrual.ts +39 -2
- package/src/dex/generated/index.ts +1 -0
- package/src/dex/generated/shared/index.ts +52 -0
- package/src/dex/trade.ts +12 -13
- package/src/launchpad/create.ts +8 -8
- package/src/launchpad/generated/accounts/bondingCurve.ts +39 -2
- package/src/launchpad/generated/accounts/globalConfig.ts +39 -2
- package/src/launchpad/generated/accounts/rewardAccrual.ts +39 -2
- package/src/launchpad/generated/index.ts +1 -0
- package/src/launchpad/generated/shared/index.ts +52 -0
- package/src/launchpad/migrate.ts +1 -1
- 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 +5 -3
- package/src/nexus/generated/accounts/altRegistry.ts +41 -2
- package/src/nexus/generated/accounts/creatorFeeConfig.ts +39 -2
- package/src/nexus/generated/accounts/feePreset.ts +41 -2
- package/src/nexus/generated/accounts/globalConfig.ts +39 -2
- package/src/nexus/generated/accounts/partnerConfig.ts +39 -2
- package/src/nexus/generated/accounts/partnerMetadata.ts +39 -2
- package/src/nexus/generated/accounts/rewardState.ts +41 -2
- package/src/nexus/generated/accounts/stakingConfig.ts +39 -2
- package/src/nexus/generated/accounts/userRewardDebt.ts +39 -2
- package/src/nexus/generated/accounts/userStakePosition.ts +39 -2
- package/src/nexus/generated/index.ts +1 -0
- package/src/nexus/generated/shared/index.ts +52 -0
- 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/creator-hash.ts +6 -3
- package/src/utils/index.ts +2 -0
- package/src/utils/mint-info.ts +8 -7
- package/src/utils/partner.ts +1 -1
- package/src/utils/pda.ts +1 -1
|
@@ -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
|
|
|
48
49
|
export const STAKING_CONFIG_DISCRIMINATOR: ReadonlyUint8Array = new Uint8Array([
|
|
49
50
|
45, 134, 252, 82, 37, 57, 84, 25,
|
|
@@ -115,9 +116,25 @@ export function getStakingConfigCodec(): FixedSizeCodec<
|
|
|
115
116
|
return combineCodec(getStakingConfigEncoder(), getStakingConfigDecoder());
|
|
116
117
|
}
|
|
117
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Decodes a `StakingConfig` account, throwing when another program owns it or its discriminator
|
|
121
|
+
* does not match.
|
|
122
|
+
*
|
|
123
|
+
* Unlike {@link fetchMaybeStakingConfig}, this throws for an address that only holds lamports rather
|
|
124
|
+
* than returning the non-existing variant: an account passed as existing must never come back as that
|
|
125
|
+
* variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
|
|
126
|
+
*/
|
|
118
127
|
export function decodeStakingConfig<TAddress extends string = string>(
|
|
119
128
|
encodedAccount: EncodedAccount<TAddress>,
|
|
120
129
|
): Account<StakingConfig, TAddress>;
|
|
130
|
+
/**
|
|
131
|
+
* Decodes a `StakingConfig` account, throwing when another program owns it or its discriminator
|
|
132
|
+
* does not match.
|
|
133
|
+
*
|
|
134
|
+
* Unlike {@link fetchMaybeStakingConfig}, 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
|
+
*/
|
|
121
138
|
export function decodeStakingConfig<TAddress extends string = string>(
|
|
122
139
|
encodedAccount: MaybeEncodedAccount<TAddress>,
|
|
123
140
|
): MaybeAccount<StakingConfig, TAddress>;
|
|
@@ -148,6 +165,7 @@ export function decodeStakingConfig<TAddress extends string = string>(
|
|
|
148
165
|
);
|
|
149
166
|
}
|
|
150
167
|
|
|
168
|
+
/** Fetches a `StakingConfig` account, throwing when it does not exist or only holds lamports. */
|
|
151
169
|
export async function fetchStakingConfig<TAddress extends string = string>(
|
|
152
170
|
rpc: Parameters<typeof fetchEncodedAccount>[0],
|
|
153
171
|
address: Address<TAddress>,
|
|
@@ -158,15 +176,25 @@ export async function fetchStakingConfig<TAddress extends string = string>(
|
|
|
158
176
|
return maybeAccount;
|
|
159
177
|
}
|
|
160
178
|
|
|
179
|
+
/**
|
|
180
|
+
* Fetches a `StakingConfig` account, or the non-existing variant when the address holds no
|
|
181
|
+
* account or only lamports (see {@link accountIsCreated}).
|
|
182
|
+
* {@link decodeStakingConfig} throws for a lamports-only account instead.
|
|
183
|
+
*/
|
|
161
184
|
export async function fetchMaybeStakingConfig<TAddress extends string = string>(
|
|
162
185
|
rpc: Parameters<typeof fetchEncodedAccount>[0],
|
|
163
186
|
address: Address<TAddress>,
|
|
164
187
|
config?: FetchAccountConfig,
|
|
165
188
|
): Promise<MaybeAccount<StakingConfig, TAddress>> {
|
|
166
189
|
const maybeAccount = await fetchEncodedAccount(rpc, address, config);
|
|
167
|
-
return decodeStakingConfig(
|
|
190
|
+
return decodeStakingConfig(
|
|
191
|
+
accountIsCreated(maybeAccount)
|
|
192
|
+
? maybeAccount
|
|
193
|
+
: { address, exists: false },
|
|
194
|
+
);
|
|
168
195
|
}
|
|
169
196
|
|
|
197
|
+
/** Fetches `StakingConfig` accounts, throwing when any does not exist or only holds lamports. */
|
|
170
198
|
export async function fetchAllStakingConfig(
|
|
171
199
|
rpc: Parameters<typeof fetchEncodedAccounts>[0],
|
|
172
200
|
addresses: Array<Address>,
|
|
@@ -181,6 +209,11 @@ export async function fetchAllStakingConfig(
|
|
|
181
209
|
return maybeAccounts;
|
|
182
210
|
}
|
|
183
211
|
|
|
212
|
+
/**
|
|
213
|
+
* Fetches `StakingConfig` accounts, with the non-existing variant for each address that holds
|
|
214
|
+
* no account or only lamports (see {@link accountIsCreated}).
|
|
215
|
+
* {@link decodeStakingConfig} throws for a lamports-only account instead.
|
|
216
|
+
*/
|
|
184
217
|
export async function fetchAllMaybeStakingConfig(
|
|
185
218
|
rpc: Parameters<typeof fetchEncodedAccounts>[0],
|
|
186
219
|
addresses: Array<Address>,
|
|
@@ -188,7 +221,11 @@ export async function fetchAllMaybeStakingConfig(
|
|
|
188
221
|
): Promise<MaybeAccount<StakingConfig>[]> {
|
|
189
222
|
const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
|
|
190
223
|
return maybeAccounts.map((maybeAccount) =>
|
|
191
|
-
decodeStakingConfig(
|
|
224
|
+
decodeStakingConfig(
|
|
225
|
+
accountIsCreated(maybeAccount)
|
|
226
|
+
? maybeAccount
|
|
227
|
+
: { address: maybeAccount.address, exists: false },
|
|
228
|
+
),
|
|
192
229
|
);
|
|
193
230
|
}
|
|
194
231
|
|
|
@@ -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
|
|
|
48
49
|
export const USER_REWARD_DEBT_DISCRIMINATOR: ReadonlyUint8Array =
|
|
49
50
|
new Uint8Array([92, 166, 184, 23, 127, 201, 251, 148]);
|
|
@@ -133,9 +134,25 @@ export function getUserRewardDebtCodec(): FixedSizeCodec<
|
|
|
133
134
|
return combineCodec(getUserRewardDebtEncoder(), getUserRewardDebtDecoder());
|
|
134
135
|
}
|
|
135
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Decodes a `UserRewardDebt` account, throwing when another program owns it or its discriminator
|
|
139
|
+
* does not match.
|
|
140
|
+
*
|
|
141
|
+
* Unlike {@link fetchMaybeUserRewardDebt}, this throws for an address that only holds lamports rather
|
|
142
|
+
* than returning the non-existing variant: an account passed as existing must never come back as that
|
|
143
|
+
* variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
|
|
144
|
+
*/
|
|
136
145
|
export function decodeUserRewardDebt<TAddress extends string = string>(
|
|
137
146
|
encodedAccount: EncodedAccount<TAddress>,
|
|
138
147
|
): Account<UserRewardDebt, TAddress>;
|
|
148
|
+
/**
|
|
149
|
+
* Decodes a `UserRewardDebt` account, throwing when another program owns it or its discriminator
|
|
150
|
+
* does not match.
|
|
151
|
+
*
|
|
152
|
+
* Unlike {@link fetchMaybeUserRewardDebt}, this throws for an address that only holds lamports rather
|
|
153
|
+
* than returning the non-existing variant: an account passed as existing must never come back as that
|
|
154
|
+
* variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
|
|
155
|
+
*/
|
|
139
156
|
export function decodeUserRewardDebt<TAddress extends string = string>(
|
|
140
157
|
encodedAccount: MaybeEncodedAccount<TAddress>,
|
|
141
158
|
): MaybeAccount<UserRewardDebt, TAddress>;
|
|
@@ -170,6 +187,7 @@ export function decodeUserRewardDebt<TAddress extends string = string>(
|
|
|
170
187
|
);
|
|
171
188
|
}
|
|
172
189
|
|
|
190
|
+
/** Fetches a `UserRewardDebt` account, throwing when it does not exist or only holds lamports. */
|
|
173
191
|
export async function fetchUserRewardDebt<TAddress extends string = string>(
|
|
174
192
|
rpc: Parameters<typeof fetchEncodedAccount>[0],
|
|
175
193
|
address: Address<TAddress>,
|
|
@@ -180,6 +198,11 @@ export async function fetchUserRewardDebt<TAddress extends string = string>(
|
|
|
180
198
|
return maybeAccount;
|
|
181
199
|
}
|
|
182
200
|
|
|
201
|
+
/**
|
|
202
|
+
* Fetches a `UserRewardDebt` account, or the non-existing variant when the address holds no
|
|
203
|
+
* account or only lamports (see {@link accountIsCreated}).
|
|
204
|
+
* {@link decodeUserRewardDebt} throws for a lamports-only account instead.
|
|
205
|
+
*/
|
|
183
206
|
export async function fetchMaybeUserRewardDebt<
|
|
184
207
|
TAddress extends string = string,
|
|
185
208
|
>(
|
|
@@ -188,9 +211,14 @@ export async function fetchMaybeUserRewardDebt<
|
|
|
188
211
|
config?: FetchAccountConfig,
|
|
189
212
|
): Promise<MaybeAccount<UserRewardDebt, TAddress>> {
|
|
190
213
|
const maybeAccount = await fetchEncodedAccount(rpc, address, config);
|
|
191
|
-
return decodeUserRewardDebt(
|
|
214
|
+
return decodeUserRewardDebt(
|
|
215
|
+
accountIsCreated(maybeAccount)
|
|
216
|
+
? maybeAccount
|
|
217
|
+
: { address, exists: false },
|
|
218
|
+
);
|
|
192
219
|
}
|
|
193
220
|
|
|
221
|
+
/** Fetches `UserRewardDebt` accounts, throwing when any does not exist or only holds lamports. */
|
|
194
222
|
export async function fetchAllUserRewardDebt(
|
|
195
223
|
rpc: Parameters<typeof fetchEncodedAccounts>[0],
|
|
196
224
|
addresses: Array<Address>,
|
|
@@ -205,6 +233,11 @@ export async function fetchAllUserRewardDebt(
|
|
|
205
233
|
return maybeAccounts;
|
|
206
234
|
}
|
|
207
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Fetches `UserRewardDebt` accounts, with the non-existing variant for each address that holds
|
|
238
|
+
* no account or only lamports (see {@link accountIsCreated}).
|
|
239
|
+
* {@link decodeUserRewardDebt} throws for a lamports-only account instead.
|
|
240
|
+
*/
|
|
208
241
|
export async function fetchAllMaybeUserRewardDebt(
|
|
209
242
|
rpc: Parameters<typeof fetchEncodedAccounts>[0],
|
|
210
243
|
addresses: Array<Address>,
|
|
@@ -212,7 +245,11 @@ export async function fetchAllMaybeUserRewardDebt(
|
|
|
212
245
|
): Promise<MaybeAccount<UserRewardDebt>[]> {
|
|
213
246
|
const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
|
|
214
247
|
return maybeAccounts.map((maybeAccount) =>
|
|
215
|
-
decodeUserRewardDebt(
|
|
248
|
+
decodeUserRewardDebt(
|
|
249
|
+
accountIsCreated(maybeAccount)
|
|
250
|
+
? maybeAccount
|
|
251
|
+
: { address: maybeAccount.address, exists: false },
|
|
252
|
+
),
|
|
216
253
|
);
|
|
217
254
|
}
|
|
218
255
|
|
|
@@ -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
|
|
|
48
49
|
export const USER_STAKE_POSITION_DISCRIMINATOR: ReadonlyUint8Array =
|
|
49
50
|
new Uint8Array([123, 46, 248, 17, 240, 135, 201, 17]);
|
|
@@ -124,9 +125,25 @@ export function getUserStakePositionCodec(): FixedSizeCodec<
|
|
|
124
125
|
);
|
|
125
126
|
}
|
|
126
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Decodes a `UserStakePosition` account, throwing when another program owns it or its discriminator
|
|
130
|
+
* does not match.
|
|
131
|
+
*
|
|
132
|
+
* Unlike {@link fetchMaybeUserStakePosition}, this throws for an address that only holds lamports rather
|
|
133
|
+
* than returning the non-existing variant: an account passed as existing must never come back as that
|
|
134
|
+
* variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
|
|
135
|
+
*/
|
|
127
136
|
export function decodeUserStakePosition<TAddress extends string = string>(
|
|
128
137
|
encodedAccount: EncodedAccount<TAddress>,
|
|
129
138
|
): Account<UserStakePosition, TAddress>;
|
|
139
|
+
/**
|
|
140
|
+
* Decodes a `UserStakePosition` account, throwing when another program owns it or its discriminator
|
|
141
|
+
* does not match.
|
|
142
|
+
*
|
|
143
|
+
* Unlike {@link fetchMaybeUserStakePosition}, this throws for an address that only holds lamports rather
|
|
144
|
+
* than returning the non-existing variant: an account passed as existing must never come back as that
|
|
145
|
+
* variant typed as an `Account`. Apply {@link accountIsCreated} first to accounts fetched another way.
|
|
146
|
+
*/
|
|
130
147
|
export function decodeUserStakePosition<TAddress extends string = string>(
|
|
131
148
|
encodedAccount: MaybeEncodedAccount<TAddress>,
|
|
132
149
|
): MaybeAccount<UserStakePosition, TAddress>;
|
|
@@ -163,6 +180,7 @@ export function decodeUserStakePosition<TAddress extends string = string>(
|
|
|
163
180
|
);
|
|
164
181
|
}
|
|
165
182
|
|
|
183
|
+
/** Fetches a `UserStakePosition` account, throwing when it does not exist or only holds lamports. */
|
|
166
184
|
export async function fetchUserStakePosition<TAddress extends string = string>(
|
|
167
185
|
rpc: Parameters<typeof fetchEncodedAccount>[0],
|
|
168
186
|
address: Address<TAddress>,
|
|
@@ -177,6 +195,11 @@ export async function fetchUserStakePosition<TAddress extends string = string>(
|
|
|
177
195
|
return maybeAccount;
|
|
178
196
|
}
|
|
179
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Fetches a `UserStakePosition` account, or the non-existing variant when the address holds no
|
|
200
|
+
* account or only lamports (see {@link accountIsCreated}).
|
|
201
|
+
* {@link decodeUserStakePosition} throws for a lamports-only account instead.
|
|
202
|
+
*/
|
|
180
203
|
export async function fetchMaybeUserStakePosition<
|
|
181
204
|
TAddress extends string = string,
|
|
182
205
|
>(
|
|
@@ -185,9 +208,14 @@ export async function fetchMaybeUserStakePosition<
|
|
|
185
208
|
config?: FetchAccountConfig,
|
|
186
209
|
): Promise<MaybeAccount<UserStakePosition, TAddress>> {
|
|
187
210
|
const maybeAccount = await fetchEncodedAccount(rpc, address, config);
|
|
188
|
-
return decodeUserStakePosition(
|
|
211
|
+
return decodeUserStakePosition(
|
|
212
|
+
accountIsCreated(maybeAccount)
|
|
213
|
+
? maybeAccount
|
|
214
|
+
: { address, exists: false },
|
|
215
|
+
);
|
|
189
216
|
}
|
|
190
217
|
|
|
218
|
+
/** Fetches `UserStakePosition` accounts, throwing when any does not exist or only holds lamports. */
|
|
191
219
|
export async function fetchAllUserStakePosition(
|
|
192
220
|
rpc: Parameters<typeof fetchEncodedAccounts>[0],
|
|
193
221
|
addresses: Array<Address>,
|
|
@@ -202,6 +230,11 @@ export async function fetchAllUserStakePosition(
|
|
|
202
230
|
return maybeAccounts;
|
|
203
231
|
}
|
|
204
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Fetches `UserStakePosition` accounts, with the non-existing variant for each address that holds
|
|
235
|
+
* no account or only lamports (see {@link accountIsCreated}).
|
|
236
|
+
* {@link decodeUserStakePosition} throws for a lamports-only account instead.
|
|
237
|
+
*/
|
|
205
238
|
export async function fetchAllMaybeUserStakePosition(
|
|
206
239
|
rpc: Parameters<typeof fetchEncodedAccounts>[0],
|
|
207
240
|
addresses: Array<Address>,
|
|
@@ -209,7 +242,11 @@ export async function fetchAllMaybeUserStakePosition(
|
|
|
209
242
|
): Promise<MaybeAccount<UserStakePosition>[]> {
|
|
210
243
|
const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
|
|
211
244
|
return maybeAccounts.map((maybeAccount) =>
|
|
212
|
-
decodeUserStakePosition(
|
|
245
|
+
decodeUserStakePosition(
|
|
246
|
+
accountIsCreated(maybeAccount)
|
|
247
|
+
? maybeAccount
|
|
248
|
+
: { address: maybeAccount.address, exists: false },
|
|
249
|
+
),
|
|
213
250
|
);
|
|
214
251
|
}
|
|
215
252
|
|
|
@@ -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/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> {
|