@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.
@@ -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
- // ── Instruction Builders ───────────────────────────────
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 (writable)
119
- * 9. user_ccm_ata (writable)
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) — CCM mint (Token-2022 on mainnet)
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 of instructions: idempotent ATA create for CCM + the settle IX.
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
- // Fetch vault to discover mints
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, ccmTokenProgram);
148
- // Prepend idempotent ATA creation for CCM (no-op if exists)
149
- const ixs = [
150
- createAtaIdempotentIx(user, userCcmAta, user, protocol.mint, ccmTokenProgram),
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
- ixs.push(new TransactionInstruction({
158
- programId,
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: ccmTokenProgram, isSigner: false, isWritable: false },
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
+ }