@lombard.finance/sdk-solana 1.2.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +48 -15
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +37 -31
  4. package/dist/index2.cjs +57 -54
  5. package/dist/index2.js +7608 -7206
  6. package/package.json +2 -2
  7. package/src/const/errors.ts +0 -4
  8. package/src/const/getConfig.ts +43 -20
  9. package/src/const/rpcUrls.ts +2 -2
  10. package/src/idl/asset_router.json +548 -179
  11. package/src/idl/consortium.json +24 -43
  12. package/src/idl/mailbox.json +118 -107
  13. package/src/index.ts +1 -3
  14. package/src/services/SolanaServiceImpl.test.ts +123 -0
  15. package/src/services/SolanaServiceImpl.ts +53 -17
  16. package/src/stories/components/OutputSelector/OutputSelector.tsx +1 -0
  17. package/src/types/errors.ts +2 -0
  18. package/src/utils/createDebugLogger.ts +6 -13
  19. package/src/utils/errors.ts +2 -0
  20. package/src/utils/tokenAccount.ts +3 -1
  21. package/src/utils/transactions.ts +1 -1
  22. package/src/web3Sdk/claimToken/claimBtcb.ts +37 -28
  23. package/src/web3Sdk/claimToken/claimLbtcGmp.ts +66 -8
  24. package/src/web3Sdk/claimToken/claimToken.stories.tsx +2 -2
  25. package/src/web3Sdk/claimToken/claimToken.ts +20 -16
  26. package/src/web3Sdk/claimToken/constants.ts +5 -0
  27. package/src/web3Sdk/claimToken/index.ts +1 -0
  28. package/src/web3Sdk/claimToken/shared.ts +88 -80
  29. package/src/web3Sdk/deposit/deposit.stories.tsx +240 -0
  30. package/src/web3Sdk/deposit/deposit.test.ts +327 -0
  31. package/src/web3Sdk/deposit/deposit.ts +339 -0
  32. package/src/web3Sdk/deposit/index.ts +1 -0
  33. package/src/web3Sdk/getTokenFeeConfig/getTokenFeeConfig.stories.tsx +166 -0
  34. package/src/web3Sdk/getTokenFeeConfig/getTokenFeeConfig.test.ts +224 -0
  35. package/src/web3Sdk/getTokenFeeConfig/getTokenFeeConfig.ts +154 -0
  36. package/src/web3Sdk/getTokenFeeConfig/index.ts +11 -0
  37. package/src/web3Sdk/index.ts +3 -4
  38. package/src/web3Sdk/redeem/index.ts +1 -0
  39. package/src/web3Sdk/redeem/redeem.stories.tsx +226 -0
  40. package/src/web3Sdk/redeem/redeem.test.ts +327 -0
  41. package/src/web3Sdk/redeem/redeem.ts +352 -0
  42. package/src/web3Sdk/redeemToken/redeemBtcb.ts +174 -0
  43. package/src/web3Sdk/redeemToken/redeemForBtc.stories.tsx +35 -21
  44. package/src/web3Sdk/redeemToken/redeemForBtc.test.ts +306 -0
  45. package/src/web3Sdk/redeemToken/redeemForBtc.ts +54 -215
  46. package/src/web3Sdk/redeemToken/redeemLbtc.ts +174 -0
  47. package/src/web3Sdk/redeemToken/shared.test.ts +45 -0
  48. package/src/web3Sdk/redeemToken/shared.ts +97 -0
  49. package/src/web3Sdk/claimLBTC/claimLBTC.stories.tsx +0 -189
  50. package/src/web3Sdk/claimLBTC/claimLBTC.ts +0 -225
  51. package/src/web3Sdk/claimLBTC/index.ts +0 -1
  52. package/src/web3Sdk/claimLBTC/utils/generateDepositId.ts +0 -75
  53. package/src/web3Sdk/claimLBTC/utils/index.ts +0 -2
  54. package/src/web3Sdk/claimLBTC/utils/parseTransactionLogs.ts +0 -44
  55. package/src/web3Sdk/claimLBTC/utils/payloadUtils.ts +0 -58
  56. package/src/web3Sdk/claimLBTC/utils/postMintSignatures.ts +0 -50
  57. package/src/web3Sdk/unstakeLBTC/index.ts +0 -1
  58. package/src/web3Sdk/unstakeLBTC/unstakeLBTC.stories.tsx +0 -141
  59. package/src/web3Sdk/unstakeLBTC/unstakeLBTC.ts +0 -140
  60. /package/src/web3Sdk/{claimLBTC → claimToken}/utils/__tests__/signatureUtils.test.ts +0 -0
  61. /package/src/web3Sdk/{claimLBTC → claimToken}/utils/signatureUtils.ts +0 -0
