@satsterminal-sdk/borrow
v1.7.3
Published
Borrowing module for SatsTerminal SDK
Maintainers
Readme
@satsterminal-sdk/borrow
Borrow stablecoins against your Bitcoin collateral. Gasless, non-custodial, powered by smart accounts.
Installation
npm install @satsterminal-sdk/borrowOr use the aggregator if you also need swaps:
import { createClient } from 'satsterminal-sdk';
const { borrow } = createClient({
apiKey: process.env.API_KEY!,
borrow: { wallet }
});Quick Start
import { BorrowSDK } from '@satsterminal-sdk/borrow';
const sdk = new BorrowSDK({
apiKey: 'your-api-key',
wallet: {
address: 'bc1q...',
signMessage: async (msg) => wallet.signMessage(msg),
sendBitcoin: async (to, sats) => wallet.sendBitcoin(to, sats)
}
});
const quotes = await sdk.getQuotes({
collateralAmount: '0.1',
loanAmount: '5000'
});
const workflowId = await sdk.executeBorrow(quotes[0]);
await sdk.trackWorkflow(workflowId, {
onDepositReady: (info) => console.log(`Deposit ${info.amount} BTC to ${info.address}`),
onComplete: () => console.log('Loan complete!')
}, 'borrow');Features
- Non-custodial: Your Bitcoin, your keys
- Gasless: All EVM transactions are sponsored
- Multi-chain: Arbitrum, Base, Ethereum, and BNB Chain supported
- Smart accounts: ERC-4337 account abstraction via ZeroDev
Usage
Get a Loan
interface BorrowSDKConfig {
apiKey: string; // Required: Your API key
baseUrl?: string; // Optional: API base URL (defaults to https://api.satsterminal.com)
wallet: WalletProvider; // Required: Wallet provider
rpcUrl?: string; // Optional: Custom RPC URL
bundlerUrl?: string; // Optional: Custom bundler URL
storage?: StorageProvider; // Optional: Custom storage (defaults to localStorage)
workflowPollInterval?: number; // Optional: Polling interval in ms (default: 2000)
sessionValiditySeconds?: number; // Optional: Session validity (default: 259200)
autoTrackWorkflows?: boolean; // Optional: Auto-track workflows (default: true)
quoteSelector?: (quotes: Quote[]) => Quote; // Optional: Custom quote selector
retryConfig?: RetryConfig; // Optional: Retry configuration
logger?: Logger; // Optional: Custom logger
}Wallet Provider
The SDK requires a wallet provider that implements:
interface WalletProvider {
address: string; // Bitcoin address
publicKey?: string; // Optional: Public key
signMessage: (message: string) => Promise<string>; // Sign messages
sendBitcoin?: (toAddress: string, satoshis: number) => Promise<string>; // Send BTC
}Example Configuration
const sdk = new BorrowSDK({
apiKey: process.env.SATSTERMINAL_API_KEY!,
wallet: {
address: bitcoinAddress,
signMessage: async (message) => {
// Use sats-connect, Xverse, or UniSat wallet
const response = await signMessage({ message });
return response.signature;
},
sendBitcoin: async (to, amount) => {
const response = await sendBtcTransaction({
recipients: [{ address: to, amount }]
});
return response.txid;
}
},
autoTrackWorkflows: true,
workflowPollInterval: 2000
});Core Concepts
1. Setup
For dashboards or account preloading, call setup() to initialize the smart account, create a session, and fetch history. Borrow execution can also prepare the required wallet/session state automatically.
The constructor is chainless. Account preload methods such as setup() use the active chain when available and otherwise default to Base for preload; borrow execution derives the chain from the selected quote.
2. Quotes
Get loan quotes based on your collateral amount and desired loan amount. The SDK fetches quotes from multiple protocols.
3. Borrowing
Once you have a quote, you can execute the borrow operation. The SDK handles the entire workflow.
4. Repaying
Repay your loan partially or fully. You can also withdraw collateral if your loan health allows it.
5. Workflow Tracking
The SDK tracks asynchronous workflows and provides status updates via callbacks.
Repay Flow
The repay workflow is an asynchronous process that handles loan repayment and collateral withdrawal. Here's how it works:
Overview
When you call repay() or withdrawCollateral(), the SDK:
- Validates the amounts against the current loan state
- Prepares the platform wallet address/signature automatically if needed
- Creates a repay transaction on the backend
- Starts a Temporal workflow that handles the entire process
- Tracks the workflow and provides status updates
Workflow Stages
The repay workflow goes through the following stages:
1. INITIALIZING
- The workflow is being set up
- Validates amounts and prepares the transaction
- User Action: None - wait for next stage
2. TRANSFERRING_TO_KERNEL (if repaying)
- USDC is transferred from your wallet to the smart account (kernel wallet)
- User Action: If insufficient balance, deposit USDC to the smart account address shown
- Status: "Moving repay tokens to kernel wallet"
3. REPAYING_LOAN (if repaying)
- The loan is repaid on the protocol (AAVE, Morpho, etc.)
- USDC is used to pay down the debt
- User Action: None - automatic
- Status: "Executing loan repayment on protocol"
4. WITHDRAWING_COLLATERAL (if withdrawing)
- Collateral (WBTC) is withdrawn from the protocol
- User Action: None - automatic
- Status: "Withdrawing collateral from protocol"
5. TRANSFERRING_TO_PLATFORM_WALLET (if withdrawing)
- Withdrawn collateral is transferred to the platform wallet
- User Action: None - automatic
- Status: "Moving collateral to platform wallet"
6. BRIDGE Stages (if withdrawing)
If you're withdrawing collateral, it needs to be bridged from EVM (WBTC) to Bitcoin (BTC):
- BRIDGE_INITIALIZING: Preparing the bridge
- BRIDGE_QUOTE_READY: Bridge quote received
- BRIDGE_SWAP_CREATED: Bridge swap order created
- BRIDGE_EXECUTING_APPROVAL: Approving bridge contract
- BRIDGE_APPROVAL_CONFIRMED: Approval confirmed
- BRIDGE_EXECUTING_INITIATE: Initiating bridge
- BRIDGE_INITIATE_CONFIRMED: Bridge initiated
- BRIDGE_AWAITING_BRIDGE_COMPLETION: Waiting for bridge to complete
- BRIDGE_COMPLETED: Bridge completed, BTC ready
7. COMPLETED
- All operations completed successfully
- If repaying: Loan debt reduced
- If withdrawing: BTC sent to your withdrawal address
8. FAILED or CANCELLED
- Workflow encountered an error or was cancelled
- Check error details for resolution
Repay Flow Scenarios
Scenario 1: Repay Only (No Withdrawal)
INITIALIZING
↓
TRANSFERRING_TO_KERNEL (User deposits USDC)
↓
REPAYING_LOAN
↓
COMPLETEDScenario 2: Withdraw Collateral Only (No Repay)
INITIALIZING
↓
WITHDRAWING_COLLATERAL
↓
TRANSFERRING_TO_PLATFORM_WALLET
↓
BRIDGE_INITIALIZING → ... → BRIDGE_COMPLETED
↓
COMPLETEDScenario 3: Repay and Withdraw (Both)
INITIALIZING
↓
TRANSFERRING_TO_KERNEL (User deposits USDC)
↓
REPAYING_LOAN
↓
WITHDRAWING_COLLATERAL
↓
TRANSFERRING_TO_PLATFORM_WALLET
↓
BRIDGE_INITIALIZING → ... → BRIDGE_COMPLETED
↓
COMPLETEDImportant Notes
USDC Deposit Required: If repaying, you must deposit USDC to the smart account address before the workflow can proceed past
TRANSFERRING_TO_KERNEL.Max Withdrawable: Before withdrawing, check
getLoanCollateralInfo()to get the accuratemaxWithdrawableamount (accounts for fees and health factor).Workflow Tracking: Use callbacks to track progress:
await sdk.repay(loanId, amount, { trackWorkflow: true, callbacks: { onStatusUpdate: (status) => { console.log(`[${status.step}] ${status.label}`); console.log(status.description); }, onComplete: () => console.log('Done!'), onError: (error) => console.error('Error:', error) } });Resuming Stuck Workflows: If a workflow gets stuck (e.g., waiting for USDC deposit), you can retry by calling
repay()again with the same parameters, or use the "I've Deposited Funds" button in the UI.Partial Repays: You can repay any amount less than the total debt. The workflow will handle partial repayment correctly.
Health Factor: The system ensures your loan remains healthy after operations. If a withdrawal would make the loan unhealthy, it will be rejected with an error message showing the maximum withdrawable amount.
API Reference
Initialization
new BorrowSDK(config: BorrowSDKConfig)
Creates a new SDK instance.
import { BorrowSDK } from '@satsterminal-sdk/borrow';
const sdk = new BorrowSDK({
apiKey: 'your-api-key',
wallet: walletProvider
});Wallet Connection
setup(): Promise<SetupResult>
Initializes the smart account, creates a session, and fetches history. This is useful for dashboard/account preload flows; executeBorrow() can prepare the required borrow session automatically.
const { userStatus, transactions } = await sdk.setup();
console.log('Smart Account:', userStatus.smartAccountAddress);
console.log('Transactions:', transactions.length);Returns: Platform wallet, user status, active session, and transaction history.
Loan Operations
setup(): Promise<SetupResult>
Sets up the SDK for account-oriented flows and returns current history. It is optional for borrowing because executeBorrow() prepares the required borrow wallet/session if needed.
const { userStatus, transactions } = await sdk.setup();getQuotes(request: QuoteRequest): Promise<Quote[]>
Fetches loan quotes from available protocols.
const quotes = await sdk.getQuotes({
collateralAmount: '0.01',
loanAmount: '1000'
});
// Select a quote (or use default selector)
const selectedQuote = quotes[0]; // Or use custom selectorReturns: Array of Quote objects from different protocols.
executeBorrow(quote: Quote, options?: BorrowOptions): Promise<string>
Executes a borrow transaction with the selected quote.
const workflowId = await sdk.executeBorrow(selectedQuote, {
destinationAddress: '0x...'
});
await sdk.trackWorkflow(workflowId, {
onStatusUpdate: (status) => console.log(status),
onComplete: () => console.log('Done!')
}, 'borrow');Returns: Workflow ID string.
Repay Operations
repay(originalBorrowId: string, repayAmount: string, options?: RepayOptions): Promise<string>
Repay a loan (full or partial). The SDK prepares the platform wallet address/signature automatically if needed.
const transactionId = await sdk.repay('loan-id', '500', {
useCollateral: false, // Use collateral to repay?
collateralToWithdraw: '0.001', // Optional: Withdraw collateral
userBtcWithdrawAddress: 'bc1q...', // Required if withdrawing
trackWorkflow: true,
callbacks: {
onStatusUpdate: (status) => console.log(status),
onComplete: () => console.log('Repay complete!')
}
});Parameters:
originalBorrowId: string- The loan IDrepayAmount: string- Amount to repay in USDC (use '0' for collateral-only withdrawal)options?: RepayOptionsuseCollateral?: boolean- Use collateral for repaymentcollateralToWithdraw?: string- Amount of collateral to withdrawuserBtcWithdrawAddress?: string- BTC address for withdrawaltrackWorkflow?: boolean- Track the workflowcallbacks?: WorkflowCallbacks- Workflow callbacks
Returns: Transaction ID string.
withdrawCollateral(originalBorrowId: string, collateralAmount: string, btcWithdrawAddress: string, options?: WithdrawOptions): Promise<string>
Withdraw collateral from a loan. The SDK prepares the platform wallet address/signature automatically if needed.
const transactionId = await sdk.withdrawCollateral(
'loan-id',
'0.001',
'bc1q...',
{
trackWorkflow: true,
callbacks: {
onStatusUpdate: (status) => console.log(status),
onComplete: () => console.log('Withdrawal complete!')
}
}
);Returns: Transaction ID string.
getLoanCollateralInfo(loanId: string): Promise<LoanCollateralInfo | null>
Get loan collateral information including max withdrawable amount.
const collateralInfo = await sdk.getLoanCollateralInfo('loan-id');
if (collateralInfo) {
console.log('Max Withdrawable:', collateralInfo.maxWithdrawable, 'BTC');
console.log('Available Collateral:', collateralInfo.availableCollateral, 'BTC');
console.log('Total Debt:', collateralInfo.totalDebt, 'USDC');
console.log('Remaining Debt:', collateralInfo.remainingDebt, 'USDC');
}Returns: LoanCollateralInfo object or null if not available.
Transaction History
getLoanHistory(options?: HistoryOptions): Promise<PaginatedResponse<UserTransaction>>
Get loan transaction history.
const history = await sdk.getLoanHistory({
page: 1,
limit: 20,
status: 'active' // 'active' | 'pending' | 'all'
});
console.log(`Found ${history.transactions.length} transactions`);
console.log(`Total: ${history.pagination.totalTransactions}`);Options:
page?: number- Page number (default: 1)limit?: number- Items per page (default: 10)status?: 'active' | 'pending' | 'all'- Filter by status (default: 'all')
Returns: Paginated response with transactions and pagination info.
getPendingLoans(): Promise<UserTransaction[]>
Get all pending loans (awaiting deposit).
const pendingLoans = await sdk.getPendingLoans();
console.log(`You have ${pendingLoans.length} pending loans`);getRepayTransactions(loanId?: string): Promise<RepayTransaction[]>
Get repay transactions for a loan or all repay transactions.
// Get repay transactions for a specific loan
const repayTxs = await sdk.getRepayTransactions('loan-id');
// Get all repay transactions
const allRepayTxs = await sdk.getRepayTransactions();getRepayStatus(transactionId: string): Promise<RepayTransactionStatusResponse>
Get status of a repay transaction.
const status = await sdk.getRepayStatus('transaction-id');
console.log('Status:', status.transactionDetails);
console.log('Workflow State:', status.transactionState);Workflow Tracking
trackWorkflow(workflowId: string, callbacks: WorkflowCallbacks, workflowType?: 'borrow' | 'repay'): Promise<void>
Manually track a workflow.
await sdk.trackWorkflow('workflow-id', {
onStatusUpdate: (status) => {
console.log('Status:', status.label);
},
onComplete: (result) => {
console.log('Workflow complete!', result);
},
onError: (error) => {
console.error('Error:', error);
}
}, 'borrow'); // or 'repay'resumeLoan(workflowId: string, callbacks?: WorkflowCallbacks): Promise<void>
Resume tracking a loan workflow.
await sdk.resumeLoan('workflow-id', {
onStatusUpdate: (status) => console.log(status),
onComplete: () => console.log('Resumed and completed!')
});Utility Methods
getStatus(workflowId: string): Promise<any>
Get workflow status.
const status = await sdk.getStatus('workflow-id');clearSession(): void
Clear the current session.
sdk.clearSession();Examples
Complete Loan Flow
import { BorrowSDK, ChainType } from '@satsterminal-sdk/borrow';
const sdk = new BorrowSDK({
apiKey: 'your-api-key',
wallet: walletProvider
});
const quotes = await sdk.getQuotes({
collateralAmount: '0.01',
loanAmount: '1000'
});
const workflowId = await sdk.executeBorrow(quotes[0]);
await sdk.trackWorkflow(workflowId, {
onStatusUpdate: (status) => {
console.log(`[${status.step}/${status.totalSteps}] ${status.label}`);
console.log(status.description);
},
onDepositReady: (info) => {
console.log('📥 Deposit Required:');
console.log(`Address: ${info.depositAddress}`);
console.log(`Amount: ${info.amount} BTC`);
// User deposits BTC here
},
onComplete: () => {
console.log('✅ Loan completed!');
console.log('Workflow ID:', workflowId);
},
onError: (error) => {
console.error('❌ Error:', error);
}
}, 'borrow');Manual Loan Flow (Advanced)
// Step 1: Get quotes
const quotes = await sdk.getQuotes({
collateralAmount: '0.01',
loanAmount: '1000'
});
// Step 2: Select best quote (or use default selector)
const selectedQuote = quotes[0];
// Step 3: Execute borrow with workflow tracking.
// executeBorrow() prepares the platform wallet/session automatically if needed.
const workflowId = await sdk.executeBorrow(selectedQuote);
await sdk.trackWorkflow(workflowId, {
onStatusUpdate: (status) => {
console.log(`[${status.step}/${status.totalSteps}] ${status.label}`);
},
onDepositReady: (info) => {
console.log('Deposit to:', info.depositAddress);
},
onComplete: () => console.log('Done!')
}, 'borrow');Withdraw USDC (Gasless)
// Withdraw borrowed USDC to your wallet - gas is sponsored.
// The SDK prepares platform wallet signing automatically if needed.
const transactionId = await sdk.withdrawToEVM({
chain: ChainType.ARBITRUM,
amount: '100',
assetSymbol: 'USDC',
destinationAddress: '0x...'
});
const status = await sdk.getWithdrawStatus(transactionId);
console.log(status.data.transactionDetails?.transactionHash);Repay a Loan
await sdk.repay(loanId, '1000', {
trackWorkflow: true,
callbacks: {
onComplete: () => console.log('Repaid!')
}
});Withdraw Collateral to Bitcoin
await sdk.withdrawCollateral(loanId, '0.01', 'bc1q...', {
trackWorkflow: true
});Check Positions
// Get wallet balances
const positions = await sdk.getWalletPositions();
// Get loan history
const history = await sdk.getLoanHistory({ status: 'active' });
// Get collateral info
const info = await sdk.getLoanCollateralInfo(loanId);
console.log('Max withdrawable:', info.maxWithdrawable);Configuration
interface BorrowSDKConfig {
apiKey: string; // Required
wallet: WalletProvider; // Required
workflowPollInterval?: number; // Default: 2000ms
sessionValiditySeconds?: number; // Default: 259200 (3 days)
autoTrackWorkflows?: boolean; // Default: true
}
interface WalletProvider {
address: string;
signMessage: (message: string) => Promise<string>;
sendBitcoin?: (toAddress: string, satoshis: number) => Promise<string>;
}Wallet Integration
Works with any Bitcoin wallet that can sign messages:
// With sats-connect
import { signMessage, sendBtcTransaction } from 'sats-connect';
const wallet = {
address: bitcoinAddress,
signMessage: async (msg) => {
const res = await signMessage({ message: msg });
return res.signature;
},
sendBitcoin: async (to, amount) => {
const res = await sendBtcTransaction({
recipients: [{ address: to, amount }]
});
return res.txid;
}
};API Reference
| Method | Description |
|--------|-------------|
| setup() | Optional account preload: initialize wallet/session and fetch history |
| getQuotes(params) | Get loan quotes |
| executeBorrow(quote) | Execute borrow; prepares required wallet/session automatically |
| repay(loanId, amount, options) | Repay a loan; prepares platform wallet signing automatically |
| withdrawCollateral(loanId, amount, btcAddress) | Withdraw collateral to BTC; prepares platform wallet signing automatically |
| withdrawToEVM(params) | Create a gasless platform-wallet transfer to EVM; returns a trackable withdrawal transaction ID |
| withdrawToBitcoin(params) | Withdraw USDC to BTC; prepares platform wallet signing automatically |
| getLoanHistory(options) | Get transaction history |
| getLoanCollateralInfo(loanId) | Get loan collateral details |
| getWalletPositions() | Get wallet token balances |
Error Handling
import { BorrowSDKError, ApiError } from '@satsterminal-sdk/borrow';
try {
const workflowId = await sdk.executeBorrow(selectedQuote);
await sdk.trackWorkflow(workflowId, callbacks, 'borrow');
} catch (error) {
if (error instanceof ApiError) {
console.error('API Error:', error.statusCode, error.message);
}
}Documentation
Full documentation: docs.satsterminal.com
License
MIT
