Skip to main content

Repaying a Loan

This guide covers how to repay loans, including full repayment, partial repayment, and repayment using collateral.

Overview

Repayment options:
  • Full Repayment - Pay off the entire loan
  • Partial Repayment - Pay a portion of the loan
  • Repay with Collateral - Use your BTC collateral to repay
  • Collateral Withdrawal - Withdraw excess collateral

Prerequisites

Ensure you have an active loan:
const history = await sdk.getLoanHistory({ status: 'active' });
const activeLoan = history.transactions[0];
console.log('Loan ID:', activeLoan.id);

Full Repayment

Repay the entire outstanding balance:
const loanId = 'your-loan-id';
const repayAmount = '5000'; // Full amount in USD

const txId = await sdk.repay(loanId, repayAmount, {
  trackWorkflow: true,
  callbacks: {
    onStatusUpdate: (status) => {
      console.log(`[${status.step}] ${status.label}`);
    },
    onComplete: () => {
      console.log('Loan fully repaid!');
    },
    onError: (error) => {
      console.error('Repayment failed:', error);
    }
  }
});

console.log('Repayment transaction:', txId);

Partial Repayment

Pay a portion of the loan:
const txId = await sdk.repay(loanId, '1000', { // Partial amount
  trackWorkflow: true,
  callbacks: {
    onStatusUpdate: (status) => console.log(status.label),
    onComplete: () => console.log('Partial repayment complete')
  }
});

Repay with Collateral

Use your BTC collateral to repay the loan:
const txId = await sdk.repay(loanId, '2000', {
  useCollateral: true,
  trackWorkflow: true,
  callbacks: {
    onStatusUpdate: (status) => console.log(status.label),
    onComplete: () => console.log('Repaid using collateral')
  }
});

Repay and Withdraw Collateral

Repay and withdraw excess collateral in one transaction:
const txId = await sdk.repay(loanId, '5000', {
  collateralToWithdraw: '0.02',           // BTC to withdraw
  userBtcWithdrawAddress: 'bc1q...',      // Where to send BTC
  trackWorkflow: true,
  callbacks: {
    onStatusUpdate: (status) => {
      console.log(`${status.label}`);
      if (status.stage.startsWith('BRIDGE_')) {
        console.log('  Bridging BTC to your wallet...');
      }
    },
    onComplete: () => {
      console.log('Repayment and withdrawal complete!');
    }
  }
});

Checking Repayment Status

Get Repay Transaction Status

const status = await sdk.getRepayStatus(transactionId);

console.log('Status:', status.transactionDetails.transactionStatuses);
console.log('Workflow stage:', status.transactionState?.stage);

Get All Repay Transactions for a Loan

const repayments = await sdk.getRepayTransactions(loanId);

repayments.forEach(tx => {
  console.log(`Amount: ${tx.repayAmount}`);
  console.log(`Is Partial: ${tx.isPartialRepay}`);
  console.log(`Status: ${tx.transactionStatuses[tx.transactionStatuses.length - 1]?.status}`);
});

Repayment Workflow Stages

StageDescription
INITIALIZINGStarting repayment
TRANSFERRING_TO_KERNELMoving funds internally
REPAYING_LOANExecuting repayment on protocol
WITHDRAWING_COLLATERALWithdrawing BTC (if requested)
TRANSFERRING_TO_PLATFORM_WALLETPreparing for bridge
BRIDGE_INITIALIZINGStarting bridge
BRIDGE_QUOTE_READYBridge quote received
BRIDGE_SWAP_CREATEDSwap created
BRIDGE_EXECUTING_APPROVALApproving tokens
BRIDGE_APPROVAL_CONFIRMEDApproval done
BRIDGE_EXECUTING_INITIATEStarting bridge tx
BRIDGE_INITIATE_CONFIRMEDBridge initiated
BRIDGE_AWAITING_BRIDGE_COMPLETIONWaiting for bridge
BRIDGE_COMPLETEDBridge complete
COMPLETEDAll done
FAILEDFailed

Checking Available Collateral

Before withdrawing, check available collateral:
const collateralInfo = await sdk.getLoanCollateralInfo(loanId);

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

