npm package: @satsterminal-sdk/borrow
Description: Borrowing module for SatsTerminal SDK
Author: Sats Terminal
Latest version: 1.6.27

# @satsterminal-sdk/borrow

Borrow stablecoins against your Bitcoin collateral. Gasless, non-custodial, powered by smart accounts.

## Installation

```bash
npm install @satsterminal-sdk/borrow
```

Or use the aggregator if you also need swaps:

```typescript
import { createClient } from 'satsterminal-sdk';
import { ChainType } from '@satsterminal-sdk/borrow';

const { borrow } = createClient({
  apiKey: process.env.API_KEY!,
  borrow: { chain: ChainType.BASE, wallet }
});
```

## Quick Start

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

const sdk = new BorrowSDK({
  apiKey: 'your-api-key',
  // baseUrl optional (defaults to https://api.satsterminal.com)
  chain: ChainType.ARBITRUM, // or ChainType.BASE, ChainType.ETHEREUM, ChainType.BSC
  wallet: {
    address: 'bc1q...',
    signMessage: async (msg) => wallet.signMessage(msg),
    sendBitcoin: async (to, sats) => wallet.sendBitcoin(to, sats)
  }
});

// Get a loan
const result = await sdk.getLoan({
  collateralBTC: 0.1,
  loanAmountUSD: 5000,
  onDepositReady: (info) => console.log(`Deposit ${info.amount} BTC to ${info.address}`),
  onComplete: () => console.log('Loan complete!')
});
```

## 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

```typescript
interface BorrowSDKConfig {
  apiKey: string;                    // Required: Your API key
  baseUrl?: string;                  // Optional: API base URL (defaults to https://api.satsterminal.com)
  chain: ChainType;                  // Required: Chain to use
  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:

```typescript
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

```typescript
const sdk = new BorrowSDK({
  apiKey: process.env.SATSTERMINAL_API_KEY!,
  chain: ChainType.ARBITRUM,
  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
Before borrowing, you need to set up the SDK by calling `setup()`. This initializes the smart account and creates a session.

### 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:
1. Validates the amounts against the current loan state
2. Creates a repay transaction on the backend
3. Starts a Temporal workflow that handles the entire process
4. 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
  ↓
COMPLETED
```

#### Scenario 2: Withdraw Collateral Only (No Repay)

```
INITIALIZING
  ↓
WITHDRAWING_COLLATERAL
  ↓
TRANSFERRING_TO_PLATFORM_WALLET
  ↓
BRIDGE_INITIALIZING → ... → BRIDGE_COMPLETED
  ↓
COMPLETED
```

#### Scenario 3: Repay and Withdraw (Both)

```
INITIALIZING
  ↓
TRANSFERRING_TO_KERNEL (User deposits USDC)
  ↓
REPAYING_LOAN
  ↓
WITHDRAWING_COLLATERAL
  ↓
TRANSFERRING_TO_PLATFORM_WALLET
  ↓
BRIDGE_INITIALIZING → ... → BRIDGE_COMPLETED
  ↓
COMPLETED
```

### Important Notes

1. **USDC Deposit Required:** If repaying, you must deposit USDC to the smart account address before the workflow can proceed past `TRANSFERRING_TO_KERNEL`.

2. **Max Withdrawable:** Before withdrawing, check `getLoanCollateralInfo()` to get the accurate `maxWithdrawable` amount (accounts for fees and health factor).

3. **Workflow Tracking:** Use callbacks to track progress:
   ```typescript
   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)
     }
   });
   ```

4. **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.

5. **Partial Repays:** You can repay any amount less than the total debt. The workflow will handle partial repayment correctly.

6. **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.

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

const sdk = new BorrowSDK({
  apiKey: 'your-api-key',
  chain: ChainType.ARBITRUM,
  wallet: walletProvider
});
```

### Wallet Connection

#### `setup(): Promise<UserStatus>`

Initializes the smart account and creates a session. Must be called before borrowing.

```typescript
const userStatus = await sdk.setup();
console.log('Smart Account:', userStatus.smartAccountAddress);
console.log('Is Deployed:', userStatus.isDeployed);
```

**Returns:** `UserStatus` object with connection details.

### Loan Operations

#### `getLoan(options: GetLoanOptions): Promise<LoanResult>`

High-level method that handles the entire loan process: setup, quoting, and borrowing.

```typescript
const result = await sdk.getLoan({
  collateralBTC: 0.01,
  loanAmountUSD: 1000,
  ltv: 70,
  destinationAddress: '0x...', // Optional: Where to send the loan
  onStatusUpdate: (status) => {
    console.log('Status:', status.label, status.description);
  },
  onDepositReady: (info) => {
    console.log('Deposit to:', info.depositAddress);
    console.log('Amount:', info.amount, 'BTC');
  },
  onComplete: (result) => {
    console.log('Loan completed!', result);
  },
  onError: (error) => {
    console.error('Error:', error);
  }
});
```

**Options:**
- `collateralBTC: number` - Amount of BTC to use as collateral
- `loanAmountUSD: number` - Desired loan amount in USD
- `ltv?: number` - Loan-to-value ratio (default: 70)
- `term?: number` - Loan term in days (default: 30)
- `destinationAddress?: string` - Address to receive the loan
- `quoteSelector?: (quotes: Quote[]) => Quote` - Custom quote selection logic
- `onStatusUpdate?: (status: WorkflowStatus) => void` - Status update callback
- `onDepositReady?: (info: DepositInfo) => void` - Deposit ready callback
- `onComplete?: (result: any) => void` - Completion callback
- `onError?: (error: string) => void` - Error callback

**Returns:** `LoanResult` with workflow ID and tracker.

#### `setup(): Promise<UserStatus>`

