Skip to main content

Managing Collateral

This guide covers collateral management including checking balances, withdrawing collateral, and understanding health factors.

Understanding Collateral

When you take a loan, your Bitcoin is locked as collateral:
  • Total Collateral - All BTC locked for the loan
  • Available Collateral - Collateral not at risk of liquidation
  • Max Withdrawable - Maximum you can withdraw safely

Checking Collateral Info

const loanId = 'your-loan-id';
const info = await sdk.getLoanCollateralInfo(loanId);

if (info) {
  console.log('Collateral Information:');
  console.log('  Total:', info.totalCollateral, 'BTC');
  console.log('  Available:', info.availableCollateral, 'BTC');
  console.log('  Max Withdrawable:', info.maxWithdrawable, 'BTC');
  console.log('  Total Debt:', info.totalDebt, 'USD');
  console.log('  Remaining Debt:', info.remainingDebt, 'USD');
}

LoanCollateralInfo Interface

interface LoanCollateralInfo {
  totalCollateral: string;      // Total BTC locked
  availableCollateral: string;  // BTC above liquidation threshold
  maxWithdrawable: string;      // Safe withdrawal amount
  totalDebt: string;            // Original debt amount
  remainingDebt: string;        // Current outstanding debt
}

Withdrawing Collateral

Prerequisites

Before withdrawing:
  1. Check maxWithdrawable amount
  2. Ensure withdrawal won’t trigger liquidation
  3. Have a valid BTC address ready

Basic Withdrawal

const txId = await sdk.withdrawCollateral(
  loanId,
  '0.01',           // Amount in BTC
  'bc1q...',        // Your BTC address
  {
    trackWorkflow: true,
    callbacks: {
      onStatusUpdate: (status) => {
        console.log(`[${status.step}] ${status.label}`);
      },
      onComplete: () => {
        console.log('Withdrawal complete!');
      },
      onError: (error) => {
        console.error('Withdrawal failed:', error);
      }
    }
  }
);

console.log('Transaction ID:', txId);

Maximum Safe Withdrawal

const info = await sdk.getLoanCollateralInfo(loanId);

if (parseFloat(info.maxWithdrawable) > 0) {
  await sdk.withdrawCollateral(
    loanId,
    info.maxWithdrawable,  // Withdraw maximum safe amount
    'bc1q...',
    { trackWorkflow: true, callbacks }
  );
} else {
  console.log('No collateral available for withdrawal');
}

Withdrawal with Repayment

Withdraw collateral while repaying part of the loan:
await sdk.repay(loanId, '1000', {  // Repay $1000
  collateralToWithdraw: '0.02',    // Withdraw 0.02 BTC
  userBtcWithdrawAddress: 'bc1q...',
  trackWorkflow: true,
  callbacks: {
    onStatusUpdate: (status) => console.log(status.label),
    onComplete: () => console.log('Repaid and withdrew collateral')
  }
});

Withdrawal Workflow

The withdrawal process involves bridging BTC from the EVM chain:

Monitoring Withdrawal Status

Track Active Withdrawal

await sdk.withdrawCollateral(loanId, amount, address, {
  trackWorkflow: true,
  callbacks: {
    onStatusUpdate: (status) => {
      // Update UI with progress
      updateProgress({
        stage: status.stage,
        step: status.step,
        label: status.label
      });

      // Special handling for bridge stages
      if (status.stage === 'BRIDGE_AWAITING_BRIDGE_COMPLETION') {
        showNotification('Bridge in progress. This may take 10-30 minutes.');
      }
    }
  }
});

Check Status Later

const status = await sdk.getRepayStatus(transactionId);

console.log('Current stage:', status.transactionState?.stage);
console.log('Redeem TX:', status.transactionState?.redeemTxHash);

Health Factor Considerations

What is Health Factor?

Health Factor = (Collateral Value × Liquidation Threshold) / Debt
Health FactorStatus
> 1.5Safe
1.0 - 1.5Caution
< 1.0Liquidation risk

Calculating Safe Withdrawal

const info = await sdk.getLoanCollateralInfo(loanId);

