@wzrd_sol/sdk 0.1.1 → 0.1.3

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,50 @@ 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
+ const accountsMode = options.accountsMode
225
+ ?? 'current';
226
+ // Fetch vault + protocol state to discover mints/accounts.
131
227
  const vaultInfo = await connection.getAccountInfo(marketVault);
132
228
  if (!vaultInfo)
133
229
  throw new Error(`MarketVault not found for market ${marketId}`);
134
230
  const vault = parseMarketVault(Buffer.from(vaultInfo.data));
135
- // Fetch protocol state to discover CCM mint
136
231
  const protocolInfo = await connection.getAccountInfo(protocolState);
137
232
  if (!protocolInfo)
138
233
  throw new Error('ProtocolState not found');
139
234
  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
235
  const userVlofiAta = getAta(user, vault.vlofiMint, TOKEN_PROGRAM_ID);
146
236
  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
- ];
237
+ const userCcmAta = getAta(user, protocol.mint, TOKEN_2022_PROGRAM_ID);
238
+ const ixs = [];
239
+ if (accountsMode === 'legacy_ccm') {
240
+ // The deployed mainnet settle ABI still validates the user's CCM ATA even though
241
+ // no mint_to CPI occurs anymore. Make the ATA creation idempotent so settle works
242
+ // whether or not the wallet has claimed before.
243
+ ixs.push(createAtaIdempotentIx(user, userCcmAta, user, protocol.mint, TOKEN_2022_PROGRAM_ID));
244
+ }
152
245
  // Instruction data: [8 disc][8 market_id LE]
153
246
  const disc = await anchorDisc('settle_market');
154
247
  const data = Buffer.alloc(16);
155
248
  disc.copy(data, 0);
156
249
  data.writeBigUInt64LE(BigInt(marketId), 8);
