import { Env } from '@lombard.finance/sdk-common'; import type { Meta, StoryObj } from '@storybook/react'; import { useState } from 'react'; import { envToNetwork, getConfig } from '../../const/getConfig'; import { Button, CodeBlock, ConnectButton, ErrorDisplay, ResultDisplay, SectionCard, } from '../../stories/components'; import { functionType } from '../../stories/decorators/function-type'; import { useConnect } from '../../stories/hooks/useConnect'; import useQuery from '../../stories/hooks/useQuery'; import { redeemForBtc } from './redeemForBtc'; type RedeemToken = 'BTC.b' | 'LBTC'; interface RedeemForBtcStoryArgs { environment: Env; amount: string; btcAddress: string; token: RedeemToken; } const getTokenMint = ( token: RedeemToken, config: ReturnType, ): string | undefined => token === 'LBTC' ? config.lbtcTokenMint : (config.btcbTokenMint ?? undefined); export const StoryView = ({ environment, amount, btcAddress, token, }: RedeemForBtcStoryArgs) => { const network = envToNetwork[environment]; const config = getConfig(environment); const tokenMint = getTokenMint(token, config); const [transactionLogs, setTransactionLogs] = useState(null); const { data: connectionData, error: connectError, isLoading: isConnecting, connect, disconnect, } = useConnect(); const isConnected = !!connectionData; const address = connectionData?.address; const provider = connectionData?.provider; const request = async () => { if (!provider || !address) throw new Error('Wallet not connected.'); if (!btcAddress) throw new Error('Destination Bitcoin address is required (set in args).'); const parsedAmount = parseFloat(amount); if (!amount || isNaN(parsedAmount) || parsedAmount <= 0) throw new Error('Amount must be a positive number in BTC (set in args).'); const amountSats = Math.round(parsedAmount * 1e8).toString(); if (!tokenMint) { throw new Error( 'No mint for the selected token in this environment (e.g. BTC.b may be unset on mainnet).', ); } setTransactionLogs(null); try { const result = await redeemForBtc(provider, { amount: amountSats, btcAddress, tokenMint, network, env: environment, debug: true, }); return result; } catch (err: unknown) { if (err instanceof Error && err.message.includes('Debug logs:')) { const parts = err.message.split('Debug logs:\n'); setTransactionLogs(parts[1]?.split('\n') || []); } throw err; } }; const { data: txHash, error, isLoading, refetch: handleRedeem, } = useQuery( request, [provider, address, amount, btcAddress, token, environment], false, ); return ( <> {isConnected && ( <>

Environment: {environment}

Network: {network}

Amount: {amount} BTC

BTC Address: {btcAddress || Not set}

Token: {token} {tokenMint ? ` (${tokenMint})` : ' — not configured'}

{txHash && ( )} {(error || connectError) && ( )} {transactionLogs && transactionLogs.length > 0 && ( )} )} ); }; const meta: Meta = { title: 'write/redeemForBtc (Asset Router)', component: StoryView, tags: ['autodocs'], decorators: [functionType('write')], parameters: { docs: { description: { component: `Demonstrates redeeming BTC.b or LBTC → BTC via the Asset Router's \`redeem_for_btc\` instruction. **Flow:** 1. Connect a Solana wallet holding BTC.b or LBTC tokens 2. Enter the destination Bitcoin address and amount (in BTC) 3. Optionally set the token mint to the LBTC address to redeem LBTC instead of BTC.b 4. Call \`redeemForBtc\` — burns the token and sends a GMP message through the Mailbox 5. The Lombard protocol processes the GMP message and sends BTC to the specified address`, }, }, }, args: { environment: Env.stage, amount: '0.0002', btcAddress: '', token: 'BTC.b', }, argTypes: { environment: { control: { type: 'select' }, options: Object.values(Env), }, token: { control: { type: 'select' }, options: ['BTC.b', 'LBTC'] satisfies RedeemToken[], description: 'Token to redeem for BTC', }, amount: { control: { type: 'text' }, description: 'Amount to redeem in BTC (e.g. 0.0002)', }, btcAddress: { control: { type: 'text' }, description: 'Destination Bitcoin address (taproot, segwit, etc.)', }, }, }; export default meta; type Story = StoryObj;