Getting a Loan
This guide walks through the complete process of getting a Bitcoin-backed loan using the SDK.
Overview
The loan process involves:
- Quote - Get available loan quotes
- Borrow - Execute the loan; the SDK prepares wallet/session state automatically
- Deposit - Send Bitcoin collateral
- Complete - Receive borrowed stablecoins
Prerequisites
import { BorrowSDK, ChainType } from '@satsterminal-sdk/borrow';
const sdk = new BorrowSDK({
apiKey: 'your-api-key',
wallet: {
address: 'bc1q...',
signMessage: async (msg) => wallet.signMessage(msg)
}
});
Recommended Flow
Use the explicit borrow flow so your UI can show quotes, fees, and a final review before creating the borrow workflow:
const quotes = await sdk.getQuotes({
collateralAmount: '0.1',
loanAmount: '5000'
});
const selectedQuote = quotes.reduce((best, q) =>
parseFloat(q.borrowApy.variable) < parseFloat(best.borrowApy.variable) ? q : best
);
const workflowId = await sdk.executeBorrow(selectedQuote, {
destinationAddress: '0x...'
});
await sdk.trackWorkflow(workflowId, {
onStatusUpdate: (status) => {
console.log(`[Step ${status.step}] ${status.label}`);
},
onDepositReady: (info) => {
console.log('\n=== DEPOSIT REQUIRED ===');
console.log(`Amount: ${info.amountBTC} BTC`);
console.log(`Address: ${info.address}`);
console.log('========================\n');
},
onComplete: (result) => {
console.log('Loan completed successfully!');
},
onError: (error) => {
console.error('Loan failed:', error);
}
}, 'borrow');
console.log('Workflow ID:', workflowId);
console.log('Selected Quote:', selectedQuote);
Optional: Setup
const { platformWallet, userStatus, activeSession } = await sdk.setup();
console.log('Smart Account:', userStatus.smartAccountAddress);
console.log('Session expires:', new Date(activeSession.validUntil * 1000));
Use setup() when you want to preload account state for a dashboard. You can skip it for a borrow-only path; executeBorrow() prepares the required platform wallet, loan wallet, and session automatically.
Step 2: Get Quotes
const quotes = await sdk.getQuotes({
collateralAmount: '0.1', // BTC amount as string
loanAmount: '5000' // USD amount as string
});
console.log(`Found ${quotes.length} quotes:`);
quotes.forEach((q, i) => {
console.log(`${i + 1}. ${q.protocol} - APY: ${q.borrowApy.variable}%`);
});
Step 3: Select Quote
// Select the best quote (e.g., lowest APY)
const selectedQuote = quotes.reduce((best, q) =>
parseFloat(q.borrowApy.variable) < parseFloat(best.borrowApy.variable) ? q : best
);
console.log('Selected:', selectedQuote.protocol);
Step 3a: Fetch Fees for the Selected Quote
Call getQuoteFees() to get the bridge fees and the borrow-side breakdown (platform fee, applied disbursement bps, campaign waiver, net loan amount):
const fees = await sdk.getQuoteFees({
collateralAmount: '0.1',
loanAmount: '5000',
fromChain: ChainType.BITCOIN,
fromAssetSymbol: 'BTC',
toChain: selectedQuote.chain,
toAssetSymbol: selectedQuote.collateralAssetSymbol ?? 'WBTC',
loanChain: selectedQuote.loanChain ?? selectedQuote.chain,
loanAssetSymbol: selectedQuote.loanAssetSymbol ?? 'USDC',
});
console.log('Bridge fee USD:', fees.bridgeFees.totalBridgeFeeUSD);
console.log('Platform fee:', fees.borrowFees.platformFee);
console.log('Net loan to user:', fees.borrowFees.netLoanAmount);
For lower latency, fetch quotes and quote fees in parallel with Promise.all once your UI has both a collateral and a loan amount.
Step 4: Start New Loan
// Create a new loan wallet
await sdk.startNewLoan();
Step 5: Execute Borrow
const workflowId = await sdk.executeBorrow(selectedQuote, {
destinationAddress: '0x...' // Optional
});
console.log('Workflow started:', workflowId);
Step 6: Track Workflow
await sdk.trackWorkflow(workflowId, {
onStatusUpdate: (status) => console.log(status.label),
onDepositReady: (info) => console.log(`Deposit ${info.amountBTC} BTC to ${info.address}`),
onComplete: () => console.log('Complete!'),
onError: (error) => console.error(error)
}, 'borrow');
Understanding Quotes
A quote contains:
interface Quote {
collateralAmount: string; // BTC collateral
loanAmount: string; // USD loan amount
protocol: string; // Lending protocol (e.g., "aave")
chain: ChainType; // Target chain
borrowApy: {
variable: string; // Variable APY
stable: string; // Stable APY
};
effectiveApy?: {
variable: string; // Effective variable APY
stable: string; // Effective stable APY
};
}
Quote Selection Strategies
// Lowest APY
const lowestApyQuote = quotes.reduce((best, q) =>
parseFloat(q.borrowApy.variable) < parseFloat(best.borrowApy.variable) ? q : best
);
// Specific protocol
const aaveQuote = quotes.find(q => q.protocol === 'aave') || quotes[0];
// Highest loan amount
const highestLoanQuote = quotes.reduce((best, q) =>
parseFloat(q.loanAmount) > parseFloat(best.loanAmount) ? q : best
);
Loan Parameters
Collateral Amount
The amount of BTC you’re willing to put up as collateral:
collateralAmount: '0.1' // 0.1 BTC
Loan Amount
The USD value you want to borrow:
loanAmount: '5000' // $5,000
Loan-to-Value (LTV)
The ratio of loan amount to collateral value. For example, borrowing $7,000 against $10,000 of BTC collateral is 70% LTV.
| LTV | Risk Level | Max Borrow (on $10k collateral) |
|---|
| 50% | Conservative | $5,000 |
| 70% | Moderate | $7,000 |
| 80% | Aggressive | $8,000 |
Handling the Deposit
When onDepositReady fires, you need to send BTC to the provided address:
Manual Deposit
Display the deposit info to the user:
onDepositReady: (info) => {
showModal({
title: 'Deposit Required',
address: info.address,
amount: `${info.amountBTC} BTC`,
qrCode: generateQR(info.address)
});
}
Programmatic Deposit
If your wallet supports sending:
onDepositReady: async (info) => {
const satoshis = Math.round(info.amountBTC * 100_000_000);
const txHash = await sdk.sendBitcoin(info.address, satoshis);
console.log('Deposit TX:', txHash);
}
Monitoring Progress
Status Updates
onStatusUpdate: (status) => {
// Update UI
setProgress({
step: status.step,
label: status.label,
description: status.description,
isComplete: status.isComplete
});
// Log for debugging
console.log(`[${status.stage}] ${status.label}`);
}
Progress Stages
| Step | Stage | Description |
|---|
| 1 | INITIALIZING | Starting workflow |
| 2 | DEPOSIT_ADDRESS_READY | Address generated |
| 3 | AWAITING_DEPOSIT | Waiting for BTC |
| 4 | DEPOSIT_CONFIRMED | Deposit received |
| 5 | COLLATERAL_DEPOSITED | Collateral processed |
| 6 | PREPARING_LOAN | Creating loan |
| 7 | LOAN_CONFIRMED | Loan executed |
| 8 | COMPLETED | All done |
Error Handling
try {
const quotes = await sdk.getQuotes({
collateralAmount: '0.1',
loanAmount: '5000'
});
const workflowId = await sdk.executeBorrow(quotes[0]);
await sdk.trackWorkflow(workflowId, {
onError: (error) => console.error('Workflow error:', error)
});
} catch (error) {
// Handle setup/execution errors
if (error instanceof QuoteError) {
console.error('No quotes available');
} else if (error instanceof SmartAccountError) {
console.error('Wallet error:', error.message);
} else if (error instanceof ApiError) {
console.error('API error:', error.statusCode, error.message);
}
}
After the Loan
Once complete, your borrowed stablecoins are in your smart account. You can:
Check Positions
const positions = await sdk.getWalletPositions();
positions.data.forEach(pos => {
console.log(`${pos.attributes.fungible_info?.symbol}: ${pos.attributes.quantity.float}`);
});
View Loan History
const history = await sdk.getLoanHistory({ status: 'active' });
history.transactions.forEach(tx => {
console.log(`Loan: ${tx.amount} ${tx.currency} - Status: ${tx.status}`);
});
Withdraw to Bitcoin
const txId = await sdk.withdrawToBitcoin({
chain: ChainType.ARBITRUM,
amount: '1000',
assetSymbol: 'USDC',
btcAddress: 'bc1q...'
});
Complete Example
import { BorrowSDK, ChainType } from '@satsterminal-sdk/borrow';
async function openLoan() {
const sdk = new BorrowSDK({
apiKey: process.env.API_KEY!,
wallet: {
address: userBtcAddress,
signMessage: async (msg) => wallet.signMessage(msg),
sendBitcoin: async (to, sats) => wallet.send(to, sats)
}
});
try {
const quotes = await sdk.getQuotes({
collateralAmount: '0.1',
loanAmount: '5000'
});
const selectedQuote = quotes[0];
const workflowId = await sdk.executeBorrow(selectedQuote, {
destinationAddress: '0x...'
});
await sdk.trackWorkflow(workflowId, {
onStatusUpdate: (status) => {
updateUI({ step: status.step, label: status.label });
},
onDepositReady: async (info) => {
showDepositModal(info);
// Or send automatically
// await sdk.sendBitcoin(info.address, info.amount);
},
onComplete: () => {
showSuccessModal('Loan created successfully!');
router.push('/dashboard');
},
onError: (error) => {
showErrorModal(error);
}
}, 'borrow');
console.log('Loan workflow started:', workflowId);
} catch (error) {
console.error('Failed to start loan:', error);
}
}