Skip to main content

Getting a Loan

This guide walks through the complete process of getting a Bitcoin-backed loan using the SDK.

Overview

The loan process involves:
  1. Setup - Initialize SDK and create session
  2. Quote - Get available loan quotes
  3. Borrow - Execute the loan
  4. Deposit - Send Bitcoin collateral
  5. Complete - Receive borrowed stablecoins

Prerequisites

import { BorrowSDK, ChainType } from '@satsterminal-sdk/borrow';

const sdk = new BorrowSDK({
  apiKey: 'your-api-key',
  chain: ChainType.ARBITRUM,
  wallet: {
    address: 'bc1q...',
    signMessage: async (msg) => wallet.signMessage(msg)
  }
});
The getLoan() method handles the entire flow automatically:
const result = await sdk.getLoan({
  // Loan parameters
  collateralBTC: 0.1,      // Amount of BTC to collateralize
  loanAmountUSD: 5000,     // Amount to borrow in USD
  ltv: 70,                 // Loan-to-value ratio (default: 70)
  term: 30,                // Term in days (default: 30)

  // Optional: destination for borrowed funds
  destinationAddress: '0x...',

  // Optional: custom quote selection
  quoteSelector: (quotes) => {
    // Select quote with lowest APY
    return quotes.reduce((best, q) =>
      parseFloat(q.borrowApy.variable) < parseFloat(best.borrowApy.variable) ? q : best
    );
  },

  // Callbacks
  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);
  }
});

console.log('Workflow ID:', result.workflowId);
console.log('Selected Quote:', result.quote);

Stopping the Workflow

// If you need to stop tracking
result.stop();

Method 2: Step-by-Step API

For more control, use the individual methods:

Step 1: Setup

const { baseWallet, userStatus, activeSession } = await sdk.setup();

console.log('Smart Account:', userStatus.smartAccountAddress);
console.log('Session expires:', new Date(activeSession.validUntil * 1000));

Step 2: Get Quotes

const quotes = await sdk.getQuotes({
  collateralAmount: '0.1',  // BTC amount as string
  loanAmount: '5000',       // USD amount as string
  ltv: 70,
  term: 30
});

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 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
quoteSelector: (quotes) => quotes.reduce((best, q) =>
  parseFloat(q.borrowApy.variable) < parseFloat(best.borrowApy.variable) ? q : best
);

// Specific protocol
quoteSelector: (quotes) => quotes.find(q => q.protocol === 'aave') || quotes[0];

// Highest loan amount
quoteSelector: (quotes) => 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:
collateralBTC: 0.1  // 0.1 BTC = 100,000 sats

Loan Amount

The USD value you want to borrow:
loanAmountUSD: 5000  // $5,000

Loan-to-Value (LTV)

The ratio of loan amount to collateral value:
ltv: 70  // 70% LTV
LTVRisk LevelMax Borrow (on $10k collateral)
50%Conservative$5,000
70%Moderate$7,000
80%Aggressive$8,000

Term

Loan duration in days:
term: 30  // 30 days

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

StepStageDescription
1INITIALIZINGStarting workflow
2DEPOSIT_ADDRESS_READYAddress generated
3AWAITING_DEPOSITWaiting for BTC
4DEPOSIT_CONFIRMEDDeposit received
5COLLATERAL_DEPOSITEDCollateral processed
6PREPARING_LOANCreating loan
7LOAN_CONFIRMEDLoan executed
8COMPLETEDAll done

Error Handling

try {
  await sdk.getLoan({
    collateralBTC: 0.1,
    loanAmountUSD: 5000,
    onError: (error) => {
      // Handle workflow errors
      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 getLoan() {
  const sdk = new BorrowSDK({
    apiKey: process.env.API_KEY!,
    chain: ChainType.ARBITRUM,
    wallet: {
      address: userBtcAddress,
      signMessage: async (msg) => wallet.signMessage(msg),
      sendBitcoin: async (to, sats) => wallet.send(to, sats)
    }
  });

  try {
    const result = await sdk.getLoan({
      collateralBTC: 0.1,
      loanAmountUSD: 5000,
      ltv: 70,

      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);
      }
    });

    console.log('Loan workflow started:', result.workflowId);

  } catch (error) {
    console.error('Failed to start loan:', error);
  }
}