Self-repaying lending on Lux. Deposit yield-bearing collateral, borrow synthetic debt against it, and let yield retire the debt over time. No liquidation spirals, no manual repayments — the position pays itself off.
- Core:
src/Liquid.sol— lending engine (UUPS-style proxy,initializepattern) - Synthetic debt: LETH (
alETH-style 1:1-pegged synthetic), redeemable for the underlying via the Transmuter on a time-decay schedule - Yield routing: VaultV2 + curated strategy adapters (Aave, Compound, Lido, EtherFi, Pendle, Morpho, Yearn, Ethena, EigenLayer, Maker DSR, plus native Lux-side strategies)
- Compliance: optional
LiquidCompliancefor whitelisted/KYC'd redemption flows on regulated collateral
┌──────────────────────────┐
user deposits yield asset │ Liquid.sol │ mints LETH
─────────────────────────▶ │ (lending + position) │ ─────────────▶ user
└─────────┬────────────────┘
│ accounts position as ERC-721
▼
┌──────────────────────────┐
│ LiquidPosition │ NFT id ↔ owner
└──────────────────────────┘
│
yield │ (continuous)
▼
┌──────────────────────────┐
│ LiquidTransmuter │ burns LETH,
LETH holders stake here ──▶│ (Fenwick / StakingGraph)│ releases yield-token
└──────────────────────────┘
- Deposit a yield-bearing asset (wstETH, sfrxETH, weETH, sDAI, …) into
Liquid. You receive an ERC-721 position viaLiquidPosition. - Mint LETH against the position up to the protocol's collateralization floor. LETH is a transferable, fungible synthetic.
- Yield earned by the deposited collateral is harvested into the
LiquidTransmuter, which burns LETH 1:1 against the yield asset over time. - Self-repayment: as yield accrues, the position's debt is automatically reduced — no liquidation needed as long as the collateral keeps yielding.
- Transmute: LETH holders can stake LETH in the Transmuter to redeem the
underlying yield asset on a linear, block-weighted schedule (Fenwick tree
accounting in
StakingGraph).
Risk is bounded because debt is always backed by the same asset that produces the yield repaying it. Liquidations exist as a backstop for tail-risk de-pegging only.
| Contract | Purpose |
|---|---|
Liquid.sol |
Lending engine. Holds positions, mints/burns synthetic debt, harvests yield, enforces collateralization. UUPS-initializable; constructor empty. |
LiquidPosition.sol |
ERC-721 representing a user's collateral+debt position. Only Liquid can mint/burn. |
LiquidTransmuter.sol |
Converts synthetic LETH → underlying yield-token over time. Uses a Fenwick tree (StakingGraph) for O(log n) per-block accrual. ERC-721 tickets per stake. |
| Contract | Purpose |
|---|---|
LiquidETHVault.sol |
Native ETH/WETH fee vault. Owner-gated withdrawals, anyone-deposits. |
LiquidTokenVault.sol |
ERC-20 fee vault counterpart. |
adapters/AbstractFeeVault.sol |
Shared base — auth, accounting, events. |
| Contract | Purpose |
|---|---|
LiquidStrategy.sol |
Base strategy that wraps a vault-v2 adapter. APR/APY snapshotting, kill-switch, allocator whitelist, 0x-swap verification via ZeroXSwapVerifier. |
LiquidCurator.sol |
DAO-only contract that sets absolute and relative caps on each adapter. Permissioned proxy pattern. |
LiquidAllocator.sol |
Routes capital from the underlying VaultV2 into approved strategy adapters. Per-strategy allocation cap. |
LiquidStrategyClassifier.sol |
Risk-class registry (per-class globalCap/localCap). |
LiquidGauge.sol |
Token-weighted gauge for voting strategy weights; keeper executes the weighted allocation each epoch. |
Strategy adapters (src/strategies/)
DeFi blue-chips: AaveV3Strategy, CompoundV3Strategy, LidoStrategy (stETH/wstETH),
EETH (EtherFi weETH), SfrxETH (Frax), MakerDSRStrategy (sDAI),
MorphoStrategy, MorphoYearnOGWETH, YearnV3Strategy, PendleStrategy,
EthenaStrategy (sUSDe), EigenLayerStrategy, PeapodsETH, TokeAutoEth,
plus LuxNative for Lux-chain native staking.
Each adapter is a thin wrapper that conforms to ITokenAdapter and is risk-classified by the
classifier before the curator approves it for allocation.
| Contract | Purpose |
|---|---|
governance/LiquidToken.sol |
Governance token (LIQUID). |
governance/LiquidGovernor.sol |
OZ Governor over LiquidToken. Sets caps, classes, transmuter parameters, and curator/allocator admins. |
| Contract | Purpose |
|---|---|
LiquidGate.sol |
Per-vault, per-account allowlist for redemptions. |
LiquidCompliance.sol |
KYC-tier gate (0=none, 1=basic, 2=accredited Reg D, 3=qualified purchaser Reg S). Set per-vault required level. Used for regulated-collateral deployments. |
adapters/SecurityTokenAdapter.sol |
Adapter for tokenized securities collateral, paired with the compliance gate. |
utils/Whitelist.sol |
Generic whitelist primitive. |
utils/PermissionedProxy.sol |
Selector-gated proxy used by curator/allocator. |
PositionDecay,
StakingGraph (Fenwick tree),
FixedPointMath,
SafeCast,
SafeERC20,
TokenUtils,
Sets,
NFTMetadataGenerator.
| Token | Address |
|---|---|
| LETH (synthetic) | 0x60E0a8167FC13dE89348978860466C9ceC24B9ba |
| WLUX | 0x4888E4a2Ee0F03051c72D2BD3ACf755eD3498B3E |
| LBTC | 0x1E48D32a4F5e9f08DB9aE4959163300FaF8A6C8e |
Network IDs supported by script/DeployLux.s.sol:
Lux mainnet/testnet/devnet, plus Liquidity chain IDs 8675309/10/11,
local dev 1337/31337.
git clone https://github.com/luxfi/liquid.git
cd liquid
forge install
forge build
forge test -vvEnd-to-end deploy + smoke flow on Anvil or a local luxd:
anvil --chain-id 1337 &
forge script script/DeployLocal.s.sol --rpc-url http://127.0.0.1:8545 --broadcast
forge script script/TestFlow.s.sol --rpc-url http://127.0.0.1:8545 --broadcastDeployLocal brings up: Liquid + Position + Transmuter + ETHVault + Curator +
StrategyClassifier + ComplianceGate, plus dev tokens (WLUX, LUSD, mock
securities) and a SecurityTokenAdapter so the regulated-collateral path is
exercisable locally.
make deploy-devnet # api.lux-dev.network
make deploy-testnet # api.lux-test.network
make deploy-mainnet # api.lux.network (uses canonical LETH/WLUX/LBTC)Set LUX_MNEMONIC in .env. Add --verify (and the appropriate verifier
key) for explorer verification.
// 1. core
Liquid liquid = new Liquid(); // empty ctor
LiquidPosition position = new LiquidPosition(address(liquid));
LiquidTransmuter transmuter = new LiquidTransmuter(transmuterParams); // needs debtToken
LiquidETHVault feeVault = new LiquidETHVault(WETH, address(liquid), owner);
// 2. wire
liquid.initialize(LiquidInitializationParams({ ... transmuter: address(transmuter) ... }));
liquid.setLiquidPositionNFT(address(position));
transmuter.setLiquid(address(liquid));
// 3. authorize the engine to mint synthetic debt
ILiquidMintable(LETH).setMinter(address(liquid), true);
// 4. yield routing (optional but typical)
LiquidStrategyClassifier classifier = new LiquidStrategyClassifier(admin);
LiquidCurator curator = new LiquidCurator(admin, operator);
LiquidAllocator allocator = new LiquidAllocator(vaultV2, admin, operator);See script/DeployMainnet.s.sol for the full,
parameterized version with collateralization floors, fee BPS, and block-rate
constants.
IERC20(yieldToken).approve(address(liquid), amount);
uint256 tokenId = 0; // 0 = mint a new position NFT
uint256 shares = liquid.deposit(amount, msg.sender, tokenId);
liquid.mint(tokenId, debtAmount, msg.sender); // borrow LETH against positionliquid.burn(tokenId, repayAmount); // burn LETH, reduce debt
liquid.repay(tokenId, repayAmount); // alt: repay in underlying yield-asset
liquid.withdraw(tokenId, shares, msg.sender);IERC20(LETH).approve(address(transmuter), amount);
uint256 ticketId = transmuter.createRedemption(amount);
// ... time passes ...
transmuter.claimRedemption(ticketId); // proportionally vested- Implement
ITokenAdapter(or extendLiquidStrategyfor VaultV2-backed flows). - Register risk class with
LiquidStrategyClassifier.setStrategyRiskLevel(strategyId, riskLevel). - Curator sets caps:
LiquidCurator.setCaps(adapter, abs, rel). - Allocator routes flow:
LiquidAllocator.allocate(adapter, data, assets). - (Optional) wire into
LiquidGaugefor token-vote-weighted allocation.
Pair SecurityTokenAdapter with LiquidCompliance:
gate.setRequiredLevel(vault, 2); // accredited (Reg D)
gate.approve(investor, /*level*/ 2);
gate.setAuthorization(vault, investor, true);The standard ERC-20 LETH path stays open for deposits; redemption and collateral-backed mints route through the gate.
make build # forge build
make test # forge test --summary
make test-fuzz # fuzz suite (1000 runs)
make coverage # IR-min coverage summary
make halmos # symbolic execution on `check_*` properties
# static analysis (slither + semgrep + aderyn, set up via uv venv)
make security
make audit # lint + test + securityLiquid.t.sol, LiquidTransmuter.t.sol, fuzz/, and integration tests live in
src/test/. Fork-mode strategy tests (SfrxETH, MorphoYearnOGWETH,
PeapodsETH, TokeAutoEth, IntegrationTest) require MAINNET_RPC_URL.
src/
Liquid.sol LiquidPosition.sol LiquidTransmuter.sol
LiquidETHVault.sol LiquidTokenVault.sol
LiquidStrategy.sol LiquidCurator.sol LiquidAllocator.sol
LiquidStrategyClassifier.sol LiquidGauge.sol
LiquidGate.sol LiquidCompliance.sol
adapters/ AbstractFeeVault, EulerUSDCAdapter, SecurityTokenAdapter
governance/ LiquidToken, LiquidGovernor
strategies/ Aave/Compound/Lido/EETH/SfrxETH/Pendle/Morpho/Yearn/Ethena/…
libraries/ StakingGraph, PositionDecay, FixedPointMath, SafeCast, …
interfaces/ ILiquid*, ITokenAdapter, IWETH, IYieldToken, …
utils/ PermissionedProxy, Whitelist, ZeroXSwapVerifier
external/ AlEth (canonical synthetic), interfaces
test/ unit + fuzz + integration
script/
DeployLux.s.sol multi-network (mainnet/testnet/devnet + Liquidity IDs)
DeployMainnet.s.sol Lux mainnet, canonical LETH/WLUX/LBTC
DeployLocal.s.sol end-to-end local stack on Anvil/luxd
TestFlow.s.sol smoke test against a deployed local stack
lib/
forge-std, openzeppelin-{contracts,upgradeable}, vault-v2,
permit2, solmate, chainlink-brownie-contracts, halmos-cheatcodes
IVersioned is implemented across core contracts. Liquid + Transmuter are at
3.0.0. Update only the version of the contract that changed.
Drop received reports under audits/ at the repo root. Static analyzers
(Slither, Semgrep, Aderyn) and Halmos symbolic execution run via make security / make halmos.
Disclosure: see SECURITY.md.
MIT — see LICENSE.