Sets up the SDK for loan operations. Must be called before `getQuotes()` or `executeBorrow()`.

```typescript
const userStatus = await sdk.setup();
```

#### `getQuotes(request: QuoteRequest): Promise<Quote[]>`

Fetches loan quotes from available protocols.

```typescript
const quotes = await sdk.getQuotes({
  collateralAmount: '0.01',
  loanAmount: '1000',
  ltv: 70,
  term: 30 // Optional: Loan term in days (default: 30)
});

// Select a quote (or use default selector)
const selectedQuote = quotes[0]; // Or use custom selector
```

**Returns:** Array of `Quote` objects from different protocols.

#### `executeBorrow(quote: Quote, options?: BorrowOptions): Promise<string>`

Executes a borrow transaction with the selected quote.

```typescript
const workflowId = await sdk.executeBorrow(selectedQuote, {
  destinationAddress: '0x...',
  trackWorkflow: true,
  callbacks: {
    onStatusUpdate: (status) => console.log(status),
    onComplete: () => console.log('Done!')
  }
});
```

**Returns:** Workflow ID string.

### Repay Operations

#### `repay(originalBorrowId: string, repayAmount: string, options?: RepayOptions): Promise<string>`

Repay a loan (full or partial).

```typescript
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 ID
- `repayAmount: string` - Amount to repay in USDC (use '0' for collateral-only withdrawal)
- `options?: RepayOptions`
  - `useCollateral?: boolean` - Use collateral for repayment
  - `collateralToWithdraw?: string` - Amount of collateral to withdraw
  - `userBtcWithdrawAddress?: string` - BTC address for withdrawal
  - `trackWorkflow?: boolean` - Track the workflow
  - `callbacks?: WorkflowCallbacks` - Workflow callbacks

**Returns:** Transaction ID string.

#### `withdrawCollateral(originalBorrowId: string, collateralAmount: string, btcWithdrawAddress: string, options?: WithdrawOptions): Promise<string>`

Withdraw collateral from a loan.

```typescript
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.

```typescript
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.

```typescript
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).

```typescript
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.

```typescript
// 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.

```typescript
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.

```typescript
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.

```typescript
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.

```typescript
const status = await sdk.getStatus('workflow-id');
```

#### `clearSession(): void`

Clear the current session.

```typescript
sdk.clearSession();
```

## Examples

### Complete Loan Flow

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

const sdk = new BorrowSDK({
  apiKey: 'your-api-key',
  chain: ChainType.ARBITRUM,
  wallet: walletProvider
});

// Get a loan with automatic workflow tracking
const result = await sdk.getLoan({
  collateralBTC: 0.01,
  loanAmountUSD: 1000,
  ltv: 70,
  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: (result) => {
    console.log('✅ Loan completed!');
    console.log('Workflow ID:', result.workflowId);
  },
  onError: (error) => {
    console.error('❌ Error:', error);
  }
});

// Stop tracking when done
result.stop();
```

### Manual Loan Flow (Advanced)

```typescript
// Step 1: Setup
await sdk.setup();

// Step 2: Get quotes
const quotes = await sdk.getQuotes({
  collateralAmount: '0.01',
  loanAmount: '1000',
  ltv: 70
});

// Step 3: Select best quote (or use default selector)
const selectedQuote = quotes[0];

// Step 4: Execute borrow with workflow tracking
const workflowId = await sdk.executeBorrow(selectedQuote, {
  trackWorkflow: true,
  callbacks: {
    onStatusUpdate: (status) => {
      console.log(`[${status.step}/${status.totalSteps}] ${status.label}`);
    },
    onDepositReady: (info) => {
      console.log('Deposit to:', info.depositAddress);
    },
    onComplete: () => console.log('Done!')
  }
});
```

### Withdraw USDC (Gasless)

```typescript
// Withdraw borrowed USDC to your wallet - gas is sponsored
const txHash = await sdk.withdrawToEVM({
  chain: ChainType.ARBITRUM,
  amount: '100',
  destinationAddress: '0x...'
});
```

### Repay a Loan

```typescript
await sdk.repay(loanId, '1000', {
  trackWorkflow: true,
  callbacks: {
    onComplete: () => console.log('Repaid!')
  }
});
```

### Withdraw Collateral to Bitcoin

```typescript
await sdk.withdrawCollateral(loanId, '0.01', 'bc1q...', {
  trackWorkflow: true
});
```

### Check Positions

```typescript
// 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

```typescript
interface BorrowSDKConfig {
  apiKey: string;              // Required
  baseUrl: string;             // Required
  chain: ChainType;            // Required: ARBITRUM | BASE
  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:

```typescript
// 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()` | Initialize SDK and create session |
| `getLoan(options)` | Get a loan (handles full flow) |
| `getQuotes(params)` | Get loan quotes |
| `executeBorrow(quote)` | Execute borrow with quote |
| `repay(loanId, amount, options)` | Repay a loan |
| `withdrawCollateral(loanId, amount, btcAddress)` | Withdraw collateral to BTC |
| `withdrawToEVM(params)` | Withdraw USDC to EVM (gasless) |
| `withdrawToBitcoin(params)` | Withdraw USDC to BTC |
| `getLoanHistory(options)` | Get transaction history |
| `getLoanCollateralInfo(loanId)` | Get loan collateral details |
| `getWalletPositions()` | Get wallet token balances |

## Error Handling

```typescript
import { BorrowSDKError, ApiError } from '@satsterminal-sdk/borrow';

try {
  await sdk.getLoan({ ... });
} catch (error) {
  if (error instanceof ApiError) {
    console.error('API Error:', error.statusCode, error.message);
  }
}
```

## Documentation

Full documentation: [docs.satsterminal.com](https://docs.satsterminal.com)

## License

MIT