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

Buy and redeem.

LusgDesk lets anyone subscribe to LUSG with USDV and redeem it back, at the basket's current value. It is the clock's manager, so every trade is booked in the same books the multiplier is computed from.

The price, and why trades don't move it

price   = clock.nav() × 1e18 / LUSG.totalSupply()        // USD per LUSG, 18 decimals
buy:      lusgOut = usdIn × 1e12 × 1e18 / price;  then clock.depositCash(usdIn);  LUSG.mint(buyer, lusgOut)
redeem:   usdOut  = lusgIn × price / 1e18 / 1e12; then LUSG.burn(lusgIn);       clock.withdrawCash(usdOut)

Value and supply move together, so the price per LUSG is unchanged by a buy or a redemption (tested to rounding dust). Both quotes round down, in the basket's favour, and both calls take a minimum-out to guard against the price moving between quote and execution.

The liquidity buffer

┌─ FOUND IN TESTING ─┐A buyer who redeems later is owed their share of the bills' accrual, not just what they paid. Twenty days after a $2,000 buy the desk owed $2,004.54 but held only the $2,000. The bills, which earned the difference, are not dollars in the desk.

The fix mirrors a fund: the treasury keeps a cash buffer at the desk (fundLiquidity) for redemptions. It backs cash the clock already counts, so nothing is booked twice. Redemptions are capped by what the desk holds, and the app shows that figure live. On Vibenet the buffer started at 3,000 USDV (0xc20c6e5c…e6b2 →).

In production, redemptions beyond the buffer would be met by bills maturing on the ladder, or by selling a bill, outside this contract.

Test KYC, on Vibenet only

The desk is the admin of LUSG's KYC allowlist. A buyer who isn't on it is added in the same transaction as their first buy, standing in for a real KYC check. The sanctions blocklist still applies: LUSG checks the combined policy on every mint and transfer, so a blocklisted wallet can neither buy nor redeem (both tested).

Limits

LimitValue on Vibenet
Largest buy per transaction10,000 USDV (maxBuy, owner-set)
Largest redemptionThe desk's USDV balance (liquidity())
Supply cap100,000,000 LUSG
Slippage guard in the app0.05% under the quote

Verified end to end

StepResultTransaction
A wallet outside the allowlist buys 500 USDV499.9976 LUSG; added to the allowlist in the same transaction0x467dc97b…cac4 →
It redeems 250 LUSG250.001212 USDV, accrual included0x4786bb9f…2e75 →
The booksClock cash $1,649.37 + $500 − $250.00 = $1,899.37

The app's own transaction calldata was compared byte for byte with cast and simulated against live state (100 USDV → 99.9995 LUSG). The wallet pop-up path itself was not driven by us; it needs a person with a real wallet.

Interface

function buy(uint256 usdIn, uint256 minLusgOut) returns (uint256 lusgOut)
function redeem(uint256 lusgIn, uint256 minUsdOut) returns (uint256 usdOut)
function price() view returns (uint256)
function quoteBuy(uint256 usdIn) view returns (uint256)
function quoteRedeem(uint256 lusgIn) view returns (uint256)
function liquidity() view returns (uint256)
// owner: fundLiquidity, drainLiquidity, setMaxBuy, depositCash, withdrawCash, buyLot, buyLotAtMarket,
//        setClockOracle, setClockManager, finalizeAllowlistAdmin, setOwner, setCompliance
// compliance or owner: setKyc(address[] accounts, bool allowed)