@wzrd_sol/sdk 0.1.1 → 0.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 +2 -0
- package/dist/accounts.d.ts +13 -0
- package/dist/accounts.js +42 -1
- package/dist/accounts.test.d.ts +1 -0
- package/dist/accounts.test.js +47 -0
- package/dist/agent-auth.d.ts +89 -0
- package/dist/agent-auth.js +147 -0
- package/dist/agent-loop.d.ts +87 -0
- package/dist/agent-loop.js +388 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +3 -0
- package/dist/index.d.ts +15 -6
- package/dist/index.js +13 -5
- package/dist/instructions.d.ts +551 -5
- package/dist/instructions.js +1466 -24
- package/dist/instructions.test.d.ts +7 -0
- package/dist/instructions.test.js +318 -0
- package/dist/model-selector.d.ts +141 -0
- package/dist/model-selector.js +247 -0
- package/dist/nav.d.ts +40 -0
- package/dist/nav.js +39 -0
- package/dist/nav.test.d.ts +4 -0
- package/dist/nav.test.js +98 -0
- package/dist/pda.d.ts +4 -0
- package/dist/pda.js +10 -1
- package/dist/stream.d.ts +88 -0
- package/dist/stream.js +220 -0
- package/dist/stream.test.d.ts +7 -0
- package/dist/stream.test.js +296 -0
- package/package.json +9 -3
package/dist/instructions.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Builds deposit_market, settle_market, and claim_global TransactionInstructions
|
|
5
5
|
* that the wallet adapter signs directly — no server signing needed.
|
|
6
6
|
*/
|
|
7
|
-
import { SystemProgram, TransactionInstruction, } from '@solana/web3.js';
|
|
7
|
+
import { PublicKey, SystemProgram, TransactionInstruction, } from '@solana/web3.js';
|
|
8
8
|
import { PROGRAM_ID, TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID, } from './constants.js';
|
|
9
9
|
import { getProtocolStatePDA, getMarketVaultPDA, getUserPositionPDA, getGlobalRootConfigPDA, getClaimStatePDA, getChannelConfigV2PDA, getAta, } from './pda.js';
|
|
10
10
|
import { parseMarketVault, parseProtocolState } from './accounts.js';
|
|
@@ -41,7 +41,97 @@ export function createAtaIdempotentIx(payer, ata, owner, mint, tokenProgramId =
|
|
|
41
41
|
data: Buffer.from([1]), // CreateIdempotent
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
|
-
// ──
|
|
44
|
+
// ── Meteora DLMM LP Helpers ──────────────────────────────
|
|
45
|
+
/**
|
|
46
|
+
* Meteora DLMM program ID (mainnet).
|
|
47
|
+
* Used for addLiquidity / removeLiquidity instructions.
|
|
48
|
+
*/
|
|
49
|
+
export const DLMM_PROGRAM_ID = new PublicKey('LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo');
|
|
50
|
+
/**
|
|
51
|
+
* Build a Meteora DLMM `addLiquidity` TransactionInstruction.
|
|
52
|
+
*
|
|
53
|
+
* This is the agent LP pattern: deposit CCM + counterpart (SOL/USDC) into
|
|
54
|
+
* a DLMM concentrated liquidity position to earn swap fees from other agents.
|
|
55
|
+
*
|
|
56
|
+
* Note: For Token-2022 mints like CCM, transfer fees are deducted automatically
|
|
57
|
+
* on transfer_checked. The DLMM program handles this internally.
|
|
58
|
+
*
|
|
59
|
+
* @param pool - DLMM pool address (e.g., CCM/SOL, CCM/USDC)
|
|
60
|
+
* @param position - Position account (create with DLMM initPosition first)
|
|
61
|
+
* @param owner - Position owner (signer)
|
|
62
|
+
* @param tokenXMint - Pool token X mint
|
|
63
|
+
* @param tokenYMint - Pool token Y mint
|
|
64
|
+
* @param userTokenX - Owner's token X ATA
|
|
65
|
+
* @param userTokenY - Owner's token Y ATA
|
|
66
|
+
* @param reserveX - Pool reserve X account
|
|
67
|
+
* @param reserveY - Pool reserve Y account
|
|
68
|
+
* @param binArrayLower - Lower bin array account
|
|
69
|
+
* @param binArrayUpper - Upper bin array account
|
|
70
|
+
* @param amountX - Token X amount (native units)
|
|
71
|
+
* @param amountY - Token Y amount (native units)
|
|
72
|
+
*/
|
|
73
|
+
export function createAddLiquidityIx(pool, position, owner, tokenXMint, tokenYMint, userTokenX, userTokenY, reserveX, reserveY, binArrayLower, binArrayUpper, amountX, amountY, activeBinId, binCount, tokenXProgram = TOKEN_PROGRAM_ID, tokenYProgram = TOKEN_2022_PROGRAM_ID) {
|
|
74
|
+
// Meteora addLiquidityByStrategy disc = SHA-256("global:add_liquidity_by_strategy")[0..8]
|
|
75
|
+
// Strategy: Spot distribution centered on activeBinId
|
|
76
|
+
const disc = Buffer.from([28, 190, 93, 94, 229, 198, 83, 56]); // add_liquidity_by_strategy
|
|
77
|
+
const data = Buffer.alloc(8 + 8 + 8 + 4 + 4 + 1);
|
|
78
|
+
disc.copy(data, 0);
|
|
79
|
+
data.writeBigUInt64LE(amountX, 8);
|
|
80
|
+
data.writeBigUInt64LE(amountY, 16);
|
|
81
|
+
data.writeInt32LE(activeBinId, 24);
|
|
82
|
+
data.writeInt32LE(binCount, 28);
|
|
83
|
+
data.writeUInt8(0, 32); // StrategyType::Spot
|
|
84
|
+
return new TransactionInstruction({
|
|
85
|
+
programId: DLMM_PROGRAM_ID,
|
|
86
|
+
keys: [
|
|
87
|
+
{ pubkey: position, isSigner: false, isWritable: true },
|
|
88
|
+
{ pubkey: pool, isSigner: false, isWritable: true },
|
|
89
|
+
{ pubkey: binArrayLower, isSigner: false, isWritable: true },
|
|
90
|
+
{ pubkey: binArrayUpper, isSigner: false, isWritable: true },
|
|
91
|
+
{ pubkey: owner, isSigner: true, isWritable: false },
|
|
92
|
+
{ pubkey: reserveX, isSigner: false, isWritable: true },
|
|
93
|
+
{ pubkey: reserveY, isSigner: false, isWritable: true },
|
|
94
|
+
{ pubkey: userTokenX, isSigner: false, isWritable: true },
|
|
95
|
+
{ pubkey: userTokenY, isSigner: false, isWritable: true },
|
|
96
|
+
{ pubkey: tokenXMint, isSigner: false, isWritable: false },
|
|
97
|
+
{ pubkey: tokenYMint, isSigner: false, isWritable: false },
|
|
98
|
+
{ pubkey: tokenXProgram, isSigner: false, isWritable: false },
|
|
99
|
+
{ pubkey: tokenYProgram, isSigner: false, isWritable: false },
|
|
100
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
101
|
+
],
|
|
102
|
+
data,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Build a Meteora DLMM `removeLiquidity` TransactionInstruction.
|
|
107
|
+
*
|
|
108
|
+
* Agent pattern: withdraw LP position to recover tokens and accrued fees.
|
|
109
|
+
*/
|
|
110
|
+
export function createRemoveLiquidityIx(pool, position, owner, reserveX, reserveY, userTokenX, userTokenY, tokenXMint, tokenYMint, binArrayLower, binArrayUpper, bpsBasisPointsToRemove, tokenXProgram = TOKEN_PROGRAM_ID, tokenYProgram = TOKEN_2022_PROGRAM_ID) {
|
|
111
|
+
const disc = Buffer.from([80, 85, 209, 72, 24, 206, 177, 108]); // remove_liquidity
|
|
112
|
+
const data = Buffer.alloc(8 + 2);
|
|
113
|
+
disc.copy(data, 0);
|
|
114
|
+
data.writeUInt16LE(bpsBasisPointsToRemove, 8); // 10000 = 100%
|
|
115
|
+
return new TransactionInstruction({
|
|
116
|
+
programId: DLMM_PROGRAM_ID,
|
|
117
|
+
keys: [
|
|
118
|
+
{ pubkey: position, isSigner: false, isWritable: true },
|
|
119
|
+
{ pubkey: pool, isSigner: false, isWritable: true },
|
|
120
|
+
{ pubkey: binArrayLower, isSigner: false, isWritable: true },
|
|
121
|
+
{ pubkey: binArrayUpper, isSigner: false, isWritable: true },
|
|
122
|
+
{ pubkey: owner, isSigner: true, isWritable: false },
|
|
123
|
+
{ pubkey: reserveX, isSigner: false, isWritable: true },
|
|
124
|
+
{ pubkey: reserveY, isSigner: false, isWritable: true },
|
|
125
|
+
{ pubkey: userTokenX, isSigner: false, isWritable: true },
|
|
126
|
+
{ pubkey: userTokenY, isSigner: false, isWritable: true },
|
|
127
|
+
{ pubkey: tokenXMint, isSigner: false, isWritable: false },
|
|
128
|
+
{ pubkey: tokenYMint, isSigner: false, isWritable: false },
|
|
129
|
+
{ pubkey: tokenXProgram, isSigner: false, isWritable: false },
|
|
130
|
+
{ pubkey: tokenYProgram, isSigner: false, isWritable: false },
|
|
131
|
+
],
|
|
132
|
+
data,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
45
135
|
/**
|
|
46
136
|
* Build a `deposit_market` TransactionInstruction.
|
|
47
137
|
*
|
|
@@ -115,48 +205,52 @@ amount, programId = PROGRAM_ID) {
|
|
|
115
205
|
* 5. user_vlofi_ata (writable)
|
|
116
206
|
* 6. vault_usdc_ata (writable)
|
|
117
207
|
* 7. user_usdc_ata (writable)
|
|
118
|
-
* 8. ccm_mint (
|
|
119
|
-
* 9. user_ccm_ata (
|
|
208
|
+
* 8. ccm_mint (legacy mainnet ABI only)
|
|
209
|
+
* 9. user_ccm_ata (legacy mainnet ABI only)
|
|
120
210
|
* 10. token_program (readonly) — legacy SPL (USDC)
|
|
121
211
|
* 11. token_2022_program (readonly) — vLOFI burn
|
|
122
|
-
* 12. ccm_token_program (readonly) —
|
|
212
|
+
* 12. ccm_token_program (readonly) — Token-2022 for legacy mainnet ABI
|
|
213
|
+
*
|
|
214
|
+
* The deployed mainnet program still expects the legacy CCM accounts even though
|
|
215
|
+
* the instruction no longer mints CCM. In `auto` mode we include them for mainnet
|
|
216
|
+
* and use the slimmer layout elsewhere.
|
|
123
217
|
*
|
|
124
|
-
* @returns Array
|
|
218
|
+
* @returns Array containing the settle IX.
|
|
125
219
|
*/
|
|
126
|
-
export async function createSettleMarketIx(connection, user, marketId, programId = PROGRAM_ID) {
|
|
220
|
+
export async function createSettleMarketIx(connection, user, marketId, programId = PROGRAM_ID, options = {}) {
|
|
127
221
|
const protocolState = getProtocolStatePDA(programId);
|
|
128
222
|
const marketVault = getMarketVaultPDA(protocolState, marketId, programId);
|
|
129
223
|
const userPosition = getUserPositionPDA(marketVault, user, programId);
|
|
130
|
-
//
|
|
224
|
+
// 'auto' resolves based on program ID: mainnet uses legacy_ccm layout
|
|
225
|
+
const accountsMode = options.accountsMode === 'auto' || !options.accountsMode
|
|
226
|
+
? (programId.equals(PROGRAM_ID) ? 'legacy_ccm' : 'current')
|
|
227
|
+
: options.accountsMode;
|
|
228
|
+
// Fetch vault + protocol state to discover mints/accounts.
|
|
131
229
|
const vaultInfo = await connection.getAccountInfo(marketVault);
|
|
132
230
|
if (!vaultInfo)
|
|
133
231
|
throw new Error(`MarketVault not found for market ${marketId}`);
|
|
134
232
|
const vault = parseMarketVault(Buffer.from(vaultInfo.data));
|
|
135
|
-
// Fetch protocol state to discover CCM mint
|
|
136
233
|
const protocolInfo = await connection.getAccountInfo(protocolState);
|
|
137
234
|
if (!protocolInfo)
|
|
138
235
|
throw new Error('ProtocolState not found');
|
|
139
236
|
const protocol = parseProtocolState(Buffer.from(protocolInfo.data));
|
|
140
|
-
// Detect CCM mint's token program (Token-2022 on mainnet, legacy SPL on devnet)
|
|
141
|
-
const ccmMintInfo = await connection.getAccountInfo(protocol.mint);
|
|
142
|
-
if (!ccmMintInfo)
|
|
143
|
-
throw new Error('CCM mint account not found');
|
|
144
|
-
const ccmTokenProgram = ccmMintInfo.owner;
|
|
145
237
|
const userVlofiAta = getAta(user, vault.vlofiMint, TOKEN_PROGRAM_ID);
|
|
146
238
|
const userUsdcAta = getAta(user, vault.depositMint, TOKEN_PROGRAM_ID);
|
|
147
|
-
const userCcmAta = getAta(user, protocol.mint,
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
239
|
+
const userCcmAta = getAta(user, protocol.mint, TOKEN_2022_PROGRAM_ID);
|
|
240
|
+
const ixs = [];
|
|
241
|
+
if (accountsMode === 'legacy_ccm') {
|
|
242
|
+
// The deployed mainnet settle ABI still validates the user's CCM ATA even though
|
|
243
|
+
// no mint_to CPI occurs anymore. Make the ATA creation idempotent so settle works
|
|
244
|
+
// whether or not the wallet has claimed before.
|
|
245
|
+
ixs.push(createAtaIdempotentIx(user, userCcmAta, user, protocol.mint, TOKEN_2022_PROGRAM_ID));
|
|
246
|
+
}
|
|
152
247
|
// Instruction data: [8 disc][8 market_id LE]
|
|
153
248
|
const disc = await anchorDisc('settle_market');
|
|
154
249
|
const data = Buffer.alloc(16);
|
|
155
250
|
disc.copy(data, 0);
|
|
156
251
|
data.writeBigUInt64LE(BigInt(marketId), 8);
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
keys: [
|
|
252
|
+
const keys = accountsMode === 'legacy_ccm'
|
|
253
|
+
? [
|
|
160
254
|
{ pubkey: user, isSigner: true, isWritable: true },
|
|
161
255
|
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
162
256
|
{ pubkey: marketVault, isSigner: false, isWritable: true },
|
|
@@ -169,8 +263,23 @@ export async function createSettleMarketIx(connection, user, marketId, programId
|
|
|
169
263
|
{ pubkey: userCcmAta, isSigner: false, isWritable: true },
|
|
170
264
|
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
171
265
|
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
172
|
-
{ pubkey:
|
|
173
|
-
]
|
|
266
|
+
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
267
|
+
]
|
|
268
|
+
: [
|
|
269
|
+
{ pubkey: user, isSigner: true, isWritable: true },
|
|
270
|
+
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
271
|
+
{ pubkey: marketVault, isSigner: false, isWritable: true },
|
|
272
|
+
{ pubkey: userPosition, isSigner: false, isWritable: true },
|
|
273
|
+
{ pubkey: vault.vlofiMint, isSigner: false, isWritable: true },
|
|
274
|
+
{ pubkey: userVlofiAta, isSigner: false, isWritable: true },
|
|
275
|
+
{ pubkey: vault.vaultAta, isSigner: false, isWritable: true },
|
|
276
|
+
{ pubkey: userUsdcAta, isSigner: false, isWritable: true },
|
|
277
|
+
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
278
|
+
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
279
|
+
];
|
|
280
|
+
ixs.push(new TransactionInstruction({
|
|
281
|
+
programId,
|
|
282
|
+
keys,
|
|
174
283
|
data,
|
|
175
284
|
}));
|
|
176
285
|
return ixs;
|
|
@@ -285,6 +394,64 @@ proofHex, programId = PROGRAM_ID) {
|
|
|
285
394
|
}));
|
|
286
395
|
return ixs;
|
|
287
396
|
}
|
|
397
|
+
/**
|
|
398
|
+
* Build a `claim_global_v2` TransactionInstruction (V5 leaf format).
|
|
399
|
+
*
|
|
400
|
+
* Same accounts as claim_global, but args split cumulative_total into
|
|
401
|
+
* base_yield + attention_bonus for V5 merkle leaf verification.
|
|
402
|
+
*/
|
|
403
|
+
export async function createClaimGlobalV2Ix(connection, claimer, rootSeq, baseYield, attentionBonus, proofHex, programId = PROGRAM_ID) {
|
|
404
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
405
|
+
const protocolInfo = await connection.getAccountInfo(protocolState);
|
|
406
|
+
if (!protocolInfo)
|
|
407
|
+
throw new Error('ProtocolState not found');
|
|
408
|
+
const protocol = parseProtocolState(Buffer.from(protocolInfo.data));
|
|
409
|
+
const ccmMint = protocol.mint;
|
|
410
|
+
const globalRootConfig = getGlobalRootConfigPDA(ccmMint, programId);
|
|
411
|
+
const claimState = getClaimStatePDA(ccmMint, claimer, programId);
|
|
412
|
+
const treasuryAta = getAta(protocolState, ccmMint, TOKEN_2022_PROGRAM_ID);
|
|
413
|
+
const claimerAta = getAta(claimer, ccmMint, TOKEN_2022_PROGRAM_ID);
|
|
414
|
+
const ixs = [
|
|
415
|
+
createAtaIdempotentIx(claimer, claimerAta, claimer, ccmMint, TOKEN_2022_PROGRAM_ID),
|
|
416
|
+
];
|
|
417
|
+
const proofBytes = proofHex.map((h) => Buffer.from(h, 'hex'));
|
|
418
|
+
// Data: [8 disc][8 root_seq][8 base_yield][8 attention_bonus][4 proof_len][N*32 proof]
|
|
419
|
+
const disc = await anchorDisc('claim_global_v2');
|
|
420
|
+
const dataLen = 8 + 8 + 8 + 8 + 4 + proofBytes.length * 32;
|
|
421
|
+
const data = Buffer.alloc(dataLen);
|
|
422
|
+
let offset = 0;
|
|
423
|
+
disc.copy(data, offset);
|
|
424
|
+
offset += 8;
|
|
425
|
+
data.writeBigUInt64LE(BigInt(rootSeq), offset);
|
|
426
|
+
offset += 8;
|
|
427
|
+
data.writeBigUInt64LE(BigInt(baseYield), offset);
|
|
428
|
+
offset += 8;
|
|
429
|
+
data.writeBigUInt64LE(BigInt(attentionBonus), offset);
|
|
430
|
+
offset += 8;
|
|
431
|
+
data.writeUInt32LE(proofBytes.length, offset);
|
|
432
|
+
offset += 4;
|
|
433
|
+
for (const node of proofBytes) {
|
|
434
|
+
node.copy(data, offset);
|
|
435
|
+
offset += 32;
|
|
436
|
+
}
|
|
437
|
+
ixs.push(new TransactionInstruction({
|
|
438
|
+
programId,
|
|
439
|
+
keys: [
|
|
440
|
+
{ pubkey: claimer, isSigner: true, isWritable: true },
|
|
441
|
+
{ pubkey: protocolState, isSigner: false, isWritable: true },
|
|
442
|
+
{ pubkey: globalRootConfig, isSigner: false, isWritable: false },
|
|
443
|
+
{ pubkey: claimState, isSigner: false, isWritable: true },
|
|
444
|
+
{ pubkey: ccmMint, isSigner: false, isWritable: false },
|
|
445
|
+
{ pubkey: treasuryAta, isSigner: false, isWritable: true },
|
|
446
|
+
{ pubkey: claimerAta, isSigner: false, isWritable: true },
|
|
447
|
+
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
448
|
+
{ pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
449
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
450
|
+
],
|
|
451
|
+
data,
|
|
452
|
+
}));
|
|
453
|
+
return ixs;
|
|
454
|
+
}
|
|
288
455
|
// ── Admin Instruction Builders ────────────────────────
|
|
289
456
|
/**
|
|
290
457
|
* Build a `create_channel_config_v2` TransactionInstruction.
|
|
@@ -330,3 +497,1278 @@ mint, subject, authority, creatorWallet, creatorFeeBps, programId = PROGRAM_ID)
|
|
|
330
497
|
data,
|
|
331
498
|
});
|
|
332
499
|
}
|
|
500
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
501
|
+
// Extended Instruction Builders (Phase 2 + Full On-Chain Coverage)
|
|
502
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
503
|
+
/** Sysvar Instructions program address (required by Kamino CPI). */
|
|
504
|
+
const SYSVAR_INSTRUCTIONS_ID = new PublicKey('Sysvar1nstructions1111111111111111111111111');
|
|
505
|
+
/** PDA seed constant for legacy protocol state (seeds = ["protocol", mint]). */
|
|
506
|
+
const PROTOCOL_SEED = Buffer.from('protocol');
|
|
507
|
+
/** PDA seed constant for strategy vault. */
|
|
508
|
+
const STRATEGY_VAULT_SEED = Buffer.from('strategy_vault');
|
|
509
|
+
/** PDA seed constant for prediction market state. */
|
|
510
|
+
const MARKET_STATE_SEED = Buffer.from('market');
|
|
511
|
+
/** PDA seed for channel stake pool. */
|
|
512
|
+
const CHANNEL_STAKE_POOL_SEED = Buffer.from('channel_pool');
|
|
513
|
+
/** PDA seed for channel user stake. */
|
|
514
|
+
const CHANNEL_USER_STAKE_SEED = Buffer.from('channel_user');
|
|
515
|
+
/** PDA seed for soulbound NFT mint. */
|
|
516
|
+
const STAKE_NFT_MINT_SEED = Buffer.from('stake_nft');
|
|
517
|
+
/** PDA seed for stake vault (holds staked CCM). */
|
|
518
|
+
const STAKE_VAULT_SEED = Buffer.from('stake_vault');
|
|
519
|
+
// ── PDA derivation helpers (local, not exported from pda.ts) ─────────
|
|
520
|
+
function getStrategyVaultPDA(marketVault, programId = PROGRAM_ID) {
|
|
521
|
+
return PublicKey.findProgramAddressSync([STRATEGY_VAULT_SEED, marketVault.toBuffer()], programId)[0];
|
|
522
|
+
}
|
|
523
|
+
function getMarketStatePDA(ccmMint, marketId, programId = PROGRAM_ID) {
|
|
524
|
+
const idBuf = Buffer.alloc(8);
|
|
525
|
+
idBuf.writeBigUInt64LE(BigInt(marketId));
|
|
526
|
+
return PublicKey.findProgramAddressSync([MARKET_STATE_SEED, ccmMint.toBuffer(), idBuf], programId)[0];
|
|
527
|
+
}
|
|
528
|
+
// ── Prediction market PDA helpers ────────────────────────────────────
|
|
529
|
+
const PM_VAULT_SEED = Buffer.from('market_vault');
|
|
530
|
+
const MARKET_YES_MINT_SEED = Buffer.from('market_yes');
|
|
531
|
+
const MARKET_NO_MINT_SEED = Buffer.from('market_no');
|
|
532
|
+
const MARKET_MINT_AUTH_SEED = Buffer.from('market_auth');
|
|
533
|
+
function getPredictionVaultPDA(ccmMint, marketId, programId = PROGRAM_ID) {
|
|
534
|
+
const idBuf = Buffer.alloc(8);
|
|
535
|
+
idBuf.writeBigUInt64LE(BigInt(marketId));
|
|
536
|
+
return PublicKey.findProgramAddressSync([PM_VAULT_SEED, ccmMint.toBuffer(), idBuf], programId)[0];
|
|
537
|
+
}
|
|
538
|
+
function getPredictionYesMintPDA(ccmMint, marketId, programId = PROGRAM_ID) {
|
|
539
|
+
const idBuf = Buffer.alloc(8);
|
|
540
|
+
idBuf.writeBigUInt64LE(BigInt(marketId));
|
|
541
|
+
return PublicKey.findProgramAddressSync([MARKET_YES_MINT_SEED, ccmMint.toBuffer(), idBuf], programId)[0];
|
|
542
|
+
}
|
|
543
|
+
function getPredictionNoMintPDA(ccmMint, marketId, programId = PROGRAM_ID) {
|
|
544
|
+
const idBuf = Buffer.alloc(8);
|
|
545
|
+
idBuf.writeBigUInt64LE(BigInt(marketId));
|
|
546
|
+
return PublicKey.findProgramAddressSync([MARKET_NO_MINT_SEED, ccmMint.toBuffer(), idBuf], programId)[0];
|
|
547
|
+
}
|
|
548
|
+
function getPredictionMintAuthorityPDA(ccmMint, marketId, programId = PROGRAM_ID) {
|
|
549
|
+
const idBuf = Buffer.alloc(8);
|
|
550
|
+
idBuf.writeBigUInt64LE(BigInt(marketId));
|
|
551
|
+
return PublicKey.findProgramAddressSync([MARKET_MINT_AUTH_SEED, ccmMint.toBuffer(), idBuf], programId)[0];
|
|
552
|
+
}
|
|
553
|
+
function getLegacyProtocolStatePDA(ccmMint, programId = PROGRAM_ID) {
|
|
554
|
+
return PublicKey.findProgramAddressSync([PROTOCOL_SEED, ccmMint.toBuffer()], programId)[0];
|
|
555
|
+
}
|
|
556
|
+
function getStakePoolPDA(channelConfig, programId = PROGRAM_ID) {
|
|
557
|
+
return PublicKey.findProgramAddressSync([CHANNEL_STAKE_POOL_SEED, channelConfig.toBuffer()], programId)[0];
|
|
558
|
+
}
|
|
559
|
+
function getStakeVaultPDA(stakePool, programId = PROGRAM_ID) {
|
|
560
|
+
return PublicKey.findProgramAddressSync([STAKE_VAULT_SEED, stakePool.toBuffer()], programId)[0];
|
|
561
|
+
}
|
|
562
|
+
function getUserStakePDA(channelConfig, user, programId = PROGRAM_ID) {
|
|
563
|
+
return PublicKey.findProgramAddressSync([CHANNEL_USER_STAKE_SEED, channelConfig.toBuffer(), user.toBuffer()], programId)[0];
|
|
564
|
+
}
|
|
565
|
+
function getStakeNftMintPDA(stakePool, user, programId = PROGRAM_ID) {
|
|
566
|
+
return PublicKey.findProgramAddressSync([STAKE_NFT_MINT_SEED, stakePool.toBuffer(), user.toBuffer()], programId)[0];
|
|
567
|
+
}
|
|
568
|
+
function getPriceFeedPDA(label, programId = PROGRAM_ID) {
|
|
569
|
+
return PublicKey.findProgramAddressSync([Buffer.from('price_feed'), label], programId)[0];
|
|
570
|
+
}
|
|
571
|
+
// ── 1. createCreateMarketIx ──────────────────────────────────────────
|
|
572
|
+
/**
|
|
573
|
+
* Build a `create_market` TransactionInstruction (prediction markets).
|
|
574
|
+
*
|
|
575
|
+
* Accounts (order must match CreateMarket struct in markets.rs):
|
|
576
|
+
* 0. authority (signer, writable)
|
|
577
|
+
* 1. protocol_state (readonly)
|
|
578
|
+
* 2. global_root_config (readonly)
|
|
579
|
+
* 3. market_state (writable, init)
|
|
580
|
+
* 4. system_program (readonly)
|
|
581
|
+
*
|
|
582
|
+
* Args (Borsh):
|
|
583
|
+
* market_id: u64
|
|
584
|
+
* creator_wallet: Pubkey (32 bytes)
|
|
585
|
+
* metric: u8
|
|
586
|
+
* target: u64
|
|
587
|
+
* resolution_root_seq: u64
|
|
588
|
+
*/
|
|
589
|
+
export async function createCreateMarketIx(authority, ccmMint, marketId, creatorWallet, metric, target, resolutionRootSeq, programId = PROGRAM_ID) {
|
|
590
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
591
|
+
const globalRootConfig = getGlobalRootConfigPDA(ccmMint, programId);
|
|
592
|
+
const marketState = getMarketStatePDA(ccmMint, marketId, programId);
|
|
593
|
+
// Data: [8 disc][8 market_id][32 creator_wallet][1 metric][8 target][8 resolution_root_seq]
|
|
594
|
+
const disc = await anchorDisc('create_market');
|
|
595
|
+
const data = Buffer.alloc(8 + 8 + 32 + 1 + 8 + 8);
|
|
596
|
+
let offset = 0;
|
|
597
|
+
disc.copy(data, offset);
|
|
598
|
+
offset += 8;
|
|
599
|
+
data.writeBigUInt64LE(BigInt(marketId), offset);
|
|
600
|
+
offset += 8;
|
|
601
|
+
creatorWallet.toBuffer().copy(data, offset);
|
|
602
|
+
offset += 32;
|
|
603
|
+
data.writeUInt8(metric, offset);
|
|
604
|
+
offset += 1;
|
|
605
|
+
data.writeBigUInt64LE(BigInt(target), offset);
|
|
606
|
+
offset += 8;
|
|
607
|
+
data.writeBigUInt64LE(BigInt(resolutionRootSeq), offset);
|
|
608
|
+
return new TransactionInstruction({
|
|
609
|
+
programId,
|
|
610
|
+
keys: [
|
|
611
|
+
{ pubkey: authority, isSigner: true, isWritable: true },
|
|
612
|
+
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
613
|
+
{ pubkey: globalRootConfig, isSigner: false, isWritable: false },
|
|
614
|
+
{ pubkey: marketState, isSigner: false, isWritable: true },
|
|
615
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
616
|
+
],
|
|
617
|
+
data,
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
// ── 2. createUpdateAttentionIx ───────────────────────────────────────
|
|
621
|
+
/**
|
|
622
|
+
* Build an `update_attention` TransactionInstruction.
|
|
623
|
+
*
|
|
624
|
+
* Oracle pushes attention multiplier to a user's market position.
|
|
625
|
+
*
|
|
626
|
+
* Accounts (order must match UpdateAttention struct in vault.rs):
|
|
627
|
+
* 0. oracle_authority (signer, writable)
|
|
628
|
+
* 1. protocol_state (readonly)
|
|
629
|
+
* 2. market_vault (readonly)
|
|
630
|
+
* 3. user_market_position (writable)
|
|
631
|
+
*
|
|
632
|
+
* Args: market_id (u64), user_pubkey (Pubkey), multiplier_bps (u64)
|
|
633
|
+
*/
|
|
634
|
+
export async function createUpdateAttentionIx(oracleAuthority, marketId, userPubkey, multiplierBps, programId = PROGRAM_ID) {
|
|
635
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
636
|
+
const marketVault = getMarketVaultPDA(protocolState, marketId, programId);
|
|
637
|
+
const userPosition = getUserPositionPDA(marketVault, userPubkey, programId);
|
|
638
|
+
// Data: [8 disc][8 market_id][32 user_pubkey][8 multiplier_bps]
|
|
639
|
+
const disc = await anchorDisc('update_attention');
|
|
640
|
+
const data = Buffer.alloc(8 + 8 + 32 + 8);
|
|
641
|
+
let offset = 0;
|
|
642
|
+
disc.copy(data, offset);
|
|
643
|
+
offset += 8;
|
|
644
|
+
data.writeBigUInt64LE(BigInt(marketId), offset);
|
|
645
|
+
offset += 8;
|
|
646
|
+
userPubkey.toBuffer().copy(data, offset);
|
|
647
|
+
offset += 32;
|
|
648
|
+
data.writeBigUInt64LE(BigInt(multiplierBps), offset);
|
|
649
|
+
return new TransactionInstruction({
|
|
650
|
+
programId,
|
|
651
|
+
keys: [
|
|
652
|
+
{ pubkey: oracleAuthority, isSigner: true, isWritable: true },
|
|
653
|
+
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
654
|
+
{ pubkey: marketVault, isSigner: false, isWritable: false },
|
|
655
|
+
{ pubkey: userPosition, isSigner: false, isWritable: true },
|
|
656
|
+
],
|
|
657
|
+
data,
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
// ── 3. createPublishGlobalRootIx ─────────────────────────────────────
|
|
661
|
+
/**
|
|
662
|
+
* Build a `publish_global_root` TransactionInstruction.
|
|
663
|
+
*
|
|
664
|
+
* Accounts (order must match PublishGlobalRoot struct in global.rs):
|
|
665
|
+
* 0. payer (signer, writable)
|
|
666
|
+
* 1. protocol_state (readonly)
|
|
667
|
+
* 2. global_root_config (writable)
|
|
668
|
+
*
|
|
669
|
+
* Args: root_seq (u64), root ([u8; 32]), dataset_hash ([u8; 32])
|
|
670
|
+
*/
|
|
671
|
+
export async function createPublishGlobalRootIx(payer, ccmMint, rootSeq,
|
|
672
|
+
/** 32-byte merkle root (hex string or Buffer) */
|
|
673
|
+
root,
|
|
674
|
+
/** 32-byte dataset hash (hex string or Buffer) */
|
|
675
|
+
datasetHash, programId = PROGRAM_ID) {
|
|
676
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
677
|
+
const globalRootConfig = getGlobalRootConfigPDA(ccmMint, programId);
|
|
678
|
+
const rootBuf = typeof root === 'string' ? Buffer.from(root, 'hex') : root;
|
|
679
|
+
const hashBuf = typeof datasetHash === 'string' ? Buffer.from(datasetHash, 'hex') : datasetHash;
|
|
680
|
+
// Data: [8 disc][8 root_seq][32 root][32 dataset_hash]
|
|
681
|
+
const disc = await anchorDisc('publish_global_root');
|
|
682
|
+
const data = Buffer.alloc(8 + 8 + 32 + 32);
|
|
683
|
+
let offset = 0;
|
|
684
|
+
disc.copy(data, offset);
|
|
685
|
+
offset += 8;
|
|
686
|
+
data.writeBigUInt64LE(BigInt(rootSeq), offset);
|
|
687
|
+
offset += 8;
|
|
688
|
+
rootBuf.copy(data, offset);
|
|
689
|
+
offset += 32;
|
|
690
|
+
hashBuf.copy(data, offset);
|
|
691
|
+
return new TransactionInstruction({
|
|
692
|
+
programId,
|
|
693
|
+
keys: [
|
|
694
|
+
{ pubkey: payer, isSigner: true, isWritable: true },
|
|
695
|
+
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
696
|
+
{ pubkey: globalRootConfig, isSigner: false, isWritable: true },
|
|
697
|
+
],
|
|
698
|
+
data,
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
// ── 4. createClaimYieldIx ────────────────────────────────────────────
|
|
702
|
+
/**
|
|
703
|
+
* Build a `claim_yield` TransactionInstruction.
|
|
704
|
+
*
|
|
705
|
+
* NOTE: This instruction is deprecated on-chain and always returns
|
|
706
|
+
* `ClaimYieldDeprecated`. Included for SDK completeness.
|
|
707
|
+
*
|
|
708
|
+
* Accounts (order must match ClaimYield struct in vault.rs):
|
|
709
|
+
* 0. user (signer, writable)
|
|
710
|
+
* 1. protocol_state (readonly)
|
|
711
|
+
* 2. market_vault (readonly)
|
|
712
|
+
* 3. user_market_position (writable)
|
|
713
|
+
*
|
|
714
|
+
* Args: market_id (u64)
|
|
715
|
+
*/
|
|
716
|
+
export async function createClaimYieldIx(user, marketId, programId = PROGRAM_ID) {
|
|
717
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
718
|
+
const marketVault = getMarketVaultPDA(protocolState, marketId, programId);
|
|
719
|
+
const userPosition = getUserPositionPDA(marketVault, user, programId);
|
|
720
|
+
const disc = await anchorDisc('claim_yield');
|
|
721
|
+
const data = Buffer.alloc(16);
|
|
722
|
+
disc.copy(data, 0);
|
|
723
|
+
data.writeBigUInt64LE(BigInt(marketId), 8);
|
|
724
|
+
return new TransactionInstruction({
|
|
725
|
+
programId,
|
|
726
|
+
keys: [
|
|
727
|
+
{ pubkey: user, isSigner: true, isWritable: true },
|
|
728
|
+
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
729
|
+
{ pubkey: marketVault, isSigner: false, isWritable: false },
|
|
730
|
+
{ pubkey: userPosition, isSigner: false, isWritable: true },
|
|
731
|
+
],
|
|
732
|
+
data,
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
// ── 5. createCloseMarketIx ───────────────────────────────────────────
|
|
736
|
+
/**
|
|
737
|
+
* Build a `close_market` TransactionInstruction (prediction markets).
|
|
738
|
+
*
|
|
739
|
+
* Accounts (order must match CloseMarket struct in markets.rs):
|
|
740
|
+
* 0. admin (signer, writable)
|
|
741
|
+
* 1. protocol_state (readonly)
|
|
742
|
+
* 2. market_state (writable, close -> admin)
|
|
743
|
+
* 3. vault (writable) — must be empty
|
|
744
|
+
* 4. ccm_mint (readonly)
|
|
745
|
+
* 5. mint_authority (readonly)
|
|
746
|
+
* 6. token_program (readonly) — Token-2022
|
|
747
|
+
*/
|
|
748
|
+
export async function createCloseMarketIx(admin, ccmMint, marketId, vault, mintAuthority, programId = PROGRAM_ID) {
|
|
749
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
750
|
+
const marketState = getMarketStatePDA(ccmMint, marketId, programId);
|
|
751
|
+
const disc = await anchorDisc('close_market');
|
|
752
|
+
const data = Buffer.alloc(8);
|
|
753
|
+
disc.copy(data, 0);
|
|
754
|
+
return new TransactionInstruction({
|
|
755
|
+
programId,
|
|
756
|
+
keys: [
|
|
757
|
+
{ pubkey: admin, isSigner: true, isWritable: true },
|
|
758
|
+
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
759
|
+
{ pubkey: marketState, isSigner: false, isWritable: true },
|
|
760
|
+
{ pubkey: vault, isSigner: false, isWritable: true },
|
|
761
|
+
{ pubkey: ccmMint, isSigner: false, isWritable: false },
|
|
762
|
+
{ pubkey: mintAuthority, isSigner: false, isWritable: false },
|
|
763
|
+
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
764
|
+
],
|
|
765
|
+
data,
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
// ── 6. createUpdateNavIx ─────────────────────────────────────────────
|
|
769
|
+
/**
|
|
770
|
+
* Build an `update_nav` TransactionInstruction.
|
|
771
|
+
*
|
|
772
|
+
* Oracle authority sets NAV per vLOFI share on a MarketVault.
|
|
773
|
+
*
|
|
774
|
+
* Accounts (order must match UpdateNav struct in vault.rs):
|
|
775
|
+
* 0. oracle_authority (signer, writable)
|
|
776
|
+
* 1. protocol_state (readonly)
|
|
777
|
+
* 2. market_vault (writable)
|
|
778
|
+
*
|
|
779
|
+
* Args: market_id (u64), nav_per_share_bps (u64)
|
|
780
|
+
*/
|
|
781
|
+
export async function createUpdateNavIx(oracleAuthority, marketId, navPerShareBps, programId = PROGRAM_ID) {
|
|
782
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
783
|
+
const marketVault = getMarketVaultPDA(protocolState, marketId, programId);
|
|
784
|
+
// Data: [8 disc][8 market_id][8 nav_per_share_bps]
|
|
785
|
+
const disc = await anchorDisc('update_nav');
|
|
786
|
+
const data = Buffer.alloc(24);
|
|
787
|
+
disc.copy(data, 0);
|
|
788
|
+
data.writeBigUInt64LE(BigInt(marketId), 8);
|
|
789
|
+
data.writeBigUInt64LE(BigInt(navPerShareBps), 16);
|
|
790
|
+
return new TransactionInstruction({
|
|
791
|
+
programId,
|
|
792
|
+
keys: [
|
|
793
|
+
{ pubkey: oracleAuthority, isSigner: true, isWritable: true },
|
|
794
|
+
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
795
|
+
{ pubkey: marketVault, isSigner: false, isWritable: true },
|
|
796
|
+
],
|
|
797
|
+
data,
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
// ── 7. createInitializeProtocolStateIx ───────────────────────────────
|
|
801
|
+
/**
|
|
802
|
+
* Build an `initialize_protocol_state` TransactionInstruction.
|
|
803
|
+
*
|
|
804
|
+
* One-time protocol setup. Creates the singleton ProtocolState PDA.
|
|
805
|
+
*
|
|
806
|
+
* Accounts (order must match InitializeProtocolState struct in vault.rs):
|
|
807
|
+
* 0. admin (signer, writable)
|
|
808
|
+
* 1. protocol_state (writable, init)
|
|
809
|
+
* 2. system_program (readonly)
|
|
810
|
+
*
|
|
811
|
+
* Args: publisher (Pubkey), treasury (Pubkey), oracle_authority (Pubkey), ccm_mint (Pubkey)
|
|
812
|
+
*/
|
|
813
|
+
export async function createInitializeProtocolStateIx(admin, publisher, treasury, oracleAuthority, ccmMint, programId = PROGRAM_ID) {
|
|
814
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
815
|
+
// Data: [8 disc][32 publisher][32 treasury][32 oracle_authority][32 ccm_mint]
|
|
816
|
+
const disc = await anchorDisc('initialize_protocol_state');
|
|
817
|
+
const data = Buffer.alloc(8 + 32 + 32 + 32 + 32);
|
|
818
|
+
let offset = 0;
|
|
819
|
+
disc.copy(data, offset);
|
|
820
|
+
offset += 8;
|
|
821
|
+
publisher.toBuffer().copy(data, offset);
|
|
822
|
+
offset += 32;
|
|
823
|
+
treasury.toBuffer().copy(data, offset);
|
|
824
|
+
offset += 32;
|
|
825
|
+
oracleAuthority.toBuffer().copy(data, offset);
|
|
826
|
+
offset += 32;
|
|
827
|
+
ccmMint.toBuffer().copy(data, offset);
|
|
828
|
+
return new TransactionInstruction({
|
|
829
|
+
programId,
|
|
830
|
+
keys: [
|
|
831
|
+
{ pubkey: admin, isSigner: true, isWritable: true },
|
|
832
|
+
{ pubkey: protocolState, isSigner: false, isWritable: true },
|
|
833
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
834
|
+
],
|
|
835
|
+
data,
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
// ── 8. createUpdateProtocolStateIx (update_publisher_open) ───────────
|
|
839
|
+
/**
|
|
840
|
+
* Build an `update_publisher_open` TransactionInstruction.
|
|
841
|
+
*
|
|
842
|
+
* Admin updates the allowlisted publisher address.
|
|
843
|
+
*
|
|
844
|
+
* Accounts (order must match UpdatePublisherOpen struct in admin.rs):
|
|
845
|
+
* 0. admin (signer, writable)
|
|
846
|
+
* 1. protocol_state (writable)
|
|
847
|
+
*
|
|
848
|
+
* Args: new_publisher (Pubkey)
|
|
849
|
+
*/
|
|
850
|
+
export async function createUpdateProtocolStateIx(admin, newPublisher, programId = PROGRAM_ID) {
|
|
851
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
852
|
+
const disc = await anchorDisc('update_publisher_open');
|
|
853
|
+
const data = Buffer.alloc(8 + 32);
|
|
854
|
+
disc.copy(data, 0);
|
|
855
|
+
newPublisher.toBuffer().copy(data, 8);
|
|
856
|
+
return new TransactionInstruction({
|
|
857
|
+
programId,
|
|
858
|
+
keys: [
|
|
859
|
+
{ pubkey: admin, isSigner: true, isWritable: true },
|
|
860
|
+
{ pubkey: protocolState, isSigner: false, isWritable: true },
|
|
861
|
+
],
|
|
862
|
+
data,
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
// ── 9. createSetTreasuryIx ───────────────────────────────────────────
|
|
866
|
+
/**
|
|
867
|
+
* Build a `set_treasury` TransactionInstruction.
|
|
868
|
+
*
|
|
869
|
+
* Admin updates the treasury wallet (fee destination owner).
|
|
870
|
+
*
|
|
871
|
+
* Accounts (order must match SetTreasury struct in admin.rs):
|
|
872
|
+
* 0. admin (signer, writable)
|
|
873
|
+
* 1. protocol_state (writable)
|
|
874
|
+
*
|
|
875
|
+
* Args: new_treasury (Pubkey)
|
|
876
|
+
*/
|
|
877
|
+
export async function createSetTreasuryIx(admin, newTreasury, programId = PROGRAM_ID) {
|
|
878
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
879
|
+
const disc = await anchorDisc('set_treasury');
|
|
880
|
+
const data = Buffer.alloc(8 + 32);
|
|
881
|
+
disc.copy(data, 0);
|
|
882
|
+
newTreasury.toBuffer().copy(data, 8);
|
|
883
|
+
return new TransactionInstruction({
|
|
884
|
+
programId,
|
|
885
|
+
keys: [
|
|
886
|
+
{ pubkey: admin, isSigner: true, isWritable: true },
|
|
887
|
+
{ pubkey: protocolState, isSigner: false, isWritable: true },
|
|
888
|
+
],
|
|
889
|
+
data,
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
// ── 10. createStakeChannelIx ─────────────────────────────────────────
|
|
893
|
+
/**
|
|
894
|
+
* Build a `stake_channel` TransactionInstruction.
|
|
895
|
+
*
|
|
896
|
+
* Accounts (order must match StakeChannel struct in staking.rs):
|
|
897
|
+
* 0. user (signer)
|
|
898
|
+
* 1. payer (signer, writable)
|
|
899
|
+
* 2. protocol_state (readonly)
|
|
900
|
+
* 3. channel_config (readonly)
|
|
901
|
+
* 4. mint (readonly) — CCM Token-2022
|
|
902
|
+
* 5. stake_pool (writable)
|
|
903
|
+
* 6. user_stake (writable, init)
|
|
904
|
+
* 7. vault (writable) — pool's staking vault
|
|
905
|
+
* 8. user_token_account (writable) — user's CCM ATA
|
|
906
|
+
* 9. nft_mint (writable) — soulbound NFT mint PDA
|
|
907
|
+
* 10. nft_ata (writable) — user's NFT ATA
|
|
908
|
+
* 11. token_program (readonly) — Token-2022
|
|
909
|
+
* 12. associated_token_program (readonly)
|
|
910
|
+
* 13. system_program (readonly)
|
|
911
|
+
* 14. rent (readonly)
|
|
912
|
+
*
|
|
913
|
+
* Args: amount (u64), lock_duration (u64)
|
|
914
|
+
*/
|
|
915
|
+
export async function createStakeChannelIx(user, payer, ccmMint, channelConfig, amount, lockDuration, programId = PROGRAM_ID) {
|
|
916
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
917
|
+
const stakePool = getStakePoolPDA(channelConfig, programId);
|
|
918
|
+
const userStake = getUserStakePDA(channelConfig, user, programId);
|
|
919
|
+
const vault = getStakeVaultPDA(stakePool, programId);
|
|
920
|
+
const userTokenAccount = getAta(user, ccmMint, TOKEN_2022_PROGRAM_ID);
|
|
921
|
+
const nftMint = getStakeNftMintPDA(stakePool, user, programId);
|
|
922
|
+
const nftAta = getAta(user, nftMint, TOKEN_2022_PROGRAM_ID);
|
|
923
|
+
const disc = await anchorDisc('stake_channel');
|
|
924
|
+
const data = Buffer.alloc(8 + 8 + 8);
|
|
925
|
+
disc.copy(data, 0);
|
|
926
|
+
data.writeBigUInt64LE(BigInt(amount), 8);
|
|
927
|
+
data.writeBigUInt64LE(BigInt(lockDuration), 16);
|
|
928
|
+
return new TransactionInstruction({
|
|
929
|
+
programId,
|
|
930
|
+
keys: [
|
|
931
|
+
{ pubkey: user, isSigner: true, isWritable: false },
|
|
932
|
+
{ pubkey: payer, isSigner: true, isWritable: true },
|
|
933
|
+
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
934
|
+
{ pubkey: channelConfig, isSigner: false, isWritable: false },
|
|
935
|
+
{ pubkey: ccmMint, isSigner: false, isWritable: false },
|
|
936
|
+
{ pubkey: stakePool, isSigner: false, isWritable: true },
|
|
937
|
+
{ pubkey: userStake, isSigner: false, isWritable: true },
|
|
938
|
+
{ pubkey: vault, isSigner: false, isWritable: true },
|
|
939
|
+
{ pubkey: userTokenAccount, isSigner: false, isWritable: true },
|
|
940
|
+
{ pubkey: nftMint, isSigner: false, isWritable: true },
|
|
941
|
+
{ pubkey: nftAta, isSigner: false, isWritable: true },
|
|
942
|
+
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
943
|
+
{ pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
944
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
945
|
+
{ pubkey: new PublicKey('SysvarRent111111111111111111111111111111111'), isSigner: false, isWritable: false },
|
|
946
|
+
],
|
|
947
|
+
data,
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
// ── 11. createUnstakeChannelIx ───────────────────────────────────────
|
|
951
|
+
/**
|
|
952
|
+
* Build an `unstake_channel` TransactionInstruction.
|
|
953
|
+
*
|
|
954
|
+
* Accounts (order must match UnstakeChannel struct in staking.rs):
|
|
955
|
+
* 0. user (signer, writable)
|
|
956
|
+
* 1. channel_config (readonly)
|
|
957
|
+
* 2. mint (readonly) — CCM Token-2022
|
|
958
|
+
* 3. stake_pool (writable)
|
|
959
|
+
* 4. user_stake (writable, close -> user)
|
|
960
|
+
* 5. vault (writable)
|
|
961
|
+
* 6. user_token_account (writable)
|
|
962
|
+
* 7. nft_mint (writable)
|
|
963
|
+
* 8. nft_ata (writable)
|
|
964
|
+
* 9. token_program (readonly) — Token-2022
|
|
965
|
+
* 10. associated_token_program (readonly)
|
|
966
|
+
*
|
|
967
|
+
* No args (no instruction data beyond disc).
|
|
968
|
+
*/
|
|
969
|
+
export async function createUnstakeChannelIx(user, ccmMint, channelConfig, nftMint, programId = PROGRAM_ID) {
|
|
970
|
+
const stakePool = getStakePoolPDA(channelConfig, programId);
|
|
971
|
+
const userStake = getUserStakePDA(channelConfig, user, programId);
|
|
972
|
+
const vault = getStakeVaultPDA(stakePool, programId);
|
|
973
|
+
const userTokenAccount = getAta(user, ccmMint, TOKEN_2022_PROGRAM_ID);
|
|
974
|
+
const nftAta = getAta(user, nftMint, TOKEN_2022_PROGRAM_ID);
|
|
975
|
+
const disc = await anchorDisc('unstake_channel');
|
|
976
|
+
const data = Buffer.alloc(8);
|
|
977
|
+
disc.copy(data, 0);
|
|
978
|
+
return new TransactionInstruction({
|
|
979
|
+
programId,
|
|
980
|
+
keys: [
|
|
981
|
+
{ pubkey: user, isSigner: true, isWritable: true },
|
|
982
|
+
{ pubkey: channelConfig, isSigner: false, isWritable: false },
|
|
983
|
+
{ pubkey: ccmMint, isSigner: false, isWritable: false },
|
|
984
|
+
{ pubkey: stakePool, isSigner: false, isWritable: true },
|
|
985
|
+
{ pubkey: userStake, isSigner: false, isWritable: true },
|
|
986
|
+
{ pubkey: vault, isSigner: false, isWritable: true },
|
|
987
|
+
{ pubkey: userTokenAccount, isSigner: false, isWritable: true },
|
|
988
|
+
{ pubkey: nftMint, isSigner: false, isWritable: true },
|
|
989
|
+
{ pubkey: nftAta, isSigner: false, isWritable: true },
|
|
990
|
+
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
991
|
+
{ pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
992
|
+
],
|
|
993
|
+
data,
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
// ── 12. createClaimChannelRewardsIx ──────────────────────────────────
|
|
997
|
+
/**
|
|
998
|
+
* Build a `claim_channel_rewards` TransactionInstruction.
|
|
999
|
+
*
|
|
1000
|
+
* Accounts (order must match ClaimChannelRewards struct in staking.rs):
|
|
1001
|
+
* 0. user (signer, writable)
|
|
1002
|
+
* 1. channel_config (readonly)
|
|
1003
|
+
* 2. mint (readonly) — CCM Token-2022
|
|
1004
|
+
* 3. stake_pool (writable)
|
|
1005
|
+
* 4. user_stake (writable)
|
|
1006
|
+
* 5. vault (writable) — pool vault
|
|
1007
|
+
* 6. user_token_account (writable) — receives rewards
|
|
1008
|
+
* 7. token_program (readonly) — Token-2022
|
|
1009
|
+
*
|
|
1010
|
+
* No args beyond disc.
|
|
1011
|
+
*/
|
|
1012
|
+
export async function createClaimChannelRewardsIx(user, ccmMint, channelConfig, programId = PROGRAM_ID) {
|
|
1013
|
+
const stakePool = getStakePoolPDA(channelConfig, programId);
|
|
1014
|
+
const userStake = getUserStakePDA(channelConfig, user, programId);
|
|
1015
|
+
const vault = getStakeVaultPDA(stakePool, programId);
|
|
1016
|
+
const userTokenAccount = getAta(user, ccmMint, TOKEN_2022_PROGRAM_ID);
|
|
1017
|
+
const disc = await anchorDisc('claim_channel_rewards');
|
|
1018
|
+
const data = Buffer.alloc(8);
|
|
1019
|
+
disc.copy(data, 0);
|
|
1020
|
+
return new TransactionInstruction({
|
|
1021
|
+
programId,
|
|
1022
|
+
keys: [
|
|
1023
|
+
{ pubkey: user, isSigner: true, isWritable: true },
|
|
1024
|
+
{ pubkey: channelConfig, isSigner: false, isWritable: false },
|
|
1025
|
+
{ pubkey: ccmMint, isSigner: false, isWritable: false },
|
|
1026
|
+
{ pubkey: stakePool, isSigner: false, isWritable: true },
|
|
1027
|
+
{ pubkey: userStake, isSigner: false, isWritable: true },
|
|
1028
|
+
{ pubkey: vault, isSigner: false, isWritable: true },
|
|
1029
|
+
{ pubkey: userTokenAccount, isSigner: false, isWritable: true },
|
|
1030
|
+
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1031
|
+
],
|
|
1032
|
+
data,
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
// ── 13. createInitializeStakePoolIx ──────────────────────────────────
|
|
1036
|
+
/**
|
|
1037
|
+
* Build an `initialize_stake_pool` TransactionInstruction.
|
|
1038
|
+
*
|
|
1039
|
+
* Accounts (order must match InitializeStakePool struct in staking.rs):
|
|
1040
|
+
* 0. payer (signer, writable)
|
|
1041
|
+
* 1. protocol_state (readonly)
|
|
1042
|
+
* 2. channel_config (readonly)
|
|
1043
|
+
* 3. mint (readonly) — CCM Token-2022
|
|
1044
|
+
* 4. stake_pool (writable, init)
|
|
1045
|
+
* 5. vault (writable, init) — pool's CCM vault
|
|
1046
|
+
* 6. token_program (readonly) — Token-2022
|
|
1047
|
+
* 7. system_program (readonly)
|
|
1048
|
+
*
|
|
1049
|
+
* No args beyond disc.
|
|
1050
|
+
*/
|
|
1051
|
+
export async function createInitializeStakePoolIx(payer, ccmMint, channelConfig, programId = PROGRAM_ID) {
|
|
1052
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
1053
|
+
const stakePool = getStakePoolPDA(channelConfig, programId);
|
|
1054
|
+
const vault = getStakeVaultPDA(stakePool, programId);
|
|
1055
|
+
const disc = await anchorDisc('initialize_stake_pool');
|
|
1056
|
+
const data = Buffer.alloc(8);
|
|
1057
|
+
disc.copy(data, 0);
|
|
1058
|
+
return new TransactionInstruction({
|
|
1059
|
+
programId,
|
|
1060
|
+
keys: [
|
|
1061
|
+
{ pubkey: payer, isSigner: true, isWritable: true },
|
|
1062
|
+
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
1063
|
+
{ pubkey: channelConfig, isSigner: false, isWritable: false },
|
|
1064
|
+
{ pubkey: ccmMint, isSigner: false, isWritable: false },
|
|
1065
|
+
{ pubkey: stakePool, isSigner: false, isWritable: true },
|
|
1066
|
+
{ pubkey: vault, isSigner: false, isWritable: true },
|
|
1067
|
+
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1068
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1069
|
+
],
|
|
1070
|
+
data,
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
// ── 14. createInitializeStrategyVaultIx ──────────────────────────────
|
|
1074
|
+
/**
|
|
1075
|
+
* Build an `initialize_strategy_vault` TransactionInstruction.
|
|
1076
|
+
*
|
|
1077
|
+
* Accounts (order must match InitializeStrategyVault struct in strategy.rs):
|
|
1078
|
+
* 0. admin_authority (signer, writable)
|
|
1079
|
+
* 1. protocol_state (readonly)
|
|
1080
|
+
* 2. market_vault (readonly)
|
|
1081
|
+
* 3. deposit_mint (readonly)
|
|
1082
|
+
* 4. strategy_vault (writable, init)
|
|
1083
|
+
* 5. system_program (readonly)
|
|
1084
|
+
*
|
|
1085
|
+
* Args: reserve_ratio_bps (u16), utilization_cap_bps (u16),
|
|
1086
|
+
* operator_authority (Pubkey), klend_program (Pubkey),
|
|
1087
|
+
* klend_reserve (Pubkey), klend_lending_market (Pubkey), ctoken_ata (Pubkey)
|
|
1088
|
+
*/
|
|
1089
|
+
export async function createInitializeStrategyVaultIx(adminAuthority, marketId, depositMint, reserveRatioBps, utilizationCapBps, operatorAuthority, klendProgram, klendReserve, klendLendingMarket, ctokenAta, programId = PROGRAM_ID) {
|
|
1090
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
1091
|
+
const marketVault = getMarketVaultPDA(protocolState, marketId, programId);
|
|
1092
|
+
const strategyVault = getStrategyVaultPDA(marketVault, programId);
|
|
1093
|
+
// Data: [8 disc][2 reserve_ratio_bps][2 utilization_cap_bps]
|
|
1094
|
+
// [32 operator][32 klend_program][32 klend_reserve][32 klend_lending_market][32 ctoken_ata]
|
|
1095
|
+
const disc = await anchorDisc('initialize_strategy_vault');
|
|
1096
|
+
const data = Buffer.alloc(8 + 2 + 2 + 32 * 5);
|
|
1097
|
+
let offset = 0;
|
|
1098
|
+
disc.copy(data, offset);
|
|
1099
|
+
offset += 8;
|
|
1100
|
+
data.writeUInt16LE(reserveRatioBps, offset);
|
|
1101
|
+
offset += 2;
|
|
1102
|
+
data.writeUInt16LE(utilizationCapBps, offset);
|
|
1103
|
+
offset += 2;
|
|
1104
|
+
operatorAuthority.toBuffer().copy(data, offset);
|
|
1105
|
+
offset += 32;
|
|
1106
|
+
klendProgram.toBuffer().copy(data, offset);
|
|
1107
|
+
offset += 32;
|
|
1108
|
+
klendReserve.toBuffer().copy(data, offset);
|
|
1109
|
+
offset += 32;
|
|
1110
|
+
klendLendingMarket.toBuffer().copy(data, offset);
|
|
1111
|
+
offset += 32;
|
|
1112
|
+
ctokenAta.toBuffer().copy(data, offset);
|
|
1113
|
+
return new TransactionInstruction({
|
|
1114
|
+
programId,
|
|
1115
|
+
keys: [
|
|
1116
|
+
{ pubkey: adminAuthority, isSigner: true, isWritable: true },
|
|
1117
|
+
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
1118
|
+
{ pubkey: marketVault, isSigner: false, isWritable: false },
|
|
1119
|
+
{ pubkey: depositMint, isSigner: false, isWritable: false },
|
|
1120
|
+
{ pubkey: strategyVault, isSigner: false, isWritable: true },
|
|
1121
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1122
|
+
],
|
|
1123
|
+
data,
|
|
1124
|
+
});
|
|
1125
|
+
}
|
|
1126
|
+
// ── 15. createDeployToStrategyIx ─────────────────────────────────────
|
|
1127
|
+
/**
|
|
1128
|
+
* Build a `deploy_to_strategy` TransactionInstruction.
|
|
1129
|
+
*
|
|
1130
|
+
* Deploys USDC from MarketVault into Kamino K-Lend. Requires all Kamino
|
|
1131
|
+
* oracle accounts. All account addresses are pinned at strategy vault init.
|
|
1132
|
+
*
|
|
1133
|
+
* Accounts (order must match DeployToStrategy struct in strategy.rs):
|
|
1134
|
+
* 0. operator_authority (signer, writable)
|
|
1135
|
+
* 1. protocol_state (readonly)
|
|
1136
|
+
* 2. market_vault (readonly)
|
|
1137
|
+
* 3. strategy_vault (writable)
|
|
1138
|
+
* 4. deposit_mint (readonly)
|
|
1139
|
+
* 5. vault_usdc_ata (writable)
|
|
1140
|
+
* 6. ctoken_ata (writable)
|
|
1141
|
+
* 7. klend_program (readonly)
|
|
1142
|
+
* 8. klend_reserve (writable)
|
|
1143
|
+
* 9. klend_lending_market (readonly)
|
|
1144
|
+
* 10. klend_lending_market_authority (readonly)
|
|
1145
|
+
* 11. reserve_liquidity_supply (writable)
|
|
1146
|
+
* 12. reserve_collateral_mint (writable)
|
|
1147
|
+
* 13. pyth_oracle (readonly)
|
|
1148
|
+
* 14. switchboard_price_oracle (readonly)
|
|
1149
|
+
* 15. switchboard_twap_oracle (readonly)
|
|
1150
|
+
* 16. scope_prices (readonly)
|
|
1151
|
+
* 17. instruction_sysvar_account (readonly)
|
|
1152
|
+
* 18. token_program (readonly) — SPL Token
|
|
1153
|
+
*
|
|
1154
|
+
* Args: amount (u64)
|
|
1155
|
+
*/
|
|
1156
|
+
export async function createDeployToStrategyIx(operatorAuthority, marketId, depositMint, vaultUsdcAta, ctokenAta, klendProgram, klendReserve, klendLendingMarket, klendLendingMarketAuthority, reserveLiquiditySupply, reserveCollateralMint, pythOracle, switchboardPriceOracle, switchboardTwapOracle, scopePrices, amount, programId = PROGRAM_ID) {
|
|
1157
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
1158
|
+
const marketVault = getMarketVaultPDA(protocolState, marketId, programId);
|
|
1159
|
+
const strategyVault = getStrategyVaultPDA(marketVault, programId);
|
|
1160
|
+
const disc = await anchorDisc('deploy_to_strategy');
|
|
1161
|
+
const data = Buffer.alloc(16);
|
|
1162
|
+
disc.copy(data, 0);
|
|
1163
|
+
data.writeBigUInt64LE(BigInt(amount), 8);
|
|
1164
|
+
return new TransactionInstruction({
|
|
1165
|
+
programId,
|
|
1166
|
+
keys: [
|
|
1167
|
+
{ pubkey: operatorAuthority, isSigner: true, isWritable: true },
|
|
1168
|
+
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
1169
|
+
{ pubkey: marketVault, isSigner: false, isWritable: false },
|
|
1170
|
+
{ pubkey: strategyVault, isSigner: false, isWritable: true },
|
|
1171
|
+
{ pubkey: depositMint, isSigner: false, isWritable: false },
|
|
1172
|
+
{ pubkey: vaultUsdcAta, isSigner: false, isWritable: true },
|
|
1173
|
+
{ pubkey: ctokenAta, isSigner: false, isWritable: true },
|
|
1174
|
+
{ pubkey: klendProgram, isSigner: false, isWritable: false },
|
|
1175
|
+
{ pubkey: klendReserve, isSigner: false, isWritable: true },
|
|
1176
|
+
{ pubkey: klendLendingMarket, isSigner: false, isWritable: false },
|
|
1177
|
+
{ pubkey: klendLendingMarketAuthority, isSigner: false, isWritable: false },
|
|
1178
|
+
{ pubkey: reserveLiquiditySupply, isSigner: false, isWritable: true },
|
|
1179
|
+
{ pubkey: reserveCollateralMint, isSigner: false, isWritable: true },
|
|
1180
|
+
{ pubkey: pythOracle, isSigner: false, isWritable: false },
|
|
1181
|
+
{ pubkey: switchboardPriceOracle, isSigner: false, isWritable: false },
|
|
1182
|
+
{ pubkey: switchboardTwapOracle, isSigner: false, isWritable: false },
|
|
1183
|
+
{ pubkey: scopePrices, isSigner: false, isWritable: false },
|
|
1184
|
+
{ pubkey: SYSVAR_INSTRUCTIONS_ID, isSigner: false, isWritable: false },
|
|
1185
|
+
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1186
|
+
],
|
|
1187
|
+
data,
|
|
1188
|
+
});
|
|
1189
|
+
}
|
|
1190
|
+
// ── 16. createWithdrawFromStrategyIx ─────────────────────────────────
|
|
1191
|
+
/**
|
|
1192
|
+
* Build a `withdraw_from_strategy` TransactionInstruction.
|
|
1193
|
+
*
|
|
1194
|
+
* Withdraws USDC from Kamino back into the MarketVault reserve.
|
|
1195
|
+
* Same Kamino oracle accounts as deploy_to_strategy.
|
|
1196
|
+
*
|
|
1197
|
+
* Accounts (order must match WithdrawFromStrategy struct in strategy.rs):
|
|
1198
|
+
* Same layout as DeployToStrategy.
|
|
1199
|
+
*
|
|
1200
|
+
* Args: amount (u64)
|
|
1201
|
+
*/
|
|
1202
|
+
export async function createWithdrawFromStrategyIx(operatorAuthority, marketId, depositMint, vaultUsdcAta, ctokenAta, klendProgram, klendReserve, klendLendingMarket, klendLendingMarketAuthority, reserveLiquiditySupply, reserveCollateralMint, pythOracle, switchboardPriceOracle, switchboardTwapOracle, scopePrices, amount, programId = PROGRAM_ID) {
|
|
1203
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
1204
|
+
const marketVault = getMarketVaultPDA(protocolState, marketId, programId);
|
|
1205
|
+
const strategyVault = getStrategyVaultPDA(marketVault, programId);
|
|
1206
|
+
const disc = await anchorDisc('withdraw_from_strategy');
|
|
1207
|
+
const data = Buffer.alloc(16);
|
|
1208
|
+
disc.copy(data, 0);
|
|
1209
|
+
data.writeBigUInt64LE(BigInt(amount), 8);
|
|
1210
|
+
return new TransactionInstruction({
|
|
1211
|
+
programId,
|
|
1212
|
+
keys: [
|
|
1213
|
+
{ pubkey: operatorAuthority, isSigner: true, isWritable: true },
|
|
1214
|
+
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
1215
|
+
{ pubkey: marketVault, isSigner: false, isWritable: false },
|
|
1216
|
+
{ pubkey: strategyVault, isSigner: false, isWritable: true },
|
|
1217
|
+
{ pubkey: depositMint, isSigner: false, isWritable: false },
|
|
1218
|
+
{ pubkey: vaultUsdcAta, isSigner: false, isWritable: true },
|
|
1219
|
+
{ pubkey: ctokenAta, isSigner: false, isWritable: true },
|
|
1220
|
+
{ pubkey: klendProgram, isSigner: false, isWritable: false },
|
|
1221
|
+
{ pubkey: klendReserve, isSigner: false, isWritable: true },
|
|
1222
|
+
{ pubkey: klendLendingMarket, isSigner: false, isWritable: false },
|
|
1223
|
+
{ pubkey: klendLendingMarketAuthority, isSigner: false, isWritable: false },
|
|
1224
|
+
{ pubkey: reserveLiquiditySupply, isSigner: false, isWritable: true },
|
|
1225
|
+
{ pubkey: reserveCollateralMint, isSigner: false, isWritable: true },
|
|
1226
|
+
{ pubkey: pythOracle, isSigner: false, isWritable: false },
|
|
1227
|
+
{ pubkey: switchboardPriceOracle, isSigner: false, isWritable: false },
|
|
1228
|
+
{ pubkey: switchboardTwapOracle, isSigner: false, isWritable: false },
|
|
1229
|
+
{ pubkey: scopePrices, isSigner: false, isWritable: false },
|
|
1230
|
+
{ pubkey: SYSVAR_INSTRUCTIONS_ID, isSigner: false, isWritable: false },
|
|
1231
|
+
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1232
|
+
],
|
|
1233
|
+
data,
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
// ── 17. createHarvestStrategyYieldIx ─────────────────────────────────
|
|
1237
|
+
/**
|
|
1238
|
+
* Build a `harvest_strategy_yield` TransactionInstruction.
|
|
1239
|
+
*
|
|
1240
|
+
* Harvests yield (NAV above principal) from Kamino and sends it to treasury.
|
|
1241
|
+
*
|
|
1242
|
+
* Accounts (order must match HarvestStrategyYield struct in strategy.rs):
|
|
1243
|
+
* 0. operator_authority (signer, writable)
|
|
1244
|
+
* 1. protocol_state (readonly)
|
|
1245
|
+
* 2. market_vault (readonly)
|
|
1246
|
+
* 3. strategy_vault (writable)
|
|
1247
|
+
* 4. deposit_mint (readonly)
|
|
1248
|
+
* 5. vault_usdc_ata (readonly) — for validation
|
|
1249
|
+
* 6. treasury_ata (writable) — receives yield USDC
|
|
1250
|
+
* 7. ctoken_ata (writable)
|
|
1251
|
+
* 8. klend_program (readonly)
|
|
1252
|
+
* 9. klend_reserve (writable)
|
|
1253
|
+
* 10. klend_lending_market (readonly)
|
|
1254
|
+
* 11. klend_lending_market_authority (readonly)
|
|
1255
|
+
* 12. reserve_liquidity_supply (writable)
|
|
1256
|
+
* 13. reserve_collateral_mint (writable)
|
|
1257
|
+
* 14. pyth_oracle (readonly)
|
|
1258
|
+
* 15. switchboard_price_oracle (readonly)
|
|
1259
|
+
* 16. switchboard_twap_oracle (readonly)
|
|
1260
|
+
* 17. scope_prices (readonly)
|
|
1261
|
+
* 18. instruction_sysvar_account (readonly)
|
|
1262
|
+
* 19. token_program (readonly) — SPL Token
|
|
1263
|
+
*
|
|
1264
|
+
* No args beyond disc.
|
|
1265
|
+
*/
|
|
1266
|
+
export async function createHarvestStrategyYieldIx(operatorAuthority, marketId, depositMint, vaultUsdcAta, treasuryAta, ctokenAta, klendProgram, klendReserve, klendLendingMarket, klendLendingMarketAuthority, reserveLiquiditySupply, reserveCollateralMint, pythOracle, switchboardPriceOracle, switchboardTwapOracle, scopePrices, programId = PROGRAM_ID) {
|
|
1267
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
1268
|
+
const marketVault = getMarketVaultPDA(protocolState, marketId, programId);
|
|
1269
|
+
const strategyVault = getStrategyVaultPDA(marketVault, programId);
|
|
1270
|
+
const disc = await anchorDisc('harvest_strategy_yield');
|
|
1271
|
+
const data = Buffer.alloc(8);
|
|
1272
|
+
disc.copy(data, 0);
|
|
1273
|
+
return new TransactionInstruction({
|
|
1274
|
+
programId,
|
|
1275
|
+
keys: [
|
|
1276
|
+
{ pubkey: operatorAuthority, isSigner: true, isWritable: true },
|
|
1277
|
+
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
1278
|
+
{ pubkey: marketVault, isSigner: false, isWritable: false },
|
|
1279
|
+
{ pubkey: strategyVault, isSigner: false, isWritable: true },
|
|
1280
|
+
{ pubkey: depositMint, isSigner: false, isWritable: false },
|
|
1281
|
+
{ pubkey: vaultUsdcAta, isSigner: false, isWritable: false },
|
|
1282
|
+
{ pubkey: treasuryAta, isSigner: false, isWritable: true },
|
|
1283
|
+
{ pubkey: ctokenAta, isSigner: false, isWritable: true },
|
|
1284
|
+
{ pubkey: klendProgram, isSigner: false, isWritable: false },
|
|
1285
|
+
{ pubkey: klendReserve, isSigner: false, isWritable: true },
|
|
1286
|
+
{ pubkey: klendLendingMarket, isSigner: false, isWritable: false },
|
|
1287
|
+
{ pubkey: klendLendingMarketAuthority, isSigner: false, isWritable: false },
|
|
1288
|
+
{ pubkey: reserveLiquiditySupply, isSigner: false, isWritable: true },
|
|
1289
|
+
{ pubkey: reserveCollateralMint, isSigner: false, isWritable: true },
|
|
1290
|
+
{ pubkey: pythOracle, isSigner: false, isWritable: false },
|
|
1291
|
+
{ pubkey: switchboardPriceOracle, isSigner: false, isWritable: false },
|
|
1292
|
+
{ pubkey: switchboardTwapOracle, isSigner: false, isWritable: false },
|
|
1293
|
+
{ pubkey: scopePrices, isSigner: false, isWritable: false },
|
|
1294
|
+
{ pubkey: SYSVAR_INSTRUCTIONS_ID, isSigner: false, isWritable: false },
|
|
1295
|
+
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1296
|
+
],
|
|
1297
|
+
data,
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
// ── 18. createHarvestAndDistributeFeesIx ─────────────────────────────
|
|
1301
|
+
/**
|
|
1302
|
+
* Build a `harvest_fees` TransactionInstruction.
|
|
1303
|
+
*
|
|
1304
|
+
* Permissionless. Sweeps Token-2022 withheld fees from source accounts
|
|
1305
|
+
* into the treasury ATA. Source accounts are passed via `sourceAccounts`.
|
|
1306
|
+
*
|
|
1307
|
+
* Accounts (order must match HarvestFees struct in governance.rs):
|
|
1308
|
+
* 0. authority (signer, writable)
|
|
1309
|
+
* 1. protocol_state (readonly) — legacy PDA (seeds = ["protocol", mint])
|
|
1310
|
+
* 2. mint (writable)
|
|
1311
|
+
* 3. treasury (writable) — treasury ATA
|
|
1312
|
+
* 4. token_program (readonly) — Token-2022
|
|
1313
|
+
* + remaining_accounts: source token accounts to sweep
|
|
1314
|
+
*
|
|
1315
|
+
* No args beyond disc.
|
|
1316
|
+
*/
|
|
1317
|
+
export async function createHarvestAndDistributeFeesIx(authority, ccmMint, treasuryAta,
|
|
1318
|
+
/** Token-2022 accounts with withheld fees to sweep (max 30). */
|
|
1319
|
+
sourceAccounts, programId = PROGRAM_ID) {
|
|
1320
|
+
const legacyProtocolState = getLegacyProtocolStatePDA(ccmMint, programId);
|
|
1321
|
+
const disc = await anchorDisc('harvest_fees');
|
|
1322
|
+
const data = Buffer.alloc(8);
|
|
1323
|
+
disc.copy(data, 0);
|
|
1324
|
+
const keys = [
|
|
1325
|
+
{ pubkey: authority, isSigner: true, isWritable: true },
|
|
1326
|
+
{ pubkey: legacyProtocolState, isSigner: false, isWritable: false },
|
|
1327
|
+
{ pubkey: ccmMint, isSigner: false, isWritable: true },
|
|
1328
|
+
{ pubkey: treasuryAta, isSigner: false, isWritable: true },
|
|
1329
|
+
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1330
|
+
];
|
|
1331
|
+
// Append source accounts as remaining_accounts
|
|
1332
|
+
for (const source of sourceAccounts) {
|
|
1333
|
+
keys.push({ pubkey: source, isSigner: false, isWritable: true });
|
|
1334
|
+
}
|
|
1335
|
+
return new TransactionInstruction({ programId, keys, data });
|
|
1336
|
+
}
|
|
1337
|
+
// ── 19. createRouteTreasuryIx ────────────────────────────────────────
|
|
1338
|
+
/**
|
|
1339
|
+
* Build a `route_treasury` TransactionInstruction.
|
|
1340
|
+
*
|
|
1341
|
+
* Phase 2 treasury routing — moves CCM from treasury to destination with min_reserve guard.
|
|
1342
|
+
*
|
|
1343
|
+
* Accounts (order must match RouteTreasury struct in governance.rs):
|
|
1344
|
+
* 0. admin (signer, writable)
|
|
1345
|
+
* 1. protocol_state (readonly) — legacy PDA (seeds = ["protocol", mint])
|
|
1346
|
+
* 2. mint (readonly)
|
|
1347
|
+
* 3. treasury_ata (writable) — source
|
|
1348
|
+
* 4. destination_ata (writable) — target
|
|
1349
|
+
* 5. token_program (readonly) — Token-2022
|
|
1350
|
+
*
|
|
1351
|
+
* Args: amount (u64), min_reserve (u64)
|
|
1352
|
+
*/
|
|
1353
|
+
export async function createRouteTreasuryIx(admin, ccmMint, treasuryAta, destinationAta, amount, minReserve, programId = PROGRAM_ID) {
|
|
1354
|
+
const legacyProtocolState = getLegacyProtocolStatePDA(ccmMint, programId);
|
|
1355
|
+
const disc = await anchorDisc('route_treasury');
|
|
1356
|
+
const data = Buffer.alloc(24);
|
|
1357
|
+
disc.copy(data, 0);
|
|
1358
|
+
data.writeBigUInt64LE(BigInt(amount), 8);
|
|
1359
|
+
data.writeBigUInt64LE(BigInt(minReserve), 16);
|
|
1360
|
+
return new TransactionInstruction({
|
|
1361
|
+
programId,
|
|
1362
|
+
keys: [
|
|
1363
|
+
{ pubkey: admin, isSigner: true, isWritable: true },
|
|
1364
|
+
{ pubkey: legacyProtocolState, isSigner: false, isWritable: false },
|
|
1365
|
+
{ pubkey: ccmMint, isSigner: false, isWritable: false },
|
|
1366
|
+
{ pubkey: treasuryAta, isSigner: false, isWritable: true },
|
|
1367
|
+
{ pubkey: destinationAta, isSigner: false, isWritable: true },
|
|
1368
|
+
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1369
|
+
],
|
|
1370
|
+
data,
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
// ── 20. createWithdrawFeesFromMintIx ─────────────────────────────────
|
|
1374
|
+
/**
|
|
1375
|
+
* Build a `withdraw_fees_from_mint` TransactionInstruction.
|
|
1376
|
+
*
|
|
1377
|
+
* Permissionless. Moves accumulated withheld fees from the mint itself to treasury.
|
|
1378
|
+
*
|
|
1379
|
+
* Accounts (order must match WithdrawFeesFromMint struct in governance.rs):
|
|
1380
|
+
* 0. authority (signer, writable)
|
|
1381
|
+
* 1. protocol_state (readonly) — legacy PDA (seeds = ["protocol", mint])
|
|
1382
|
+
* 2. mint (writable)
|
|
1383
|
+
* 3. treasury_ata (writable)
|
|
1384
|
+
* 4. token_program (readonly) — Token-2022
|
|
1385
|
+
*
|
|
1386
|
+
* No args beyond disc.
|
|
1387
|
+
*/
|
|
1388
|
+
export async function createWithdrawFeesFromMintIx(authority, ccmMint, treasuryAta, programId = PROGRAM_ID) {
|
|
1389
|
+
const legacyProtocolState = getLegacyProtocolStatePDA(ccmMint, programId);
|
|
1390
|
+
const disc = await anchorDisc('withdraw_fees_from_mint');
|
|
1391
|
+
const data = Buffer.alloc(8);
|
|
1392
|
+
disc.copy(data, 0);
|
|
1393
|
+
return new TransactionInstruction({
|
|
1394
|
+
programId,
|
|
1395
|
+
keys: [
|
|
1396
|
+
{ pubkey: authority, isSigner: true, isWritable: true },
|
|
1397
|
+
{ pubkey: legacyProtocolState, isSigner: false, isWritable: false },
|
|
1398
|
+
{ pubkey: ccmMint, isSigner: false, isWritable: true },
|
|
1399
|
+
{ pubkey: treasuryAta, isSigner: false, isWritable: true },
|
|
1400
|
+
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1401
|
+
],
|
|
1402
|
+
data,
|
|
1403
|
+
});
|
|
1404
|
+
}
|
|
1405
|
+
// ── 21. createReallocLegacyProtocolIx ────────────────────────────────
|
|
1406
|
+
/**
|
|
1407
|
+
* Build a `realloc_legacy_protocol` TransactionInstruction.
|
|
1408
|
+
*
|
|
1409
|
+
* One-shot migration: extends the legacy 141-byte ProtocolState PDA
|
|
1410
|
+
* (seeds = ["protocol", mint]) to 173 bytes and inserts oracle_authority.
|
|
1411
|
+
*
|
|
1412
|
+
* Accounts (order must match ReallocLegacyProtocol struct in governance.rs):
|
|
1413
|
+
* 0. admin (signer, writable)
|
|
1414
|
+
* 1. live_protocol_state (readonly) — current ProtocolState (seeds = ["protocol_state"])
|
|
1415
|
+
* 2. legacy_protocol_state (writable) — legacy PDA (seeds = ["protocol", mint])
|
|
1416
|
+
* 3. mint (readonly)
|
|
1417
|
+
* 4. system_program (readonly)
|
|
1418
|
+
*
|
|
1419
|
+
* No args beyond disc.
|
|
1420
|
+
*/
|
|
1421
|
+
export async function createReallocLegacyProtocolIx(admin, ccmMint, programId = PROGRAM_ID) {
|
|
1422
|
+
const liveProtocolState = getProtocolStatePDA(programId);
|
|
1423
|
+
const legacyProtocolState = getLegacyProtocolStatePDA(ccmMint, programId);
|
|
1424
|
+
const disc = await anchorDisc('realloc_legacy_protocol');
|
|
1425
|
+
const data = Buffer.alloc(8);
|
|
1426
|
+
disc.copy(data, 0);
|
|
1427
|
+
return new TransactionInstruction({
|
|
1428
|
+
programId,
|
|
1429
|
+
keys: [
|
|
1430
|
+
{ pubkey: admin, isSigner: true, isWritable: true },
|
|
1431
|
+
{ pubkey: liveProtocolState, isSigner: false, isWritable: false },
|
|
1432
|
+
{ pubkey: legacyProtocolState, isSigner: false, isWritable: true },
|
|
1433
|
+
{ pubkey: ccmMint, isSigner: false, isWritable: false },
|
|
1434
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1435
|
+
],
|
|
1436
|
+
data,
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
// ── 22. createReallocMarketVaultIx ───────────────────────────────────
|
|
1440
|
+
/**
|
|
1441
|
+
* Build a `realloc_market_vault` TransactionInstruction.
|
|
1442
|
+
*
|
|
1443
|
+
* Grows existing MarketVault PDA from 137 to 153 bytes (Phase 2 NAV fields).
|
|
1444
|
+
* Admin-only. No-op if already at target size.
|
|
1445
|
+
*
|
|
1446
|
+
* Accounts (order must match ReallocMarketVault struct in vault.rs):
|
|
1447
|
+
* 0. payer (signer, writable)
|
|
1448
|
+
* 1. protocol_state (readonly)
|
|
1449
|
+
* 2. market_vault (writable) — UncheckedAccount (may be undersized)
|
|
1450
|
+
* 3. system_program (readonly)
|
|
1451
|
+
*
|
|
1452
|
+
* Args: market_id (u64)
|
|
1453
|
+
*/
|
|
1454
|
+
export async function createReallocMarketVaultIx(payer, marketId, programId = PROGRAM_ID) {
|
|
1455
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
1456
|
+
const marketVault = getMarketVaultPDA(protocolState, marketId, programId);
|
|
1457
|
+
const disc = await anchorDisc('realloc_market_vault');
|
|
1458
|
+
const data = Buffer.alloc(16);
|
|
1459
|
+
disc.copy(data, 0);
|
|
1460
|
+
data.writeBigUInt64LE(BigInt(marketId), 8);
|
|
1461
|
+
return new TransactionInstruction({
|
|
1462
|
+
programId,
|
|
1463
|
+
keys: [
|
|
1464
|
+
{ pubkey: payer, isSigner: true, isWritable: true },
|
|
1465
|
+
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
1466
|
+
{ pubkey: marketVault, isSigner: false, isWritable: true },
|
|
1467
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1468
|
+
],
|
|
1469
|
+
data,
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
// ── 23. createResolveMarketIx ────────────────────────────────────────
|
|
1473
|
+
/**
|
|
1474
|
+
* Build a `resolve_market` TransactionInstruction (prediction markets).
|
|
1475
|
+
*
|
|
1476
|
+
* Accounts (order must match ResolveMarket struct in markets.rs):
|
|
1477
|
+
* 0. resolver (signer)
|
|
1478
|
+
* 1. protocol_state (readonly)
|
|
1479
|
+
* 2. global_root_config (readonly)
|
|
1480
|
+
* 3. market_state (writable)
|
|
1481
|
+
*
|
|
1482
|
+
* Args: cumulative_total (u64), proof (Vec<[u8; 32]>)
|
|
1483
|
+
*/
|
|
1484
|
+
export async function createResolveMarketIx(resolver, ccmMint, marketId, cumulativeTotal, proofHex, programId = PROGRAM_ID) {
|
|
1485
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
1486
|
+
const globalRootConfig = getGlobalRootConfigPDA(ccmMint, programId);
|
|
1487
|
+
const marketState = getMarketStatePDA(ccmMint, marketId, programId);
|
|
1488
|
+
const proofBytes = proofHex.map((h) => Buffer.from(h, 'hex'));
|
|
1489
|
+
// Data: [8 disc][8 cumulative_total][4 proof_len][N * 32 proof]
|
|
1490
|
+
const disc = await anchorDisc('resolve_market');
|
|
1491
|
+
const dataLen = 8 + 8 + 4 + proofBytes.length * 32;
|
|
1492
|
+
const data = Buffer.alloc(dataLen);
|
|
1493
|
+
let offset = 0;
|
|
1494
|
+
disc.copy(data, offset);
|
|
1495
|
+
offset += 8;
|
|
1496
|
+
data.writeBigUInt64LE(BigInt(cumulativeTotal), offset);
|
|
1497
|
+
offset += 8;
|
|
1498
|
+
data.writeUInt32LE(proofBytes.length, offset);
|
|
1499
|
+
offset += 4;
|
|
1500
|
+
for (const node of proofBytes) {
|
|
1501
|
+
node.copy(data, offset);
|
|
1502
|
+
offset += 32;
|
|
1503
|
+
}
|
|
1504
|
+
return new TransactionInstruction({
|
|
1505
|
+
programId,
|
|
1506
|
+
keys: [
|
|
1507
|
+
{ pubkey: resolver, isSigner: true, isWritable: false },
|
|
1508
|
+
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
1509
|
+
{ pubkey: globalRootConfig, isSigner: false, isWritable: false },
|
|
1510
|
+
{ pubkey: marketState, isSigner: false, isWritable: true },
|
|
1511
|
+
],
|
|
1512
|
+
data,
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
// ── 24. createSetPausedOpenIx ────────────────────────────────────────
|
|
1516
|
+
/**
|
|
1517
|
+
* Build a `set_paused_open` TransactionInstruction.
|
|
1518
|
+
*
|
|
1519
|
+
* Emergency pause/unpause toggle. Admin-only.
|
|
1520
|
+
*
|
|
1521
|
+
* Accounts (order must match SetPausedOpen struct in admin.rs):
|
|
1522
|
+
* 0. admin (signer, writable)
|
|
1523
|
+
* 1. protocol_state (writable)
|
|
1524
|
+
*
|
|
1525
|
+
* Args: paused (bool, serialized as u8: 0 or 1)
|
|
1526
|
+
*/
|
|
1527
|
+
export async function createSetPausedOpenIx(admin, paused, programId = PROGRAM_ID) {
|
|
1528
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
1529
|
+
const disc = await anchorDisc('set_paused_open');
|
|
1530
|
+
const data = Buffer.alloc(9);
|
|
1531
|
+
disc.copy(data, 0);
|
|
1532
|
+
data.writeUInt8(paused ? 1 : 0, 8);
|
|
1533
|
+
return new TransactionInstruction({
|
|
1534
|
+
programId,
|
|
1535
|
+
keys: [
|
|
1536
|
+
{ pubkey: admin, isSigner: true, isWritable: true },
|
|
1537
|
+
{ pubkey: protocolState, isSigner: false, isWritable: true },
|
|
1538
|
+
],
|
|
1539
|
+
data,
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
// ── 25. createInitializeGlobalRootIx ─────────────────────────────────
|
|
1543
|
+
/**
|
|
1544
|
+
* Build an `initialize_global_root` TransactionInstruction.
|
|
1545
|
+
*
|
|
1546
|
+
* Creates the singleton GlobalRootConfig PDA for merkle root publishing.
|
|
1547
|
+
*
|
|
1548
|
+
* Accounts (order must match InitializeGlobalRoot struct in global.rs):
|
|
1549
|
+
* 0. payer (signer, writable)
|
|
1550
|
+
* 1. protocol_state (readonly)
|
|
1551
|
+
* 2. global_root_config (writable, init)
|
|
1552
|
+
* 3. system_program (readonly)
|
|
1553
|
+
*
|
|
1554
|
+
* No args beyond disc.
|
|
1555
|
+
*/
|
|
1556
|
+
export async function createInitializeGlobalRootIx(payer, ccmMint, programId = PROGRAM_ID) {
|
|
1557
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
1558
|
+
const globalRootConfig = getGlobalRootConfigPDA(ccmMint, programId);
|
|
1559
|
+
const disc = await anchorDisc('initialize_global_root');
|
|
1560
|
+
const data = Buffer.alloc(8);
|
|
1561
|
+
disc.copy(data, 0);
|
|
1562
|
+
return new TransactionInstruction({
|
|
1563
|
+
programId,
|
|
1564
|
+
keys: [
|
|
1565
|
+
{ pubkey: payer, isSigner: true, isWritable: true },
|
|
1566
|
+
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
1567
|
+
{ pubkey: globalRootConfig, isSigner: false, isWritable: true },
|
|
1568
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1569
|
+
],
|
|
1570
|
+
data,
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
// ── 26. createSetPriceUpdaterIx ──────────────────────────────────────
|
|
1574
|
+
/**
|
|
1575
|
+
* Build a `set_price_updater` TransactionInstruction.
|
|
1576
|
+
*
|
|
1577
|
+
* Authority rotates the cranker key for a price feed.
|
|
1578
|
+
*
|
|
1579
|
+
* Accounts (order must match SetPriceUpdater struct in price_feed.rs):
|
|
1580
|
+
* 0. authority (signer)
|
|
1581
|
+
* 1. price_feed (writable)
|
|
1582
|
+
*
|
|
1583
|
+
* Args: label ([u8; 32]), new_updater (Pubkey)
|
|
1584
|
+
*/
|
|
1585
|
+
export async function createSetPriceUpdaterIx(authority,
|
|
1586
|
+
/** 32-byte label (zero-padded). Use `Buffer.alloc(32)` and write your label. */
|
|
1587
|
+
label, newUpdater, programId = PROGRAM_ID) {
|
|
1588
|
+
const priceFeed = getPriceFeedPDA(label, programId);
|
|
1589
|
+
// Data: [8 disc][32 label][32 new_updater]
|
|
1590
|
+
const disc = await anchorDisc('set_price_updater');
|
|
1591
|
+
const data = Buffer.alloc(8 + 32 + 32);
|
|
1592
|
+
disc.copy(data, 0);
|
|
1593
|
+
label.copy(data, 8);
|
|
1594
|
+
newUpdater.toBuffer().copy(data, 40);
|
|
1595
|
+
return new TransactionInstruction({
|
|
1596
|
+
programId,
|
|
1597
|
+
keys: [
|
|
1598
|
+
{ pubkey: authority, isSigner: true, isWritable: false },
|
|
1599
|
+
{ pubkey: priceFeed, isSigner: false, isWritable: true },
|
|
1600
|
+
],
|
|
1601
|
+
data,
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
1605
|
+
// PREDICTION MARKET TRADING INSTRUCTIONS
|
|
1606
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
1607
|
+
/** Standard SPL Token program (used for YES/NO outcome tokens). */
|
|
1608
|
+
const SPL_TOKEN_PROGRAM_ID = new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');
|
|
1609
|
+
/**
|
|
1610
|
+
* Build a `mint_shares` TransactionInstruction (prediction markets).
|
|
1611
|
+
*
|
|
1612
|
+
* Deposits CCM into the prediction market vault and receives 1:1 YES + NO tokens.
|
|
1613
|
+
*
|
|
1614
|
+
* Accounts (13 fixed, order matches markets.rs mint_shares):
|
|
1615
|
+
* 0. depositor (signer, writable)
|
|
1616
|
+
* 1. protocol_state (readonly)
|
|
1617
|
+
* 2. market_state (writable)
|
|
1618
|
+
* 3. ccm_mint (readonly)
|
|
1619
|
+
* 4. depositor_ccm_ata (writable)
|
|
1620
|
+
* 5. vault (writable)
|
|
1621
|
+
* 6. yes_mint (writable)
|
|
1622
|
+
* 7. no_mint (writable)
|
|
1623
|
+
* 8. depositor_yes_ata (writable)
|
|
1624
|
+
* 9. depositor_no_ata (writable)
|
|
1625
|
+
* 10. mint_authority (readonly)
|
|
1626
|
+
* 11. token_program (Token-2022, for CCM)
|
|
1627
|
+
* 12. outcome_token_prog (SPL Token, for YES/NO)
|
|
1628
|
+
*
|
|
1629
|
+
* Args: amount (u64) — CCM native units to deposit
|
|
1630
|
+
*/
|
|
1631
|
+
export async function createMintSharesIx(depositor, ccmMint, marketId, amount, programId = PROGRAM_ID) {
|
|
1632
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
1633
|
+
const marketState = getMarketStatePDA(ccmMint, marketId, programId);
|
|
1634
|
+
const vault = getPredictionVaultPDA(ccmMint, marketId, programId);
|
|
1635
|
+
const yesMint = getPredictionYesMintPDA(ccmMint, marketId, programId);
|
|
1636
|
+
const noMint = getPredictionNoMintPDA(ccmMint, marketId, programId);
|
|
1637
|
+
const mintAuthority = getPredictionMintAuthorityPDA(ccmMint, marketId, programId);
|
|
1638
|
+
const depositorCcm = getAta(depositor, ccmMint, TOKEN_2022_PROGRAM_ID);
|
|
1639
|
+
const depositorYes = getAta(depositor, yesMint, SPL_TOKEN_PROGRAM_ID);
|
|
1640
|
+
const depositorNo = getAta(depositor, noMint, SPL_TOKEN_PROGRAM_ID);
|
|
1641
|
+
const disc = await anchorDisc('mint_shares');
|
|
1642
|
+
const data = Buffer.alloc(16);
|
|
1643
|
+
disc.copy(data, 0);
|
|
1644
|
+
data.writeBigUInt64LE(BigInt(amount), 8);
|
|
1645
|
+
return new TransactionInstruction({
|
|
1646
|
+
programId,
|
|
1647
|
+
keys: [
|
|
1648
|
+
{ pubkey: depositor, isSigner: true, isWritable: true },
|
|
1649
|
+
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
1650
|
+
{ pubkey: marketState, isSigner: false, isWritable: true },
|
|
1651
|
+
{ pubkey: ccmMint, isSigner: false, isWritable: false },
|
|
1652
|
+
{ pubkey: depositorCcm, isSigner: false, isWritable: true },
|
|
1653
|
+
{ pubkey: vault, isSigner: false, isWritable: true },
|
|
1654
|
+
{ pubkey: yesMint, isSigner: false, isWritable: true },
|
|
1655
|
+
{ pubkey: noMint, isSigner: false, isWritable: true },
|
|
1656
|
+
{ pubkey: depositorYes, isSigner: false, isWritable: true },
|
|
1657
|
+
{ pubkey: depositorNo, isSigner: false, isWritable: true },
|
|
1658
|
+
{ pubkey: mintAuthority, isSigner: false, isWritable: false },
|
|
1659
|
+
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1660
|
+
{ pubkey: SPL_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1661
|
+
],
|
|
1662
|
+
data,
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1665
|
+
/**
|
|
1666
|
+
* Build a `redeem_shares` TransactionInstruction (prediction markets).
|
|
1667
|
+
*
|
|
1668
|
+
* Burns equal YES + NO shares and returns CCM. Pre-resolution only.
|
|
1669
|
+
*
|
|
1670
|
+
* Accounts (13 fixed, order matches markets.rs redeem_shares):
|
|
1671
|
+
* 0. redeemer (signer, writable)
|
|
1672
|
+
* 1. protocol_state (readonly)
|
|
1673
|
+
* 2. market_state (writable)
|
|
1674
|
+
* 3. ccm_mint (readonly)
|
|
1675
|
+
* 4. vault (writable)
|
|
1676
|
+
* 5. yes_mint (writable)
|
|
1677
|
+
* 6. no_mint (writable)
|
|
1678
|
+
* 7. redeemer_yes_ata (writable)
|
|
1679
|
+
* 8. redeemer_no_ata (writable)
|
|
1680
|
+
* 9. redeemer_ccm_ata (writable)
|
|
1681
|
+
* 10. mint_authority (readonly)
|
|
1682
|
+
* 11. token_program (Token-2022, for CCM)
|
|
1683
|
+
* 12. outcome_token_prog (SPL Token, for YES/NO)
|
|
1684
|
+
*
|
|
1685
|
+
* Args: shares (u64) — number of YES+NO pairs to redeem
|
|
1686
|
+
*/
|
|
1687
|
+
export async function createRedeemSharesIx(redeemer, ccmMint, marketId, shares, programId = PROGRAM_ID) {
|
|
1688
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
1689
|
+
const marketState = getMarketStatePDA(ccmMint, marketId, programId);
|
|
1690
|
+
const vault = getPredictionVaultPDA(ccmMint, marketId, programId);
|
|
1691
|
+
const yesMint = getPredictionYesMintPDA(ccmMint, marketId, programId);
|
|
1692
|
+
const noMint = getPredictionNoMintPDA(ccmMint, marketId, programId);
|
|
1693
|
+
const mintAuthority = getPredictionMintAuthorityPDA(ccmMint, marketId, programId);
|
|
1694
|
+
const redeemerYes = getAta(redeemer, yesMint, SPL_TOKEN_PROGRAM_ID);
|
|
1695
|
+
const redeemerNo = getAta(redeemer, noMint, SPL_TOKEN_PROGRAM_ID);
|
|
1696
|
+
const redeemerCcm = getAta(redeemer, ccmMint, TOKEN_2022_PROGRAM_ID);
|
|
1697
|
+
const disc = await anchorDisc('redeem_shares');
|
|
1698
|
+
const data = Buffer.alloc(16);
|
|
1699
|
+
disc.copy(data, 0);
|
|
1700
|
+
data.writeBigUInt64LE(BigInt(shares), 8);
|
|
1701
|
+
return new TransactionInstruction({
|
|
1702
|
+
programId,
|
|
1703
|
+
keys: [
|
|
1704
|
+
{ pubkey: redeemer, isSigner: true, isWritable: true },
|
|
1705
|
+
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
1706
|
+
{ pubkey: marketState, isSigner: false, isWritable: true },
|
|
1707
|
+
{ pubkey: ccmMint, isSigner: false, isWritable: false },
|
|
1708
|
+
{ pubkey: vault, isSigner: false, isWritable: true },
|
|
1709
|
+
{ pubkey: yesMint, isSigner: false, isWritable: true },
|
|
1710
|
+
{ pubkey: noMint, isSigner: false, isWritable: true },
|
|
1711
|
+
{ pubkey: redeemerYes, isSigner: false, isWritable: true },
|
|
1712
|
+
{ pubkey: redeemerNo, isSigner: false, isWritable: true },
|
|
1713
|
+
{ pubkey: redeemerCcm, isSigner: false, isWritable: true },
|
|
1714
|
+
{ pubkey: mintAuthority, isSigner: false, isWritable: false },
|
|
1715
|
+
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1716
|
+
{ pubkey: SPL_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1717
|
+
],
|
|
1718
|
+
data,
|
|
1719
|
+
});
|
|
1720
|
+
}
|
|
1721
|
+
/**
|
|
1722
|
+
* Build a `settle` TransactionInstruction (prediction markets).
|
|
1723
|
+
*
|
|
1724
|
+
* Burns winning-side tokens and returns CCM 1:1. Post-resolution only.
|
|
1725
|
+
*
|
|
1726
|
+
* Accounts (11 fixed, order matches markets.rs settle):
|
|
1727
|
+
* 0. settler (signer, writable)
|
|
1728
|
+
* 1. protocol_state (readonly)
|
|
1729
|
+
* 2. market_state (readonly)
|
|
1730
|
+
* 3. ccm_mint (readonly)
|
|
1731
|
+
* 4. vault (writable)
|
|
1732
|
+
* 5. winning_mint (writable) — yes_mint if YES won, no_mint if NO
|
|
1733
|
+
* 6. settler_winning (writable) — settler's ATA for the winning mint
|
|
1734
|
+
* 7. settler_ccm (writable) — settler's CCM ATA
|
|
1735
|
+
* 8. mint_authority (readonly)
|
|
1736
|
+
* 9. token_program (Token-2022, for CCM)
|
|
1737
|
+
* 10. outcome_token_prog (SPL Token, for YES/NO)
|
|
1738
|
+
*
|
|
1739
|
+
* Args: shares (u64) — winning tokens to settle
|
|
1740
|
+
*/
|
|
1741
|
+
export async function createSettlePredictionIx(settler, ccmMint, marketId, shares,
|
|
1742
|
+
/** true = YES won, false = NO won */
|
|
1743
|
+
yesOutcome, programId = PROGRAM_ID) {
|
|
1744
|
+
const protocolState = getProtocolStatePDA(programId);
|
|
1745
|
+
const marketState = getMarketStatePDA(ccmMint, marketId, programId);
|
|
1746
|
+
const vault = getPredictionVaultPDA(ccmMint, marketId, programId);
|
|
1747
|
+
const yesMint = getPredictionYesMintPDA(ccmMint, marketId, programId);
|
|
1748
|
+
const noMint = getPredictionNoMintPDA(ccmMint, marketId, programId);
|
|
1749
|
+
const mintAuthority = getPredictionMintAuthorityPDA(ccmMint, marketId, programId);
|
|
1750
|
+
const winningMint = yesOutcome ? yesMint : noMint;
|
|
1751
|
+
const settlerWinning = getAta(settler, winningMint, SPL_TOKEN_PROGRAM_ID);
|
|
1752
|
+
const settlerCcm = getAta(settler, ccmMint, TOKEN_2022_PROGRAM_ID);
|
|
1753
|
+
const disc = await anchorDisc('settle');
|
|
1754
|
+
const data = Buffer.alloc(16);
|
|
1755
|
+
disc.copy(data, 0);
|
|
1756
|
+
data.writeBigUInt64LE(BigInt(shares), 8);
|
|
1757
|
+
return new TransactionInstruction({
|
|
1758
|
+
programId,
|
|
1759
|
+
keys: [
|
|
1760
|
+
{ pubkey: settler, isSigner: true, isWritable: true },
|
|
1761
|
+
{ pubkey: protocolState, isSigner: false, isWritable: false },
|
|
1762
|
+
{ pubkey: marketState, isSigner: false, isWritable: false },
|
|
1763
|
+
{ pubkey: ccmMint, isSigner: false, isWritable: false },
|
|
1764
|
+
{ pubkey: vault, isSigner: false, isWritable: true },
|
|
1765
|
+
{ pubkey: winningMint, isSigner: false, isWritable: true },
|
|
1766
|
+
{ pubkey: settlerWinning, isSigner: false, isWritable: true },
|
|
1767
|
+
{ pubkey: settlerCcm, isSigner: false, isWritable: true },
|
|
1768
|
+
{ pubkey: mintAuthority, isSigner: false, isWritable: false },
|
|
1769
|
+
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1770
|
+
{ pubkey: SPL_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1771
|
+
],
|
|
1772
|
+
data,
|
|
1773
|
+
});
|
|
1774
|
+
}
|