157
- ixs.push(new TransactionInstruction({
158
- programId,
159
- keys: [
250
+ const keys = accountsMode === 'legacy_ccm'
251
+ ? [
160
252
  { pubkey: user, isSigner: true, isWritable: true },
161
253
  { pubkey: protocolState, isSigner: false, isWritable: false },
162
254
  { pubkey: marketVault, isSigner: false, isWritable: true },
@@ -169,8 +261,23 @@ export async function createSettleMarketIx(connection, user, marketId, programId
169
261
  { pubkey: userCcmAta, isSigner: false, isWritable: true },
170
262
  { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
171
263
  { pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
172
- { pubkey: ccmTokenProgram, isSigner: false, isWritable: false },
173
- ],
264
+ { pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
265
+ ]
266
+ : [
267
+ { pubkey: user, isSigner: true, isWritable: true },
268
+ { pubkey: protocolState, isSigner: false, isWritable: false },
269
+ { pubkey: marketVault, isSigner: false, isWritable: true },
270
+ { pubkey: userPosition, isSigner: false, isWritable: true },
271
+ { pubkey: vault.vlofiMint, isSigner: false, isWritable: true },
272
+ { pubkey: userVlofiAta, isSigner: false, isWritable: true },
273
+ { pubkey: vault.vaultAta, isSigner: false, isWritable: true },
274
+ { pubkey: userUsdcAta, isSigner: false, isWritable: true },
275
+ { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
276
+ { pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
277
+ ];
278
+ ixs.push(new TransactionInstruction({
279
+ programId,
280
+ keys,
174
281
  data,
175
282
  }));
176
283
  return ixs;
@@ -285,6 +392,64 @@ proofHex, programId = PROGRAM_ID) {
285
392
  }));
286
393
  return ixs;
287
394
  }
395
+ /**
396
+ * Build a `claim_global_v2` TransactionInstruction (V5 leaf format).
397
+ *
398
+ * Same accounts as claim_global, but args split cumulative_total into
399
+ * base_yield + attention_bonus for V5 merkle leaf verification.
400
+ */
401
+ export async function createClaimGlobalV2Ix(connection, claimer, rootSeq, baseYield, attentionBonus, proofHex, programId = PROGRAM_ID) {
402
+ const protocolState = getProtocolStatePDA(programId);
403
+ const protocolInfo = await connection.getAccountInfo(protocolState);
404
+ if (!protocolInfo)
405
+ throw new Error('ProtocolState not found');
406
+ const protocol = parseProtocolState(Buffer.from(protocolInfo.data));
407
+ const ccmMint = protocol.mint;
408
+ const globalRootConfig = getGlobalRootConfigPDA(ccmMint, programId);
409
+ const claimState = getClaimStatePDA(ccmMint, claimer, programId);
410
+ const treasuryAta = getAta(protocolState, ccmMint, TOKEN_2022_PROGRAM_ID);
411
+ const claimerAta = getAta(claimer, ccmMint, TOKEN_2022_PROGRAM_ID);
412
+ const ixs = [
413
+ createAtaIdempotentIx(claimer, claimerAta, claimer, ccmMint, TOKEN_2022_PROGRAM_ID),
414
+ ];
415
+ const proofBytes = proofHex.map((h) => Buffer.from(h, 'hex'));
416
+ // Data: [8 disc][8 root_seq][8 base_yield][8 attention_bonus][4 proof_len][N*32 proof]
417
+ const disc = await anchorDisc('claim_global_v2');
418
+ const dataLen = 8 + 8 + 8 + 8 + 4 + proofBytes.length * 32;
419
+ const data = Buffer.alloc(dataLen);
420
+ let offset = 0;
421
+ disc.copy(data, offset);
422
+ offset += 8;
423
+ data.writeBigUInt64LE(BigInt(rootSeq), offset);
424
+ offset += 8;
425
+ data.writeBigUInt64LE(BigInt(baseYield), offset);
426
+ offset += 8;
427
+ data.writeBigUInt64LE(BigInt(attentionBonus), offset);
428
+ offset += 8;
429
+ data.writeUInt32LE(proofBytes.length, offset);
430
+ offset += 4;
431
+ for (const node of proofBytes) {
432
+ node.copy(data, offset);
433
+ offset += 32;
434
+ }
435
+ ixs.push(new TransactionInstruction({
436
+ programId,
437
+ keys: [
438
+ { pubkey: claimer, isSigner: true, isWritable: true },
439
+ { pubkey: protocolState, isSigner: false, isWritable: true },
440
+ { pubkey: globalRootConfig, isSigner: false, isWritable: false },
441
+ { pubkey: claimState, isSigner: false, isWritable: true },
442
+ { pubkey: ccmMint, isSigner: false, isWritable: false },
443
+ { pubkey: treasuryAta, isSigner: false, isWritable: true },
444
+ { pubkey: claimerAta, isSigner: false, isWritable: true },
445
+ { pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
446
+ { pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
447
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
448
+ ],
449
+ data,
450
+ }));
451
+ return ixs;
452
+ }
288
453
  // ── Admin Instruction Builders ────────────────────────
289
454
  /**
290
455
  * Build a `create_channel_config_v2` TransactionInstruction.
@@ -330,3 +495,1082 @@ mint, subject, authority, creatorWallet, creatorFeeBps, programId = PROGRAM_ID)
330
495
  data,
331
496
  });
332
497
  }
498
+ // ══════════════════════════════════════════════════════════════════════════════
499
+ // Extended Instruction Builders (Phase 2 + Full On-Chain Coverage)
500
+ // ══════════════════════════════════════════════════════════════════════════════
501
+ /** Sysvar Instructions program address (required by Kamino CPI). */
502
+ const SYSVAR_INSTRUCTIONS_ID = new PublicKey('Sysvar1nstructions1111111111111111111111111');
503
+ /** PDA seed constant for legacy protocol state (seeds = ["protocol", mint]). */
504
+ const PROTOCOL_SEED = Buffer.from('protocol');
505
+ /** PDA seed constant for strategy vault. */
506
+ const STRATEGY_VAULT_SEED = Buffer.from('strategy_vault');
507
+ /** PDA seed constant for prediction market state. */
508
+ const MARKET_STATE_SEED = Buffer.from('market');
509
+ /** PDA seed for channel stake pool. */
510
+ const CHANNEL_STAKE_POOL_SEED = Buffer.from('channel_pool');
511
+ /** PDA seed for channel user stake. */
512
+ const CHANNEL_USER_STAKE_SEED = Buffer.from('channel_user');
513
+ /** PDA seed for soulbound NFT mint. */
514
+ const STAKE_NFT_MINT_SEED = Buffer.from('stake_nft');
515
+ /** PDA seed for stake vault (holds staked CCM). */
516
+ const STAKE_VAULT_SEED = Buffer.from('stake_vault');
517
+ // ── PDA derivation helpers (local, not exported from pda.ts) ─────────
518
+ function getStrategyVaultPDA(marketVault, programId = PROGRAM_ID) {
519
+ return PublicKey.findProgramAddressSync([STRATEGY_VAULT_SEED, marketVault.toBuffer()], programId)[0];
520
+ }
521
+ function getMarketStatePDA(ccmMint, marketId, programId = PROGRAM_ID) {
522
+ const idBuf = Buffer.alloc(8);
523
+ idBuf.writeBigUInt64LE(BigInt(marketId));
524
+ return PublicKey.findProgramAddressSync([MARKET_STATE_SEED, ccmMint.toBuffer(), idBuf], programId)[0];
525
+ }
526
+ function getLegacyProtocolStatePDA(ccmMint, programId = PROGRAM_ID) {
527
+ return PublicKey.findProgramAddressSync([PROTOCOL_SEED, ccmMint.toBuffer()], programId)[0];
528
+ }
529
+ function getStakePoolPDA(channelConfig, programId = PROGRAM_ID) {
530
+ return PublicKey.findProgramAddressSync([CHANNEL_STAKE_POOL_SEED, channelConfig.toBuffer()], programId)[0];
531
+ }
532
+ function getStakeVaultPDA(stakePool, programId = PROGRAM_ID) {
533
+ return PublicKey.findProgramAddressSync([STAKE_VAULT_SEED, stakePool.toBuffer()], programId)[0];
534
+ }
535
+ function getUserStakePDA(channelConfig, user, programId = PROGRAM_ID) {
536
+ return PublicKey.findProgramAddressSync([CHANNEL_USER_STAKE_SEED, channelConfig.toBuffer(), user.toBuffer()], programId)[0];
537
+ }
538
+ function getStakeNftMintPDA(stakePool, user, programId = PROGRAM_ID) {
539
+ return PublicKey.findProgramAddressSync([STAKE_NFT_MINT_SEED, stakePool.toBuffer(), user.toBuffer()], programId)[0];
540
+ }
541
+ function getPriceFeedPDA(label, programId = PROGRAM_ID) {
542
+ return PublicKey.findProgramAddressSync([Buffer.from('price_feed'), label], programId)[0];
543
+ }
544
+ // ── 1. createCreateMarketIx ──────────────────────────────────────────
545
+ /**
546
+ * Build a `create_market` TransactionInstruction (prediction markets).
547
+ *
548
+ * Accounts (order must match CreateMarket struct in markets.rs):
549
+ * 0. authority (signer, writable)
550
+ * 1. protocol_state (readonly)
551
+ * 2. global_root_config (readonly)
552
+ * 3. market_state (writable, init)
553
+ * 4. system_program (readonly)
554
+ *
555
+ * Args (Borsh):
556
+ * market_id: u64
557
+ * creator_wallet: Pubkey (32 bytes)
558
+ * metric: u8
559
+ * target: u64
560
+ * resolution_root_seq: u64
561
+ */
562
+ export async function createCreateMarketIx(authority, ccmMint, marketId, creatorWallet, metric, target, resolutionRootSeq, programId = PROGRAM_ID) {
563
+ const protocolState = getProtocolStatePDA(programId);
564
+ const globalRootConfig = getGlobalRootConfigPDA(ccmMint, programId);
565
+ const marketState = getMarketStatePDA(ccmMint, marketId, programId);
566
+ // Data: [8 disc][8 market_id][32 creator_wallet][1 metric][8 target][8 resolution_root_seq]
567
+ const disc = await anchorDisc('create_market');
568
+ const data = Buffer.alloc(8 + 8 + 32 + 1 + 8 + 8);
569
+ let offset = 0;
570
+ disc.copy(data, offset);
571
+ offset += 8;
572
+ data.writeBigUInt64LE(BigInt(marketId), offset);
573
+ offset += 8;
574
+ creatorWallet.toBuffer().copy(data, offset);
575
+ offset += 32;
576
+ data.writeUInt8(metric, offset);
577
+ offset += 1;
578
+ data.writeBigUInt64LE(BigInt(target), offset);
579
+ offset += 8;
580
+ data.writeBigUInt64LE(BigInt(resolutionRootSeq), offset);
581
+ return new TransactionInstruction({
582
+ programId,
583
+ keys: [
584
+ { pubkey: authority, isSigner: true, isWritable: true },
585
+ { pubkey: protocolState, isSigner: false, isWritable: false },
586
+ { pubkey: globalRootConfig, isSigner: false, isWritable: false },
587
+ { pubkey: marketState, isSigner: false, isWritable: true },
588
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
589
+ ],
590
+ data,
591
+ });
592
+ }
593
+ // ── 2. createUpdateAttentionIx ───────────────────────────────────────
594
+ /**
595
+ * Build an `update_attention` TransactionInstruction.
596
+ *
597
+ * Oracle pushes attention multiplier to a user's market position.
598
+ *
599
+ * Accounts (order must match UpdateAttention struct in vault.rs):
600
+ * 0. oracle_authority (signer, writable)
601
+ * 1. protocol_state (readonly)
602
+ * 2. market_vault (readonly)
603
+ * 3. user_market_position (writable)
604
+ *
605
+ * Args: market_id (u64), user_pubkey (Pubkey), multiplier_bps (u64)
606
+ */
607
+ export async function createUpdateAttentionIx(oracleAuthority, marketId, userPubkey, multiplierBps, programId = PROGRAM_ID) {
608
+ const protocolState = getProtocolStatePDA(programId);
609
+ const marketVault = getMarketVaultPDA(protocolState, marketId, programId);
610
+ const userPosition = getUserPositionPDA(marketVault, userPubkey, programId);
611
+ // Data: [8 disc][8 market_id][32 user_pubkey][8 multiplier_bps]
612
+ const disc = await anchorDisc('update_attention');
613
+ const data = Buffer.alloc(8 + 8 + 32 + 8);
614
+ let offset = 0;
615
+ disc.copy(data, offset);
616
+ offset += 8;
617
+ data.writeBigUInt64LE(BigInt(marketId), offset);
618
+ offset += 8;
619
+ userPubkey.toBuffer().copy(data, offset);
620
+ offset += 32;
621
+ data.writeBigUInt64LE(BigInt(multiplierBps), offset);
622
+ return new TransactionInstruction({
623
+ programId,
624
+ keys: [
625
+ { pubkey: oracleAuthority, isSigner: true, isWritable: true },
626
+ { pubkey: protocolState, isSigner: false, isWritable: false },
627
+ { pubkey: marketVault, isSigner: false, isWritable: false },
628
+ { pubkey: userPosition, isSigner: false, isWritable: true },
629
+ ],
630
+ data,
631
+ });
632
+ }
633
+ // ── 3. createPublishGlobalRootIx ─────────────────────────────────────
634
+ /**
635
+ * Build a `publish_global_root` TransactionInstruction.
636
+ *
637
+ * Accounts (order must match PublishGlobalRoot struct in global.rs):
638
+ * 0. payer (signer, writable)
639
+ * 1. protocol_state (readonly)
640
+ * 2. global_root_config (writable)
641
+ *
642
+ * Args: root_seq (u64), root ([u8; 32]), dataset_hash ([u8; 32])
643
+ */
644
+ export async function createPublishGlobalRootIx(payer, ccmMint, rootSeq,
645
+ /** 32-byte merkle root (hex string or Buffer) */
646
+ root,
647
+ /** 32-byte dataset hash (hex string or Buffer) */
648
+ datasetHash, programId = PROGRAM_ID) {
649
+ const protocolState = getProtocolStatePDA(programId);
650
+ const globalRootConfig = getGlobalRootConfigPDA(ccmMint, programId);
651
+ const rootBuf = typeof root === 'string' ? Buffer.from(root, 'hex') : root;
652
+ const hashBuf = typeof datasetHash === 'string' ? Buffer.from(datasetHash, 'hex') : datasetHash;
653
+ // Data: [8 disc][8 root_seq][32 root][32 dataset_hash]
654
+ const disc = await anchorDisc('publish_global_root');
655
+ const data = Buffer.alloc(8 + 8 + 32 + 32);
656
+ let offset = 0;
657
+ disc.copy(data, offset);
658
+ offset += 8;
659
+ data.writeBigUInt64LE(BigInt(rootSeq), offset);
660
+ offset += 8;
661
+ rootBuf.copy(data, offset);
662
+ offset += 32;
663
+ hashBuf.copy(data, offset);
664
+ return new TransactionInstruction({
665
+ programId,
666
+ keys: [
667
+ { pubkey: payer, isSigner: true, isWritable: true },
668
+ { pubkey: protocolState, isSigner: false, isWritable: false },
669
+ { pubkey: globalRootConfig, isSigner: false, isWritable: true },
670
+ ],
671
+ data,
672
+ });
673
+ }
674
+ // ── 4. createClaimYieldIx ────────────────────────────────────────────
675
+ /**
676
+ * Build a `claim_yield` TransactionInstruction.
677
+ *
678
+ * NOTE: This instruction is deprecated on-chain and always returns
679
+ * `ClaimYieldDeprecated`. Included for SDK completeness.
680
+ *
681
+ * Accounts (order must match ClaimYield struct in vault.rs):
682
+ * 0. user (signer, writable)
683
+ * 1. protocol_state (readonly)
684
+ * 2. market_vault (readonly)
685
+ * 3. user_market_position (writable)
686
+ *
687
+ * Args: market_id (u64)
688
+ */
689
+ export async function createClaimYieldIx(user, marketId, programId = PROGRAM_ID) {
690
+ const protocolState = getProtocolStatePDA(programId);
691
+ const marketVault = getMarketVaultPDA(protocolState, marketId, programId);
692
+ const userPosition = getUserPositionPDA(marketVault, user, programId);
693
+ const disc = await anchorDisc('claim_yield');
694
+ const data = Buffer.alloc(16);
695
+ disc.copy(data, 0);
696
+ data.writeBigUInt64LE(BigInt(marketId), 8);
697
+ return new TransactionInstruction({
698
+ programId,
699
+ keys: [
700
+ { pubkey: user, isSigner: true, isWritable: true },
701
+ { pubkey: protocolState, isSigner: false, isWritable: false },
702
+ { pubkey: marketVault, isSigner: false, isWritable: false },
703
+ { pubkey: userPosition, isSigner: false, isWritable: true },
704
+ ],
705
+ data,
706
+ });
707
+ }
708
+ // ── 5. createCloseMarketIx ───────────────────────────────────────────
709
+ /**
710
+ * Build a `close_market` TransactionInstruction (prediction markets).
711
+ *
712
+ * Accounts (order must match CloseMarket struct in markets.rs):
713
+ * 0. admin (signer, writable)
714
+ * 1. protocol_state (readonly)
715
+ * 2. market_state (writable, close -> admin)
716
+ * 3. vault (writable) — must be empty
717
+ * 4. ccm_mint (readonly)
718
+ * 5. mint_authority (readonly)
719
+ * 6. token_program (readonly) — Token-2022
720
+ */
721
+ export async function createCloseMarketIx(admin, ccmMint, marketId, vault, mintAuthority, programId = PROGRAM_ID) {
722
+ const protocolState = getProtocolStatePDA(programId);
723
+ const marketState = getMarketStatePDA(ccmMint, marketId, programId);
724
+ const disc = await anchorDisc('close_market');
725
+ const data = Buffer.alloc(8);
726
+ disc.copy(data, 0);
727
+ return new TransactionInstruction({
728
+ programId,
729
+ keys: [
730
+ { pubkey: admin, isSigner: true, isWritable: true },
731
+ { pubkey: protocolState, isSigner: false, isWritable: false },
732
+ { pubkey: marketState, isSigner: false, isWritable: true },
733
+ { pubkey: vault, isSigner: false, isWritable: true },
734
+ { pubkey: ccmMint, isSigner: false, isWritable: false },
735
+ { pubkey: mintAuthority, isSigner: false, isWritable: false },
736
+ { pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
737
+ ],
738
+ data,
739
+ });
740
+ }
741
+ // ── 6. createUpdateNavIx ─────────────────────────────────────────────
742
+ /**
743
+ * Build an `update_nav` TransactionInstruction.
744
+ *
745
+ * Oracle authority sets NAV per vLOFI share on a MarketVault.
746
+ *
747
+ * Accounts (order must match UpdateNav struct in vault.rs):
748
+ * 0. oracle_authority (signer, writable)
749
+ * 1. protocol_state (readonly)
750
+ * 2. market_vault (writable)
751
+ *
752
+ * Args: market_id (u64), nav_per_share_bps (u64)
753
+ */
754
+ export async function createUpdateNavIx(oracleAuthority, marketId, navPerShareBps, programId = PROGRAM_ID) {
755
+ const protocolState = getProtocolStatePDA(programId);
756
+ const marketVault = getMarketVaultPDA(protocolState, marketId, programId);
757
+ // Data: [8 disc][8 market_id][8 nav_per_share_bps]
758
+ const disc = await anchorDisc('update_nav');
759
+ const data = Buffer.alloc(24);
760
+ disc.copy(data, 0);
761
+ data.writeBigUInt64LE(BigInt(marketId), 8);
762
+ data.writeBigUInt64LE(BigInt(navPerShareBps), 16);
763
+ return new TransactionInstruction({
764
+ programId,
765
+ keys: [
766
+ { pubkey: oracleAuthority, isSigner: true, isWritable: true },
767
+ { pubkey: protocolState, isSigner: false, isWritable: false },
768
+ { pubkey: marketVault, isSigner: false, isWritable: true },
769
+ ],
770
+ data,
771
+ });
772
+ }
773
+ // ── 7. createInitializeProtocolStateIx ───────────────────────────────
774
+ /**
775
+ * Build an `initialize_protocol_state` TransactionInstruction.
776
+ *
777
+ * One-time protocol setup. Creates the singleton ProtocolState PDA.
778
+ *
779
+ * Accounts (order must match InitializeProtocolState struct in vault.rs):
780
+ * 0. admin (signer, writable)
781
+ * 1. protocol_state (writable, init)
782
+ * 2. system_program (readonly)
783
+ *
784
+ * Args: publisher (Pubkey), treasury (Pubkey), oracle_authority (Pubkey), ccm_mint (Pubkey)
785
+ */
786
+ export async function createInitializeProtocolStateIx(admin, publisher, treasury, oracleAuthority, ccmMint, programId = PROGRAM_ID) {
787
+ const protocolState = getProtocolStatePDA(programId);
788
+ // Data: [8 disc][32 publisher][32 treasury][32 oracle_authority][32 ccm_mint]
789
+ const disc = await anchorDisc('initialize_protocol_state');
790
+ const data = Buffer.alloc(8 + 32 + 32 + 32 + 32);
791
+ let offset = 0;
792
+ disc.copy(data, offset);
793
+ offset += 8;
794
+ publisher.toBuffer().copy(data, offset);
795
+ offset += 32;
796
+ treasury.toBuffer().copy(data, offset);
797
+ offset += 32;
798
+ oracleAuthority.toBuffer().copy(data, offset);
799
+ offset += 32;
800
+ ccmMint.toBuffer().copy(data, offset);
801
+ return new TransactionInstruction({
802
+ programId,
803
+ keys: [
804
+ { pubkey: admin, isSigner: true, isWritable: true },
805
+ { pubkey: protocolState, isSigner: false, isWritable: true },
806
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
807
+ ],
808
+ data,
809
+ });
810
+ }
811
+ // ── 8. createUpdateProtocolStateIx (update_publisher_open) ───────────
812
+ /**
813
+ * Build an `update_publisher_open` TransactionInstruction.
814
+ *
815
+ * Admin updates the allowlisted publisher address.
816
+ *
817
+ * Accounts (order must match UpdatePublisherOpen struct in admin.rs):
818
+ * 0. admin (signer, writable)
819
+ * 1. protocol_state (writable)
820
+ *
821
+ * Args: new_publisher (Pubkey)
822
+ */
823
+ export async function createUpdateProtocolStateIx(admin, newPublisher, programId = PROGRAM_ID) {
824
+ const protocolState = getProtocolStatePDA(programId);
825
+ const disc = await anchorDisc('update_publisher_open');
826
+ const data = Buffer.alloc(8 + 32);
827
+ disc.copy(data, 0);
828
+ newPublisher.toBuffer().copy(data, 8);
829
+ return new TransactionInstruction({
830
+ programId,
831
+ keys: [
832
+ { pubkey: admin, isSigner: true, isWritable: true },
833
+ { pubkey: protocolState, isSigner: false, isWritable: true },
834
+ ],
835
+ data,
836
+ });
837
+ }
838
+ // ── 9. createSetTreasuryIx ───────────────────────────────────────────
839
+ /**
840
+ * Build a `set_treasury` TransactionInstruction.
841
+ *
842
+ * Admin updates the treasury wallet (fee destination owner).
843
+ *
844
+ * Accounts (order must match SetTreasury struct in admin.rs):
845
+ * 0. admin (signer, writable)
846
+ * 1. protocol_state (writable)
847
+ *
848
+ * Args: new_treasury (Pubkey)
849
+ */
850
+ export async function createSetTreasuryIx(admin, newTreasury, programId = PROGRAM_ID) {
851
+ const protocolState = getProtocolStatePDA(programId);
852
+ const disc = await anchorDisc('set_treasury');
853
+ const data = Buffer.alloc(8 + 32);
854
+ disc.copy(data, 0);
855
+ newTreasury.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
+ // ── 10. createStakeChannelIx ─────────────────────────────────────────
866
+ /**
867
+ * Build a `stake_channel` TransactionInstruction.
868
+ *
869
+ * Accounts (order must match StakeChannel struct in staking.rs):
870
+ * 0. user (signer)
871
+ * 1. payer (signer, writable)
872
+ * 2. protocol_state (readonly)
873
+ * 3. channel_config (readonly)
874
+ * 4. mint (readonly) — CCM Token-2022
875
+ * 5. stake_pool (writable)
876
+ * 6. user_stake (writable, init)
877
+ * 7. vault (writable) — pool's staking vault
878
+ * 8. user_token_account (writable) — user's CCM ATA
879
+ * 9. nft_mint (writable) — soulbound NFT mint PDA
880
+ * 10. nft_ata (writable) — user's NFT ATA
881
+ * 11. token_program (readonly) — Token-2022
882
+ * 12. associated_token_program (readonly)
883
+ * 13. system_program (readonly)
884
+ * 14. rent (readonly)
885
+ *
886
+ * Args: amount (u64), lock_duration (u64)
887
+ */
888
+ export async function createStakeChannelIx(user, payer, ccmMint, channelConfig, amount, lockDuration, programId = PROGRAM_ID) {
889
+ const protocolState = getProtocolStatePDA(programId);
890
+ const stakePool = getStakePoolPDA(channelConfig, programId);
891
+ const userStake = getUserStakePDA(channelConfig, user, programId);
892
+ const vault = getStakeVaultPDA(stakePool, programId);
893
+ const userTokenAccount = getAta(user, ccmMint, TOKEN_2022_PROGRAM_ID);
894
+ const nftMint = getStakeNftMintPDA(stakePool, user, programId);
895
+ const nftAta = getAta(user, nftMint, TOKEN_2022_PROGRAM_ID);
896
+ const disc = await anchorDisc('stake_channel');
897
+ const data = Buffer.alloc(8 + 8 + 8);
898
+ disc.copy(data, 0);
899
+ data.writeBigUInt64LE(BigInt(amount), 8);
900
+ data.writeBigUInt64LE(BigInt(lockDuration), 16);
901
+ return new TransactionInstruction({
902
+ programId,
903
+ keys: [
904
+ { pubkey: user, isSigner: true, isWritable: false },
905
+ { pubkey: payer, isSigner: true, isWritable: true },
906
+ { pubkey: protocolState, isSigner: false, isWritable: false },
907
+ { pubkey: channelConfig, isSigner: false, isWritable: false },
908
+ { pubkey: ccmMint, isSigner: false, isWritable: false },
909
+ { pubkey: stakePool, isSigner: false, isWritable: true },
910
+ { pubkey: userStake, isSigner: false, isWritable: true },
911
+ { pubkey: vault, isSigner: false, isWritable: true },
912
+ { pubkey: userTokenAccount, isSigner: false, isWritable: true },
913
+ { pubkey: nftMint, isSigner: false, isWritable: true },
914
+ { pubkey: nftAta, isSigner: false, isWritable: true },
915
+ { pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
916
+ { pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
917
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
918
+ { pubkey: new PublicKey('SysvarRent111111111111111111111111111111111'), isSigner: false, isWritable: false },
919
+ ],
920
+ data,
921
+ });
922
+ }
923
+ // ── 11. createUnstakeChannelIx ───────────────────────────────────────
924
+ /**
925
+ * Build an `unstake_channel` TransactionInstruction.
926
+ *
927
+ * Accounts (order must match UnstakeChannel struct in staking.rs):
928
+ * 0. user (signer, writable)
929
+ * 1. channel_config (readonly)
930
+ * 2. mint (readonly) — CCM Token-2022
931
+ * 3. stake_pool (writable)
932
+ * 4. user_stake (writable, close -> user)
933
+ * 5. vault (writable)
934
+ * 6. user_token_account (writable)
935
+ * 7. nft_mint (writable)
936
+ * 8. nft_ata (writable)
937
+ * 9. token_program (readonly) — Token-2022
938
+ * 10. associated_token_program (readonly)
939
+ *
940
+ * No args (no instruction data beyond disc).
941
+ */
942
+ export async function createUnstakeChannelIx(user, ccmMint, channelConfig, nftMint, programId = PROGRAM_ID) {
943
+ const stakePool = getStakePoolPDA(channelConfig, programId);
944
+ const userStake = getUserStakePDA(channelConfig, user, programId);
945
+ const vault = getStakeVaultPDA(stakePool, programId);
946
+ const userTokenAccount = getAta(user, ccmMint, TOKEN_2022_PROGRAM_ID);
947
+ const nftAta = getAta(user, nftMint, TOKEN_2022_PROGRAM_ID);
948
+ const disc = await anchorDisc('unstake_channel');
949
+ const data = Buffer.alloc(8);
950
+ disc.copy(data, 0);
951
+ return new TransactionInstruction({
952
+ programId,
953
+ keys: [
954
+ { pubkey: user, isSigner: true, isWritable: true },
955
+ { pubkey: channelConfig, isSigner: false, isWritable: false },
956
+ { pubkey: ccmMint, isSigner: false, isWritable: false },
957
+ { pubkey: stakePool, isSigner: false, isWritable: true },
958
+ { pubkey: userStake, isSigner: false, isWritable: true },
959
+ { pubkey: vault, isSigner: false, isWritable: true },
960
+ { pubkey: userTokenAccount, isSigner: false, isWritable: true },
961
+ { pubkey: nftMint, isSigner: false, isWritable: true },
962
+ { pubkey: nftAta, isSigner: false, isWritable: true },
963
+ { pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
964
+ { pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
965
+ ],
966
+ data,
967
+ });
968
+ }
969
+ // ── 12. createClaimChannelRewardsIx ──────────────────────────────────
970
+ /**
971
+ * Build a `claim_channel_rewards` TransactionInstruction.
972
+ *
973
+ * Accounts (order must match ClaimChannelRewards struct in staking.rs):
974
+ * 0. user (signer, writable)
975
+ * 1. channel_config (readonly)
976
+ * 2. mint (readonly) — CCM Token-2022
977
+ * 3. stake_pool (writable)
978
+ * 4. user_stake (writable)
979
+ * 5. vault (writable) — pool vault
980
+ * 6. user_token_account (writable) — receives rewards
981
+ * 7. token_program (readonly) — Token-2022
982
+ *
983
+ * No args beyond disc.
984
+ */
985
+ export async function createClaimChannelRewardsIx(user, ccmMint, channelConfig, programId = PROGRAM_ID) {
986
+ const stakePool = getStakePoolPDA(channelConfig, programId);
987
+ const userStake = getUserStakePDA(channelConfig, user, programId);
988
+ const vault = getStakeVaultPDA(stakePool, programId);
989
+ const userTokenAccount = getAta(user, ccmMint, TOKEN_2022_PROGRAM_ID);
990
+ const disc = await anchorDisc('claim_channel_rewards');
991
+ const data = Buffer.alloc(8);
992
+ disc.copy(data, 0);
993
+ return new TransactionInstruction({
994
+ programId,
995
+ keys: [
996
+ { pubkey: user, isSigner: true, isWritable: true },
997
+ { pubkey: channelConfig, isSigner: false, isWritable: false },
998
+ { pubkey: ccmMint, isSigner: false, isWritable: false },
999
+ { pubkey: stakePool, isSigner: false, isWritable: true },
1000
+ { pubkey: userStake, isSigner: false, isWritable: true },
1001
+ { pubkey: vault, isSigner: false, isWritable: true },
1002
+ { pubkey: userTokenAccount, isSigner: false, isWritable: true },
1003
+ { pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
1004
+ ],
1005
+ data,
1006
+ });
1007
+ }
1008
+ // ── 13. createInitializeStakePoolIx ──────────────────────────────────
1009
+ /**
1010
+ * Build an `initialize_stake_pool` TransactionInstruction.
1011
+ *
1012
+ * Accounts (order must match InitializeStakePool struct in staking.rs):
1013
+ * 0. payer (signer, writable)
1014
+ * 1. protocol_state (readonly)
1015
+ * 2. channel_config (readonly)
1016
+ * 3. mint (readonly) — CCM Token-2022
1017
+ * 4. stake_pool (writable, init)
1018
+ * 5. vault (writable, init) — pool's CCM vault
1019
+ * 6. token_program (readonly) — Token-2022
1020
+ * 7. system_program (readonly)
1021
+ *
1022
+ * No args beyond disc.
1023
+ */
1024
+ export async function createInitializeStakePoolIx(payer, ccmMint, channelConfig, programId = PROGRAM_ID) {
1025
+ const protocolState = getProtocolStatePDA(programId);
1026
+ const stakePool = getStakePoolPDA(channelConfig, programId);
1027
+ const vault = getStakeVaultPDA(stakePool, programId);
1028
+ const disc = await anchorDisc('initialize_stake_pool');
1029
+ const data = Buffer.alloc(8);
1030
+ disc.copy(data, 0);
1031
+ return new TransactionInstruction({
1032
+ programId,
1033
+ keys: [
1034
+ { pubkey: payer, isSigner: true, isWritable: true },
1035
+ { pubkey: protocolState, isSigner: false, isWritable: false },
1036
+ { pubkey: channelConfig, isSigner: false, isWritable: false },
1037
+ { pubkey: ccmMint, isSigner: false, isWritable: false },
1038
+ { pubkey: stakePool, isSigner: false, isWritable: true },
1039
+ { pubkey: vault, isSigner: false, isWritable: true },
1040
+ { pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
1041
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
1042
+ ],
1043
+ data,
1044
+ });
1045
+ }
1046
+ // ── 14. createInitializeStrategyVaultIx ──────────────────────────────
1047
+ /**
1048
+ * Build an `initialize_strategy_vault` TransactionInstruction.
1049
+ *
1050
+ * Accounts (order must match InitializeStrategyVault struct in strategy.rs):
1051
+ * 0. admin_authority (signer, writable)
1052
+ * 1. protocol_state (readonly)
1053
+ * 2. market_vault (readonly)
1054
+ * 3. deposit_mint (readonly)
1055
+ * 4. strategy_vault (writable, init)
1056
+ * 5. system_program (readonly)
1057
+ *
1058
+ * Args: reserve_ratio_bps (u16), utilization_cap_bps (u16),
1059
+ * operator_authority (Pubkey), klend_program (Pubkey),
1060
+ * klend_reserve (Pubkey), klend_lending_market (Pubkey), ctoken_ata (Pubkey)
1061
+ */
1062
+ export async function createInitializeStrategyVaultIx(adminAuthority, marketId, depositMint, reserveRatioBps, utilizationCapBps, operatorAuthority, klendProgram, klendReserve, klendLendingMarket, ctokenAta, programId = PROGRAM_ID) {
1063
+ const protocolState = getProtocolStatePDA(programId);
1064
+ const marketVault = getMarketVaultPDA(protocolState, marketId, programId);
1065
+ const strategyVault = getStrategyVaultPDA(marketVault, programId);
1066
+ // Data: [8 disc][2 reserve_ratio_bps][2 utilization_cap_bps]
1067
+ // [32 operator][32 klend_program][32 klend_reserve][32 klend_lending_market][32 ctoken_ata]
1068
+ const disc = await anchorDisc('initialize_strategy_vault');
1069
+ const data = Buffer.alloc(8 + 2 + 2 + 32 * 5);
1070
+ let offset = 0;
1071
+ disc.copy(data, offset);
1072
+ offset += 8;
1073
+ data.writeUInt16LE(reserveRatioBps, offset);
1074
+ offset += 2;
1075
+ data.writeUInt16LE(utilizationCapBps, offset);
1076
+ offset += 2;
1077
+ operatorAuthority.toBuffer().copy(data, offset);
1078
+ offset += 32;
1079
+ klendProgram.toBuffer().copy(data, offset);
1080
+ offset += 32;
1081
+ klendReserve.toBuffer().copy(data, offset);
1082
+ offset += 32;
1083
+ klendLendingMarket.toBuffer().copy(data, offset);
1084
+ offset += 32;
1085
+ ctokenAta.toBuffer().copy(data, offset);
1086
+ return new TransactionInstruction({
1087
+ programId,
1088
+ keys: [
1089
+ { pubkey: adminAuthority, isSigner: true, isWritable: true },
1090
+ { pubkey: protocolState, isSigner: false, isWritable: false },
1091
+ { pubkey: marketVault, isSigner: false, isWritable: false },
1092
+ { pubkey: depositMint, isSigner: false, isWritable: false },
1093
+ { pubkey: strategyVault, isSigner: false, isWritable: true },
1094
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
1095
+ ],
1096
+ data,
1097
+ });
1098
+ }
1099
+ // ── 15. createDeployToStrategyIx ─────────────────────────────────────
1100
+ /**
1101
+ * Build a `deploy_to_strategy` TransactionInstruction.
1102
+ *
1103
+ * Deploys USDC from MarketVault into Kamino K-Lend. Requires all Kamino
1104
+ * oracle accounts. All account addresses are pinned at strategy vault init.
1105
+ *
1106
+ * Accounts (order must match DeployToStrategy struct in strategy.rs):
1107
+ * 0. operator_authority (signer, writable)
1108
+ * 1. protocol_state (readonly)
1109
+ * 2. market_vault (readonly)
1110
+ * 3. strategy_vault (writable)
1111
+ * 4. deposit_mint (readonly)
1112
+ * 5. vault_usdc_ata (writable)
1113
+ * 6. ctoken_ata (writable)
1114
+ * 7. klend_program (readonly)
1115
+ * 8. klend_reserve (writable)
1116
+ * 9. klend_lending_market (readonly)
1117
+ * 10. klend_lending_market_authority (readonly)
1118
+ * 11. reserve_liquidity_supply (writable)
1119
+ * 12. reserve_collateral_mint (writable)
1120
+ * 13. pyth_oracle (readonly)
1121
+ * 14. switchboard_price_oracle (readonly)
1122
+ * 15. switchboard_twap_oracle (readonly)
1123
+ * 16. scope_prices (readonly)
1124
+ * 17. instruction_sysvar_account (readonly)
1125
+ * 18. token_program (readonly) — SPL Token
1126
+ *
1127
+ * Args: amount (u64)
1128
+ */
1129
+ export async function createDeployToStrategyIx(operatorAuthority, marketId, depositMint, vaultUsdcAta, ctokenAta, klendProgram, klendReserve, klendLendingMarket, klendLendingMarketAuthority, reserveLiquiditySupply, reserveCollateralMint, pythOracle, switchboardPriceOracle, switchboardTwapOracle, scopePrices, amount, programId = PROGRAM_ID) {
1130
+ const protocolState = getProtocolStatePDA(programId);
1131
+ const marketVault = getMarketVaultPDA(protocolState, marketId, programId);
1132
+ const strategyVault = getStrategyVaultPDA(marketVault, programId);
1133
+ const disc = await anchorDisc('deploy_to_strategy');
1134
+ const data = Buffer.alloc(16);
1135
+ disc.copy(data, 0);
1136
+ data.writeBigUInt64LE(BigInt(amount), 8);
1137
+ return new TransactionInstruction({
1138
+ programId,
1139
+ keys: [
1140
+ { pubkey: operatorAuthority, isSigner: true, isWritable: true },
1141
+ { pubkey: protocolState, isSigner: false, isWritable: false },
1142
+ { pubkey: marketVault, isSigner: false, isWritable: false },
1143
+ { pubkey: strategyVault, isSigner: false, isWritable: true },
1144
+ { pubkey: depositMint, isSigner: false, isWritable: false },
1145
+ { pubkey: vaultUsdcAta, isSigner: false, isWritable: true },
1146
+ { pubkey: ctokenAta, isSigner: false, isWritable: true },
1147
+ { pubkey: klendProgram, isSigner: false, isWritable: false },
1148
+ { pubkey: klendReserve, isSigner: false, isWritable: true },
1149
+ { pubkey: klendLendingMarket, isSigner: false, isWritable: false },
1150
+ { pubkey: klendLendingMarketAuthority, isSigner: false, isWritable: false },
1151
+ { pubkey: reserveLiquiditySupply, isSigner: false, isWritable: true },
1152
+ { pubkey: reserveCollateralMint, isSigner: false, isWritable: true },
1153
+ { pubkey: pythOracle, isSigner: false, isWritable: false },
1154
+ { pubkey: switchboardPriceOracle, isSigner: false, isWritable: false },
1155
+ { pubkey: switchboardTwapOracle, isSigner: false, isWritable: false },
1156
+ { pubkey: scopePrices, isSigner: false, isWritable: false },
1157
+ { pubkey: SYSVAR_INSTRUCTIONS_ID, isSigner: false, isWritable: false },
1158
+ { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
1159
+ ],
1160
+ data,
1161
+ });
1162
+ }
1163
+ // ── 16. createWithdrawFromStrategyIx ─────────────────────────────────
1164
+ /**
1165
+ * Build a `withdraw_from_strategy` TransactionInstruction.
1166
+ *
1167
+ * Withdraws USDC from Kamino back into the MarketVault reserve.
1168
+ * Same Kamino oracle accounts as deploy_to_strategy.
1169
+ *
1170
+ * Accounts (order must match WithdrawFromStrategy struct in strategy.rs):
1171
+ * Same layout as DeployToStrategy.
1172
+ *
1173
+ * Args: amount (u64)
1174
+ */
1175
+ export async function createWithdrawFromStrategyIx(operatorAuthority, marketId, depositMint, vaultUsdcAta, ctokenAta, klendProgram, klendReserve, klendLendingMarket, klendLendingMarketAuthority, reserveLiquiditySupply, reserveCollateralMint, pythOracle, switchboardPriceOracle, switchboardTwapOracle, scopePrices, amount, programId = PROGRAM_ID) {
1176
+ const protocolState = getProtocolStatePDA(programId);
1177
+ const marketVault = getMarketVaultPDA(protocolState, marketId, programId);
1178
+ const strategyVault = getStrategyVaultPDA(marketVault, programId);
1179
+ const disc = await anchorDisc('withdraw_from_strategy');
1180
+ const data = Buffer.alloc(16);
1181
+ disc.copy(data, 0);
1182
+ data.writeBigUInt64LE(BigInt(amount), 8);
1183
+ return new TransactionInstruction({
1184
+ programId,
1185
+ keys: [
1186
+ { pubkey: operatorAuthority, isSigner: true, isWritable: true },
1187
+ { pubkey: protocolState, isSigner: false, isWritable: false },
1188
+ { pubkey: marketVault, isSigner: false, isWritable: false },
1189
+ { pubkey: strategyVault, isSigner: false, isWritable: true },
1190
+ { pubkey: depositMint, isSigner: false, isWritable: false },
1191
+ { pubkey: vaultUsdcAta, isSigner: false, isWritable: true },
1192
+ { pubkey: ctokenAta, isSigner: false, isWritable: true },
1193
+ { pubkey: klendProgram, isSigner: false, isWritable: false },
1194
+ { pubkey: klendReserve, isSigner: false, isWritable: true },
1195
+ { pubkey: klendLendingMarket, isSigner: false, isWritable: false },
1196
+ { pubkey: klendLendingMarketAuthority, isSigner: false, isWritable: false },
1197
+ { pubkey: reserveLiquiditySupply, isSigner: false, isWritable: true },
1198
+ { pubkey: reserveCollateralMint, isSigner: false, isWritable: true },
1199
+ { pubkey: pythOracle, isSigner: false, isWritable: false },
1200
+ { pubkey: switchboardPriceOracle, isSigner: false, isWritable: false },
1201
+ { pubkey: switchboardTwapOracle, isSigner: false, isWritable: false },
1202
+ { pubkey: scopePrices, isSigner: false, isWritable: false },
1203
+ { pubkey: SYSVAR_INSTRUCTIONS_ID, isSigner: false, isWritable: false },
1204
+ { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
1205
+ ],
1206
+ data,
1207
+ });
1208
+ }
1209
+ // ── 17. createHarvestStrategyYieldIx ─────────────────────────────────
1210
+ /**
1211
+ * Build a `harvest_strategy_yield` TransactionInstruction.
1212
+ *
1213
+ * Harvests yield (NAV above principal) from Kamino and sends it to treasury.
1214
+ *
1215
+ * Accounts (order must match HarvestStrategyYield struct in strategy.rs):
1216
+ * 0. operator_authority (signer, writable)
1217
+ * 1. protocol_state (readonly)
1218
+ * 2. market_vault (readonly)
1219
+ * 3. strategy_vault (writable)
1220
+ * 4. deposit_mint (readonly)
1221
+ * 5. vault_usdc_ata (readonly) — for validation
1222
+ * 6. treasury_ata (writable) — receives yield USDC
1223
+ * 7. ctoken_ata (writable)
1224
+ * 8. klend_program (readonly)
1225
+ * 9. klend_reserve (writable)
1226
+ * 10. klend_lending_market (readonly)
1227
+ * 11. klend_lending_market_authority (readonly)
1228
+ * 12. reserve_liquidity_supply (writable)
1229
+ * 13. reserve_collateral_mint (writable)
1230
+ * 14. pyth_oracle (readonly)
1231
+ * 15. switchboard_price_oracle (readonly)
1232
+ * 16. switchboard_twap_oracle (readonly)
1233
+ * 17. scope_prices (readonly)
1234
+ * 18. instruction_sysvar_account (readonly)
1235
+ * 19. token_program (readonly) — SPL Token
1236
+ *
1237
+ * No args beyond disc.
1238
+ */
1239
+ export async function createHarvestStrategyYieldIx(operatorAuthority, marketId, depositMint, vaultUsdcAta, treasuryAta, ctokenAta, klendProgram, klendReserve, klendLendingMarket, klendLendingMarketAuthority, reserveLiquiditySupply, reserveCollateralMint, pythOracle, switchboardPriceOracle, switchboardTwapOracle, scopePrices, programId = PROGRAM_ID) {
1240
+ const protocolState = getProtocolStatePDA(programId);
1241
+ const marketVault = getMarketVaultPDA(protocolState, marketId, programId);
1242
+ const strategyVault = getStrategyVaultPDA(marketVault, programId);
1243
+ const disc = await anchorDisc('harvest_strategy_yield');
1244
+ const data = Buffer.alloc(8);
1245
+ disc.copy(data, 0);
1246
+ return new TransactionInstruction({
1247
+ programId,
1248
+ keys: [
1249
+ { pubkey: operatorAuthority, isSigner: true, isWritable: true },
1250
+ { pubkey: protocolState, isSigner: false, isWritable: false },
1251
+ { pubkey: marketVault, isSigner: false, isWritable: false },
1252
+ { pubkey: strategyVault, isSigner: false, isWritable: true },
1253
+ { pubkey: depositMint, isSigner: false, isWritable: false },
1254
+ { pubkey: vaultUsdcAta, isSigner: false, isWritable: false },
1255
+ { pubkey: treasuryAta, isSigner: false, isWritable: true },
1256
+ { pubkey: ctokenAta, isSigner: false, isWritable: true },
1257
+ { pubkey: klendProgram, isSigner: false, isWritable: false },
1258
+ { pubkey: klendReserve, isSigner: false, isWritable: true },
1259
+ { pubkey: klendLendingMarket, isSigner: false, isWritable: false },
1260
+ { pubkey: klendLendingMarketAuthority, isSigner: false, isWritable: false },
1261
+ { pubkey: reserveLiquiditySupply, isSigner: false, isWritable: true },
1262
+ { pubkey: reserveCollateralMint, isSigner: false, isWritable: true },
1263
+ { pubkey: pythOracle, isSigner: false, isWritable: false },
1264
+ { pubkey: switchboardPriceOracle, isSigner: false, isWritable: false },
1265
+ { pubkey: switchboardTwapOracle, isSigner: false, isWritable: false },
1266
+ { pubkey: scopePrices, isSigner: false, isWritable: false },
1267
+ { pubkey: SYSVAR_INSTRUCTIONS_ID, isSigner: false, isWritable: false },
1268
+ { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
1269
+ ],
1270
+ data,
1271
+ });
1272
+ }
1273
+ // ── 18. createHarvestAndDistributeFeesIx ─────────────────────────────
1274
+ /**
1275
+ * Build a `harvest_fees` TransactionInstruction.
1276
+ *
1277
+ * Permissionless. Sweeps Token-2022 withheld fees from source accounts
1278
+ * into the treasury ATA. Source accounts are passed via `sourceAccounts`.
1279
+ *
1280
+ * Accounts (order must match HarvestFees struct in governance.rs):
1281
+ * 0. authority (signer, writable)
1282
+ * 1. protocol_state (readonly) — legacy PDA (seeds = ["protocol", mint])
1283
+ * 2. mint (writable)
1284
+ * 3. treasury (writable) — treasury ATA
1285
+ * 4. token_program (readonly) — Token-2022
1286
+ * + remaining_accounts: source token accounts to sweep
1287
+ *
1288
+ * No args beyond disc.
1289
+ */
1290
+ export async function createHarvestAndDistributeFeesIx(authority, ccmMint, treasuryAta,
1291
+ /** Token-2022 accounts with withheld fees to sweep (max 30). */
1292
+ sourceAccounts, programId = PROGRAM_ID) {
1293
+ const legacyProtocolState = getLegacyProtocolStatePDA(ccmMint, programId);
1294
+ const disc = await anchorDisc('harvest_fees');
1295
+ const data = Buffer.alloc(8);
1296
+ disc.copy(data, 0);
1297
+ const keys = [
1298
+ { pubkey: authority, isSigner: true, isWritable: true },
1299
+ { pubkey: legacyProtocolState, isSigner: false, isWritable: false },
1300
+ { pubkey: ccmMint, isSigner: false, isWritable: true },
1301
+ { pubkey: treasuryAta, isSigner: false, isWritable: true },
1302
+ { pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
1303
+ ];
1304
+ // Append source accounts as remaining_accounts
1305
+ for (const source of sourceAccounts) {
1306
+ keys.push({ pubkey: source, isSigner: false, isWritable: true });
1307
+ }
1308
+ return new TransactionInstruction({ programId, keys, data });
1309
+ }
1310
+ // ── 19. createRouteTreasuryIx ────────────────────────────────────────
1311
+ /**
1312
+ * Build a `route_treasury` TransactionInstruction.
1313
+ *
1314
+ * Phase 2 treasury routing — moves CCM from treasury to destination with min_reserve guard.
1315
+ *
1316
+ * Accounts (order must match RouteTreasury struct in governance.rs):
1317
+ * 0. admin (signer, writable)
1318
+ * 1. protocol_state (readonly) — legacy PDA (seeds = ["protocol", mint])
1319
+ * 2. mint (readonly)
1320
+ * 3. treasury_ata (writable) — source
1321
+ * 4. destination_ata (writable) — target
1322
+ * 5. token_program (readonly) — Token-2022
1323
+ *
1324
+ * Args: amount (u64), min_reserve (u64)
1325
+ */
1326
+ export async function createRouteTreasuryIx(admin, ccmMint, treasuryAta, destinationAta, amount, minReserve, programId = PROGRAM_ID) {
1327
+ const legacyProtocolState = getLegacyProtocolStatePDA(ccmMint, programId);
1328
+ const disc = await anchorDisc('route_treasury');
1329
+ const data = Buffer.alloc(24);
1330
+ disc.copy(data, 0);
1331
+ data.writeBigUInt64LE(BigInt(amount), 8);
1332
+ data.writeBigUInt64LE(BigInt(minReserve), 16);
1333
+ return new TransactionInstruction({
1334
+ programId,
1335
+ keys: [
1336
+ { pubkey: admin, isSigner: true, isWritable: true },
1337
+ { pubkey: legacyProtocolState, isSigner: false, isWritable: false },
1338
+ { pubkey: ccmMint, isSigner: false, isWritable: false },
1339
+ { pubkey: treasuryAta, isSigner: false, isWritable: true },
1340
+ { pubkey: destinationAta, isSigner: false, isWritable: true },
1341
+ { pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
1342
+ ],
1343
+ data,
1344
+ });
1345
+ }
1346
+ // ── 20. createWithdrawFeesFromMintIx ─────────────────────────────────
1347
+ /**
1348
+ * Build a `withdraw_fees_from_mint` TransactionInstruction.
1349
+ *
1350
+ * Permissionless. Moves accumulated withheld fees from the mint itself to treasury.
1351
+ *
1352
+ * Accounts (order must match WithdrawFeesFromMint struct in governance.rs):
1353
+ * 0. authority (signer, writable)
1354
+ * 1. protocol_state (readonly) — legacy PDA (seeds = ["protocol", mint])
1355
+ * 2. mint (writable)
1356
+ * 3. treasury_ata (writable)
1357
+ * 4. token_program (readonly) — Token-2022
1358
+ *
1359
+ * No args beyond disc.
1360
+ */
1361
+ export async function createWithdrawFeesFromMintIx(authority, ccmMint, treasuryAta, programId = PROGRAM_ID) {
1362
+ const legacyProtocolState = getLegacyProtocolStatePDA(ccmMint, programId);
1363
+ const disc = await anchorDisc('withdraw_fees_from_mint');
1364
+ const data = Buffer.alloc(8);
1365
+ disc.copy(data, 0);
1366
+ return new TransactionInstruction({
1367
+ programId,
1368
+ keys: [
1369
+ { pubkey: authority, isSigner: true, isWritable: true },
1370
+ { pubkey: legacyProtocolState, isSigner: false, isWritable: false },
1371
+ { pubkey: ccmMint, isSigner: false, isWritable: true },
1372
+ { pubkey: treasuryAta, isSigner: false, isWritable: true },
1373
+ { pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
1374
+ ],
1375
+ data,
1376
+ });
1377
+ }
1378
+ // ── 21. createReallocLegacyProtocolIx ────────────────────────────────
1379
+ /**
1380
+ * Build a `realloc_legacy_protocol` TransactionInstruction.
1381
+ *
1382
+ * One-shot migration: extends the legacy 141-byte ProtocolState PDA
1383
+ * (seeds = ["protocol", mint]) to 173 bytes and inserts oracle_authority.
1384
+ *
1385
+ * Accounts (order must match ReallocLegacyProtocol struct in governance.rs):
1386
+ * 0. admin (signer, writable)
1387
+ * 1. live_protocol_state (readonly) — current ProtocolState (seeds = ["protocol_state"])
1388
+ * 2. legacy_protocol_state (writable) — legacy PDA (seeds = ["protocol", mint])
1389
+ * 3. mint (readonly)
1390
+ * 4. system_program (readonly)
1391
+ *
1392
+ * No args beyond disc.
1393
+ */
1394
+ export async function createReallocLegacyProtocolIx(admin, ccmMint, programId = PROGRAM_ID) {
1395
+ const liveProtocolState = getProtocolStatePDA(programId);
1396
+ const legacyProtocolState = getLegacyProtocolStatePDA(ccmMint, programId);
1397
+ const disc = await anchorDisc('realloc_legacy_protocol');
1398
+ const data = Buffer.alloc(8);
1399
+ disc.copy(data, 0);
1400
+ return new TransactionInstruction({
1401
+ programId,
1402
+ keys: [
1403
+ { pubkey: admin, isSigner: true, isWritable: true },
1404
+ { pubkey: liveProtocolState, isSigner: false, isWritable: false },
1405
+ { pubkey: legacyProtocolState, isSigner: false, isWritable: true },
1406
+ { pubkey: ccmMint, isSigner: false, isWritable: false },
1407
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
1408
+ ],
1409
+ data,
1410
+ });
1411
+ }
1412
+ // ── 22. createReallocMarketVaultIx ───────────────────────────────────
1413
+ /**
1414
+ * Build a `realloc_market_vault` TransactionInstruction.
1415
+ *
1416
+ * Grows existing MarketVault PDA from 137 to 153 bytes (Phase 2 NAV fields).
1417
+ * Admin-only. No-op if already at target size.
1418
+ *
1419
+ * Accounts (order must match ReallocMarketVault struct in vault.rs):
1420
+ * 0. payer (signer, writable)
1421
+ * 1. protocol_state (readonly)
1422
+ * 2. market_vault (writable) — UncheckedAccount (may be undersized)
1423
+ * 3. system_program (readonly)
1424
+ *
1425
+ * Args: market_id (u64)
1426
+ */
1427
+ export async function createReallocMarketVaultIx(payer, marketId, programId = PROGRAM_ID) {
1428
+ const protocolState = getProtocolStatePDA(programId);
1429
+ const marketVault = getMarketVaultPDA(protocolState, marketId, programId);
1430
+ const disc = await anchorDisc('realloc_market_vault');
1431
+ const data = Buffer.alloc(16);
1432
+ disc.copy(data, 0);
1433
+ data.writeBigUInt64LE(BigInt(marketId), 8);
1434
+ return new TransactionInstruction({
1435
+ programId,
1436
+ keys: [
1437
+ { pubkey: payer, isSigner: true, isWritable: true },
1438
+ { pubkey: protocolState, isSigner: false, isWritable: false },
1439
+ { pubkey: marketVault, isSigner: false, isWritable: true },
1440
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
1441
+ ],
1442
+ data,
1443
+ });
1444
+ }
1445
+ // ── 23. createResolveMarketIx ────────────────────────────────────────
1446
+ /**
1447
+ * Build a `resolve_market` TransactionInstruction (prediction markets).
1448
+ *
1449
+ * Accounts (order must match ResolveMarket struct in markets.rs):
1450
+ * 0. resolver (signer)
1451
+ * 1. protocol_state (readonly)
1452
+ * 2. global_root_config (readonly)
1453
+ * 3. market_state (writable)
1454
+ *
1455
+ * Args: cumulative_total (u64), proof (Vec<[u8; 32]>)
1456
+ */
1457
+ export async function createResolveMarketIx(resolver, ccmMint, marketId, cumulativeTotal, proofHex, programId = PROGRAM_ID) {
1458
+ const protocolState = getProtocolStatePDA(programId);
1459
+ const globalRootConfig = getGlobalRootConfigPDA(ccmMint, programId);
1460
+ const marketState = getMarketStatePDA(ccmMint, marketId, programId);
1461
+ const proofBytes = proofHex.map((h) => Buffer.from(h, 'hex'));
1462
+ // Data: [8 disc][8 cumulative_total][4 proof_len][N * 32 proof]
1463
+ const disc = await anchorDisc('resolve_market');
1464
+ const dataLen = 8 + 8 + 4 + proofBytes.length * 32;
1465
+ const data = Buffer.alloc(dataLen);
1466
+ let offset = 0;
1467
+ disc.copy(data, offset);
1468
+ offset += 8;
1469
+ data.writeBigUInt64LE(BigInt(cumulativeTotal), offset);
1470
+ offset += 8;
1471
+ data.writeUInt32LE(proofBytes.length, offset);
1472
+ offset += 4;
1473
+ for (const node of proofBytes) {
1474
+ node.copy(data, offset);
1475
+ offset += 32;
1476
+ }
1477
+ return new TransactionInstruction({
1478
+ programId,
1479
+ keys: [
1480
+ { pubkey: resolver, isSigner: true, isWritable: false },
1481
+ { pubkey: protocolState, isSigner: false, isWritable: false },
1482
+ { pubkey: globalRootConfig, isSigner: false, isWritable: false },
1483
+ { pubkey: marketState, isSigner: false, isWritable: true },
1484
+ ],
1485
+ data,
1486
+ });
1487
+ }
1488
+ // ── 24. createSetPausedOpenIx ────────────────────────────────────────
1489
+ /**
1490
+ * Build a `set_paused_open` TransactionInstruction.
1491
+ *
1492
+ * Emergency pause/unpause toggle. Admin-only.
1493
+ *
1494
+ * Accounts (order must match SetPausedOpen struct in admin.rs):
1495
+ * 0. admin (signer, writable)
1496
+ * 1. protocol_state (writable)
1497
+ *
1498
+ * Args: paused (bool, serialized as u8: 0 or 1)
1499
+ */
1500
+ export async function createSetPausedOpenIx(admin, paused, programId = PROGRAM_ID) {
1501
+ const protocolState = getProtocolStatePDA(programId);
1502
+ const disc = await anchorDisc('set_paused_open');
1503
+ const data = Buffer.alloc(9);
1504
+ disc.copy(data, 0);
1505
+ data.writeUInt8(paused ? 1 : 0, 8);
1506
+ return new TransactionInstruction({
1507
+ programId,
1508
+ keys: [
1509
+ { pubkey: admin, isSigner: true, isWritable: true },
1510
+ { pubkey: protocolState, isSigner: false, isWritable: true },
1511
+ ],
1512
+ data,
1513
+ });
1514
+ }
1515
+ // ── 25. createInitializeGlobalRootIx ─────────────────────────────────
1516
+ /**
1517
+ * Build an `initialize_global_root` TransactionInstruction.
1518
+ *
1519
+ * Creates the singleton GlobalRootConfig PDA for merkle root publishing.
1520
+ *
1521
+ * Accounts (order must match InitializeGlobalRoot struct in global.rs):
1522
+ * 0. payer (signer, writable)
1523
+ * 1. protocol_state (readonly)
1524
+ * 2. global_root_config (writable, init)
1525
+ * 3. system_program (readonly)
1526
+ *
1527
+ * No args beyond disc.
1528
+ */
1529
+ export async function createInitializeGlobalRootIx(payer, ccmMint, programId = PROGRAM_ID) {
1530
+ const protocolState = getProtocolStatePDA(programId);
1531
+ const globalRootConfig = getGlobalRootConfigPDA(ccmMint, programId);
1532
+ const disc = await anchorDisc('initialize_global_root');
1533
+ const data = Buffer.alloc(8);
1534
+ disc.copy(data, 0);
1535
+ return new TransactionInstruction({
1536
+ programId,
1537
+ keys: [
1538
+ { pubkey: payer, isSigner: true, isWritable: true },
1539
+ { pubkey: protocolState, isSigner: false, isWritable: false },
1540
+ { pubkey: globalRootConfig, isSigner: false, isWritable: true },
1541
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
1542
+ ],
1543
+ data,
1544
+ });
1545
+ }
1546
+ // ── 26. createSetPriceUpdaterIx ──────────────────────────────────────
1547
+ /**
1548
+ * Build a `set_price_updater` TransactionInstruction.
1549
+ *
1550
+ * Authority rotates the cranker key for a price feed.
1551
+ *
1552
+ * Accounts (order must match SetPriceUpdater struct in price_feed.rs):
1553
+ * 0. authority (signer)
1554
+ * 1. price_feed (writable)
1555
+ *
1556
+ * Args: label ([u8; 32]), new_updater (Pubkey)
1557
+ */
1558
+ export async function createSetPriceUpdaterIx(authority,
1559
+ /** 32-byte label (zero-padded). Use `Buffer.alloc(32)` and write your label. */
1560
+ label, newUpdater, programId = PROGRAM_ID) {
1561
+ const priceFeed = getPriceFeedPDA(label, programId);
1562
+ // Data: [8 disc][32 label][32 new_updater]
1563
+ const disc = await anchorDisc('set_price_updater');
1564
+ const data = Buffer.alloc(8 + 32 + 32);
1565
+ disc.copy(data, 0);
1566
+ label.copy(data, 8);
1567
+ newUpdater.toBuffer().copy(data, 40);
1568
+ return new TransactionInstruction({
1569
+ programId,
1570
+ keys: [
1571
+ { pubkey: authority, isSigner: true, isWritable: false },
1572
+ { pubkey: priceFeed, isSigner: false, isWritable: true },
1573
+ ],
1574
+ data,
1575
+ });
1576
+ }