Liquidium
Liquidium
Welcome to Liquidium
Quick start
Resources
Technical Documentation
Technical Concepts
Architecture
Operations
Security
Security, governance & riskAtomicity & Write-Ahead LoggingAuthenticationUse Liquidium without the app
SDK
Main site
Technical DocumentationSecurity

Use Liquidium without the app

Check, repay, and withdraw Advanced positions directly from the canisters if the app is unavailable.

Your Advanced positions live in Liquidium's canisters (smart contracts) on the Internet Computer, not on Liquidium's servers. If app.liquidium.fi is unavailable, you can still read your position, repay debt, and, if you sign in with a Bitcoin or Ethereum wallet, withdraw collateral by calling the canisters directly.

This page is for technical users. The examples use dfx with --network ic. Read-only queries can use --identity anonymous. Transfers and withdrawals need a dfx identity that holds the funds or can submit the call. If you can use the app, use the app.

Sending the wrong asset, using the wrong subaccount, or calling the wrong canister can lose funds. Test with a small amount first and double-check every canister ID against the table below.

Mainnet canister IDs

Canister

ID

Role

Lending Canister

hyk4r-jqaaa-aaaar-qb4ca-cai

Profiles, positions, health factor, withdrawals, borrows, liquidations

BTC Pool

hkmli-faaaa-aaaar-qb4ba-cai

BTC liquidity (held as ckBTC)

ETH Pool

qcg7y-syaaa-aaaar-qb75q-cai

ETH liquidity (held as ckETH)

USDC Pool

6sna2-oiaaa-aaaar-qb66q-cai

USDC liquidity (held as ckUSDC)

USDT Pool

hnnn4-iyaaa-aaaar-qb4bq-cai

USDT liquidity (held as ckUSDT)

ICP Pool

r2pk3-4yaaa-aaaar-qb7zq-cai

Native ICP liquidity

Deposit Accounts

z5jz7-nyaaa-aaaar-qb6pq-cai

Ethereum deposit and repay addresses

Simple Loans

u5rm3-niaaa-aaaar-qb7eq-cai

Accountless Simple Loans

Identity Anchor

cicgv-7yaaa-aaaar-qca2a-cai

Internet Identity origin for Liquidium

The pools use these ledgers and minters, run by the Internet Computer:

Asset

Ledger

Minter

ckBTC

mxzaz-hqaaa-aaaar-qaada-cai

mqygn-kiaaa-aaaar-qaadq-cai

ckETH

ss2fx-dyaaa-aaaar-qacoq-cai

sv3dd-oaaaa-aaaar-qacoa-cai

ckUSDC

xevnm-gaaaa-aaaar-qafnq-cai

sv3dd-oaaaa-aaaar-qacoa-cai

ckUSDT

cngnf-vqaaa-aaaar-qag4q-cai

sv3dd-oaaaa-aaaar-qacoa-cai

ICP

ryjl3-tyaaa-aaaaa-aaaba-cai

None

You can also call these methods from the Candid UI on the ICP dashboard.

1. Find your profile

Every Advanced account is a profile principal. Look it up with a wallet address you signed in with:

plaintext
dfx canister call --network ic --identity anonymous --query \
  hyk4r-jqaaa-aaaar-qb4ca-cai get_wallet_profile '("bc1q...your-address")'

The result is opt principal "...". Enter the address exactly as your wallet shows it. Ethereum addresses are matched case-sensitively, so if you get null, try the checksummed (mixed-case) form.

To list every wallet linked to a profile, call get_profile_wallets '(principal "<profile>")'.

2. Read your position

Health factor and totals for the whole profile:

plaintext
dfx canister call --network ic --identity anonymous --query \
  hyk4r-jqaaa-aaaar-qb4ca-cai get_health_factor '(principal "<profile>")'
  • The first value is the health factor with three decimals: 1000 means 1.0. Below 1000 the position can be liquidated.
  • collateral and debt are values in USDT terms with 27 decimals. With no debt, the health factor is the maximum nat value.
  • get_profile_stats '(principal "<profile>")' returns the same totals without the health factor.

One pool at a time:

plaintext
dfx canister call --network ic --identity anonymous --query \
  hyk4r-jqaaa-aaaar-qb4ca-cai get_position '(principal "<profile>", principal "<pool-id>")'

deposited_native_now is your supply and debt_native_now is your current debt, both in the asset's smallest unit: sats for BTC, wei for ETH, 6 decimals for USDC and USDT, and e8s for ICP.

3. Repay

Repayments are transfers into a repay subaccount that belongs to your profile on the borrowed asset's pool. The subaccount is derived from your profile, so it never changes. Get it from the pool:

plaintext
dfx canister call --network ic --identity anonymous --query \
  hnnn4-iyaaa-aaaar-qb4bq-cai get_inflow_subaccount '(variant { Repayment = principal "<profile>" })'

Use the pool of the asset you borrowed. The reply is a 32-byte blob. Use Deposit instead of Repayment only if you want to supply collateral.

ckBTC, ckETH, ckUSDC, ckUSDT

Send the ck version of the borrowed asset with an ICRC-1 transfer to the pool and your repay subaccount. The pool scans its ledger about once a minute and credits the repayment on its own.

