Lotus · One-Way Directional Liquidity
Lotus lets a holder provide liquidity as directional intent: deposit sell-side inventory across ascending price bins and let takers buy through it cheapest-first. Filled inventory becomes claimable quote and can never re-enter the market in the opposite direction. It is an inventory schedule, not a set of orders.
Overview#
Concentrated-liquidity range orders have a flaw for large holders: if you place a range to sell ETH into strength and price retraces, the position re-converts back into ETH. You never actually exit. Because of this round-trip risk, the biggest holders avoid providing liquidity at all.
Lotus removes the reverse path. A maker deposits inventory (e.g. WETH) across a ladderof ascending price bins — “sell 20% at 2×, 30% at 3×”. Takers buy through the bins cheapest-first. Whatever fills converts to claimable QUOTE (e.g. USDC) and stays there. State is monotonic: inventory only goes down, proceeds only go up.
Built at ETHGlobal NY. Tracks 1inch Aqua (the fill primitive) and LI.FI (cross-chain entry).
How a ladder works#
A ladder is a list of bins. Each bin holds a slice of sell-side inventory at a fixed price. The maker creates it once; from then on the bins are passive targets that takers consume.
Cheapest-first fill
A taker brings QUOTE and calls a buy. The fill walks bins from the lowest price upward, consuming each bin before moving to the next. This guarantees takers always get the best available price and that the maker’s lowest bins clear first.
The one-way invariant
Every bin carries strictly monotonic state. Inventory can only decrease; filled amount and accrued proceeds can only increase. There is no opcode, no path, no admin action that converts proceeds back into inventory.
inventory only ↓ // never refilled, never bought back
filled only ↑ // cumulative sold, monotonic
proceeds only ↑ // claimable QUOTE, monotonic
// no reverse path: QUOTE ⇏ inventoryLifecycle
The core exposes a small, frozen surface. A maker calls createLadder, takers preview with quoteBuy and execute with fillBuy, and the maker pulls proceeds with claim. Unsold bins can be withdrawn with cancel.
createLadder(
address inventory, // sell-side asset, e.g. WETH
address quote, // proceeds asset, e.g. USDC
uint256[] prices, // ascending bin prices
uint256[] amounts // inventory per bin
) returns (uint256 ladderId)
quoteBuy(ladderId, quoteIn) view returns (out, bins[])
fillBuy(ladderId, quoteIn, minOut) // cheapest-first, atomic
claim(ladderId) // pull accrued QUOTE
cancel(ladderId) // withdraw unsold inventoryProductive yield#
Idle inventory should not sit dead in the ladder. While a bin waits to be filled, its inventory is supplied to a Morpho ERC-4626 vault — for example the Moonwell Flagship ETH vault (mwETH) on Base. The maker earns vault yield on inventory that has not sold yet.
Just-in-time recall
When a taker fills, the needed inventory is recalled from the vault inside the taker’s fill transaction. Nothing is pre-withdrawn and nothing is left exposed; the withdraw and the swap settle in the same atomic step.
The maxWithdraw guard
Before recalling, the yield adapter checks the vault’s maxWithdraw. If the vault cannot return enough liquidity right now, the fill reverts — the whole transaction rolls back. The maker never loses inventory to an illiquid vault; the worst case is a temporarily un-fillable bin.
// inside fillBuy(), per touched bin:
need = binAmount(bin)
require(vault.maxWithdraw(adapter) >= need); // else revert, no loss
vault.withdraw(need, to, adapter); // recall just-in-time
// ... router swaps inventory -> QUOTE, proceeds booked to binmaxWithdraw limit. Yield and fill-reliability both improve with vault depth.Cross-chain entry#
Inventory rarely starts where the ladder lives. Lotus uses LI.FI to bring inventory in from another chain, then create the ladder on the destination.
Sequenced, not atomic
Atomic destination contract-calls are not reliably available across every route, so Lotus sequences the flow rather than assuming a single atomic hop: first bridge the inventory, then call createLadder on the destination once funds land.
1. bridge — LI.FI route: source chain ──▶ destination chain
2. wait — inventory confirmed on destination
3. create — createLadder(inventory, quote, prices, amounts)createLadder executed on Base Sepolia end-to-end.Supported source chains
Inventory can be brought from:
- Ethereum
- Base
- Arbitrum
- Optimism
- BNB Chain
Architecture#
Lotus is three pieces: a frozen core, a fill adapter, and a yield adapter.
Core — LotusPositionManager (frozen ABI)
The position manager owns all ladder state and enforces the per-bin monotonic invariant. Its ABI is frozen — createLadder, quoteBuy, fillBuy, claim, cancel — so adapters and integrators can build against a stable surface.
Fill adapter — the ONEWAY_FILL Aqua opcode
A 1inch Aqua adapter exposes a custom SwapVM opcode, ONEWAY_FILL. Takers swap through 1inch’s router straight into a ladder: the router routes ordinary liquidity, and when it reaches the Lotus leg the ONEWAY_FILL opcode drives the cheapest-first fill against the core. This is proven end-to-end.
ONEWAY_FILL(ladderId, minOut)
└─ LotusPositionManager.fillBuy(...) // cheapest-first, monotonic
proceeds booked to bins; taker receives inventoryYield adapter — ERC-4626
A pluggable ERC-4626 adapter supplies idle inventory to a vault and performs the maxWithdraw-guarded just-in-time recall during a fill. Because it speaks the 4626 standard, the vault is swappable (Moonwell Flagship ETH today). See Productive yield.
FAQ#
- What if the price drops?
- Nothing liquidates. You simply keep the unsold inventory sitting in the bottom bins, still earning vault yield, waiting for price to come back. There is no margin, no liquidation, no forced round-trip. Only bins that actually traded are converted to proceeds.
- What if the yield vault is illiquid at fill time?
- The fill reverts and no loss occurs. The adapter checks
maxWithdrawbefore recalling inventory; if the vault can’t return enough, the entire transaction rolls back atomically. The bin is temporarily un-fillable, never lost. - Can filled proceeds turn back into inventory?
- No. That is the whole premise. The invariant is enforced per bin — inventory only decreases, proceeds only increase — and there is no reverse path in the core.
- How is this different from a Uniswap V3 range order?
- A V3 range re-converts if price retraces back through your range, so you can end up holding the asset you tried to sell. Lotus bins are one-way: once filled, they stay filled. It is an inventory schedule, not a reversible liquidity position.
- How do takers reach a ladder?
- Through the
ONEWAY_FILL1inch Aqua opcode — takers swap via the 1inch router and the Lotus leg fills the ladder cheapest-first. Or directly againstfillBuyon the core.