Skip to main content

Create and restore a Simple Loan

Create a Simple Loan, show the deposit target, and restore the loan by reference.

Before you start

Complete the following preparation:

  • Install @liquidium/client.
  • Prepare an Ethereum address to receive the borrowed USDC.
  • Prepare a Bitcoin address to receive a collateral refund.
  • Confirm that your runtime provides fetch and BigInt.
Mainnet operation

new LiquidiumClient() uses Liquidium mainnet defaults. This example creates a real Simple Loan. Review every amount and destination before you send funds.

The example uses 50_000n satoshis (0.0005 BTC) as collateral and 9_000_000n base units (9 USDC) as the borrow amount. Replace ETHEREUM_BORROW_ADDRESS with the Ethereum address that receives the USDC. Replace BITCOIN_REFUND_ADDRESS with the Bitcoin address that receives a collateral refund.

Before you create the loan, make sure that the borrow amount meets the SDK minimum for the selected asset. If your UI shows the minimum, use getMinimumBorrowAmount(borrowPool.asset).

Create and restore the loan:

import { Asset, Chain, LiquidiumClient, type Pool } from "@liquidium/client";

const client = new LiquidiumClient();

const [pools, prices] = await Promise.all([
client.market.listPools(),
client.market.getAssetPrices(),
]);

const collateralPool = requirePool(pools, Asset.BTC, Chain.BTC);
const borrowPool = requirePool(pools, Asset.USDC, Chain.ETH);

const collateralAmount = 50_000n;
const borrowAmount = 9_000_000n;

const ltv = client.quote.calculateLtv(
{
collateralPoolId: collateralPool.id,
borrowPoolId: borrowPool.id,
collateralAmount,
borrowAmount,
},
pools,
prices
);

if (ltv.validationErrors.length > 0) {
throw new Error(ltv.validationErrors.map((error) => error.message).join(" "));
}

const loan = await client.simpleLoans.create({
collateral: {
poolId: collateralPool.id,
asset: Asset.BTC,
amount: collateralAmount,
},
borrow: {
poolId: borrowPool.id,
asset: Asset.USDC,
amount: borrowAmount,
chain: Chain.ETH,
destination: "ETHEREUM_BORROW_ADDRESS",
},
refund: {
chain: Chain.BTC,
destination: "BITCOIN_REFUND_ADDRESS",
},
ltvMaxBps: ltv.maxAllowedLtvBps,
depositWindowSeconds: 3_600n,
});

const initialDeposit = loan.initialDeposit.targets[Chain.BTC];
if (!initialDeposit) {
throw new Error("Missing BTC initial-deposit target.");
}

console.log("Save this loan reference:", loan.ref);
console.log("Send collateral amount:", initialDeposit.amount.toString());
console.log("Send collateral to:", initialDeposit.target.address);

const restoredLoan = await client.simpleLoans.get({ ref: loan.ref });
const repayment = restoredLoan.repayment.targets[Chain.ETH];

console.log("Loan status:", restoredLoan.status);
if (repayment && repayment.amount > 0n) {
console.log("Repay amount:", repayment.amount.toString());
console.log("Repay target:", repayment.target.address);
} else {
console.log("No repayment due yet.");
}

function requirePool(
pools: Pool[],
asset: Pool["asset"],
chain: Pool["chain"]
): Pool {
const pool = pools.find(
(candidatePool) =>
candidatePool.asset === asset && candidatePool.chain === chain
);

if (!pool) {
throw new Error(`Missing ${chain}/${asset} pool.`);
}

if (pool.frozen) {
throw new Error(`${chain}/${asset} pool is frozen.`);
}

return pool;
}

Save loan.ref. Use it for refreshes, status pages, and support links.

If create(...) throws SimpleLoanCreatedError, recover the loan with the error's loanId or ref. Do not call create(...) again. For recovery code, see Handle SDK errors.