// The maxWithdrawable already accounts for health factor
const safeAmount = info.maxWithdrawable;

// For extra safety, withdraw less than max
const conservativeAmount = (parseFloat(safeAmount) * 0.8).toFixed(8);

console.log('Safe to withdraw:', conservativeAmount, 'BTC');

Error Handling

Common Errors

try {
  await sdk.withdrawCollateral(loanId, amount, address);
} catch (error) {
  if (error.message.includes('health factor')) {
    console.error('Cannot withdraw: would lower health factor too much');
  } else if (error.message.includes('insufficient collateral')) {
    console.error('Not enough collateral to withdraw this amount');
  } else if (error.message.includes('invalid address')) {
    console.error('Invalid BTC address provided');
  } else {
    console.error('Withdrawal error:', error.message);
  }
}

Validation Before Withdrawal

async function validateWithdrawal(loanId: string, amount: string, address: string) {
  // Check amount
  const info = await sdk.getLoanCollateralInfo(loanId);
  if (parseFloat(amount) > parseFloat(info.maxWithdrawable)) {
    throw new Error(`Cannot withdraw ${amount} BTC. Max: ${info.maxWithdrawable} BTC`);
  }

  // Validate address
  if (!isValidBtcAddress(address)) {
    throw new Error('Invalid BTC address');
  }

  return true;
}

Complete Example

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

async function manageCollateral() {
  const sdk = new BorrowSDK({
    apiKey: process.env.API_KEY!,
    chain: ChainType.ARBITRUM,
    wallet: walletProvider
  });

  await sdk.setup();

  // Get active loan
  const history = await sdk.getLoanHistory({ status: 'active' });
  const loan = history.transactions[0];

  if (!loan) {
    console.log('No active loans');
    return;
  }

  // Check collateral
  const info = await sdk.getLoanCollateralInfo(loan.id);

  console.log('\n=== Collateral Status ===');
  console.log(`Total Collateral: ${info.totalCollateral} BTC`);
  console.log(`Outstanding Debt: ${info.remainingDebt} USD`);
  console.log(`Max Withdrawable: ${info.maxWithdrawable} BTC`);

  // Withdraw if possible
  if (parseFloat(info.maxWithdrawable) > 0.001) {
    console.log('\nInitiating withdrawal...');

    const withdrawAmount = (parseFloat(info.maxWithdrawable) * 0.5).toFixed(8);

    await sdk.withdrawCollateral(
      loan.id,
      withdrawAmount,
      'bc1q...',
      {
        trackWorkflow: true,
        callbacks: {
          onStatusUpdate: (status) => {
            console.log(`[${status.step}] ${status.label}`);
          },
          onComplete: () => {
            console.log(`\nSuccessfully withdrew ${withdrawAmount} BTC`);
          },
          onError: (error) => {
            console.error('Withdrawal failed:', error);
          }
        }
      }
    );
  } else {
    console.log('\nNo collateral available for withdrawal');
  }
}

Best Practices

1. Always Check Max Withdrawable First

const info = await sdk.getLoanCollateralInfo(loanId);
if (parseFloat(amount) > parseFloat(info.maxWithdrawable)) {
  throw new Error('Amount exceeds safe withdrawal limit');
}

2. Leave a Safety Buffer

// Withdraw 90% of max to leave buffer
const safeAmount = (parseFloat(info.maxWithdrawable) * 0.9).toFixed(8);

3. Verify BTC Address

function isValidBtcAddress(address: string): boolean {
  // Basic validation - extend as needed
  return /^(bc1|[13])[a-zA-HJ-NP-Z0-9]{25,62}$/.test(address);
}

4. Handle Bridge Delays

onStatusUpdate: (status) => {
  if (status.stage.includes('BRIDGE')) {
    showMessage('Bridging in progress. Please wait...');
  }
}

5. Monitor After Withdrawal

// After withdrawal completes, refresh collateral info
const updatedInfo = await sdk.getLoanCollateralInfo(loanId);
console.log('Remaining collateral:', updatedInfo.totalCollateral);