Integration notes
Everything the app does, you can do directly — the frontend has no privileged path. This page lists the calls that matter. Addresses live in the address book; ABIs ship in the repository (frontend/src/config/abis.ts, generated from the verified sources).
Minting
PositionManager.mint(address asset, uint256 amount, uint256 mintRatioBps)
returns (uint256 receiptId, uint256 debtAmount)
Pull-based: approve the PositionManager for amount of the asset first. mintRatioBps must sit inside the market's [min, max] (currently 0–8000). Debt is minted to the caller in the market's synth, scaled from asset decimals: amount × 10^(synthDec − assetDec) × ratio / 10⁴.
Quote first with previewMint(asset, amount, ratioBps) → (debtAmount, shares, unlockTime).
Reading positions
WithdrawalReceipt.getPosition(uint256 id) returns (ReceiptPosition)
// { asset, principal, shares, mintRatioBps, debtAmount, createdAt, unlockTime, redeemed }
WithdrawalReceipt.receiptsOf(address owner) returns (uint256[])
The receipt is ERC-721 Enumerable; receiptsOf is a convenience for small sets — index events for scale.
Redemption
ReceiptRedeemer.previewRedeem(uint256 id) returns (uint256 gross)
ReceiptRedeemer.previewRedeemDetailed(uint256 id) returns (uint256 gross, uint256 fee, uint256 net)
ReceiptRedeemer.redeem(uint256 id) returns (uint256 assetsOut)
redeem requires: caller owns the receipt, block.timestamp ≥ unlockTime, not yet redeemed, and an lUSD allowance ≥ debtAmount to the redeemer — burning spends allowance by design. Show users net, not gross: the performance fee (5% of yield only) is inside previewRedeemDetailed.
Marketplace
list(uint256 id, uint256 priceLusd) // escrows the NFT; price is absolute lUSD
buy(uint256 id) // pays the ask, receives the NFT
placeBid(uint256 id, uint256 price, uint256 expiry) // escrows lUSD; ≥ 1e18; expiry 0 = GTC
acceptBid(uint256 id, address bidder) // seller-side instant fill
cancel / cancelBid // full refunds, any time
getListing(id) / bestBid(id) / activeListings()
Every trade entry reverts with ReceiptRedeemed for spent claims — you cannot buy, list, or settle a husk. bestBid is an O(n) view over that receipt's bidders; fine for RPC, don't call it on-chain in a loop.
Events worth indexing
Minted(user, asset, synth, principal, shares, ratio, debt, receiptId) · Redeemed(receiptId, owner, asset, debtBurned, assetsOut, fee) · Listed / Sold / BidPlaced / BidAccepted / Cancelled on the marketplace · Transfer on the receipt for ownership tracking.
Invariants you can monitor
lUSD.totalSupply() == Σ debtAmountover unredeemed receipts — the solvency identity.YieldVault.totalAssets()(via the adapter)≥what redemption math implies — venue health.- Any
MinterSet / BurnerSet / AdapterSet / ManagerSet / RedeemerSetevent — role changes; on mainnet these will sit behind a timelock, so an event without a preceding queue is an alarm.