plaintext
dfx canister call --network ic <ledger-id> icrc1_transfer '(record {
  to = record { owner = principal "<pool-id>"; subaccount = opt blob "<repay-subaccount>" };
  amount = <amount-in-smallest-unit>;
})'

Native ICP

Make the same ICRC-1 transfer on the ICP ledger to the ICP Pool. The ICP Pool does not scan for new transfers by itself. After the transfer, ask it to process your subaccount:

plaintext
dfx canister call --network ic r2pk3-4yaaa-aaaar-qb7zq-cai check_subaccount '(blob "<repay-subaccount>")'

Native BTC

Your BTC repay address is a ckBTC deposit address owned by the BTC Pool. It is the same address the app shows. To get it without the app, call the ckBTC minter:

plaintext
dfx canister call --network ic mqygn-kiaaa-aaaar-qaadq-cai get_btc_address '(record {
  owner = opt principal "hkmli-faaaa-aaaar-qb4ba-cai";
  subaccount = opt blob "<repay-subaccount>";
})'

Send BTC to that address. After the ckBTC minter's required confirmations (currently 4), call update_balance with the same owner and subaccount to mint the ckBTC. The BTC Pool then picks it up like a ckBTC repayment. The minter deducts its own fee.

Native ETH, USDC, USDT

The Ethereum repay addresses shown in the app are processed with help from Liquidium's backend. If the app is down, repay with the matching ck asset over ICP instead (see above).

Check debt_native_now right before you send. Interest keeps accruing, so a full repayment needs slightly more. Excess above the pool's refund minimum (overpay_refund_min in get_pool) is parked in a recovery balance for your profile, and smaller excess is kept as a fee. Nothing is sent back to the sending address. Contact support to recover an overpayment.

4. Withdraw

If you signed in with a Bitcoin or Ethereum wallet, a withdrawal is a message signed by that wallet and sent straight to the Lending Canister. Liquidium holds no key that can sign it for you.

The easiest way is the public Liquidium SDK. client.lending.withdraw(...) builds the message, asks your wallet to sign it, and submits it to the canister. See the SDK docs.

To do it by hand:

  1. Read your wallet's nonce: get_nonce '("<wallet-address>")' on the Lending Canister.
  2. Build this exact message, with Unix-seconds expiry no more than 5 minutes ahead:
plaintext
Liquidium: Withdraw Assets

Action: Withdraw from pool
Pool ID: <pool-id>
Amount: <amount-in-smallest-unit>
Address:<destination-address>
Expires: <unix-seconds>
Nonce: <nonce>
  1. Sign it with your wallet. Bitcoin wallets can use BIP-322 or legacy message signing. Ethereum wallets use personal_sign (EIP-191). Encode the signature as hex without a 0x prefix. Bitcoin wallets often return base64, so convert it.
  2. Call withdraw on the Lending Canister with exactly the values you signed:
plaintext
dfx canister call --network ic hyk4r-jqaaa-aaaar-qb4ca-cai withdraw '(principal "<profile>", record {
  data = record {
    pool_id = principal "<pool-id>";
    amount = <amount-in-smallest-unit>;
    account = variant { External = "<destination-address>" };
    expiry_timestamp = <unix-seconds>;
  };
  signature_info = variant { Wallet = record {
    signature = "<hex-signature>";
    chain = variant { BTC };
    account = "<wallet-address>";
  } };
})'

Use chain = variant { ETH } for an Ethereum wallet. External sends the native asset to a Bitcoin or Ethereum address. To receive the ck asset on ICP instead, use account = variant { Native = principal "<principal>" } and the message line Principal:<principal>. The ICP Pool does not take External; use Native, Icrc, or AccountIdentifier (message lines Principal:..., Icrc:..., AccountId:...). For Bitcoin or Ethereum destinations, withdraw at least 5,000 sats, 0.005 ETH, or 1 USDC or USDT; smaller amounts can get stuck. The withdrawal must keep your health factor at or above 1.0, and it fails while the pool is frozen or prices are stale. See Security, governance & risk.

Internet Identity

Internet Identity profiles live in the same canisters, and Liquidium holds no key that can act for you. You can check your position and repay with the steps above, as long as you know your profile principal. Linking a Bitcoin or Ethereum wallet to your profile lets you look it up with get_wallet_profile.

Withdrawing currently depends on the website. Internet Identity only gives you your Liquidium identity on websites approved by Liquidium's Identity Anchor canister cicgv-7yaaa-aaaar-qca2a-cai. Today the only approved origin is https://app.liquidium.fi, and adding another website needs the Liquidium multisig. If you want to be able to withdraw without the app, link a supported Bitcoin or Ethereum wallet to your profile.

Simple Loans

Simple Loans work differently. Each loan's position sits in the Lending Canister, but it is controlled by the Simple Loans canister, which signs for the loan with its own key. Liquidium's service tells that canister when a deposit or full repayment has landed, and the canister then opens the loan or returns the collateral. You cannot release Simple Loan collateral with your own wallet. Keep your Loan ID and receipt, and contact support if a Simple Loan looks stuck.

Authentication

Previous Page

SDK

Next Page

On this page

Mainnet canister IDs1. Find your profile2. Read your position3. RepayckBTC, ckETH, ckUSDC, ckUSDTNative ICPNative BTCNative ETH, USDC, USDT4. WithdrawInternet IdentitySimple Loans