Developer Reference
This page covers all contract addresses, function signatures, ABI fragments, React hooks, and integration patterns for building on top of UnitFlow Lending.
Contract Addresses (Arc Testnet — chainId 5042002)
| Contract | Address | Role |
|---|---|---|
| LendingPool | 0xb6cBD47e67DeF8270916A9F756526015Fabb2f1B | Core pool — all user interactions |
| LiquidationEngine | 0xc3f1024ff9ff178D7B321a28966F4bBCa7db3788 | Liquidations & health factor reads |
| InterestRateModel | 0x07AeC51d8448b4a6560ec8cB185ce396ACD2E1B9 | Rate calculation (read-only) |
| PriceOracle | 0x5bD0c70dDD1cC439E613dEF1Da7F06E1a24DB007 | 8-decimal USD prices |
| FeeDistributor | 0x4fF873fAE73a5FcDa5De06E63Ef22c32207e4F0c | Protocol fee routing |
| Configurator | 0xAa7cefeb7FD04dF05CDA389bD1fc4097BDB0b61a | Admin — risk parameters |
| uUSDC | 0xbab299155CDC34b790e2d96DdadC3Ac64708C003 | Supply token for USDC |
| uEURC | 0x6bdaa941dE3D01b97B6010b2e5eFd6439ec8D2c6 | Supply token for EURC |
| dUSDC | 0xD21Fe44773107824cee0E36F6B38A072A02024F5 | Debt token for USDC |
| dEURC | 0x169138F1551B2D4c78240e67fD0B9075d2366788 | Debt token for EURC |
| USDC | 0x3600000000000000000000000000000000000000 | Underlying |
| EURC | 0x89b50855aa3be2f677cd6303cec089b5f319d72a | Underlying |
Precision Constants
WAD = 10n ** 18n // health factor, share ratios RAY = 10n ** 27n // interest indices, rates // Token decimals: 6 (USDC & EURC) // Oracle price decimals: 8
LendingPool ABI — Key Functions
Write Functions
// Supply assets into the pool supply( asset: address, // underlying token address amount: uint256, // token amount (6 decimals) onBehalfOf: address // recipient of uTokens ) external // Withdraw supplied assets withdraw( asset: address, amount: uint256, // pass type(uint256).max to withdraw all to: address ) external returns (uint256 withdrawn) // Borrow against collateral borrow( asset: address, amount: uint256, onBehalfOf: address ) external // Repay borrowed debt repay( asset: address, amount: uint256, // pass type(uint256).max to repay all onBehalfOf: address ) external returns (uint256 repaid)
Read Functions
// Account summary — all values 8-decimal USD getUserAccountData(user: address) external view returns ( totalCollateralUSD: uint256, totalDebtUSD: uint256, healthFactor: uint256, // WAD-scaled availableBorrowsUSD: uint256 ) // Full reserve state for an asset reserves(asset: address) external view returns ( uToken: address, debtToken: address, underlyingAsset: address, liquidityIndex: uint256, // RAY variableBorrowIndex: uint256, // RAY currentLiquidityRate: uint256, // RAY per-second currentBorrowRate: uint256, // RAY per-second totalLiquidity: uint256, // 6-decimal tokens totalBorrows: uint256, // 6-decimal tokens reserveBalance: uint256, lastUpdateTimestamp: uint256, active: bool, frozen: bool ) // Utilisation ratio for an asset getUtilizationRate(asset: address) external view returns (uint256) // RAY // Per-user, per-asset collateral balance userCollateral(user: address, asset: address) external view returns (uint256)
LiquidationEngine ABI
// Returns WAD-scaled health factor for any address getHealthFactor(user: address) external view returns (uint256) // Execute a liquidation liquidate( borrower: address, collateralAsset: address, // asset to receive debtAsset: address, // asset to repay debtToCover: uint256 // ≤ 50% of outstanding debt ) external
uToken / dToken ABI
// Standard ERC-20 — balanceOf returns scaled amount balanceOf(account: address) external view returns (uint256) // dTokens are non-transferable (transfer() reverts)
React Hooks
All hooks live under src/hooks/lending/ and are re-exported from src/hooks/lending/index.ts.
useLendingPool()
import { useLendingPool } from '@/hooks/lending'
const { reserves, totalTvlUsd, isLoading, isError, refetch } = useLendingPool()
// reserves: ReserveInfo[]
interface ReserveInfo {
symbol: 'USDC' | 'EURC'
totalLiquidity: bigint
totalBorrows: bigint
totalLiquidityFormatted: string // human-readable
totalBorrowsFormatted: string
supplyApy: number // percent, e.g. 4.25
borrowApy: number
utilizationRate: number // 0–100
liquidityIndex: bigint // RAY
borrowIndex: bigint // RAY
active: boolean
}Fetches both reserves in a single multicall. Refreshes every 15 seconds. Converts RAY per-second rates to annual percentages using rate × 31_536_000 / RAY × 100.
useUserPosition(address)
import { useUserPosition } from '@/hooks/lending'
const position = useUserPosition(address)
interface UserPositionData {
totalCollateralUsd: number
totalDebtUsd: number
healthFactor: bigint // WAD
healthFactorDisplay: string // "1.45" or "∞"
availableBorrowsUsd: number
borrowingPowerUsedPct: number // 0–100
netWorthUsd: number
positions: AssetPosition[]
isLoading: boolean
isError: boolean
refetch: () => void
}
interface AssetPosition {
symbol: 'USDC' | 'EURC'
supplied: bigint
suppliedFormatted: string
borrowed: bigint
borrowedFormatted: string
}Batches getUserAccountData, both reserves calls, and both uToken balanceOf calls into a single multicall. Refreshes every 12 seconds. Pass undefined when no wallet is connected — the hook returns zeroed data without making any RPC calls.
useSupply(assetSymbol, amount)
import { useSupply } from '@/hooks/lending'
const { write, state, isPending, isSuccess, isError, needsApproval, reset } =
useSupply('USDC', '100')
// write() → approve (if needed) then supply
// state.step: 'idle' | 'approving' | 'supplying' | 'success' | 'error'
// state.txHash: string | undefined
// needsApproval: boolean (current allowance < amount)useBorrow(assetSymbol, amount)
const { write, state, isPending, isSuccess, isError, reset } =
useBorrow('EURC', '50')
// state.step: 'idle' | 'borrowing' | 'success' | 'error'
// No approval needed — pool sends tokens to youuseRepay(assetSymbol, amount)
const { write, state, isPending, isSuccess, isError, needsApproval, reset } =
useRepay('USDC', 'max') // pass 'max' to repay full debt
// 'max' maps to type(uint256).max on-chain
// state.step: 'idle' | 'approving' | 'repaying' | 'success' | 'error'useWithdraw(assetSymbol, amount)
const { write, state, isPending, isSuccess, isError, reset } =
useWithdraw('USDC', 'max') // 'max' → type(uint256).max
// state.step: 'idle' | 'withdrawing' | 'success' | 'error'
// No approval needed — pool burns your uTokensuseLiquidatablePositions()
const { positions, isLoading } = useLiquidatablePositions()
interface LiquidatablePosition {
borrower: `0x${string}`
healthFactor: bigint
healthFactorDisplay: string
collateralAsset: 'USDC' | 'EURC'
collateralAmount: bigint
collateralFormatted: string
debtAsset: 'USDC' | 'EURC'
debtAmount: bigint
debtFormatted: string
debtToCover: bigint // 50% of debt
}Scans Supply events from the last 50,000 blocks to discover borrowers, then filters to HF < 1.2 via a batched multicall. Refreshes every 12 seconds.
useLiquidate(borrower, collateralAsset, debtAsset, debtToCover)
const { write, state, isPending, isSuccess, isError, reset } = useLiquidate(
borrower, // 0x...
collateralAsset, // underlying address (not uToken)
debtAsset, // underlying address
debtToCover // bigint — 50% of debt
)
// state.step: 'idle' | 'approving' | 'liquidating' | 'success' | 'error'Configuration File
// src/lib/contracts/lending/config.ts
export const LENDING_CHAIN_ID = 5042002
export const LENDING_ASSETS: Record<'USDC' | 'EURC', {
symbol: 'USDC' | 'EURC'
decimals: number // 6
underlying: `0x${string}`
uToken: `0x${string}`
dToken: `0x${string}`
fallbackPrice: bigint // 8-decimal USD
}>Integration Example — Read Pool State
import { createPublicClient, http } from 'viem'
import { LENDING_POOL_ABI } from '@/lib/contracts/lending/abis'
const client = createPublicClient({
chain: arcTestnet,
transport: http('https://rpc.arc-testnet.io'),
})
const [usdcReserve, eurcReserve] = await client.multicall({
contracts: [
{
address: '0xb6cBD47e67DeF8270916A9F756526015Fabb2f1B',
abi: LENDING_POOL_ABI,
functionName: 'reserves',
args: ['0x3600000000000000000000000000000000000000'],
},
{
address: '0xb6cBD47e67DeF8270916A9F756526015Fabb2f1B',
abi: LENDING_POOL_ABI,
functionName: 'reserves',
args: ['0x89b50855aa3be2f677cd6303cec089b5f319d72a'],
},
],
})
// Convert RAY per-second rate to APR%
const SECONDS_PER_YEAR = 31_536_000n
const RAY = 10n ** 27n
const supplyApr = Number(usdcReserve.result[5] * SECONDS_PER_YEAR * 10_000n / RAY) / 100Events
// LendingPool emits: event Supply( address indexed asset, address indexed user, uint256 amount, uint256 index ) event Withdraw( address indexed asset, address indexed user, address indexed to, uint256 amount ) event Borrow( address indexed asset, address indexed user, uint256 amount, uint256 borrowRate, uint256 index ) event Repay( address indexed asset, address indexed user, address indexed repayer, uint256 amount ) // LiquidationEngine emits: event Liquidation( address indexed collateralAsset, address indexed debtAsset, address indexed borrower, uint256 debtToCover, uint256 collateralSeized, address liquidator )