@@ -1,225 +0,0 @@
1
- import { AnchorProvider, Program, setProvider } from '@coral-xyz/anchor';
2
- import { getAssociatedTokenAddress, TOKEN_PROGRAM_ID } from '@solana/spl-token';
3
- import { PublicKey, SystemProgram } from '@solana/web3.js';
4
-
5
- import { MintPayload } from '../../common/mintPayload';
6
- import { getConfig, networkToEnv } from '../../const/getConfig';
7
- import { getConnection } from '../../const/rpcUrls';
8
- import { getLbtcIdl } from '../../idl/getLbtcIdl';
9
- import { ISolanaWalletProvider, SolanaNetwork } from '../../types';
10
- import { sendAndConfirmTransaction } from '../../utils';
11
- import { createDebugLogger } from '../../utils/createDebugLogger';
12
- import { verifyMatchingRecipient } from '../../utils/recipients';
13
- import { createOrGetAssociatedTokenAccount } from '../../utils/tokenAccount';
14
- import { checkPayloadStatus } from './utils';
15
- import { generateDepositId } from './utils/generateDepositId';
16
- import { postMintSignatures } from './utils/postMintSignatures';
17
-
18
- export const ALREADY_MINTED_TX_HASH = 'ALREADY_MINTED';
19
-
20
- /**
21
- * Parameters for claiming LBTC tokens on Solana
22
- */
23
- export interface ClaimLBTCParams {
24
- /**
25
- * Address to receive the LBTC tokens
26
- */
27
- recipientAddress: string;
28
- /**
29
- * Amount of LBTC to claim in base units
30
- */
31
- amount: string;
32
- /**
33
- * Network to use
34
- */
35
- network: SolanaNetwork;
36
- /**
37
- * Proof signature from the backend
38
- */
39
- proofSignature: string;
40
- /**
41
- * Raw payload data
42
- */
43
- rawPayload: string;
44
- /**
45
- * Optional RPC endpoint to use
46
- */
47
- rpcUrl?: string;
48
- /**
49
- * Enable verbose debug logging
50
- * @default false
51
- */
52
- debug?: boolean;
53
- }
54
-
55
- /**
56
- * Claim LBTC tokens on Solana using a three-step process:
57
- * 1. Create mint payload
58
- * 2. Post mint signatures
59
- * 3. Mint from payload
60
- *
61
- * @param provider Solana wallet provider
62
- * @param params Claim parameters
63
- * @returns Transaction signature
64
- */
65
- export async function claimLBTC(
66
- provider: ISolanaWalletProvider,
67
- params: ClaimLBTCParams,
68
- ): Promise<string> {
69
- const {
70
- recipientAddress,
71
- network,
72
- proofSignature,
73
- rawPayload,
74
- rpcUrl,
75
- debug = false,
76
- } = params;
77
- const { debugLog, printLogs } = createDebugLogger({ debug });
78
-
79
- try {
80
- if (!provider.publicKey) {
81
- throw new Error('Wallet not found');
82
- }
83
-
84
- const config = getConfig(networkToEnv[network]);
85
- const connection = getConnection(network, rpcUrl);
86
-
87
- const wallet = {
88
- publicKey: new PublicKey(provider.publicKey),
89
- signTransaction: provider.signTransaction,
90
- signAllTransactions: provider.signAllTransactions,
91
- };
92
- const anchorProvider = new AnchorProvider(connection, wallet, {});
93
- setProvider(anchorProvider);
94
- const program = new Program(getLbtcIdl(network), anchorProvider);
95
-
96
- const mint = new PublicKey(config.lbtcTokenMint);
97
- const programId = new PublicKey(config.lbtcProgramId);
98
- const lzMultisig = new PublicKey(config.lzMultisig);
99
- const mintPayload = new MintPayload(rawPayload);
100
- const [mintPayloadPDA] = PublicKey.findProgramAddressSync(
101
- [mintPayload.hashAsBytes()],
102
- programId,
103
- );
104
- const [configPDA] = PublicKey.findProgramAddressSync(
105
- [Buffer.from('lbtc_config')],
106
- programId,
107
- );
108
- const [tokenAuth] = PublicKey.findProgramAddressSync(
109
- [Buffer.from('token_authority')],
110
- programId,
111
- );
112
- const payloadHashArray = Array.from(mintPayload.hashAsBytes());
113
-
114
- await verifyMatchingRecipient(mint, mintPayload, recipientAddress);
115
-
116
- const payloadStatus = await checkPayloadStatus(
117
- program,
118
- mintPayloadPDA,
119
- debugLog,
120
- );
121
-
122
- if (payloadStatus.isMinted) {
123
- return ALREADY_MINTED_TX_HASH;
124
- }
125
-
126
- await createOrGetAssociatedTokenAccount({
127
- provider,
128
- connection,
129
- ownerAddress: recipientAddress,
130
- mintAddress: config.lbtcTokenMint,
131
- });
132
-
133
- // Step 1: Create Mint Payload (if it doesn't exist)
134
- if (!payloadStatus.exists) {
135
- debugLog('Adding Create Mint Payload instruction...');
136
- const createPayloadIx = await program.methods
137
- .createMintPayload(payloadHashArray, Array.from(mintPayload.bytes()))
138
- .accounts({
139
- payer: provider.publicKey,
140
- config: configPDA,
141
- // @ts-ignore -- type error from idl even though payload is required
142
- payload: mintPayloadPDA,
143
- systemProgram: SystemProgram.programId,
144
- })
145
- .transaction();
146
- await sendAndConfirmTransaction({
147
- instruction: createPayloadIx,
148
- connection,
149
- provider,
150
- debugLabel: 'Create Mint Payload',
151
- });
152
- } else {
153
- debugLog('Mint payload already exists, skipping create instruction.');
154
- }
155
-
156
- // Step 2: Post Mint Signatures (if not already signed)
157
- if (!payloadStatus.isSigned) {
158
- await postMintSignatures({
159
- connection,
160
- provider,
161
- program,
162
- configPDA,
163
- mintPayloadPDA,
164
- payloadHashArray,
165
- proofSignature,
166
- });
167
- } else {
168
- debugLog('Payload already signed, skipping post signatures instruction.');
169
- }
170
-
171
- // Extract necessary data from the payload
172
- const recipient = new PublicKey(recipientAddress);
173
- const recipientATA = await getAssociatedTokenAddress(mint, recipient);
174
- const amount = mintPayload.amount.startsWith('0x')
175
- ? BigInt(mintPayload.amount)
176
- : BigInt(`0x${mintPayload.amount}`);
177
- const depositId = generateDepositId(
178
- recipientATA,
179
- amount,
180
- mintPayload.txId,
181
- Number(mintPayload.vout),
182
- );
183
- const DEPOSIT_SEED = Buffer.from('deposit');
184
- const [depositPDA] = config.bascule
185
- ? PublicKey.findProgramAddressSync(
186
- [DEPOSIT_SEED, depositId],
187
- new PublicKey(config.bascule),
188
- )
189
- : [];
190
-
191
- // Step 3: Mint From Payload (Always add if not already minted)
192
- const mintTx = await program.methods
193
- .mintFromPayload(payloadHashArray)
194
- .accounts({
195
- config: configPDA,
196
- tokenProgram: TOKEN_PROGRAM_ID,
197
- recipient: recipientATA,
198
- mint,
199
- mintAuthority: lzMultisig,
200
- tokenAuthority: tokenAuth,
201
- payload: mintPayloadPDA,
202
- // @ts-ignore -- type error from idl even though payload is required
203
- bascule: config.bascule,
204
- basculeData: config.basculeData,
205
- deposit: depositPDA,
206
- systemProgram: SystemProgram.programId,
207
- signer: provider.publicKey,
208
- })
209
- .transaction();
210
-
211
- const { signature } = await sendAndConfirmTransaction({
212
- instruction: mintTx,
213
- connection,
214
- provider,
215
- debugLabel: 'Mint From Payload',
216
- });
217
-
218
- return signature;
219
- } catch (error: unknown) {
220
- if (error instanceof Error) {
221
- error.message = `${error.message}\n\nDebug logs:\n${printLogs()}`;
222
- }
223
- throw error;
224
- }
225
- }
@@ -1 +0,0 @@
1
- export * from './claimLBTC';
@@ -1,75 +0,0 @@
1
- import { Buffer } from 'node:buffer'; // Use buffer package for cross-platform compatibility
2
-
3
- import { PublicKey } from '@solana/web3.js';
4
- import { keccak256 } from 'js-sha3';
5
-
6
- /**
7
- * Represents the unique identifier for a deposit, derived from transaction details.
8
- * It's a 32-byte array, typically the result of a Keccak-256 hash.
9
- */
10
- export type DepositId = Uint8Array;
11
-
12
- /**
13
- * Generates a unique deposit ID (address) based on transaction details, mirroring
14
- * the logic used in the corresponding Solana smart contract.
15
- *
16
- * The ID is computed using the following formula:
17
- * keccak256(
18
- * [0u8; 32] // 32-byte zero prefix
19
- * || [0x03, 0x53, 0x4f, 0x4c] // 4-byte chain ID ("\x03SOL")
20
- * || recipient (32 bytes)
21
- * || amount (u64, 8 bytes, big-endian)
22
- * || tx_id (32 bytes)
23
- * || tx_vout (u32, 4 bytes, big-endian)
24
- * )
25
- *
26
- * Adapted from: https://github.com/lombard-finance/sol-contracts/blob/632491cb92fd2f206b0e563b1e72e4b2ae2eadf5/programs/bascule/src/instructions/validator.rs#L118-L138
27
- *
28
- * @param recipient - The recipient's Solana public key.
29
- * @param amount - The amount of the transaction (as a bigint, representing u64).
30
- * @param txId - The transaction ID (as a 32-byte Uint8Array or Buffer).
31
- * @param txVout - The transaction output index (as a number, representing u32).
32
- * @returns A 32-byte Uint8Array representing the calculated deposit ID.
33
- * @throws Error if txId is not 32 bytes long.
34
- */
35
- export function generateDepositId(
36
- recipient: PublicKey,
37
- amount: bigint,
38
- txId: string,
39
- txVout: number,
40
- ): DepositId {
41
- const txIdBuffer = Buffer.from(txId, 'hex');
42
- const reversedTxIdBuffer = txIdBuffer.reverse();
43
-
44
- if (reversedTxIdBuffer.length !== 32) {
45
- throw new Error('txId must be a 32-byte array.');
46
- }
47
-
48
- const prefix = Buffer.alloc(32, 0);
49
- const chainId = Buffer.from([0x03, 0x53, 0x4f, 0x4c]);
50
- const recipientBuffer = recipient.toBuffer();
51
- const amountBuffer = Buffer.alloc(8);
52
- amountBuffer.writeBigUInt64BE(amount);
53
-
54
- const txVoutBuffer = Buffer.alloc(4);
55
- txVoutBuffer.writeUInt32BE(txVout);
56
-
57
- const dataToHash = Buffer.concat([
58
- new Uint8Array(prefix),
59
- new Uint8Array(chainId),
60
- new Uint8Array(recipientBuffer),
61
- new Uint8Array(amountBuffer),
62
- new Uint8Array(reversedTxIdBuffer),
63
- new Uint8Array(txVoutBuffer),
64
- ]);
65
-
66
- const dataToHashUint8Array = new Uint8Array(
67
- dataToHash.buffer,
68
- dataToHash.byteOffset,
69
- dataToHash.byteLength,
70
- );
71
- const hashString = keccak256(dataToHashUint8Array);
72
- const hashBuffer = Buffer.from(hashString, 'hex');
73
-
74
- return new Uint8Array(hashBuffer);
75
- }
@@ -1,2 +0,0 @@
1
- export * from './payloadUtils';
2
- export * from './signatureUtils';
@@ -1,44 +0,0 @@
1
- export const parseTransactionLogs = (
2
- err: unknown,
3
- ): { errorMessage: string; errorLogs: string[] } => {
4
- console.error('Error claiming LBTC:', err);
5
- let errorMessage =
6
- typeof err === 'object' && err !== null && 'message' in err
7
- ? (err.message as string)
8
- : String(err);
9
- let errorLogs: string[] = [];
10
-
11
- if (typeof errorMessage === 'string') {
12
- const logParts = errorMessage.split('Logs:');
13
- if (logParts.length > 1) {
14
- errorMessage = logParts[0].trim();
15
- try {
16
- let logsText = logParts[1].trim();
17
-
18
- if (logsText.startsWith('[') && logsText.endsWith(']')) {
19
- try {
20
- errorLogs = JSON.parse(logsText);
21
-
22
- return { errorMessage, errorLogs };
23
- } catch {
24
- logsText = logsText.replace(/^\[|\]$/g, '');
25
- errorLogs = logsText
26
- .split(/",?\s*"/)
27
- .map(line => line.replace(/^"|"$/g, '')); // Remove quotes
28
-
29
- return { errorMessage, errorLogs };
30
- }
31
- } else {
32
- errorLogs = logsText.split('\n').filter(line => line.trim() !== '');
33
- return { errorMessage, errorLogs };
34
- }
35
- } catch (parseError) {
36
- console.error('Failed to parse logs:', parseError);
37
- errorMessage = err instanceof Error ? err.message : String(err);
38
- return { errorMessage, errorLogs: [] };
39
- }
40
- }
41
- }
42
-
43
- return { errorMessage, errorLogs };
44
- };
@@ -1,58 +0,0 @@
1
- import { Program } from '@coral-xyz/anchor';
2
- import { PublicKey } from '@solana/web3.js';
3
-
4
- import type { Lbtc } from '../../../idl/lbtc';
5
-
6
- /**
7
- * Status of a mint payload on-chain
8
- */
9
- export interface PayloadStatus {
10
- /**
11
- * Whether the payload exists on-chain
12
- */
13
- exists: boolean;
14
- /**
15
- * Whether the payload has been signed (signatures posted)
16
- */
17
- isSigned: boolean;
18
- /**
19
- * Whether the LBTC tokens have been minted
20
- */
21
- isMinted: boolean;
22
- }
23
-
24
- /**
25
- * Checks the status of a mint payload on the Solana blockchain
26
- *
27
- * @param program Anchor program instance
28
- * @param mintPayloadPDA The PDA address where the mint payload is stored
29
- * @param debugLog Debug logging function
30
- * @returns The status of the payload
31
- */
32
- export async function checkPayloadStatus(
33
- program: Program<Lbtc>,
34
- mintPayloadPDA: PublicKey,
35
- debugLog: (...args: unknown[]) => void,
36
- ): Promise<PayloadStatus> {
37
- try {
38
- const payloadAccount =
39
- await program.account.mintPayload.fetch(mintPayloadPDA);
40
-
41
- if (!payloadAccount) {
42
- return { exists: false, isSigned: false, isMinted: false };
43
- }
44
-
45
- const isSigned = Array.isArray(payloadAccount.signed)
46
- ? payloadAccount.signed.every((signed: boolean) => signed === true)
47
- : false;
48
-
49
- return {
50
- exists: true,
51
- isSigned,
52
- isMinted: !!payloadAccount.minted,
53
- };
54
- } catch (error) {
55
- debugLog('Error checking payload status:', error);
56
- return { exists: false, isSigned: false, isMinted: false };
57
- }
58
- }
@@ -1,50 +0,0 @@
1
- import { Program } from '@coral-xyz/anchor';
2
- import { Connection, PublicKey } from '@solana/web3.js';
3
-
4
- import { Lbtc } from '../../../idl/lbtc';
5
- import { ISolanaWalletProvider } from '../../../types';
6
- import { sendAndConfirmTransaction } from '../../../utils';
7
- import { parseSignaturesFromProof } from './signatureUtils';
8
-
9
- export const postMintSignatures = async ({
10
- connection,
11
- provider,
12
- program,
13
- configPDA,
14
- mintPayloadPDA,
15
- payloadHashArray,
16
- proofSignature,
17
- }: {
18
- connection: Connection;
19
- provider: ISolanaWalletProvider;
20
- program: Program<Lbtc>;
21
- configPDA: PublicKey;
22
- mintPayloadPDA: PublicKey;
23
- payloadHashArray: number[];
24
- proofSignature: string;
25
- }) => {
26
- const { signatures: parsedSignaturesUint8, indices } =
27
- parseSignaturesFromProof(proofSignature);
28
-
29
- if (parsedSignaturesUint8.length === 0 || indices.length === 0) {
30
- throw new Error('No valid signatures found in the proof');
31
- }
32
-
33
- // Convert Uint8Array[] to number[][]
34
- const signatures = parsedSignaturesUint8.map(sig => Array.from(sig));
35
-
36
- const postSignaturesIx = await program.methods
37
- .postMintSignatures(payloadHashArray, signatures, indices)
38
- .accounts({
39
- config: configPDA,
40
- // @ts-ignore -- type error from idl even though payload is required
41
- payload: mintPayloadPDA,
42
- })
43
- .transaction();
44
- return await sendAndConfirmTransaction({
45
- instruction: postSignaturesIx,
46
- connection,
47
- provider,
48
- debugLabel: 'Post Mint Signatures',
49
- });
50
- };
@@ -1 +0,0 @@
1
- export * from './unstakeLBTC';
@@ -1,141 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
2
-
3
- import {
4
- Button,
5
- ConnectButton,
6
- ErrorDisplay,
7
- ResultDisplay,
8
- } from '../../stories/components';
9
- import { functionType } from '../../stories/decorators/function-type';
10
- import { useConnect } from '../../stories/hooks/useConnect';
11
- import useQuery from '../../stories/hooks/useQuery';
12
- import { SolanaNetwork } from '../../types';
13
- import { unstakeLBTC } from './unstakeLBTC';
14
-
15
- interface UnstakeLbtcStoryArgs {
16
- network: SolanaNetwork;
17
- amountSats: string;
18
- btcAddress: string;
19
- }
20
-
21
- const DEFAULT_AMOUNT_SATS = '22000';
22
-
23
- export const StoryView = ({
24
- network,
25
- amountSats,
26
- btcAddress,
27
- }: UnstakeLbtcStoryArgs) => {
28
- const {
29
- data: connectionData,
30
- error: connectError,
31
- isLoading: isConnecting,
32
- connect,
33
- disconnect,
34
- } = useConnect();
35
- const isConnected = !!connectionData;
36
- const address = connectionData?.address;
37
- const provider = connectionData?.provider;
38
-
39
- const request = async () => {
40
- if (!provider || !address) throw new Error('Wallet not connected.');
41
- if (!btcAddress)
42
- throw new Error('Destination Bitcoin address is required (set in args).');
43
- if (!amountSats || !/^[1-9]\d*$/.test(amountSats))
44
- throw new Error(
45
- 'Amount must be a positive integer in satoshis (set in args).',
46
- );
47
-
48
- const result = await unstakeLBTC(provider, {
49
- amount: amountSats,
50
- btcAddress,
51
- network: network,
52
- });
53
- return result;
54
- };
55
-
56
- const {
57
- data: txHash,
58
- error,
59
- isLoading,
60
- refetch: handleUnstake,
61
- } = useQuery(
62
- request,
63
- [provider, address, amountSats, btcAddress, network],
64
- false,
65
- );
66
-
67
- return (
68
- <>
69
- <ConnectButton
70
- connect={connect}
71
- disconnect={disconnect}
72
- isConnected={isConnected}
73
- isLoading={isConnecting}
74
- error={connectError}
75
- walletName={connectionData?.walletName}
76
- address={connectionData?.address}
77
- network={network}
78
- />
79
-
80
- {isConnected && (
81
- <>
82
- <div className="d-grid gap-2 my-4">
83
- <Button
84
- primary
85
- size="large"
86
- onClick={handleUnstake}
87
- isLoading={isLoading}
88
- actionName={unstakeLBTC.name}
89
- />
90
- </div>
91
-
92
- {txHash && (
93
- <ResultDisplay
94
- result={txHash}
95
- title="Unstake Transaction Hash"
96
- successMessage="Success! Unstake transaction submitted."
97
- />
98
- )}
99
- {(error || connectError) && (
100
- <ErrorDisplay error={error || connectError} title="Unstake Error" />
101
- )}
102
- </>
103
- )}
104
- </>
105
- );
106
- };
107
-
108
- const meta: Meta<typeof StoryView> = {
109
- title: 'write/unstakeLBTC',
110
- component: StoryView,
111
- tags: ['autodocs'],
112
- decorators: [functionType('write')],
113
- parameters: {
114
- docs: {
115
- description: {
116
- component:
117
- 'Demonstrates unstaking LBTC from Solana back to a Bitcoin address using the `unstakeLBTC` SDK function. Requires the amount in satoshis and the destination Bitcoin address.',
118
- },
119
- },
120
- },
121
- args: {
122
- network: SolanaNetwork.devnet,
123
- amountSats: DEFAULT_AMOUNT_SATS,
124
- btcAddress: '',
125
- },
126
- argTypes: {
127
- network: {
128
- control: { type: 'select' },
129
- options: Object.values(SolanaNetwork),
130
- },
131
- amountSats: {
132
- control: { type: 'text' },
133
- },
134
- btcAddress: {
135
- control: { type: 'text' },
136
- },
137
- },
138
- };
139
-
140
- export default meta;
141
- type Story = StoryObj<typeof meta>;