@lombard.finance/sdk-solana 1.2.1 → 1.2.2

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.
@@ -1,5 +1,9 @@
1
- import { Program } from '@coral-xyz/anchor';
1
+ import { BN, Program } from '@coral-xyz/anchor';
2
2
  import { Env } from '@lombard.finance/sdk-common';
3
+ import {
4
+ ASSOCIATED_TOKEN_PROGRAM_ID,
5
+ getAssociatedTokenAddress,
6
+ } from '@solana/spl-token';
3
7
  import { Connection, PublicKey, SystemProgram } from '@solana/web3.js';
4
8
  import { keccak256 } from 'js-sha3';
5
9
  import { sha256 } from 'js-sha256';
@@ -9,6 +13,11 @@ import { ISolanaWalletProvider, SolanaNetwork } from '../../types';
9
13
  import { sendAndConfirmTransaction } from '../../utils';
10
14
  import { parseSignaturesFromProof } from '../claimLBTC/utils/signatureUtils';
11
15
 
16
+ // ── PDA seeds ──
17
+
18
+ export const CONSORTIUM_SESSION_SEED = Buffer.from('session');
19
+ export const CONSORTIUM_CONFIG_SEED = Buffer.from('consortium_config');
20
+
12
21
  // ── Payload selectors (first 4 bytes) ──
13
22
 
14
23
  export const DEPOSIT_SELECTOR_V1 = Buffer.from([0xce, 0x25, 0xe7, 0xc2]);
@@ -97,6 +106,68 @@ export interface ClaimContext {
97
106
  debugLog: DebugLog;
98
107
  }
99
108
 
109
+ // ── Consortium PDA helpers ──
110
+
111
+ export function getConsortiumConfigPDA(programId: PublicKey): PublicKey {
112
+ return PublicKey.findProgramAddressSync(
113
+ [CONSORTIUM_CONFIG_SEED],
114
+ programId,
115
+ )[0];
116
+ }
117
+
118
+ /**
119
+ * Derive the session PDA using the new seeds that include the ValSet epoch.
120
+ * Seeds: ["session", epoch (8 bytes BE), payer, payloadHash]
121
+ */
122
+ export function getConsortiumSessionPDA(
123
+ programId: PublicKey,
124
+ payer: PublicKey,
125
+ payloadHash: Buffer,
126
+ epoch: BN,
127
+ ): PublicKey {
128
+ return PublicKey.findProgramAddressSync(
129
+ [
130
+ CONSORTIUM_SESSION_SEED,
131
+ epoch.toBuffer('be', 8),
132
+ payer.toBytes(),
133
+ payloadHash,
134
+ ],
135
+ programId,
136
+ )[0];
137
+ }
138
+
139
+ /**
140
+ * Fetch the current epoch from the on-chain consortium config account.
141
+ *
142
+ * Borsh layout of Consortium Config:
143
+ * discriminator: 8 bytes (offset 0)
144
+ * admin: 32 bytes (offset 8)
145
+ * pending_admin: 32 bytes (offset 40)
146
+ * current_epoch: 8 bytes (offset 72, u64 LE)
147
+ */
148
+ export async function fetchCurrentEpoch(
149
+ connection: Connection,
150
+ consortiumConfigPDA: PublicKey,
151
+ ): Promise<BN> {
152
+ const accountInfo = await connection.getAccountInfo(consortiumConfigPDA);
153
+ if (!accountInfo) {
154
+ throw new Error(
155
+ `Consortium config account not found at ${consortiumConfigPDA.toBase58()}`,
156
+ );
157
+ }
158
+
159
+ const EPOCH_OFFSET = 72; // 8 (discriminator) + 32 (admin) + 32 (pending_admin)
160
+ const MIN_SIZE = EPOCH_OFFSET + 8;
161
+ if (accountInfo.data.length < MIN_SIZE) {
162
+ throw new Error(
163
+ `Consortium config data too short: expected >= ${MIN_SIZE} bytes, got ${accountInfo.data.length}`,
164
+ );
165
+ }
166
+
167
+ const epochLe = accountInfo.data.readBigUInt64LE(EPOCH_OFFSET);
168
+ return new BN(epochLe.toString());
169
+ }
170
+
100
171
  // ── parseAssetRouterConfig ──
101
172
 
102
173
  /**
@@ -277,11 +348,48 @@ export function computePayloadHash(payloadBytes: Buffer): Buffer {
277
348
  );
278
349
  }
279
350
 
351
+ /**
352
+ * BTC.B deposit payload embeds the SPL associated token account at bytes 36–68
353
+ * (not the wallet owner). On-chain `mint_from_payload` requires that account
354
+ * pubkey to match the payload exactly.
355
+ *
356
+ * @returns The recipient token account from the payload (verified against the wallet's ATA).
357
+ */
358
+ export async function assertBtcbDepositRecipientMatchesWallet({
359
+ payloadBytes,
360
+ mint,
361
+ tokenProgramId,
362
+ recipientWallet,
363
+ }: {
364
+ payloadBytes: Buffer;
365
+ mint: PublicKey;
366
+ tokenProgramId: PublicKey;
367
+ recipientWallet: string;
368
+ }): Promise<PublicKey> {
369
+ const payloadRecipient = new PublicKey(payloadBytes.subarray(36, 68));
370
+ const expectedAta = await getAssociatedTokenAddress(
371
+ mint,
372
+ new PublicKey(recipientWallet),
373
+ true,
374
+ tokenProgramId,
375
+ ASSOCIATED_TOKEN_PROGRAM_ID,
376
+ );
377
+
378
+ if (!expectedAta.equals(payloadRecipient)) {
379
+ throw new Error(
380
+ `Recipient mismatch: payload expects token account ${payloadRecipient.toBase58()} ` +
381
+ `but ATA for wallet ${recipientWallet} is ${expectedAta.toBase58()}.`,
382
+ );
383
+ }
384
+
385
+ return payloadRecipient;
386
+ }
387
+
280
388
  /**
281
389
  * Compute bascule deposit ID from raw 196-byte BTC.B payload.
282
390
  *
283
- * keccak256( [0u8;32] || "\x03SOL" || recipient(32) || amount(8) || txid(32) || vout(4) )
284
- * Payload offsets: recipient 36-68, amount 68-76, txid 76-108, vout 108-112.
391
+ * keccak256( [0u8;32] || "\x03SOL" || recipient_token_account(32) || amount(8) || txid(32) || vout(4) )
392
+ * Payload offsets: recipient ATA 36-68, amount 68-76, txid 76-108, vout 108-112.
285
393
  */
286
394
  export function computeDepositIdFromPayload(payloadBytes: Buffer): Uint8Array {
287
395
  const prefix = Buffer.alloc(32, 0);