LUSG docsLIUM FINANCE Vibenet live · Base Sepolia next Open the app →
Contents
Design

Architecture.

Three small contracts around one B20 token. The token holds balances and enforces compliance; the clock holds the books and moves the multiplier; the oracle prices bills; the desk lets anyone in and out.

Components

ComponentWhat it doesWhat it may change
LUSG (B20 Asset precompile)Balances, transfers, the multiplier, roles, policies, pause, capItself, as its roles allow
LusgClockThe basket's books: bill lots and cash. Values each lot at amortized cost. tick() schedules the next minute's multiplier; check() compares market and amortized valueLUSG's multiplier only (it holds OPERATOR_ROLE and nothing else)
BillOracleTreasury's seven bill rates and every outstanding bill by CUSIP; priceOf(cusip)Its own data, posted by the poster
LusgDeskBuy LUSG with USDV and redeem it, at the basket's value; test KYC on VibenetMints and burns LUSG; books cash in the clock; edits the KYC allowlist
Keeper (off chain)Calls tick() each minute and the daily checkNothing it can set: both calls are permissionless and compute their own values

Who holds what

LUSG tokenB20 Asset · rolesand policies Admin walletDEFAULT_ADMIN, METADATA Minter walletMINT, BURN Compliance walletPAUSE, UNPAUSE, SEIZE LusgClockOPERATOR (multiplier) LusgDeskMINT, BURN, allowlist admin Keeper walletoracle poster, ticks
Vibenet: each role is a separate throwaway wallet, and Vibenet has no Safe deployed. On Base Sepolia, the admin, minter and compliance roles move to a 2-of-3 Safe.
RightHolder on Vibenet
LUSG DEFAULT_ADMIN_ROLE, METADATA_ROLEAdmin (deployer) wallet
LUSG MINT_ROLE, BURN_ROLEMinter wallet and LusgDesk
LUSG PAUSE_ROLE, UNPAUSE_ROLE, SEIZE_ROLECompliance wallet
LUSG OPERATOR_ROLELusgClock
Clock manager (books)LusgDesk, which passes the admin wallet's calls through
KYC allowlist adminLusgDesk (staged by compliance, accepted by the desk)
Sanctions blocklist admin, seize-exempt list adminCompliance wallet
BillOracle posterKeeper wallet

The four flows

1 · Buy

wallet ── approve(desk, usd) ─────────────▶ USDV wallet ── buy(usd, minOut) ──────────────▶ LusgDesk │ price = clock.nav() / LUSG.totalSupply() │ not on allowlist? registry.updateAllowlist(+wallet) ← Vibenet test KYC │ USDV.transferFrom(wallet → desk, usd) │ clock.depositCash(usd) ← basket value rises by usd └ LUSG.mint(wallet, usd / price) ← supply rises in proportion B20 checks MINT_RECEIVER policy: allowlisted and not blocklisted

2 · The minute step

keeper ── tick() ──▶ LusgClock │ pending step still live? return false (one pending update at a time) │ at = next whole minute │ m = navAt(at) / totalSupply ← lots straight-line from cost to face, plus cash └ LUSG.updateUIMultiplier(m, at) ← takes effect at `at`, no event at the flip

3 · The daily check

Treasury CSV ──▶ keeper ── postRates(asOf, 7 rates) ──▶ BillOracle anyone ── check() ──▶ LusgClock ── priceOf(cusip) per lot ──▶ BillOracle └ gap = market / amortized − 1; |gap| > 0.5% → alerting = true

4 · Redeem

wallet ── approve(desk, lusg) ───────────▶ LUSG wallet ── redeem(lusg, minOut) ──────────▶ LusgDesk │ usdOut = lusg × price; usdOut ≤ desk's USDV? │ LUSG.transferFrom(wallet → desk) ← sender must not be blocklisted │ LUSG.burn(lusg) │ clock.withdrawCash(usdOut) └ USDV.transfer(wallet, usdOut)

Design rules the code keeps