Complete Loan Flow
This example demonstrates the entire loan lifecycle from setup to repayment.Overview
Complete Example
Copy
import {
BorrowSDK,
ChainType,
Quote,
UserTransaction,
WorkflowStatus,
DepositInfo,
LoanCollateralInfo
} from '@satsterminal-sdk/borrow';
// Configuration
const CONFIG = {
apiKey: process.env.SATSTERMINAL_API_KEY!,
chain: ChainType.ARBITRUM,
btcAddress: process.env.BTC_ADDRESS!
};
// Loan parameters
const LOAN_PARAMS = {
collateralBTC: 0.1,
loanAmountUSD: 5000,
ltv: 70,
term: 30
};
class LoanManager {
private sdk: BorrowSDK;
private activeLoanId: string | null = null;
private workflowId: string | null = null;
constructor(signMessage: (msg: string) => Promise<string>) {
this.sdk = new BorrowSDK({
...CONFIG,
wallet: {
address: CONFIG.btcAddress,
signMessage
}
});
}
// ========================================
// PHASE 1: SETUP
// ========================================
async setup(): Promise<void> {
console.log('='.repeat(50));
console.log('PHASE 1: SETUP');
console.log('='.repeat(50));
const { baseWallet, userStatus, activeSession, transactions } = await this.sdk.setup();
console.log('\nSetup Complete:');
console.log(` BTC Address: ${userStatus.btcAddress}`);
console.log(` Smart Account: ${baseWallet.address}`);
console.log(` Is Deployed: ${userStatus.isDeployed}`);
console.log(` Session Expires: ${new Date(activeSession.validUntil * 1000).toLocaleString()}`);
console.log(` Existing Transactions: ${transactions.length}`);
}
// ========================================
// PHASE 2: GET QUOTES
// ========================================
async getQuotes(): Promise<Quote[]> {
console.log('\n' + '='.repeat(50));
console.log('PHASE 2: GET QUOTES');
console.log('='.repeat(50));
const quotes = await this.sdk.getQuotes({
collateralAmount: LOAN_PARAMS.collateralBTC.toString(),
loanAmount: LOAN_PARAMS.loanAmountUSD.toString(),
ltv: LOAN_PARAMS.ltv,
term: LOAN_PARAMS.term
});
console.log(`\nFound ${quotes.length} quotes:`);
quotes.forEach((q, i) => {
console.log(`\n ${i + 1}. ${q.protocol.toUpperCase()}`);
console.log(` Chain: ${q.chain}`);
console.log(` Collateral: ${q.collateralAmount} BTC`);
console.log(` Loan Amount: $${q.loanAmount}`);
console.log(` Variable APY: ${q.borrowApy.variable}%`);
console.log(` Stable APY: ${q.borrowApy.stable}%`);
});
return quotes;
}
selectBestQuote(quotes: Quote[]): Quote {
// Select quote with lowest variable APY
const bestQuote = quotes.reduce((best, current) => {
const bestApy = parseFloat(best.borrowApy.variable);
const currentApy = parseFloat(current.borrowApy.variable);
return currentApy < bestApy ? current : best;
});
console.log(`\nSelected: ${bestQuote.protocol} with ${bestQuote.borrowApy.variable}% APY`);
return bestQuote;
}
// ========================================
// PHASE 3: EXECUTE BORROW
// ========================================
async executeBorrow(): Promise<void> {
console.log('\n' + '='.repeat(50));
console.log('PHASE 3: EXECUTE BORROW');
console.log('='.repeat(50));
return new Promise(async (resolve, reject) => {
try {
const result = await this.sdk.getLoan({
collateralBTC: LOAN_PARAMS.collateralBTC,
loanAmountUSD: LOAN_PARAMS.loanAmountUSD,
ltv: LOAN_PARAMS.ltv,
term: LOAN_PARAMS.term,
onStatusUpdate: (status: WorkflowStatus) => {
this.handleStatusUpdate(status);
},
onDepositReady: (info: DepositInfo) => {
this.handleDepositReady(info);
},
onComplete: (result: any) => {
this.handleBorrowComplete(result);
resolve();
},
onError: (error: string) => {
console.error('\n❌ Borrow Error:', error);
reject(new Error(error));
}
});
this.workflowId = result.workflowId;
console.log(`\nWorkflow Started: ${this.workflowId}`);
} catch (error) {
reject(error);
}
});
}
private handleStatusUpdate(status: WorkflowStatus): void {
const icon = status.isComplete ? '✅' :
status.isFailed ? '❌' : '⏳';
console.log(`\n${icon} [Step ${status.step}] ${status.label}`);
console.log(` ${status.description}`);
if (status.stage === 'COLLATERAL_DEPOSITED') {
console.log(' 💰 Collateral received and processing...');
}
}
private handleDepositReady(info: DepositInfo): void {
console.log('\n' + '─'.repeat(50));
console.log('📥 DEPOSIT REQUIRED');
console.log('─'.repeat(50));
console.log(` Amount: ${info.amountBTC} BTC (${info.amount} sats)`);
console.log(` Address: ${info.address}`);
console.log('─'.repeat(50));
console.log('\n⏳ Waiting for deposit confirmation...');
}
private handleBorrowComplete(result: any): void {
console.log('\n' + '─'.repeat(50));
console.log('✅ BORROW COMPLETE');
console.log('─'.repeat(50));
console.log(` Workflow ID: ${this.workflowId}`);
console.log(' Funds are now in your smart account.');
}
// ========================================
// PHASE 4: MONITOR LOAN
// ========================================
async monitorLoan(): Promise<void> {
console.log('\n' + '='.repeat(50));
console.log('PHASE 4: MONITOR LOAN');
console.log('='.repeat(50));
// Get active loans
const history = await this.sdk.getLoanHistory({ status: 'active' });
if (history.transactions.length === 0) {
console.log('\nNo active loans found.');
return;
}
const loan = history.transactions[0];
this.activeLoanId = loan.id;
console.log(`\nActive Loan: ${loan.id}`);
console.log(` Type: ${loan.type}`);
console.log(` Amount: ${loan.amount} ${loan.currency}`);
console.log(` Status: ${loan.status}`);
console.log(` Created: ${new Date(loan.timestamp).toLocaleString()}`);
// Get collateral info
const collateralInfo = await this.sdk.getLoanCollateralInfo(loan.id);
if (collateralInfo) {
console.log('\nCollateral Information:');
console.log(` Total Collateral: ${collateralInfo.totalCollateral} BTC`);
console.log(` Available Collateral: ${collateralInfo.availableCollateral} BTC`);
console.log(` Max Withdrawable: ${collateralInfo.maxWithdrawable} BTC`);
console.log(` Total Debt: $${collateralInfo.totalDebt}`);
console.log(` Remaining Debt: $${collateralInfo.remainingDebt}`);
// Calculate health
const health = parseFloat(collateralInfo.availableCollateral) /
parseFloat(collateralInfo.totalDebt);
console.log(` Health Ratio: ${health.toFixed(4)}`);
}
// Check wallet positions
const positions = await this.sdk.getWalletPositions();
console.log('\nWallet Positions:');
positions.data.forEach(p => {
const symbol = p.attributes.fungible_info?.symbol || 'Unknown';
const amount = p.attributes.quantity.float;
const value = p.attributes.value || 0;
console.log(` ${symbol}: ${amount.toFixed(4)} ($${value.toFixed(2)})`);
});
}
// ========================================
// PHASE 5: REPAY LOAN
// ========================================
async repayLoan(btcWithdrawAddress: string): Promise<void> {
console.log('\n' + '='.repeat(50));
console.log('PHASE 5: REPAY LOAN');
console.log('='.repeat(50));
if (!this.activeLoanId) {
// Get active loan
const history = await this.sdk.getLoanHistory({ status: 'active' });
if (history.transactions.length === 0) {
console.log('\nNo active loans to repay.');
return;
}
this.activeLoanId = history.transactions[0].id;
}
// Get collateral info for repayment
const collateralInfo = await this.sdk.getLoanCollateralInfo(this.activeLoanId);
if (!collateralInfo) {
console.error('\nCould not get collateral info.');
return;
}
console.log(`\nRepaying loan ${this.activeLoanId}`);
console.log(` Debt: $${collateralInfo.remainingDebt}`);
console.log(` Collateral to withdraw: ${collateralInfo.maxWithdrawable} BTC`);
console.log(` Withdraw to: ${btcWithdrawAddress}`);
return new Promise(async (resolve, reject) => {
try {
const txId = await this.sdk.repay(
this.activeLoanId!,
collateralInfo.remainingDebt, // Full repayment
{
collateralToWithdraw: collateralInfo.maxWithdrawable,
userBtcWithdrawAddress: btcWithdrawAddress,
trackWorkflow: true,
callbacks: {
onStatusUpdate: (status: WorkflowStatus) => {
console.log(`\n⏳ [${status.step}] ${status.label}`);
if (status.stage.includes('BRIDGE')) {
console.log(' 🌉 Bridge in progress...');
}
},
onComplete: () => {
console.log('\n' + '─'.repeat(50));
console.log('✅ REPAYMENT COMPLETE');
console.log('─'.repeat(50));
console.log(' Loan fully repaid.');
console.log(` Collateral withdrawn to ${btcWithdrawAddress}`);
resolve();
},
onError: (error: string) => {
console.error('\n❌ Repayment Error:', error);
reject(new Error(error));
}
}
}
);
console.log(`\nRepayment Transaction: ${txId}`);
} catch (error) {
reject(error);
}
});
}
// ========================================
// CLEANUP
// ========================================
cleanup(): void {
console.log('\n' + '='.repeat(50));
console.log('CLEANUP');
console.log('='.repeat(50));
this.sdk.clearSession();
console.log('\nSession cleared.');
}
}
// ========================================
// MAIN EXECUTION
// ========================================
async function main() {
console.log('\n');
console.log('╔══════════════════════════════════════════════════╗');
console.log('║ SATSTERMINAL BORROW - COMPLETE LOAN FLOW ║');
console.log('╚══════════════════════════════════════════════════╝');
console.log('\n');
// Initialize with your signing function
const signMessage = async (message: string): Promise<string> => {
// Replace with your actual wallet signing implementation
return await yourWallet.signMessage(message);
};
const manager = new LoanManager(signMessage);
try {
// Phase 1: Setup
await manager.setup();
// Phase 2: Get Quotes
const quotes = await manager.getQuotes();
const selectedQuote = manager.selectBestQuote(quotes);
// Phase 3: Execute Borrow
await manager.executeBorrow();
// Phase 4: Monitor (wait a bit for loan to be active)
console.log('\n⏳ Waiting for loan to become active...');
await new Promise(r => setTimeout(r, 5000));
await manager.monitorLoan();
// Phase 5: Repay (optional - uncomment to repay)
// const btcAddress = 'bc1q...';
// await manager.repayLoan(btcAddress);
console.log('\n');
console.log('╔══════════════════════════════════════════════════╗');
console.log('║ LOAN FLOW COMPLETE ║');
console.log('╚══════════════════════════════════════════════════╝');
} catch (error) {
console.error('\n❌ Error:', error);
} finally {
manager.cleanup();
}
}
main().catch(console.error);
Flow Diagram
Key Takeaways
- Always call
setup()first - This initializes the smart account and session - Handle all callbacks - Status updates, deposit ready, complete, and error
- Monitor loan health - Check collateral info periodically
- Provide BTC address for repayment - Required for collateral withdrawal
- Clear session when done - Cleanup resources properly