@lombard.finance/sdk-solana 2.0.1 → 2.0.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.
- package/dist/index.cjs +1 -1
- package/dist/index.js +23 -22
- package/dist/index2.cjs +47 -47
- package/dist/index2.js +6103 -6029
- package/dist/sendBridgeTransaction.cjs +1 -1
- package/dist/sendBridgeTransaction.js +19 -19
- package/package.json +1 -1
- package/src/bridge/sendBridgeTransaction.ts +2 -2
- package/src/const/__tests__/rpcUrls.test.ts +73 -0
- package/src/const/errors.ts +0 -1
- package/src/const/getConfig.ts +30 -15
- package/src/const/rpcUrls.ts +53 -7
- package/src/idl/asset_router.json +182 -1532
- package/src/idl/bridge.json +139 -1185
- package/src/idl/consortium.json +62 -498
- package/src/idl/getAssetRouterIdl.ts +1 -3
- package/src/idl/getConsortiumIdl.ts +1 -3
- package/src/idl/getLbtcIdl.ts +4 -1
- package/src/idl/getMailboxIdl.ts +1 -3
- package/src/idl/lombard_token_pool.json +92 -932
- package/src/idl/mailbox.json +114 -1018
- package/src/idl/ratio_oracle.json +35 -332
- package/src/services/SolanaServiceImpl.test.ts +5 -4
- package/src/stories/components/ConnectButton/ConnectButton.tsx +2 -2
- package/src/stories/components/NetworkSelector/NetworkSelector.tsx +1 -1
- package/src/stories/components/OutputSelector/OutputSelector.tsx +4 -4
- package/src/stories/components/SelectField/SelectField.tsx +2 -3
- package/src/stories/hooks/useFetchOutputs.ts +1 -1
- package/src/stories/utils/fromCamelCase.ts +1 -1
- package/src/types/sdkTypes.ts +7 -0
- package/src/utils/createDebugLogger.ts +1 -1
- package/src/web3Sdk/claimToken/claimBtcb.ts +19 -5
- package/src/web3Sdk/claimToken/claimLbtcGmp.ts +55 -25
- package/src/web3Sdk/claimToken/claimToken.stories.tsx +4 -3
- package/src/web3Sdk/claimToken/claimToken.ts +16 -6
- package/src/web3Sdk/claimToken/shared.ts +18 -12
- package/src/web3Sdk/claimToken/utils/__tests__/signatureUtils.test.ts +10 -4
- package/src/web3Sdk/claimToken/utils/signatureUtils.ts +3 -1
- package/src/web3Sdk/deposit/deposit.stories.tsx +1 -4
- package/src/web3Sdk/deposit/deposit.test.ts +67 -37
- package/src/web3Sdk/deposit/deposit.ts +14 -14
- package/src/web3Sdk/detectWallet/detectWallet.test.ts +2 -2
- package/src/web3Sdk/getBalance/getBalance.test.ts +1 -1
- package/src/web3Sdk/getBalance/getBalance.ts +2 -1
- package/src/web3Sdk/getTokenFeeConfig/getTokenFeeConfig.stories.tsx +8 -18
- package/src/web3Sdk/getTokenFeeConfig/getTokenFeeConfig.ts +2 -4
- package/src/web3Sdk/redeem/redeem.stories.tsx +17 -8
- package/src/web3Sdk/redeem/redeem.test.ts +45 -13
- package/src/web3Sdk/redeem/redeem.ts +46 -20
- package/src/web3Sdk/redeemToken/redeemBtcb.ts +28 -12
- package/src/web3Sdk/redeemToken/redeemForBtc.stories.tsx +6 -6
- package/src/web3Sdk/redeemToken/redeemForBtc.test.ts +43 -13
- package/src/web3Sdk/redeemToken/redeemForBtc.ts +27 -14
- package/src/web3Sdk/redeemToken/redeemLbtc.ts +28 -12
- package/src/web3Sdk/redeemToken/shared.test.ts +6 -2
- package/src/web3Sdk/redeemToken/shared.ts +1 -3
|
@@ -15,10 +15,7 @@ import { ClaimContext, executeConsortiumSession } from './shared';
|
|
|
15
15
|
* Payload: nonce u256 at [36:68], token at [232:264], recipient at [264:296], amount u256 at [296:328].
|
|
16
16
|
* Only the low 8 bytes of each u256 are used (placed in the last 8 bytes of the 32-byte slot).
|
|
17
17
|
*/
|
|
18
|
-
function computeBasculeGmpMintId(
|
|
19
|
-
payload: Buffer,
|
|
20
|
-
chainId: Buffer,
|
|
21
|
-
): Uint8Array {
|
|
18
|
+
function computeBasculeGmpMintId(payload: Buffer, chainId: Buffer): Uint8Array {
|
|
22
19
|
if (payload.length < 328) {
|
|
23
20
|
throw new Error(
|
|
24
21
|
`payload too short for bascule_gmp mint_id: ${payload.length} bytes`,
|
|
@@ -49,11 +46,22 @@ function computeBasculeGmpMintId(
|
|
|
49
46
|
*/
|
|
50
47
|
export async function claimLbtcGmp(ctx: ClaimContext): Promise<string> {
|
|
51
48
|
const {
|
|
52
|
-
provider,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
provider,
|
|
50
|
+
connection,
|
|
51
|
+
params,
|
|
52
|
+
config,
|
|
53
|
+
payloadBytes,
|
|
54
|
+
payloadHash,
|
|
55
|
+
payloadHashArray,
|
|
56
|
+
payer,
|
|
57
|
+
assetRouterProgramId,
|
|
58
|
+
consortiumProgramId,
|
|
59
|
+
consortiumProgram,
|
|
60
|
+
assetRouterConfigPDA,
|
|
61
|
+
tokenAuthorityPDA,
|
|
62
|
+
validatedPayloadPDA,
|
|
63
|
+
arConfig,
|
|
64
|
+
debugLog,
|
|
57
65
|
} = ctx;
|
|
58
66
|
|
|
59
67
|
if (payloadBytes.length < 328) {
|
|
@@ -67,7 +75,8 @@ export async function claimLbtcGmp(ctx: ClaimContext): Promise<string> {
|
|
|
67
75
|
[Buffer.from('message_handled'), payloadHash],
|
|
68
76
|
assetRouterProgramId,
|
|
69
77
|
);
|
|
70
|
-
const messageHandledAccount =
|
|
78
|
+
const messageHandledAccount =
|
|
79
|
+
await connection.getAccountInfo(messageHandledPDA);
|
|
71
80
|
if (messageHandledAccount) {
|
|
72
81
|
debugLog('Message already handled (LBTC already minted)');
|
|
73
82
|
return ALREADY_MINTED_TX_HASH;
|
|
@@ -82,7 +91,8 @@ export async function claimLbtcGmp(ctx: ClaimContext): Promise<string> {
|
|
|
82
91
|
consortiumProgramId,
|
|
83
92
|
);
|
|
84
93
|
|
|
85
|
-
const sessionPayloadAccount =
|
|
94
|
+
const sessionPayloadAccount =
|
|
95
|
+
await connection.getAccountInfo(sessionPayloadPDA);
|
|
86
96
|
if (sessionPayloadAccount) {
|
|
87
97
|
debugLog('Session payload already exists, skipping post_session_payload');
|
|
88
98
|
} else {
|
|
@@ -112,7 +122,9 @@ export async function claimLbtcGmp(ctx: ClaimContext): Promise<string> {
|
|
|
112
122
|
|
|
113
123
|
// ── Step 5: deliver_message (mailbox) ──
|
|
114
124
|
if (!config.mailbox) {
|
|
115
|
-
throw new Error(
|
|
125
|
+
throw new Error(
|
|
126
|
+
`Mailbox program not configured for network: ${params.network}`,
|
|
127
|
+
);
|
|
116
128
|
}
|
|
117
129
|
const mailboxProgramId = new PublicKey(config.mailbox);
|
|
118
130
|
const mailboxProgram = new Program(
|
|
@@ -130,7 +142,9 @@ export async function claimLbtcGmp(ctx: ClaimContext): Promise<string> {
|
|
|
130
142
|
);
|
|
131
143
|
|
|
132
144
|
if (!config.ledgerChainId) {
|
|
133
|
-
throw new Error(
|
|
145
|
+
throw new Error(
|
|
146
|
+
`Ledger chain ID not configured for network: ${params.network}`,
|
|
147
|
+
);
|
|
134
148
|
}
|
|
135
149
|
const ledgerChainId = Buffer.from(config.ledgerChainId, 'hex');
|
|
136
150
|
debugLog('Ledger chain ID:', config.ledgerChainId);
|
|
@@ -188,7 +202,12 @@ export async function claimLbtcGmp(ctx: ClaimContext): Promise<string> {
|
|
|
188
202
|
}
|
|
189
203
|
const tokenProgramId = mintAccountInfo.owner;
|
|
190
204
|
|
|
191
|
-
const mintAccount = await getMint(
|
|
205
|
+
const mintAccount = await getMint(
|
|
206
|
+
connection,
|
|
207
|
+
mint,
|
|
208
|
+
undefined,
|
|
209
|
+
tokenProgramId,
|
|
210
|
+
);
|
|
192
211
|
if (!mintAccount.mintAuthority) {
|
|
193
212
|
throw new Error('Mint has no mint authority');
|
|
194
213
|
}
|
|
@@ -206,7 +225,7 @@ export async function claimLbtcGmp(ctx: ClaimContext): Promise<string> {
|
|
|
206
225
|
// Build handle_message instruction
|
|
207
226
|
const handleIx = await mailboxProgram.methods
|
|
208
227
|
.handleMessage(payloadHashArray)
|
|
209
|
-
|
|
228
|
+
.accounts({
|
|
210
229
|
handler: provider.publicKey,
|
|
211
230
|
config: mailboxConfigPDA,
|
|
212
231
|
messageInfo: messageInfoPDA,
|
|
@@ -262,12 +281,22 @@ export async function claimLbtcGmp(ctx: ClaimContext): Promise<string> {
|
|
|
262
281
|
debugLog('Bascule GMP program:', effectiveBasculeGmpProgramId.toBase58());
|
|
263
282
|
debugLog('Bascule validator PDA:', basculeValidatorPDA.toBase58());
|
|
264
283
|
debugLog('Bascule GMP config PDA:', basculeGmpConfigPDA.toBase58());
|
|
265
|
-
debugLog(
|
|
266
|
-
|
|
284
|
+
debugLog(
|
|
285
|
+
'Bascule GMP account roles PDA:',
|
|
286
|
+
basculeGmpAccountRolesPDA.toBase58(),
|
|
287
|
+
);
|
|
288
|
+
debugLog(
|
|
289
|
+
'Bascule GMP mint payload PDA:',
|
|
290
|
+
basculeGmpMintPayloadPDA.toBase58(),
|
|
291
|
+
);
|
|
267
292
|
|
|
268
293
|
handleIx.keys.push(
|
|
269
294
|
{ pubkey: basculeValidatorPDA, isSigner: false, isWritable: true },
|
|
270
|
-
{
|
|
295
|
+
{
|
|
296
|
+
pubkey: effectiveBasculeGmpProgramId,
|
|
297
|
+
isSigner: false,
|
|
298
|
+
isWritable: false,
|
|
299
|
+
},
|
|
271
300
|
{ pubkey: basculeGmpConfigPDA, isSigner: false, isWritable: false },
|
|
272
301
|
{ pubkey: basculeGmpAccountRolesPDA, isSigner: false, isWritable: false },
|
|
273
302
|
{ pubkey: basculeGmpMintPayloadPDA, isSigner: false, isWritable: true },
|
|
@@ -275,18 +304,19 @@ export async function claimLbtcGmp(ctx: ClaimContext): Promise<string> {
|
|
|
275
304
|
} else {
|
|
276
305
|
debugLog('Bascule GMP not enabled, adding placeholder accounts');
|
|
277
306
|
for (let i = 0; i < 5; i++) {
|
|
278
|
-
handleIx.keys.push(
|
|
279
|
-
|
|
280
|
-
|
|
307
|
+
handleIx.keys.push({
|
|
308
|
+
pubkey: assetRouterProgramId,
|
|
309
|
+
isSigner: false,
|
|
310
|
+
isWritable: false,
|
|
311
|
+
});
|
|
281
312
|
}
|
|
282
313
|
}
|
|
283
314
|
|
|
284
315
|
debugLog('handle_message account count:', handleIx.keys.length);
|
|
316
|
+
debugLog('handle_message program:', handleIx.programId.toBase58());
|
|
285
317
|
debugLog(
|
|
286
|
-
'handle_message
|
|
287
|
-
|
|
288
|
-
debugLog(
|
|
289
|
-
'handle_message data (hex):', Buffer.from(handleIx.data).toString('hex'),
|
|
318
|
+
'handle_message data (hex):',
|
|
319
|
+
Buffer.from(handleIx.data).toString('hex'),
|
|
290
320
|
);
|
|
291
321
|
handleIx.keys.forEach((k, i) => {
|
|
292
322
|
debugLog(
|
|
@@ -60,10 +60,11 @@ export const StoryView = ({ environment, token }: ClaimTokenStoryArgs) => {
|
|
|
60
60
|
if (!selectedOutput) throw new Error('Please select an output to claim.');
|
|
61
61
|
if (!selectedOutput.raw_payload)
|
|
62
62
|
throw new Error('Selected output has no raw_payload.');
|
|
63
|
-
if (!selectedOutput.proof)
|
|
64
|
-
throw new Error('Selected output has no proof.');
|
|
63
|
+
if (!selectedOutput.proof) throw new Error('Selected output has no proof.');
|
|
65
64
|
if (!tokenMint)
|
|
66
|
-
throw new Error(
|
|
65
|
+
throw new Error(
|
|
66
|
+
`Token mint not configured for ${token} on ${environment}.`,
|
|
67
|
+
);
|
|
67
68
|
|
|
68
69
|
setTransactionLogs(null);
|
|
69
70
|
try {
|
|
@@ -34,7 +34,13 @@ export async function claimToken(
|
|
|
34
34
|
provider: ISolanaWalletProvider,
|
|
35
35
|
params: ClaimTokenParams,
|
|
36
36
|
): Promise<string> {
|
|
37
|
-
const {
|
|
37
|
+
const {
|
|
38
|
+
network,
|
|
39
|
+
env = DEFAULT_ENV,
|
|
40
|
+
rawPayload,
|
|
41
|
+
rpcUrl,
|
|
42
|
+
debug = false,
|
|
43
|
+
} = params;
|
|
38
44
|
const { debugLog, printLogs } = createDebugLogger({ debug });
|
|
39
45
|
|
|
40
46
|
try {
|
|
@@ -51,7 +57,7 @@ export async function claimToken(
|
|
|
51
57
|
throw new Error(`Consortium not configured for network: ${network}`);
|
|
52
58
|
}
|
|
53
59
|
|
|
54
|
-
const connection = getConnection(network, rpcUrl);
|
|
60
|
+
const connection = getConnection(network, rpcUrl, env);
|
|
55
61
|
const wallet = {
|
|
56
62
|
publicKey: new PublicKey(provider.publicKey),
|
|
57
63
|
signTransaction: provider.signTransaction,
|
|
@@ -127,10 +133,14 @@ export async function claimToken(
|
|
|
127
133
|
assetRouterConfigPDA,
|
|
128
134
|
);
|
|
129
135
|
debugLog(
|
|
130
|
-
'Asset Router config — paused:',
|
|
131
|
-
|
|
132
|
-
'
|
|
133
|
-
|
|
136
|
+
'Asset Router config — paused:',
|
|
137
|
+
arConfig.paused,
|
|
138
|
+
'nativeMint:',
|
|
139
|
+
arConfig.nativeMint.toBase58(),
|
|
140
|
+
'bascule:',
|
|
141
|
+
arConfig.basculeProgramId?.toBase58() ?? 'null',
|
|
142
|
+
'basculeGmp:',
|
|
143
|
+
arConfig.basculeGmpProgramId?.toBase58() ?? 'null',
|
|
134
144
|
);
|
|
135
145
|
|
|
136
146
|
if (arConfig.paused) {
|
|
@@ -201,11 +201,19 @@ export async function fetchAssetRouterConfig(
|
|
|
201
201
|
* Execute the consortium session flow: create_session, post_signatures, finalize_session.
|
|
202
202
|
* Skips steps that are already completed on-chain.
|
|
203
203
|
*/
|
|
204
|
-
export async function executeConsortiumSession(
|
|
204
|
+
export async function executeConsortiumSession(
|
|
205
|
+
ctx: ClaimContext,
|
|
206
|
+
): Promise<void> {
|
|
205
207
|
const {
|
|
206
|
-
provider,
|
|
207
|
-
|
|
208
|
-
|
|
208
|
+
provider,
|
|
209
|
+
connection,
|
|
210
|
+
consortiumProgram,
|
|
211
|
+
consortiumConfigPDA,
|
|
212
|
+
sessionPDA,
|
|
213
|
+
validatedPayloadPDA,
|
|
214
|
+
payloadHashArray,
|
|
215
|
+
params,
|
|
216
|
+
debugLog,
|
|
209
217
|
} = ctx;
|
|
210
218
|
|
|
211
219
|
const validatedPayloadAccount =
|
|
@@ -294,14 +302,15 @@ export async function executeConsortiumSession(ctx: ClaimContext): Promise<void>
|
|
|
294
302
|
|
|
295
303
|
if (!sessionSigned) {
|
|
296
304
|
debugLog('Step 2: post_session_signatures...');
|
|
297
|
-
const { signatures: parsedSigs, indices } =
|
|
298
|
-
|
|
305
|
+
const { signatures: parsedSigs, indices } = parseSignaturesFromProof(
|
|
306
|
+
params.proofSignature,
|
|
307
|
+
);
|
|
299
308
|
|
|
300
309
|
if (parsedSigs.length === 0 || indices.length === 0) {
|
|
301
310
|
throw new Error('No valid signatures found in the proof');
|
|
302
311
|
}
|
|
303
312
|
|
|
304
|
-
const signatures = parsedSigs.map(sig => Array.from(sig));
|
|
313
|
+
const signatures = parsedSigs.map((sig) => Array.from(sig));
|
|
305
314
|
|
|
306
315
|
const postSigsTx = await consortiumProgram.methods
|
|
307
316
|
.postSessionSignatures(payloadHashArray, signatures, indices)
|
|
@@ -328,7 +337,7 @@ export async function executeConsortiumSession(ctx: ClaimContext): Promise<void>
|
|
|
328
337
|
debugLog('Step 3: finalize_session...');
|
|
329
338
|
const finalizeSessionTx = await consortiumProgram.methods
|
|
330
339
|
.finalizeSession(payloadHashArray)
|
|
331
|
-
|
|
340
|
+
.accounts({
|
|
332
341
|
payer: provider.publicKey,
|
|
333
342
|
config: consortiumConfigPDA,
|
|
334
343
|
session: sessionPDA,
|
|
@@ -350,10 +359,7 @@ export async function executeConsortiumSession(ctx: ClaimContext): Promise<void>
|
|
|
350
359
|
// ── Helpers ──
|
|
351
360
|
|
|
352
361
|
export function computePayloadHash(payloadBytes: Buffer): Buffer {
|
|
353
|
-
return Buffer.from(
|
|
354
|
-
sha256(payloadBytes as unknown as Uint8Array),
|
|
355
|
-
'hex',
|
|
356
|
-
);
|
|
362
|
+
return Buffer.from(sha256(payloadBytes as unknown as Uint8Array), 'hex');
|
|
357
363
|
}
|
|
358
364
|
|
|
359
365
|
/**
|
|
@@ -25,10 +25,12 @@ describe('signatureUtils', () => {
|
|
|
25
25
|
expect(result.indices).toHaveLength(expectedSignatureCount);
|
|
26
26
|
|
|
27
27
|
// Check indices
|
|
28
|
-
expect(result.indices.map(bn => bn.toNumber())).toEqual(
|
|
28
|
+
expect(result.indices.map((bn) => bn.toNumber())).toEqual(
|
|
29
|
+
expectedIndices,
|
|
30
|
+
);
|
|
29
31
|
|
|
30
32
|
// Check each signature
|
|
31
|
-
result.signatures.forEach(sig => {
|
|
33
|
+
result.signatures.forEach((sig) => {
|
|
32
34
|
expect(sig).toBeInstanceOf(Uint8Array);
|
|
33
35
|
expect(sig.length).toBe(64);
|
|
34
36
|
});
|
|
@@ -54,7 +56,9 @@ describe('signatureUtils', () => {
|
|
|
54
56
|
const result = parseSignaturesFromProof(proof);
|
|
55
57
|
|
|
56
58
|
expect(result.signatures).toHaveLength(expectedSignatureCount);
|
|
57
|
-
expect(result.indices.map(bn => bn.toNumber())).toEqual(
|
|
59
|
+
expect(result.indices.map((bn) => bn.toNumber())).toEqual(
|
|
60
|
+
expectedIndices,
|
|
61
|
+
);
|
|
58
62
|
|
|
59
63
|
for (const sig of result.signatures) {
|
|
60
64
|
expect(sig).toBeInstanceOf(Uint8Array);
|
|
@@ -82,7 +86,9 @@ describe('signatureUtils', () => {
|
|
|
82
86
|
const result = parseSignaturesFromProof(proof);
|
|
83
87
|
|
|
84
88
|
expect(result.signatures).toHaveLength(expectedSignatureCount);
|
|
85
|
-
expect(result.indices.map(bn => bn.toNumber())).toEqual(
|
|
89
|
+
expect(result.indices.map((bn) => bn.toNumber())).toEqual(
|
|
90
|
+
expectedIndices,
|
|
91
|
+
);
|
|
86
92
|
|
|
87
93
|
for (const sig of result.signatures) {
|
|
88
94
|
expect(sig).toBeInstanceOf(Uint8Array);
|
|
@@ -150,7 +150,9 @@ export const parseSignaturesFromProof = (
|
|
|
150
150
|
continue;
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
const isEmptyPlaceholder = signatureBytesCheck.every(
|
|
153
|
+
const isEmptyPlaceholder = signatureBytesCheck.every(
|
|
154
|
+
(byte) => byte === 0,
|
|
155
|
+
);
|
|
154
156
|
|
|
155
157
|
if (isEmptyPlaceholder) {
|
|
156
158
|
// Skip placeholders
|
|
@@ -159,10 +159,7 @@ export const StoryView = ({
|
|
|
159
159
|
/>
|
|
160
160
|
)}
|
|
161
161
|
{(error || connectError) && (
|
|
162
|
-
<ErrorDisplay
|
|
163
|
-
error={error || connectError}
|
|
164
|
-
title="Deposit Error"
|
|
165
|
-
/>
|
|
162
|
+
<ErrorDisplay error={error || connectError} title="Deposit Error" />
|
|
166
163
|
)}
|
|
167
164
|
|
|
168
165
|
{transactionLogs && transactionLogs.length > 0 && (
|
|
@@ -11,8 +11,10 @@ const MOCK_LBTC_MINT = 'LBTCojyVJ63rsEED2DLEGWMzSxWJyQynXE91LMLgV1J';
|
|
|
11
11
|
const MOCK_BTCB_MINT = 'BTCB3ripBAut19jM8kDPVbJHb2ZdR2GcZvGZkCmFPtV8';
|
|
12
12
|
const MOCK_ASSET_ROUTER = 'LomVyJDZ91jeVbNnTupJXKJTQFakJVMc87CmwDHYt95';
|
|
13
13
|
const MOCK_MAILBOX = 'LomJw912MoUd7iiAesTQAgz1paLcTqi6ndG3w3pnKH9';
|
|
14
|
-
const MOCK_SOLANA_CHAIN_ID =
|
|
15
|
-
|
|
14
|
+
const MOCK_SOLANA_CHAIN_ID =
|
|
15
|
+
'0259db5080fc2c6d3bcf7ca90712d3c2e5e6c28f27f0dfbb9953bdb0894c03ab';
|
|
16
|
+
const MOCK_LEDGER_CHAIN_ID =
|
|
17
|
+
'031f51c4e4cc1dae1c752d2f8fe2ae045da668a13f2e47a465964d630f5ed22e';
|
|
16
18
|
const MOCK_PAYER = '8yarEiDaJVikHZbk3PQSoWiDn2T3oM1FHZN1Jv4VZFdr';
|
|
17
19
|
const MOCK_RECIPIENT = 'DVMiNi7uxHEPABTBt1nLMoxnPniPKbLAFj4MPJq1RDjg';
|
|
18
20
|
/** Distinct pubkey returned for ATA(mint=LBTC, owner=recipient) */
|
|
@@ -26,7 +28,8 @@ const fullConfig: IConfig = {
|
|
|
26
28
|
assetRouter: MOCK_ASSET_ROUTER,
|
|
27
29
|
mailbox: MOCK_MAILBOX,
|
|
28
30
|
solanaRoutingChainId: MOCK_SOLANA_CHAIN_ID,
|
|
29
|
-
bitcoinRoutingChainId:
|
|
31
|
+
bitcoinRoutingChainId:
|
|
32
|
+
'ff000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6',
|
|
30
33
|
ledgerChainId: MOCK_LEDGER_CHAIN_ID,
|
|
31
34
|
lbtcProgramId: 'HEY7PCJe3GB27UWdopuYb1xDbB5SNtTcYPxRjntvfBSA',
|
|
32
35
|
treasuryAddress: 'ByHNGi4zPJw5StyWZoLQJ9n2wT12oupJF2pTSNKMnnAZ',
|
|
@@ -92,7 +95,11 @@ vi.mock('../../const/rpcUrls', () => ({
|
|
|
92
95
|
}));
|
|
93
96
|
|
|
94
97
|
vi.mock('../../utils/tokenAccount', () => ({
|
|
95
|
-
getTokenProgramForMint: vi
|
|
98
|
+
getTokenProgramForMint: vi
|
|
99
|
+
.fn()
|
|
100
|
+
.mockResolvedValue(
|
|
101
|
+
new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'),
|
|
102
|
+
),
|
|
96
103
|
}));
|
|
97
104
|
|
|
98
105
|
vi.mock('../../idl/getAssetRouterIdl', () => ({
|
|
@@ -112,7 +119,9 @@ vi.mock('@solana/spl-token', () => ({
|
|
|
112
119
|
}
|
|
113
120
|
return new PublicKey(MOCK_PAYER_ATA);
|
|
114
121
|
}),
|
|
115
|
-
ASSOCIATED_TOKEN_PROGRAM_ID: new PublicKey(
|
|
122
|
+
ASSOCIATED_TOKEN_PROGRAM_ID: new PublicKey(
|
|
123
|
+
'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL',
|
|
124
|
+
),
|
|
116
125
|
}));
|
|
117
126
|
|
|
118
127
|
const mockTx = { instructions: [{ keys: [] }] };
|
|
@@ -195,20 +204,23 @@ describe('deposit', () => {
|
|
|
195
204
|
|
|
196
205
|
it('should throw when Asset Router is not configured', async () => {
|
|
197
206
|
const { getConfig } = await import('../../const/getConfig');
|
|
198
|
-
vi.mocked(getConfig).mockReturnValueOnce({
|
|
207
|
+
vi.mocked(getConfig).mockReturnValueOnce({
|
|
208
|
+
...fullConfig,
|
|
209
|
+
assetRouter: null,
|
|
210
|
+
});
|
|
199
211
|
|
|
200
|
-
await expect(
|
|
201
|
-
|
|
202
|
-
)
|
|
212
|
+
await expect(depositFn(testWallet(MOCK_PAYER), baseParams)).rejects.toThrow(
|
|
213
|
+
'Asset Router not configured',
|
|
214
|
+
);
|
|
203
215
|
});
|
|
204
216
|
|
|
205
217
|
it('should throw when Mailbox is not configured', async () => {
|
|
206
218
|
const { getConfig } = await import('../../const/getConfig');
|
|
207
219
|
vi.mocked(getConfig).mockReturnValueOnce({ ...fullConfig, mailbox: null });
|
|
208
220
|
|
|
209
|
-
await expect(
|
|
210
|
-
|
|
211
|
-
)
|
|
221
|
+
await expect(depositFn(testWallet(MOCK_PAYER), baseParams)).rejects.toThrow(
|
|
222
|
+
'Mailbox not configured',
|
|
223
|
+
);
|
|
212
224
|
});
|
|
213
225
|
|
|
214
226
|
it('should throw when Solana routing chain ID is not configured', async () => {
|
|
@@ -218,27 +230,33 @@ describe('deposit', () => {
|
|
|
218
230
|
solanaRoutingChainId: null,
|
|
219
231
|
});
|
|
220
232
|
|
|
221
|
-
await expect(
|
|
222
|
-
|
|
223
|
-
)
|
|
233
|
+
await expect(depositFn(testWallet(MOCK_PAYER), baseParams)).rejects.toThrow(
|
|
234
|
+
'Solana routing chain ID not configured',
|
|
235
|
+
);
|
|
224
236
|
});
|
|
225
237
|
|
|
226
238
|
it('should throw when source token mint is not resolved', async () => {
|
|
227
239
|
const { getConfig } = await import('../../const/getConfig');
|
|
228
|
-
vi.mocked(getConfig).mockReturnValueOnce({
|
|
240
|
+
vi.mocked(getConfig).mockReturnValueOnce({
|
|
241
|
+
...fullConfig,
|
|
242
|
+
btcbTokenMint: '',
|
|
243
|
+
});
|
|
229
244
|
|
|
230
|
-
await expect(
|
|
231
|
-
|
|
232
|
-
)
|
|
245
|
+
await expect(depositFn(testWallet(MOCK_PAYER), baseParams)).rejects.toThrow(
|
|
246
|
+
'Source token mint not configured',
|
|
247
|
+
);
|
|
233
248
|
});
|
|
234
249
|
|
|
235
250
|
it('should throw when destination token is not configured', async () => {
|
|
236
251
|
const { getConfig } = await import('../../const/getConfig');
|
|
237
|
-
vi.mocked(getConfig).mockReturnValueOnce({
|
|
252
|
+
vi.mocked(getConfig).mockReturnValueOnce({
|
|
253
|
+
...fullConfig,
|
|
254
|
+
lbtcTokenMint: '',
|
|
255
|
+
});
|
|
238
256
|
|
|
239
|
-
await expect(
|
|
240
|
-
|
|
241
|
-
)
|
|
257
|
+
await expect(depositFn(testWallet(MOCK_PAYER), baseParams)).rejects.toThrow(
|
|
258
|
+
'Destination token not configured',
|
|
259
|
+
);
|
|
242
260
|
});
|
|
243
261
|
|
|
244
262
|
it('should throw when amount is zero', async () => {
|
|
@@ -255,9 +273,9 @@ describe('deposit', () => {
|
|
|
255
273
|
return Promise.resolve({ data: buildMailboxConfigData() });
|
|
256
274
|
});
|
|
257
275
|
|
|
258
|
-
await expect(
|
|
259
|
-
|
|
260
|
-
)
|
|
276
|
+
await expect(depositFn(testWallet(MOCK_PAYER), baseParams)).rejects.toThrow(
|
|
277
|
+
'Asset Router is paused',
|
|
278
|
+
);
|
|
261
279
|
});
|
|
262
280
|
|
|
263
281
|
it('should throw on insufficient balance', async () => {
|
|
@@ -265,22 +283,27 @@ describe('deposit', () => {
|
|
|
265
283
|
value: { amount: '10', uiAmountString: '0.0000001' },
|
|
266
284
|
});
|
|
267
285
|
|
|
268
|
-
await expect(
|
|
269
|
-
|
|
270
|
-
)
|
|
286
|
+
await expect(depositFn(testWallet(MOCK_PAYER), baseParams)).rejects.toThrow(
|
|
287
|
+
'Insufficient balance',
|
|
288
|
+
);
|
|
271
289
|
});
|
|
272
290
|
|
|
273
291
|
it('should throw when Ledger chain ID is not configured', async () => {
|
|
274
292
|
const { getConfig } = await import('../../const/getConfig');
|
|
275
|
-
vi.mocked(getConfig).mockReturnValueOnce({
|
|
293
|
+
vi.mocked(getConfig).mockReturnValueOnce({
|
|
294
|
+
...fullConfig,
|
|
295
|
+
ledgerChainId: null,
|
|
296
|
+
});
|
|
276
297
|
|
|
277
|
-
await expect(
|
|
278
|
-
|
|
279
|
-
)
|
|
298
|
+
await expect(depositFn(testWallet(MOCK_PAYER), baseParams)).rejects.toThrow(
|
|
299
|
+
'Ledger chain ID not configured',
|
|
300
|
+
);
|
|
280
301
|
});
|
|
281
302
|
|
|
282
303
|
it('should return transaction signature on success', async () => {
|
|
283
|
-
mockConnection.getAccountInfo.mockResolvedValue({
|
|
304
|
+
mockConnection.getAccountInfo.mockResolvedValue({
|
|
305
|
+
data: buildMailboxConfigData(),
|
|
306
|
+
});
|
|
284
307
|
|
|
285
308
|
const sig = await depositFn(testWallet(MOCK_PAYER), baseParams);
|
|
286
309
|
|
|
@@ -288,7 +311,9 @@ describe('deposit', () => {
|
|
|
288
311
|
});
|
|
289
312
|
|
|
290
313
|
it('should pass destination associated token account bytes to deposit()', async () => {
|
|
291
|
-
mockConnection.getAccountInfo.mockResolvedValue({
|
|
314
|
+
mockConnection.getAccountInfo.mockResolvedValue({
|
|
315
|
+
data: buildMailboxConfigData(),
|
|
316
|
+
});
|
|
292
317
|
|
|
293
318
|
await depositFn(testWallet(MOCK_PAYER), baseParams);
|
|
294
319
|
|
|
@@ -301,7 +326,10 @@ describe('deposit', () => {
|
|
|
301
326
|
|
|
302
327
|
it('should wrap errors with SolanaSdkError', async () => {
|
|
303
328
|
const { getConfig } = await import('../../const/getConfig');
|
|
304
|
-
vi.mocked(getConfig).mockReturnValueOnce({
|
|
329
|
+
vi.mocked(getConfig).mockReturnValueOnce({
|
|
330
|
+
...fullConfig,
|
|
331
|
+
assetRouter: null,
|
|
332
|
+
});
|
|
305
333
|
|
|
306
334
|
try {
|
|
307
335
|
await depositFn(testWallet(MOCK_PAYER), baseParams);
|
|
@@ -313,7 +341,9 @@ describe('deposit', () => {
|
|
|
313
341
|
});
|
|
314
342
|
|
|
315
343
|
it('should use env override when provided', async () => {
|
|
316
|
-
mockConnection.getAccountInfo.mockResolvedValue({
|
|
344
|
+
mockConnection.getAccountInfo.mockResolvedValue({
|
|
345
|
+
data: buildMailboxConfigData(),
|
|
346
|
+
});
|
|
317
347
|
|
|
318
348
|
const { getConfig } = await import('../../const/getConfig');
|
|
319
349
|
|
|
@@ -107,7 +107,7 @@ export async function deposit(
|
|
|
107
107
|
|
|
108
108
|
validateAmount(amount);
|
|
109
109
|
|
|
110
|
-
const connection = getConnection(network, rpcUrl);
|
|
110
|
+
const connection = getConnection(network, rpcUrl, env);
|
|
111
111
|
const payer = new PublicKey(provider.publicKey);
|
|
112
112
|
const mint = new PublicKey(sourceMintAddress);
|
|
113
113
|
const recipientPubkey = new PublicKey(recipient);
|
|
@@ -132,7 +132,10 @@ export async function deposit(
|
|
|
132
132
|
getTokenProgramForMint(connection, destinationMint),
|
|
133
133
|
]);
|
|
134
134
|
debugLog('Source token program:', tokenProgramId.toBase58());
|
|
135
|
-
debugLog(
|
|
135
|
+
debugLog(
|
|
136
|
+
'Destination token program:',
|
|
137
|
+
destinationTokenProgramId.toBase58(),
|
|
138
|
+
);
|
|
136
139
|
|
|
137
140
|
const [assetRouterConfigPDA] = PublicKey.findProgramAddressSync(
|
|
138
141
|
[Buffer.from('asset_router_config')],
|
|
@@ -162,9 +165,7 @@ export async function deposit(
|
|
|
162
165
|
mailboxProgramId,
|
|
163
166
|
);
|
|
164
167
|
if (!config.ledgerChainId) {
|
|
165
|
-
throw new Error(
|
|
166
|
-
`Ledger chain ID not configured for network: ${network}`,
|
|
167
|
-
);
|
|
168
|
+
throw new Error(`Ledger chain ID not configured for network: ${network}`);
|
|
168
169
|
}
|
|
169
170
|
const ledgerChainId = Buffer.from(config.ledgerChainId, 'hex');
|
|
170
171
|
const [outboundMessagePathPDA] = PublicKey.findProgramAddressSync(
|
|
@@ -177,10 +178,7 @@ export async function deposit(
|
|
|
177
178
|
);
|
|
178
179
|
|
|
179
180
|
debugLog('Mailbox config PDA:', mailboxConfigPDA.toBase58());
|
|
180
|
-
debugLog(
|
|
181
|
-
'Outbound message path PDA:',
|
|
182
|
-
outboundMessagePathPDA.toBase58(),
|
|
183
|
-
);
|
|
181
|
+
debugLog('Outbound message path PDA:', outboundMessagePathPDA.toBase58());
|
|
184
182
|
debugLog('Sender config PDA:', senderConfigPDA.toBase58());
|
|
185
183
|
|
|
186
184
|
const [arConfigInfo, mailboxConfigInfo] = await Promise.all([
|
|
@@ -230,7 +228,10 @@ export async function deposit(
|
|
|
230
228
|
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
231
229
|
);
|
|
232
230
|
debugLog('Payer token account:', payerTokenAccount.toBase58());
|
|
233
|
-
debugLog(
|
|
231
|
+
debugLog(
|
|
232
|
+
'Recipient token account (payload):',
|
|
233
|
+
recipientTokenAccount.toBase58(),
|
|
234
|
+
);
|
|
234
235
|
|
|
235
236
|
const tokenBalance =
|
|
236
237
|
await connection.getTokenAccountBalance(payerTokenAccount);
|
|
@@ -242,10 +243,9 @@ export async function deposit(
|
|
|
242
243
|
);
|
|
243
244
|
}
|
|
244
245
|
|
|
245
|
-
const assetRouterProgram = new Program(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
);
|
|
246
|
+
const assetRouterProgram = new Program(getAssetRouterIdl(env), {
|
|
247
|
+
connection,
|
|
248
|
+
});
|
|
249
249
|
|
|
250
250
|
const toLchainIdArray = Array.from(toLchainId);
|
|
251
251
|
const toTokenAddressArray = Array.from(toTokenAddress);
|
|
@@ -33,7 +33,7 @@ describe('detectWallet utilities', () => {
|
|
|
33
33
|
describe('getSolanaWalletProvider', () => {
|
|
34
34
|
it.each([InjectedWallet.PHANTOM])(
|
|
35
35
|
'should return a solana wallet provider if available',
|
|
36
|
-
wallet => {
|
|
36
|
+
(wallet) => {
|
|
37
37
|
const provider = getSolanaWalletProvider(wallet);
|
|
38
38
|
expect(provider).toBeDefined();
|
|
39
39
|
},
|
|
@@ -41,7 +41,7 @@ describe('detectWallet utilities', () => {
|
|
|
41
41
|
|
|
42
42
|
it.each([InjectedWallet.COINBASE, InjectedWallet.OKX])(
|
|
43
43
|
'should throw if wallet is not available',
|
|
44
|
-
wallet => {
|
|
44
|
+
(wallet) => {
|
|
45
45
|
expect(() => getSolanaWalletProvider(wallet)).toThrow();
|
|
46
46
|
},
|
|
47
47
|
);
|
|
@@ -127,6 +127,7 @@ export async function getBalance({
|
|
|
127
127
|
tokenAddress,
|
|
128
128
|
network = DEFAULT_NETWORK,
|
|
129
129
|
rpcUrl,
|
|
130
|
+
env,
|
|
130
131
|
}: GetBalanceParams): Promise<GetBalanceResult> {
|
|
131
132
|
try {
|
|
132
133
|
// Validate the public key
|
|
@@ -151,7 +152,7 @@ export async function getBalance({
|
|
|
151
152
|
}
|
|
152
153
|
|
|
153
154
|
// Setup connection with appropriate RPC URL
|
|
154
|
-
const connection = getConnection(network, rpcUrl);
|
|
155
|
+
const connection = getConnection(network, rpcUrl, env);
|
|
155
156
|
|
|
156
157
|
try {
|
|
157
158
|
return await fetchBalance(connection, pubKey, tokenAddress);
|