Collateral-Only Withdrawal

Withdraw collateral without repaying (if health factor allows):
const txId = await sdk.withdrawCollateral(
  loanId,
  '0.01',           // Amount to withdraw
  'bc1q...',        // BTC address
  {
    trackWorkflow: true,
    callbacks: {
      onStatusUpdate: (s) => console.log(s.label),
      onComplete: () => console.log('Withdrawal complete!')
    }
  }
);

Error Handling

try {
  await sdk.repay(loanId, repayAmount, {
    trackWorkflow: true,
    callbacks: {
      onError: (error) => {
        // Handle workflow errors
        if (error.includes('insufficient balance')) {
          showError('Insufficient funds to repay');
        } else if (error.includes('health factor')) {
          showError('Cannot withdraw - would liquidate loan');
        } else {
          showError(error);
        }
      }
    }
  });
} catch (error) {
  // Handle setup errors
  if (error instanceof ApiError) {
    console.error('API Error:', error.statusCode);
  }
}

Repay Options Reference

interface RepayOptions {
  // Use collateral to repay instead of wallet balance
  useCollateral?: boolean;

  // Amount of collateral to withdraw (BTC as string)
  collateralToWithdraw?: string;

  // BTC address for withdrawn collateral
  userBtcWithdrawAddress?: string;

  // Track the repayment workflow
  trackWorkflow?: boolean;

  // Workflow callbacks
  callbacks?: {
    onStatusUpdate?: (status: WorkflowStatus) => void;
    onComplete?: (result: any) => void;
    onError?: (error: string) => void;
  };
}

Complete Example

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

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

  await sdk.setup();

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

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

  // Check collateral info
  const collateral = await sdk.getLoanCollateralInfo(loan.id);
  console.log('Outstanding debt:', collateral?.remainingDebt);
  console.log('Max withdrawable:', collateral?.maxWithdrawable);

  // Full repayment with collateral withdrawal
  await sdk.repay(loan.id, collateral?.remainingDebt || '0', {
    collateralToWithdraw: collateral?.maxWithdrawable,
    userBtcWithdrawAddress: 'bc1q...',
    trackWorkflow: true,
    callbacks: {
      onStatusUpdate: (status) => {
        console.log(`[${status.step}] ${status.label}`);

        // Show bridge progress
        if (status.stage.startsWith('BRIDGE_')) {
          console.log('  Bridging collateral to BTC...');
        }
      },
      onComplete: () => {
        console.log('Loan fully repaid and collateral withdrawn!');
      },
      onError: (error) => {
        console.error('Repayment failed:', error);
      }
    }
  });
}

Best Practices

1. Check Balance Before Repaying

const positions = await sdk.getWalletPositions();
const usdcBalance = positions.data.find(
  p => p.attributes.fungible_info?.symbol === 'USDC'
);

if (parseFloat(usdcBalance?.attributes.quantity.numeric || '0') < parseFloat(repayAmount)) {
  throw new Error('Insufficient USDC balance');
}

2. Calculate Optimal Withdrawal

const info = await sdk.getLoanCollateralInfo(loanId);
const repayRatio = parseFloat(repayAmount) / parseFloat(info.totalDebt);
const proportionalWithdraw = parseFloat(info.totalCollateral) * repayRatio;
const safeWithdraw = Math.min(proportionalWithdraw, parseFloat(info.maxWithdrawable));

3. Handle Bridge Delays

The bridge process can take several minutes:
onStatusUpdate: (status) => {
  if (status.stage === 'BRIDGE_AWAITING_BRIDGE_COMPLETION') {
    showMessage('Bridging BTC... This may take 10-30 minutes.');
  }
}

4. Verify Withdrawal Address

Always double-check the BTC address:
const btcAddress = 'bc1q...';

// Validate address format
if (!btcAddress.match(/^(bc1|[13])[a-zA-HJ-NP-Z0-9]{25,39}$/)) {
  throw new Error('Invalid BTC address');
}

// Confirm with user
const confirmed = await confirmDialog(`Withdraw to ${btcAddress}?`);
if (!confirmed) return;