@forgedevv/core 0.1.0 → 0.3.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/CHANGELOG.md +227 -4
- package/README.md +4 -3
- package/dist/addresses.d.ts +73 -7
- package/dist/addresses.js +96 -5
- package/dist/claims.d.ts +43 -6
- package/dist/claims.js +75 -16
- package/dist/config.d.ts +48 -37
- package/dist/config.js +282 -52
- package/dist/constants.d.ts +16 -0
- package/dist/constants.js +20 -0
- package/dist/index.d.ts +9 -5
- package/dist/index.js +15 -4
- package/dist/jupiter-referral.d.ts +334 -0
- package/dist/jupiter-referral.js +666 -0
- package/dist/launch-metadata.d.ts +5 -1
- package/dist/launch-metadata.js +2 -0
- package/dist/meteora-referral.d.ts +119 -0
- package/dist/meteora-referral.js +188 -0
- package/dist/payments.d.ts +17 -0
- package/dist/payments.js +20 -2
- package/dist/pool.d.ts +108 -17
- package/dist/pool.js +237 -60
- package/dist/position-claims.d.ts +215 -0
- package/dist/position-claims.js +457 -0
- package/dist/priority-fee.d.ts +202 -0
- package/dist/priority-fee.js +322 -0
- package/dist/reads.d.ts +20 -0
- package/dist/reads.js +39 -2
- package/dist/settings.d.ts +73 -0
- package/dist/settings.js +119 -0
- package/dist/swap.d.ts +95 -23
- package/dist/swap.js +144 -30
- package/dist/tx.d.ts +2 -0
- package/dist/tx.js +4 -2
- package/dist/validate.d.ts +14 -1
- package/dist/validate.js +86 -6
- package/package.json +3 -2
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jupiter referral fee claim (docs/METEORA.md "The platform fee", after graduation; D7, D8).
|
|
3
|
+
*
|
|
4
|
+
* After a coin graduates, trades go through Jupiter and FORGE's 50 bps integrator fee is collected
|
|
5
|
+
* by the Jupiter referral program into token accounts of FORGE's referral account
|
|
6
|
+
* (`FORGE_JUPITER_REFERRAL_ACCOUNT`), one per currency Jupiter collects in. Claiming is
|
|
7
|
+
* permissionless: it moves a token account's balance to the referral account's `partner` (the
|
|
8
|
+
* owner of the referral account: the FORGE revenue wallet since D34), minus the project's share.
|
|
9
|
+
* The revenue job of the signer then converts the balances to SOL and forwards them to the vault,
|
|
10
|
+
* which feeds the buyback (D8). The fee payer only pays the network fee and, the first time, the
|
|
11
|
+
* rent of the partner's token account.
|
|
12
|
+
*
|
|
13
|
+
* Built from the referral program's IDL (program `REFER4ZgmyYx9c6He5XfaTMiGfdLwRnkV4RPp9t9iF3`,
|
|
14
|
+
* `@jup-ag/referral-sdk` 0.3.0) with raw instructions: the SDK pulls Anchor, too heavy for the
|
|
15
|
+
* transaction core. Two collection accounts exist per mint and both are claimed when funded:
|
|
16
|
+
* - `claim`: the classic referral token account, PDA `["referral_ata", referral account, mint]`;
|
|
17
|
+
* - `claimV2`: the associated token account of the referral account (classic or Token-2022).
|
|
18
|
+
* Account order, PDA seeds and Anchor discriminators are the IDL's; the unit tests pin them.
|
|
19
|
+
*
|
|
20
|
+
* The Jupiter Plugin (Ultra) only pays referral accounts of Jupiter's Ultra project
|
|
21
|
+
* (`JUPITER_ULTRA_REFERRAL_PROJECT`), into their v2 collection accounts. `claim_v2` only accepts a
|
|
22
|
+
* NAMED referral account at the PDA `["referral", project, name]` (created with
|
|
23
|
+
* `initialize_referral_account_with_name`): an unnamed one (plain `initialize_referral_account`)
|
|
24
|
+
* can never claim them. So the builder refuses a referral account of another project
|
|
25
|
+
* (`project_mismatch`; `expectedProject` overrides the Ultra project), and builds a v2 claim only
|
|
26
|
+
* for a named PDA (`referral_account_unnamed` or `referral_account_not_pda` otherwise).
|
|
27
|
+
*
|
|
28
|
+
* Mainnet only: the referral program and Jupiter's fee collection live on mainnet-beta. The
|
|
29
|
+
* builder checks the connection's genesis hash and refuses any other cluster (`wrong_cluster`)
|
|
30
|
+
* before it reads any account, so the project and name checks never run on devnet (where neither
|
|
31
|
+
* the Ultra project nor Jupiter's fee collection exist): the devnet behaviour is unchanged.
|
|
32
|
+
* The partner can be asserted (`expectedPartner` = the revenue wallet): a referral account owned by
|
|
33
|
+
* anyone else is refused (`partner_mismatch`), fees would not reach the revenue wallet.
|
|
34
|
+
*
|
|
35
|
+
* The last section builds the one-off SETUP of that referral account (the signer's
|
|
36
|
+
* `--setup-revenue-referrals` command): `initialize_referral_account_with_name` under the Ultra
|
|
37
|
+
* project with the revenue wallet as partner, and the v2 collection accounts of the currencies
|
|
38
|
+
* Jupiter collects in. Only the payer signs; the program facts are listed there.
|
|
39
|
+
*/
|
|
40
|
+
import { createHash } from 'node:crypto';
|
|
41
|
+
import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID, createAssociatedTokenAccountIdempotentInstruction, getAssociatedTokenAddressSync, unpackAccount, } from '@solana/spl-token';
|
|
42
|
+
import { PublicKey, SystemProgram, Transaction, TransactionInstruction, } from '@solana/web3.js';
|
|
43
|
+
import { PaymentError, assertCluster } from './payments.js';
|
|
44
|
+
import { estimatePriorityFee, priorityFeeInstructions, resolvePriorityFeePolicy, writableAccountsOf, } from './priority-fee.js';
|
|
45
|
+
import { retryRpc } from './reads.js';
|
|
46
|
+
// ---------------------------------------------------------------------------------------------
|
|
47
|
+
// Constants (IDL)
|
|
48
|
+
// ---------------------------------------------------------------------------------------------
|
|
49
|
+
/** Jupiter referral program (mainnet-beta). */
|
|
50
|
+
export const JUPITER_REFERRAL_PROGRAM_ID = new PublicKey('REFER4ZgmyYx9c6He5XfaTMiGfdLwRnkV4RPp9t9iF3');
|
|
51
|
+
/**
|
|
52
|
+
* Jupiter's Ultra project in the referral program (mainnet-beta). The Jupiter Plugin and Ultra
|
|
53
|
+
* `/order` only collect the referral fee into referral accounts of this project.
|
|
54
|
+
* @see https://developers.jup.ag/docs/ultra/add-fees-to-ultra.md
|
|
55
|
+
*/
|
|
56
|
+
export const JUPITER_ULTRA_REFERRAL_PROJECT = new PublicKey('DkiqsTrw1u1bYFumumC7sCG2S8K25qc2vemJFHyW2wJc');
|
|
57
|
+
function anchorDiscriminator(name) {
|
|
58
|
+
return createHash('sha256').update(name).digest().subarray(0, 8);
|
|
59
|
+
}
|
|
60
|
+
export const JUPITER_REFERRAL_ACCOUNT_DISCRIMINATOR = anchorDiscriminator('account:ReferralAccount');
|
|
61
|
+
export const JUPITER_PROJECT_DISCRIMINATOR = anchorDiscriminator('account:Project');
|
|
62
|
+
export const JUPITER_CLAIM_DISCRIMINATOR = anchorDiscriminator('global:claim');
|
|
63
|
+
export const JUPITER_CLAIM_V2_DISCRIMINATOR = anchorDiscriminator('global:claim_v2');
|
|
64
|
+
export const JUPITER_CREATE_ADMIN_TOKEN_ACCOUNT_DISCRIMINATOR = anchorDiscriminator('global:create_admin_token_account');
|
|
65
|
+
export class JupiterReferralError extends Error {
|
|
66
|
+
code;
|
|
67
|
+
constructor(code, message, options) {
|
|
68
|
+
super(message, options);
|
|
69
|
+
this.name = 'JupiterReferralError';
|
|
70
|
+
this.code = code;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function readString(data, offset) {
|
|
74
|
+
if (data.length < offset + 4)
|
|
75
|
+
throw new RangeError('string length out of bounds');
|
|
76
|
+
const length = data.readUInt32LE(offset);
|
|
77
|
+
const start = offset + 4;
|
|
78
|
+
if (data.length < start + length)
|
|
79
|
+
throw new RangeError('string out of bounds');
|
|
80
|
+
return { value: data.subarray(start, start + length).toString('utf8'), next: start + length };
|
|
81
|
+
}
|
|
82
|
+
/** `ReferralAccount { partner, project, share_bps: u16, name: Option<String> }`. */
|
|
83
|
+
export function decodeJupiterReferralAccount(data) {
|
|
84
|
+
const fail = (reason) => new JupiterReferralError('not_a_referral_account', reason);
|
|
85
|
+
if (data.length < 8 + 32 + 32 + 2 + 1)
|
|
86
|
+
throw fail('referral account data too short');
|
|
87
|
+
if (!data.subarray(0, 8).equals(JUPITER_REFERRAL_ACCOUNT_DISCRIMINATOR)) {
|
|
88
|
+
throw fail('not a Jupiter referral account');
|
|
89
|
+
}
|
|
90
|
+
const partner = new PublicKey(data.subarray(8, 40));
|
|
91
|
+
const project = new PublicKey(data.subarray(40, 72));
|
|
92
|
+
const shareBps = data.readUInt16LE(72);
|
|
93
|
+
const tag = data[74];
|
|
94
|
+
let name = null;
|
|
95
|
+
if (tag === 1) {
|
|
96
|
+
try {
|
|
97
|
+
name = readString(data, 75).value;
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
throw new JupiterReferralError('not_a_referral_account', 'referral account name invalid', {
|
|
101
|
+
cause: error,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else if (tag !== 0) {
|
|
106
|
+
throw fail('referral account name tag invalid');
|
|
107
|
+
}
|
|
108
|
+
return { partner, project, shareBps, name };
|
|
109
|
+
}
|
|
110
|
+
/** `Project { base, admin, name: String, default_share_bps: u16 }`. */
|
|
111
|
+
export function decodeJupiterProject(data) {
|
|
112
|
+
const fail = (reason, cause) => new JupiterReferralError('not_a_project', reason, { cause });
|
|
113
|
+
if (data.length < 8 + 32 + 32 + 4 + 2)
|
|
114
|
+
throw fail('project data too short');
|
|
115
|
+
if (!data.subarray(0, 8).equals(JUPITER_PROJECT_DISCRIMINATOR)) {
|
|
116
|
+
throw fail('not a Jupiter referral project');
|
|
117
|
+
}
|
|
118
|
+
const base = new PublicKey(data.subarray(8, 40));
|
|
119
|
+
const admin = new PublicKey(data.subarray(40, 72));
|
|
120
|
+
let name;
|
|
121
|
+
try {
|
|
122
|
+
name = readString(data, 72);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
throw fail('project name invalid', error);
|
|
126
|
+
}
|
|
127
|
+
if (data.length < name.next + 2)
|
|
128
|
+
throw fail('project data too short');
|
|
129
|
+
return { base, admin, name: name.value, defaultShareBps: data.readUInt16LE(name.next) };
|
|
130
|
+
}
|
|
131
|
+
// ---------------------------------------------------------------------------------------------
|
|
132
|
+
// Addresses
|
|
133
|
+
// ---------------------------------------------------------------------------------------------
|
|
134
|
+
/** Classic referral token account: PDA `["referral_ata", referral account, mint]`. */
|
|
135
|
+
export function deriveJupiterReferralTokenAccount(referralAccount, mint, programId = JUPITER_REFERRAL_PROGRAM_ID) {
|
|
136
|
+
return PublicKey.findProgramAddressSync([Buffer.from('referral_ata', 'utf8'), referralAccount.toBuffer(), mint.toBuffer()], programId)[0];
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Named referral account: PDA `["referral", project, name]` (`initialize_referral_account_with_name`,
|
|
140
|
+
* and the signer seeds of `claim_v2`). Throws when the name is longer than a seed (32 bytes).
|
|
141
|
+
*/
|
|
142
|
+
export function deriveJupiterReferralAccount(project, name, programId = JUPITER_REFERRAL_PROGRAM_ID) {
|
|
143
|
+
return PublicKey.findProgramAddressSync([Buffer.from('referral', 'utf8'), project.toBuffer(), Buffer.from(name, 'utf8')], programId)[0];
|
|
144
|
+
}
|
|
145
|
+
/** V2 referral token account: the associated token account of the referral account. */
|
|
146
|
+
export function deriveJupiterReferralTokenAccountV2(referralAccount, mint, tokenProgram) {
|
|
147
|
+
return getAssociatedTokenAddressSync(mint, referralAccount, true, tokenProgram);
|
|
148
|
+
}
|
|
149
|
+
/** Project authority: PDA `["project_authority", project.base]`. */
|
|
150
|
+
export function deriveJupiterProjectAuthority(base, programId = JUPITER_REFERRAL_PROGRAM_ID) {
|
|
151
|
+
return PublicKey.findProgramAddressSync([Buffer.from('project_authority', 'utf8'), base.toBuffer()], programId)[0];
|
|
152
|
+
}
|
|
153
|
+
const ro = (pubkey) => ({ pubkey, isSigner: false, isWritable: false });
|
|
154
|
+
const rw = (pubkey) => ({ pubkey, isSigner: false, isWritable: true });
|
|
155
|
+
function assertTokenProgram(id) {
|
|
156
|
+
if (!id.equals(TOKEN_PROGRAM_ID) && !id.equals(TOKEN_2022_PROGRAM_ID)) {
|
|
157
|
+
throw new JupiterReferralError('invalid_params', 'tokenProgram is not a token program');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* `claim` (v1) or `claim_v2`: the IDL's accounts in the IDL's order. The two differ only in the
|
|
162
|
+
* position of the program accounts at the end (v1: associated token, system, token; v2: system,
|
|
163
|
+
* token, associated token). No arguments.
|
|
164
|
+
*/
|
|
165
|
+
export function buildJupiterReferralClaimIx(accounts, version, programId = JUPITER_REFERRAL_PROGRAM_ID) {
|
|
166
|
+
assertTokenProgram(accounts.tokenProgram);
|
|
167
|
+
if (version !== 'v1' && version !== 'v2') {
|
|
168
|
+
throw new JupiterReferralError('invalid_params', 'version must be v1 or v2');
|
|
169
|
+
}
|
|
170
|
+
const shared = [
|
|
171
|
+
{ pubkey: accounts.payer, isSigner: true, isWritable: true },
|
|
172
|
+
ro(accounts.project),
|
|
173
|
+
ro(accounts.admin),
|
|
174
|
+
rw(accounts.projectAdminTokenAccount),
|
|
175
|
+
ro(accounts.referralAccount),
|
|
176
|
+
rw(accounts.referralTokenAccount),
|
|
177
|
+
ro(accounts.partner),
|
|
178
|
+
rw(accounts.partnerTokenAccount),
|
|
179
|
+
ro(accounts.mint),
|
|
180
|
+
];
|
|
181
|
+
const programs = version === 'v1'
|
|
182
|
+
? [ro(ASSOCIATED_TOKEN_PROGRAM_ID), ro(SystemProgram.programId), ro(accounts.tokenProgram)]
|
|
183
|
+
: [ro(SystemProgram.programId), ro(accounts.tokenProgram), ro(ASSOCIATED_TOKEN_PROGRAM_ID)];
|
|
184
|
+
return new TransactionInstruction({
|
|
185
|
+
programId,
|
|
186
|
+
keys: [...shared, ...programs],
|
|
187
|
+
data: Buffer.from(version === 'v1' ? JUPITER_CLAIM_DISCRIMINATOR : JUPITER_CLAIM_V2_DISCRIMINATOR),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
/** `create_admin_token_account`: needed once per mint before the first claim of that mint. */
|
|
191
|
+
export function buildJupiterCreateAdminTokenAccountIx(accounts, programId = JUPITER_REFERRAL_PROGRAM_ID) {
|
|
192
|
+
assertTokenProgram(accounts.tokenProgram);
|
|
193
|
+
return new TransactionInstruction({
|
|
194
|
+
programId,
|
|
195
|
+
keys: [
|
|
196
|
+
ro(accounts.project),
|
|
197
|
+
rw(accounts.projectAuthority),
|
|
198
|
+
ro(accounts.admin),
|
|
199
|
+
rw(accounts.projectAdminTokenAccount),
|
|
200
|
+
ro(accounts.mint),
|
|
201
|
+
ro(SystemProgram.programId),
|
|
202
|
+
ro(accounts.tokenProgram),
|
|
203
|
+
ro(ASSOCIATED_TOKEN_PROGRAM_ID),
|
|
204
|
+
],
|
|
205
|
+
data: Buffer.from(JUPITER_CREATE_ADMIN_TOKEN_ACCOUNT_DISCRIMINATOR),
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Compute unit limit of a Jupiter referral claim with a priority fee: up to two token account
|
|
210
|
+
* creations and two claims (each a token transfer pair), with a wide margin.
|
|
211
|
+
*/
|
|
212
|
+
export const JUPITER_REFERRAL_CLAIM_COMPUTE_UNITS = 300_000;
|
|
213
|
+
function tokenAmount(address, info, tokenProgram) {
|
|
214
|
+
if (!info || !info.owner.equals(tokenProgram))
|
|
215
|
+
return 0n;
|
|
216
|
+
try {
|
|
217
|
+
const account = unpackAccount(address, info, tokenProgram);
|
|
218
|
+
return account.isFrozen ? 0n : account.amount;
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return 0n;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* `claim_v2` signs with the PDA `["referral", project, name]`: refuses (typed error) a referral
|
|
226
|
+
* account without a name, or one that is not that PDA, before a v2 claim is built.
|
|
227
|
+
*/
|
|
228
|
+
function assertClaimableV2(referralAccount, referral, programId) {
|
|
229
|
+
if (referral.name === null) {
|
|
230
|
+
throw new JupiterReferralError('referral_account_unnamed', 'the referral account has no name: claim_v2 needs a referral account created with a name ' +
|
|
231
|
+
'(PDA ["referral", project, name]), so its v2 collection account cannot be claimed');
|
|
232
|
+
}
|
|
233
|
+
let expected;
|
|
234
|
+
try {
|
|
235
|
+
expected = deriveJupiterReferralAccount(referral.project, referral.name, programId);
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
throw new JupiterReferralError('referral_account_not_pda', 'the referral account name is not a valid PDA seed, so claim_v2 cannot sign for it', { cause: error });
|
|
239
|
+
}
|
|
240
|
+
if (!expected.equals(referralAccount)) {
|
|
241
|
+
throw new JupiterReferralError('referral_account_not_pda', 'the referral account is not the PDA ["referral", project, name] that claim_v2 signs with');
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Reads the referral account, its project, the mint and the collection accounts, and builds the
|
|
246
|
+
* claim(s) of every funded collection account of `mint` to the partner's associated token account.
|
|
247
|
+
* Mainnet-beta only (genesis hash). Returns `transaction: null` when nothing is claimable.
|
|
248
|
+
* The referral account must belong to `expectedProject` (default: Jupiter's Ultra project), and a
|
|
249
|
+
* funded v2 collection account is only claimed for a named referral PDA (typed errors otherwise).
|
|
250
|
+
*/
|
|
251
|
+
export async function buildJupiterReferralClaimTx(p) {
|
|
252
|
+
const { connection } = p;
|
|
253
|
+
const commitment = p.commitment ?? 'confirmed';
|
|
254
|
+
const programId = p.programId ?? JUPITER_REFERRAL_PROGRAM_ID;
|
|
255
|
+
if (p.priority !== undefined)
|
|
256
|
+
resolvePriorityFeePolicy(p.priority);
|
|
257
|
+
const expectedProject = p.expectedProject ?? JUPITER_ULTRA_REFERRAL_PROJECT;
|
|
258
|
+
const retry = p.retry ?? {};
|
|
259
|
+
if (p.payer.equals(p.referralAccount) || p.mint.equals(p.referralAccount)) {
|
|
260
|
+
throw new JupiterReferralError('invalid_params', 'payer, mint and referral account must differ');
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
await assertCluster(connection, 'mainnet-beta', retry);
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
if (error instanceof PaymentError && error.code === 'wrong_cluster') {
|
|
267
|
+
throw new JupiterReferralError('wrong_cluster', 'Jupiter referral claims only exist on mainnet-beta', { cause: error });
|
|
268
|
+
}
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
271
|
+
const [referralInfo, mintInfo] = await retryRpc(() => connection.getMultipleAccountsInfo([p.referralAccount, p.mint], commitment), retry);
|
|
272
|
+
if (!referralInfo) {
|
|
273
|
+
throw new JupiterReferralError('referral_account_not_found', 'referral account not found');
|
|
274
|
+
}
|
|
275
|
+
if (!referralInfo.owner.equals(programId)) {
|
|
276
|
+
throw new JupiterReferralError('not_a_referral_account', 'referral account is not owned by the Jupiter referral program');
|
|
277
|
+
}
|
|
278
|
+
const referral = decodeJupiterReferralAccount(Buffer.from(referralInfo.data));
|
|
279
|
+
if (p.expectedPartner && !referral.partner.equals(p.expectedPartner)) {
|
|
280
|
+
throw new JupiterReferralError('partner_mismatch', 'the referral account partner is not the expected receiver');
|
|
281
|
+
}
|
|
282
|
+
if (!referral.project.equals(expectedProject)) {
|
|
283
|
+
throw new JupiterReferralError('project_mismatch', `the referral account belongs to project ${referral.project.toBase58()}, not to the ` +
|
|
284
|
+
`expected project ${expectedProject.toBase58()} (Jupiter Ultra by default)`);
|
|
285
|
+
}
|
|
286
|
+
if (!mintInfo)
|
|
287
|
+
throw new JupiterReferralError('mint_not_found', 'mint not found');
|
|
288
|
+
const tokenProgram = mintInfo.owner;
|
|
289
|
+
if (!tokenProgram.equals(TOKEN_PROGRAM_ID) && !tokenProgram.equals(TOKEN_2022_PROGRAM_ID)) {
|
|
290
|
+
throw new JupiterReferralError('not_a_mint', 'mint is not owned by a token program');
|
|
291
|
+
}
|
|
292
|
+
const projectInfo = await retryRpc(() => connection.getAccountInfo(referral.project, commitment), retry);
|
|
293
|
+
if (!projectInfo)
|
|
294
|
+
throw new JupiterReferralError('project_not_found', 'project not found');
|
|
295
|
+
if (!projectInfo.owner.equals(programId)) {
|
|
296
|
+
throw new JupiterReferralError('not_a_project', 'project is not owned by the referral program');
|
|
297
|
+
}
|
|
298
|
+
const project = decodeJupiterProject(Buffer.from(projectInfo.data));
|
|
299
|
+
const v1Account = deriveJupiterReferralTokenAccount(p.referralAccount, p.mint, programId);
|
|
300
|
+
const v2Account = deriveJupiterReferralTokenAccountV2(p.referralAccount, p.mint, tokenProgram);
|
|
301
|
+
const partnerTokenAccount = getAssociatedTokenAddressSync(p.mint, referral.partner, true, tokenProgram);
|
|
302
|
+
const adminTokenAccount = getAssociatedTokenAddressSync(p.mint, project.admin, true, tokenProgram);
|
|
303
|
+
const [v1Info, v2Info, partnerInfo, adminInfo] = await retryRpc(() => connection.getMultipleAccountsInfo([v1Account, v2Account, partnerTokenAccount, adminTokenAccount], commitment), retry);
|
|
304
|
+
const claims = [];
|
|
305
|
+
const v1Amount = tokenAmount(v1Account, v1Info ?? null, tokenProgram);
|
|
306
|
+
if (v1Amount > 0n)
|
|
307
|
+
claims.push({ version: 'v1', referralTokenAccount: v1Account, amount: v1Amount });
|
|
308
|
+
const v2Amount = tokenAmount(v2Account, v2Info ?? null, tokenProgram);
|
|
309
|
+
if (v2Amount > 0n) {
|
|
310
|
+
assertClaimableV2(p.referralAccount, referral, programId);
|
|
311
|
+
claims.push({ version: 'v2', referralTokenAccount: v2Account, amount: v2Amount });
|
|
312
|
+
}
|
|
313
|
+
const projectAuthority = deriveJupiterProjectAuthority(project.base, programId);
|
|
314
|
+
const summary = {
|
|
315
|
+
referralAccount: p.referralAccount,
|
|
316
|
+
partner: referral.partner,
|
|
317
|
+
partnerTokenAccount,
|
|
318
|
+
project: referral.project,
|
|
319
|
+
admin: project.admin,
|
|
320
|
+
projectAuthority,
|
|
321
|
+
mint: p.mint,
|
|
322
|
+
tokenProgram,
|
|
323
|
+
claims,
|
|
324
|
+
createsPartnerTokenAccount: claims.length > 0 && !partnerInfo,
|
|
325
|
+
createsAdminTokenAccount: claims.length > 0 && !adminInfo,
|
|
326
|
+
};
|
|
327
|
+
if (claims.length === 0) {
|
|
328
|
+
return { transaction: null, lastValidBlockHeight: null, summary, priorityFee: null };
|
|
329
|
+
}
|
|
330
|
+
const transaction = new Transaction();
|
|
331
|
+
if (summary.createsPartnerTokenAccount) {
|
|
332
|
+
transaction.add(createAssociatedTokenAccountIdempotentInstruction(p.payer, partnerTokenAccount, referral.partner, p.mint, tokenProgram));
|
|
333
|
+
}
|
|
334
|
+
if (summary.createsAdminTokenAccount) {
|
|
335
|
+
transaction.add(buildJupiterCreateAdminTokenAccountIx({
|
|
336
|
+
project: referral.project,
|
|
337
|
+
projectAuthority,
|
|
338
|
+
admin: project.admin,
|
|
339
|
+
projectAdminTokenAccount: adminTokenAccount,
|
|
340
|
+
mint: p.mint,
|
|
341
|
+
tokenProgram,
|
|
342
|
+
}, programId));
|
|
343
|
+
}
|
|
344
|
+
for (const claim of claims) {
|
|
345
|
+
transaction.add(buildJupiterReferralClaimIx({
|
|
346
|
+
payer: p.payer,
|
|
347
|
+
project: referral.project,
|
|
348
|
+
admin: project.admin,
|
|
349
|
+
projectAdminTokenAccount: adminTokenAccount,
|
|
350
|
+
referralAccount: p.referralAccount,
|
|
351
|
+
referralTokenAccount: claim.referralTokenAccount,
|
|
352
|
+
partner: referral.partner,
|
|
353
|
+
partnerTokenAccount,
|
|
354
|
+
mint: p.mint,
|
|
355
|
+
tokenProgram,
|
|
356
|
+
}, claim.version, programId));
|
|
357
|
+
}
|
|
358
|
+
let priorityFee = null;
|
|
359
|
+
if (p.priority !== undefined) {
|
|
360
|
+
priorityFee = await estimatePriorityFee({
|
|
361
|
+
connection,
|
|
362
|
+
writableAccounts: writableAccountsOf(transaction.instructions),
|
|
363
|
+
computeUnitLimit: JUPITER_REFERRAL_CLAIM_COMPUTE_UNITS,
|
|
364
|
+
setting: p.priority,
|
|
365
|
+
});
|
|
366
|
+
transaction.instructions.unshift(...priorityFeeInstructions(priorityFee));
|
|
367
|
+
}
|
|
368
|
+
const latest = await retryRpc(() => connection.getLatestBlockhash(commitment), retry);
|
|
369
|
+
transaction.feePayer = p.payer;
|
|
370
|
+
transaction.recentBlockhash = latest.blockhash;
|
|
371
|
+
return { transaction, lastValidBlockHeight: latest.lastValidBlockHeight, summary, priorityFee };
|
|
372
|
+
}
|
|
373
|
+
// ---------------------------------------------------------------------------------------------
|
|
374
|
+
// Setup of FORGE's referral account (pure builders; docs/MAINNET.md section 5)
|
|
375
|
+
// ---------------------------------------------------------------------------------------------
|
|
376
|
+
//
|
|
377
|
+
// Verified against the program source (github.com/TeamRaccoons/referral, program/programs/
|
|
378
|
+
// referral/src/instructions/initialize_referral_account_with_name.rs, lib.rs, claim_v2.rs; the
|
|
379
|
+
// IDL of @jup-ag/referral-sdk 0.3.0 agrees):
|
|
380
|
+
// - `initialize_referral_account_with_name(params: { name: String })`: accounts `payer` (mut,
|
|
381
|
+
// signer), `partner` (SystemAccount, read-only, NOT a signer), `project` (read-only, PDA
|
|
382
|
+
// `["project", base]`), `referral_account` (mut, init, PDA `["referral", project, name]`,
|
|
383
|
+
// `ReferralAccount::LEN` = 274 bytes, rent paid by `payer`), `system_program`. The share is the
|
|
384
|
+
// project's `default_share_bps`. Name: `params.name.chars().count() < 20`, so 1 to 19
|
|
385
|
+
// characters; the name is also a PDA seed, so at most 32 UTF-8 bytes.
|
|
386
|
+
// - The v2 collection accounts `claim_v2` empties are the associated token accounts of the
|
|
387
|
+
// referral account (`associated_token::authority = referral_account`): the SDK's
|
|
388
|
+
// `initializeReferralTokenAccountV2` is a plain `CreateIdempotent` of the Associated Token
|
|
389
|
+
// program, paid by the payer, which is what `buildJupiterReferralTokenAccountV2Ix` builds.
|
|
390
|
+
// Only the payer signs anywhere: the partner (the revenue wallet) never does.
|
|
391
|
+
export const JUPITER_INITIALIZE_REFERRAL_ACCOUNT_WITH_NAME_DISCRIMINATOR = anchorDiscriminator('global:initialize_referral_account_with_name');
|
|
392
|
+
/** `ReferralAccount::LEN` = 8 + 32 + 32 + 200 + 2: what the initialization allocates. */
|
|
393
|
+
export const JUPITER_REFERRAL_ACCOUNT_SIZE = 274;
|
|
394
|
+
/** `params.name.chars().count() < 20` in the program. */
|
|
395
|
+
export const JUPITER_REFERRAL_NAME_MAX_CHARS = 19;
|
|
396
|
+
/** The name is a PDA seed: at most 32 bytes. */
|
|
397
|
+
export const JUPITER_REFERRAL_NAME_MAX_BYTES = 32;
|
|
398
|
+
/**
|
|
399
|
+
* Compute unit limit of the setup transaction with a priority fee: one Anchor account
|
|
400
|
+
* initialization and up to a few associated token account creations, with a wide margin.
|
|
401
|
+
*/
|
|
402
|
+
export const JUPITER_REFERRAL_SETUP_COMPUTE_UNITS = 150_000;
|
|
403
|
+
/**
|
|
404
|
+
* Checks a referral account name: 1 to 19 characters (code points, the program's rule), at most
|
|
405
|
+
* 32 UTF-8 bytes (a PDA seed), and FORGE's own restriction to printable characters without
|
|
406
|
+
* whitespace (a name goes into environment files and documentation). Returns the name.
|
|
407
|
+
* @throws JupiterReferralError `invalid_params`
|
|
408
|
+
*/
|
|
409
|
+
export function assertJupiterReferralName(name) {
|
|
410
|
+
const fail = (reason) => new JupiterReferralError('invalid_params', reason);
|
|
411
|
+
if (typeof name !== 'string' || name.length === 0) {
|
|
412
|
+
throw fail('the name must be a non-empty string');
|
|
413
|
+
}
|
|
414
|
+
const chars = [...name].length;
|
|
415
|
+
if (chars > JUPITER_REFERRAL_NAME_MAX_CHARS) {
|
|
416
|
+
throw fail(`the name has ${chars} characters, at most ${JUPITER_REFERRAL_NAME_MAX_CHARS} are allowed`);
|
|
417
|
+
}
|
|
418
|
+
const bytes = Buffer.byteLength(name, 'utf8');
|
|
419
|
+
if (bytes > JUPITER_REFERRAL_NAME_MAX_BYTES) {
|
|
420
|
+
throw fail(`the name is ${bytes} bytes, at most ${JUPITER_REFERRAL_NAME_MAX_BYTES} are allowed`);
|
|
421
|
+
}
|
|
422
|
+
if (/[\s\p{Cc}\p{Cf}]/u.test(name)) {
|
|
423
|
+
throw fail('the name must not contain whitespace or control characters');
|
|
424
|
+
}
|
|
425
|
+
return name;
|
|
426
|
+
}
|
|
427
|
+
/** Borsh `String`: u32 little-endian byte length, then the UTF-8 bytes. */
|
|
428
|
+
export function encodeJupiterReferralName(name) {
|
|
429
|
+
const bytes = Buffer.from(assertJupiterReferralName(name), 'utf8');
|
|
430
|
+
const length = Buffer.alloc(4);
|
|
431
|
+
length.writeUInt32LE(bytes.length);
|
|
432
|
+
return Buffer.concat([length, bytes]);
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* `initialize_referral_account_with_name`: the IDL's accounts in the IDL's order, the Anchor
|
|
436
|
+
* discriminator then the Borsh-encoded `{ name }`. Refuses a referral account that is not the
|
|
437
|
+
* PDA of the project and the name, and a payer, partner, project or referral account that
|
|
438
|
+
* coincide.
|
|
439
|
+
*/
|
|
440
|
+
export function buildJupiterInitializeReferralAccountWithNameIx(accounts, name, programId = JUPITER_REFERRAL_PROGRAM_ID) {
|
|
441
|
+
const data = encodeJupiterReferralName(name);
|
|
442
|
+
const expected = deriveJupiterReferralAccount(accounts.project, name, programId);
|
|
443
|
+
if (!expected.equals(accounts.referralAccount)) {
|
|
444
|
+
throw new JupiterReferralError('invalid_params', 'referralAccount is not the PDA ["referral", project, name] of that name');
|
|
445
|
+
}
|
|
446
|
+
const keys = [accounts.payer, accounts.partner, accounts.project, accounts.referralAccount];
|
|
447
|
+
if (new Set(keys.map((k) => k.toBase58())).size !== keys.length) {
|
|
448
|
+
throw new JupiterReferralError('invalid_params', 'payer, partner, project and referral account must differ');
|
|
449
|
+
}
|
|
450
|
+
return new TransactionInstruction({
|
|
451
|
+
programId,
|
|
452
|
+
keys: [
|
|
453
|
+
{ pubkey: accounts.payer, isSigner: true, isWritable: true },
|
|
454
|
+
ro(accounts.partner),
|
|
455
|
+
ro(accounts.project),
|
|
456
|
+
rw(accounts.referralAccount),
|
|
457
|
+
ro(SystemProgram.programId),
|
|
458
|
+
],
|
|
459
|
+
data: Buffer.concat([JUPITER_INITIALIZE_REFERRAL_ACCOUNT_WITH_NAME_DISCRIMINATOR, data]),
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* The v2 collection account of `mint` (what the Jupiter Plugin pays and `claim_v2` empties): an
|
|
464
|
+
* idempotent creation of the referral account's associated token account, paid by `payer`
|
|
465
|
+
* (the SDK's `initializeReferralTokenAccountV2`). Idempotent on-chain: an existing account is
|
|
466
|
+
* left as it is.
|
|
467
|
+
*/
|
|
468
|
+
export function buildJupiterReferralTokenAccountV2Ix(p) {
|
|
469
|
+
assertTokenProgram(p.tokenProgram);
|
|
470
|
+
if (p.payer.equals(p.referralAccount) || p.mint.equals(p.referralAccount)) {
|
|
471
|
+
throw new JupiterReferralError('invalid_params', 'payer, mint and referral account must differ');
|
|
472
|
+
}
|
|
473
|
+
const tokenAccount = deriveJupiterReferralTokenAccountV2(p.referralAccount, p.mint, p.tokenProgram);
|
|
474
|
+
return {
|
|
475
|
+
tokenAccount,
|
|
476
|
+
instruction: createAssociatedTokenAccountIdempotentInstruction(p.payer, tokenAccount, p.referralAccount, p.mint, p.tokenProgram),
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Unsigned transaction setting up FORGE's Jupiter referral account: the optional compute budget,
|
|
481
|
+
* `initialize_referral_account_with_name` when `createReferralAccount`, then one v2 collection
|
|
482
|
+
* account creation per `tokenAccounts` entry. Refuses an empty plan and a duplicate mint. Does
|
|
483
|
+
* not read the chain: inspect first (`readJupiterReferralSetup`). Mainnet-beta is the only
|
|
484
|
+
* cluster where the program and the Ultra project exist; the read helper enforces it.
|
|
485
|
+
*/
|
|
486
|
+
export function buildJupiterReferralSetupTx(p) {
|
|
487
|
+
const programId = p.programId ?? JUPITER_REFERRAL_PROGRAM_ID;
|
|
488
|
+
const project = p.project ?? JUPITER_ULTRA_REFERRAL_PROJECT;
|
|
489
|
+
const name = assertJupiterReferralName(p.name);
|
|
490
|
+
if (typeof p.blockhash !== 'string' || p.blockhash.length === 0) {
|
|
491
|
+
throw new JupiterReferralError('invalid_params', 'blockhash required');
|
|
492
|
+
}
|
|
493
|
+
if (!Number.isInteger(p.lastValidBlockHeight) || p.lastValidBlockHeight < 0) {
|
|
494
|
+
throw new JupiterReferralError('invalid_params', 'lastValidBlockHeight must be a non-negative integer');
|
|
495
|
+
}
|
|
496
|
+
if (!p.createReferralAccount && p.tokenAccounts.length === 0) {
|
|
497
|
+
throw new JupiterReferralError('invalid_params', 'nothing to set up');
|
|
498
|
+
}
|
|
499
|
+
const seen = new Set();
|
|
500
|
+
for (const { mint } of p.tokenAccounts) {
|
|
501
|
+
if (seen.has(mint.toBase58())) {
|
|
502
|
+
throw new JupiterReferralError('invalid_params', 'duplicate mint in tokenAccounts');
|
|
503
|
+
}
|
|
504
|
+
seen.add(mint.toBase58());
|
|
505
|
+
}
|
|
506
|
+
const referralAccount = deriveJupiterReferralAccount(project, name, programId);
|
|
507
|
+
const transaction = new Transaction({
|
|
508
|
+
feePayer: p.payer,
|
|
509
|
+
blockhash: p.blockhash,
|
|
510
|
+
lastValidBlockHeight: p.lastValidBlockHeight,
|
|
511
|
+
});
|
|
512
|
+
if (p.priorityFee && p.priorityFee.microLamports > 0) {
|
|
513
|
+
transaction.add(...priorityFeeInstructions(p.priorityFee));
|
|
514
|
+
}
|
|
515
|
+
if (p.createReferralAccount) {
|
|
516
|
+
transaction.add(buildJupiterInitializeReferralAccountWithNameIx({ payer: p.payer, partner: p.partner, project, referralAccount }, name, programId));
|
|
517
|
+
}
|
|
518
|
+
const tokenAccounts = [];
|
|
519
|
+
for (const entry of p.tokenAccounts) {
|
|
520
|
+
const built = buildJupiterReferralTokenAccountV2Ix({
|
|
521
|
+
payer: p.payer,
|
|
522
|
+
referralAccount,
|
|
523
|
+
mint: entry.mint,
|
|
524
|
+
tokenProgram: entry.tokenProgram,
|
|
525
|
+
});
|
|
526
|
+
transaction.add(built.instruction);
|
|
527
|
+
tokenAccounts.push({ ...entry, tokenAccount: built.tokenAccount });
|
|
528
|
+
}
|
|
529
|
+
return {
|
|
530
|
+
transaction,
|
|
531
|
+
lastValidBlockHeight: p.lastValidBlockHeight,
|
|
532
|
+
referralAccount,
|
|
533
|
+
project,
|
|
534
|
+
name,
|
|
535
|
+
tokenAccounts,
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* What the referral account address holds, against the expected one: absent, ready (a referral
|
|
540
|
+
* account of `project` with `partner` and exactly `name`), or a mismatch with its reason. The
|
|
541
|
+
* caller derives the address as the PDA of the project and the name.
|
|
542
|
+
*/
|
|
543
|
+
export function inspectJupiterReferralAccount(info, expected) {
|
|
544
|
+
if (!info)
|
|
545
|
+
return { status: 'absent' };
|
|
546
|
+
const programId = expected.programId ?? JUPITER_REFERRAL_PROGRAM_ID;
|
|
547
|
+
const mismatch = (reason, detail) => ({ status: 'mismatch', reason, detail });
|
|
548
|
+
if (!info.owner.equals(programId)) {
|
|
549
|
+
return mismatch('not_a_referral_account', `the address is in use by an account owned by ${info.owner.toBase58()}`);
|
|
550
|
+
}
|
|
551
|
+
let account;
|
|
552
|
+
try {
|
|
553
|
+
account = decodeJupiterReferralAccount(Buffer.from(info.data));
|
|
554
|
+
}
|
|
555
|
+
catch (error) {
|
|
556
|
+
return mismatch('not_a_referral_account', error.message);
|
|
557
|
+
}
|
|
558
|
+
if (!account.partner.equals(expected.partner)) {
|
|
559
|
+
return mismatch('partner_mismatch', `the referral account partner is ${account.partner.toBase58()}`);
|
|
560
|
+
}
|
|
561
|
+
if (!account.project.equals(expected.project)) {
|
|
562
|
+
return mismatch('project_mismatch', `the referral account project is ${account.project.toBase58()}`);
|
|
563
|
+
}
|
|
564
|
+
if (account.name !== expected.name) {
|
|
565
|
+
return mismatch('name_mismatch', account.name === null
|
|
566
|
+
? 'the referral account has no name'
|
|
567
|
+
: `the referral account name is "${account.name}"`);
|
|
568
|
+
}
|
|
569
|
+
return { status: 'ready', account };
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* What the v2 collection account address holds: absent, a token account of `mint` owned by the
|
|
573
|
+
* referral account, or not.
|
|
574
|
+
*/
|
|
575
|
+
export function inspectJupiterReferralTokenAccountV2(address, info, expected) {
|
|
576
|
+
if (!info)
|
|
577
|
+
return { status: 'absent' };
|
|
578
|
+
const mismatch = (reason, detail) => ({ status: 'mismatch', reason, detail });
|
|
579
|
+
if (!info.owner.equals(expected.tokenProgram)) {
|
|
580
|
+
return mismatch('not_a_token_account', `the address is in use by an account owned by ${info.owner.toBase58()}`);
|
|
581
|
+
}
|
|
582
|
+
let account;
|
|
583
|
+
try {
|
|
584
|
+
account = unpackAccount(address, info, expected.tokenProgram);
|
|
585
|
+
}
|
|
586
|
+
catch {
|
|
587
|
+
return mismatch('not_a_token_account', 'the account data is not a token account');
|
|
588
|
+
}
|
|
589
|
+
if (!account.mint.equals(expected.mint)) {
|
|
590
|
+
return mismatch('wrong_mint', `the token account mint is ${account.mint.toBase58()}`);
|
|
591
|
+
}
|
|
592
|
+
if (!account.owner.equals(expected.referralAccount)) {
|
|
593
|
+
return mismatch('wrong_owner', `the token account owner is ${account.owner.toBase58()}`);
|
|
594
|
+
}
|
|
595
|
+
return { status: 'ready', amount: account.amount };
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Reads what the setup needs: the project (must exist and decode), the mints (their token
|
|
599
|
+
* program), the named referral account and its v2 collection accounts, each inspected. Mainnet
|
|
600
|
+
* only (`wrong_cluster` on any other cluster, before any read). The caller decides what to
|
|
601
|
+
* create (the absent ones) and refuses on any mismatch.
|
|
602
|
+
*/
|
|
603
|
+
export async function readJupiterReferralSetup(p) {
|
|
604
|
+
const programId = p.programId ?? JUPITER_REFERRAL_PROGRAM_ID;
|
|
605
|
+
const project = p.project ?? JUPITER_ULTRA_REFERRAL_PROJECT;
|
|
606
|
+
const name = assertJupiterReferralName(p.name);
|
|
607
|
+
const commitment = p.commitment ?? 'confirmed';
|
|
608
|
+
const retry = p.retry ?? {};
|
|
609
|
+
if (p.mints.length === 0)
|
|
610
|
+
throw new JupiterReferralError('invalid_params', 'at least one mint');
|
|
611
|
+
if (new Set(p.mints.map((m) => m.toBase58())).size !== p.mints.length) {
|
|
612
|
+
throw new JupiterReferralError('invalid_params', 'duplicate mint');
|
|
613
|
+
}
|
|
614
|
+
try {
|
|
615
|
+
await assertCluster(p.connection, 'mainnet-beta', retry);
|
|
616
|
+
}
|
|
617
|
+
catch (error) {
|
|
618
|
+
if (error instanceof PaymentError && error.code === 'wrong_cluster') {
|
|
619
|
+
throw new JupiterReferralError('wrong_cluster', 'the Jupiter referral program and the Ultra project only exist on mainnet-beta', { cause: error });
|
|
620
|
+
}
|
|
621
|
+
throw error;
|
|
622
|
+
}
|
|
623
|
+
const referralAccount = deriveJupiterReferralAccount(project, name, programId);
|
|
624
|
+
const [projectInfo, referralInfo, ...mintInfos] = await retryRpc(() => p.connection.getMultipleAccountsInfo([project, referralAccount, ...p.mints], commitment), retry);
|
|
625
|
+
if (!projectInfo)
|
|
626
|
+
throw new JupiterReferralError('project_not_found', 'project not found');
|
|
627
|
+
if (!projectInfo.owner.equals(programId)) {
|
|
628
|
+
throw new JupiterReferralError('not_a_project', 'project is not owned by the referral program');
|
|
629
|
+
}
|
|
630
|
+
const projectAccount = decodeJupiterProject(Buffer.from(projectInfo.data));
|
|
631
|
+
const entries = p.mints.map((mint, i) => {
|
|
632
|
+
const info = mintInfos[i];
|
|
633
|
+
if (!info) {
|
|
634
|
+
throw new JupiterReferralError('mint_not_found', `mint ${mint.toBase58()} not found`);
|
|
635
|
+
}
|
|
636
|
+
if (!info.owner.equals(TOKEN_PROGRAM_ID) && !info.owner.equals(TOKEN_2022_PROGRAM_ID)) {
|
|
637
|
+
throw new JupiterReferralError('not_a_mint', `${mint.toBase58()} is not owned by a token program`);
|
|
638
|
+
}
|
|
639
|
+
return {
|
|
640
|
+
mint,
|
|
641
|
+
tokenProgram: info.owner,
|
|
642
|
+
tokenAccount: deriveJupiterReferralTokenAccountV2(referralAccount, mint, info.owner),
|
|
643
|
+
};
|
|
644
|
+
});
|
|
645
|
+
const tokenInfos = await retryRpc(() => p.connection.getMultipleAccountsInfo(entries.map((e) => e.tokenAccount), commitment), retry);
|
|
646
|
+
return {
|
|
647
|
+
project,
|
|
648
|
+
projectAccount,
|
|
649
|
+
name,
|
|
650
|
+
referralAccount,
|
|
651
|
+
referral: inspectJupiterReferralAccount(referralInfo ?? null, {
|
|
652
|
+
partner: p.partner,
|
|
653
|
+
project,
|
|
654
|
+
name,
|
|
655
|
+
programId,
|
|
656
|
+
}),
|
|
657
|
+
tokenAccounts: entries.map((entry, i) => ({
|
|
658
|
+
...entry,
|
|
659
|
+
status: inspectJupiterReferralTokenAccountV2(entry.tokenAccount, tokenInfos[i] ?? null, {
|
|
660
|
+
referralAccount,
|
|
661
|
+
mint: entry.mint,
|
|
662
|
+
tokenProgram: entry.tokenProgram,
|
|
663
|
+
}),
|
|
664
|
+
})),
|
|
665
|
+
};
|
|
666
|
+
}
|