Borrowing

Request a loan by depositing ETH or BTC collateral. The SDK handles WETH wrapping, collateral approval, ECDH key generation, and event parsing automatically.

Full Borrowing Flow

import { ethers } from 'ethers';
import { ThetanutsClient } from '@thetanuts-finance/thetanuts-client';

const provider = new ethers.JsonRpcProvider('https://mainnet.base.org');
const signer = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const client = new ThetanutsClient({ chainId: 8453, provider, signer });

// Step 1: Browse available strikes
const groups = await client.loan.getStrikeOptions('ETH', {
  minDurationDays: 30,
  maxStrikes: 10,
  sortOrder: 'highestStrike',
  maxApr: 20,
});

for (const group of groups) {
  console.log(`\n${group.expiryFormatted}`);
  for (const opt of group.options) {
    const tag = opt.isPromo ? ' [PROMO]' : '';
    console.log(`  ${opt.strikeFormatted} | APR: ${opt.effectiveApr}%${tag}`);
  }
}

// Step 2: Calculate costs for a specific strike
const selected = groups[0].options[0];
const calc = client.loan.calculateLoan({
  depositAmount: '1.0',
  underlying: 'ETH',
  strike: selected.strike,
  expiryTimestamp: selected.expiry,
  askPrice: selected.askPrice,
  underlyingPrice: selected.underlyingPrice,
});

if (!calc) throw new Error('Invalid loan parameters');

console.log(`Receive:      ${calc.formatted.receive} USDC`);
console.log(`Repay:        ${calc.formatted.repay} USDC`);
console.log(`Option cost:  ${calc.formatted.optionCost} USDC`);
console.log(`Borrow fee:   ${calc.formatted.capitalCost} USDC`);
console.log(`Protocol fee: ${calc.formatted.protocolFee} USDC`);
console.log(`APR:          ${calc.formatted.apr}%`);
console.log(`Promo:        ${calc.isPromo}`);

// Step 3: Submit the loan request
const result = await client.loan.requestLoan({
  underlying: 'ETH',
  collateralAmount: '1.0',
  strike: selected.strike,
  expiryTimestamp: selected.expiry,
  minSettlementAmount: calc.finalLoanAmount,
  // keepOrderOpen is deprecated as of v0.2.1 (Base_r12) and ignored at the
  // contract level — the contract no longer supports converting an unfilled
  // RFQ into a limit order. The field remains in LoanRequest for source
  // compatibility but its value has no on-chain effect.
});

console.log(`TX: ${result.receipt.hash}`);
console.log(`Quotation ID: ${result.quotationId}`);
console.log(`Public Key: ${result.keyPair.compressedPublicKey}`);

Auto-Wrapping ETH

When borrowing with ETH, requestLoan() automatically checks your WETH balance. If it's insufficient, it wraps the difference from native ETH before submitting the loan request.

No auto-wrapping happens for BTC (cbBTC). You must hold sufficient cbBTC.


Accept an Offer

When a market maker sends an encrypted offer during the RFQ auction, decrypt it and accept:


Cancel a Loan Request

Cancel before any offer is accepted to reclaim your collateral:


Query Loan State

On-Chain State

From Indexer


At Expiry

Once the option reaches expiry, you have a 1-hour exercise window. Three choices:

Exercise (Repay and Reclaim)

Repay the owed USDC amount and get your collateral back:

Walk Away (Keep USDC)

If the collateral has dropped below the strike price, it may be better to keep the borrowed USDC:

Swap and Exercise

Swap collateral to USDC via a DEX aggregator, then exercise in one transaction. Useful when you want to repay without holding USDC:


Encode for External Wallets

Generate transaction calldata for use with viem, wagmi, or any wallet library:


See Also

Last updated