Plether Protocol
Plether is a DeFi protocol for synthetic dollar-denominated tokens with inverse and direct exposure to the US Dollar Index (USDX). Users deposit USDC to mint paired tokens that track USD strength, enabling speculation and hedging on dollar movements.
How It Works
The protocol creates two synthetic tokens from USDC collateral:
- plDXY-BEAR - Appreciates when USD weakens (USDX falls)
- plDXY-BULL - Appreciates when USD strengthens (USDX rises)
These tokens are always minted and burned in pairs, maintaining a zero-sum relationship. When you deposit 100 USDC, you receive equal amounts of both tokens. The combined value of a BEAR + BULL pair always equals the original USDC deposit.
Architecture
Core Contracts
| Contract | Description |
|---|---|
SyntheticSplitter | Central protocol contract. Accepts USDC, mints/burns token pairs. Permissionless deployToAdapter() pushes idle USDC to yield. EIP-2612 permit support on mint. |
SyntheticToken | ERC20 + ERC20FlashMint implementation for plDXY-BEAR and plDXY-BULL |
Staking Layer
| Contract | Description |
|---|---|
StakedToken | ERC-4626 vault wrapper (splDXY-BEAR, splDXY-BULL) with streaming rewards to prevent reward sniping |
RewardDistributor | Distributes USDC yield to StakedToken vaults, favoring the underperforming token |
Oracle Layer
| Contract | Description |
|---|---|
BasketOracle | Computes plDXY as weighted basket of 6 price feeds, with bound validation against Curve EMA price |
PythAdapter | Adapts Pyth Network feeds to Chainlink’s AggregatorV3Interface (used for SEK/USD) |
MorphoOracle | Adapts BasketOracle to Morpho Blue’s oracle scale (24 decimals for USDC/plDXY) |
StakedOracle | Wraps underlying oracle to price ERC-4626 staked token shares |
BasketOracle Design
The BasketOracle computes a USDX-like index using normalized arithmetic weighting rather than the geometric weighting of the official ICE USDX index:
Price = Σ(Weight_i × Price_i / BasePrice_i)
Each currency’s contribution is normalized by its base price, ensuring the intended USDX weights are preserved regardless of absolute FX rate scales. Without normalization, low-priced currencies like JPY (~$0.007) would be nearly ignored compared to EUR (~$1.08), causing severe weight distortion.
This design enables gas-efficient on-chain computation and eliminates rebalancing requirements, which guarantees protocol solvency.
Inverse Relationship: Because the oracle measures the USD value of a foreign currency basket, it moves inversely to the real USDX index. When the dollar strengthens, USDX rises but our basket value falls (foreign currencies are worth less in USD terms). This is why plDXY-BEAR appreciates when the basket value rises (dollar weakens).
Fixed Base Prices and Weights (immutable, set at deployment based on January 1, 2026 prices):
| Currency | Weight | Base Price (USD) |
|---|---|---|
| EUR | 57.6% | 1.1750 |
| JPY | 13.6% | 0.00638 |
| GBP | 11.9% | 1.3448 |
| CAD | 9.1% | 0.7288 |
| SEK | 4.2% | 0.1086 |
| CHF | 3.6% | 1.2610 |
Both weights and base prices are permanently fixed and cannot be changed after deployment.
Routing Layer
| Contract | Description |
|---|---|
ZapRouter | Single-sided plDXY-BULL minting and burning using flash mints. Permit support. |
LeverageRouter | Leveraged plDXY-BEAR positions via Morpho Blue flash loans. Open/close/add/remove collateral. Permit support. |
BullLeverageRouter | Leveraged plDXY-BULL positions via Morpho + plDXY-BEAR flash mints. Open/close/add/remove collateral. Permit support. |
Yield Adapters (ERC-4626)
| Contract | Description |
|---|---|
VaultAdapter | ERC-4626 wrapper for Morpho Vault vault yield. Owner can claimRewards() from external distributors (Merkl, URD). |
InvarCoin (INVAR)
| Contract | Description |
|---|---|
InvarCoin | Global purchasing power vault backed 50/50 by USDC + plDXY-BEAR via Curve LP |
InvarCoin is a passive savings token that maintains exposure to a basket of global currencies. Users deposit USDC, which the vault pairs with plDXY-BEAR through Curve to create a balanced position that hedges against USD weakness.
Deposit flow: USDC in → mint INVAR shares (priced against optimistic NAV to prevent dilution).
Withdraw flow: Burn INVAR → receive USDC from local buffer + JIT Curve LP unwinding. An EMA-based slippage floor prevents MEV sandwich amplification on the Curve leg.
LP deposit: Advanced path for depositing USDC + BEAR directly into Curve LP.
LP withdraw: Balanced exit returning pro-rata USDC + BEAR. lpWithdraw intentionally works when paused, serving as the emergency exit.
Yield: Curve LP trading fees accrue as virtual price growth. Keepers call harvest() to mint INVAR proportional to the fee yield and donate it to sINVAR (StakedToken) stakers via a 1-hour streaming window. Only fee yield is captured — price appreciation of the underlying assets is excluded via VP-based cost tracking.
Operational setup sequence:
- Deploy
InvarCoinandStakedToken, then callproposeStakedInvarCoin(sINVAR) - Wait
STAKED_INVAR_TIMELOCK(7 days), then callfinalizeStakedInvarCoin()before relying on harvest/donation flows - Propose the reward sink with
proposeGaugeRewardsReceiver(receiver) - Wait
GAUGE_REWARDS_TIMELOCK(7 days), then callfinalizeGaugeRewardsReceiver() - Mark CRV and any other gauge incentives with
protectRewardToken(token)so they cannot be swept withrescueToken() - After claiming rewards, route protected balances only through
sweepGaugeRewards(token)
Keeper operations:
deployToCurve()— pushes excess USDC buffer (>2% target) into single-sided Curve LPreplenishBuffer()— burns Curve LP to restore the 2% USDC bufferharvest()— captures LP fee yield and streams to sINVAR stakers
Safety:
- Dual LP pricing: pessimistic (min of EMA, oracle) for withdrawals, optimistic (max) for deposits
- Spot-vs-EMA deviation guard (0.5%) blocks deposits/deployments during pool manipulation
- Virtual shares (1e18/1e6) prevent first-depositor inflation attacks
totalAssets()is a best-effort NAV view for UX/monitoring; usetotalAssetsValidated()for strict oracle-validated accounting reads_harvestSafe()gracefully skips when Curve VP reads fail; if yield is pending, strict oracle validation is still enforcedsetEmergencyMode()pauses deposits and single-sided withdrawals without discarding LP accounting;emergencyWithdrawFromCurve()only zeroes LP tracking after assets are actually recoveredstakedInvarCoinandgaugeRewardsReceiverboth use 7-day timelocked propose/finalize flows- Protected reward tokens cannot be rescued arbitrarily and must be swept to the configured reward receiver
- L2 sequencer uptime validation is enforced on state-changing oracle-critical flows (deposit/lpDeposit/harvest/deploy/replenish)
Ecosystem Integrations
Token Flow
- Chainlink - Price feeds for EUR/USD, JPY/USD, GBP/USD, CAD/USD, CHF/USD
- Pyth Network - Price feed for SEK/USD (via PythAdapter)
- Curve Finance - AMM pools for USDC/plDXY-BEAR swaps
- Morpho Vault - Yield generation on idle USDC reserves via VaultAdapter
Bear Leverage
Flash loan USDC from Morpho → swap to plDXY-BEAR on Curve → stake → deposit splDXY-BEAR as Morpho collateral.
Bull Leverage
Flash loan USDC from Morpho → mint BEAR+BULL pairs → sell BEAR on Curve → stake BULL → deposit splDXY-BULL as Morpho collateral.
Both routers use fee-free Morpho flash loans and a fixed debt model: debt = principal × (leverage - 1).
Staking & Rewards
- StakedToken - ERC-4626 vaults (splDXY-BEAR, splDXY-BULL) that receive streaming USDC rewards
- RewardDistributor - Permissionless
distributeRewards()allocates yield from SyntheticSplitter, favoring the underperforming token’s stakers to incentivize price convergence
Protocol Mechanics
Liquidity Management
The SyntheticSplitter maintains a 10% local buffer of USDC for redemptions. Anyone can call deployToAdapter() to push excess USDC to yield adapters, targeting 90% deployment. This generates yield while ensuring liquidity for normal operations.
If adapter liquidity is constrained (e.g., high Morpho utilization), the owner can pause the protocol and use withdrawFromAdapter() for gradual extraction as liquidity becomes available.
Leverage
Users can open leveraged positions through the routers:
- LeverageRouter (Bear): Morpho flash loan USDC → Swap to plDXY-BEAR → Stake → Deposit to Morpho as collateral → Borrow USDC to repay flash loan
- BullLeverageRouter (Bull): Morpho flash loan USDC → Mint pairs → Sell plDXY-BEAR → Stake plDXY-BULL → Deposit to Morpho → Borrow to repay
Both routers use a fixed debt model: debt = principal × (leverage - 1). For 2x leverage with $100 principal, Morpho debt is always $100.
Morpho Blue provides fee-free flash loans, making leveraged positions more capital-efficient.
After opening, users can adjust positions with addCollateral() and removeCollateral() without closing.
Both routers include MEV protection via user-defined slippage caps (max 1%). All USDC entry points support EIP-2612 permits for gasless approvals.
Reward Distribution
The RewardDistributor receives yield from SyntheticSplitter and allocates it to StakedToken vaults based on the price discrepancy between the oracle and Curve EMA:
- ≥2% discrepancy: 100% to underperforming token stakers
- <2% discrepancy: Quadratic interpolation from 50/50 toward 100/0
- 0% discrepancy: 50/50 split
This mechanism incentivizes arbitrageurs to correct price deviations by rewarding stakers of the underpriced token. A 0.1% caller reward incentivizes permissionless distribution.
Lifecycle States
The protocol operates in three states:
- ACTIVE - Normal operations (mint, burn, swap). If Chainlink and Curve EMA prices diverge >2%, BasketOracle reverts, blocking minting, leverage, and reward distribution until prices converge. Burns and swaps remain available—the 10% liquid buffer ensures users can always exit.
- PAUSED - Emergency pause (minting and reward distribution blocked, burning allowed so users can exit, gradual adapter withdrawal enabled)
- SETTLED - End-of-life when plDXY hits CAP price (only redemptions allowed)
Development
Prerequisites
Build
forge build
Test
forge test # Run all tests
forge test -vvv # Verbose output
forge coverage # Generate coverage report
Fork Tests
Fork tests run against mainnet state using real Chainlink oracles, Curve pools, and Morpho Blue. They require an RPC URL:
# Set RPC URL (or add to .env file)
export MAINNET_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY
# Run all fork tests
forge test --match-path "test/fork/*.sol" --fork-url $MAINNET_RPC_URL -vvv
# Or source from .env
source .env && forge test --match-path "test/fork/*.sol" --fork-url $MAINNET_RPC_URL -vvv
Fork test files:
| File | Description |
|---|---|
BaseForkTest.sol | Shared base contract, constants, and test helpers |
ZapRouterFork.t.sol | ZapRouter integration with real Curve swaps |
FullCycleFork.t.sol | Complete mint → yield → burn lifecycle |
LeverageRouterFork.t.sol | Bear and Bull leverage via real Morpho |
SlippageProtectionFork.t.sol | MEV protection and slippage scenarios |
LiquidationFork.t.sol | Interest accrual and liquidation mechanics |
BasketOracleFork.t.sol | Full 6-feed plDXY basket oracle validation |
RewardDistributorFork.t.sol | Reward distribution with real oracle prices |
YieldIntegrationFork.t.sol | E2E yield pipeline: Morpho vault → harvest → distribute → staker share price |
PermitFork.t.sol | EIP-2612 permit-based deposits |
SlippageReport.t.sol | Slippage analysis across trade sizes |
Run a specific fork test file:
source .env && forge test --match-path test/fork/LeverageRouterFork.t.sol --fork-url $MAINNET_RPC_URL -vvv
Testnet Deployment
Sepolia
Deploy to Sepolia testnet with a custom Morpho Blue instance (the public Morpho on Sepolia has no enabled IRMs/LLTVs):
# Required environment variables in .env:
# TEST_PRIVATE_KEY=0x... (your deployer private key)
# SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/YOUR_KEY
# Deploy (43 transactions - may take a few minutes)
source .env && forge script script/DeployToSepolia.s.sol --tc DeployToSepolia \
--rpc-url $SEPOLIA_RPC_URL \
--broadcast
# Verify contracts on Etherscan (optional)
# ETHERSCAN_API_KEY=... in .env
source .env && forge verify-contract <ADDRESS> <CONTRACT> \
--chain sepolia --etherscan-api-key $ETHERSCAN_API_KEY
The Sepolia deployment script:
- Deploys its own Morpho Blue instance with a ZeroRateIrm (0% interest for testnet)
- Creates all protocol contracts, oracles, and routers
- Seeds Curve pool and Morpho markets with liquidity
- Mints 100k MockUSDC to the deployer
Anvil (Local)
For frontend development and testing without spending real ETH:
# 1. Start local Anvil node forking Ethereum mainnet
anvil --fork-url $MAINNET_RPC_URL --chain-id 31337
# 2. Deploy all contracts with real Chainlink/Pyth oracles (mints 100k USDC to deployer)
TEST_PRIVATE_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d \
forge script script/DeployToAnvilFork.s.sol --tc DeployToAnvilFork \
--rpc-url http://127.0.0.1:8545 \
--broadcast
# 3. (Optional) Simulate yield accrual (1% of adapter assets)
cast send <MOCK_YIELD_ADAPTER> "generateYield()" \
--rpc-url http://127.0.0.1:8545 \
--private-key 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
# 4. (Optional) Seed Morpho markets for leverage testing
# Morpho seeding is built into DeployToTest.s.sol for testnet deploys.
# For Anvil fork, create markets manually using cast commands.
The Anvil fork deployment uses real mainnet oracles (Chainlink for EUR/JPY/GBP/CAD/CHF, Pyth for SEK) with prices frozen at the fork block. MockUSDC and MockYieldAdapter are still used for flexible testing.
Anvil Test Accounts (pre-funded with 10,000 ETH each):
| Account | Address | Private Key |
|---|---|---|
| #0 | 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 | 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 |
| #1 | 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 | 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d |
MetaMask Setup:
- Import a test private key (Settings → Import Account)
- Add network: RPC
http://127.0.0.1:8545, Chain ID31337
Format
forge fmt # Format code
forge fmt --check # Check formatting
Documentation
Generate HTML documentation locally from NatSpec comments:
forge doc # Generate docs to ./docs
forge doc --serve # Serve docs locally at http://localhost:3000
forge doc --build # Build static site to ./docs/book
Security
- All contracts use OpenZeppelin’s battle-tested implementations
- Reentrancy protection on state-changing functions
- 7-day timelock for critical governance changes
- Oracle staleness checks (8–24 hour timeouts depending on context)
- Oracle bound validation against Curve EMA to prevent price manipulation
- Flash loan callback validation (initiator + lender checks)
- Yield adapter uses Morpho Vault vault accounting for yield generation
For detailed security assumptions, trust model, and emergency procedures, see SECURITY.md.
License
Disclaimer
This software is provided “as is” without warranty of any kind. Use at your own risk. This protocol has not been audited. Do not use in production without a professional security audit.
Contents
FlashLoanBase
Inherits: IERC3156FlashBorrower, IMorphoFlashLoanCallback
Title: FlashLoanBase
Abstract base for flash loan borrowers with validation logic.
Supports both Morpho flash loans and ERC-3156 flash mints.
Constants
CALLBACK_SUCCESS
ERC-3156 callback success return value.
bytes32 internal constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan")
Functions
_validateFlashLoan
Validates ERC-3156 flash loan callback parameters.
function _validateFlashLoan(
address lender,
address expectedLender,
address initiator
) internal view;
Parameters
| Name | Type | Description |
|---|---|---|
lender | address | Actual msg.sender. |
expectedLender | address | Expected flash lender address. |
initiator | address | Initiator passed to callback (must be this contract). |
_validateLender
Validates that msg.sender is the expected lender.
function _validateLender(
address lender,
address expectedLender
) internal pure;
Parameters
| Name | Type | Description |
|---|---|---|
lender | address | Actual msg.sender. |
expectedLender | address | Expected flash lender address. |
Errors
FlashLoan__InvalidLender
Thrown when flash loan callback called by wrong lender.
error FlashLoan__InvalidLender();
FlashLoan__InvalidInitiator
Thrown when flash loan initiator is not this contract.
error FlashLoan__InvalidInitiator();
FlashLoan__InvalidOperation
Thrown when callback receives unknown operation type.
error FlashLoan__InvalidOperation();
LeverageRouterBase
Inherits: FlashLoanBase, Ownable2Step, Pausable, ReentrancyGuard
Title: LeverageRouterBase
Abstract base for leverage routers with shared validation and admin logic.
Common infrastructure for LeverageRouter (plDXY-BEAR) and BullLeverageRouter (plDXY-BULL).
Note: security-contact: contact@plether.com
Constants
MAX_SLIPPAGE_BPS
Maximum slippage in basis points (1% = 100 bps).
uint256 public constant MAX_SLIPPAGE_BPS = 100
EXCHANGE_RATE_BUFFER_BPS
Buffer for exchange rate drift protection (1% = 100 bps).
uint256 public constant EXCHANGE_RATE_BUFFER_BPS = 100
USDC_INDEX
USDC index in Curve USDC/plDXY-BEAR pool.
uint256 public constant USDC_INDEX = 0
PLDXY_BEAR_INDEX
plDXY-BEAR index in Curve USDC/plDXY-BEAR pool.
uint256 public constant PLDXY_BEAR_INDEX = 1
OP_OPEN
Operation type: open leverage position.
uint8 internal constant OP_OPEN = 1
OP_CLOSE
Operation type: close leverage position.
uint8 internal constant OP_CLOSE = 2
MORPHO
Morpho Blue lending protocol.
IMorpho public immutable MORPHO
CURVE_POOL
Curve pool for USDC/plDXY-BEAR swaps.
ICurvePool public immutable CURVE_POOL
USDC
USDC stablecoin.
IERC20 public immutable USDC
PLDXY_BEAR
plDXY-BEAR token.
IERC20 public immutable PLDXY_BEAR
State Variables
marketParams
Morpho market configuration.
MarketParams public marketParams
Functions
constructor
Initializes base router with core dependencies.
constructor(
address _morpho,
address _curvePool,
address _usdc,
address _plDxyBear
) Ownable(msg.sender);
Parameters
| Name | Type | Description |
|---|---|---|
_morpho | address | Morpho Blue protocol address. |
_curvePool | address | Curve USDC/plDXY-BEAR pool address. |
_usdc | address | USDC token address. |
_plDxyBear | address | plDXY-BEAR token address. |
pause
Pause the router. Blocks openLeverage and closeLeverage.
function pause() external onlyOwner;
unpause
Unpause the router.
function unpause() external onlyOwner;
getActualDebt
Returns the user’s current debt in this market (includes accrued interest).
function getActualDebt(
address user
) external view returns (uint256 debt);
Parameters
| Name | Type | Description |
|---|---|---|
user | address | The address to query debt for. |
Returns
| Name | Type | Description |
|---|---|---|
debt | uint256 | The actual debt amount in USDC (rounded up). |
getCollateral
Returns the user’s collateral in this market.
function getCollateral(
address user
) external view returns (uint256 collateral);
Parameters
| Name | Type | Description |
|---|---|---|
user | address | The address to query collateral for. |
Returns
| Name | Type | Description |
|---|---|---|
collateral | uint256 | The collateral amount in staked token shares. |
_marketId
Computes market ID from marketParams.
function _marketId() internal view returns (bytes32);
_getActualDebt
Computes actual debt from Morpho position, rounded up to ensure full repayment.
function _getActualDebt(
address user
) internal view returns (uint256);
_getBorrowShares
Returns user’s borrow shares from Morpho position.
function _getBorrowShares(
address user
) internal view returns (uint256);
_getCollateral
Returns user’s collateral from Morpho position.
function _getCollateral(
address user
) internal view returns (uint256);
Errors
LeverageRouterBase__ZeroAddress
Thrown when zero address provided.
error LeverageRouterBase__ZeroAddress();
LeverageRouterBase__ZeroPrincipal
Thrown when principal is zero.
error LeverageRouterBase__ZeroPrincipal();
LeverageRouterBase__ZeroCollateral
Thrown when collateral is zero.
error LeverageRouterBase__ZeroCollateral();
LeverageRouterBase__Expired
Thrown when deadline has passed.
error LeverageRouterBase__Expired();
LeverageRouterBase__LeverageTooLow
Thrown when leverage multiplier <= 1x.
error LeverageRouterBase__LeverageTooLow();
LeverageRouterBase__SlippageExceedsMax
Thrown when slippage exceeds MAX_SLIPPAGE_BPS.
error LeverageRouterBase__SlippageExceedsMax();
LeverageRouterBase__NotAuthorized
Thrown when user hasn’t authorized router in Morpho.
error LeverageRouterBase__NotAuthorized();
LeverageRouterBase__InsufficientOutput
Thrown when swap output is insufficient.
error LeverageRouterBase__InsufficientOutput();
LeverageRouterBase__InvalidCurvePrice
Thrown when Curve price query returns zero.
error LeverageRouterBase__InvalidCurvePrice();
LeverageRouterBase__SplitterNotActive
Thrown when Splitter is not active.
error LeverageRouterBase__SplitterNotActive();
LeverageRouterBase__ZeroAmount
Thrown when amount is zero.
error LeverageRouterBase__ZeroAmount();
LeverageRouterBase__NoPosition
Thrown when user has no position in Morpho.
error LeverageRouterBase__NoPosition();
LeverageRouterBase__Unhealthy
Thrown when withdrawal would make position unhealthy.
error LeverageRouterBase__Unhealthy();
LeverageRouterBase__AmountTooSmall
Thrown when amount is too small after conversion.
error LeverageRouterBase__AmountTooSmall();
LeverageRouterBase__PermitFailed
error LeverageRouterBase__PermitFailed();
LeverageRouterBase__BelowMinAmountOut
Thrown when output is below user-specified minimum (MEV protection).
error LeverageRouterBase__BelowMinAmountOut();
Contents
- AggregatorV3Interface
- ICurveGauge
- ICurveMinter
- ICurvePool
- ICurveTwocrypto
- IInvarCoin
- MarketParams
- IMorpho
- IMorphoFlashLoanCallback
- PythStructs
- IPyth
- IRewardDistributor
- ISyntheticSplitter
- IYieldAdapter
AggregatorV3Interface
Title: AggregatorV3Interface
Chainlink price feed interface.
Standard interface for Chainlink oracles. See https://docs.chain.link/data-feeds.
Functions
decimals
Returns the number of decimals in the price.
function decimals() external view returns (uint8);
description
Returns a human-readable description of the feed.
function description() external view returns (string memory);
version
Returns the feed version number.
function version() external view returns (uint256);
getRoundData
Returns historical round data.
function getRoundData(
uint80 _roundId
)
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
Parameters
| Name | Type | Description |
|---|---|---|
_roundId | uint80 | The round ID to query. |
Returns
| Name | Type | Description |
|---|---|---|
roundId | uint80 | The round ID. |
answer | int256 | The price answer. |
startedAt | uint256 | Timestamp when round started. |
updatedAt | uint256 | Timestamp of last update. |
answeredInRound | uint80 | The round in which answer was computed. |
latestRoundData
Returns the latest round data.
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
Returns
| Name | Type | Description |
|---|---|---|
roundId | uint80 | The current round ID. |
answer | int256 | The latest price. |
startedAt | uint256 | Timestamp when round started. |
updatedAt | uint256 | Timestamp of last update. |
answeredInRound | uint80 | The round in which answer was computed. |
ICurveGauge
Functions
lp_token
function lp_token() external view returns (address);
deposit
function deposit(
uint256 amount
) external;
withdraw
function withdraw(
uint256 amount
) external;
claim_rewards
function claim_rewards() external;
balanceOf
function balanceOf(
address
) external view returns (uint256);
ICurveMinter
Functions
mint
function mint(
address gauge
) external;
ICurvePool
Title: ICurvePool
Interface for Curve StableSwap pools.
Used for USDC/plDXY-BEAR swaps. Indices: USDC=0, plDXY-BEAR=1.
Functions
get_dy
Calculates expected output for a swap.
function get_dy(
uint256 i,
uint256 j,
uint256 dx
) external view returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
i | uint256 | Input token index. |
j | uint256 | Output token index. |
dx | uint256 | Input amount. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Expected output amount. |
exchange
Executes a token swap.
function exchange(
uint256 i,
uint256 j,
uint256 dx,
uint256 min_dy
) external payable returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
i | uint256 | Input token index. |
j | uint256 | Output token index. |
dx | uint256 | Input amount. |
min_dy | uint256 | Minimum output (slippage protection). |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Actual output amount. |
price_oracle
Returns EMA oracle price (18 decimals).
function price_oracle() external view returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Price of token1 in terms of token0. |
ICurveTwocrypto
Functions
add_liquidity
function add_liquidity(
uint256[2] calldata amounts,
uint256 min_mint_amount
) external returns (uint256);
remove_liquidity_one_coin
function remove_liquidity_one_coin(
uint256 token_amount,
uint256 i,
uint256 min_amount
) external returns (uint256);
remove_liquidity
function remove_liquidity(
uint256 amount,
uint256[2] calldata min_amounts
) external returns (uint256[2] memory);
get_virtual_price
function get_virtual_price() external view returns (uint256);
lp_price
function lp_price() external view returns (uint256);
calc_token_amount
function calc_token_amount(
uint256[2] calldata amounts,
bool deposit
) external view returns (uint256);
calc_withdraw_one_coin
function calc_withdraw_one_coin(
uint256 token_amount,
uint256 i
) external view returns (uint256);
IInvarCoin
Functions
donateUsdc
function donateUsdc(
uint256 usdcAmount
) external;
totalAssets
function totalAssets() external view returns (uint256);
MarketParams
Morpho Blue market configuration.
struct MarketParams {
address loanToken;
address collateralToken;
address oracle;
address irm;
uint256 lltv;
}
Properties
| Name | Type | Description |
|---|---|---|
loanToken | address | Asset being borrowed. |
collateralToken | address | Asset used as collateral. |
oracle | address | Price oracle for collateral valuation. |
irm | address | Interest rate model contract. |
lltv | uint256 | Liquidation loan-to-value ratio. |
IMorpho
Title: IMorpho
Minimal interface for Morpho Blue lending protocol.
See https://docs.morpho.org for full documentation.
Functions
setAuthorization
Set authorization for an address to act on behalf of the caller
function setAuthorization(
address authorized,
bool newIsAuthorized
) external;
isAuthorized
Check if an address is authorized to act on behalf of another
function isAuthorized(
address authorizer,
address authorized
) external view returns (bool);
createMarket
Create a new market
function createMarket(
MarketParams memory marketParams
) external;
idToMarketParams
Get market ID from params
function idToMarketParams(
bytes32 id
) external view returns (MarketParams memory);
supply
Supply loan assets to a Morpho market as a lender
function supply(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalfOf,
bytes calldata data
) external returns (uint256 assetsSupplied, uint256 sharesSupplied);
withdraw
Withdraw loan assets from a Morpho market as a lender
function withdraw(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalfOf,
address receiver
) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn);
supplyCollateral
Supply collateral to a Morpho market as a borrower
function supplyCollateral(
MarketParams memory marketParams,
uint256 assets,
address onBehalfOf,
bytes calldata data
) external;
withdrawCollateral
Withdraw collateral from a Morpho market as a borrower
function withdrawCollateral(
MarketParams memory marketParams,
uint256 assets,
address onBehalfOf,
address receiver
) external;
borrow
Borrow assets from a Morpho market
function borrow(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalfOf,
address receiver
) external returns (uint256 assetsBorrowed, uint256 sharesIssued);
repay
Repay borrowed assets to a Morpho market
function repay(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalfOf,
bytes calldata data
) external returns (uint256 assetsRepaid, uint256 sharesRepaid);
accrueInterest
Accrue interest for a market
function accrueInterest(
MarketParams memory marketParams
) external;
liquidate
Liquidate an unhealthy position
function liquidate(
MarketParams memory marketParams,
address borrower,
uint256 seizedAssets,
uint256 repaidShares,
bytes calldata data
) external returns (uint256 assetsSeized, uint256 assetsRepaid);
Parameters
| Name | Type | Description |
|---|---|---|
marketParams | MarketParams | The market parameters |
borrower | address | The address of the borrower to liquidate |
seizedAssets | uint256 | The amount of collateral to seize |
repaidShares | uint256 | The amount of debt shares to repay (alternative to seizedAssets) |
data | bytes | Callback data |
flashLoan
Execute a flash loan
Morpho flash loans are fee-free. Callback must repay exact amount.
function flashLoan(
address token,
uint256 assets,
bytes calldata data
) external;
Parameters
| Name | Type | Description |
|---|---|---|
token | address | The token to flash loan |
assets | uint256 | The amount of tokens to flash loan |
data | bytes | Arbitrary data to pass to the callback |
position
Get position data for a user in a market
function position(
bytes32 id,
address user
) external view returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral);
market
Get market data
function market(
bytes32 id
)
external
view
returns (
uint128 totalSupplyAssets,
uint128 totalSupplyShares,
uint128 totalBorrowAssets,
uint128 totalBorrowShares,
uint128 lastUpdate,
uint128 fee
);
Structs
MarketState
Market state for IRM calculations.
struct MarketState {
uint128 totalSupplyAssets;
uint128 totalSupplyShares;
uint128 totalBorrowAssets;
uint128 totalBorrowShares;
uint128 lastUpdate;
uint128 fee;
}
IMorphoFlashLoanCallback
Title: IMorphoFlashLoanCallback
Callback interface for Morpho flash loan receivers.
Functions
onMorphoFlashLoan
Called by Morpho during flash loan execution.
function onMorphoFlashLoan(
uint256 assets,
bytes calldata data
) external;
Parameters
| Name | Type | Description |
|---|---|---|
assets | uint256 | Amount of tokens borrowed. |
data | bytes | Arbitrary data passed through from flashLoan call. |
PythStructs
Title: Pyth Price Structs
Structs
Price
struct Price {
int64 price;
uint64 conf;
int32 expo;
uint256 publishTime;
}
IPyth
Title: Pyth Network Interface (minimal)
Minimal interface for reading Pyth price feeds.
Functions
getPriceUnsafe
Returns the price without staleness checks.
function getPriceUnsafe(
bytes32 id
) external view returns (PythStructs.Price memory price);
Parameters
| Name | Type | Description |
|---|---|---|
id | bytes32 | The Pyth price feed ID. |
Returns
| Name | Type | Description |
|---|---|---|
price | PythStructs.Price | The price data. |
getPriceNoOlderThan
Returns the price if it’s no older than age seconds.
function getPriceNoOlderThan(
bytes32 id,
uint256 age
) external view returns (PythStructs.Price memory price);
Parameters
| Name | Type | Description |
|---|---|---|
id | bytes32 | The Pyth price feed ID. |
age | uint256 | Maximum acceptable age in seconds. |
Returns
| Name | Type | Description |
|---|---|---|
price | PythStructs.Price | The price data. |
updatePriceFeeds
Updates price feeds with signed data from Pyth.
function updatePriceFeeds(
bytes[] calldata updateData
) external payable;
Parameters
| Name | Type | Description |
|---|---|---|
updateData | bytes[] | Array of price update data. |
getUpdateFee
Returns the fee required to update price feeds.
function getUpdateFee(
bytes[] calldata updateData
) external view returns (uint256 feeAmount);
Parameters
| Name | Type | Description |
|---|---|---|
updateData | bytes[] | Array of price update data. |
Returns
| Name | Type | Description |
|---|---|---|
feeAmount | uint256 | The required fee in wei. |
IRewardDistributor
Title: IRewardDistributor
Interface for the RewardDistributor contract that allocates staking rewards based on price discrepancy between oracle and Curve pool.
Functions
distributeRewards
Permissionless function to distribute accumulated USDC rewards.
Calculates price discrepancy, acquires tokens, and donates to vaults.
function distributeRewards() external returns (uint256 callerReward);
Returns
| Name | Type | Description |
|---|---|---|
callerReward | uint256 | Amount of USDC sent to caller as incentive. |
distributeRewardsWithPriceUpdate
Distributes rewards after updating the Pyth oracle price.
Bundles Pyth price update with reward distribution. Pass empty array if no update needed.
function distributeRewardsWithPriceUpdate(
bytes[] calldata pythUpdateData
) external payable returns (uint256 callerReward);
Parameters
| Name | Type | Description |
|---|---|---|
pythUpdateData | bytes[] | Price update data from Pyth Hermes API. |
Returns
| Name | Type | Description |
|---|---|---|
callerReward | uint256 | Amount of USDC sent to caller as incentive. |
previewDistribution
Preview the distribution without executing.
function previewDistribution()
external
view
returns (uint256 bearPct, uint256 bullPct, uint256 usdcBalance, uint256 callerReward);
Returns
| Name | Type | Description |
|---|---|---|
bearPct | uint256 | Expected percentage to BEAR stakers (basis points). |
bullPct | uint256 | Expected percentage to BULL stakers (basis points). |
usdcBalance | uint256 | Current USDC balance available for distribution. |
callerReward | uint256 | Expected caller reward. |
Events
RewardsDistributed
Emitted when rewards are distributed to staking vaults.
event RewardsDistributed(
uint256 bearAmount, uint256 bullAmount, uint256 invarUsdcAmount, uint256 bearPct, uint256 bullPct
);
Parameters
| Name | Type | Description |
|---|---|---|
bearAmount | uint256 | Amount of plDXY-BEAR donated to StakedBear. |
bullAmount | uint256 | Amount of plDXY-BULL donated to StakedBull. |
invarUsdcAmount | uint256 | Amount of USDC donated to InvarCoin. |
bearPct | uint256 | Percentage of rewards allocated to BEAR stakers (basis points). |
bullPct | uint256 | Percentage of rewards allocated to BULL stakers (basis points). |
Errors
RewardDistributor__DistributionTooSoon
Thrown when distribution is attempted before cooldown expires.
error RewardDistributor__DistributionTooSoon();
RewardDistributor__NoRewards
Thrown when there are no rewards to distribute.
error RewardDistributor__NoRewards();
RewardDistributor__SplitterNotActive
Thrown when the SyntheticSplitter is not in ACTIVE status.
error RewardDistributor__SplitterNotActive();
RewardDistributor__ZeroAddress
Thrown when a constructor parameter is zero address.
error RewardDistributor__ZeroAddress();
RewardDistributor__RefundFailed
Thrown when ETH refund to caller fails.
error RewardDistributor__RefundFailed();
RewardDistributor__InvalidPrice
Thrown when oracle returns zero or negative price.
error RewardDistributor__InvalidPrice();
ISyntheticSplitter
Title: ISyntheticSplitter
Minimal interface for external contracts to interact with SyntheticSplitter.
Used by ZapRouter and other integrations.
Functions
mint
Deposits collateral to mint equal amounts of plDXY-BEAR and plDXY-BULL tokens.
Requires approval on USDC. Amount is in 18-decimal token units.
function mint(
uint256 amount
) external;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | The amount of token pairs to mint. |
mintWithPermit
Deposits collateral to mint tokens with a USDC permit signature (gasless approval).
function mintWithPermit(
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | The amount of token pairs to mint. |
deadline | uint256 | Unix timestamp after which the permit and transaction revert. |
v | uint8 | Signature recovery byte. |
r | bytes32 | Signature r component. |
s | bytes32 | Signature s component. |
burn
Burns equal amounts of plDXY-BEAR and plDXY-BULL tokens to retrieve collateral.
Works when not liquidated. May be restricted when paused and insolvent.
function burn(
uint256 amount
) external;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | The amount of token pairs to burn. |
emergencyRedeem
Emergency exit after liquidation. Burns plDXY-BEAR for its full CAP value.
Only works when protocol is liquidated (price >= CAP).
function emergencyRedeem(
uint256 amount
) external;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | The amount of plDXY-BEAR tokens to burn. |
currentStatus
Returns the current protocol lifecycle status.
function currentStatus() external view returns (Status);
Returns
| Name | Type | Description |
|---|---|---|
<none> | Status | The current Status enum value. |
CAP
Returns the protocol CAP price (8 decimals, oracle format).
function CAP() external view returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | The CAP value in 8 decimal format (e.g., 2e8 = $2.00). |
liquidationTimestamp
Returns the timestamp when the protocol was liquidated.
function liquidationTimestamp() external view returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | The liquidation timestamp (0 if not liquidated). |
treasury
Returns the treasury address.
function treasury() external view returns (address);
Returns
| Name | Type | Description |
|---|---|---|
<none> | address | The treasury address. |
Enums
Status
Defines the current lifecycle state of the protocol.
enum Status {
ACTIVE,
PAUSED,
SETTLED
}
Variants
| Name | Description |
|---|---|
ACTIVE | Normal operations. Minting and burning enabled. |
PAUSED | Security pause. Minting disabled, burn may be restricted if insolvent. |
SETTLED | End of life. Cap breached. Only emergencyRedeem enabled. |
IYieldAdapter
Title: IYieldAdapter
Extended interface for yield adapters that support interest accrual.
Optional extension to IERC4626 for adapters with pending interest.
Functions
accrueInterest
Forces the underlying protocol to accrue pending interest.
Call before reading totalAssets() if exact values are needed for calculations.
function accrueInterest() external;
Contents
DecimalConstants
Title: DecimalConstants
Shared decimal scaling constants for the Plether protocol.
Centralizes decimal conversions to prevent scaling bugs.
Constants
ONE_WAD
One unit with 18 decimals (standard ERC20/leverage scale).
uint256 internal constant ONE_WAD = 1e18
ONE_USDC
One USDC (6 decimals).
uint256 internal constant ONE_USDC = 1e6
USDC_TO_TOKEN_SCALE
USDC (6 dec) + Chainlink (8 dec) -> Token (18 dec): 10^20.
uint256 internal constant USDC_TO_TOKEN_SCALE = 1e20
CHAINLINK_TO_MORPHO_SCALE
Chainlink (8 dec) -> Morpho (36 + loanDec - collateralDec = 24 dec): 10^16.
uint256 internal constant CHAINLINK_TO_MORPHO_SCALE = 1e16
CHAINLINK_TO_TOKEN_SCALE
Chainlink (8 dec) -> Token (18 dec): 10^10.
uint256 internal constant CHAINLINK_TO_TOKEN_SCALE = 1e10
IIrm
Title: IIrm
Minimal interface for Morpho Blue Interest Rate Models.
Functions
borrowRateView
Returns the borrow rate per second (scaled by 1e18).
function borrowRateView(
MarketParams memory marketParams,
IMorpho.MarketState memory market
) external view returns (uint256);
MorphoBalancesLib
Title: MorphoBalancesLib
Library to compute expected Morpho balances including pending interest.
Mirrors Morpho Blue’s internal interest accrual logic for accurate view functions.
Constants
WAD
uint256 internal constant WAD = 1e18
Functions
expectedTotalSupplyAssets
Computes expected total supply assets including pending interest.
function expectedTotalSupplyAssets(
IMorpho morpho,
MarketParams memory marketParams
) internal view returns (uint256 expectedSupplyAssets);
Parameters
| Name | Type | Description |
|---|---|---|
morpho | IMorpho | Morpho Blue contract. |
marketParams | MarketParams | Market parameters. |
Returns
| Name | Type | Description |
|---|---|---|
expectedSupplyAssets | uint256 | Total supply assets after pending interest accrual. |
expectedSupplyAssets
Converts supply shares to expected assets including pending interest.
function expectedSupplyAssets(
IMorpho morpho,
MarketParams memory marketParams,
uint256 shares
) internal view returns (uint256 assets);
Parameters
| Name | Type | Description |
|---|---|---|
morpho | IMorpho | Morpho Blue contract. |
marketParams | MarketParams | Market parameters. |
shares | uint256 | Supply shares to convert. |
Returns
| Name | Type | Description |
|---|---|---|
assets | uint256 | Expected asset amount. |
OracleLib
Title: OracleLib
Library for common oracle validation patterns.
Provides reusable functions for sequencer checks, staleness validation, and price validation.
Functions
checkSequencer
Check if the L2 sequencer is up and grace period has passed.
Skips check if sequencerFeed is address(0) (e.g., on L1 or testnets).
function checkSequencer(
AggregatorV3Interface sequencerFeed,
uint256 gracePeriod
) internal view;
Parameters
| Name | Type | Description |
|---|---|---|
sequencerFeed | AggregatorV3Interface | The Chainlink sequencer uptime feed. |
gracePeriod | uint256 | The grace period in seconds after sequencer comes back up. |
checkStaleness
Check if the oracle price is stale.
function checkStaleness(
uint256 updatedAt,
uint256 timeout
) internal view;
Parameters
| Name | Type | Description |
|---|---|---|
updatedAt | uint256 | The timestamp when the price was last updated. |
timeout | uint256 | The maximum age in seconds for a valid price. |
checkStalenessAt
Check staleness relative to a specific reference timestamp instead of block.timestamp.
function checkStalenessAt(
uint256 updatedAt,
uint256 timeout,
uint256 referenceTime
) internal pure;
Parameters
| Name | Type | Description |
|---|---|---|
updatedAt | uint256 | The timestamp when the price was last updated. |
timeout | uint256 | The maximum age in seconds for a valid price. |
referenceTime | uint256 | The timestamp to measure staleness against. |
verifyHistoricalPrice
Verifies a caller-provided hint round is the correct price at a target timestamp.
Avoids backward traversal that breaks on Chainlink phase boundaries.
function verifyHistoricalPrice(
AggregatorV3Interface feed,
uint256 targetTimestamp,
uint80 hintRoundId
) internal view returns (int256 price, uint256 updatedAt);
Parameters
| Name | Type | Description |
|---|---|---|
feed | AggregatorV3Interface | The Chainlink price feed. |
targetTimestamp | uint256 | The timestamp to look up the price for. |
hintRoundId | uint80 | The round ID that the caller claims was active at targetTimestamp. |
Returns
| Name | Type | Description |
|---|---|---|
price | int256 | The price at the target timestamp. |
updatedAt | uint256 | The timestamp of the round found. |
getValidatedPrice
Get a validated price from an oracle with staleness and sequencer checks.
Reverts on zero or negative prices to prevent operations during oracle failures.
function getValidatedPrice(
AggregatorV3Interface oracle,
AggregatorV3Interface sequencerFeed,
uint256 gracePeriod,
uint256 timeout
) internal view returns (uint256 price);
Parameters
| Name | Type | Description |
|---|---|---|
oracle | AggregatorV3Interface | The price oracle. |
sequencerFeed | AggregatorV3Interface | The sequencer uptime feed (can be address(0) to skip). |
gracePeriod | uint256 | The sequencer grace period in seconds. |
timeout | uint256 | The staleness timeout in seconds. |
Returns
| Name | Type | Description |
|---|---|---|
price | uint256 | The validated price. |
tryGetValidatedPrice
Non-reverting variant of getValidatedPrice for best-effort oracle reads.
function tryGetValidatedPrice(
AggregatorV3Interface oracle,
AggregatorV3Interface sequencerFeed,
uint256 gracePeriod,
uint256 timeout
) internal view returns (bool success, uint256 price);
Returns
| Name | Type | Description |
|---|---|---|
success | bool | True if the price passed all validation checks. |
price | uint256 | The validated price (0 if success is false). |
Errors
OracleLib__SequencerDown
error OracleLib__SequencerDown();
OracleLib__SequencerGracePeriod
error OracleLib__SequencerGracePeriod();
OracleLib__StalePrice
error OracleLib__StalePrice();
OracleLib__InvalidPrice
error OracleLib__InvalidPrice();
OracleLib__NoPriceAtExpiry
error OracleLib__NoPriceAtExpiry();
OracleLib__InsufficientGas
error OracleLib__InsufficientGas();
Contents
DOVZapRouter
Inherits: FlashLoanBase, ReentrancyGuard, Ownable2Step
Title: DOVZapRouter
Coordinates USDC→splDXY zaps across BEAR and BULL DOVs.
Mints pairs for the overlapping USDC amount (zero slippage), routes excess through Curve or flash mint.
Note: security-contact: contact@plether.com
Constants
USDC_INDEX
uint256 public constant USDC_INDEX = 0
PLDXY_BEAR_INDEX
uint256 public constant PLDXY_BEAR_INDEX = 1
SAFETY_BUFFER_BPS
uint256 public constant SAFETY_BUFFER_BPS = 50
SPLITTER
ISyntheticSplitter public immutable SPLITTER
CURVE_POOL
ICurvePool public immutable CURVE_POOL
USDC
IERC20 public immutable USDC
PLDXY_BEAR
IERC20 public immutable PLDXY_BEAR
PLDXY_BULL
IERC20 public immutable PLDXY_BULL
STAKED_BEAR
IERC4626 public immutable STAKED_BEAR
STAKED_BULL
IERC4626 public immutable STAKED_BULL
BEAR_DOV
PletherDOV public immutable BEAR_DOV
BULL_DOV
PletherDOV public immutable BULL_DOV
CAP
uint256 public immutable CAP
CAP_PRICE
uint256 public immutable CAP_PRICE
Functions
constructor
constructor(
address _splitter,
address _curvePool,
address _usdc,
address _plDxyBear,
address _plDxyBull,
address _stakedBear,
address _stakedBull,
address _bearDov,
address _bullDov
) Ownable(msg.sender);
coordinatedZapAndStartEpochs
Pulls USDC from both DOVs, mints matched pairs, routes excess, starts epoch auctions.
function coordinatedZapAndStartEpochs(
EpochParams calldata bearParams,
EpochParams calldata bullParams,
uint256 minBearSwapOut,
uint256 minBullSwapOut
) external onlyOwner nonReentrant;
_flashZapBull
function _flashZapBull(
uint256 usdcAmount,
uint256 minSwapOut
) internal;
onFlashLoan
function onFlashLoan(
address initiator,
address,
uint256 amount,
uint256 fee,
bytes calldata data
) external override returns (bytes32);
onMorphoFlashLoan
function onMorphoFlashLoan(
uint256,
bytes calldata
) external pure override;
Events
CoordinatedZap
event CoordinatedZap(uint256 bearUsdc, uint256 bullUsdc, uint256 matchedAmount);
Errors
DOVZapRouter__SolvencyBreach
error DOVZapRouter__SolvencyBreach();
DOVZapRouter__BearPriceAboveCap
error DOVZapRouter__BearPriceAboveCap();
Structs
EpochParams
struct EpochParams {
uint256 strike;
uint256 expiry;
uint256 maxPremium;
uint256 minPremium;
uint256 duration;
}
ISettlementOracle
Functions
getSettlementPrices
function getSettlementPrices(
uint256 expiry,
uint80[] calldata roundHints
) external view returns (uint256 bearPrice, uint256 bullPrice);
IOptionToken
Functions
initialize
function initialize(
string memory name,
string memory symbol,
address marginEngine
) external;
mint
function mint(
address to,
uint256 amount
) external;
burn
function burn(
address from,
uint256 amount
) external;
totalSupply
function totalSupply() external view returns (uint256);
MarginEngine
Inherits: ReentrancyGuard, AccessControl
Title: MarginEngine
Core options clearinghouse for Plether DOVs.
Enforces 100% margin requirements and Fractional In-Kind Settlement via StakedTokens.
Note: security-contact: contact@plether.com
Constants
SERIES_CREATOR_ROLE
bytes32 public constant SERIES_CREATOR_ROLE = keccak256("SERIES_CREATOR_ROLE")
SPLITTER
ISyntheticSplitter public immutable SPLITTER
ORACLE
ISettlementOracle public immutable ORACLE
STAKED_BEAR
IERC4626 public immutable STAKED_BEAR
STAKED_BULL
IERC4626 public immutable STAKED_BULL
OPTION_IMPLEMENTATION
address public immutable OPTION_IMPLEMENTATION
CAP
uint256 public immutable CAP
State Variables
nextSeriesId
uint256 public nextSeriesId = 1
series
mapping(uint256 => Series) public series
seriesCreator
mapping(uint256 => address) public seriesCreator
writerLockedShares
mapping(uint256 => mapping(address => uint256)) public writerLockedShares
writerOptions
mapping(uint256 => mapping(address => uint256)) public writerOptions
totalSeriesShares
mapping(uint256 => uint256) public totalSeriesShares
totalSeriesMinted
mapping(uint256 => uint256) public totalSeriesMinted
totalSeriesExercisedShares
mapping(uint256 => uint256) public totalSeriesExercisedShares
settlementTimestamp
mapping(uint256 => uint256) public settlementTimestamp
Functions
constructor
constructor(
address _splitter,
address _oracle,
address _stakedBear,
address _stakedBull,
address _optionImplementation
) ;
createSeries
Admin function to deploy a new Option Token Series via EIP-1167.
function createSeries(
bool isBull,
uint256 strike,
uint256 expiry,
string memory name,
string memory symbol
) external onlyRole(SERIES_CREATOR_ROLE) returns (uint256 seriesId);
mintOptions
Writers (DOVs) call this to lock splDXY yield-bearing shares and mint options.
Ensures exact 1:1 backing of the underlying asset capacity.
function mintOptions(
uint256 seriesId,
uint256 optionsAmount
) external nonReentrant;
settle
Locks the settlement price and exchange rate at expiration.
Callable by anyone. Triggers “Early Acceleration” if protocol liquidates mid-cycle.
function settle(
uint256 seriesId,
uint80[] calldata roundHints
) external;
Parameters
| Name | Type | Description |
|---|---|---|
seriesId | uint256 | |
roundHints | uint80[] | Chainlink round IDs for oracle lookup (one per feed component). |
adminSettle
Admin fallback for oracle failures — settles with a manually provided price.
2-day grace period after expiry gives the oracle time to recover first.
function adminSettle(
uint256 seriesId,
uint256 settlementPrice
) external onlyRole(DEFAULT_ADMIN_ROLE);
exercise
Buyers burn ITM Options to extract their fractional payout of the collateral pool.
Share conversion uses the settlement-time rate for economically correct payouts.
function exercise(
uint256 seriesId,
uint256 optionsAmount
) external nonReentrant;
unlockCollateral
Writers unlock their remaining splDXY shares post-settlement.
Uses global debt pro-rata to stay consistent with the exercise cap.
globalDebtShares represents the theoretical max debt assuming 100% exercise.
If some option holders don’t exercise, their unclaimed share of globalDebtShares
remains locked until sweepUnclaimedShares is called after 90 days.
function unlockCollateral(
uint256 seriesId
) external nonReentrant;
sweepUnclaimedShares
Sweeps unclaimed exercise shares 90 days after settlement.
Returns shares reserved for unexercised ITM options to the admin for distribution.
function sweepUnclaimedShares(
uint256 seriesId
) external onlyRole(DEFAULT_ADMIN_ROLE) nonReentrant;
_getGlobalDebtShares
function _getGlobalDebtShares(
Series storage s,
uint256 totalMinted,
uint256 totalShares
) private view returns (uint256 globalDebtShares);
_snapshotShareRate
function _snapshotShareRate(
Series storage s
) private view returns (uint256);
_transferVaultShares
function _transferVaultShares(
bool isBull,
address to,
uint256 amount
) private;
Events
SeriesCreated
event SeriesCreated(uint256 indexed seriesId, address optionToken, bool isBull, uint256 strike, uint256 expiry);
OptionsMinted
event OptionsMinted(uint256 indexed seriesId, address indexed writer, uint256 optionsAmount, uint256 sharesLocked);
SeriesSettled
event SeriesSettled(uint256 indexed seriesId, uint256 settlementPrice, uint256 settlementShareRate);
OptionsExercised
event OptionsExercised(
uint256 indexed seriesId, address indexed buyer, uint256 optionsAmount, uint256 sharesReceived
);
CollateralUnlocked
event CollateralUnlocked(
uint256 indexed seriesId, address indexed writer, uint256 optionsAmount, uint256 sharesReturned
);
UnclaimedSharesSwept
event UnclaimedSharesSwept(uint256 indexed seriesId, uint256 sharesSwept);
Errors
MarginEngine__InvalidParams
error MarginEngine__InvalidParams();
MarginEngine__Expired
error MarginEngine__Expired();
MarginEngine__NotExpired
error MarginEngine__NotExpired();
MarginEngine__AlreadySettled
error MarginEngine__AlreadySettled();
MarginEngine__NotSettled
error MarginEngine__NotSettled();
MarginEngine__OptionIsOTM
error MarginEngine__OptionIsOTM();
MarginEngine__ZeroAmount
error MarginEngine__ZeroAmount();
MarginEngine__SplitterNotActive
error MarginEngine__SplitterNotActive();
MarginEngine__AdminSettleTooEarly
error MarginEngine__AdminSettleTooEarly();
MarginEngine__SweepTooEarly
error MarginEngine__SweepTooEarly();
MarginEngine__Unauthorized
error MarginEngine__Unauthorized();
Structs
Series
struct Series {
bool isBull;
uint256 strike;
uint256 expiry;
address optionToken;
uint256 settlementPrice;
uint256 settlementShareRate;
bool isSettled;
}
OptionToken
Title: OptionToken
EIP-1167 Minimal Proxy Implementation for Option Series.
Constants
decimals
uint8 public constant decimals = 18
State Variables
name
string public name
symbol
string public symbol
totalSupply
uint256 public totalSupply
balanceOf
mapping(address => uint256) public balanceOf
allowance
mapping(address => mapping(address => uint256)) public allowance
marginEngine
address public marginEngine
_initialized
bool private _initialized
Functions
constructor
constructor() ;
onlyEngine
modifier onlyEngine() ;
initialize
Initialize the proxy (called once by MarginEngine).
function initialize(
string memory _name,
string memory _symbol,
address _marginEngine
) external;
mint
function mint(
address to,
uint256 amount
) external onlyEngine;
burn
function burn(
address from,
uint256 amount
) external onlyEngine;
approve
function approve(
address spender,
uint256 amount
) external returns (bool);
transfer
function transfer(
address to,
uint256 amount
) external returns (bool);
transferFrom
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
Events
Transfer
event Transfer(address indexed from, address indexed to, uint256 value);
Approval
event Approval(address indexed owner, address indexed spender, uint256 value);
Initialized
event Initialized(string name, string symbol, address marginEngine);
Errors
OptionToken__AlreadyInitialized
error OptionToken__AlreadyInitialized();
OptionToken__Unauthorized
error OptionToken__Unauthorized();
OptionToken__InsufficientBalance
error OptionToken__InsufficientBalance();
OptionToken__InsufficientAllowance
error OptionToken__InsufficientAllowance();
OptionToken__ZeroAddress
error OptionToken__ZeroAddress();
IMarginEngine
Functions
createSeries
function createSeries(
bool isBull,
uint256 strike,
uint256 expiry,
string memory name,
string memory sym
) external returns (uint256);
mintOptions
function mintOptions(
uint256 seriesId,
uint256 optionsAmount
) external;
settle
function settle(
uint256 seriesId,
uint80[] calldata roundHints
) external;
unlockCollateral
function unlockCollateral(
uint256 seriesId
) external;
exercise
function exercise(
uint256 seriesId,
uint256 optionsAmount
) external;
series
function series(
uint256 seriesId
) external view returns (bool, uint256, uint256, address, uint256, uint256, bool);
SPLITTER
function SPLITTER() external view returns (address);
Errors
MarginEngine__ZeroAmount
error MarginEngine__ZeroAmount();
PletherDOV
Inherits: ERC20, ReentrancyGuard, Ownable2Step
Title: PletherDOV
Automated Covered Call Vault for Plether synthetic assets.
Natively holds splDXY to prevent weekly AMM slippage. Implements on-chain Dutch Auctions.
Note: security-contact: contact@plether.com
Constants
MARGIN_ENGINE
IMarginEngine public immutable MARGIN_ENGINE
STAKED_TOKEN
IERC4626 public immutable STAKED_TOKEN
USDC
IERC20 public immutable USDC
IS_BULL
bool public immutable IS_BULL
State Variables
currentState
State public currentState = State.UNLOCKED
currentEpochId
uint256 public currentEpochId = 0
zapKeeper
address public zapKeeper
epochs
mapping(uint256 => Epoch) public epochs
pendingUsdcDeposits
uint256 public pendingUsdcDeposits
userUsdcDeposits
mapping(address => uint256) public userUsdcDeposits
userDepositEpoch
mapping(address => uint256) public userDepositEpoch
epochDeposits
mapping(uint256 => EpochDeposits) public epochDeposits
_preZapSplDXYBalance
uint256 internal _preZapSplDXYBalance
_preZapDepositUsdc
uint256 internal _preZapDepositUsdc
_preZapPremiumUsdc
uint256 internal _preZapPremiumUsdc
_zapSnapshotTaken
bool internal _zapSnapshotTaken
Functions
constructor
constructor(
string memory _name,
string memory _symbol,
address _marginEngine,
address _stakedToken,
address _usdc,
bool _isBull
) ERC20(_name, _symbol) Ownable(msg.sender);
initializeShares
Mints initial shares to the owner for seed capital already held by the vault.
Must be called before the first epoch if the vault holds pre-seeded splDXY.
function initializeShares() external onlyOwner;
deposit
Queue USDC to be deposited into the DOV at the start of the next epoch.
Auto-claims shares from any previous epoch deposit before recording the new one.
function deposit(
uint256 amount
) external nonReentrant;
withdrawDeposit
Withdraw queued USDC that has not yet been processed into an epoch.
function withdrawDeposit(
uint256 amount
) external nonReentrant;
claimShares
Claims DOV shares earned from a previous epoch’s processed deposit.
function claimShares() external nonReentrant;
withdraw
Redeems vault shares for proportional splDXY (and any premium USDC).
Only callable during UNLOCKED. Auto-claims pending deposit shares first.
function withdraw(
uint256 shares
) external nonReentrant;
setZapKeeper
function setZapKeeper(
address _keeper
) external onlyOwner;
releaseUsdcForZap
Releases all USDC held by this DOV to the caller (zapKeeper only).
Snapshots pre-zap state for share calculation in startEpochAuction.
function releaseUsdcForZap() external returns (uint256 amount);
startEpochAuction
Step 1: Rolls the vault into a new epoch, mints options, starts Dutch Auction.
If deposits are pending, releaseUsdcForZap must have been called first to snapshot pre-zap state. Deposit shares are minted proportionally based on the zap conversion.
function startEpochAuction(
uint256 strike,
uint256 expiry,
uint256 maxPremium,
uint256 minPremium,
uint256 duration
) external nonReentrant;
getCurrentOptionPrice
Calculates the current linearly decaying price per option.
function getCurrentOptionPrice() public view returns (uint256);
fillAuction
Step 2: Market Makers call this to buy the entire batch of options.
Premium calculation: optionsMinted (18 decimals) * currentPremium (6 decimals) / 1e18 = totalPremiumUsdc (6 decimals). Relies on OptionToken.decimals() == 18.
function fillAuction() external nonReentrant;
cancelAuction
Cancels an expired auction that received no fill, returning the vault to UNLOCKED.
Also allows immediate cancellation when the Splitter has liquidated.
function cancelAuction() external nonReentrant;
exerciseUnsoldOptions
Exercises unsold option tokens held by the DOV after a cancelled auction.
Skips exercise for OTM options so keeper batch transactions don’t revert.
function exerciseUnsoldOptions(
uint256 epochId
) external nonReentrant;
reclaimCollateral
Reclaims collateral from an unsold series after it has been settled.
function reclaimCollateral(
uint256 epochId
) external nonReentrant;
settleEpoch
Step 3: At expiration, settles the series and unlocks remaining collateral.
function settleEpoch(
uint80[] calldata roundHints
) external nonReentrant;
Parameters
| Name | Type | Description |
|---|---|---|
roundHints | uint80[] | Chainlink round IDs for oracle lookup (one per feed component). Ignored if the series is already settled. |
emergencyWithdraw
Recovers stranded funds after Splitter liquidation (protocol end-of-life).
Only callable by owner when the Splitter has permanently settled.
function emergencyWithdraw(
IERC20 token
) external onlyOwner;
pendingSharesOf
Returns the number of shares a user can claim from a processed deposit.
function pendingSharesOf(
address user
) external view returns (uint256);
totalVaultAssets
Returns the vault’s total assets excluding pending deposits.
function totalVaultAssets() external view returns (uint256 splDXYShares, uint256 usdcBalance);
_claimShares
Claims DOV shares for a user whose deposit was processed in a previous epoch.
function _claimShares(
address user
) internal;
_calculatePendingShares
function _calculatePendingShares(
address user
) private view returns (uint256);
_mintDepositShares
Computes and mints aggregate shares for all depositors whose USDC was zapped. Uses the pre-zap snapshot from releaseUsdcForZap to attribute splDXY proportionally between existing shareholders (premium) and new depositors (deposit USDC).
function _mintDepositShares() internal;
Events
DepositQueued
event DepositQueued(address indexed user, uint256 amount);
DepositWithdrawn
event DepositWithdrawn(address indexed user, uint256 amount);
EpochRolled
event EpochRolled(uint256 indexed epochId, uint256 seriesId, uint256 optionsMinted);
AuctionFilled
event AuctionFilled(uint256 indexed epochId, address indexed buyer, uint256 premiumPaid);
AuctionCancelled
event AuctionCancelled(uint256 indexed epochId);
EpochSettled
event EpochSettled(uint256 indexed epochId, uint256 collateralReturned);
EmergencyWithdraw
event EmergencyWithdraw(address indexed token, uint256 amount);
ZapKeeperSet
event ZapKeeperSet(address indexed keeper);
SharesInitialized
event SharesInitialized(address indexed owner, uint256 shares);
DepositSharesMinted
event DepositSharesMinted(uint256 indexed epochId, uint256 totalShares, uint256 totalDepositsUsdc);
SharesClaimed
event SharesClaimed(address indexed user, uint256 shares);
Withdrawn
event Withdrawn(address indexed user, uint256 shares, uint256 splDXYAmount, uint256 usdcAmount);
Errors
PletherDOV__WrongState
error PletherDOV__WrongState();
PletherDOV__ZeroAmount
error PletherDOV__ZeroAmount();
PletherDOV__AuctionEnded
error PletherDOV__AuctionEnded();
PletherDOV__AuctionNotExpired
error PletherDOV__AuctionNotExpired();
PletherDOV__SplitterNotSettled
error PletherDOV__SplitterNotSettled();
PletherDOV__InvalidParams
error PletherDOV__InvalidParams();
PletherDOV__InsufficientDeposit
error PletherDOV__InsufficientDeposit();
PletherDOV__DepositProcessed
error PletherDOV__DepositProcessed();
PletherDOV__Unauthorized
error PletherDOV__Unauthorized();
PletherDOV__AlreadyInitialized
error PletherDOV__AlreadyInitialized();
PletherDOV__NotInitialized
error PletherDOV__NotInitialized();
PletherDOV__DepositsNotZapped
error PletherDOV__DepositsNotZapped();
PletherDOV__NothingToClaim
error PletherDOV__NothingToClaim();
Structs
Epoch
struct Epoch {
uint256 seriesId;
uint256 optionsMinted;
uint256 auctionStartTime;
uint256 maxPremium; // Max USDC price per option (6 decimals)
uint256 minPremium; // Min USDC price per option (6 decimals)
uint256 auctionDuration;
address winningMaker;
}
EpochDeposits
struct EpochDeposits {
uint256 totalUsdc;
uint256 sharesMinted;
}
Enums
State
enum State {
UNLOCKED,
AUCTIONING,
LOCKED
}
Contents
BasketOracle
Inherits: AggregatorV3Interface, Ownable2Step
Title: BasketOracle
Aggregates multiple Chainlink feeds into a normalized weighted plDXY basket price.
Price = Sum(Weight_i * Price_i / BasePrice_i). Normalization preserves intended currency weights.
Note: security-contact: contact@plether.com
Constants
DECIMALS
Chainlink standard decimals for fiat/USD pairs.
uint8 public constant DECIMALS = 8
DESCRIPTION
Oracle description string.
string public constant DESCRIPTION = "plDXY Fixed Basket (Bounded)"
TIMELOCK_DELAY
Timelock delay for Curve pool updates (7 days).
uint256 public constant TIMELOCK_DELAY = 7 days
MAX_DEVIATION_BPS
Maximum allowed deviation from Curve spot (basis points).
uint256 public immutable MAX_DEVIATION_BPS
CAP
Protocol CAP price (8 decimals). Theoretical price is clamped to this before deviation check.
uint256 public immutable CAP
State Variables
components
Array of currency components (EUR, JPY, GBP, CAD, SEK, CHF).
Component[] public components
curvePool
Curve pool for deviation validation.
ICurvePool public curvePool
pendingCurvePool
Pending Curve pool for timelock-protected updates.
address public pendingCurvePool
hasPendingProposal
Whether a proposal is active (needed because address(0) is a valid proposal).
bool public hasPendingProposal
curvePoolActivationTime
Timestamp when pending Curve pool can be finalized.
uint256 public curvePoolActivationTime
Functions
constructor
Creates basket oracle with currency components.
constructor(
address[] memory _feeds,
uint256[] memory _quantities,
uint256[] memory _basePrices,
uint256 _maxDeviationBps,
uint256 _cap,
address _owner
) Ownable(_owner);
Parameters
| Name | Type | Description |
|---|---|---|
_feeds | address[] | Array of Chainlink feed addresses. |
_quantities | uint256[] | Array of basket weights (1e18 precision). |
_basePrices | uint256[] | Array of base prices for normalization (8 decimals). |
_maxDeviationBps | uint256 | Maximum deviation from Curve (e.g., 200 = 2%). |
_cap | uint256 | Protocol CAP price (8 decimals). Clamps theoretical price in deviation check. |
_owner | address | Admin address for Curve pool management. |
setCurvePool
Sets the Curve pool for deviation validation (initial setup only).
function setCurvePool(
address _curvePool
) external onlyOwner;
Parameters
| Name | Type | Description |
|---|---|---|
_curvePool | address | Curve USDC/plDXY-BEAR pool address. |
proposeCurvePool
Proposes a new Curve pool (requires 7-day timelock).
function proposeCurvePool(
address _newPool
) external onlyOwner;
Parameters
| Name | Type | Description |
|---|---|---|
_newPool | address | New Curve pool address. |
finalizeCurvePool
Finalizes the Curve pool update after timelock expires.
function finalizeCurvePool() external onlyOwner;
latestRoundData
Returns the aggregated basket price from all component feeds.
function latestRoundData() public view returns (uint80, int256, uint256, uint256, uint80);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint80 | roundId Mock round ID (always 1). |
<none> | int256 | answer The calculated basket price in 8 decimals. |
<none> | uint256 | startedAt Oldest component updatedAt (no separate round start; same as updatedAt). |
<none> | uint256 | updatedAt Oldest component updatedAt (weakest link for staleness checks). |
<none> | uint80 | answeredInRound Mock answered round (always 1). |
_checkDeviation
Validates basket price against Curve spot. Reverts on excessive deviation. Intentionally acts as a global circuit breaker: if Chainlink and Curve EMA diverge beyond MAX_DEVIATION_BPS, all consumers (SyntheticSplitter, MorphoOracle, StakedOracle) are frozen — including Morpho liquidations. This is the desired behavior: large divergence signals either a compromised feed or a manipulated pool, and freezing is safer than acting on potentially bad price data.
function _checkDeviation(
uint256 theoreticalDxy8Dec
) internal view;
Parameters
| Name | Type | Description |
|---|---|---|
theoreticalDxy8Dec | uint256 | Computed basket price (8 decimals). |
decimals
Returns oracle decimals (8).
function decimals() external pure returns (uint8);
description
Returns oracle description.
function description() external pure returns (string memory);
version
Returns oracle version (1).
function version() external pure returns (uint256);
getRoundData
Returns data for a specific round ID.
Only round ID 1 is supported (synthetic basket has no historical rounds).
function getRoundData(
uint80 _roundId
) external view returns (uint80, int256, uint256, uint256, uint80);
Events
CurvePoolProposed
Emitted when a new Curve pool is proposed.
event CurvePoolProposed(address indexed newPool, uint256 activationTime);
CurvePoolUpdated
Emitted when Curve pool is updated.
event CurvePoolUpdated(address indexed oldPool, address indexed newPool);
Errors
BasketOracle__InvalidPrice
Thrown when a component feed returns invalid price.
error BasketOracle__InvalidPrice(address feed);
BasketOracle__LengthMismatch
Thrown when feeds and quantities arrays have different lengths.
error BasketOracle__LengthMismatch();
BasketOracle__PriceDeviation
Thrown when basket price deviates too far from Curve spot.
error BasketOracle__PriceDeviation(uint256 theoretical, uint256 spot);
BasketOracle__AlreadySet
Thrown when Curve pool is already configured.
error BasketOracle__AlreadySet();
BasketOracle__TimelockActive
Thrown when timelock period has not elapsed.
error BasketOracle__TimelockActive();
BasketOracle__InvalidProposal
Thrown when no pending proposal exists.
error BasketOracle__InvalidProposal();
BasketOracle__InvalidDeviation
Thrown when max deviation is zero.
error BasketOracle__InvalidDeviation();
BasketOracle__InvalidBasePrice
Thrown when a base price is zero.
error BasketOracle__InvalidBasePrice();
BasketOracle__InvalidWeights
Thrown when quantities don’t sum to 1e18.
error BasketOracle__InvalidWeights();
BasketOracle__InvalidRoundId
error BasketOracle__InvalidRoundId();
Structs
Component
Component feed with its basket weight and base price for normalization.
struct Component {
AggregatorV3Interface feed;
uint256 quantity;
uint256 basePrice;
}
IMorphoOracle
Interface for Morpho-compatible price oracles.
Functions
price
Returns price of 1 collateral unit in loan asset terms (Morpho scale: 36 + loanDec - colDec = 24 decimals).
function price() external view returns (uint256);
MorphoOracle
Inherits: IMorphoOracle
Title: MorphoOracle
Adapts BasketOracle price to Morpho Blue’s oracle scale (24 decimals for USDC/plDXY).
Supports both plDXY-BEAR (direct) and plDXY-BULL (inverse) pricing.
Note: security-contact: contact@plether.com
Constants
BASKET_ORACLE
Source price feed (BasketOracle).
AggregatorV3Interface public immutable BASKET_ORACLE
CAP
Protocol CAP price (8 decimals).
uint256 public immutable CAP
IS_INVERSE
If true, returns CAP - Price (for plDXY-BULL).
bool public immutable IS_INVERSE
STALENESS_TIMEOUT
Maximum age for valid oracle price.
uint256 public constant STALENESS_TIMEOUT = 24 hours
Functions
constructor
Creates Morpho-compatible oracle wrapper.
constructor(
address _basketOracle,
uint256 _cap,
bool _isInverse
) ;
Parameters
| Name | Type | Description |
|---|---|---|
_basketOracle | address | BasketOracle address. |
_cap | uint256 | Protocol CAP (8 decimals, e.g., 2e8 = $2.00). |
_isInverse | bool | True for plDXY-BULL (CAP - Price), false for plDXY-BEAR. |
price
Returns collateral price in Morpho scale (24 decimals for USDC(6)/plDXY(18)).
Morpho expects: 36 + loanDecimals - collateralDecimals = 36 + 6 - 18 = 24. BEAR (IS_INVERSE=false): min(basketPrice, CAP) * CHAINLINK_TO_MORPHO_SCALE. BULL (IS_INVERSE=true): (CAP - basketPrice) * CHAINLINK_TO_MORPHO_SCALE. Returns 1 (not 0) when BULL price would be zero to avoid Morpho division errors.
function price() external view override returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Price of 1 plDXY token in USDC terms (24 decimals). |
Errors
MorphoOracle__InvalidPrice
Thrown when source oracle returns zero or negative price.
error MorphoOracle__InvalidPrice();
MorphoOracle__StalePrice
Thrown when source oracle data is stale.
error MorphoOracle__StalePrice();
MorphoOracle__ZeroAddress
Thrown when zero address provided to constructor.
error MorphoOracle__ZeroAddress();
PythAdapter
Inherits: AggregatorV3Interface
Title: PythAdapter
Adapts Pyth Network price feeds to Chainlink’s AggregatorV3Interface.
Pyth is pull-based: prices must be pushed on-chain before reading. This adapter reads the latest price and converts it to 8 decimals. Supports price inversion for feeds like USD/SEK → SEK/USD.
Note: security-contact: contact@plether.com
Constants
PYTH
IPyth public immutable PYTH
PRICE_ID
bytes32 public immutable PRICE_ID
MAX_STALENESS
uint256 public immutable MAX_STALENESS
MAX_CONFIDENCE_BPS
uint256 public immutable MAX_CONFIDENCE_BPS
INVERSE
bool public immutable INVERSE
DECIMALS
uint8 public constant DECIMALS = 8
State Variables
DESCRIPTION
string public DESCRIPTION
Functions
constructor
constructor(
address pyth_,
bytes32 priceId_,
uint256 maxStaleness_,
string memory description_,
bool inverse_,
uint256 maxConfidenceBps_
) ;
Parameters
| Name | Type | Description |
|---|---|---|
pyth_ | address | Pyth contract address on this chain. |
priceId_ | bytes32 | Pyth price feed ID (e.g., USD/SEK). |
maxStaleness_ | uint256 | Maximum age of price in seconds before considered stale. |
description_ | string | Human-readable description (e.g., “SEK / USD”). |
inverse_ | bool | If true, inverts the price (e.g., USD/SEK → SEK/USD). |
maxConfidenceBps_ | uint256 | Maximum confidence interval as basis points of price (e.g., 500 = 5%). |
latestRoundData
Returns the latest price data in Chainlink-compatible format.
Converts Pyth’s variable exponent to fixed 8 decimals.
Reports block.timestamp as updatedAt (self-attestation): this adapter
validates freshness internally against publishTime using MAX_STALENESS,
then attests “price valid as of now.” Downstream consumers (BasketOracle,
SyntheticSplitter) see a fresh timestamp and skip their own staleness check,
avoiding double-timeout rejection during weekend forex market closures.
function latestRoundData() external view returns (uint80, int256, uint256, uint256, uint80);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint80 | roundId Always returns 1 (Pyth doesn’t use rounds). |
<none> | int256 | answer Price in 8 decimals. |
<none> | uint256 | startedAt Current block timestamp (adapter attestation time). |
<none> | uint256 | updatedAt Current block timestamp (adapter attestation time). |
<none> | uint80 | answeredInRound Always returns 1. |
getRoundData
Returns data for a specific round ID.
Only round ID 1 is supported (Pyth doesn’t use rounds).
function getRoundData(
uint80 _roundId
) external view returns (uint80, int256, uint256, uint256, uint80);
decimals
Returns the number of decimals (always 8 for compatibility).
function decimals() external pure returns (uint8);
description
Returns the price feed description.
function description() external view returns (string memory);
version
Returns the adapter version.
function version() external pure returns (uint256);
updatePrice
Updates the Pyth price feed with new data.
Anyone can call this to push fresh prices on-chain.
function updatePrice(
bytes[] calldata updateData
) external payable;
Parameters
| Name | Type | Description |
|---|---|---|
updateData | bytes[] | Price update data from Pyth’s Hermes API. |
getUpdateFee
Returns the fee required to update the price.
function getUpdateFee(
bytes[] calldata updateData
) external view returns (uint256 fee);
Parameters
| Name | Type | Description |
|---|---|---|
updateData | bytes[] | Price update data to calculate fee for. |
Returns
| Name | Type | Description |
|---|---|---|
fee | uint256 | Fee in wei. |
_convertTo8Decimals
Converts Pyth price to 8 decimals.
Pyth uses variable exponents (e.g., -8, -6). This normalizes to -8.
function _convertTo8Decimals(
int64 price,
int32 expo
) internal pure returns (int256);
Parameters
| Name | Type | Description |
|---|---|---|
price | int64 | Pyth price value. |
expo | int32 | Pyth exponent (negative for decimal places). |
Returns
| Name | Type | Description |
|---|---|---|
<none> | int256 | Normalized price in 8 decimals. |
_invertTo8Decimals
Inverts Pyth price and converts to 8 decimals.
For converting USD/SEK to SEK/USD: 1 / (price * 10^expo) * 10^8
function _invertTo8Decimals(
int64 price,
int32 expo
) internal pure returns (int256);
Parameters
| Name | Type | Description |
|---|---|---|
price | int64 | Pyth price value. |
expo | int32 | Pyth exponent (negative for decimal places). |
Returns
| Name | Type | Description |
|---|---|---|
<none> | int256 | Inverted price in 8 decimals. |
Errors
PythAdapter__StalePrice
error PythAdapter__StalePrice(uint256 publishTime, uint256 maxAge);
PythAdapter__InvalidPrice
error PythAdapter__InvalidPrice();
PythAdapter__ConfidenceTooWide
error PythAdapter__ConfidenceTooWide(uint64 conf, int64 price);
PythAdapter__InvalidRoundId
error PythAdapter__InvalidRoundId();
PythAdapter__RefundFailed
error PythAdapter__RefundFailed();
SettlementOracle
Title: SettlementOracle
Pure theoretical oracle for option settlement.
Uses historical Chainlink round data to look up prices at expiry, eliminating the settlement window and preventing lookback MEV.
Note: security-contact: contact@plether.com
Constants
CAP
uint256 public immutable CAP
SEQUENCER_UPTIME_FEED
AggregatorV3Interface public immutable SEQUENCER_UPTIME_FEED
SEQUENCER_GRACE_PERIOD
uint256 public constant SEQUENCER_GRACE_PERIOD = 1 hours
ORACLE_TIMEOUT
uint256 public constant ORACLE_TIMEOUT = 24 hours
State Variables
components
Component[] public components
Functions
constructor
constructor(
address[] memory _feeds,
uint256[] memory _quantities,
uint256[] memory _basePrices,
uint256 _cap,
address _sequencerUptimeFeed
) ;
getSettlementPrices
Returns the pure theoretical settlement prices at a given expiry timestamp.
function getSettlementPrices(
uint256 expiry,
uint80[] calldata roundHints
) external view returns (uint256 bearPrice, uint256 bullPrice);
Parameters
| Name | Type | Description |
|---|---|---|
expiry | uint256 | The timestamp at which to look up prices. |
roundHints | uint80[] | Caller-provided Chainlink round IDs (one per component) active at expiry. |
Returns
| Name | Type | Description |
|---|---|---|
bearPrice | uint256 | min(BasketPrice, CAP) in 8 decimals |
bullPrice | uint256 | CAP - bearPrice in 8 decimals |
Errors
SettlementOracle__InvalidPrice
error SettlementOracle__InvalidPrice(address feed);
SettlementOracle__LengthMismatch
error SettlementOracle__LengthMismatch();
SettlementOracle__WrongHintCount
error SettlementOracle__WrongHintCount();
SettlementOracle__InvalidBasePrice
error SettlementOracle__InvalidBasePrice();
SettlementOracle__InvalidWeights
error SettlementOracle__InvalidWeights();
Structs
Component
struct Component {
AggregatorV3Interface feed;
uint256 quantity;
uint256 basePrice;
}
IOracle
Interface for price oracles.
Functions
price
Returns price of 1 collateral unit in loan asset terms.
function price() external view returns (uint256);
StakedOracle
Inherits: IOracle
Title: StakedOracle
Prices ERC4626 vault shares by combining underlying price with exchange rate.
Price = UnderlyingPrice * ExchangeRate. Used for splDXY-BEAR/splDXY-BULL in Morpho.
Note: security-contact: contact@plether.com
Constants
VAULT
The staking vault (splDXY-BEAR or splDXY-BULL).
IERC4626 public immutable VAULT
UNDERLYING_ORACLE
Oracle for the underlying plDXY token.
IOracle public immutable UNDERLYING_ORACLE
SHARE_DECIMALS
Decimal multiplier for vault shares (accounts for ERC4626 decimal offset).
uint256 public immutable SHARE_DECIMALS
Functions
constructor
Creates staked oracle for a vault.
constructor(
address _vault,
address _underlyingOracle
) ;
Parameters
| Name | Type | Description |
|---|---|---|
_vault | address | ERC4626 staking vault address. |
_underlyingOracle | address | Price oracle for the underlying plDXY token. |
price
Returns price of 1 vault share including accrued yield.
function price() external view override returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Price in Morpho scale (24 decimals: underlying price * exchange rate). |
Errors
StakedOracle__InvalidPrice
Thrown when underlying oracle returns zero price.
error StakedOracle__InvalidPrice();
StakedOracle__ZeroAddress
Thrown when zero address provided to constructor.
error StakedOracle__ZeroAddress();
Contents
- interfaces
- libraries
- modules
- CfdEngine
- CfdEnginePlanTypes
- CfdMath
- CfdTypes
- HousePool
- MarginClearinghouse
- OrderRouter
- TrancheVault
Contents
- ICfdEngine
- ICfdVault
- IHousePool
- IMarginClearinghouse
- IOrderRouterAccounting
- ITrancheVaultBootstrap
- IWithdrawGuard
ICfdEngine
Stateful CFD trading engine: processes orders, settles funding, and liquidates positions.
Functions
clearinghouse
Margin clearinghouse address used for account margin locking/unlocking
function clearinghouse() external view returns (address);
orderRouter
Current order router allowed to execute orders through the engine.
function orderRouter() external view returns (address);
USDC
Settlement token used for fees, margin, and payouts
function USDC() external view returns (IERC20);
lastMarkPrice
Last mark price observed by the engine (8 decimals)
function lastMarkPrice() external view returns (uint256);
processOrder
Settles funding and processes an open/close order at the given oracle price
function processOrder(
CfdTypes.Order memory order,
uint256 currentOraclePrice,
uint256 vaultDepthUsdc,
uint64 publishTime
) external;
Parameters
| Name | Type | Description |
|---|---|---|
order | CfdTypes.Order | Order to execute (contains accountId, market, direction, size) |
currentOraclePrice | uint256 | Mark price from the oracle (8 decimals) |
vaultDepthUsdc | uint256 | Available vault liquidity, used for open-interest caps (6 decimals) |
publishTime | uint64 | Oracle publish timestamp, used for funding rate accrual |
processOrderTyped
Router-facing order execution entrypoint with typed business-rule failures.
Reverts with CfdEngine__TypedOrderFailure for expected order invalidations so the
router can apply deterministic failed-order bounty policy without selector matching.
function processOrderTyped(
CfdTypes.Order memory order,
uint256 currentOraclePrice,
uint256 vaultDepthUsdc,
uint64 publishTime
) external;
previewOpenRevertCode
Returns the current open-path revert code using canonical vault depth and a caller-supplied oracle snapshot.
function previewOpenRevertCode(
bytes32 accountId,
CfdTypes.Side side,
uint256 sizeDelta,
uint256 marginDelta,
uint256 oraclePrice,
uint64 publishTime
) external view returns (uint8 code);
previewOpenFailurePolicyCategory
Returns the semantic commit-time policy category for the current open-path invalidation, if any.
function previewOpenFailurePolicyCategory(
bytes32 accountId,
CfdTypes.Side side,
uint256 sizeDelta,
uint256 marginDelta,
uint256 oraclePrice,
uint64 publishTime
) external view returns (CfdEnginePlanTypes.OpenFailurePolicyCategory category);
recordDeferredClearerBounty
Records a deferred clearer bounty when immediate vault payment is unavailable.
Deferred keeper bounties are later claimed as clearinghouse credit, not direct wallet transfer.
function recordDeferredClearerBounty(
address keeper,
uint256 amountUsdc
) external;
getDeferredClaimHead
function getDeferredClaimHead() external view returns (DeferredClaim memory claim);
getDeferredTraderStatus
function getDeferredTraderStatus(
bytes32 accountId
) external view returns (DeferredTraderStatus memory status);
getDeferredClearerStatus
function getDeferredClearerStatus(
address keeper
) external view returns (DeferredClearerStatus memory status);
reserveCloseOrderExecutionBounty
Reserves close-order execution bounty from free settlement first, then active position margin.
function reserveCloseOrderExecutionBounty(
bytes32 accountId,
uint256 amountUsdc,
address recipient
) external;
absorbRouterCancellationFee
Pulls router-custodied cancellation fees into protocol revenue.
function absorbRouterCancellationFee(
uint256 amountUsdc
) external;
recordRouterProtocolFee
Books router-delivered protocol-owned inflow as accumulated fees after the router has already paid the vault.
function recordRouterProtocolFee(
uint256 amountUsdc
) external;
liquidatePosition
Liquidates an undercollateralized position, returns keeper bounty in USDC
function liquidatePosition(
bytes32 accountId,
uint256 currentOraclePrice,
uint256 vaultDepthUsdc,
uint64 publishTime
) external returns (uint256 keeperBountyUsdc);
Parameters
| Name | Type | Description |
|---|---|---|
accountId | bytes32 | Account holding the position to liquidate |
currentOraclePrice | uint256 | Mark price from the oracle (8 decimals) |
vaultDepthUsdc | uint256 | Available vault liquidity (6 decimals) |
publishTime | uint64 | Oracle publish timestamp |
Returns
| Name | Type | Description |
|---|---|---|
keeperBountyUsdc | uint256 | Bounty paid to the liquidation keeper (6 decimals) |
previewLiquidation
Canonical liquidation preview using the vault’s current accounted depth.
function previewLiquidation(
bytes32 accountId,
uint256 oraclePrice
) external view returns (LiquidationPreview memory preview);
simulateLiquidation
Hypothetical liquidation simulation at a caller-supplied vault depth.
function simulateLiquidation(
bytes32 accountId,
uint256 oraclePrice,
uint256 vaultDepthUsdc
) external view returns (LiquidationPreview memory preview);
getSideState
Returns the accounting state for a given side.
function getSideState(
CfdTypes.Side side
) external view returns (SideState memory);
getMaxLiability
Worst-case directional liability after taking the max of bull/bear payout bounds.
function getMaxLiability() external view returns (uint256);
getAccountLedgerView
Compact per-account ledger view spanning clearinghouse, router escrow, and deferred trader payout state.
function getAccountLedgerView(
bytes32 accountId
) external view returns (AccountLedgerView memory viewData);
getAccountLedgerSnapshot
Expanded per-account ledger snapshot for debugging account health and settlement reachability across protocol components.
function getAccountLedgerSnapshot(
bytes32 accountId
) external view returns (AccountLedgerSnapshot memory snapshot);
getProtocolAccountingSnapshot
Canonical protocol-wide accounting snapshot across physical assets, liabilities, fees, bad debt, and deferred obligations.
function getProtocolAccountingSnapshot() external view returns (ProtocolAccountingSnapshot memory snapshot);
accumulatedFeesUsdc
Accumulated execution fees awaiting withdrawal (6 decimals)
function accumulatedFeesUsdc() external view returns (uint256);
getWithdrawalReservedUsdc
Total withdrawal reserve required by current protocol liabilities.
function getWithdrawalReservedUsdc() external view returns (uint256);
getHousePoolInputSnapshot
Canonical accounting snapshot consumed by HousePool.
function getHousePoolInputSnapshot(
uint256 markStalenessLimit
) external view returns (HousePoolInputSnapshot memory snapshot);
Parameters
| Name | Type | Description |
|---|---|---|
markStalenessLimit | uint256 | Normal live-market staleness limit configured by HousePool. |
getHousePoolStatusSnapshot
Canonical non-accounting market/status snapshot consumed by HousePool.
function getHousePoolStatusSnapshot() external view returns (HousePoolStatusSnapshot memory snapshot);
totalDeferredPayoutUsdc
Deferred profitable-close payouts still owed to traders.
function totalDeferredPayoutUsdc() external view returns (uint256);
totalDeferredClearerBountyUsdc
Deferred liquidation bounties still owed after failed immediate payout.
function totalDeferredClearerBountyUsdc() external view returns (uint256);
deferredClaimHeadId
function deferredClaimHeadId() external view returns (uint64);
getUnrealizedTraderPnl
Aggregate unrealized PnL of all open positions at lastMarkPrice. Positive = traders winning (house liability). Negative = traders losing (house asset).
function getUnrealizedTraderPnl() external view returns (int256);
getUnrealizedFundingPnl
Aggregate unrealized funding PnL across all open positions. Positive = traders are net funding receivers (vault liability).
function getUnrealizedFundingPnl() external view returns (int256);
getCappedFundingPnl
Aggregate unrealized funding PnL with negative per-side funding capped by backing margin. Positive = traders are net funding receivers after clipping uncollectible debts.
function getCappedFundingPnl() external view returns (int256);
getLiabilityOnlyFundingPnl
Aggregate funding liabilities only, excluding any trader debts owed to the vault. Used by withdrawal firewalls that must assume funding receivables are uncollectible until physically seized.
function getLiabilityOnlyFundingPnl() external view returns (uint256);
getVaultMtmAdjustment
Combined MtM liability: per-side (PnL + funding), clamped at zero. Positive = vault owes traders (unrealized liability). Zero = traders losing or neutral. Unrealized trader losses are not counted as vault assets.
function getVaultMtmAdjustment() external view returns (uint256);
lastMarkTime
Timestamp of the last mark price update
function lastMarkTime() external view returns (uint64);
hasLiveLiability
Returns true when the engine currently has open bounded liability that depends on mark freshness.
function hasLiveLiability() external view returns (bool);
syncFunding
Materializes accrued funding into storage so subsequent reads reflect current state.
function syncFunding() external;
updateMarkPrice
Push a fresh mark price without processing an order
function updateMarkPrice(
uint256 price,
uint64 publishTime
) external;
Parameters
| Name | Type | Description |
|---|---|---|
price | uint256 | New mark price (8 decimals) |
publishTime | uint64 | Oracle publish timestamp for the price update |
CAP_PRICE
Protocol cap price (8 decimals). Oracle prices are clamped to this.
function CAP_PRICE() external view returns (uint256);
isFadWindow
True during weekend FX closure or admin-configured FAD days
function isFadWindow() external view returns (bool);
fadMaxStaleness
Maximum oracle staleness allowed during FAD windows
function fadMaxStaleness() external view returns (uint256);
isOracleFrozen
True only when FX markets are actually closed and oracle freshness can be relaxed.
function isOracleFrozen() external view returns (bool);
hasOpenPosition
Returns true when the account currently has an open position.
function hasOpenPosition(
bytes32 accountId
) external view returns (bool);
getPositionSize
Returns the current position size for an account (18 decimals).
function getPositionSize(
bytes32 accountId
) external view returns (uint256);
getPositionSide
Returns the stored side for an open position.
function getPositionSide(
bytes32 accountId
) external view returns (CfdTypes.Side);
degradedMode
True when the engine has latched degraded mode after a close revealed insolvency.
function degradedMode() external view returns (bool);
fadDayOverrides
Whether a given day number is an admin-configured FAD override
function fadDayOverrides(
uint256 dayNumber
) external view returns (bool);
getProtocolPhase
function getProtocolPhase() external view returns (ProtocolPhase);
getProtocolStatus
function getProtocolStatus() external view returns (ProtocolStatus memory);
Errors
CfdEngine__TypedOrderFailure
error CfdEngine__TypedOrderFailure(
CfdEnginePlanTypes.ExecutionFailurePolicyCategory failureCategory, uint8 failureCode, bool isClose
);
CfdEngine__MarkPriceOutOfOrder
error CfdEngine__MarkPriceOutOfOrder();
Structs
AccountLedgerView
Compact per-account ledger view spanning trader-owned settlement buckets and router-reserved order state.
settlementBalanceUsdc, freeSettlementUsdc, activePositionMarginUsdc, otherLockedMarginUsdc, and
deferredPayoutUsdc are trader-owned value or obligations recorded by the protocol.
activePositionMarginUsdc is the canonical clearinghouse custody bucket for live position backing, while
engine economic position margin is exposed separately on AccountLedgerSnapshot.margin.
executionEscrowUsdc is router-custodied order bounty escrow attributed to the account.
committedMarginUsdc remains trader-owned settlement reserved for queued orders inside the clearinghouse.
struct AccountLedgerView {
uint256 settlementBalanceUsdc;
uint256 freeSettlementUsdc;
uint256 activePositionMarginUsdc;
uint256 otherLockedMarginUsdc;
uint256 executionEscrowUsdc;
uint256 committedMarginUsdc;
uint256 deferredPayoutUsdc;
uint256 pendingOrderCount;
}
AccountLedgerSnapshot
Expanded per-account ledger snapshot for debugging account health, reachability, and queued-order state.
Extends AccountLedgerView with typed clearinghouse locked-margin buckets, terminal settlement reachability,
equity, buying power, and live position risk.
margin is the engine’s economic position margin, while positionMarginBucketUsdc is the clearinghouse
custody bucket that should back it.
struct AccountLedgerSnapshot {
uint256 settlementBalanceUsdc;
uint256 freeSettlementUsdc;
uint256 activePositionMarginUsdc;
uint256 otherLockedMarginUsdc;
uint256 positionMarginBucketUsdc;
uint256 committedOrderMarginBucketUsdc;
uint256 reservedSettlementBucketUsdc;
uint256 executionEscrowUsdc;
uint256 committedMarginUsdc;
uint256 deferredPayoutUsdc;
uint256 pendingOrderCount;
uint256 closeReachableUsdc;
uint256 terminalReachableUsdc;
uint256 accountEquityUsdc;
uint256 freeBuyingPowerUsdc;
bool hasPosition;
CfdTypes.Side side;
uint256 size;
uint256 margin;
uint256 entryPrice;
int256 unrealizedPnlUsdc;
int256 pendingFundingUsdc;
int256 netEquityUsdc;
bool liquidatable;
}
ProtocolAccountingSnapshot
struct ProtocolAccountingSnapshot {
uint256 vaultAssetsUsdc;
uint256 netPhysicalAssetsUsdc;
uint256 maxLiabilityUsdc;
uint256 effectiveSolvencyAssetsUsdc;
uint256 withdrawalReservedUsdc;
uint256 freeUsdc;
uint256 accumulatedFeesUsdc;
uint256 accumulatedBadDebtUsdc;
int256 cappedFundingPnlUsdc;
uint256 liabilityOnlyFundingPnlUsdc;
uint256 totalDeferredPayoutUsdc;
uint256 totalDeferredClearerBountyUsdc;
bool degradedMode;
bool hasLiveLiability;
}
HousePoolInputSnapshot
struct HousePoolInputSnapshot {
uint256 physicalAssetsUsdc;
uint256 netPhysicalAssetsUsdc;
uint256 maxLiabilityUsdc;
uint256 withdrawalFundingLiabilityUsdc;
uint256 unrealizedMtmLiabilityUsdc;
uint256 deferredTraderPayoutUsdc;
uint256 deferredClearerBountyUsdc;
uint256 protocolFeesUsdc;
bool markFreshnessRequired;
uint256 maxMarkStaleness;
}
HousePoolStatusSnapshot
struct HousePoolStatusSnapshot {
uint64 lastMarkTime;
bool oracleFrozen;
bool degradedMode;
}
SideState
struct SideState {
uint256 maxProfitUsdc;
uint256 openInterest;
uint256 entryNotional;
uint256 totalMargin;
int256 fundingIndex;
int256 entryFunding;
}
LiquidationPreview
struct LiquidationPreview {
bool liquidatable;
uint256 oraclePrice;
int256 equityUsdc;
int256 pnlUsdc;
int256 fundingUsdc;
uint256 reachableCollateralUsdc;
uint256 keeperBountyUsdc;
uint256 seizedCollateralUsdc;
uint256 settlementRetainedUsdc;
uint256 freshTraderPayoutUsdc;
uint256 existingDeferredConsumedUsdc;
uint256 existingDeferredRemainingUsdc;
uint256 immediatePayoutUsdc;
uint256 deferredPayoutUsdc;
uint256 badDebtUsdc;
bool triggersDegradedMode;
bool postOpDegradedMode;
uint256 effectiveAssetsAfterUsdc;
uint256 maxLiabilityAfterUsdc;
int256 solvencyFundingPnlUsdc;
}
DeferredClaim
struct DeferredClaim {
DeferredClaimType claimType;
bytes32 accountId;
address keeper;
uint256 remainingUsdc;
uint64 prevClaimId;
uint64 nextClaimId;
}
DeferredTraderStatus
struct DeferredTraderStatus {
uint64 claimId;
uint256 deferredPayoutUsdc;
bool isHead;
bool claimableNow;
}
DeferredClearerStatus
struct DeferredClearerStatus {
uint64 claimId;
uint256 deferredBountyUsdc;
bool isHead;
bool claimableNow;
}
ProtocolStatus
struct ProtocolStatus {
ProtocolPhase phase;
uint64 lastMarkTime;
uint256 lastMarkPrice;
bool oracleFrozen;
bool fadWindow;
uint256 fadMaxStaleness;
}
Enums
DeferredClaimType
enum DeferredClaimType {
TraderPayout,
ClearerBounty
}
ProtocolPhase
High-level protocol lifecycle used by external status consumers.
Active means the engine is wired and the vault has enabled live risk-taking.
enum ProtocolPhase {
Configuring,
Active,
Degraded
}
ICfdVault
Vault that custodies USDC backing the CFD trading system.
Functions
totalAssets
Canonical economic USDC backing recognized by the vault (6 decimals). Ignores unsolicited positive token transfers until explicitly accounted, but still reflects raw-balance shortfalls if assets leave the vault unexpectedly.
function totalAssets() external view returns (uint256);
payOut
Transfers USDC from the vault to a recipient
function payOut(
address recipient,
uint256 amount
) external;
Parameters
| Name | Type | Description |
|---|---|---|
recipient | address | Address to receive USDC |
amount | uint256 | USDC amount to transfer (6 decimals) |
recordProtocolInflow
Increases canonical vault assets to recognize a legitimate protocol-owned inflow.
This is the controlled accounting path for endogenous protocol gains that should
increase economic vault depth. It does not require raw excess to be present and may
also be used to restore canonical accounting after a raw-balance shortfall has already
reduced totalAssets() via the min(rawBalance, accountedAssets) boundary.
Reverts if the caller is unauthorized.
function recordProtocolInflow(
uint256 amount
) external;
recordRecapitalizationInflow
Records an explicit recapitalization inflow intended to restore senior first.
function recordRecapitalizationInflow(
uint256 amount
) external;
recordTradingRevenueInflow
Records LP-owned trading revenue and directly attaches it to seeded claimants when both tranches are otherwise at zero principal.
function recordTradingRevenueInflow(
uint256 amount
) external;
markStalenessLimit
Maximum age for mark price freshness checks outside FAD mode (seconds)
function markStalenessLimit() external view returns (uint256);
isSeedLifecycleComplete
Returns true once both tranche seed positions exist.
function isSeedLifecycleComplete() external view returns (bool);
hasSeedLifecycleStarted
Returns true if bootstrap seeding has started for either tranche.
function hasSeedLifecycleStarted() external view returns (bool);
canAcceptOrdinaryDeposits
Returns true once ordinary LP deposits are allowed.
function canAcceptOrdinaryDeposits() external view returns (bool);
canIncreaseRisk
Returns true once risk-increasing trader actions are allowed.
function canIncreaseRisk() external view returns (bool);
isTradingActive
Returns true if owner has activated trading after seed completion.
function isTradingActive() external view returns (bool);
IHousePool
Two-tranche USDC pool that acts as counterparty to CFD traders. Senior tranche earns fixed yield; junior absorbs first-loss and excess profit.
Functions
seniorPrincipal
Total USDC attributed to the senior tranche (6 decimals)
function seniorPrincipal() external view returns (uint256);
juniorPrincipal
Total USDC attributed to the junior tranche (6 decimals)
function juniorPrincipal() external view returns (uint256);
seniorHighWaterMark
Senior high-water mark used to block dilutive recapitalizing deposits.
function seniorHighWaterMark() external view returns (uint256);
unassignedAssets
Accounted LP assets currently quarantined pending explicit bootstrap / assignment (6 decimals)
function unassignedAssets() external view returns (uint256);
depositSenior
function depositSenior(
uint256 amount
) external;
withdrawSenior
function withdrawSenior(
uint256 amount,
address receiver
) external;
depositJunior
function depositJunior(
uint256 amount
) external;
withdrawJunior
function withdrawJunior(
uint256 amount,
address receiver
) external;
assignUnassignedAssets
Explicitly bootstraps quarantined LP assets into a tranche and mints matching shares.
function assignUnassignedAssets(
bool toSenior,
address receiver
) external;
initializeSeedPosition
Seeds a tranche with permanent share-backed minimum ownership using real USDC.
Canonical deployment should initialize both tranche seeds before enabling ordinary LP lifecycle.
function initializeSeedPosition(
bool toSenior,
uint256 amount,
address receiver
) external;
getMaxSeniorWithdraw
Max withdrawable by senior, capped by free USDC
function getMaxSeniorWithdraw() external view returns (uint256);
getMaxJuniorWithdraw
Max withdrawable by junior, subordinated behind senior
function getMaxJuniorWithdraw() external view returns (uint256);
getPendingTrancheState
Read-only tranche state as if reconcile() ran immediately with current inputs.
function getPendingTrancheState()
external
view
returns (
uint256 seniorPrincipalUsdc,
uint256 juniorPrincipalUsdc,
uint256 maxSeniorWithdrawUsdc,
uint256 maxJuniorWithdrawUsdc
);
Returns
| Name | Type | Description |
|---|---|---|
seniorPrincipalUsdc | uint256 | Simulated senior principal after reconcile (6 decimals) |
juniorPrincipalUsdc | uint256 | Simulated junior principal after reconcile (6 decimals) |
maxSeniorWithdrawUsdc | uint256 | Simulated senior withdrawal cap after reconcile (6 decimals) |
maxJuniorWithdrawUsdc | uint256 | Simulated junior withdrawal cap after reconcile (6 decimals) |
reconcile
Settles revenue/loss waterfall between tranches
function reconcile() external;
isWithdrawalLive
Whether withdrawals are currently possible (not degraded, mark is fresh)
function isWithdrawalLive() external view returns (bool);
hasSeedLifecycleStarted
function hasSeedLifecycleStarted() external view returns (bool);
canAcceptOrdinaryDeposits
function canAcceptOrdinaryDeposits() external view returns (bool);
canAcceptTrancheDeposits
function canAcceptTrancheDeposits(
bool isSenior
) external view returns (bool);
canIncreaseRisk
function canIncreaseRisk() external view returns (bool);
isTradingActive
function isTradingActive() external view returns (bool);
IMarginClearinghouse
USDC-only cross-margin account system that holds settlement balances and settles PnL for CFD positions.
Functions
balanceUsdc
Returns the settlement USDC balance for an account.
function balanceUsdc(
bytes32 accountId
) external view returns (uint256);
lockedMarginUsdc
Returns the locked USDC margin for an account
function lockedMarginUsdc(
bytes32 accountId
) external view returns (uint256);
getLockedMarginBuckets
Returns the typed locked-margin buckets for an account.
function getLockedMarginBuckets(
bytes32 accountId
) external view returns (LockedMarginBuckets memory buckets);
getOrderReservation
Returns the reservation record for a specific order id.
function getOrderReservation(
uint64 orderId
) external view returns (OrderReservation memory reservation);
getAccountReservationSummary
Returns the aggregate active reservation summary for an account.
function getAccountReservationSummary(
bytes32 accountId
) external view returns (AccountReservationSummary memory summary);
lockPositionMargin
Locks trader-owned settlement into the active position margin bucket.
function lockPositionMargin(
bytes32 accountId,
uint256 amountUsdc
) external;
unlockPositionMargin
Unlocks active position margin back into free settlement.
function unlockPositionMargin(
bytes32 accountId,
uint256 amountUsdc
) external;
lockCommittedOrderMargin
Locks trader-owned settlement into the committed-order bucket reserved for queued open orders.
function lockCommittedOrderMargin(
bytes32 accountId,
uint256 amountUsdc
) external;
reserveCommittedOrderMargin
Reserves committed-order margin for a specific order id inside the clearinghouse reservation ledger.
function reserveCommittedOrderMargin(
bytes32 accountId,
uint64 orderId,
uint256 amountUsdc
) external;
unlockCommittedOrderMargin
Unlocks committed-order margin back into free settlement when a queued open order is released.
function unlockCommittedOrderMargin(
bytes32 accountId,
uint256 amountUsdc
) external;
releaseOrderReservation
Releases any remaining reservation balance for an order back into free settlement.
function releaseOrderReservation(
uint64 orderId
) external returns (uint256 releasedUsdc);
releaseOrderReservationIfActive
Releases any remaining reservation balance for an order if it is still active.
function releaseOrderReservationIfActive(
uint64 orderId
) external returns (uint256 releasedUsdc);
consumeOrderReservation
Consumes a specific amount from an order reservation, capped by its remaining balance.
function consumeOrderReservation(
uint64 orderId,
uint256 amountUsdc
) external returns (uint256 consumedUsdc);
consumeAccountOrderReservations
Consumes active order reservations for an account in FIFO reservation order.
function consumeAccountOrderReservations(
bytes32 accountId,
uint256 amountUsdc
) external returns (uint256 consumedUsdc);
consumeOrderReservationsById
Consumes the supplied active order reservations in FIFO order until the requested amount is exhausted.
function consumeOrderReservationsById(
uint64[] calldata orderIds,
uint256 amountUsdc
) external returns (uint256 consumedUsdc);
lockReservedSettlement
Locks settlement into a reserved bucket excluded from generic order/position margin release paths.
function lockReservedSettlement(
bytes32 accountId,
uint256 amountUsdc
) external;
unlockReservedSettlement
Unlocks settlement from the reserved bucket back into free settlement.
function unlockReservedSettlement(
bytes32 accountId,
uint256 amountUsdc
) external;
settleUsdc
Adjusts USDC balance for funding, PnL, or rebates (+credit, -debit)
function settleUsdc(
bytes32 accountId,
int256 amount
) external;
creditSettlementAndLockMargin
Credits settlement USDC and locks the same amount as active margin.
function creditSettlementAndLockMargin(
bytes32 accountId,
uint256 amountUsdc
) external;
applyOpenCost
Applies an open/increase trade cost by debiting or crediting settlement and updating locked margin.
function applyOpenCost(
bytes32 accountId,
uint256 marginDeltaUsdc,
int256 tradeCostUsdc,
address recipient
) external returns (int256 netMarginChangeUsdc);
consumeFundingLoss
Consumes funding loss from free settlement plus the active position margin bucket.
function consumeFundingLoss(
bytes32 accountId,
uint256 lockedPositionMarginUsdc,
uint256 lossUsdc,
address recipient
) external returns (uint256 marginConsumedUsdc, uint256 freeSettlementConsumedUsdc, uint256 uncoveredUsdc);
consumeCloseLoss
Consumes close-path losses from settlement buckets while preserving the remaining live position margin and reserved escrow.
function consumeCloseLoss(
bytes32 accountId,
uint64[] calldata reservationOrderIds,
uint256 lossUsdc,
uint256 protectedLockedMarginUsdc,
bool includeOtherLockedMargin,
address recipient
) external returns (uint256 seizedUsdc, uint256 shortfallUsdc);
applyLiquidationSettlementPlan
Applies a pre-planned liquidation settlement mutation while preserving reserved escrow.
function applyLiquidationSettlementPlan(
bytes32 accountId,
uint64[] calldata reservationOrderIds,
LiquidationSettlementPlan calldata plan,
address recipient
) external returns (uint256 seizedUsdc);
seizeUsdc
Transfers settlement USDC from an account to a recipient (losses, fees, or bad debt)
function seizeUsdc(
bytes32 accountId,
uint256 amount,
address recipient
) external;
seizePositionMarginUsdc
Transfers settlement USDC from active position margin to a recipient and unlocks the same amount.
function seizePositionMarginUsdc(
bytes32 accountId,
uint256 amount,
address recipient
) external;
getAccountUsdcBuckets
function getAccountUsdcBuckets(
bytes32 accountId
) external view returns (AccountUsdcBuckets memory buckets);
getAccountEquityUsdc
Returns total account equity in settlement USDC (6 decimals)
function getAccountEquityUsdc(
bytes32 accountId
) external view returns (uint256);
getFreeBuyingPowerUsdc
Returns strictly free buying power after subtracting locked margin (6 decimals)
function getFreeBuyingPowerUsdc(
bytes32 accountId
) external view returns (uint256);
getFreeSettlementBalanceUsdc
Returns free settlement-asset balance after subtracting locked margin (6 decimals)
function getFreeSettlementBalanceUsdc(
bytes32 accountId
) external view returns (uint256);
getTerminalReachableUsdc
Returns settlement-asset balance reachable during a terminal settlement path.
function getTerminalReachableUsdc(
bytes32 accountId
) external view returns (uint256);
getSettlementReachableUsdc
Returns settlement-asset balance reachable for a terminal or partial settlement path.
Protects only the explicitly supplied remaining locked margin bucket and treats all other settlement-asset balance as reachable for loss collection.
function getSettlementReachableUsdc(
bytes32 accountId,
uint256 protectedLockedMarginUsdc
) external view returns (uint256);
Structs
LockedMarginBuckets
struct LockedMarginBuckets {
// Canonical custody bucket backing currently live positions.
uint256 positionMarginUsdc;
uint256 committedOrderMarginUsdc;
uint256 reservedSettlementUsdc;
uint256 totalLockedMarginUsdc;
}
OrderReservation
struct OrderReservation {
bytes32 accountId;
ReservationBucket bucket;
ReservationStatus status;
uint96 originalAmountUsdc;
uint96 remainingAmountUsdc;
}
AccountReservationSummary
struct AccountReservationSummary {
uint256 activeCommittedOrderMarginUsdc;
uint256 activeReservedSettlementUsdc;
uint256 activeReservationCount;
}
AccountUsdcBuckets
struct AccountUsdcBuckets {
uint256 settlementBalanceUsdc;
uint256 totalLockedMarginUsdc;
uint256 activePositionMarginUsdc;
uint256 otherLockedMarginUsdc;
uint256 freeSettlementUsdc;
}
LiquidationSettlementPlan
struct LiquidationSettlementPlan {
uint256 settlementRetainedUsdc;
uint256 settlementSeizedUsdc;
uint256 freshTraderPayoutUsdc;
uint256 badDebtUsdc;
uint256 positionMarginUnlockedUsdc;
uint256 otherLockedMarginUnlockedUsdc;
}
Enums
MarginBucket
enum MarginBucket {
Position,
CommittedOrder,
ReservedSettlement
}
ReservationBucket
enum ReservationBucket {
CommittedOrder,
ReservedSettlement
}
ReservationStatus
enum ReservationStatus {
None,
Active,
Consumed,
Released
}
IOrderRouterAccounting
Shared accounting-facing subset of OrderRouter used by engine views and margin bookkeeping.
Functions
syncMarginQueue
Prunes any zero-remaining committed-order reservations out of the router’s margin queue for an account.
function syncMarginQueue(
bytes32 accountId
) external;
getAccountEscrow
Returns aggregate queued escrow attributed to an account across all pending orders.
function getAccountEscrow(
bytes32 accountId
) external view returns (AccountEscrowView memory escrow);
getAccountOrderSummary
Returns aggregate queued order state attributed to an account.
Committed margin value is derived from the clearinghouse reservation summary; the router only owns queue structure.
function getAccountOrderSummary(
bytes32 accountId
) external view returns (AccountOrderSummary memory summary);
getPendingOrdersForAccount
Returns pending orders for an account with escrow-aware queue details.
function getPendingOrdersForAccount(
bytes32 accountId
) external view returns (PendingOrderView[] memory pending);
pendingOrderCounts
Returns the number of pending orders currently attributed to an account.
function pendingOrderCounts(
bytes32 accountId
) external view returns (uint256);
getMarginReservationIds
Returns the current router-maintained margin-queue order ids for an account in FIFO order.
This is a structural traversal helper; committed-margin value remains owned by the clearinghouse reservation ledger.
function getMarginReservationIds(
bytes32 accountId
) external view returns (uint64[] memory orderIds);
Structs
AccountEscrowView
Router/accounting view of queued order escrow attributed to an account.
committedMarginUsdc is derived from canonical MarginClearinghouse reservation state.
executionBountyUsdc is router-custodied bounty escrow reserved for queued orders.
struct AccountEscrowView {
uint256 committedMarginUsdc;
uint256 executionBountyUsdc;
uint256 pendingOrderCount;
}
AccountOrderSummary
struct AccountOrderSummary {
uint256 pendingOrderCount;
uint256 pendingCloseSize;
uint256 committedMarginUsdc;
uint256 executionBountyUsdc;
bool hasTerminalCloseQueued;
}
PendingOrderView
struct PendingOrderView {
uint64 orderId;
bool isClose;
CfdTypes.Side side;
uint256 sizeDelta;
uint256 marginDelta;
uint256 targetPrice;
uint64 commitTime;
uint64 commitBlock;
uint64 retryAfterTimestamp;
uint256 committedMarginUsdc;
uint256 executionBountyUsdc;
}
Enums
OrderStatus
enum OrderStatus {
None,
Pending,
Executed,
Failed
}
ITrancheVaultBootstrap
Functions
previewDeposit
function previewDeposit(
uint256 assets
) external view returns (uint256 shares);
bootstrapMint
function bootstrapMint(
uint256 shares,
address receiver
) external;
configureSeedPosition
function configureSeedPosition(
address receiver,
uint256 floorShares
) external;
IWithdrawGuard
Pre-withdrawal hook that prevents clearinghouse withdrawals while positions are open.
Functions
checkWithdraw
Reverts if the account is not allowed to withdraw from the clearinghouse. Implementations may inspect current clearinghouse balances after tentative debit.
function checkWithdraw(
bytes32 accountId
) external view;
Parameters
| Name | Type | Description |
|---|---|---|
accountId | bytes32 | Cross-margin account to check |
Contents
- CashPriorityLib
- CfdEnginePlanLib
- CfdEnginePlanLib constants
- CfdEngineSettlementLib
- CfdEngineSnapshotsLib
- CloseAccountingLib
- HousePoolAccountingLib
- HousePoolFreshnessLib
- HousePoolPendingLivePlanLib
- HousePoolPendingPreviewLib
- HousePoolReconcilePlanLib
- HousePoolSeedLifecycleLib
- HousePoolTrancheGateLib
- HousePoolWaterfallAccountingLib
- HousePoolWithdrawalPreviewLib
- LiquidationAccountingLib
- MarginClearinghouseAccountingLib
- MarketCalendarLib
- OpenAccountingLib
- OrderFailurePolicyLib
- OrderOraclePolicyLib
- PositionRiskAccountingLib
- SolvencyAccountingLib
- WithdrawalAccountingLib
CashPriorityLib
Functions
reserveFreshPayouts
function reserveFreshPayouts(
uint256 physicalAssetsUsdc,
uint256 protocolFeesUsdc,
uint256 deferredTraderPayoutUsdc,
uint256 deferredClearerBountyUsdc
) internal pure returns (SeniorCashReservation memory reservation);
reserveDeferredHeadClaim
function reserveDeferredHeadClaim(
uint256 physicalAssetsUsdc,
uint256 protocolFeesUsdc,
uint256 deferredTraderPayoutUsdc,
uint256 deferredClearerBountyUsdc,
uint256 headClaimAmountUsdc
) internal pure returns (SeniorCashReservation memory reservation);
reservedSeniorCashUsdc
function reservedSeniorCashUsdc(
uint256 deferredTraderPayoutUsdc,
uint256 deferredClearerBountyUsdc
) internal pure returns (uint256);
availableCashForFreshPayouts
function availableCashForFreshPayouts(
uint256 physicalAssetsUsdc,
uint256 protocolFeesUsdc,
uint256 deferredTraderPayoutUsdc,
uint256 deferredClearerBountyUsdc
) internal pure returns (uint256);
availableCashForDeferredClaim
function availableCashForDeferredClaim(
uint256 physicalAssetsUsdc,
uint256 protocolFeesUsdc,
uint256 deferredTraderPayoutUsdc,
uint256 deferredClearerBountyUsdc,
uint256 claimAmountUsdc
) internal pure returns (uint256);
availableCashForProtocolFeeWithdrawal
function availableCashForProtocolFeeWithdrawal(
uint256 physicalAssetsUsdc,
uint256 protocolFeesUsdc,
uint256 deferredTraderPayoutUsdc,
uint256 deferredClearerBountyUsdc
) internal pure returns (uint256);
canPayFreshPayout
function canPayFreshPayout(
uint256 physicalAssetsUsdc,
uint256 protocolFeesUsdc,
uint256 deferredTraderPayoutUsdc,
uint256 deferredClearerBountyUsdc,
uint256 amountUsdc
) internal pure returns (bool);
canPayDeferredClaim
function canPayDeferredClaim(
uint256 physicalAssetsUsdc,
uint256 protocolFeesUsdc,
uint256 deferredTraderPayoutUsdc,
uint256 deferredClearerBountyUsdc,
uint256 claimAmountUsdc
) internal pure returns (bool);
canWithdrawProtocolFees
function canWithdrawProtocolFees(
uint256 physicalAssetsUsdc,
uint256 protocolFeesUsdc,
uint256 deferredTraderPayoutUsdc,
uint256 deferredClearerBountyUsdc,
uint256 amountUsdc
) internal pure returns (bool);
_saturatingSub
function _saturatingSub(
uint256 lhs,
uint256 rhs
) private pure returns (uint256);
_buildSeniorCashReservation
function _buildSeniorCashReservation(
uint256 physicalAssetsUsdc,
uint256 protocolFeesUsdc,
uint256 deferredTraderPayoutUsdc,
uint256 deferredClearerBountyUsdc
) private pure returns (SeniorCashReservation memory reservation);
Structs
SeniorCashReservation
struct SeniorCashReservation {
uint256 physicalAssetsUsdc;
uint256 protocolFeesUsdc;
uint256 deferredTraderPayoutUsdc;
uint256 deferredClearerBountyUsdc;
uint256 totalSeniorClaimsUsdc;
uint256 reservedSeniorCashUsdc;
uint256 protocolFeeWithdrawalUsdc;
uint256 freeCashUsdc;
uint256 headClaimServiceableUsdc;
}
CfdEnginePlanLib
Title: CfdEnginePlanLib
Pure plan functions for the CfdEngine plan→apply architecture. Each function takes a RawSnapshot and returns a typed delta describing all effects. No storage reads, no external calls — purely deterministic over memory inputs.
Functions
computeOpenMarginAfter
function computeOpenMarginAfter(
uint256 marginAfterFunding,
int256 netMarginChange
) internal pure returns (bool drained, uint256 marginAfter);
computeSideTotalMarginAfterOpen
function computeSideTotalMarginAfterOpen(
uint256 sideTotalMarginAfterFunding,
uint256 effectivePositionMarginAfterFunding,
uint256 positionMarginAfterOpen
) internal pure returns (uint256 sideTotalMarginAfterOpen);
getOpenFailurePolicyCategory
function getOpenFailurePolicyCategory(
CfdEnginePlanTypes.OpenRevertCode code
) internal pure returns (CfdEnginePlanTypes.OpenFailurePolicyCategory);
getExecutionFailurePolicyCategory
function getExecutionFailurePolicyCategory(
CfdEnginePlanTypes.OpenRevertCode code
) internal pure returns (CfdEnginePlanTypes.ExecutionFailurePolicyCategory);
getExecutionFailurePolicyCategory
function getExecutionFailurePolicyCategory(
CfdEnginePlanTypes.CloseRevertCode code
) internal pure returns (CfdEnginePlanTypes.ExecutionFailurePolicyCategory);
_selectedAndOpposite
function _selectedAndOpposite(
CfdEnginePlanTypes.RawSnapshot memory snap,
CfdTypes.Side side
)
private
pure
returns (CfdEnginePlanTypes.SideSnapshot memory selected, CfdEnginePlanTypes.SideSnapshot memory opposite);
_absSkewUsdc
function _absSkewUsdc(
CfdEnginePlanTypes.SideSnapshot memory bull,
CfdEnginePlanTypes.SideSnapshot memory bear,
uint256 price
) private pure returns (uint256);
_postOpenSkewUsdc
function _postOpenSkewUsdc(
CfdEnginePlanTypes.SideSnapshot memory bull,
CfdEnginePlanTypes.SideSnapshot memory bear,
CfdTypes.Side side,
uint256 sizeDelta,
uint256 price
) private pure returns (uint256);
_computeGlobalFundingPnl
function _computeGlobalFundingPnl(
CfdEnginePlanTypes.SideSnapshot memory bull,
CfdEnginePlanTypes.SideSnapshot memory bear
) private pure returns (int256 bullFunding, int256 bearFunding);
_isCollectedFundingLoss
function _isCollectedFundingLoss(
CfdEnginePlanTypes.FundingPayoutType payoutType
) private pure returns (bool);
_solvencyCappedFundingPnl
function _solvencyCappedFundingPnl(
CfdEnginePlanTypes.SideSnapshot memory bull,
CfdEnginePlanTypes.SideSnapshot memory bear
) private pure returns (int256);
_planDeferredPayoutConsumption
function _planDeferredPayoutConsumption(
uint256 deferredPayoutUsdc,
uint256 shortfallUsdc,
bool shortfallAlreadyIncludesDeferred
) private pure returns (uint256 consumedUsdc, uint256 remainingUsdc, uint256 badDebtUsdc);
_planCloseDeferredPayoutConsumption
function _planCloseDeferredPayoutConsumption(
uint256 deferredPayoutUsdc,
CfdEngineSettlementLib.CloseSettlementResult memory lossResult
)
private
pure
returns (uint256 consumedUsdc, uint256 remainingUsdc, uint256 feeRecoveredUsdc, uint256 badDebtUsdc);
planGlobalFunding
function planGlobalFunding(
CfdEnginePlanTypes.RawSnapshot memory snap,
uint256 executionPrice,
uint64 publishTime
) internal pure returns (CfdEnginePlanTypes.GlobalFundingDelta memory gfd);
planFunding
function planFunding(
CfdEnginePlanTypes.RawSnapshot memory snap,
uint256 executionPrice,
uint64 publishTime,
bool isClose,
bool isFullClose
) internal pure returns (CfdEnginePlanTypes.FundingDelta memory fd);
planOpen
function planOpen(
CfdEnginePlanTypes.RawSnapshot memory snap,
CfdTypes.Order memory order,
uint256 executionPrice,
uint64 publishTime
) internal pure returns (CfdEnginePlanTypes.OpenDelta memory delta);
_buildPostOpenRiskState
function _buildPostOpenRiskState(
CfdEnginePlanTypes.RawSnapshot memory snap,
CfdEnginePlanTypes.OpenDelta memory delta
) private pure returns (PositionRiskAccountingLib.PositionRiskState memory riskState);
_isOpenInsolventAfterPlan
function _isOpenInsolventAfterPlan(
CfdEnginePlanTypes.RawSnapshot memory snap,
CfdTypes.Side side,
CfdEnginePlanTypes.OpenDelta memory delta,
CfdEnginePlanTypes.SideSnapshot memory bull,
CfdEnginePlanTypes.SideSnapshot memory bear
) private pure returns (bool);
planClose
function planClose(
CfdEnginePlanTypes.RawSnapshot memory snap,
CfdTypes.Order memory order,
uint256 executionPrice,
uint64 publishTime
) internal pure returns (CfdEnginePlanTypes.CloseDelta memory delta);
_computeCloseSolvency
function _computeCloseSolvency(
CfdEnginePlanTypes.RawSnapshot memory snap,
CfdEnginePlanTypes.CloseDelta memory delta,
CfdEnginePlanTypes.SideSnapshot memory bull,
CfdEnginePlanTypes.SideSnapshot memory bear
) private pure returns (CfdEnginePlanTypes.SolvencyPreview memory sp);
_buildCloseSettlementBuckets
function _buildCloseSettlementBuckets(
CfdEnginePlanTypes.RawSnapshot memory snap,
uint256 marginToFreeUsdc,
CfdEnginePlanTypes.FundingDelta memory fd,
bool includeOtherLockedMargin
) private pure returns (IMarginClearinghouse.AccountUsdcBuckets memory);
planLiquidation
function planLiquidation(
CfdEnginePlanTypes.RawSnapshot memory snap,
uint256 executionPrice,
uint64 publishTime
) internal pure returns (CfdEnginePlanTypes.LiquidationDelta memory delta);
Constants
EXECUTION_FEE_BPS
uint256 constant EXECUTION_FEE_BPS = 4
CfdEngineSettlementLib
Functions
collectSettlementDeficit
function collectSettlementDeficit(
uint256 availableUsdc,
uint256 owedUsdc
) internal pure returns (DebtCollectionResult memory result);
closeSettlementResult
function closeSettlementResult(
uint256 availableUsdc,
uint256 owedUsdc,
uint256 execFeeUsdc
) internal pure returns (CloseSettlementResult memory result);
closeSettlementResultForTerminalBuckets
function closeSettlementResultForTerminalBuckets(
IMarginClearinghouse.AccountUsdcBuckets memory buckets,
uint256 protectedLockedMarginUsdc,
uint256 owedUsdc,
uint256 execFeeUsdc
) internal pure returns (CloseSettlementResult memory result);
liquidationSettlementResult
function liquidationSettlementResult(
uint256 accountBalanceUsdc,
int256 residualUsdc
) internal pure returns (LiquidationSettlementResult memory result);
Structs
DebtCollectionResult
struct DebtCollectionResult {
uint256 seizedUsdc;
uint256 shortfallUsdc;
}
CloseSettlementResult
struct CloseSettlementResult {
uint256 seizedUsdc;
uint256 shortfallUsdc;
uint256 collectedExecFeeUsdc;
uint256 badDebtUsdc;
}
LiquidationSettlementResult
struct LiquidationSettlementResult {
uint256 targetBalanceUsdc;
uint256 seizedUsdc;
uint256 payoutUsdc;
uint256 badDebtUsdc;
}
CfdEngineSnapshotsLib
Functions
buildFundingSnapshot
function buildFundingSnapshot(
int256 bullFunding,
int256 bearFunding,
uint256 totalBullMargin,
uint256 totalBearMargin
) internal pure returns (FundingSnapshot memory snapshot);
getWithdrawalReservedUsdc
function getWithdrawalReservedUsdc(
uint256 maxLiability,
uint256 protocolFees,
uint256 fundingLiability
) internal pure returns (uint256 reservedUsdc);
Structs
FundingSnapshot
struct FundingSnapshot {
int256 bullFunding;
int256 bearFunding;
int256 solvencyFunding;
uint256 withdrawalFundingLiability;
}
SolvencySnapshot
struct SolvencySnapshot {
uint256 physicalAssets;
uint256 protocolFees;
uint256 netPhysicalAssets;
uint256 maxLiability;
int256 solvencyFunding;
uint256 effectiveSolvencyAssets;
}
CloseAccountingLib
Functions
buildCloseState
function buildCloseState(
uint256 positionSize,
uint256 positionMarginUsdc,
uint256 entryPrice,
uint256 maxProfitUsdc,
int256 vpiAccrued,
CfdTypes.Side side,
uint256 sizeDelta,
uint256 oraclePrice,
uint256 capPrice,
uint256 preSkewUsdc,
uint256 postSkewUsdc,
uint256 vaultDepthUsdc,
uint256 vpiFactor,
uint256 executionFeeBps,
int256 fundingSettlementUsdc
) internal pure returns (CloseState memory state);
Structs
CloseState
struct CloseState {
int256 realizedPnlUsdc;
uint256 marginToFreeUsdc;
uint256 remainingMarginUsdc;
uint256 remainingSize;
uint256 maxProfitReductionUsdc;
int256 proportionalAccrualUsdc;
int256 vpiDeltaUsdc;
uint256 executionFeeUsdc;
int256 netSettlementUsdc;
}
HousePoolAccountingLib
Functions
buildWithdrawalSnapshot
function buildWithdrawalSnapshot(
ICfdEngine.HousePoolInputSnapshot memory engineSnapshot
) internal pure returns (WithdrawalSnapshot memory snapshot);
buildReconcileSnapshot
function buildReconcileSnapshot(
ICfdEngine.HousePoolInputSnapshot memory engineSnapshot
) internal pure returns (ReconcileSnapshot memory snapshot);
getMarkFreshnessPolicy
function getMarkFreshnessPolicy(
ICfdEngine.HousePoolInputSnapshot memory accountingSnapshot
) internal pure returns (MarkFreshnessPolicy memory policy);
isMarkFresh
function isMarkFresh(
uint64 lastMarkTime,
uint256 limit,
uint256 currentTimestamp
) internal pure returns (bool);
Structs
WithdrawalSnapshot
struct WithdrawalSnapshot {
uint256 physicalAssets;
uint256 maxLiability;
uint256 protocolFees;
uint256 reserved;
uint256 freeUsdc;
}
ReconcileSnapshot
struct ReconcileSnapshot {
uint256 physicalAssets;
uint256 protocolFees;
uint256 deferredLiabilities;
uint256 cashMinusFees;
uint256 mtm;
uint256 distributable;
}
MarkFreshnessPolicy
struct MarkFreshnessPolicy {
bool required;
uint256 maxStaleness;
}
HousePoolFreshnessLib
Functions
markIsFreshForReconcile
function markIsFreshForReconcile(
ICfdEngine.HousePoolInputSnapshot memory accountingSnapshot,
ICfdEngine.HousePoolStatusSnapshot memory statusSnapshot,
uint256 currentTimestamp
) internal pure returns (bool);
withdrawalsLive
function withdrawalsLive(
ICfdEngine.HousePoolInputSnapshot memory accountingSnapshot,
ICfdEngine.HousePoolStatusSnapshot memory statusSnapshot,
uint256 currentTimestamp
) internal pure returns (bool);
markFresh
function markFresh(
ICfdEngine.HousePoolInputSnapshot memory accountingSnapshot,
ICfdEngine.HousePoolStatusSnapshot memory statusSnapshot,
uint256 currentTimestamp
) internal pure returns (bool);
HousePoolPendingLivePlanLib
Functions
planApplyPendingBuckets
function planApplyPendingBuckets(
HousePoolPendingPreviewLib.PendingAccountingState memory state,
uint256 currentSeniorPrincipal,
uint256 pendingRecapitalizationUsdc,
uint256 pendingTradingRevenueUsdc
) internal pure returns (PendingLivePlan memory plan);
Structs
PendingLivePlan
struct PendingLivePlan {
HousePoolPendingPreviewLib.PendingAccountingState state;
bool seniorPrincipalChanged;
}
HousePoolPendingPreviewLib
Functions
applyPendingBucketsPreview
function applyPendingBucketsPreview(
PendingAccountingState memory state,
uint256 pendingRecapitalizationUsdc,
uint256 pendingTradingRevenueUsdc
) internal pure;
applyRecapitalizationIntent
function applyRecapitalizationIntent(
PendingAccountingState memory state,
uint256 amount
) internal pure;
routeSeededRevenue
function routeSeededRevenue(
PendingAccountingState memory state,
uint256 amount
) internal pure;
Structs
PendingAccountingState
struct PendingAccountingState {
HousePoolWaterfallAccountingLib.WaterfallState waterfall;
uint256 unassignedAssets;
uint256 seniorSupply;
uint256 juniorSupply;
}
HousePoolReconcilePlanLib
Functions
planReconcile
function planReconcile(
HousePoolPendingPreviewLib.PendingAccountingState memory state,
HousePoolAccountingLib.ReconcileSnapshot memory snapshot,
uint256 pendingBucketAssets,
uint256 seniorRateBps,
uint256 yieldElapsed,
bool markFresh
) internal pure returns (ReconcilePlan memory plan);
juniorRevenueWithoutOwners
function juniorRevenueWithoutOwners(
ReconcilePlan memory plan
) internal pure returns (uint256);
Structs
ReconcilePlan
struct ReconcilePlan {
HousePoolPendingPreviewLib.PendingAccountingState state;
uint256 yieldAccrued;
bool markFresh;
bool juniorSupplyZero;
bool claimedEquityZero;
bool revenue;
uint256 deltaUsdc;
uint256 juniorPrincipalBeforeRevenue;
}
HousePoolSeedLifecycleLib
Functions
isSeedLifecycleComplete
function isSeedLifecycleComplete(
bool seniorSeedInitialized,
bool juniorSeedInitialized
) internal pure returns (bool);
hasSeedLifecycleStarted
function hasSeedLifecycleStarted(
bool seniorSeedInitialized,
bool juniorSeedInitialized
) internal pure returns (bool);
canAcceptOrdinaryDeposits
function canAcceptOrdinaryDeposits(
bool seniorSeedInitialized,
bool juniorSeedInitialized,
bool isTradingActive
) internal pure returns (bool);
canIncreaseRisk
function canIncreaseRisk(
bool seniorSeedInitialized,
bool juniorSeedInitialized,
bool isTradingActive
) internal pure returns (bool);
tradingActivationReady
function tradingActivationReady(
bool seniorSeedInitialized,
bool juniorSeedInitialized
) internal pure returns (bool);
hasPendingBootstrap
function hasPendingBootstrap(
uint256 unassignedAssets
) internal pure returns (bool);
HousePoolTrancheGateLib
Functions
trancheDepositsAllowed
function trancheDepositsAllowed(
bool ordinaryDepositsAllowed,
bool paused,
uint256 unassignedAssets,
bool markFreshForReconcile,
uint256 projectedUnassignedAssets,
bool isSenior,
uint256 projectedSeniorPrincipal,
uint256 projectedSeniorHighWaterMark
) internal pure returns (bool);
HousePoolWaterfallAccountingLib
Constants
BPS
uint256 internal constant BPS = 10_000
SECONDS_PER_YEAR
uint256 internal constant SECONDS_PER_YEAR = 31_536_000
Functions
accrueSeniorYield
function accrueSeniorYield(
uint256 seniorPrincipal,
uint256 seniorRateBps,
uint256 elapsed
) internal pure returns (uint256);
planReconcile
function planReconcile(
uint256 seniorPrincipal,
uint256 juniorPrincipal,
uint256 distributableUsdc,
uint256 seniorRateBps,
uint256 elapsed
) internal pure returns (ReconcilePlan memory plan);
scaleSeniorOnWithdraw
function scaleSeniorOnWithdraw(
WaterfallState memory state,
uint256 withdrawAmountUsdc
) internal pure returns (WaterfallState memory nextState);
distributeRevenue
function distributeRevenue(
WaterfallState memory state,
uint256 revenueUsdc
) internal pure returns (WaterfallState memory nextState);
absorbLoss
function absorbLoss(
WaterfallState memory state,
uint256 lossUsdc
) internal pure returns (WaterfallState memory nextState);
Structs
WaterfallState
struct WaterfallState {
uint256 seniorPrincipal;
uint256 juniorPrincipal;
uint256 unpaidSeniorYield;
uint256 seniorHighWaterMark;
}
ReconcilePlan
struct ReconcilePlan {
uint256 yieldAccrued;
bool isRevenue;
uint256 deltaUsdc;
}
HousePoolWithdrawalPreviewLib
Functions
reserveAssets
function reserveAssets(
HousePoolAccountingLib.WithdrawalSnapshot memory snapshot,
uint256 reservedAssets
) internal pure returns (HousePoolAccountingLib.WithdrawalSnapshot memory nextSnapshot);
seniorWithdrawCap
function seniorWithdrawCap(
uint256 freeUsdc,
uint256 seniorPrincipal
) internal pure returns (uint256);
juniorWithdrawCap
function juniorWithdrawCap(
uint256 freeUsdc,
uint256 seniorPrincipal,
uint256 juniorPrincipal
) internal pure returns (uint256);
LiquidationAccountingLib
Functions
buildLiquidationState
function buildLiquidationState(
uint256 size,
uint256 oraclePrice,
uint256 reachableCollateralUsdc,
int256 fundingUsdc,
int256 pnlUsdc,
uint256 maintMarginBps,
uint256 minBountyUsdc,
uint256 bountyBps,
uint256 tokenScale
) internal pure returns (LiquidationState memory state);
settlementForState
function settlementForState(
LiquidationState memory state
) internal pure returns (CfdEngineSettlementLib.LiquidationSettlementResult memory result);
Structs
LiquidationState
struct LiquidationState {
int256 equityUsdc;
uint256 reachableCollateralUsdc;
uint256 maintenanceMarginUsdc;
uint256 keeperBountyUsdc;
}
MarginClearinghouseAccountingLib
Functions
buildAccountUsdcBuckets
function buildAccountUsdcBuckets(
uint256 settlementBalanceUsdc,
uint256 positionMarginUsdc,
uint256 committedOrderMarginUsdc,
uint256 reservedSettlementUsdc
) internal pure returns (IMarginClearinghouse.AccountUsdcBuckets memory buckets);
buildPartialCloseUsdcBuckets
function buildPartialCloseUsdcBuckets(
uint256 settlementBalanceUsdc,
uint256 positionMarginUsdc,
uint256 committedOrderMarginUsdc,
uint256 reservedSettlementUsdc
) internal pure returns (IMarginClearinghouse.AccountUsdcBuckets memory buckets);
planFundingLossConsumption
function planFundingLossConsumption(
IMarginClearinghouse.AccountUsdcBuckets memory buckets,
uint256 lossUsdc
) internal pure returns (SettlementConsumption memory consumption);
getTerminalReachableUsdc
function getTerminalReachableUsdc(
IMarginClearinghouse.AccountUsdcBuckets memory buckets
) internal pure returns (uint256 reachableUsdc);
getSettlementReachableUsdc
function getSettlementReachableUsdc(
IMarginClearinghouse.AccountUsdcBuckets memory buckets,
uint256 protectedLockedMarginUsdc
) internal pure returns (uint256 reachableUsdc);
planTerminalLossConsumption
function planTerminalLossConsumption(
IMarginClearinghouse.AccountUsdcBuckets memory buckets,
uint256 protectedLockedMarginUsdc,
uint256 lossUsdc
) internal pure returns (SettlementConsumption memory consumption);
applyFundingLossMutation
function applyFundingLossMutation(
IMarginClearinghouse.AccountUsdcBuckets memory,
SettlementConsumption memory consumption
) internal pure returns (BucketMutation memory mutation);
applyTerminalLossMutation
function applyTerminalLossMutation(
IMarginClearinghouse.AccountUsdcBuckets memory,
uint256,
SettlementConsumption memory consumption
) internal pure returns (BucketMutation memory mutation);
planLiquidationResidual
function planLiquidationResidual(
IMarginClearinghouse.AccountUsdcBuckets memory buckets,
int256 residualUsdc
) internal pure returns (LiquidationResidualPlan memory plan);
Structs
SettlementConsumption
struct SettlementConsumption {
uint256 freeSettlementConsumedUsdc;
uint256 activeMarginConsumedUsdc;
uint256 otherLockedMarginConsumedUsdc;
uint256 totalConsumedUsdc;
uint256 uncoveredUsdc;
}
BucketMutation
struct BucketMutation {
uint256 settlementDebitUsdc;
uint256 positionMarginUnlockedUsdc;
uint256 otherLockedMarginUnlockedUsdc;
}
LiquidationResidualPlan
struct LiquidationResidualPlan {
uint256 settlementRetainedUsdc;
uint256 settlementSeizedUsdc;
uint256 freshTraderPayoutUsdc;
uint256 badDebtUsdc;
BucketMutation mutation;
}
MarketCalendarLib
Constants
SECONDS_PER_DAY
uint256 internal constant SECONDS_PER_DAY = 86_400
SECONDS_PER_HOUR
uint256 internal constant SECONDS_PER_HOUR = 3600
Functions
isFadWindow
function isFadWindow(
uint256 timestamp,
bool todayOverride,
bool tomorrowOverride,
uint256 fadRunwaySeconds
) internal pure returns (bool);
isOracleFrozen
function isOracleFrozen(
uint256 timestamp,
bool todayOverride
) internal pure returns (bool);
_dayAndHour
function _dayAndHour(
uint256 timestamp
) private pure returns (uint256 dayOfWeek, uint256 hourOfDay);
OpenAccountingLib
Functions
buildOpenState
function buildOpenState(
OpenInputs memory inputs
) internal pure returns (OpenState memory state);
effectiveMarginAfterTradeCost
function effectiveMarginAfterTradeCost(
uint256 marginUsdc,
int256 tradeCostUsdc
) internal pure returns (uint256 effectiveMarginUsdc);
Structs
OpenInputs
struct OpenInputs {
uint256 currentSize;
uint256 currentEntryPrice;
CfdTypes.Side side;
uint256 sizeDelta;
uint256 price;
uint256 capPrice;
uint256 preSkewUsdc;
uint256 postSkewUsdc;
uint256 vaultDepthUsdc;
uint256 executionFeeBps;
int256 currentFundingIndex;
CfdTypes.RiskParams riskParams;
}
OpenState
struct OpenState {
uint256 addedMaxProfitUsdc;
uint256 oldEntryNotional;
uint256 newEntryPrice;
uint256 newSize;
uint256 newEntryNotional;
uint256 postSkewUsdc;
int256 positionFundingContribution;
int256 vpiUsdc;
uint256 notionalUsdc;
uint256 executionFeeUsdc;
int256 tradeCostUsdc;
uint256 maintenanceMarginUsdc;
uint256 initialMarginRequirementUsdc;
}
OrderFailurePolicyLib
Functions
isPredictablyInvalidOpen
function isPredictablyInvalidOpen(
CfdEnginePlanTypes.OpenFailurePolicyCategory category
) internal pure returns (bool);
failureDomainForExecutionCategory
function failureDomainForExecutionCategory(
CfdEnginePlanTypes.ExecutionFailurePolicyCategory category
) internal pure returns (FailureDomain);
bountyPolicyForFailure
function bountyPolicyForFailure(
FailureContext memory context
) internal pure returns (FailedOrderBountyPolicy);
Structs
RoutedFailure
struct RoutedFailure {
FailureDomain domain;
uint8 code;
bool isClose;
}
FailureContext
struct FailureContext {
RoutedFailure failure;
FailureSource source;
bool closeOnly;
bool oracleFrozen;
bool isFad;
bool degradedMode;
}
Enums
FailedOrderBountyPolicy
enum FailedOrderBountyPolicy {
None,
ClearerFull,
RefundUser
}
FailureSource
enum FailureSource {
RouterPolicy,
EngineTyped,
UntypedRevert,
Expired
}
FailureDomain
enum FailureDomain {
UserInvalid,
ProtocolStateInvalidated,
Retryable,
Expired
}
RouterFailureCode
enum RouterFailureCode {
None,
CloseOnlyOracleFrozen,
CloseOnlyFad
}
OrderOraclePolicyLib
Functions
getOracleExecutionPolicy
function getOracleExecutionPolicy(
OracleAction action,
bool oracleFrozen,
bool isFad,
uint256 liveExecutionStaleness,
uint256 liveLiquidationStaleness,
uint256 fadMaxStaleness
) internal pure returns (OracleExecutionPolicy memory policy);
isStale
function isStale(
uint64 oraclePublishTime,
uint256 maxStaleness,
uint256 currentTimestamp
) internal pure returns (bool);
Structs
OracleExecutionPolicy
struct OracleExecutionPolicy {
bool oracleFrozen;
bool isFad;
bool closeOnly;
bool mevChecks;
uint256 maxStaleness;
}
Enums
OracleAction
enum OracleAction {
OrderExecution,
MarkRefresh,
Liquidation
}
PositionRiskAccountingLib
Functions
getPendingFunding
function getPendingFunding(
CfdTypes.Position memory pos,
int256 currentIndex
) internal pure returns (int256 fundingUsdc);
previewPendingFunding
function previewPendingFunding(
CfdTypes.Position memory pos,
int256 bullFundingIndex,
int256 bearFundingIndex,
uint256 lastMarkPrice,
uint256 bullOi,
uint256 bearOi,
uint64 lastFundingTime,
uint256 currentTimestamp,
uint256 vaultDepthUsdc,
CfdTypes.RiskParams memory riskParams
) internal pure returns (int256 fundingUsdc);
computeFundingStep
function computeFundingStep(
FundingStepInputs memory inputs
) internal pure returns (FundingStepResult memory result);
buildPositionRiskState
function buildPositionRiskState(
CfdTypes.Position memory pos,
uint256 price,
uint256 capPrice,
int256 pendingFundingUsdc,
uint256 reachableCollateralUsdc,
uint256 requiredBps
) internal pure returns (PositionRiskState memory state);
Structs
FundingStepInputs
struct FundingStepInputs {
uint256 price;
uint256 bullOi;
uint256 bearOi;
uint256 timeDelta;
uint256 vaultDepthUsdc;
CfdTypes.RiskParams riskParams;
}
FundingStepResult
struct FundingStepResult {
uint256 absSkewUsdc;
int256 bullFundingIndexDelta;
int256 bearFundingIndexDelta;
}
PositionRiskState
struct PositionRiskState {
int256 pendingFundingUsdc;
int256 unrealizedPnlUsdc;
int256 equityUsdc;
uint256 currentNotionalUsdc;
uint256 maintenanceMarginUsdc;
bool liquidatable;
}
SolvencyAccountingLib
Functions
getMaxLiability
function getMaxLiability(
uint256 bullMaxProfitUsdc,
uint256 bearMaxProfitUsdc
) internal pure returns (uint256);
getMaxLiabilityAfterClose
function getMaxLiabilityAfterClose(
uint256 bullMaxProfitUsdc,
uint256 bearMaxProfitUsdc,
CfdTypes.Side side,
uint256 maxProfitReductionUsdc
) internal pure returns (uint256);
buildSolvencyState
function buildSolvencyState(
uint256 physicalAssetsUsdc,
uint256 protocolFeesUsdc,
uint256 maxLiabilityUsdc,
int256 solvencyFundingPnlUsdc,
uint256 deferredTraderPayoutUsdc,
uint256 deferredClearerBountyUsdc
) internal pure returns (SolvencyState memory state);
effectiveAssetsAfterPendingPayout
function effectiveAssetsAfterPendingPayout(
SolvencyState memory state,
uint256 pendingVaultPayoutUsdc
) internal pure returns (uint256);
isInsolvent
function isInsolvent(
SolvencyState memory state
) internal pure returns (bool);
previewPostOpSolvency
function previewPostOpSolvency(
SolvencyState memory currentState,
PreviewDelta memory delta,
bool alreadyDegraded
) internal pure returns (PreviewResult memory result);
_applySignedDelta
function _applySignedDelta(
uint256 value,
int256 delta
) private pure returns (uint256 updatedValue);
Structs
PreviewDelta
struct PreviewDelta {
int256 physicalAssetsDeltaUsdc;
uint256 protocolFeesDeltaUsdc;
uint256 maxLiabilityAfterUsdc;
int256 deferredTraderPayoutDeltaUsdc;
int256 deferredLiquidationBountyDeltaUsdc;
uint256 pendingVaultPayoutUsdc;
}
PreviewResult
struct PreviewResult {
uint256 effectiveAssetsAfterUsdc;
uint256 maxLiabilityAfterUsdc;
bool triggersDegradedMode;
bool postOpDegradedMode;
}
SolvencyState
struct SolvencyState {
uint256 physicalAssetsUsdc;
uint256 protocolFeesUsdc;
uint256 netPhysicalAssetsUsdc;
uint256 maxLiabilityUsdc;
int256 solvencyFundingPnlUsdc;
uint256 deferredTraderPayoutUsdc;
uint256 deferredClearerBountyUsdc;
uint256 effectiveAssetsUsdc;
}
WithdrawalAccountingLib
Functions
buildWithdrawalState
function buildWithdrawalState(
uint256 physicalAssetsUsdc,
uint256 maxLiabilityUsdc,
uint256 protocolFeesUsdc,
uint256 fundingLiabilityUsdc,
uint256 deferredTraderPayoutUsdc,
uint256 deferredClearerBountyUsdc
) internal pure returns (WithdrawalState memory state);
Structs
WithdrawalState
struct WithdrawalState {
uint256 physicalAssetsUsdc;
uint256 maxLiabilityUsdc;
uint256 protocolFeesUsdc;
uint256 fundingLiabilityUsdc;
uint256 deferredTraderPayoutUsdc;
uint256 deferredClearerBountyUsdc;
uint256 reservedUsdc;
uint256 freeUsdc;
}
Contents
OrderEscrowAccounting
Inherits: IOrderRouterAccounting
Constants
engine
ICfdEngine public immutable engine
clearinghouse
IMarginClearinghouse internal immutable clearinghouse
USDC
IERC20 internal immutable USDC
State Variables
orderRecords
mapping(uint64 => OrderRecord) internal orderRecords
pendingOrderCounts
mapping(bytes32 => uint256) public pendingOrderCounts
pendingCloseSize
mapping(bytes32 => uint256) public pendingCloseSize
marginHeadOrderId
mapping(bytes32 => uint64) public marginHeadOrderId
marginTailOrderId
mapping(bytes32 => uint64) public marginTailOrderId
claimableUsdc
mapping(address => uint256) public claimableUsdc
Functions
constructor
constructor(
address _engine
) ;
getAccountEscrow
function getAccountEscrow(
bytes32 accountId
) public view override returns (IOrderRouterAccounting.AccountEscrowView memory escrow);
getAccountOrderSummary
function getAccountOrderSummary(
bytes32 accountId
) public view returns (IOrderRouterAccounting.AccountOrderSummary memory summary);
_summarizePendingOrders
function _summarizePendingOrders(
bytes32 accountId
)
internal
view
returns (
uint256 pendingOrderCount,
uint256 executionBountyUsdc,
uint256 pendingCloseSize_,
bool hasTerminalCloseQueued
);
getMarginReservationIds
function getMarginReservationIds(
bytes32 accountId
) public view override returns (uint64[] memory orderIds);
_reserveExecutionBounty
function _reserveExecutionBounty(
bytes32 accountId,
uint64 orderId,
uint256 executionBountyUsdc,
bool isClose
) internal;
_reserveCommittedMargin
function _reserveCommittedMargin(
bytes32 accountId,
uint64 orderId,
bool isClose,
uint256 marginDelta
) internal;
_consumeOrderEscrow
function _consumeOrderEscrow(
uint64 orderId,
bool success,
uint8 failedPolicy
) internal returns (uint256 executionBountyUsdc);
_collectExecutionBounty
function _collectExecutionBounty(
uint64 orderId
) internal returns (uint256 executionBountyUsdc);
_refundExecutionBounty
function _refundExecutionBounty(
uint64 orderId
) internal;
_tryTransferUsdc
function _tryTransferUsdc(
address to,
uint256 amount
) internal returns (bool success);
_releaseCommittedMargin
function _releaseCommittedMargin(
uint64 orderId
) internal;
_linkMarginOrder
function _linkMarginOrder(
bytes32 accountId,
uint64 orderId
) internal;
_unlinkMarginOrder
function _unlinkMarginOrder(
bytes32 accountId,
uint64 orderId
) internal;
_pruneMarginQueue
function _pruneMarginQueue(
bytes32 accountId
) internal;
_orderRecord
function _orderRecord(
uint64 orderId
) internal view virtual returns (OrderRecord storage record);
_pendingHeadOrderId
function _pendingHeadOrderId(
bytes32 accountId
) internal view virtual returns (uint64);
_reserveCloseExecutionBounty
function _reserveCloseExecutionBounty(
bytes32 accountId,
uint256 executionBountyUsdc
) internal virtual;
_revertInsufficientFreeEquity
function _revertInsufficientFreeEquity() internal pure virtual;
_revertMarginOrderLinkCorrupted
function _revertMarginOrderLinkCorrupted() internal pure virtual;
Structs
OrderRecord
struct OrderRecord {
CfdTypes.Order core;
IOrderRouterAccounting.OrderStatus status;
uint256 executionBountyUsdc;
uint64 retryAfterTimestamp;
uint64 nextPendingOrderId;
uint64 prevPendingOrderId;
uint64 nextGlobalOrderId;
uint64 prevGlobalOrderId;
uint64 nextMarginOrderId;
uint64 prevMarginOrderId;
bool inMarginQueue;
}
CfdEngine
Inherits: IWithdrawGuard, Ownable2Step, ReentrancyGuardTransient
Title: CfdEngine
The core mathematical ledger for Plether CFDs.
Settles all funds through the MarginClearinghouse and CfdVault.
Note: security-contact: contact@plether.com
Constants
CAP_PRICE
uint256 public immutable CAP_PRICE
USDC
IERC20 public immutable USDC
clearinghouse
IMarginClearinghouse public immutable clearinghouse
EXECUTION_FEE_BPS
uint256 public constant EXECUTION_FEE_BPS = 4
TIMELOCK_DELAY
uint256 public constant TIMELOCK_DELAY = 48 hours
State Variables
vault
ICfdVault public vault
sides
SideState[2] public sides
lastMarkPrice
uint256 public lastMarkPrice
lastMarkTime
uint64 public lastMarkTime
accumulatedFeesUsdc
uint256 public accumulatedFeesUsdc
accumulatedBadDebtUsdc
uint256 public accumulatedBadDebtUsdc
degradedMode
bool public degradedMode
lastFundingTime
uint64 public lastFundingTime
riskParams
CfdTypes.RiskParams public riskParams
_positions
mapping(bytes32 => StoredPosition) internal _positions
deferredPayoutUsdc
mapping(bytes32 => uint256) public deferredPayoutUsdc
totalDeferredPayoutUsdc
uint256 public totalDeferredPayoutUsdc
deferredClearerBountyUsdc
mapping(address => uint256) public deferredClearerBountyUsdc
totalDeferredClearerBountyUsdc
uint256 public totalDeferredClearerBountyUsdc
deferredClaims
mapping(uint64 => ICfdEngine.DeferredClaim) public deferredClaims
nextDeferredClaimId
uint64 public nextDeferredClaimId = 1
deferredClaimHeadId
uint64 public deferredClaimHeadId
deferredClaimTailId
uint64 public deferredClaimTailId
traderDeferredClaimIdByAccount
mapping(bytes32 => uint64) public traderDeferredClaimIdByAccount
clearerDeferredClaimIdByKeeper
mapping(address => uint64) public clearerDeferredClaimIdByKeeper
orderRouter
address public orderRouter
fadDayOverrides
mapping(uint256 => bool) public fadDayOverrides
fadMaxStaleness
uint256 public fadMaxStaleness = 3 days
fadRunwaySeconds
uint256 public fadRunwaySeconds = 3 hours
engineMarkStalenessLimit
uint256 public engineMarkStalenessLimit = 60
pendingRiskParams
CfdTypes.RiskParams public pendingRiskParams
riskParamsActivationTime
uint256 public riskParamsActivationTime
_pendingAddFadDays
uint256[] private _pendingAddFadDays
addFadDaysActivationTime
uint256 public addFadDaysActivationTime
_pendingRemoveFadDays
uint256[] private _pendingRemoveFadDays
removeFadDaysActivationTime
uint256 public removeFadDaysActivationTime
pendingFadMaxStaleness
uint256 public pendingFadMaxStaleness
fadMaxStalenessActivationTime
uint256 public fadMaxStalenessActivationTime
pendingFadRunway
uint256 public pendingFadRunway
fadRunwayActivationTime
uint256 public fadRunwayActivationTime
pendingEngineMarkStalenessLimit
uint256 public pendingEngineMarkStalenessLimit
engineMarkStalenessActivationTime
uint256 public engineMarkStalenessActivationTime
Functions
_sideIndex
function _sideIndex(
CfdTypes.Side side
) internal pure returns (uint256);
_sideState
function _sideState(
CfdTypes.Side side
) internal view returns (SideState storage state);
_oppositeSide
function _oppositeSide(
CfdTypes.Side side
) internal pure returns (CfdTypes.Side);
_sideAndOppositeStates
function _sideAndOppositeStates(
CfdTypes.Side side
) internal view returns (SideState storage selected, SideState storage opposite);
_bullAndBearStates
function _bullAndBearStates() internal view returns (SideState storage bullState, SideState storage bearState);
getSideState
function getSideState(
CfdTypes.Side side
) public view returns (ICfdEngine.SideState memory state);
_requireTimelockReady
function _requireTimelockReady(
uint256 activationTime
) internal view;
onlyRouter
modifier onlyRouter() ;
constructor
constructor(
address _usdc,
address _clearinghouse,
uint256 _capPrice,
CfdTypes.RiskParams memory _riskParams
) Ownable(msg.sender);
Parameters
| Name | Type | Description |
|---|---|---|
_usdc | address | USDC token used as margin and settlement currency |
_clearinghouse | address | Margin clearinghouse that custodies trader balances |
_capPrice | uint256 | Maximum oracle price — positions are clamped here (also determines BULL max profit) |
_riskParams | CfdTypes.RiskParams | Initial risk parameters (margin requirements, funding curve, bounty config) |
setVault
One-time setter for the HousePool vault backing all positions
function setVault(
address _vault
) external onlyOwner;
setOrderRouter
One-time setter for the authorized OrderRouter
function setOrderRouter(
address _router
) external onlyOwner;
proposeRiskParams
Proposes new risk parameters (margin BPS, funding curve, bounty config) subject to timelock
function proposeRiskParams(
CfdTypes.RiskParams memory _riskParams
) external onlyOwner;
finalizeRiskParams
Applies proposed risk parameters after timelock expires; settles funding first
function finalizeRiskParams() external onlyOwner;
cancelRiskParamsProposal
Cancels a pending risk parameters proposal
function cancelRiskParamsProposal() external onlyOwner;
proposeAddFadDays
Proposes adding FAD (Friday Afternoon Deleverage) override days — elevated margin on those dates
function proposeAddFadDays(
uint256[] calldata timestamps
) external onlyOwner;
finalizeAddFadDays
Applies proposed FAD day additions after timelock expires
function finalizeAddFadDays() external onlyOwner;
cancelAddFadDaysProposal
Cancels a pending add-FAD-days proposal
function cancelAddFadDaysProposal() external onlyOwner;
proposeRemoveFadDays
Proposes removing FAD override days (restores normal margin on those dates)
function proposeRemoveFadDays(
uint256[] calldata timestamps
) external onlyOwner;
finalizeRemoveFadDays
Applies proposed FAD day removals after timelock expires
function finalizeRemoveFadDays() external onlyOwner;
cancelRemoveFadDaysProposal
Cancels a pending remove-FAD-days proposal
function cancelRemoveFadDaysProposal() external onlyOwner;
proposeFadMaxStaleness
Proposes a new fadMaxStaleness — max age of the last mark price before FAD kicks in
function proposeFadMaxStaleness(
uint256 _seconds
) external onlyOwner;
finalizeFadMaxStaleness
Applies proposed fadMaxStaleness after timelock expires
function finalizeFadMaxStaleness() external onlyOwner;
cancelFadMaxStalenessProposal
Cancels a pending fadMaxStaleness proposal
function cancelFadMaxStalenessProposal() external onlyOwner;
proposeFadRunway
Proposes a new fadRunway — how many seconds before an FAD day the elevated margin activates
function proposeFadRunway(
uint256 _seconds
) external onlyOwner;
finalizeFadRunway
Applies proposed fadRunway after timelock expires
function finalizeFadRunway() external onlyOwner;
cancelFadRunwayProposal
Cancels a pending fadRunway proposal
function cancelFadRunwayProposal() external onlyOwner;
proposeEngineMarkStalenessLimit
function proposeEngineMarkStalenessLimit(
uint256 newStaleness
) external onlyOwner;
finalizeEngineMarkStalenessLimit
function finalizeEngineMarkStalenessLimit() external onlyOwner;
cancelEngineMarkStalenessLimitProposal
function cancelEngineMarkStalenessLimitProposal() external onlyOwner;
withdrawFees
Withdraws accumulated execution fees from the vault to a recipient
function withdrawFees(
address recipient
) external onlyOwner;
absorbRouterCancellationFee
Pulls router-custodied cancellation fees into the vault and books them as protocol revenue.
function absorbRouterCancellationFee(
uint256 amountUsdc
) external onlyRouter;
recordRouterProtocolFee
Books router-delivered protocol-owned inflow as protocol fees after the router has already synced funding and funded the vault.
function recordRouterProtocolFee(
uint256 amountUsdc
) external onlyRouter;
addMargin
Adds isolated margin to an existing open position without changing size.
function addMargin(
bytes32 accountId,
uint256 amount
) external nonReentrant;
claimDeferredPayout
Claims a previously deferred profitable close payout into the clearinghouse.
The payout remains subject to current vault cash availability. Funds are credited to the clearinghouse first, so traders access them through the normal account-balance path.
function claimDeferredPayout(
bytes32 accountId
) external nonReentrant;
claimDeferredClearerBounty
Claims a previously deferred clearer bounty when the vault has replenished cash.
Deferred keeper bounties settle to clearinghouse credit for the recorded keeper address-derived account, rather than attempting a direct USDC wallet transfer.
function claimDeferredClearerBounty() external nonReentrant;
recordDeferredClearerBounty
Records a liquidation bounty that could not be paid immediately because vault cash was unavailable.
function recordDeferredClearerBounty(
address keeper,
uint256 amountUsdc
) external onlyRouter;
reserveCloseOrderExecutionBounty
function reserveCloseOrderExecutionBounty(
bytes32 accountId,
uint256 amountUsdc,
address recipient
) external onlyRouter;
clearBadDebt
Reduces accumulated bad debt after governance-confirmed recapitalization
function clearBadDebt(
uint256 amount
) external onlyOwner;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | USDC amount of bad debt to clear (6 decimals) |
clearDegradedMode
function clearDegradedMode() external onlyOwner;
checkWithdraw
Reverts if the account has an open position that would be undercollateralized after withdrawal
function checkWithdraw(
bytes32 accountId
) external view override;
Parameters
| Name | Type | Description |
|---|---|---|
accountId | bytes32 | Clearinghouse account to check |
syncFunding
Materializes accrued funding into storage so subsequent reads reflect current state. O(1) gas, idempotent (no-op if called twice in the same block).
function syncFunding() external;
_syncFunding
Canonical internal funding sync. Every function that changes vault cash or reads
funding-dependent state must call this first. Using a dedicated helper instead of
inline _updateFunding(lastMarkPrice, vault.totalAssets()) ensures new call sites
cannot silently skip the sync.
function _syncFunding() internal;
_syncFundingForMarkUpdate
function _syncFundingForMarkUpdate() internal;
_updateFunding
function _updateFunding(
uint256 currentOraclePrice,
uint256 vaultDepthUsdc
) internal;
getPendingFunding
Returns unsettled funding owed to (+) or by (-) a position in USDC (6 decimals)
function getPendingFunding(
CfdTypes.Position memory pos
) public view returns (int256 fundingUsdc);
Parameters
| Name | Type | Description |
|---|---|---|
pos | CfdTypes.Position | The position to compute pending funding for |
Returns
| Name | Type | Description |
|---|---|---|
fundingUsdc | int256 | Positive if the position is owed funding, negative if it owes |
processOrder
Executes an order: settles funding, then increases or decreases the position. Called exclusively by OrderRouter after MEV and slippage checks pass.
function processOrder(
CfdTypes.Order memory order,
uint256 currentOraclePrice,
uint256 vaultDepthUsdc,
uint64 publishTime
) external onlyRouter nonReentrant;
Parameters
| Name | Type | Description |
|---|---|---|
order | CfdTypes.Order | The order to execute (account, side, size delta, margin delta, isClose) |
currentOraclePrice | uint256 | Pyth oracle price (8 decimals), clamped to CAP_PRICE |
vaultDepthUsdc | uint256 | HousePool total assets — used to scale funding rate |
publishTime | uint64 | Pyth publish timestamp, stored as lastMarkTime |
processOrderTyped
Router-facing order execution entrypoint with typed business-rule failures.
function processOrderTyped(
CfdTypes.Order memory order,
uint256 currentOraclePrice,
uint256 vaultDepthUsdc,
uint64 publishTime
) external onlyRouter nonReentrant;
_processOrder
function _processOrder(
CfdTypes.Order memory order,
uint256 currentOraclePrice,
uint256 vaultDepthUsdc,
uint64 publishTime,
bool typedFailures
) internal;
_syncTotalSideMargin
function _syncTotalSideMargin(
CfdTypes.Side side,
uint256 marginBefore,
uint256 marginAfter
) internal;
_syncMarginQueue
function _syncMarginQueue(
bytes32 accountId,
uint256 consumedCommittedReservationUsdc
) internal;
_payOrRecordDeferredTraderPayout
function _payOrRecordDeferredTraderPayout(
bytes32 accountId,
uint256 amountUsdc
) internal;
_enqueueOrAccrueDeferredTraderPayout
function _enqueueOrAccrueDeferredTraderPayout(
bytes32 accountId,
uint256 amountUsdc
) internal;
_enqueueOrAccrueDeferredClearerBounty
function _enqueueOrAccrueDeferredClearerBounty(
address keeper,
uint256 amountUsdc
) internal;
_accountVaultCashInflow
function _accountVaultCashInflow(
VaultCashInflow memory inflow
) internal;
_canPayFreshVaultPayout
function _canPayFreshVaultPayout(
uint256 amountUsdc
) internal view returns (bool);
_canWithdrawProtocolFees
function _canWithdrawProtocolFees(
uint256 amountUsdc
) internal view returns (bool);
_availableCashForFreshVaultPayouts
function _availableCashForFreshVaultPayouts() internal view returns (uint256);
_claimableHeadAmountUsdc
function _claimableHeadAmountUsdc() internal view returns (uint256);
_freshVaultReservation
function _freshVaultReservation() internal view returns (CashPriorityLib.SeniorCashReservation memory reservation);
_headDeferredClaimReservation
function _headDeferredClaimReservation(
uint256 headClaimAmountUsdc
) internal view returns (CashPriorityLib.SeniorCashReservation memory reservation);
_enqueueDeferredClaim
function _enqueueDeferredClaim(
ICfdEngine.DeferredClaimType claimType,
bytes32 accountId,
address keeper,
uint256 amountUsdc
) internal returns (uint64 claimId);
_popDeferredClaimHead
function _popDeferredClaimHead() internal;
_unlinkDeferredClaim
function _unlinkDeferredClaim(
uint64 claimId
) internal;
isFadWindow
Returns true during the Friday Afternoon Deleverage (FAD) window (Friday 19:00 UTC → Sunday 22:00 UTC), on admin-configured FAD days, or within fadRunwaySeconds before an admin FAD day (deleverage runway).
function isFadWindow() public view returns (bool);
isOracleFrozen
Returns true only when FX markets are closed and oracle freshness can be relaxed. Distinct from FAD, which starts earlier for deleveraging risk controls.
function isOracleFrozen() public view returns (bool);
hasOpenPosition
function hasOpenPosition(
bytes32 accountId
) external view returns (bool);
positions
function positions(
bytes32 accountId
)
external
view
returns (
uint256 size,
uint256 margin,
uint256 entryPrice,
uint256 maxProfitUsdc,
int256 entryFundingIndex,
CfdTypes.Side side,
uint64 lastUpdateTime,
int256 vpiAccrued
);
getPositionSize
function getPositionSize(
bytes32 accountId
) external view returns (uint256);
getAccountCollateralView
function getAccountCollateralView(
bytes32 accountId
) external view returns (AccountCollateralView memory viewData);
getAccountLedgerView
function getAccountLedgerView(
bytes32 accountId
) external view returns (ICfdEngine.AccountLedgerView memory viewData);
getAccountLedgerSnapshot
function getAccountLedgerSnapshot(
bytes32 accountId
) external view returns (ICfdEngine.AccountLedgerSnapshot memory snapshot);
_buildAccountLedgerSnapshot
function _buildAccountLedgerSnapshot(
bytes32 accountId
) internal view returns (ICfdEngine.AccountLedgerSnapshot memory snapshot);
getPositionView
function getPositionView(
bytes32 accountId
) external view returns (PositionView memory viewData);
getProtocolAccountingView
function getProtocolAccountingView() external view returns (ProtocolAccountingView memory viewData);
getProtocolAccountingSnapshot
function getProtocolAccountingSnapshot()
external
view
returns (ICfdEngine.ProtocolAccountingSnapshot memory snapshot);
_buildProtocolAccountingSnapshot
function _buildProtocolAccountingSnapshot()
internal
view
returns (ICfdEngine.ProtocolAccountingSnapshot memory snapshot);
previewOpenRevertCode
function previewOpenRevertCode(
bytes32 accountId,
CfdTypes.Side side,
uint256 sizeDelta,
uint256 marginDelta,
uint256 oraclePrice,
uint64 publishTime
) external view returns (uint8 code);
previewOpenFailurePolicyCategory
function previewOpenFailurePolicyCategory(
bytes32 accountId,
CfdTypes.Side side,
uint256 sizeDelta,
uint256 marginDelta,
uint256 oraclePrice,
uint64 publishTime
) external view returns (CfdEnginePlanTypes.OpenFailurePolicyCategory category);
getDeferredPayoutStatus
function getDeferredPayoutStatus(
bytes32 accountId,
address keeper
) external view returns (DeferredPayoutStatus memory status);
getDeferredClaimHead
function getDeferredClaimHead() external view returns (ICfdEngine.DeferredClaim memory claim);
getDeferredTraderStatus
function getDeferredTraderStatus(
bytes32 accountId
) public view returns (ICfdEngine.DeferredTraderStatus memory status);
getDeferredClearerStatus
function getDeferredClearerStatus(
address keeper
) public view returns (ICfdEngine.DeferredClearerStatus memory status);
previewClose
Canonical close preview using the vault’s current accounted depth.
function previewClose(
bytes32 accountId,
uint256 sizeDelta,
uint256 oraclePrice
) external view returns (ClosePreview memory preview);
simulateClose
Hypothetical close simulation at a caller-supplied vault depth.
function simulateClose(
bytes32 accountId,
uint256 sizeDelta,
uint256 oraclePrice,
uint256 vaultDepthUsdc
) external view returns (ClosePreview memory preview);
_previewClose
function _previewClose(
bytes32 accountId,
uint256 sizeDelta,
uint256 oraclePrice,
uint256 vaultDepthUsdc
) internal view returns (ClosePreview memory preview);
previewLiquidation
Canonical liquidation preview using the vault’s current accounted depth.
function previewLiquidation(
bytes32 accountId,
uint256 oraclePrice
) external view returns (LiquidationPreview memory preview);
simulateLiquidation
Hypothetical liquidation simulation at a caller-supplied vault depth.
function simulateLiquidation(
bytes32 accountId,
uint256 oraclePrice,
uint256 vaultDepthUsdc
) external view returns (LiquidationPreview memory preview);
_previewLiquidation
function _previewLiquidation(
bytes32 accountId,
uint256 oraclePrice,
uint256 vaultDepthUsdc
) internal view returns (LiquidationPreview memory preview);
_applyLiquidationPreviewForfeiture
function _applyLiquidationPreviewForfeiture(
bytes32 accountId,
CfdEnginePlanTypes.RawSnapshot memory snap
) internal view;
getPositionSide
function getPositionSide(
bytes32 accountId
) external view returns (CfdTypes.Side);
getMaintenanceMarginUsdc
Returns the maintenance margin requirement in USDC (6 decimals). Uses fadMarginBps during the FAD window, maintMarginBps otherwise.
function getMaintenanceMarginUsdc(
uint256 size,
uint256 currentOraclePrice
) public view returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
size | uint256 | Position size in tokens (18 decimals) |
currentOraclePrice | uint256 | Oracle price (8 decimals) |
liquidatePosition
Liquidates an undercollateralized position. Surplus equity (after bounty) is returned to the user. In bad-debt cases (equity < bounty), all remaining margin is seized by the vault.
function liquidatePosition(
bytes32 accountId,
uint256 currentOraclePrice,
uint256 vaultDepthUsdc,
uint64 publishTime
) external onlyRouter nonReentrant returns (uint256 keeperBountyUsdc);
Parameters
| Name | Type | Description |
|---|---|---|
accountId | bytes32 | Clearinghouse account that owns the position |
currentOraclePrice | uint256 | Pyth oracle price (8 decimals), clamped to CAP_PRICE |
vaultDepthUsdc | uint256 | HousePool total assets — used to scale funding rate |
publishTime | uint64 | Pyth publish timestamp, stored as lastMarkTime |
Returns
| Name | Type | Description |
|---|---|---|
keeperBountyUsdc | uint256 | Bounty paid to the liquidation keeper (USDC, 6 decimals) |
_assertPostSolvency
function _assertPostSolvency() internal view;
_maxLiability
function _maxLiability() internal view returns (uint256);
_getWithdrawalReservedUsdc
function _getWithdrawalReservedUsdc() internal view returns (uint256 reservedUsdc);
_buildHousePoolInputSnapshot
function _buildHousePoolInputSnapshot(
uint256 markStalenessLimit
) internal view returns (ICfdEngine.HousePoolInputSnapshot memory snapshot);
_buildHousePoolStatusSnapshot
function _buildHousePoolStatusSnapshot()
internal
view
returns (ICfdEngine.HousePoolStatusSnapshot memory snapshot);
_buildAdjustedSolvencyState
function _buildAdjustedSolvencyState() internal view returns (SolvencyAccountingLib.SolvencyState memory);
_buildAdjustedSolvencySnapshot
function _buildAdjustedSolvencySnapshot()
internal
view
returns (CfdEngineSnapshotsLib.SolvencySnapshot memory snapshot);
_buildRawSnapshot
function _buildRawSnapshot(
bytes32 accountId,
uint256,
uint256 vaultDepthUsdc,
uint64
) internal view returns (CfdEnginePlanTypes.RawSnapshot memory snap);
_copySideSnapshot
function _copySideSnapshot(
SideState storage state
) internal view returns (CfdEnginePlanTypes.SideSnapshot memory snap);
_getProjectedPendingFunding
function _getProjectedPendingFunding(
bytes32 accountId,
CfdTypes.Position memory pos
) internal view returns (int256 fundingUsdc);
_buildProjectedPositionRiskState
function _buildProjectedPositionRiskState(
bytes32 accountId,
CfdTypes.Position memory pos,
uint256 price,
uint256 reachableUsdc,
uint256 riskBps
) internal view returns (PositionRiskAccountingLib.PositionRiskState memory riskState);
_tryGetFreshLiveMarkPrice
function _tryGetFreshLiveMarkPrice() internal view returns (bool fresh, uint256 price);
_revertIfOpenInvalid
function _revertIfOpenInvalid(
CfdEnginePlanTypes.OpenRevertCode code
) internal pure;
_revertIfOpenInvalidTyped
function _revertIfOpenInvalidTyped(
CfdEnginePlanTypes.OpenRevertCode code
) internal pure;
_revertIfCloseInvalid
function _revertIfCloseInvalid(
CfdEnginePlanTypes.CloseRevertCode code
) internal pure;
_revertIfCloseInvalidTyped
function _revertIfCloseInvalidTyped(
CfdEnginePlanTypes.CloseRevertCode code
) internal pure;
_applyFundingAndMark
function _applyFundingAndMark(
int256 bullDelta,
int256 bearDelta,
uint256 absSkew,
uint64 newFundingTime,
uint256 newMarkPrice,
uint64 newMarkTime
) internal;
_applyFundingSettlement
function _applyFundingSettlement(
CfdEnginePlanTypes.FundingDelta memory fd,
bytes32 accountId,
StoredPosition storage pos,
CfdTypes.Side marginSide
) internal returns (uint256 marginAfterFunding);
_applyOpen
function _applyOpen(
CfdEnginePlanTypes.OpenDelta memory delta
) internal;
_applyClose
function _applyClose(
CfdEnginePlanTypes.CloseDelta memory delta
) internal;
_applyLiquidation
function _applyLiquidation(
CfdEnginePlanTypes.LiquidationDelta memory delta
) internal returns (uint256 keeperBountyUsdc);
_enterDegradedModeIfInsolvent
function _enterDegradedModeIfInsolvent(
bytes32 accountId,
uint256 pendingVaultPayoutUsdc
) internal;
_physicalReachableCollateralUsdc
function _physicalReachableCollateralUsdc(
bytes32 accountId
) internal view returns (uint256);
_positionMarginBucketUsdc
function _positionMarginBucketUsdc(
bytes32 accountId
) internal view returns (uint256);
_loadPosition
function _loadPosition(
bytes32 accountId
) internal view returns (CfdTypes.Position memory pos);
_liveMarkStalenessLimit
function _liveMarkStalenessLimit() internal view returns (uint256);
_consumeDeferredTraderPayout
function _consumeDeferredTraderPayout(
bytes32 accountId,
uint256 amountUsdc
) internal;
_computeGlobalFundingPnl
function _computeGlobalFundingPnl() internal view returns (int256 bullFunding, int256 bearFunding);
_getProjectedFundingIndices
function _getProjectedFundingIndices() internal view returns (int256 bullFundingIndex, int256 bearFundingIndex);
_buildFundingSnapshot
function _buildFundingSnapshot() internal view returns (CfdEngineSnapshotsLib.FundingSnapshot memory snapshot);
_getSolvencyCappedFundingPnl
function _getSolvencyCappedFundingPnl() internal view returns (int256);
_canProjectFundingStep
function _canProjectFundingStep() internal view returns (bool);
_buildFundingStep
function _buildFundingStep(
uint256 price,
uint256 vaultDepthUsdc
) internal view returns (PositionRiskAccountingLib.FundingStepResult memory step);
_getLiabilityOnlyFundingPnl
function _getLiabilityOnlyFundingPnl() internal view returns (uint256);
_validateRiskParams
function _validateRiskParams(
CfdTypes.RiskParams memory _riskParams
) internal pure;
_getUnrealizedFundingPnl
function _getUnrealizedFundingPnl() internal view returns (int256);
getUnrealizedFundingPnl
Aggregate unsettled funding across all positions (uncapped, for reporting only)
function getUnrealizedFundingPnl() external view returns (int256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | int256 | Net funding PnL in USDC (6 decimals), positive = traders are owed funding |
getCappedFundingPnl
Aggregate unsettled funding across all positions with uncollectible debts capped by margin.
function getCappedFundingPnl() external view returns (int256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | int256 | Net funding PnL in USDC (6 decimals), positive = traders are owed funding |
getLiabilityOnlyFundingPnl
Aggregate unsettled funding liabilities only, ignoring trader debts owed to the vault.
function getLiabilityOnlyFundingPnl() external view returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Funding liabilities the vault should conservatively reserve for withdrawals (6 decimals) |
getMaxLiability
Returns the protocol’s worst-case directional liability.
function getMaxLiability() external view returns (uint256);
getWithdrawalReservedUsdc
Returns the total USDC reserve required for withdrawals.
function getWithdrawalReservedUsdc() external view returns (uint256);
getHousePoolInputSnapshot
function getHousePoolInputSnapshot(
uint256 markStalenessLimit
) external view returns (ICfdEngine.HousePoolInputSnapshot memory snapshot);
getHousePoolStatusSnapshot
function getHousePoolStatusSnapshot() external view returns (ICfdEngine.HousePoolStatusSnapshot memory snapshot);
updateMarkPrice
Updates the cached mark price without settling funding or processing trades
function updateMarkPrice(
uint256 price,
uint64 publishTime
) external onlyRouter;
Parameters
| Name | Type | Description |
|---|---|---|
price | uint256 | Oracle price (8 decimals), clamped to CAP_PRICE |
publishTime | uint64 | Pyth publish timestamp |
hasLiveLiability
Returns true when the protocol still has live bounded directional liability.
function hasLiveLiability() external view returns (bool);
getUnrealizedTraderPnl
Aggregate unrealized PnL of all open positions at lastMarkPrice. Positive = traders winning (house liability). Negative = traders losing (house asset).
function getUnrealizedTraderPnl() external view returns (int256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | int256 | Net trader PnL in USDC (6 decimals), sign from the traders’ perspective |
getVaultMtmAdjustment
Combined MtM: per-side (PnL + funding), clamped at zero per side then summed. Positive = vault owes traders (unrealized liability). Zero = traders losing or neutral.
function getVaultMtmAdjustment() external view returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Net MtM liability the vault must reserve, in USDC (6 decimals). Non-negative by construction. |
_getVaultMtmLiability
function _getVaultMtmLiability() internal view returns (uint256);
_getProtocolPhase
function _getProtocolPhase() internal view returns (ICfdEngine.ProtocolPhase);
getProtocolPhase
function getProtocolPhase() external view returns (ICfdEngine.ProtocolPhase);
getProtocolStatus
function getProtocolStatus() external view returns (ICfdEngine.ProtocolStatus memory status);
Events
FundingUpdated
event FundingUpdated(int256 bullIndex, int256 bearIndex, uint256 absSkewUsdc);
PositionOpened
event PositionOpened(
bytes32 indexed accountId, CfdTypes.Side side, uint256 sizeDelta, uint256 price, uint256 marginDelta
);
PositionClosed
event PositionClosed(bytes32 indexed accountId, CfdTypes.Side side, uint256 sizeDelta, uint256 price, int256 pnl);
PositionLiquidated
event PositionLiquidated(
bytes32 indexed accountId, CfdTypes.Side side, uint256 size, uint256 price, uint256 keeperBounty
);
MarginAdded
event MarginAdded(bytes32 indexed accountId, uint256 amount);
FadDaysAdded
event FadDaysAdded(uint256[] timestamps);
FadDaysRemoved
event FadDaysRemoved(uint256[] timestamps);
FadMaxStalenessUpdated
event FadMaxStalenessUpdated(uint256 newStaleness);
FadRunwayUpdated
event FadRunwayUpdated(uint256 newRunway);
EngineMarkStalenessLimitProposed
event EngineMarkStalenessLimitProposed(uint256 newStaleness, uint256 activationTime);
EngineMarkStalenessLimitUpdated
event EngineMarkStalenessLimitUpdated(uint256 newStaleness);
RiskParamsProposed
event RiskParamsProposed(uint256 activationTime);
RiskParamsFinalized
event RiskParamsFinalized();
AddFadDaysProposed
event AddFadDaysProposed(uint256[] timestamps, uint256 activationTime);
AddFadDaysFinalized
event AddFadDaysFinalized();
RemoveFadDaysProposed
event RemoveFadDaysProposed(uint256[] timestamps, uint256 activationTime);
RemoveFadDaysFinalized
event RemoveFadDaysFinalized();
FadMaxStalenessProposed
event FadMaxStalenessProposed(uint256 newStaleness, uint256 activationTime);
FadMaxStalenessFinalized
event FadMaxStalenessFinalized();
FadRunwayProposed
event FadRunwayProposed(uint256 newRunway, uint256 activationTime);
FadRunwayFinalized
event FadRunwayFinalized();
BadDebtCleared
event BadDebtCleared(uint256 amount, uint256 remaining);
DegradedModeEntered
event DegradedModeEntered(uint256 effectiveAssets, uint256 maxLiability, bytes32 indexed triggeringAccount);
DegradedModeCleared
event DegradedModeCleared();
DeferredPayoutRecorded
event DeferredPayoutRecorded(bytes32 indexed accountId, uint256 amountUsdc);
DeferredPayoutClaimed
event DeferredPayoutClaimed(bytes32 indexed accountId, uint256 amountUsdc);
DeferredClearerBountyRecorded
event DeferredClearerBountyRecorded(address indexed keeper, uint256 amountUsdc);
DeferredClearerBountyClaimed
event DeferredClearerBountyClaimed(address indexed keeper, uint256 amountUsdc);
Errors
CfdEngine__Unauthorized
error CfdEngine__Unauthorized();
CfdEngine__VaultAlreadySet
error CfdEngine__VaultAlreadySet();
CfdEngine__RouterAlreadySet
error CfdEngine__RouterAlreadySet();
CfdEngine__NoFeesToWithdraw
error CfdEngine__NoFeesToWithdraw();
CfdEngine__NoDeferredPayout
error CfdEngine__NoDeferredPayout();
CfdEngine__InsufficientVaultLiquidity
error CfdEngine__InsufficientVaultLiquidity();
CfdEngine__NoDeferredClearerBounty
error CfdEngine__NoDeferredClearerBounty();
CfdEngine__DeferredClaimNotAtHead
error CfdEngine__DeferredClaimNotAtHead();
CfdEngine__MustCloseOpposingPosition
error CfdEngine__MustCloseOpposingPosition();
CfdEngine__FundingExceedsMargin
error CfdEngine__FundingExceedsMargin();
CfdEngine__VaultSolvencyExceeded
error CfdEngine__VaultSolvencyExceeded();
CfdEngine__MarginDrainedByFees
error CfdEngine__MarginDrainedByFees();
CfdEngine__CloseSizeExceedsPosition
error CfdEngine__CloseSizeExceedsPosition();
CfdEngine__NoPositionToLiquidate
error CfdEngine__NoPositionToLiquidate();
CfdEngine__PositionIsSolvent
error CfdEngine__PositionIsSolvent();
CfdEngine__PostOpSolvencyBreach
error CfdEngine__PostOpSolvencyBreach();
CfdEngine__InsufficientInitialMargin
error CfdEngine__InsufficientInitialMargin();
CfdEngine__PositionTooSmall
error CfdEngine__PositionTooSmall();
CfdEngine__WithdrawBlockedByOpenPosition
error CfdEngine__WithdrawBlockedByOpenPosition();
CfdEngine__EmptyDays
error CfdEngine__EmptyDays();
CfdEngine__ZeroStaleness
error CfdEngine__ZeroStaleness();
CfdEngine__RunwayTooLong
error CfdEngine__RunwayTooLong();
CfdEngine__PartialCloseUnderwaterFunding
error CfdEngine__PartialCloseUnderwaterFunding();
CfdEngine__DustPosition
error CfdEngine__DustPosition();
CfdEngine__MarkPriceStale
error CfdEngine__MarkPriceStale();
CfdEngine__MarkPriceOutOfOrder
error CfdEngine__MarkPriceOutOfOrder();
CfdEngine__NotAccountOwner
error CfdEngine__NotAccountOwner();
CfdEngine__NoOpenPosition
error CfdEngine__NoOpenPosition();
CfdEngine__TimelockNotReady
error CfdEngine__TimelockNotReady();
CfdEngine__NoProposal
error CfdEngine__NoProposal();
CfdEngine__BadDebtTooLarge
error CfdEngine__BadDebtTooLarge();
CfdEngine__InvalidRiskParams
error CfdEngine__InvalidRiskParams();
CfdEngine__SkewTooHigh
error CfdEngine__SkewTooHigh();
CfdEngine__DegradedMode
error CfdEngine__DegradedMode();
CfdEngine__NotDegraded
error CfdEngine__NotDegraded();
CfdEngine__StillInsolvent
error CfdEngine__StillInsolvent();
CfdEngine__ZeroAddress
error CfdEngine__ZeroAddress();
CfdEngine__InsufficientCloseOrderBountyBacking
error CfdEngine__InsufficientCloseOrderBountyBacking();
CfdEngine__InvalidVaultCashInflow
error CfdEngine__InvalidVaultCashInflow();
Structs
AccountCollateralView
struct AccountCollateralView {
uint256 settlementBalanceUsdc;
uint256 lockedMarginUsdc;
// Clearinghouse custody bucket for currently locked live position backing.
uint256 activePositionMarginUsdc;
uint256 otherLockedMarginUsdc;
uint256 freeSettlementUsdc;
// Current UI helper only; this does not include terminally reachable queued committed margin.
uint256 closeReachableUsdc;
uint256 terminalReachableUsdc;
uint256 accountEquityUsdc;
uint256 freeBuyingPowerUsdc;
uint256 deferredPayoutUsdc;
}
PositionView
struct PositionView {
bool exists;
CfdTypes.Side side;
uint256 size;
uint256 margin;
uint256 entryPrice;
uint256 entryNotionalUsdc;
uint256 physicalReachableCollateralUsdc;
uint256 nettableDeferredPayoutUsdc;
int256 unrealizedPnlUsdc;
int256 pendingFundingUsdc;
int256 netEquityUsdc;
uint256 maxProfitUsdc;
bool liquidatable;
}
ProtocolAccountingView
struct ProtocolAccountingView {
uint256 vaultAssetsUsdc;
uint256 maxLiabilityUsdc;
uint256 withdrawalReservedUsdc;
uint256 freeUsdc;
uint256 accumulatedFeesUsdc;
int256 cappedFundingPnlUsdc;
uint256 liabilityOnlyFundingPnlUsdc;
uint256 totalDeferredPayoutUsdc;
uint256 totalDeferredClearerBountyUsdc;
bool degradedMode;
bool hasLiveLiability;
}
ClosePreview
struct ClosePreview {
bool valid;
CfdTypes.CloseInvalidReason invalidReason;
uint256 executionPrice;
uint256 sizeDelta;
int256 realizedPnlUsdc;
int256 fundingUsdc;
int256 vpiDeltaUsdc;
uint256 vpiUsdc;
uint256 executionFeeUsdc;
uint256 freshTraderPayoutUsdc;
uint256 existingDeferredConsumedUsdc;
uint256 existingDeferredRemainingUsdc;
uint256 immediatePayoutUsdc;
uint256 deferredPayoutUsdc;
uint256 seizedCollateralUsdc;
uint256 badDebtUsdc;
uint256 remainingSize;
uint256 remainingMargin;
bool triggersDegradedMode;
bool postOpDegradedMode;
uint256 effectiveAssetsAfterUsdc;
uint256 maxLiabilityAfterUsdc;
int256 solvencyFundingPnlUsdc;
}
LiquidationPreview
struct LiquidationPreview {
bool liquidatable;
uint256 oraclePrice;
int256 equityUsdc;
int256 pnlUsdc;
int256 fundingUsdc;
uint256 reachableCollateralUsdc;
uint256 keeperBountyUsdc;
uint256 seizedCollateralUsdc;
uint256 settlementRetainedUsdc;
uint256 freshTraderPayoutUsdc;
uint256 existingDeferredConsumedUsdc;
uint256 existingDeferredRemainingUsdc;
uint256 immediatePayoutUsdc;
uint256 deferredPayoutUsdc;
uint256 badDebtUsdc;
bool triggersDegradedMode;
bool postOpDegradedMode;
uint256 effectiveAssetsAfterUsdc;
uint256 maxLiabilityAfterUsdc;
int256 solvencyFundingPnlUsdc;
}
DeferredPayoutStatus
struct DeferredPayoutStatus {
uint256 deferredTraderPayoutUsdc;
bool traderPayoutClaimableNow;
uint256 deferredClearerBountyUsdc;
bool liquidationBountyClaimableNow;
}
SideState
struct SideState {
uint256 maxProfitUsdc;
uint256 openInterest;
uint256 entryNotional;
// Cached aggregate of engine economic position margins for this side; not a custody bucket.
uint256 totalMargin;
int256 fundingIndex;
int256 entryFunding;
}
VaultCashInflow
struct VaultCashInflow {
uint256 physicalCashReceivedUsdc;
uint256 protocolOwnedUsdc;
uint256 lpOwnedUsdc;
}
StoredPosition
struct StoredPosition {
uint256 size;
uint256 entryPrice;
uint256 maxProfitUsdc;
int256 entryFundingIndex;
CfdTypes.Side side;
uint64 lastUpdateTime;
int256 vpiAccrued;
}
CfdEnginePlanTypes
Title: CfdEnginePlanTypes
Snapshot and delta structs for the plan→apply architecture. Plan functions are pure over a RawSnapshot and return typed deltas. Apply functions consume deltas to perform state mutations and external calls.
Structs
SideSnapshot
struct SideSnapshot {
uint256 maxProfitUsdc;
uint256 openInterest;
uint256 entryNotional;
uint256 totalMargin;
int256 fundingIndex;
int256 entryFunding;
}
RawSnapshot
struct RawSnapshot {
CfdTypes.Position position;
bytes32 accountId;
uint256 currentTimestamp;
uint64 lastFundingTime;
uint256 lastMarkPrice;
uint64 lastMarkTime;
SideSnapshot bullSide;
SideSnapshot bearSide;
uint256 fundingVaultDepthUsdc;
uint256 vaultAssetsUsdc;
uint256 vaultCashUsdc;
IMarginClearinghouse.AccountUsdcBuckets accountBuckets;
IMarginClearinghouse.LockedMarginBuckets lockedBuckets;
uint64[] marginReservationIds;
uint256 accumulatedFeesUsdc;
uint256 accumulatedBadDebtUsdc;
uint256 totalDeferredPayoutUsdc;
uint256 totalDeferredClearerBountyUsdc;
uint256 deferredPayoutForAccount;
bool degradedMode;
uint256 capPrice;
CfdTypes.RiskParams riskParams;
bool isFadWindow;
bool liveMarkFreshForFunding;
}
GlobalFundingDelta
struct GlobalFundingDelta {
int256 bullFundingIndexDelta;
int256 bearFundingIndexDelta;
uint256 fundingAbsSkewUsdc;
uint64 newLastFundingTime;
uint256 newLastMarkPrice;
uint64 newLastMarkTime;
}
FundingDelta
struct FundingDelta {
int256 bullFundingIndexDelta;
int256 bearFundingIndexDelta;
uint256 fundingAbsSkewUsdc;
uint64 newLastFundingTime;
uint256 newLastMarkPrice;
uint64 newLastMarkTime;
int256 pendingFundingUsdc;
int256 closeFundingSettlementUsdc;
FundingPayoutType payoutType;
uint256 fundingVaultPayoutUsdc;
uint256 fundingClearinghouseCreditUsdc;
uint256 fundingLossConsumedFromMargin;
uint256 fundingLossConsumedFromFree;
uint256 fundingLossUncovered;
uint256 posMarginIncrease;
uint256 posMarginDecrease;
int256 sideEntryFundingDelta;
int256 newPosEntryFundingIndex;
}
SolvencyPreview
struct SolvencyPreview {
uint256 effectiveAssetsAfterUsdc;
uint256 maxLiabilityAfterUsdc;
int256 solvencyFundingPnlUsdc;
bool triggersDegradedMode;
bool postOpDegradedMode;
}
OpenDelta
struct OpenDelta {
bool valid;
OpenRevertCode revertCode;
FundingDelta funding;
OpenAccountingLib.OpenState openState;
CfdTypes.Side posSide;
uint256 newPosSize;
uint256 newPosEntryPrice;
int256 posVpiAccruedDelta;
uint256 posMaxProfitIncrease;
uint256 positionMarginAfterOpen;
uint256 sideOiIncrease;
int256 sideEntryNotionalDelta;
int256 sideEntryFundingContribution;
uint256 sideMaxProfitIncrease;
int256 tradeCostUsdc;
uint256 marginDeltaUsdc;
int256 netMarginChange;
uint256 vaultRebatePayoutUsdc;
uint256 executionFeeUsdc;
uint256 sideTotalMarginBefore;
uint256 sideTotalMarginAfterFunding;
uint256 sideTotalMarginAfterOpen;
bytes32 accountId;
uint256 sizeDelta;
uint256 price;
uint256 effectivePositionMarginAfterFunding;
}
CloseDelta
struct CloseDelta {
bool valid;
CloseRevertCode revertCode;
FundingDelta funding;
CloseAccountingLib.CloseState closeState;
uint256 postBullOi;
uint256 postBearOi;
uint256 posMarginAfter;
uint256 posSizeDelta;
uint256 posMaxProfitReduction;
int256 posVpiAccruedReduction;
bool deletePosition;
CfdTypes.Side side;
uint256 sideOiDecrease;
uint256 sideEntryNotionalReduction;
int256 sideEntryFundingReduction;
uint256 sideMaxProfitReduction;
uint256 unlockMarginUsdc;
SettlementType settlementType;
uint256 freshTraderPayoutUsdc;
bool freshPayoutIsImmediate;
bool freshPayoutIsDeferred;
uint256 existingDeferredConsumedUsdc;
uint256 existingDeferredRemainingUsdc;
uint256 deferredFeeRecoveryUsdc;
CfdEngineSettlementLib.CloseSettlementResult lossResult;
MarginClearinghouseAccountingLib.SettlementConsumption lossConsumption;
uint256 syncMarginQueueAmount;
uint256 executionFeeUsdc;
uint256 badDebtUsdc;
uint256 totalMarginBefore;
uint256 totalMarginAfterFunding;
uint256 totalMarginAfterClose;
SolvencyPreview solvency;
bytes32 accountId;
uint256 sizeDelta;
uint256 price;
int256 realizedPnlUsdc;
}
LiquidationDelta
struct LiquidationDelta {
bool liquidatable;
GlobalFundingDelta funding;
PositionRiskAccountingLib.PositionRiskState riskState;
LiquidationAccountingLib.LiquidationState liquidationState;
CfdTypes.Side side;
uint256 posSize;
uint256 posMargin;
uint256 posMaxProfit;
uint256 posEntryPrice;
int256 posEntryFundingIndex;
uint256 sideOiDecrease;
uint256 sideMaxProfitDecrease;
uint256 sideEntryNotionalReduction;
int256 sideEntryFundingReduction;
uint256 sideTotalMarginReduction;
uint256 keeperBountyUsdc;
uint256 liquidationReachableCollateralUsdc;
int256 residualUsdc;
MarginClearinghouseAccountingLib.LiquidationResidualPlan residualPlan;
uint256 settlementRetainedUsdc;
uint256 freshTraderPayoutUsdc;
bool freshPayoutIsImmediate;
bool freshPayoutIsDeferred;
uint256 existingDeferredConsumedUsdc;
uint256 existingDeferredRemainingUsdc;
uint256 syncMarginQueueAmount;
uint256 badDebtUsdc;
SolvencyPreview solvency;
bytes32 accountId;
uint256 price;
}
Enums
OpenFailurePolicyCategory
enum OpenFailurePolicyCategory {
None,
CommitTimeRejectable,
ExecutionTimeUserInvalid,
ExecutionTimeProtocolStateInvalidated
}
ExecutionFailurePolicyCategory
enum ExecutionFailurePolicyCategory {
None,
UserInvalid,
ProtocolStateInvalidated
}
FundingPayoutType
enum FundingPayoutType {
NONE,
MARGIN_CREDIT,
CLOSE_SETTLEMENT,
DEFERRED_PAYOUT,
LOSS_CONSUMED,
LOSS_UNCOVERED_REVERT,
LOSS_UNCOVERED_CLOSE
}
OpenRevertCode
enum OpenRevertCode {
OK,
MUST_CLOSE_OPPOSING,
DEGRADED_MODE,
POSITION_TOO_SMALL,
SKEW_TOO_HIGH,
MARGIN_DRAINED_BY_FEES,
INSUFFICIENT_INITIAL_MARGIN,
SOLVENCY_EXCEEDED,
FUNDING_EXCEEDS_MARGIN
}
CloseRevertCode
enum CloseRevertCode {
OK,
CLOSE_SIZE_EXCEEDS,
DUST_POSITION,
PARTIAL_CLOSE_UNDERWATER,
FUNDING_PARTIAL_CLOSE_UNDERWATER
}
SettlementType
enum SettlementType {
ZERO,
GAIN,
LOSS
}
CfdMath
Title: CfdMath
Pure stateless math library for PnL, Price Impact, and Funding
Note: security-contact: contact@plether.com
Constants
WAD
uint256 internal constant WAD = 1e18
SECONDS_PER_YEAR
uint256 internal constant SECONDS_PER_YEAR = 31_536_000
USDC_TO_TOKEN_SCALE
uint256 internal constant USDC_TO_TOKEN_SCALE = 1e20
FUNDING_INDEX_SCALE
uint256 internal constant FUNDING_INDEX_SCALE = 1e30
Functions
calculatePnL
Calculates Unrealized PnL strictly bounded by the protocol CAP
function calculatePnL(
CfdTypes.Position memory pos,
uint256 currentOraclePrice,
uint256 capPrice
) internal pure returns (bool isProfit, uint256 pnlUsdc);
Parameters
| Name | Type | Description |
|---|---|---|
pos | CfdTypes.Position | The position to evaluate |
currentOraclePrice | uint256 | Current oracle price (8 decimals) |
capPrice | uint256 | Protocol cap price (8 decimals) |
Returns
| Name | Type | Description |
|---|---|---|
isProfit | bool | True if the position is in profit |
pnlUsdc | uint256 | Absolute PnL value in USDC (6 decimals) |
calculateMaxProfit
Calculates the absolute maximum payout a trade can ever achieve
function calculateMaxProfit(
uint256 size,
uint256 entryPrice,
CfdTypes.Side side,
uint256 capPrice
) internal pure returns (uint256 maxProfitUsdc);
Parameters
| Name | Type | Description |
|---|---|---|
size | uint256 | Notional size (18 decimals) |
entryPrice | uint256 | Entry oracle price (8 decimals) |
side | CfdTypes.Side | BULL or BEAR |
capPrice | uint256 | Protocol cap price (8 decimals) |
Returns
| Name | Type | Description |
|---|---|---|
maxProfitUsdc | uint256 | Maximum possible profit in USDC (6 decimals) |
_getSkewCost
Calculates the cost of a specific skew state. C(S) = 0.5 * k * (S^2 / D)
function _getSkewCost(
uint256 skewUsdc,
uint256 depthUsdc,
uint256 vpiFactorWad
) private pure returns (uint256 costUsdc);
Parameters
| Name | Type | Description |
|---|---|---|
skewUsdc | uint256 | The absolute directional imbalance in USDC (6 decimals) |
depthUsdc | uint256 | The total free USDC in the House Pool (6 decimals) |
vpiFactorWad | uint256 | The ‘k’ impact parameter (18 decimals) |
Returns
| Name | Type | Description |
|---|---|---|
costUsdc | uint256 | The theoretical cost to reach this skew (6 decimals) |
calculateVPI
Calculates the VPI charge/rebate for a trade.
If postCost > preCost, result is positive (Charge Trader). If postCost < preCost, result is negative (Rebate Trader / MM Incentive).
function calculateVPI(
uint256 preSkewUsdc,
uint256 postSkewUsdc,
uint256 depthUsdc,
uint256 vpiFactorWad
) internal pure returns (int256 vpiUsdc);
getAnnualizedFundingRate
Returns the annualized funding rate based on the kinked curve. Linear ramp up to kinkSkewRatio, quadratic acceleration above it.
function getAnnualizedFundingRate(
uint256 absSkewUsdc,
uint256 depthUsdc,
CfdTypes.RiskParams memory params
) internal pure returns (uint256 annualizedRateWad);
Parameters
| Name | Type | Description |
|---|---|---|
absSkewUsdc | uint256 | Absolute directional imbalance in USDC (6 decimals) |
depthUsdc | uint256 | Total pool depth in USDC (6 decimals) |
params | CfdTypes.RiskParams | Risk parameters defining the funding curve shape |
Returns
| Name | Type | Description |
|---|---|---|
annualizedRateWad | uint256 | Annualized rate (18 decimals WAD) |
CfdTypes
Title: CfdTypes
Core data structures for the Plether CFD Engine
Note: security-contact: contact@plether.com
Structs
Position
A user’s active position in a specific market.
margin is the engine’s canonical economic position-margin state used for risk and state transitions.
It is intentionally distinct from the clearinghouse custody bucket that holds the locked funds backing it.
struct Position {
uint256 size; // [18 dec] Notional size in synthetic tokens
uint256 margin; // [6 dec] Isolated margin backing this position
uint256 entryPrice; // [8 dec] Oracle price of BEAR at execution
uint256 maxProfitUsdc; // [6 dec] Cumulative max profit tracked to avoid truncation underflow
int256 entryFundingIndex; // [18 dec WAD] Global funding index at the time of entry
Side side; // [uint8] Trade direction
uint64 lastUpdateTime; // [uint64] Timestamp of last modification
int256 vpiAccrued; // [6 dec] Cumulative VPI charges (+) and rebates (-) across the position's lifetime
}
Order
An intent submitted by a user, waiting for Keeper execution
struct Order {
bytes32 accountId; // Maps to MarginClearinghouse unified account
uint256 sizeDelta; // [18 dec] Amount of size to add/remove
uint256 marginDelta; // [6 dec] Amount of margin to add/remove
uint256 targetPrice; // [8 dec] Slippage protection limit
uint64 commitTime; // Timestamp of intent submission (MEV shield)
uint64 commitBlock; // Block number of intent submission (same-block execution shield)
uint64 orderId; // Strict FIFO execution queue ID
Side side; // [uint8] BULL or BEAR
bool isClose; // [bool] True if strictly closing/reducing
}
RiskParams
Global configuration parameters for the VPI and Funding engines
struct RiskParams {
uint256 vpiFactor; // [18 dec WAD] Impact severity 'k'
uint256 maxSkewRatio; // [18 dec WAD] Hard cliff e.g., 40% (0.40e18)
uint256 kinkSkewRatio; // [18 dec WAD] Inflection point e.g., 25% (0.25e18)
uint256 baseApy; // [18 dec WAD] Rate at the kink e.g., 15% (0.15e18)
uint256 maxApy; // [18 dec WAD] Rate at the wall e.g., 300% (3.00e18)
uint256 maintMarginBps; // e.g., 100 (1%)
uint256 initMarginBps; // e.g., 150 (1.5%)
uint256 fadMarginBps; // e.g., 300 (3%)
uint256 minBountyUsdc; // e.g., 5_000_000 ($5 USDC floor)
uint256 bountyBps; // e.g., 15 (0.15% of Notional Size)
}
Enums
Side
BULL profits when USD strengthens (oracle price drops). BEAR profits when USD weakens (oracle price rises).
enum Side {
BULL,
BEAR
}
CloseInvalidReason
enum CloseInvalidReason {
None,
NoPosition,
BadSize,
PartialCloseUnderwater,
DustPosition
}
HousePool
Inherits: ICfdVault, IHousePool, Ownable2Step, Pausable
Title: HousePool
Tranched house pool. Senior tranche gets fixed-rate yield with last-loss protection. Junior tranche absorbs first loss but captures surplus revenue.
Note: security-contact: contact@plether.com
Constants
USDC
IERC20 public immutable USDC
ENGINE
ICfdEngine public immutable ENGINE
TIMELOCK_DELAY
uint256 public constant TIMELOCK_DELAY = 48 hours
State Variables
orderRouter
address public orderRouter
seniorVault
address public seniorVault
juniorVault
address public juniorVault
seniorPrincipal
uint256 public seniorPrincipal
juniorPrincipal
uint256 public juniorPrincipal
unpaidSeniorYield
uint256 public unpaidSeniorYield
seniorHighWaterMark
uint256 public seniorHighWaterMark
accountedAssets
uint256 public accountedAssets
unassignedAssets
uint256 public unassignedAssets
pendingRecapitalizationUsdc
uint256 public pendingRecapitalizationUsdc
pendingTradingRevenueUsdc
uint256 public pendingTradingRevenueUsdc
lastReconcileTime
uint256 public lastReconcileTime
lastSeniorYieldCheckpointTime
uint256 public lastSeniorYieldCheckpointTime
seniorRateBps
uint256 public seniorRateBps
markStalenessLimit
uint256 public markStalenessLimit = 60
isTradingActive
bool public override(ICfdVault, IHousePool) isTradingActive
seniorSeedInitialized
bool public seniorSeedInitialized
juniorSeedInitialized
bool public juniorSeedInitialized
pendingSeniorRate
uint256 public pendingSeniorRate
seniorRateActivationTime
uint256 public seniorRateActivationTime
pendingMarkStalenessLimit
uint256 public pendingMarkStalenessLimit
markStalenessLimitActivationTime
uint256 public markStalenessLimitActivationTime
Functions
onlyVault
modifier onlyVault() ;
constructor
constructor(
address _usdc,
address _engine
) Ownable(msg.sender);
Parameters
| Name | Type | Description |
|---|---|---|
_usdc | address | USDC token address used as collateral |
_engine | address | CfdEngine that manages positions and PnL |
setOrderRouter
Set the OrderRouter address (one-time, immutable after set)
function setOrderRouter(
address _router
) external onlyOwner;
setSeniorVault
Set the senior tranche vault address (one-time, immutable after set)
function setSeniorVault(
address _vault
) external onlyOwner;
setJuniorVault
Set the junior tranche vault address (one-time, immutable after set)
function setJuniorVault(
address _vault
) external onlyOwner;
proposeSeniorRate
Propose a new senior yield rate, subject to 48h timelock
function proposeSeniorRate(
uint256 _rateBps
) external onlyOwner;
finalizeSeniorRate
Finalize the proposed senior rate after timelock expires.
Syncs funding first. If the mark is stale, the new rate is applied without accruing stale-window senior yield.
function finalizeSeniorRate() external onlyOwner;
cancelSeniorRateProposal
Cancel the pending senior rate proposal
function cancelSeniorRateProposal() external onlyOwner;
proposeMarkStalenessLimit
Propose a new mark-price staleness limit, subject to 48h timelock
function proposeMarkStalenessLimit(
uint256 _limit
) external onlyOwner;
finalizeMarkStalenessLimit
Finalize the proposed staleness limit after timelock expires
function finalizeMarkStalenessLimit() external onlyOwner;
cancelMarkStalenessLimitProposal
Cancel the pending staleness limit proposal
function cancelMarkStalenessLimitProposal() external onlyOwner;
pause
Pause deposits into both tranches
function pause() external onlyOwner;
unpause
Unpause deposits into both tranches
function unpause() external onlyOwner;
totalAssets
Canonical economic USDC backing recognized by the pool. Unsolicited positive transfers are ignored until explicitly accounted, while raw-balance shortfalls still reduce the effective backing.
function totalAssets() public view returns (uint256);
isSeedLifecycleComplete
function isSeedLifecycleComplete() public view returns (bool);
hasSeedLifecycleStarted
function hasSeedLifecycleStarted() public view override(ICfdVault, IHousePool) returns (bool);
canAcceptOrdinaryDeposits
function canAcceptOrdinaryDeposits() public view override(ICfdVault, IHousePool) returns (bool);
canAcceptTrancheDeposits
function canAcceptTrancheDeposits(
bool isSenior
) public view override returns (bool);
canIncreaseRisk
function canIncreaseRisk() public view override(ICfdVault, IHousePool) returns (bool);
activateTrading
function activateTrading() external onlyOwner;
rawAssets
Raw USDC balance currently held by the pool, including unsolicited transfers.
function rawAssets() public view returns (uint256);
excessAssets
Raw USDC held above canonical accounted assets.
function excessAssets() public view returns (uint256);
accountExcess
Explicitly converts unsolicited USDC into accounted protocol assets. Syncs funding first so the added depth applies only going forward.
function accountExcess() external onlyOwner;
sweepExcess
Sweeps unsolicited USDC that has not been accounted into protocol economics.
function sweepExcess(
address recipient,
uint256 amount
) external onlyOwner;
payOut
Transfers USDC from the pool. Callable by CfdEngine (PnL/funding) or OrderRouter (keeper bounties).
function payOut(
address recipient,
uint256 amount
) external;
Parameters
| Name | Type | Description |
|---|---|---|
recipient | address | Address to receive USDC |
amount | uint256 | USDC amount to transfer (6 decimals) |
recordProtocolInflow
Accounts a legitimate protocol-owned inflow into canonical vault assets.
Only the engine or order router may use this path. Unlike accountExcess(), this does
not require raw excess to exist: it is the explicit accounting hook for endogenous
protocol gains and may also be used to restore canonical accounting after a raw-balance
shortfall has already reduced effective assets through totalAssets() = min(raw, accounted).
function recordProtocolInflow(
uint256 amount
) external;
recordRecapitalizationInflow
Accounts a governance recapitalization inflow and routes it toward senior restoration when possible.
This narrows the cases that fall into generic unassigned accounting when a seeded senior tranche exists.
function recordRecapitalizationInflow(
uint256 amount
) external;
recordTradingRevenueInflow
Accounts LP-owned trading revenue and directly attaches it to seeded claimants when no live principal exists.
Used for realized trader losses / spread capture paths whose economic owner is LP equity rather than protocol fees.
function recordTradingRevenueInflow(
uint256 amount
) external;
assignUnassignedAssets
Explicitly bootstraps quarantined LP assets into a tranche by minting matching shares.
Prevents later LPs from implicitly capturing value that arrived while no claimant shares existed.
function assignUnassignedAssets(
bool toSenior,
address receiver
) external onlyOwner;
initializeSeedPosition
Seeds a tranche with a permanent minimum share supply backed by real USDC.
Syncs funding first so the new seed depth only affects funding prospectively, then mints bootstrap shares to ensure a tranche never becomes ownerless in steady state.
function initializeSeedPosition(
bool toSenior,
uint256 amount,
address receiver
) external onlyOwner;
depositSenior
Deposit USDC into the senior tranche. Reverts if senior is impaired (below high-water mark).
function depositSenior(
uint256 amount
) external onlyVault whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | USDC to deposit (6 decimals) |
withdrawSenior
Withdraw USDC from the senior tranche. Scales high-water mark and unpaid yield proportionally.
function withdrawSenior(
uint256 amount,
address receiver
) external onlyVault;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | USDC to withdraw (6 decimals) |
receiver | address | Address to receive USDC |
depositJunior
Deposit USDC into the junior tranche.
function depositJunior(
uint256 amount
) external onlyVault whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | USDC to deposit (6 decimals) |
withdrawJunior
Withdraw USDC from the junior tranche. Limited to free USDC above senior’s claim.
function withdrawJunior(
uint256 amount,
address receiver
) external onlyVault;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | USDC to withdraw (6 decimals) |
receiver | address | Address to receive USDC |
getFreeUSDC
Returns USDC not reserved for worst-case position payouts (max of bull/bear liability)
function getFreeUSDC() public view returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Free USDC available for withdrawals (6 decimals) |
getMaxSeniorWithdraw
Max USDC the senior tranche can withdraw (limited by free USDC)
function getMaxSeniorWithdraw() public view returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Withdrawable senior USDC, capped at seniorPrincipal (6 decimals) |
getMaxJuniorWithdraw
Max USDC the junior tranche can withdraw (subordinated behind senior)
function getMaxJuniorWithdraw() public view returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Withdrawable junior USDC, capped at juniorPrincipal (6 decimals) |
getPendingTrancheState
Returns tranche principals and withdrawal caps as if reconcile ran right now.
Read-only preview for ERC4626 consumers that need same-tx parity with reconcile-first vault flows.
function getPendingTrancheState()
external
view
returns (
uint256 seniorPrincipalUsdc,
uint256 juniorPrincipalUsdc,
uint256 maxSeniorWithdrawUsdc,
uint256 maxJuniorWithdrawUsdc
);
isWithdrawalLive
function isWithdrawalLive() external view returns (bool);
getVaultLiquidityView
Snapshot of pool liquidity, tranche principals, and oracle health for frontend consumption
function getVaultLiquidityView() external view returns (VaultLiquidityView memory viewData);
Returns
| Name | Type | Description |
|---|---|---|
viewData | VaultLiquidityView | Struct containing balances, reserves, and status flags |
reconcile
Distributes revenue (senior yield first, junior gets surplus) or absorbs losses (junior first-loss, senior last-loss). Called before any deposit/withdrawal.
function reconcile() external onlyVault;
_requireFreshMark
function _requireFreshMark(
ICfdEngine.HousePoolInputSnapshot memory accountingSnapshot,
ICfdEngine.HousePoolStatusSnapshot memory statusSnapshot
) internal view;
_reconcile
function _reconcile(
ICfdEngine.HousePoolInputSnapshot memory accountingSnapshot
) internal;
_getWithdrawalSnapshot
function _getWithdrawalSnapshot()
internal
view
returns (HousePoolAccountingLib.WithdrawalSnapshot memory snapshot);
_buildHousePoolContext
function _buildHousePoolContext(
ICfdEngine.HousePoolInputSnapshot memory accountingSnapshot,
ICfdEngine.HousePoolStatusSnapshot memory statusSnapshot
) internal view returns (HousePoolContext memory ctx);
_syncAndBuildHousePoolContext
function _syncAndBuildHousePoolContext() internal returns (HousePoolContext memory ctx);
_buildCurrentHousePoolContext
function _buildCurrentHousePoolContext() internal view returns (HousePoolContext memory ctx);
_previewPendingAccountingState
function _previewPendingAccountingState(
ICfdEngine.HousePoolInputSnapshot memory accountingSnapshot,
ICfdEngine.HousePoolStatusSnapshot memory statusSnapshot
) internal view returns (PendingAccountingState memory pendingState);
_markIsFreshForReconcile
function _markIsFreshForReconcile(
ICfdEngine.HousePoolInputSnapshot memory accountingSnapshot,
ICfdEngine.HousePoolStatusSnapshot memory statusSnapshot
) internal view returns (bool);
_withdrawalsLive
function _withdrawalsLive(
ICfdEngine.HousePoolInputSnapshot memory accountingSnapshot,
ICfdEngine.HousePoolStatusSnapshot memory statusSnapshot
) internal view returns (bool);
_normalizeUnassignedAssets
function _normalizeUnassignedAssets(
uint256 distributableUsdc
) internal view returns (uint256 normalized);
_juniorShareSupply
function _juniorShareSupply() internal view returns (uint256);
_seniorShareSupply
function _seniorShareSupply() internal view returns (uint256);
_requireNoPendingBootstrap
function _requireNoPendingBootstrap() internal view;
_buildWithdrawalSnapshot
function _buildWithdrawalSnapshot(
ICfdEngine.HousePoolInputSnapshot memory accountingSnapshot,
uint256 reservedUnassignedAssets,
bool isProjected
) internal view returns (HousePoolAccountingLib.WithdrawalSnapshot memory snapshot);
_checkpointSeniorYieldBeforePrincipalMutation
function _checkpointSeniorYieldBeforePrincipalMutation(
ICfdEngine.HousePoolInputSnapshot memory accountingSnapshot,
ICfdEngine.HousePoolStatusSnapshot memory statusSnapshot
) internal;
_applyPendingBucketsLive
function _applyPendingBucketsLive(
ICfdEngine.HousePoolInputSnapshot memory accountingSnapshot,
ICfdEngine.HousePoolStatusSnapshot memory statusSnapshot
) internal;
_applyPendingBucketsPreview
function _applyPendingBucketsPreview(
PendingAccountingState memory state
) internal view;
_applyRecapitalizationIntent
function _applyRecapitalizationIntent(
PendingAccountingState memory state,
uint256 amount
) internal pure;
_routeSeededRevenue
function _routeSeededRevenue(
PendingAccountingState memory state,
uint256 amount
) internal pure;
_copyPendingAccountingState
function _copyPendingAccountingState(
PendingAccountingState memory state
) internal pure returns (HousePoolPendingPreviewLib.PendingAccountingState memory copiedState);
_pendingBucketAssets
function _pendingBucketAssets() internal view returns (uint256);
_getHousePoolInputSnapshot
function _getHousePoolInputSnapshot() internal view returns (ICfdEngine.HousePoolInputSnapshot memory snapshot);
_getHousePoolStatusSnapshot
function _getHousePoolStatusSnapshot() internal view returns (ICfdEngine.HousePoolStatusSnapshot memory snapshot);
_getHousePoolSnapshots
function _getHousePoolSnapshots()
internal
view
returns (
ICfdEngine.HousePoolInputSnapshot memory accountingSnapshot,
ICfdEngine.HousePoolStatusSnapshot memory statusSnapshot
);
_requireWithdrawalsLive
function _requireWithdrawalsLive(
ICfdEngine.HousePoolStatusSnapshot memory statusSnapshot
) internal pure;
_distributeRevenue
function _distributeRevenue(
uint256 revenue
) internal;
_absorbLoss
function _absorbLoss(
uint256 loss
) internal;
_getWaterfallState
function _getWaterfallState() internal view returns (HousePoolWaterfallAccountingLib.WaterfallState memory state);
_setWaterfallState
function _setWaterfallState(
HousePoolWaterfallAccountingLib.WaterfallState memory state
) internal;
Events
Reconciled
event Reconciled(uint256 seniorPrincipal, uint256 juniorPrincipal, int256 delta);
SeniorRateUpdated
event SeniorRateUpdated(uint256 newRateBps);
MarkStalenessLimitUpdated
event MarkStalenessLimitUpdated(uint256 newLimit);
SeniorRateProposed
event SeniorRateProposed(uint256 newRateBps, uint256 activationTime);
SeniorRateFinalized
event SeniorRateFinalized();
MarkStalenessLimitProposed
event MarkStalenessLimitProposed(uint256 newLimit, uint256 activationTime);
MarkStalenessLimitFinalized
event MarkStalenessLimitFinalized();
ExcessAccounted
event ExcessAccounted(uint256 amountUsdc, uint256 accountedAssetsUsdc);
ExcessSwept
event ExcessSwept(address indexed recipient, uint256 amountUsdc);
ProtocolInflowAccounted
event ProtocolInflowAccounted(address indexed caller, uint256 amountUsdc, uint256 accountedAssetsUsdc);
RecapitalizationInflowAccounted
event RecapitalizationInflowAccounted(address indexed caller, uint256 amountUsdc, uint256 seniorRestorationUsdc);
TradingRevenueInflowAccounted
event TradingRevenueInflowAccounted(
address indexed caller, uint256 amountUsdc, uint256 seniorAssignedUsdc, uint256 juniorAssignedUsdc
);
UnassignedAssetsAssigned
event UnassignedAssetsAssigned(
bool indexed toSenior, address indexed receiver, uint256 amountUsdc, uint256 sharesMinted
);
SeedPositionInitialized
event SeedPositionInitialized(
bool indexed toSenior, address indexed receiver, uint256 amountUsdc, uint256 sharesMinted
);
TradingActivated
event TradingActivated();
Errors
HousePool__NotAVault
error HousePool__NotAVault();
HousePool__RouterAlreadySet
error HousePool__RouterAlreadySet();
HousePool__SeniorVaultAlreadySet
error HousePool__SeniorVaultAlreadySet();
HousePool__JuniorVaultAlreadySet
error HousePool__JuniorVaultAlreadySet();
HousePool__Unauthorized
error HousePool__Unauthorized();
HousePool__ExceedsMaxSeniorWithdraw
error HousePool__ExceedsMaxSeniorWithdraw();
HousePool__ExceedsMaxJuniorWithdraw
error HousePool__ExceedsMaxJuniorWithdraw();
HousePool__MarkPriceStale
error HousePool__MarkPriceStale();
HousePool__TimelockNotReady
error HousePool__TimelockNotReady();
HousePool__NoProposal
error HousePool__NoProposal();
HousePool__SeniorImpaired
error HousePool__SeniorImpaired();
HousePool__DegradedMode
error HousePool__DegradedMode();
HousePool__ZeroAddress
error HousePool__ZeroAddress();
HousePool__ZeroStaleness
error HousePool__ZeroStaleness();
HousePool__InvalidSeniorRate
error HousePool__InvalidSeniorRate();
HousePool__NoExcessAssets
error HousePool__NoExcessAssets();
HousePool__ExcessAmountTooHigh
error HousePool__ExcessAmountTooHigh();
HousePool__PendingBootstrap
error HousePool__PendingBootstrap();
HousePool__NoUnassignedAssets
error HousePool__NoUnassignedAssets();
HousePool__BootstrapSharesZero
error HousePool__BootstrapSharesZero();
HousePool__SeedAlreadyInitialized
error HousePool__SeedAlreadyInitialized();
HousePool__TradingActivationNotReady
error HousePool__TradingActivationNotReady();
Structs
VaultLiquidityView
struct VaultLiquidityView {
uint256 totalAssetsUsdc;
uint256 freeUsdc;
uint256 withdrawalReservedUsdc;
uint256 seniorPrincipalUsdc;
uint256 juniorPrincipalUsdc;
uint256 unpaidSeniorYieldUsdc;
uint256 seniorHighWaterMarkUsdc;
bool markFresh;
bool oracleFrozen;
bool degradedMode;
}
PendingAccountingState
struct PendingAccountingState {
HousePoolWaterfallAccountingLib.WaterfallState waterfall;
uint256 unassignedAssets;
uint256 seniorSupply;
uint256 juniorSupply;
}
HousePoolContext
struct HousePoolContext {
ICfdEngine.HousePoolInputSnapshot accountingSnapshot;
ICfdEngine.HousePoolStatusSnapshot statusSnapshot;
PendingAccountingState pendingState;
}
MarginClearinghouse
Inherits: Ownable2Step
Title: MarginClearinghouse
USDC-only cross-margin account manager for Plether.
Holds settlement balances and locked margin for CFD accounts.
Note: security-contact: contact@plether.com
Constants
settlementAsset
address public immutable settlementAsset
State Variables
settlementBalances
mapping(bytes32 => uint256) internal settlementBalances
positionMarginUsdc
mapping(bytes32 => uint256) internal positionMarginUsdc
committedOrderMarginUsdc
mapping(bytes32 => uint256) internal committedOrderMarginUsdc
reservedSettlementUsdc
mapping(bytes32 => uint256) internal reservedSettlementUsdc
orderReservations
mapping(uint64 => IMarginClearinghouse.OrderReservation) internal orderReservations
reservationIdsByAccount
mapping(bytes32 => uint64[]) internal reservationIdsByAccount
reservationHeadIndexByAccount
mapping(bytes32 => uint256) internal reservationHeadIndexByAccount
activeCommittedOrderReservationUsdc
mapping(bytes32 => uint256) internal activeCommittedOrderReservationUsdc
activeReservedSettlementReservationUsdc
mapping(bytes32 => uint256) internal activeReservedSettlementReservationUsdc
activeReservationCount
mapping(bytes32 => uint256) internal activeReservationCount
engine
address public engine
Functions
onlyOperator
modifier onlyOperator() ;
constructor
constructor(
address _settlementAsset
) Ownable(msg.sender);
Parameters
| Name | Type | Description |
|---|---|---|
_settlementAsset | address | USDC address used for PnL settlement and margin backing |
setEngine
Sets the CfdEngine address (one-time, reverts if already set).
function setEngine(
address _engine
) external onlyOwner;
deposit
Deposits settlement USDC into the specified margin account.
function deposit(
bytes32 accountId,
uint256 amount
) external;
Parameters
| Name | Type | Description |
|---|---|---|
accountId | bytes32 | Deterministic account ID derived from msg.sender address |
amount | uint256 | Token amount to transfer in |
withdraw
Withdraws settlement USDC from a margin account.
function withdraw(
bytes32 accountId,
uint256 amount
) external;
Parameters
| Name | Type | Description |
|---|---|---|
accountId | bytes32 | Deterministic account ID derived from msg.sender address |
amount | uint256 | USDC amount to withdraw |
getAccountEquityUsdc
Returns the total USD buying power of the account (6 decimals).
function getAccountEquityUsdc(
bytes32 accountId
) public view returns (uint256 totalEquityUsdc);
Parameters
| Name | Type | Description |
|---|---|---|
accountId | bytes32 | Account to value |
Returns
| Name | Type | Description |
|---|---|---|
totalEquityUsdc | uint256 | Settlement balance in USDC (6 decimals) |
getFreeBuyingPowerUsdc
Returns strictly unencumbered purchasing power
function getFreeBuyingPowerUsdc(
bytes32 accountId
) public view returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
accountId | bytes32 | Account to query |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Equity minus locked margin, floored at zero (6 decimals) |
getAccountUsdcBuckets
Returns the explicit USDC bucket split after subtracting the clearinghouse’s typed locked-margin buckets.
function getAccountUsdcBuckets(
bytes32 accountId
) public view returns (IMarginClearinghouse.AccountUsdcBuckets memory buckets);
getFreeSettlementBalanceUsdc
function getFreeSettlementBalanceUsdc(
bytes32 accountId
) public view returns (uint256);
getTerminalReachableUsdc
Returns settlement balance reachable by a terminal settlement path.
function getTerminalReachableUsdc(
bytes32 accountId
) public view returns (uint256);
getSettlementReachableUsdc
Returns settlement balance reachable after protecting only an explicitly remaining margin bucket.
This is the canonical helper for terminal settlement paths: full closes and liquidations should pass zero protected margin, while partial closes should protect only the residual position margin that remains open after settlement.
function getSettlementReachableUsdc(
bytes32 accountId,
uint256 protectedLockedMarginUsdc
) public view returns (uint256);
lockPositionMargin
Locks margin to back a new CFD trade. Requires sufficient USDC to back settlement (non-USDC equity alone is insufficient).
function lockPositionMargin(
bytes32 accountId,
uint256 amountUsdc
) external onlyOperator;
Parameters
| Name | Type | Description |
|---|---|---|
accountId | bytes32 | Account to lock margin on |
amountUsdc | uint256 | USDC amount to lock (6 decimals) |
unlockPositionMargin
Unlocks active position margin when a CFD trade closes
function unlockPositionMargin(
bytes32 accountId,
uint256 amountUsdc
) external onlyOperator;
Parameters
| Name | Type | Description |
|---|---|---|
accountId | bytes32 | Account to unlock margin on |
amountUsdc | uint256 | USDC amount to unlock (6 decimals), clamped to current locked amount |
lockCommittedOrderMargin
Locks margin to back a pending order commitment.
function lockCommittedOrderMargin(
bytes32 accountId,
uint256 amountUsdc
) external onlyOperator;
Parameters
| Name | Type | Description |
|---|---|---|
accountId | bytes32 | Account to lock margin on |
amountUsdc | uint256 | USDC amount to lock (6 decimals) |
reserveCommittedOrderMargin
function reserveCommittedOrderMargin(
bytes32 accountId,
uint64 orderId,
uint256 amountUsdc
) external onlyOperator;
unlockCommittedOrderMargin
Unlocks committed order margin when an order is cancelled or filled.
function unlockCommittedOrderMargin(
bytes32 accountId,
uint256 amountUsdc
) external onlyOperator;
Parameters
| Name | Type | Description |
|---|---|---|
accountId | bytes32 | Account to unlock margin on |
amountUsdc | uint256 | USDC amount to unlock (6 decimals) |
releaseOrderReservation
function releaseOrderReservation(
uint64 orderId
) external onlyOperator returns (uint256 releasedUsdc);
releaseOrderReservationIfActive
function releaseOrderReservationIfActive(
uint64 orderId
) external onlyOperator returns (uint256 releasedUsdc);
consumeOrderReservation
function consumeOrderReservation(
uint64 orderId,
uint256 amountUsdc
) external onlyOperator returns (uint256 consumedUsdc);
consumeAccountOrderReservations
function consumeAccountOrderReservations(
bytes32 accountId,
uint256 amountUsdc
) external onlyOperator returns (uint256 consumedUsdc);
consumeOrderReservationsById
function consumeOrderReservationsById(
uint64[] calldata orderIds,
uint256 amountUsdc
) external onlyOperator returns (uint256 consumedUsdc);
_consumeOrderReservationsById
function _consumeOrderReservationsById(
uint64[] memory orderIds,
uint256 amountUsdc
) internal returns (uint256 consumedUsdc);
_consumeAccountOrderReservations
function _consumeAccountOrderReservations(
bytes32 accountId,
uint256 amountUsdc,
bool consumeBuckets
) internal returns (uint256 consumedUsdc);
_releaseReservation
function _releaseReservation(
IMarginClearinghouse.OrderReservation storage reservation,
bool consumeBuckets
) internal returns (uint256 releasedUsdc);
_consumeReservation
function _consumeReservation(
IMarginClearinghouse.OrderReservation storage reservation,
uint256 amountUsdc,
bool consumeBuckets,
IMarginClearinghouse.ReservationStatus terminalStatus
) internal;
_decreaseActiveReservation
function _decreaseActiveReservation(
bytes32 accountId,
IMarginClearinghouse.ReservationBucket bucket,
uint256 amountUsdc
) internal;
lockReservedSettlement
function lockReservedSettlement(
bytes32 accountId,
uint256 amountUsdc
) external onlyOperator;
unlockReservedSettlement
function unlockReservedSettlement(
bytes32 accountId,
uint256 amountUsdc
) external onlyOperator;
settleUsdc
Adjusts USDC balance to settle funding, PnL, and VPI rebates. Positive amounts credit the account; negative amounts debit it.
function settleUsdc(
bytes32 accountId,
int256 amount
) external onlyOperator;
Parameters
| Name | Type | Description |
|---|---|---|
accountId | bytes32 | Account to settle |
amount | int256 | Signed USDC delta: positive credits, negative debits (6 decimals) |
creditSettlementAndLockMargin
Credits settlement USDC and locks the same amount as active margin.
function creditSettlementAndLockMargin(
bytes32 accountId,
uint256 amountUsdc
) external onlyOperator;
applyOpenCost
Applies an open/increase trade cost by debiting or crediting settlement and updating locked margin.
function applyOpenCost(
bytes32 accountId,
uint256 marginDeltaUsdc,
int256 tradeCostUsdc,
address recipient
) external onlyOperator returns (int256 netMarginChangeUsdc);
consumeFundingLoss
Consumes a funding loss from free settlement first, then from the active position margin bucket.
Unrelated locked margin remains protected.
function consumeFundingLoss(
bytes32 accountId,
uint256,
uint256 lossUsdc,
address recipient
)
external
onlyOperator
returns (uint256 marginConsumedUsdc, uint256 freeSettlementConsumedUsdc, uint256 uncoveredUsdc);
consumeCloseLoss
Consumes close-path losses from settlement buckets while preserving any explicitly protected remaining position margin.
function consumeCloseLoss(
bytes32 accountId,
uint64[] calldata reservationOrderIds,
uint256 lossUsdc,
uint256 protectedLockedMarginUsdc,
bool includeOtherLockedMargin,
address recipient
) external onlyOperator returns (uint256 seizedUsdc, uint256 shortfallUsdc);
applyLiquidationSettlementPlan
Applies a pre-planned liquidation settlement mutation.
Releases the active position margin bucket and covered committed margin exactly as planned.
function applyLiquidationSettlementPlan(
bytes32 accountId,
uint64[] calldata reservationOrderIds,
IMarginClearinghouse.LiquidationSettlementPlan calldata plan,
address recipient
) external onlyOperator returns (uint256 seizedUsdc);
_buildAccountUsdcBuckets
function _buildAccountUsdcBuckets(
bytes32 accountId
) internal view returns (IMarginClearinghouse.AccountUsdcBuckets memory buckets);
_planFundingLossConsumption
function _planFundingLossConsumption(
bytes32 accountId,
uint256 lossUsdc
) internal view returns (MarginClearinghouseAccountingLib.SettlementConsumption memory consumption);
_creditSettlementUsdc
function _creditSettlementUsdc(
bytes32 accountId,
uint256 amountUsdc
) internal;
_debitSettlementUsdc
function _debitSettlementUsdc(
bytes32 accountId,
uint256 amountUsdc
) internal;
_lockMargin
function _lockMargin(
bytes32 accountId,
IMarginClearinghouse.MarginBucket bucket,
uint256 amountUsdc
) internal;
_unlockMargin
function _unlockMargin(
bytes32 accountId,
IMarginClearinghouse.MarginBucket bucket,
uint256 amountUsdc
) internal;
_consumeOtherLockedMargin
Consumes non-position locked margin by priority: committed-order margin first, then reserved settlement. Queued order margin is released before reserved settlement because failed/cancelled order intents are softer obligations than explicitly reserved settlement buckets.
function _consumeOtherLockedMargin(
bytes32 accountId,
uint256 amountUsdc
) internal;
_consumeOtherLockedMarginViaReservations
function _consumeOtherLockedMarginViaReservations(
bytes32 accountId,
uint64[] calldata reservationOrderIds,
uint256 amountUsdc
) internal;
_consumeReservationBucket
function _consumeReservationBucket(
bytes32 accountId,
IMarginClearinghouse.ReservationBucket bucket,
uint256 amountUsdc
) internal;
_activeReservation
function _activeReservation(
uint64 orderId
) internal view returns (IMarginClearinghouse.OrderReservation storage reservation);
_closeReservation
function _closeReservation(
IMarginClearinghouse.OrderReservation storage reservation,
IMarginClearinghouse.ReservationStatus terminalStatus
) internal;
_advanceReservationHead
function _advanceReservationHead(
bytes32 accountId
) internal;
_consumeLockedMargin
function _consumeLockedMargin(
bytes32 accountId,
IMarginClearinghouse.MarginBucket bucket,
uint256 amountUsdc
) internal;
_bucketStorage
function _bucketStorage(
IMarginClearinghouse.MarginBucket bucket,
bytes32 accountId
) internal view returns (uint256 bucketValue);
_setBucketStorage
function _setBucketStorage(
IMarginClearinghouse.MarginBucket bucket,
bytes32 accountId,
uint256 amountUsdc
) internal;
_totalLockedMarginUsdc
function _totalLockedMarginUsdc(
bytes32 accountId
) internal view returns (uint256);
seizeUsdc
Transfers settlement USDC from an account to the calling operator.
The recipient must equal msg.sender, so operators can only pull seized funds into their own contract/account and must forward them explicitly afterward.
function seizeUsdc(
bytes32 accountId,
uint256 amount,
address recipient
) external onlyOperator;
Parameters
| Name | Type | Description |
|---|---|---|
accountId | bytes32 | Account to seize from |
amount | uint256 | USDC amount to seize |
recipient | address | Recipient of seized tokens (must equal msg.sender) |
seizePositionMarginUsdc
function seizePositionMarginUsdc(
bytes32 accountId,
uint256 amount,
address recipient
) external onlyOperator;
balanceUsdc
function balanceUsdc(
bytes32 accountId
) external view returns (uint256);
lockedMarginUsdc
function lockedMarginUsdc(
bytes32 accountId
) external view returns (uint256);
getLockedMarginBuckets
function getLockedMarginBuckets(
bytes32 accountId
) external view returns (IMarginClearinghouse.LockedMarginBuckets memory buckets);
getOrderReservation
function getOrderReservation(
uint64 orderId
) external view returns (IMarginClearinghouse.OrderReservation memory reservation);
getAccountReservationSummary
function getAccountReservationSummary(
bytes32 accountId
) external view returns (IMarginClearinghouse.AccountReservationSummary memory summary);
reservationHeadIndex
function reservationHeadIndex(
bytes32 accountId
) external view returns (uint256);
_toUint96
function _toUint96(
uint256 value
) internal pure returns (uint96);
Events
Deposit
event Deposit(bytes32 indexed accountId, address indexed asset, uint256 amount);
Withdraw
event Withdraw(bytes32 indexed accountId, address indexed asset, uint256 amount);
MarginLocked
event MarginLocked(bytes32 indexed accountId, IMarginClearinghouse.MarginBucket indexed bucket, uint256 amountUsdc);
MarginUnlocked
event MarginUnlocked(
bytes32 indexed accountId, IMarginClearinghouse.MarginBucket indexed bucket, uint256 amountUsdc
);
ReservationCreated
event ReservationCreated(
uint64 indexed orderId,
bytes32 indexed accountId,
IMarginClearinghouse.ReservationBucket indexed bucket,
uint256 amountUsdc
);
ReservationConsumed
event ReservationConsumed(
uint64 indexed orderId, bytes32 indexed accountId, uint256 amountUsdc, uint256 remainingAmountUsdc
);
ReservationReleased
event ReservationReleased(uint64 indexed orderId, bytes32 indexed accountId, uint256 amountUsdc);
AssetSeized
event AssetSeized(bytes32 indexed accountId, address indexed asset, uint256 amount, address recipient);
Errors
MarginClearinghouse__NotOperator
error MarginClearinghouse__NotOperator();
MarginClearinghouse__NotAccountOwner
error MarginClearinghouse__NotAccountOwner();
MarginClearinghouse__ZeroAmount
error MarginClearinghouse__ZeroAmount();
MarginClearinghouse__InsufficientBalance
error MarginClearinghouse__InsufficientBalance();
MarginClearinghouse__InsufficientFreeEquity
error MarginClearinghouse__InsufficientFreeEquity();
MarginClearinghouse__InsufficientUsdcForSettlement
error MarginClearinghouse__InsufficientUsdcForSettlement();
MarginClearinghouse__InsufficientAssetToSeize
error MarginClearinghouse__InsufficientAssetToSeize();
MarginClearinghouse__InvalidSeizeRecipient
error MarginClearinghouse__InvalidSeizeRecipient();
MarginClearinghouse__InvalidMarginBucket
error MarginClearinghouse__InvalidMarginBucket();
MarginClearinghouse__ReservationAlreadyExists
error MarginClearinghouse__ReservationAlreadyExists();
MarginClearinghouse__ReservationNotActive
error MarginClearinghouse__ReservationNotActive();
MarginClearinghouse__IncompleteReservationCoverage
error MarginClearinghouse__IncompleteReservationCoverage();
MarginClearinghouse__EngineAlreadySet
error MarginClearinghouse__EngineAlreadySet();
MarginClearinghouse__ZeroAddress
error MarginClearinghouse__ZeroAddress();
MarginClearinghouse__InsufficientBucketMargin
error MarginClearinghouse__InsufficientBucketMargin();
MarginClearinghouse__AmountOverflow
error MarginClearinghouse__AmountOverflow();
OrderRouter
Inherits: Ownable2Step, Pausable, OrderEscrowAccounting
Title: OrderRouter (The MEV Shield)
Manages Commit-Reveal, MEV protection, and the un-brickable FIFO queue.
Holds only non-trader-owned keeper execution reserves. Trader collateral remains in MarginClearinghouse.
Note: security-contact: contact@plether.com
Constants
PANIC_SELECTOR
bytes4 internal constant PANIC_SELECTOR = 0x4e487b71
TYPED_ORDER_FAILURE_SELECTOR
bytes4 internal constant TYPED_ORDER_FAILURE_SELECTOR = ICfdEngine.CfdEngine__TypedOrderFailure.selector
MARK_PRICE_OUT_OF_ORDER_SELECTOR
bytes4 internal constant MARK_PRICE_OUT_OF_ORDER_SELECTOR = ICfdEngine.CfdEngine__MarkPriceOutOfOrder.selector
vault
ICfdVault internal immutable vault
TIMELOCK_DELAY
uint256 public constant TIMELOCK_DELAY = 48 hours
MIN_ENGINE_GAS
uint256 internal constant MIN_ENGINE_GAS = 600_000
MIN_MEV_PUBLISH_DELAY
uint256 internal constant MIN_MEV_PUBLISH_DELAY = 5
DEFAULT_MAX_ORDER_AGE
uint256 internal constant DEFAULT_MAX_ORDER_AGE = 60
MAX_EXPIRED_ORDER_SKIPS_PER_CALL
uint256 internal constant MAX_EXPIRED_ORDER_SKIPS_PER_CALL = 32
MAX_BATCH_ORDER_SCANS
uint256 internal constant MAX_BATCH_ORDER_SCANS = 64
MAX_PRUNE_ORDERS_PER_CALL
uint256 internal constant MAX_PRUNE_ORDERS_PER_CALL = 64
RETRYABLE_SKIP_COOLDOWN
uint64 internal constant RETRYABLE_SKIP_COOLDOWN = 5
OPEN_ORDER_EXECUTION_BOUNTY_BPS
uint256 internal constant OPEN_ORDER_EXECUTION_BOUNTY_BPS = 1
MIN_OPEN_ORDER_EXECUTION_BOUNTY_USDC
uint256 internal constant MIN_OPEN_ORDER_EXECUTION_BOUNTY_USDC = 50_000
MAX_OPEN_ORDER_EXECUTION_BOUNTY_USDC
uint256 internal constant MAX_OPEN_ORDER_EXECUTION_BOUNTY_USDC = DecimalConstants.ONE_USDC
CLOSE_ORDER_EXECUTION_BOUNTY_USDC
uint256 internal constant CLOSE_ORDER_EXECUTION_BOUNTY_USDC = DecimalConstants.ONE_USDC
MAX_PENDING_ORDERS
uint256 public constant MAX_PENDING_ORDERS = 5
State Variables
pyth
IPyth public pyth
pythFeedIds
bytes32[] public pythFeedIds
quantities
uint256[] public quantities
basePrices
uint256[] public basePrices
inversions
bool[] public inversions
nextCommitId
uint64 public nextCommitId = 1
nextExecuteId
uint64 public nextExecuteId = 1
maxOrderAge
uint256 public maxOrderAge
pendingMaxOrderAge
uint256 public pendingMaxOrderAge
maxOrderAgeActivationTime
uint256 public maxOrderAgeActivationTime
orderExecutionStalenessLimit
uint256 public orderExecutionStalenessLimit = 60
pendingOrderExecutionStalenessLimit
uint256 public pendingOrderExecutionStalenessLimit
orderExecutionStalenessActivationTime
uint256 public orderExecutionStalenessActivationTime
liquidationStalenessLimit
uint256 public liquidationStalenessLimit = 15
pendingLiquidationStalenessLimit
uint256 public pendingLiquidationStalenessLimit
liquidationStalenessActivationTime
uint256 public liquidationStalenessActivationTime
claimableEth
mapping(address => uint256) public claimableEth
pendingHeadOrderId
mapping(bytes32 => uint64) public pendingHeadOrderId
pendingTailOrderId
mapping(bytes32 => uint64) public pendingTailOrderId
globalTailOrderId
uint64 public globalTailOrderId
Functions
onlyEngine
modifier onlyEngine() ;
constructor
constructor(
address _engine,
address _vault,
address _pyth,
bytes32[] memory _feedIds,
uint256[] memory _quantities,
uint256[] memory _basePrices,
bool[] memory _inversions
) Ownable(msg.sender) OrderEscrowAccounting(_engine);
Parameters
| Name | Type | Description |
|---|---|---|
_engine | address | CfdEngine that processes trades and liquidations |
_vault | address | CfdVault used for vault depth queries and liquidation bounty payouts |
_pyth | address | Pyth oracle contract (address(0) enables mock mode on Anvil) |
_feedIds | bytes32[] | Pyth price feed IDs for each basket component |
_quantities | uint256[] | Weight of each component (must sum to 1e18) |
_basePrices | uint256[] | Base price per component for normalization (8 decimals) |
_inversions | bool[] | Whether to invert each feed (e.g. USD/JPY -> JPY/USD) |
proposeMaxOrderAge
Proposes a new maxOrderAge value, subject to 48h timelock.
function proposeMaxOrderAge(
uint256 _maxOrderAge
) external onlyOwner;
finalizeMaxOrderAge
Finalizes the pending maxOrderAge after timelock expires.
function finalizeMaxOrderAge() external onlyOwner;
cancelMaxOrderAgeProposal
Cancels the pending maxOrderAge proposal.
function cancelMaxOrderAgeProposal() external onlyOwner;
proposeOrderExecutionStalenessLimit
function proposeOrderExecutionStalenessLimit(
uint256 limit
) external onlyOwner;
finalizeOrderExecutionStalenessLimit
function finalizeOrderExecutionStalenessLimit() external onlyOwner;
cancelOrderExecutionStalenessLimitProposal
function cancelOrderExecutionStalenessLimitProposal() external onlyOwner;
proposeLiquidationStalenessLimit
function proposeLiquidationStalenessLimit(
uint256 limit
) external onlyOwner;
finalizeLiquidationStalenessLimit
function finalizeLiquidationStalenessLimit() external onlyOwner;
cancelLiquidationStalenessLimitProposal
function cancelLiquidationStalenessLimitProposal() external onlyOwner;
pause
function pause() external onlyOwner;
unpause
function unpause() external onlyOwner;
commitOrder
Submits a trade intent to the FIFO queue. Margin and the order’s execution bounty are reserved immediately.
function commitOrder(
CfdTypes.Side side,
uint256 sizeDelta,
uint256 marginDelta,
uint256 targetPrice,
bool isClose
) external;
Parameters
| Name | Type | Description |
|---|---|---|
side | CfdTypes.Side | BULL or BEAR |
sizeDelta | uint256 | Position size change (18 decimals) |
marginDelta | uint256 | Margin to add or remove (6 decimals, USDC) |
targetPrice | uint256 | Slippage limit price (8 decimals, 0 = market order) |
isClose | bool | True to allow execution even when paused or in FAD close-only mode |
quoteOpenOrderExecutionBountyUsdc
Quotes the reserved USDC execution bounty for a new open order using the latest engine mark price.
Falls back to 1.00 USD if the engine has not observed a mark yet. Result is floored at 0.05 USDC and capped at 1 USDC for non-close intents.
function quoteOpenOrderExecutionBountyUsdc(
uint256 sizeDelta
) external view returns (uint256 executionBountyUsdc);
Parameters
| Name | Type | Description |
|---|---|---|
sizeDelta | uint256 | Order size in 18-decimal notional units |
Returns
| Name | Type | Description |
|---|---|---|
executionBountyUsdc | uint256 | Reserved execution bounty in 6-decimal USDC units |
quoteCloseOrderExecutionBountyUsdc
Quotes the flat reserved USDC execution bounty for a close order.
function quoteCloseOrderExecutionBountyUsdc() external pure returns (uint256 executionBountyUsdc);
orders
function orders(
uint64 orderId
) external view returns (bytes32, uint256, uint256, uint256, uint64, uint64, uint64, CfdTypes.Side, bool);
committedMargins
function committedMargins(
uint64 orderId
) external view returns (uint256);
executionBountyReserves
function executionBountyReserves(
uint64 orderId
) external view returns (uint256);
isInMarginQueue
function isInMarginQueue(
uint64 orderId
) external view returns (bool);
getOrderRecord
function getOrderRecord(
uint64 orderId
) external view returns (OrderRecord memory);
syncMarginQueue
Returns the total queued escrow state for an account across all pending orders.
function syncMarginQueue(
bytes32 accountId
) external onlyEngine;
getPendingOrdersForAccount
function getPendingOrdersForAccount(
bytes32 accountId
) external view returns (IOrderRouterAccounting.PendingOrderView[] memory pending);
executeOrder
Keeper executes the current global queue head. Validates oracle freshness and publish-time ordering against the order commit, checks slippage, then delegates to CfdEngine. Terminal invalid/expired orders pay from router-custodied execution bounty, while retryable slippage misses are requeued to the global tail with cooldown so keepers cannot burn out-of-market intents or pin the FIFO head.
function executeOrder(
uint64 orderId,
bytes[] calldata pythUpdateData
) external payable;
Parameters
| Name | Type | Description |
|---|---|---|
orderId | uint64 | Must equal the current global queue head (expired orders are auto-skipped) |
pythUpdateData | bytes[] | Pyth price update blobs; attach ETH to cover the Pyth fee |
executeOrderBatch
Executes queued pending orders against a single Pyth price tick. Updates Pyth once, then loops through the FIFO queue. Aggregates reserved USDC execution bounties across processed orders and refunds excess ETH in a single transfer.
function executeOrderBatch(
uint64 maxOrderId,
bytes[] calldata pythUpdateData
) external payable;
Parameters
| Name | Type | Description |
|---|---|---|
maxOrderId | uint64 | Inclusive upper bound on committed order ids the batch may begin processing from |
pythUpdateData | bytes[] | Pyth price update blobs; attach ETH to cover the Pyth fee |
_skipStaleOrders
function _skipStaleOrders(
uint64 upToId,
uint256 maxSkips
) internal returns (uint256 skipped);
_currentRouterExecutionContext
function _currentRouterExecutionContext() internal view returns (RouterExecutionContext memory context);
_routedCloseOnlyFailure
function _routedCloseOnlyFailure(
CfdTypes.Order memory order,
bool oracleFrozen,
bool isFadWindow,
bool degradedMode,
bool closeOnly
) internal pure returns (OrderFailurePolicyLib.FailureContext memory failure);
_processTypedOrderExecution
function _processTypedOrderExecution(
CfdTypes.Order memory order,
uint256 executionPrice,
uint256 vaultDepth,
uint64 oraclePublishTime,
bool oracleFrozen,
bool isFadWindow,
bool degradedMode
)
internal
returns (
bool success,
OrderFailReason failureReason,
OrderFailurePolicyLib.FailedOrderBountyPolicy failureBountyPolicy
);
pruneExpiredOrders
function pruneExpiredOrders(
uint64 upToId,
uint256 maxPrunes
) external;
_resolveOraclePrice
function _resolveOraclePrice(
bytes[] calldata pythUpdateData,
uint256 mockFallbackPrice
) internal returns (uint256 price, uint64 publishTime, uint256 pythFee);
_sendEth
function _sendEth(
address to,
uint256 amount
) internal;
_pendingOrder
function _pendingOrder(
uint64 orderId
) internal view returns (OrderRecord storage record, CfdTypes.Order memory order);
_getQueuedPositionView
function _getQueuedPositionView(
bytes32 accountId
) internal view returns (QueuedPositionView memory queuedPosition);
_failedOrderBountyPolicy
function _failedOrderBountyPolicy(
OrderFailurePolicyLib.FailureContext memory context
) internal pure returns (OrderFailurePolicyLib.FailedOrderBountyPolicy);
_decodeTypedOrderFailure
function _decodeTypedOrderFailure(
bytes memory revertData
)
internal
pure
returns (CfdEnginePlanTypes.ExecutionFailurePolicyCategory failureCategory, uint8 failureCode, bool isClose);
_cleanupOrder
function _cleanupOrder(
uint64 orderId,
bool success,
OrderFailurePolicyLib.FailedOrderBountyPolicy failedPolicy
) internal returns (uint256 executionBountyUsdc);
_finalizeExecution
function _finalizeExecution(
uint64 orderId,
uint256 pythFee,
bool success,
OrderFailurePolicyLib.FailedOrderBountyPolicy failedPolicy
) internal;
_routedExpiryFailure
function _routedExpiryFailure(
CfdTypes.Order memory order
) internal view returns (OrderFailurePolicyLib.FailureContext memory context);
_routedRouterPolicyFailure
function _routedRouterPolicyFailure(
CfdTypes.Order memory order,
OrderFailurePolicyLib.RouterFailureCode failureCode,
bool oracleFrozen,
bool isFad,
bool degradedMode,
bool closeOnly
) internal pure returns (OrderFailurePolicyLib.FailureContext memory context);
_routedFailureFromEngineRevert
function _routedFailureFromEngineRevert(
CfdTypes.Order memory order,
bytes memory revertData,
bool oracleFrozen,
bool isFad,
bool degradedMode
) internal pure returns (OrderFailurePolicyLib.FailureContext memory context);
_payOrDeferLiquidationBounty
Immediate liquidation bounties still pay directly to the executing keeper wallet.
If immediate payment is unavailable or direct transfer fails, the bounty is deferred and later
settles as clearinghouse credit via claimDeferredClearerBounty().
function _payOrDeferLiquidationBounty(
uint256 liquidationBountyUsdc
) internal;
_forfeitEscrowedOrderBountiesOnLiquidation
function _forfeitEscrowedOrderBountiesOnLiquidation(
bytes32 accountId
) internal;
_clearLiquidatedAccountOrders
function _clearLiquidatedAccountOrders(
bytes32 accountId
) internal;
_quoteOpenOrderExecutionBountyUsdc
function _quoteOpenOrderExecutionBountyUsdc(
uint256 sizeDelta,
uint256 price
) internal pure returns (uint256);
_quoteCloseOrderExecutionBountyUsdc
function _quoteCloseOrderExecutionBountyUsdc() internal pure returns (uint256);
_commitReferencePrice
function _commitReferencePrice() internal view returns (uint256 price);
_canUseCommitMarkForOpenPrefilter
function _canUseCommitMarkForOpenPrefilter() internal view returns (bool);
_linkPendingOrder
function _linkPendingOrder(
bytes32 accountId,
uint64 orderId
) internal;
_linkGlobalOrder
function _linkGlobalOrder(
uint64 orderId
) internal;
_unlinkGlobalOrder
function _unlinkGlobalOrder(
uint64 orderId
) internal;
_skipRetryableOrder
function _skipRetryableOrder(
uint64 orderId,
OrderFailReason reason
) internal;
_unlinkPendingOrder
function _unlinkPendingOrder(
bytes32 accountId,
uint64 orderId
) internal;
_reserveCloseExecutionBounty
function _reserveCloseExecutionBounty(
bytes32 accountId,
uint256 executionBountyUsdc
) internal override;
_deleteOrder
function _deleteOrder(
uint64 orderId,
bool advanceHead,
IOrderRouterAccounting.OrderStatus terminalStatus
) internal;
_releaseCommittedMarginForExecution
function _releaseCommittedMarginForExecution(
uint64 orderId
) internal;
claimEth
Claims ETH stuck from failed refund transfers.
function claimEth() external;
claimUsdc
Claims USDC bounty refunds that could not be pushed during failed-order cleanup.
function claimUsdc() external;
_computeBasketPrice
function _computeBasketPrice() internal view returns (uint256 basketPrice, uint256 minPublishTime);
_checkSlippage
Opens vs closes have opposing slippage directions: BULL open wants HIGH entry (more room for drop) → exec >= target BULL close wants LOW exit (lock in profit) → exec <= target BEAR open wants LOW entry (more room for rise) → exec <= target BEAR close wants HIGH exit (lock in profit) → exec >= target targetPrice == 0 disables the check (market order).
function _checkSlippage(
CfdTypes.Order memory order,
uint256 executionPrice
) internal pure returns (bool);
_isOracleFrozen
Returns true only when FX markets are actually closed and Pyth feeds have stopped publishing. Distinct from isFadWindow() which starts 3 hours earlier for margin purposes. Uses Friday 22:00 UTC (conservative vs 21:00 EDT summer) to guarantee zero latency arbitrage.
function _isOracleFrozen() internal view returns (bool);
_isCloseOnlyWindow
function _isCloseOnlyWindow() internal view returns (bool);
_invertPythPrice
Inverts a Pyth price (e.g. USD/JPY → JPY/USD) and returns 8-decimal output. Formula: 10^(8 - expo) / price
function _invertPythPrice(
int64 price,
int32 expo
) internal pure returns (uint256);
_normalizePythPrice
Converts a Pyth price to 8-decimal format. Scales up/down based on exponent difference from -8.
function _normalizePythPrice(
int64 price,
int32 expo
) internal pure returns (uint256);
updateMarkPrice
Push a fresh mark price to the engine without processing an order. Required before LP deposits/withdrawals when mark is stale.
function updateMarkPrice(
bytes[] calldata pythUpdateData
) external payable;
Parameters
| Name | Type | Description |
|---|---|---|
pythUpdateData | bytes[] | Pyth price update blobs; attach ETH to cover the Pyth fee |
executeLiquidation
Keeper-triggered liquidation using the canonical live-market staleness policy. Forfeits any queued-order execution escrow to the vault instead of crediting it back to trader settlement, then pays the liquidation keeper bounty in USDC directly from the vault.
function executeLiquidation(
bytes32 accountId,
bytes[] calldata pythUpdateData
) external payable;
Parameters
| Name | Type | Description |
|---|---|---|
accountId | bytes32 | The account to liquidate (bytes32-encoded address) |
pythUpdateData | bytes[] | Pyth price update blobs; attach ETH to cover the Pyth fee |
_pendingHeadOrderId
function _pendingHeadOrderId(
bytes32 accountId
) internal view override returns (uint64);
_revertInsufficientFreeEquity
function _revertInsufficientFreeEquity() internal pure override;
_revertMarginOrderLinkCorrupted
function _revertMarginOrderLinkCorrupted() internal pure override;
Events
OrderCommitted
event OrderCommitted(uint64 indexed orderId, bytes32 indexed accountId, CfdTypes.Side side);
OrderExecuted
event OrderExecuted(uint64 indexed orderId, uint256 executionPrice);
OrderFailed
event OrderFailed(uint64 indexed orderId, OrderFailReason reason);
OrderSkipped
event OrderSkipped(uint64 indexed orderId, OrderFailReason reason, uint64 retryAfterTimestamp);
Errors
OrderRouter__ZeroSize
error OrderRouter__ZeroSize();
OrderRouter__CloseMarginDeltaNotAllowed
error OrderRouter__CloseMarginDeltaNotAllowed();
OrderRouter__TimelockNotReady
error OrderRouter__TimelockNotReady();
OrderRouter__NoProposal
error OrderRouter__NoProposal();
OrderRouter__FIFOViolation
error OrderRouter__FIFOViolation();
OrderRouter__OrderNotPending
error OrderRouter__OrderNotPending();
OrderRouter__InsufficientPythFee
error OrderRouter__InsufficientPythFee();
OrderRouter__MockModeDisabled
error OrderRouter__MockModeDisabled();
OrderRouter__NoOrdersToExecute
error OrderRouter__NoOrdersToExecute();
OrderRouter__MaxOrderIdNotCommitted
error OrderRouter__MaxOrderIdNotCommitted();
OrderRouter__OraclePriceTooStale
error OrderRouter__OraclePriceTooStale();
OrderRouter__NothingToClaim
error OrderRouter__NothingToClaim();
OrderRouter__EthTransferFailed
error OrderRouter__EthTransferFailed();
OrderRouter__OraclePriceNegative
error OrderRouter__OraclePriceNegative();
OrderRouter__MevOraclePriceTooStale
error OrderRouter__MevOraclePriceTooStale();
OrderRouter__LengthMismatch
error OrderRouter__LengthMismatch();
OrderRouter__InvalidWeights
error OrderRouter__InvalidWeights();
OrderRouter__InvalidBasePrice
error OrderRouter__InvalidBasePrice();
OrderRouter__EmptyFeeds
error OrderRouter__EmptyFeeds();
OrderRouter__MevDetected
error OrderRouter__MevDetected();
OrderRouter__MissingPythUpdateData
error OrderRouter__MissingPythUpdateData();
OrderRouter__OracleFrozen
error OrderRouter__OracleFrozen();
OrderRouter__InsufficientGas
error OrderRouter__InsufficientGas();
OrderRouter__NoOpenPosition
error OrderRouter__NoOpenPosition();
OrderRouter__CloseSideMismatch
error OrderRouter__CloseSideMismatch();
OrderRouter__CloseSizeExceedsPosition
error OrderRouter__CloseSizeExceedsPosition();
OrderRouter__InsufficientFreeEquity
error OrderRouter__InsufficientFreeEquity();
OrderRouter__MarginOrderLinkCorrupted
error OrderRouter__MarginOrderLinkCorrupted();
OrderRouter__PendingOrderLinkCorrupted
error OrderRouter__PendingOrderLinkCorrupted();
OrderRouter__Unauthorized
error OrderRouter__Unauthorized();
OrderRouter__TooManyPendingOrders
error OrderRouter__TooManyPendingOrders();
OrderRouter__DegradedMode
error OrderRouter__DegradedMode();
OrderRouter__CloseOnlyMode
error OrderRouter__CloseOnlyMode();
OrderRouter__SeedLifecycleIncomplete
error OrderRouter__SeedLifecycleIncomplete();
OrderRouter__TradingNotActive
error OrderRouter__TradingNotActive();
OrderRouter__RetryCooldownActive
error OrderRouter__RetryCooldownActive();
OrderRouter__OraclePublishTimeOutOfOrder
error OrderRouter__OraclePublishTimeOutOfOrder();
OrderRouter__InvalidStalenessLimit
error OrderRouter__InvalidStalenessLimit();
OrderRouter__PredictableOpenInvalid
error OrderRouter__PredictableOpenInvalid(uint8 code);
Structs
QueuedPositionView
struct QueuedPositionView {
bool exists;
CfdTypes.Side side;
uint256 size;
}
RouterExecutionContext
struct RouterExecutionContext {
bool oracleFrozen;
bool isFadWindow;
bool degradedMode;
OrderOraclePolicyLib.OracleExecutionPolicy policy;
}
Enums
OrderFailReason
enum OrderFailReason {
Expired,
CloseOnlyOracleFrozen,
CloseOnlyFad,
SlippageExceeded,
EnginePanic,
AccountLiquidated,
EngineRevert
}
TrancheVault
Inherits: ERC4626
Title: TrancheVault
ERC4626 share token for a HousePool tranche (senior or junior). Routes all deposits/withdrawals through HousePool.
Note: security-contact: contact@plether.com
Constants
POOL
IHousePool public immutable POOL
IS_SENIOR
bool public immutable IS_SENIOR
DEPOSIT_COOLDOWN
uint256 public constant DEPOSIT_COOLDOWN = 1 hours
State Variables
lastDepositTime
mapping(address => uint256) public lastDepositTime
seedReceiver
address public seedReceiver
seedShareFloor
uint256 public seedShareFloor
Functions
constructor
constructor(
IERC20 _usdc,
address _pool,
bool _isSenior,
string memory _name,
string memory _symbol
) ERC4626(_usdc) ERC20(_name, _symbol);
Parameters
| Name | Type | Description |
|---|---|---|
_usdc | IERC20 | Underlying USDC token used as the vault asset |
_pool | address | HousePool that holds USDC and manages the tranche waterfall |
_isSenior | bool | True for the senior tranche, false for junior |
_name | string | ERC20 share token name |
_symbol | string | ERC20 share token symbol |
_decimalsOffset
Virtual share offset mitigates ERC4626 first-depositor inflation attack
function _decimalsOffset() internal pure override returns (uint8);
_update
Enforces a deposit cooldown on share transfers. Prevents flash-deposit-then-transfer to bypass the withdrawal cooldown. Propagates the sender’s cooldown to the receiver if it is more recent.
function _update(
address from,
address to,
uint256 amount
) internal override;
totalAssets
function totalAssets() public view override returns (uint256);
deposit
function deposit(
uint256 assets,
address receiver
) public override returns (uint256);
mint
function mint(
uint256 shares,
address receiver
) public override returns (uint256);
maxDeposit
function maxDeposit(
address receiver
) public view override returns (uint256);
maxMint
function maxMint(
address receiver
) public view override returns (uint256);
withdraw
function withdraw(
uint256 assets,
address receiver,
address _owner
) public override returns (uint256);
redeem
function redeem(
uint256 shares,
address receiver,
address _owner
) public override returns (uint256);
maxWithdraw
function maxWithdraw(
address _owner
) public view override returns (uint256);
maxRedeem
function maxRedeem(
address _owner
) public view override returns (uint256);
_deposit
function _deposit(
address caller,
address receiver,
uint256 assets,
uint256 shares
) internal override;
_withdraw
function _withdraw(
address caller,
address receiver,
address _owner,
uint256 assets,
uint256 shares
) internal override;
bootstrapMint
Mints shares to explicitly bootstrap previously quarantined pool assets into this tranche.
Only the pool may call this. The pool must have already assigned matching assets to the tranche principal.
function bootstrapMint(
uint256 shares,
address receiver
) external;
configureSeedPosition
Registers or increases the permanent seed-share floor for this tranche.
The pool must mint the corresponding shares before or within the same flow.
function configureSeedPosition(
address receiver,
uint256 floorShares
) external;
_unlockedOwnerShares
function _unlockedOwnerShares(
address _owner
) internal view returns (uint256 ownerShares);
_isTerminallyWiped
function _isTerminallyWiped() internal view returns (bool);
_requireActiveTranche
function _requireActiveTranche() internal view;
_requireLifecycleActiveForOrdinaryDeposit
function _requireLifecycleActiveForOrdinaryDeposit() internal view;
_ordinaryDepositsAllowed
function _ordinaryDepositsAllowed() internal view returns (bool);
_canDepositNow
function _canDepositNow() internal view returns (bool);
Errors
TrancheVault__DepositCooldown
error TrancheVault__DepositCooldown();
TrancheVault__TransferDuringCooldown
error TrancheVault__TransferDuringCooldown();
TrancheVault__TrancheImpaired
error TrancheVault__TrancheImpaired();
TrancheVault__ThirdPartyDepositForExistingHolder
error TrancheVault__ThirdPartyDepositForExistingHolder();
TrancheVault__NotPool
error TrancheVault__NotPool();
TrancheVault__SeedFloorBreached
error TrancheVault__SeedFloorBreached();
TrancheVault__InvalidSeedPosition
error TrancheVault__InvalidSeedPosition();
TrancheVault__TerminallyWiped
error TrancheVault__TerminallyWiped();
TrancheVault__TradingNotActive
error TrancheVault__TradingNotActive();
BullLeverageRouter
Inherits: LeverageRouterBase
Title: BullLeverageRouter
Leverage router for plDXY-BULL positions via Morpho Blue.
Uses Morpho flash loans + Splitter minting to acquire plDXY-BULL, then deposits as Morpho collateral. Close operation uses a single plDXY-BEAR flash mint for simplicity and gas efficiency. Uses Morpho’s fee-free flash loans for capital efficiency.
STATE MACHINE - OPEN LEVERAGE: ┌─────────────────────────────────────────────────────────────────────────┐ │ openLeverage(principal, leverage) │ │ 1. Pull USDC from user │ │ 2. Flash loan additional USDC from Morpho (fee-free) │ │ └──► onMorphoFlashLoan(OP_OPEN) │ │ └──► _executeOpen() │ │ 1. Mint plDXY-BEAR + plDXY-BULL pairs via Splitter │ │ 2. Sell plDXY-BEAR on Curve → USDC │ │ 3. Stake plDXY-BULL → splDXY-BULL │ │ 4. Deposit splDXY-BULL to Morpho (user’s collateral) │ │ 5. Borrow USDC from Morpho to cover flash repayment │ │ 6. Emit LeverageOpened event │ └─────────────────────────────────────────────────────────────────────────┘
STATE MACHINE - CLOSE LEVERAGE (Single Flash Mint): ┌─────────────────────────────────────────────────────────────────────────┐ │ closeLeverage(debtToRepay, collateralToWithdraw) │ │ 1. Flash mint plDXY-BEAR (collateral + extra for debt repayment) │ │ └──► onFlashLoan(OP_CLOSE) │ │ └──► _executeClose() │ │ 1. Sell extra plDXY-BEAR on Curve → USDC │ │ 2. Repay user’s Morpho debt with USDC from sale │ │ 3. Withdraw user’s splDXY-BULL from Morpho │ │ 4. Unstake splDXY-BULL → plDXY-BULL │ │ 5. Redeem plDXY-BEAR + plDXY-BULL → USDC │ │ 6. Buy plDXY-BEAR on Curve to repay flash mint │ │ 7. Transfer remaining USDC to user │ │ 8. Emit LeverageClosed event │ └─────────────────────────────────────────────────────────────────────────┘
STATE MACHINE - ADD COLLATERAL (Morpho Flash Loan): ┌─────────────────────────────────────────────────────────────────────────┐ │ addCollateral(usdcAmount) │ │ 1. Pull USDC from user │ │ 2. Flash loan USDC from Morpho (F = U × bearPrice / bullPrice) │ │ └──► onMorphoFlashLoan(OP_ADD_COLLATERAL) │ │ └──► _executeAddCollateral() │ │ 1. Mint plDXY-BEAR + plDXY-BULL pairs via Splitter │ │ 2. Sell ALL plDXY-BEAR on Curve → USDC │ │ 3. Stake plDXY-BULL → splDXY-BULL │ │ 4. Deposit splDXY-BULL to Morpho (user’s collateral) │ │ 5. Repay flash loan from BEAR sale proceeds │ │ 6. Emit CollateralAdded event │ └─────────────────────────────────────────────────────────────────────────┘
Note: security-contact: contact@plether.com
Constants
SPLITTER
SyntheticSplitter for minting/burning token pairs.
ISyntheticSplitter public immutable SPLITTER
PLDXY_BULL
plDXY-BULL token (collateral for bull positions).
IERC20 public immutable PLDXY_BULL
STAKED_PLDXY_BULL
StakedToken vault for plDXY-BULL (used as Morpho collateral).
IERC4626 public immutable STAKED_PLDXY_BULL
CAP
Protocol CAP price (8 decimals, oracle format).
uint256 public immutable CAP
ORACLE
Oracle for plDXY basket price (returns BEAR price in 8 decimals).
AggregatorV3Interface public immutable ORACLE
SEQUENCER_UPTIME_FEED
Chainlink L2 sequencer uptime feed (address(0) on L1).
AggregatorV3Interface public immutable SEQUENCER_UPTIME_FEED
ORACLE_TIMEOUT
Maximum age for a valid oracle price.
uint256 public constant ORACLE_TIMEOUT = 24 hours
SEQUENCER_GRACE_PERIOD
Grace period after L2 sequencer restarts before accepting prices.
uint256 public constant SEQUENCER_GRACE_PERIOD = 1 hours
OP_REMOVE_COLLATERAL
Operation type: remove collateral (flash mint).
uint8 internal constant OP_REMOVE_COLLATERAL = 3
OP_ADD_COLLATERAL
Operation type: add collateral (Morpho flash loan).
uint8 internal constant OP_ADD_COLLATERAL = 4
Functions
constructor
Deploys BullLeverageRouter with Morpho market configuration.
constructor(
address _morpho,
address _splitter,
address _curvePool,
address _usdc,
address _plDxyBear,
address _plDxyBull,
address _stakedPlDxyBull,
MarketParams memory _marketParams,
address _sequencerUptimeFeed
) LeverageRouterBase(_morpho, _curvePool, _usdc, _plDxyBear);
Parameters
| Name | Type | Description |
|---|---|---|
_morpho | address | Morpho Blue protocol address. |
_splitter | address | SyntheticSplitter contract address. |
_curvePool | address | Curve USDC/plDXY-BEAR pool address. |
_usdc | address | USDC token address. |
_plDxyBear | address | plDXY-BEAR token address. |
_plDxyBull | address | plDXY-BULL token address. |
_stakedPlDxyBull | address | splDXY-BULL staking vault address. |
_marketParams | MarketParams | Morpho market parameters for splDXY-BULL/USDC. |
_sequencerUptimeFeed | address | Chainlink L2 sequencer feed (address(0) on L1/testnet). |
openLeverage
Open a Leveraged plDXY-BULL Position in one transaction.
Mints pairs via Splitter, sells plDXY-BEAR on Curve, deposits plDXY-BULL to Morpho. Uses fixed debt model (same as BEAR router) - Morpho debt equals principal * (leverage - 1).
function openLeverage(
uint256 principal,
uint256 leverage,
uint256 maxSlippageBps,
uint256 minAmountOut,
uint256 deadline
) external nonReentrant whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
principal | uint256 | Amount of USDC user sends. |
leverage | uint256 | Multiplier (e.g. 3x = 3e18). |
maxSlippageBps | uint256 | Maximum slippage tolerance in basis points (e.g., 50 = 0.5%). Capped at MAX_SLIPPAGE_BPS (1%) to limit MEV extraction. |
minAmountOut | uint256 | |
deadline | uint256 | Unix timestamp after which the transaction reverts. |
openLeverageWithPermit
Open a leveraged plDXY-BULL position with a USDC permit signature (gasless approval).
function openLeverageWithPermit(
uint256 principal,
uint256 leverage,
uint256 maxSlippageBps,
uint256 minAmountOut,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external nonReentrant whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
principal | uint256 | Amount of USDC user sends. |
leverage | uint256 | Multiplier (e.g. 3x = 3e18). |
maxSlippageBps | uint256 | Maximum slippage tolerance in basis points. |
minAmountOut | uint256 | |
deadline | uint256 | Unix timestamp after which the permit and transaction revert. |
v | uint8 | Signature recovery byte. |
r | bytes32 | Signature r component. |
s | bytes32 | Signature s component. |
_openLeverageCore
function _openLeverageCore(
uint256 principal,
uint256 leverage,
uint256 maxSlippageBps,
uint256 minAmountOut,
uint256 deadline
) internal;
closeLeverage
Close a Leveraged plDXY-BULL Position in one transaction.
Uses a single plDXY-BEAR flash mint to unwind positions efficiently. Queries actual debt from Morpho to ensure full repayment even if interest accrued.
function closeLeverage(
uint256 collateralToWithdraw,
uint256 maxSlippageBps,
uint256 minAmountOut,
uint256 deadline
) external nonReentrant whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
collateralToWithdraw | uint256 | Amount of splDXY-BULL shares to withdraw from Morpho. NOTE: This is staked token shares, not underlying plDXY-BULL amount. Use STAKED_PLDXY_BULL.previewRedeem() to convert shares to underlying. |
maxSlippageBps | uint256 | Maximum slippage tolerance in basis points. Capped at MAX_SLIPPAGE_BPS (1%) to limit MEV extraction. |
minAmountOut | uint256 | |
deadline | uint256 | Unix timestamp after which the transaction reverts. |
addCollateral
Add collateral to an existing leveraged position.
Uses Morpho flash loan so user’s USDC input ≈ collateral value added. Flow: Flash loan USDC → Mint pairs → Sell ALL BEAR to repay flash loan → Keep BULL as collateral. Formula: flashLoan = userUSDC × bearPrice / bullPrice
function addCollateral(
uint256 usdcAmount,
uint256 maxSlippageBps,
uint256 minAmountOut,
uint256 deadline
) external nonReentrant whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
usdcAmount | uint256 | Amount of USDC representing desired collateral value. |
maxSlippageBps | uint256 | Maximum slippage tolerance in basis points. Capped at MAX_SLIPPAGE_BPS (1%) to limit MEV extraction. |
minAmountOut | uint256 | |
deadline | uint256 | Unix timestamp after which the transaction reverts. |
addCollateralWithPermit
Add collateral with a USDC permit signature (gasless approval).
function addCollateralWithPermit(
uint256 usdcAmount,
uint256 maxSlippageBps,
uint256 minAmountOut,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external nonReentrant whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
usdcAmount | uint256 | Amount of USDC representing desired collateral value. |
maxSlippageBps | uint256 | Maximum slippage tolerance in basis points. |
minAmountOut | uint256 | |
deadline | uint256 | Unix timestamp after which the permit and transaction revert. |
v | uint8 | Signature recovery byte. |
r | bytes32 | Signature r component. |
s | bytes32 | Signature s component. |
_addCollateralCore
function _addCollateralCore(
uint256 usdcAmount,
uint256 maxSlippageBps,
uint256 minAmountOut,
uint256 deadline
) internal;
removeCollateral
Remove collateral from an existing leveraged position.
Uses flash mint of BEAR to redeem pairs, then buys back BEAR with USDC. Reverts if the resulting position would be unhealthy.
function removeCollateral(
uint256 collateralToWithdraw,
uint256 maxSlippageBps,
uint256 minAmountOut,
uint256 deadline
) external nonReentrant whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
collateralToWithdraw | uint256 | Amount of splDXY-BULL shares to withdraw. NOTE: This is staked token shares, not underlying plDXY-BULL amount. |
maxSlippageBps | uint256 | Maximum slippage tolerance in basis points. Capped at MAX_SLIPPAGE_BPS (1%) to limit MEV extraction. |
minAmountOut | uint256 | |
deadline | uint256 | Unix timestamp after which the transaction reverts. |
onMorphoFlashLoan
Morpho flash loan callback for USDC flash loans (OP_OPEN, OP_ADD_COLLATERAL).
function onMorphoFlashLoan(
uint256 amount,
bytes calldata data
) external override;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | Amount of USDC borrowed. |
data | bytes | Encoded operation parameters. |
onFlashLoan
ERC-3156 flash loan callback for plDXY-BEAR flash mints.
function onFlashLoan(
address initiator,
address,
uint256 amount,
uint256 fee,
bytes calldata data
) external override returns (bytes32);
Parameters
| Name | Type | Description |
|---|---|---|
initiator | address | Address that initiated the flash loan (must be this contract). |
<none> | address | |
amount | uint256 | Amount of plDXY-BEAR borrowed. |
fee | uint256 | Flash loan fee (always 0 for SyntheticToken). |
data | bytes | Encoded operation parameters. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bytes32 | CALLBACK_SUCCESS on successful execution. |
_executeOpen
Executes open leverage operation within Morpho flash loan callback.
function _executeOpen(
uint256 loanAmount,
bytes calldata data
) private;
Parameters
| Name | Type | Description |
|---|---|---|
loanAmount | uint256 | Amount of USDC borrowed from Morpho. |
data | bytes | Encoded parameters (op, user, deadline, principal, leverage, targetDebt, maxSlippageBps, minSwapOut, minAmountOut). |
_executeAddCollateral
Executes add collateral operation within Morpho flash loan callback.
function _executeAddCollateral(
uint256 flashLoanAmount,
bytes calldata data
) private;
Parameters
| Name | Type | Description |
|---|---|---|
flashLoanAmount | uint256 | Amount of USDC flash loaned. |
data | bytes | Encoded parameters (op, user, deadline, usdcAmount, maxSlippageBps, minSwapOut, minAmountOut). |
_executeClose
Executes close leverage operation within plDXY-BEAR flash mint callback.
function _executeClose(
uint256 flashAmount,
uint256 flashFee,
bytes calldata data
) private;
Parameters
| Name | Type | Description |
|---|---|---|
flashAmount | uint256 | Amount of plDXY-BEAR flash minted. |
flashFee | uint256 | Flash mint fee (always 0). |
data | bytes | Encoded parameters (op, user, deadline, collateralToWithdraw, debtToRepay, borrowShares, extraBearForDebt, maxSlippageBps, minAmountOut). |
_executeRemoveCollateral
Executes remove collateral operation within plDXY-BEAR flash mint callback.
function _executeRemoveCollateral(
uint256 flashAmount,
uint256 flashFee,
bytes calldata data
) private;
Parameters
| Name | Type | Description |
|---|---|---|
flashAmount | uint256 | Amount of plDXY-BEAR flash minted. |
flashFee | uint256 | Flash mint fee (always 0). |
data | bytes | Encoded parameters (op, user, deadline, collateralToWithdraw, maxSlippageBps, minAmountOut). |
previewOpenLeverage
Preview the result of opening a leveraged plDXY-BULL position.
BULL leverage requires a larger flash loan than BEAR because minting happens at CAP price, not market price. The flash loan is repaid by: bearSaleProceeds + morphoBorrow. Morpho debt still follows the fixed model: principal * (leverage - 1).
function previewOpenLeverage(
uint256 principal,
uint256 leverage
) external view returns (uint256 loanAmount, uint256 totalUSDC, uint256 expectedPlDxyBull, uint256 expectedDebt);
Parameters
| Name | Type | Description |
|---|---|---|
principal | uint256 | Amount of USDC user will send. |
leverage | uint256 | Multiplier (e.g. 2x = 2e18). |
Returns
| Name | Type | Description |
|---|---|---|
loanAmount | uint256 | Amount of USDC to flash loan. |
totalUSDC | uint256 | Total USDC for minting pairs (principal + loan). |
expectedPlDxyBull | uint256 | Expected plDXY-BULL tokens received. |
expectedDebt | uint256 | Expected Morpho debt (fixed: principal * (leverage - 1)). |
previewCloseLeverage
Preview the result of closing a leveraged plDXY-BULL position.
function previewCloseLeverage(
uint256 debtToRepay,
uint256 collateralToWithdraw
) external view returns (uint256 expectedUSDC, uint256 usdcForBearBuyback, uint256 expectedReturn);
Parameters
| Name | Type | Description |
|---|---|---|
debtToRepay | uint256 | Amount of USDC debt to repay. |
collateralToWithdraw | uint256 | Amount of plDXY-BULL collateral to withdraw. |
Returns
| Name | Type | Description |
|---|---|---|
expectedUSDC | uint256 | Expected USDC from redeeming pairs. |
usdcForBearBuyback | uint256 | Expected USDC needed to buy back plDXY-BEAR. |
expectedReturn | uint256 | Expected USDC returned to user after all repayments. |
previewAddCollateral
Preview the result of adding collateral.
Uses flash loan so user’s USDC input ≈ collateral value added.
function previewAddCollateral(
uint256 usdcAmount
)
external
view
returns (uint256 flashLoanAmount, uint256 totalUSDC, uint256 expectedPlDxyBull, uint256 expectedStakedShares);
Parameters
| Name | Type | Description |
|---|---|---|
usdcAmount | uint256 | Amount of USDC representing desired collateral value. |
Returns
| Name | Type | Description |
|---|---|---|
flashLoanAmount | uint256 | Amount of USDC to flash loan. |
totalUSDC | uint256 | Total USDC for minting pairs. |
expectedPlDxyBull | uint256 | Expected plDXY-BULL tokens to receive. |
expectedStakedShares | uint256 | Expected splDXY-BULL shares to receive. |
previewRemoveCollateral
Preview the result of removing collateral.
function previewRemoveCollateral(
uint256 collateralToWithdraw
)
external
view
returns (
uint256 expectedPlDxyBull,
uint256 expectedUsdcFromBurn,
uint256 usdcForBearBuyback,
uint256 expectedReturn
);
Parameters
| Name | Type | Description |
|---|---|---|
collateralToWithdraw | uint256 | Amount of splDXY-BULL shares to withdraw. |
Returns
| Name | Type | Description |
|---|---|---|
expectedPlDxyBull | uint256 | Expected plDXY-BULL from unstaking. |
expectedUsdcFromBurn | uint256 | Expected USDC from burning pairs. |
usdcForBearBuyback | uint256 | Expected USDC needed to buy back flash-minted BEAR. |
expectedReturn | uint256 | Expected USDC returned to user. |
_calculateOpenParams
Calculates parameters for opening a leveraged position.
function _calculateOpenParams(
uint256 principal,
uint256 leverage
) private view returns (OpenParams memory params);
_getValidatedOraclePrice
Returns validated oracle prices with staleness, sequencer, and CAP checks.
function _getValidatedOraclePrice() private view returns (uint256 bearPrice, uint256 bullPrice);
_estimateBearForUsdcSale
Estimates BEAR needed to sell for a target USDC amount using binary search on Curve.
function _estimateBearForUsdcSale(
uint256 targetUsdc
) private view returns (uint256);
_estimateUsdcForBearBuyback
Estimates USDC needed to buy BEAR using binary search on Curve.
function _estimateUsdcForBearBuyback(
uint256 bearAmount
) private view returns (uint256);
_binarySearchCurve
Binary search for minimum input to Curve such that get_dy(i, j, input) >= targetOut.
function _binarySearchCurve(
uint256 i,
uint256 j,
uint256 oneUnit,
uint256 targetOut
) private view returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
i | uint256 | Curve pool input index. |
j | uint256 | Curve pool output index. |
oneUnit | uint256 | One unit of the input token (1e18 for BEAR, 1e6 for USDC). |
targetOut | uint256 | Desired minimum output from the swap. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Minimum input amount that produces at least targetOut. |
Events
LeverageOpened
Emitted when a leveraged plDXY-BULL position is opened.
event LeverageOpened(
address indexed user,
uint256 principal,
uint256 leverage,
uint256 loanAmount,
uint256 plDxyBullReceived,
uint256 debtIncurred,
uint256 maxSlippageBps
);
LeverageClosed
Emitted when a leveraged plDXY-BULL position is closed.
event LeverageClosed(
address indexed user,
uint256 debtRepaid,
uint256 collateralWithdrawn,
uint256 usdcReturned,
uint256 maxSlippageBps
);
CollateralAdded
Emitted when collateral is added to a position.
Net USDC cost = usdcAmount - usdcReturned (BEAR sale proceeds returned to user).
event CollateralAdded(
address indexed user, uint256 usdcAmount, uint256 usdcReturned, uint256 collateralAdded, uint256 maxSlippageBps
);
CollateralRemoved
Emitted when collateral is removed from a position.
event CollateralRemoved(
address indexed user, uint256 collateralWithdrawn, uint256 usdcReturned, uint256 maxSlippageBps
);
Structs
OpenParams
struct OpenParams {
uint256 targetDebt;
uint256 loanAmount;
uint256 tokensToMint;
uint256 expectedBearSale;
}
InvarCoin
Inherits: ERC20, ERC20Permit, Ownable2Step, Pausable, ReentrancyGuard
Title: InvarCoin (INVAR)
Global purchasing power token backed 50/50 by USDC + plDXY-BEAR via Curve LP.
INVAR is a vault token whose backing is held as Curve USDC/plDXY-BEAR LP tokens. Users deposit USDC, which is single-sided deployed to Curve. The vault earns Curve trading fee yield (virtual price growth), which is harvested and donated to sINVAR stakers. LP tokens are valued with dual pricing to prevent manipulation:
- totalAssets() and harvest use pessimistic pricing (min of EMA, oracle) for conservative NAV.
- totalAssetsValidated() provides strict oracle-validated NAV (reverts on stale/invalid oracle).
- deposit() uses optimistic NAV (max of EMA, oracle) so new depositors cannot dilute existing holders.
- lpDeposit() values minted LP pessimistically so depositors cannot extract value from stale-high EMA.
- withdraw() and lpWithdraw() use pro-rata asset distribution (no NAV pricing needed). The oracle-derived LP price mirrors the twocrypto-ng formula: 2 * virtualPrice * sqrt(bearPrice). A 2% USDC buffer (BUFFER_TARGET_BPS) is maintained locally for gas-efficient withdrawals. Excess USDC is deployed to Curve via permissionless keeper calls (deployToCurve). Virtual shares (1e18 INVAR / 1e6 USDC) protect against inflation attacks on the first deposit.
Note: security-contact: contact@plether.com
Constants
USDC
USDC collateral token (6 decimals).
IERC20 public immutable USDC
BEAR
plDXY-BEAR synthetic token (18 decimals).
IERC20 public immutable BEAR
CURVE_LP_TOKEN
Curve USDC/plDXY-BEAR LP token.
IERC20 public immutable CURVE_LP_TOKEN
CURVE_POOL
Curve twocrypto-ng pool for USDC/plDXY-BEAR.
ICurveTwocrypto public immutable CURVE_POOL
BASKET_ORACLE
Chainlink BasketOracle — weighted basket of 6 FX feeds, returns foreign currencies priced in USD (8 decimals).
AggregatorV3Interface public immutable BASKET_ORACLE
SEQUENCER_UPTIME_FEED
L2 sequencer uptime feed for staleness protection (address(0) on L1).
AggregatorV3Interface public immutable SEQUENCER_UPTIME_FEED
CRV_MINTER
Curve Minter for CRV emissions on L1 (address(0) on L2 where claim_rewards handles CRV).
ICurveMinter public immutable CRV_MINTER
BUFFER_TARGET_BPS
uint256 public constant BUFFER_TARGET_BPS = 200
DEPLOY_THRESHOLD
uint256 public constant DEPLOY_THRESHOLD = 1000e6
MAX_SPOT_DEVIATION_BPS
uint256 public constant MAX_SPOT_DEVIATION_BPS = 50
ORACLE_TIMEOUT
uint256 public constant ORACLE_TIMEOUT = 24 hours
SEQUENCER_GRACE_PERIOD
uint256 public constant SEQUENCER_GRACE_PERIOD = 1 hours
VIRTUAL_SHARES
uint256 public constant VIRTUAL_SHARES = 1e18
VIRTUAL_ASSETS
uint256 public constant VIRTUAL_ASSETS = 1e6
USDC_INDEX
uint256 private constant USDC_INDEX = 0
BPS
uint256 private constant BPS = 10_000
STAKED_INVAR_TIMELOCK
uint256 public constant STAKED_INVAR_TIMELOCK = 7 days
GAUGE_TIMELOCK
uint256 public constant GAUGE_TIMELOCK = 7 days
GAUGE_REWARDS_TIMELOCK
uint256 public constant GAUGE_REWARDS_TIMELOCK = 7 days
State Variables
stakedInvarCoin
sINVAR staking contract that receives harvested yield.
StakedToken public stakedInvarCoin
pendingStakedInvarCoin
address public pendingStakedInvarCoin
stakedInvarCoinActivationTime
uint256 public stakedInvarCoinActivationTime
curveLpCostVp
Cumulative virtual-price cost basis of tracked LP tokens (18 decimals).
Used to isolate fee yield (VP growth) from price appreciation. Only LP tokens deployed by the vault are tracked — donated LP is excluded to prevent yield manipulation.
uint256 public curveLpCostVp
trackedLpBalance
LP token balance deployed by the vault (excludes donated LP).
uint256 public trackedLpBalance
emergencyActive
True when emergency mode is active — blocks deposits and single-sided withdrawals.
bool public emergencyActive
curveGauge
ICurveGauge public curveGauge
gaugeStakedLp
Internally-tracked LP balance staked in the gauge (avoids external balanceOf dependency).
uint256 public gaugeStakedLp
pendingGauge
address public pendingGauge
gaugeActivationTime
uint256 public gaugeActivationTime
approvedGauges
mapping(address => bool) public approvedGauges
gaugeRewardsReceiver
address public gaugeRewardsReceiver
pendingGaugeRewardsReceiver
address public pendingGaugeRewardsReceiver
gaugeRewardsReceiverActivationTime
uint256 public gaugeRewardsReceiverActivationTime
protectedRewardTokens
mapping(address => bool) public protectedRewardTokens
Functions
constructor
constructor(
address _usdc,
address _bear,
address _curveLpToken,
address _curvePool,
address _oracle,
address _sequencerUptimeFeed,
address _crvMinter
) ERC20("InvarCoin", "INVAR") ERC20Permit("InvarCoin") Ownable(msg.sender);
Parameters
| Name | Type | Description |
|---|---|---|
_usdc | address | USDC token address. |
_bear | address | plDXY-BEAR token address. |
_curveLpToken | address | Curve USDC/plDXY-BEAR LP token address. |
_curvePool | address | Curve twocrypto-ng pool address. |
_oracle | address | BasketOracle address (Chainlink AggregatorV3Interface). |
_sequencerUptimeFeed | address | L2 sequencer uptime feed (address(0) on L1). |
_crvMinter | address | Curve Minter for CRV emissions (address(0) on L2). |
_validateStakingVault
function _validateStakingVault(
address stakingVault
) private view;
proposeStakedInvarCoin
Propose the sINVAR staking contract. Finalized after STAKED_INVAR_TIMELOCK.
function proposeStakedInvarCoin(
address _stakedInvarCoin
) external onlyOwner;
Parameters
| Name | Type | Description |
|---|---|---|
_stakedInvarCoin | address | Address of the StakedToken (sINVAR) contract. |
finalizeStakedInvarCoin
Finalize the proposed sINVAR staking contract after the timelock.
function finalizeStakedInvarCoin() external onlyOwner;
proposeGaugeRewardsReceiver
Propose the receiver for protected gauge reward tokens.
function proposeGaugeRewardsReceiver(
address receiver
) external onlyOwner;
finalizeGaugeRewardsReceiver
Finalize the protected reward receiver after the timelock.
function finalizeGaugeRewardsReceiver() external onlyOwner;
protectRewardToken
Irreversibly marks a token as a protected gauge reward token.
function protectRewardToken(
address token
) external onlyOwner;
sweepGaugeRewards
Sweeps a protected gauge reward token to the configured receiver.
function sweepGaugeRewards(
address token
) external onlyOwner;
totalAssets
Total assets backing INVAR (USDC, 6 decimals).
Uses pessimistic LP pricing: min(Curve EMA, oracle-derived) to prevent stale-EMA exploitation.
function totalAssets() public view returns (uint256);
totalAssetsValidated
Total assets backing INVAR with strict oracle validation (USDC, 6 decimals).
Reverts if sequencer/oracle checks fail (stale, invalid, or within sequencer grace period).
function totalAssetsValidated() external view returns (uint256);
_totalAssetsWithPrice
Total assets using pre-fetched LP balance and validated oracle price.
function _totalAssetsWithPrice(
uint256 lpBal,
uint256 oraclePrice
) private view returns (uint256);
getBufferMetrics
Buffer health metrics for keeper bots and frontends.
Uses permissive totalAssets() (best-effort oracle read). For strict oracle-validated NAV, use totalAssetsValidated().
function getBufferMetrics()
external
view
returns (uint256 currentBuffer, uint256 targetBuffer, uint256 deployable, uint256 replenishable);
Returns
| Name | Type | Description |
|---|---|---|
currentBuffer | uint256 | USDC held locally (6 decimals). |
targetBuffer | uint256 | Target USDC buffer based on total assets (6 decimals). |
deployable | uint256 | Excess USDC deployable to Curve (0 if below threshold). |
replenishable | uint256 | USDC deficit that needs replenishing from Curve LP. |
getHarvestableYield
Estimated harvestable Curve fee yield (USDC, 6 decimals).
Read-only estimator mirroring _harvest math. Uses permissive oracle reads and returns 0 if no staking contract or no VP growth.
function getHarvestableYield() external view returns (uint256 yieldUsdc);
getSpotDeviation
Spot-vs-EMA deviation for a 1 USDC deposit (basis points).
Returns 0 if spot >= EMA (no discount). Used by keepers to check if deploy/replenish is safe.
function getSpotDeviation() external view returns (uint256 deviationBps);
_totalAssetsOptimistic
Total assets using optimistic LP pricing — max(EMA, oracle) to prevent deposit dilution.
function _totalAssetsOptimistic(
uint256 oraclePrice
) private view returns (uint256);
_oracleLpPrice
Oracle-derived LP price: mirrors twocrypto-ng formula 2 * vp * sqrt(bearPrice_18dec).
function _oracleLpPrice(
uint256 oraclePrice
) private view returns (uint256);
_pessimisticLpPrice
function _pessimisticLpPrice(
uint256 oraclePrice
) private view returns (uint256);
_optimisticLpPrice
function _optimisticLpPrice(
uint256 oraclePrice
) private view returns (uint256);
_validatedOraclePrice
function _validatedOraclePrice() private view returns (uint256);
_lpBalance
Total LP held: local + gauge-staked (internally tracked to avoid external call dependency).
function _lpBalance() private view returns (uint256);
_ensureUnstakedLp
Unstakes LP from gauge if local balance is insufficient for the requested amount.
function _ensureUnstakedLp(
uint256 amount
) private;
_reduceLpAccounting
function _reduceLpAccounting(
uint256 lpReduced,
uint256 totalLp
) private;
_recordLpDeployment
function _recordLpDeployment(
uint256 lpMinted
) private;
_stakeLpToGauge
function _stakeLpToGauge(
uint256 amount
) private;
_validateGaugeAddress
function _validateGaugeAddress(
address gauge
) private view;
_validateGauge
function _validateGauge(
address gauge
) private view;
deposit
Deposit USDC to mint INVAR shares. USDC stays in the local buffer until deployed to Curve.
Uses optimistic LP pricing for NAV to prevent deposit dilution. Harvests yield before minting. Reverts while emergencyActive is true.
function deposit(
uint256 usdcAmount,
address receiver,
uint256 minSharesOut
) public nonReentrant whenNotPaused returns (uint256 glUsdMinted);
Parameters
| Name | Type | Description |
|---|---|---|
usdcAmount | uint256 | Amount of USDC to deposit (6 decimals). |
receiver | address | Address that receives the minted INVAR shares. |
minSharesOut | uint256 | Minimum INVAR shares to receive (0 = no minimum). |
Returns
| Name | Type | Description |
|---|---|---|
glUsdMinted | uint256 | Number of INVAR shares minted. |
depositWithPermit
Gasless deposit using ERC-2612 permit. Falls back to existing allowance if permit fails (e.g., front-run griefing) and the allowance is sufficient.
function depositWithPermit(
uint256 usdcAmount,
address receiver,
uint256 minSharesOut,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
usdcAmount | uint256 | Amount of USDC to deposit (6 decimals). |
receiver | address | Address that receives the minted INVAR shares. |
minSharesOut | uint256 | Minimum INVAR shares to receive (0 = no minimum). |
deadline | uint256 | Permit signature expiry timestamp. |
v | uint8 | ECDSA recovery byte. |
r | bytes32 | ECDSA r component. |
s | bytes32 | ECDSA s component. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Number of INVAR shares minted. |
withdraw
USDC-only withdrawal via pro-rata buffer + JIT Curve LP burn.
Burns the user’s pro-rata share of local USDC and Curve LP (single-sided to USDC). Does not distribute raw BEAR balances — if the contract holds material BEAR, use lpWithdraw() or set minUsdcOut to enforce fair value. Blocked during emergencyActive since single-sided LP exit may be unavailable.
function withdraw(
uint256 glUsdAmount,
address receiver,
uint256 minUsdcOut
) external nonReentrant whenNotPaused returns (uint256 usdcOut);
Parameters
| Name | Type | Description |
|---|---|---|
glUsdAmount | uint256 | Amount of INVAR shares to burn. |
receiver | address | Address that receives the withdrawn USDC. |
minUsdcOut | uint256 | Minimum USDC to receive (slippage protection). |
Returns
| Name | Type | Description |
|---|---|---|
usdcOut | uint256 | Total USDC returned (buffer + Curve LP proceeds). |
lpWithdraw
Balanced withdrawal: returns pro-rata USDC + BEAR from Curve LP (remove_liquidity).
Intentionally lacks whenNotPaused — serves as the emergency exit when the contract is paused. Emergency mode still honors pro-rata LP claims by redeeming any remaining LP before paying out.
function lpWithdraw(
uint256 glUsdAmount,
uint256 minUsdcOut,
uint256 minBearOut
) external nonReentrant returns (uint256 usdcReturned, uint256 bearReturned);
Parameters
| Name | Type | Description |
|---|---|---|
glUsdAmount | uint256 | Amount of INVAR shares to burn. |
minUsdcOut | uint256 | Minimum USDC to receive (slippage protection). |
minBearOut | uint256 | Minimum plDXY-BEAR to receive (slippage protection). |
Returns
| Name | Type | Description |
|---|---|---|
usdcReturned | uint256 | Total USDC returned. |
bearReturned | uint256 | Total plDXY-BEAR returned. |
_lpWithdraw
function _lpWithdraw(
uint256 glUsdAmount,
uint256 minUsdcOut,
uint256 minBearOut
) internal returns (uint256 usdcReturned, uint256 bearReturned);
lpDeposit
Direct LP deposit: provide USDC and/or plDXY-BEAR, deploy to Curve, mint INVAR.
Inverse of lpWithdraw. Curve slippage is borne by the depositor, not existing holders. Shares are priced using pessimistic LP valuation so the depositor cannot extract value from a stale-high EMA. Spot deviation is checked against EMA to block sandwich attacks. Reverts while emergencyActive is true.
function lpDeposit(
uint256 usdcAmount,
uint256 bearAmount,
address receiver,
uint256 minSharesOut
) external nonReentrant whenNotPaused returns (uint256 glUsdMinted);
Parameters
| Name | Type | Description |
|---|---|---|
usdcAmount | uint256 | Amount of USDC to deposit (6 decimals, can be 0 if bearAmount > 0). |
bearAmount | uint256 | Amount of plDXY-BEAR to deposit (18 decimals, can be 0 if usdcAmount > 0). |
receiver | address | Address that receives the minted INVAR shares. |
minSharesOut | uint256 | Minimum INVAR shares to receive (slippage protection). |
Returns
| Name | Type | Description |
|---|---|---|
glUsdMinted | uint256 | Number of INVAR shares minted. |
harvest
Permissionless keeper harvest for Curve LP fee yield.
Measures fee yield as virtual price growth above the cost basis (curveLpCostVp). Mints INVAR proportional to the USDC value of yield and donates it to sINVAR stakers. Only tracks VP growth on vault-deployed LP (trackedLpBalance), not donated LP. Reverts if no yield is available — use as a heartbeat signal for keepers.
function harvest() external nonReentrant whenNotPaused returns (uint256 donated);
Returns
| Name | Type | Description |
|---|---|---|
donated | uint256 | Amount of INVAR minted and donated to sINVAR. |
_harvestSafe
Harvest yield before withdrawals. Best-effort: skips when no yield is pending, Curve is down, or oracle is unavailable. Withdrawals must never be blocked by oracle outages.
function _harvestSafe() internal;
_harvest
Uses trackedLpBalance (not _lpBalance()) so donated LP cannot inflate harvestable yield. Divergence from actual LP is prevented by internal gaugeStakedLp tracking and forceRemoveGauge() which writes off stuck LP from both trackedLpBalance and curveLpCostVp.
function _harvest() internal returns (uint256 donated);
_mintAndDonate
function _mintAndDonate(
uint256 amount
) private;
_harvestWithPrice
function _harvestWithPrice(
uint256 lpBal,
uint256 currentVpValue,
uint256 oraclePrice
) private returns (uint256 donated);
donateUsdc
Accepts USDC donations from RewardDistributor, mints proportional INVAR, and donates to sINVAR.
function donateUsdc(
uint256 usdcAmount
) external nonReentrant whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
usdcAmount | uint256 | Amount of USDC to donate (6 decimals). |
deployToCurve
Permissionless keeper function: deploys excess USDC buffer into Curve as single-sided liquidity.
Maintains a 2% USDC buffer (BUFFER_TARGET_BPS). Only deploys if excess exceeds DEPLOY_THRESHOLD ($1000). Spot-vs-EMA deviation check (MAX_SPOT_DEVIATION_BPS = 0.5%) blocks deployment during pool manipulation.
function deployToCurve(
uint256 maxUsdc
) external nonReentrant whenNotPaused returns (uint256 lpMinted);
Parameters
| Name | Type | Description |
|---|---|---|
maxUsdc | uint256 | Cap on USDC to deploy (0 = no cap, deploy entire excess). |
Returns
| Name | Type | Description |
|---|---|---|
lpMinted | uint256 | Amount of Curve LP tokens minted. |
replenishBuffer
Permissionless keeper function: restores USDC buffer by burning Curve LP (single-sided to USDC).
Inverse of deployToCurve. Uses same spot-vs-EMA deviation check for sandwich protection. The maxLpToBurn parameter allows chunked replenishment when the full withdrawal would exceed the 0.5% spot deviation limit due to price impact.
function replenishBuffer(
uint256 maxLpToBurn
) external nonReentrant whenNotPaused returns (uint256 usdcRecovered);
Parameters
| Name | Type | Description |
|---|---|---|
maxLpToBurn | uint256 | Cap on LP tokens to burn (0 = no cap, burn entire deficit). |
redeployToCurve
Re-deploys recovered BEAR + excess USDC to Curve after emergency recovery.
Clears emergencyActive flag. Should only be called after emergencyWithdrawFromCurve() has recovered the BEAR tokens from the pool.
function redeployToCurve(
uint256 minLpOut
) external onlyOwner nonReentrant;
Parameters
| Name | Type | Description |
|---|---|---|
minLpOut | uint256 | Minimum LP tokens to mint (slippage protection for the two-sided deposit). |
setEmergencyMode
Activates emergency mode without touching Curve.
Pauses the contract and blocks deposits plus single-sided withdrawals. LP accounting is left intact so users retain their pro-rata claim once balanced exits recover.
function setEmergencyMode() external onlyOwner;
forceRemoveGauge
Write off a bricked gauge and its stuck LP from accounting.
Only callable during emergencyActive. Clears curveGauge, proportionally reduces trackedLpBalance and curveLpCostVp for the lost LP, and zeros gaugeStakedLp. After this, emergencyWithdrawFromCurve() or lpWithdraw() can proceed with remaining local LP.
function forceRemoveGauge() external onlyOwner;
emergencyWithdrawFromCurve
Attempts to recover LP tokens from Curve via balanced remove_liquidity.
Also callable as a standalone emergency — sets emergencyActive, pauses, and only zeroes tracked LP accounting after the LP has actually been recovered. If Curve is bricked, the remove_liquidity call reverts and the entire tx rolls back, leaving state unchanged. Use setEmergencyMode() first in that case.
function emergencyWithdrawFromCurve() external onlyOwner nonReentrant;
setGaugeApproval
function setGaugeApproval(
address gauge,
bool approved
) external onlyOwner;
pause
function pause() external onlyOwner;
unpause
function unpause() external onlyOwner;
rescueToken
Rescue accidentally sent ERC20 tokens. Cannot rescue USDC, BEAR, or Curve LP.
function rescueToken(
address token,
address to
) external onlyOwner;
Parameters
| Name | Type | Description |
|---|---|---|
token | address | Address of the token to rescue. |
to | address | Destination address for the rescued tokens. |
proposeGauge
Propose a new Curve gauge for LP staking. Subject to GAUGE_TIMELOCK delay.
function proposeGauge(
address _gauge
) external onlyOwner;
Parameters
| Name | Type | Description |
|---|---|---|
_gauge | address | Address of the new gauge (address(0) to remove gauge). |
finalizeGauge
Finalize a pending gauge change after the timelock expires.
This MUST revert if oldGauge.withdraw() fails. Do NOT wrap in try/catch: silent failure would update curveGauge to newGauge while LP stays locked in oldGauge, causing _lpBalance() to forget the stuck LP and collapsing totalAssets(). If the old gauge is permanently bricked, use setEmergencyMode() instead.
function finalizeGauge() external onlyOwner;
stakeToGauge
Stake LP tokens to the active Curve gauge.
function stakeToGauge(
uint256 amount
) external onlyOwner;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | Amount of LP to stake (0 = all unstaked LP). |
unstakeFromGauge
Unstake LP tokens from the active Curve gauge.
function unstakeFromGauge(
uint256 amount
) external onlyOwner;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | Amount of LP to unstake (0 = all staked LP). |
claimGaugeRewards
Claim CRV + extra rewards from the gauge. Use rescueToken() to sweep reward tokens.
On L1, CRV is minted via the Curve Minter (not claim_rewards). On L2, claim_rewards handles CRV.
function claimGaugeRewards() external onlyOwner;
Events
Deposited
event Deposited(address indexed user, address indexed receiver, uint256 usdcIn, uint256 glUsdOut);
Withdrawn
event Withdrawn(address indexed user, address indexed receiver, uint256 glUsdIn, uint256 usdcOut);
LpWithdrawn
event LpWithdrawn(address indexed user, uint256 sharesBurned, uint256 usdcReturned, uint256 bearReturned);
LpDeposited
event LpDeposited(address indexed user, address indexed receiver, uint256 usdcIn, uint256 bearIn, uint256 glUsdOut);
DeployedToCurve
event DeployedToCurve(address indexed caller, uint256 usdcDeployed, uint256 bearDeployed, uint256 lpMinted);
BufferReplenished
event BufferReplenished(uint256 lpBurned, uint256 usdcRecovered);
YieldHarvested
event YieldHarvested(uint256 glUsdMinted, uint256 callerReward, uint256 donated);
TokenRescued
event TokenRescued(address indexed token, address indexed to, uint256 amount);
EmergencyWithdrawCurve
event EmergencyWithdrawCurve(uint256 lpBurned, uint256 usdcReceived, uint256 bearReceived);
StakedInvarCoinProposed
event StakedInvarCoinProposed(address indexed stakedInvarCoin, uint256 activationTime);
StakedInvarCoinSet
event StakedInvarCoinSet(address indexed stakedInvarCoin);
GaugeProposed
event GaugeProposed(address indexed gauge, uint256 activationTime);
GaugeUpdated
event GaugeUpdated(address indexed oldGauge, address indexed newGauge);
GaugeRewardsReceiverProposed
event GaugeRewardsReceiverProposed(address indexed receiver, uint256 activationTime);
GaugeRewardsReceiverSet
event GaugeRewardsReceiverSet(address indexed receiver);
RewardTokenProtected
event RewardTokenProtected(address indexed token);
GaugeStaked
event GaugeStaked(uint256 amount);
GaugeUnstaked
event GaugeUnstaked(uint256 amount);
GaugeRewardsClaimed
event GaugeRewardsClaimed();
GaugeRewardsSwept
event GaugeRewardsSwept(address indexed token, address indexed receiver, uint256 amount);
UsdcDonated
event UsdcDonated(address indexed donor, uint256 usdcAmount, uint256 invarMinted);
GaugeForceRemoved
event GaugeForceRemoved(address indexed gauge, uint256 lpLost);
Errors
InvarCoin__ZeroAmount
error InvarCoin__ZeroAmount();
InvarCoin__StakingNotSet
error InvarCoin__StakingNotSet();
InvarCoin__ZeroAddress
error InvarCoin__ZeroAddress();
InvarCoin__SlippageExceeded
error InvarCoin__SlippageExceeded();
InvarCoin__NothingToDeploy
error InvarCoin__NothingToDeploy();
InvarCoin__NoYield
error InvarCoin__NoYield();
InvarCoin__CannotRescueCoreAsset
error InvarCoin__CannotRescueCoreAsset();
InvarCoin__PermitFailed
error InvarCoin__PermitFailed();
InvarCoin__AlreadySet
error InvarCoin__AlreadySet();
InvarCoin__SpotDeviationTooHigh
error InvarCoin__SpotDeviationTooHigh();
InvarCoin__UseLpWithdraw
error InvarCoin__UseLpWithdraw();
InvarCoin__Unauthorized
error InvarCoin__Unauthorized();
InvarCoin__GaugeTimelockActive
error InvarCoin__GaugeTimelockActive();
InvarCoin__StakingTimelockActive
error InvarCoin__StakingTimelockActive();
InvarCoin__GaugeRewardsTimelockActive
error InvarCoin__GaugeRewardsTimelockActive();
InvarCoin__InvalidProposal
error InvarCoin__InvalidProposal();
InvarCoin__NoGauge
error InvarCoin__NoGauge();
InvarCoin__EmergencyActive
error InvarCoin__EmergencyActive();
InvarCoin__InvalidGauge
error InvarCoin__InvalidGauge();
InvarCoin__InvalidStakingVault
error InvarCoin__InvalidStakingVault();
InvarCoin__GaugeRewardsReceiverNotSet
error InvarCoin__GaugeRewardsReceiverNotSet();
InvarCoin__NotEmergency
error InvarCoin__NotEmergency();
LeverageRouter
Inherits: LeverageRouterBase
Title: LeverageRouter
Leverage router for plDXY-BEAR positions via Morpho Blue.
Flash loans USDC from Morpho → swaps to plDXY-BEAR on Curve → stakes → deposits to Morpho as collateral. Requires user to authorize this contract in Morpho before use. Uses Morpho’s fee-free flash loans for capital efficiency.
Note: security-contact: contact@plether.com
Constants
STAKED_PLDXY_BEAR
StakedToken vault for plDXY-BEAR (used as Morpho collateral).
IERC4626 public immutable STAKED_PLDXY_BEAR
Functions
constructor
Deploys LeverageRouter with Morpho market configuration.
constructor(
address _morpho,
address _curvePool,
address _usdc,
address _plDxyBear,
address _stakedPlDxyBear,
MarketParams memory _marketParams
) LeverageRouterBase(_morpho, _curvePool, _usdc, _plDxyBear);
Parameters
| Name | Type | Description |
|---|---|---|
_morpho | address | Morpho Blue protocol address. |
_curvePool | address | Curve USDC/plDXY-BEAR pool address. |
_usdc | address | USDC token address. |
_plDxyBear | address | plDXY-BEAR token address. |
_stakedPlDxyBear | address | splDXY-BEAR staking vault address. |
_marketParams | MarketParams | Morpho market parameters for splDXY-BEAR/USDC. |
openLeverage
Open a Leveraged Position in one transaction.
function openLeverage(
uint256 principal,
uint256 leverage,
uint256 maxSlippageBps,
uint256 minAmountOut,
uint256 deadline
) external nonReentrant whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
principal | uint256 | Amount of USDC user sends. |
leverage | uint256 | Multiplier (e.g. 3x = 3e18). |
maxSlippageBps | uint256 | Maximum slippage tolerance in basis points (e.g., 50 = 0.5%). Capped at MAX_SLIPPAGE_BPS (1%) to limit MEV extraction. |
minAmountOut | uint256 | |
deadline | uint256 | Unix timestamp after which the transaction reverts. |
openLeverageWithPermit
Open a leveraged position with a USDC permit signature (gasless approval).
function openLeverageWithPermit(
uint256 principal,
uint256 leverage,
uint256 maxSlippageBps,
uint256 minAmountOut,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external nonReentrant whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
principal | uint256 | Amount of USDC user sends. |
leverage | uint256 | Multiplier (e.g. 3x = 3e18). |
maxSlippageBps | uint256 | Maximum slippage tolerance in basis points. |
minAmountOut | uint256 | |
deadline | uint256 | Unix timestamp after which the permit and transaction revert. |
v | uint8 | Signature recovery byte. |
r | bytes32 | Signature r component. |
s | bytes32 | Signature s component. |
_openLeverageCore
function _openLeverageCore(
uint256 principal,
uint256 leverage,
uint256 maxSlippageBps,
uint256 minAmountOut,
uint256 deadline
) internal;
closeLeverage
Close a Leveraged Position in one transaction.
Queries actual debt from Morpho to ensure full repayment even if interest accrued.
function closeLeverage(
uint256 collateralToWithdraw,
uint256 maxSlippageBps,
uint256 minAmountOut,
uint256 deadline
) external nonReentrant whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
collateralToWithdraw | uint256 | Amount of splDXY-BEAR shares to withdraw from Morpho. NOTE: This is staked token shares, not underlying plDXY-BEAR amount. Use STAKED_PLDXY_BEAR.previewRedeem() to convert shares to underlying. |
maxSlippageBps | uint256 | Maximum slippage tolerance in basis points (e.g., 50 = 0.5%). Capped at MAX_SLIPPAGE_BPS (1%) to limit MEV extraction. |
minAmountOut | uint256 | |
deadline | uint256 | Unix timestamp after which the transaction reverts. |
addCollateral
Add collateral to an existing leveraged position.
Swaps USDC to plDXY-BEAR on Curve, stakes, and deposits to Morpho.
function addCollateral(
uint256 usdcAmount,
uint256 maxSlippageBps,
uint256 minAmountOut,
uint256 deadline
) external nonReentrant whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
usdcAmount | uint256 | Amount of USDC to add as collateral. |
maxSlippageBps | uint256 | Maximum slippage tolerance in basis points. Capped at MAX_SLIPPAGE_BPS (1%) to limit MEV extraction. |
minAmountOut | uint256 | |
deadline | uint256 | Unix timestamp after which the transaction reverts. |
addCollateralWithPermit
Add collateral with a USDC permit signature (gasless approval).
function addCollateralWithPermit(
uint256 usdcAmount,
uint256 maxSlippageBps,
uint256 minAmountOut,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external nonReentrant whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
usdcAmount | uint256 | Amount of USDC to add as collateral. |
maxSlippageBps | uint256 | Maximum slippage tolerance in basis points. |
minAmountOut | uint256 | |
deadline | uint256 | Unix timestamp after which the permit and transaction revert. |
v | uint8 | Signature recovery byte. |
r | bytes32 | Signature r component. |
s | bytes32 | Signature s component. |
_addCollateralCore
function _addCollateralCore(
uint256 usdcAmount,
uint256 maxSlippageBps,
uint256 minAmountOut,
uint256 deadline
) internal;
removeCollateral
Remove collateral from an existing leveraged position.
Withdraws from Morpho, unstakes, and swaps to USDC. Reverts if the resulting position would be unhealthy.
function removeCollateral(
uint256 collateralToWithdraw,
uint256 maxSlippageBps,
uint256 minAmountOut,
uint256 deadline
) external nonReentrant whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
collateralToWithdraw | uint256 | Amount of splDXY-BEAR shares to withdraw. NOTE: This is staked token shares, not underlying plDXY-BEAR amount. |
maxSlippageBps | uint256 | Maximum slippage tolerance in basis points. Capped at MAX_SLIPPAGE_BPS (1%) to limit MEV extraction. |
minAmountOut | uint256 | |
deadline | uint256 | Unix timestamp after which the transaction reverts. |
onMorphoFlashLoan
Morpho flash loan callback. Routes to open or close handler.
function onMorphoFlashLoan(
uint256 amount,
bytes calldata data
) external override;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | Amount of USDC borrowed. |
data | bytes | Encoded operation parameters. |
onFlashLoan
ERC-3156 flash loan callback - not used by LeverageRouter.
Always reverts as LeverageRouter only uses Morpho flash loans.
function onFlashLoan(
address,
address,
uint256,
uint256,
bytes calldata
) external pure override returns (bytes32);
_executeOpen
Executes open leverage operation within Morpho flash loan callback.
function _executeOpen(
uint256 loanAmount,
bytes calldata data
) private;
Parameters
| Name | Type | Description |
|---|---|---|
loanAmount | uint256 | Amount of USDC borrowed from Morpho. |
data | bytes | Encoded parameters (op, user, deadline, principal, leverage, maxSlippageBps, minPlDxyBear, minAmountOut). |
_executeClose
Executes close leverage operation within Morpho flash loan callback.
function _executeClose(
uint256 loanAmount,
bytes calldata data
) private;
Parameters
| Name | Type | Description |
|---|---|---|
loanAmount | uint256 | Amount of USDC borrowed from Morpho to repay debt. |
data | bytes | Encoded parameters (op, user, deadline, collateralToWithdraw, borrowShares, maxSlippageBps, minUsdcOut, minAmountOut). |
_executeCloseNoDebt
Closes position without flash loan when user has no Morpho debt.
function _executeCloseNoDebt(
address user,
uint256 collateralToWithdraw,
uint256 maxSlippageBps,
uint256 minUsdcOut,
uint256 minAmountOut
) private;
Parameters
| Name | Type | Description |
|---|---|---|
user | address | Position owner receiving USDC. |
collateralToWithdraw | uint256 | Amount of splDXY-BEAR shares to withdraw. |
maxSlippageBps | uint256 | Maximum slippage for Curve swap. |
minUsdcOut | uint256 | Minimum USDC to receive after swap. |
minAmountOut | uint256 | User-provided minimum USDC to receive (MEV protection). |
previewOpenLeverage
Preview the result of opening a leveraged position.
function previewOpenLeverage(
uint256 principal,
uint256 leverage
) external view returns (uint256 loanAmount, uint256 totalUSDC, uint256 expectedPlDxyBear, uint256 expectedDebt);
Parameters
| Name | Type | Description |
|---|---|---|
principal | uint256 | Amount of USDC user will send. |
leverage | uint256 | Multiplier (e.g. 3x = 3e18). |
Returns
| Name | Type | Description |
|---|---|---|
loanAmount | uint256 | Amount of USDC to flash loan. |
totalUSDC | uint256 | Total USDC to swap (principal + loan). |
expectedPlDxyBear | uint256 | Expected plDXY-BEAR (based on current curve price). |
expectedDebt | uint256 | Expected debt incurred (equals loan amount, no flash fee with Morpho). |
previewCloseLeverage
Preview the result of closing a leveraged position.
function previewCloseLeverage(
uint256 debtToRepay,
uint256 collateralToWithdraw
) external view returns (uint256 expectedUSDC, uint256 flashFee, uint256 expectedReturn);
Parameters
| Name | Type | Description |
|---|---|---|
debtToRepay | uint256 | Amount of USDC debt to repay. |
collateralToWithdraw | uint256 | Amount of plDXY-BEAR collateral to withdraw. |
Returns
| Name | Type | Description |
|---|---|---|
expectedUSDC | uint256 | Expected USDC from swap (based on current curve price). |
flashFee | uint256 | Flash loan fee (always 0 with Morpho). |
expectedReturn | uint256 | Expected USDC returned to user after repaying flash loan. |
previewAddCollateral
Preview the result of adding collateral.
function previewAddCollateral(
uint256 usdcAmount
) external view returns (uint256 expectedPlDxyBear, uint256 expectedStakedShares);
Parameters
| Name | Type | Description |
|---|---|---|
usdcAmount | uint256 | Amount of USDC to add as collateral. |
Returns
| Name | Type | Description |
|---|---|---|
expectedPlDxyBear | uint256 | Expected plDXY-BEAR from Curve swap. |
expectedStakedShares | uint256 | Expected splDXY-BEAR shares to receive. |
previewRemoveCollateral
Preview the result of removing collateral.
function previewRemoveCollateral(
uint256 collateralToWithdraw
) external view returns (uint256 expectedPlDxyBear, uint256 expectedUSDC);
Parameters
| Name | Type | Description |
|---|---|---|
collateralToWithdraw | uint256 | Amount of splDXY-BEAR shares to withdraw. |
Returns
| Name | Type | Description |
|---|---|---|
expectedPlDxyBear | uint256 | Expected plDXY-BEAR from unstaking. |
expectedUSDC | uint256 | Expected USDC from Curve swap. |
Events
LeverageOpened
Emitted when a leveraged plDXY-BEAR position is opened.
event LeverageOpened(
address indexed user,
uint256 principal,
uint256 leverage,
uint256 loanAmount,
uint256 plDxyBearReceived,
uint256 debtIncurred,
uint256 maxSlippageBps
);
LeverageClosed
Emitted when a leveraged plDXY-BEAR position is closed.
event LeverageClosed(
address indexed user,
uint256 debtRepaid,
uint256 collateralWithdrawn,
uint256 usdcReturned,
uint256 maxSlippageBps
);
CollateralAdded
Emitted when collateral is added to a position.
usdcReturned is always 0 for BEAR router (full amount swapped to BEAR).
event CollateralAdded(
address indexed user, uint256 usdcAmount, uint256 usdcReturned, uint256 collateralAdded, uint256 maxSlippageBps
);
CollateralRemoved
Emitted when collateral is removed from a position.
event CollateralRemoved(
address indexed user, uint256 collateralWithdrawn, uint256 usdcReturned, uint256 maxSlippageBps
);
RewardDistributor
Inherits: IRewardDistributor, ReentrancyGuard
Title: RewardDistributor
Distributes staking rewards based on price discrepancy between oracle and Curve EMA.
Receives USDC from SyntheticSplitter.harvestYield() and allocates to StakedToken vaults proportionally based on which token is underperforming relative to theoretical price.
Note: security-contact: contact@plether.com
Constants
DISCREPANCY_THRESHOLD_BPS
Discrepancy threshold for 100% allocation (2% = 200 bps).
uint256 public constant DISCREPANCY_THRESHOLD_BPS = 200
MIN_DISTRIBUTION_INTERVAL
Minimum time between distributions (1 hour).
uint256 public constant MIN_DISTRIBUTION_INTERVAL = 1 hours
CALLER_REWARD_BPS
Reward for calling distributeRewards (0.1% = 10 bps).
uint256 public constant CALLER_REWARD_BPS = 10
USDC_INDEX
USDC index in Curve pool.
uint256 public constant USDC_INDEX = 0
PLDXY_BEAR_INDEX
plDXY-BEAR index in Curve pool.
uint256 public constant PLDXY_BEAR_INDEX = 1
MAX_SWAP_SLIPPAGE_BPS
Maximum slippage for Curve swaps (1% = 100 bps).
uint256 public constant MAX_SWAP_SLIPPAGE_BPS = 100
MAX_ORACLE_ZAP_SLIPPAGE_BPS
Maximum slippage for oracle-based zap estimates (3% = 300 bps).
Wider than MAX_SWAP_SLIPPAGE_BPS because ZapRouter’s flash mint loop adds fees.
uint256 public constant MAX_ORACLE_ZAP_SLIPPAGE_BPS = 300
ORACLE_TIMEOUT
Maximum age for oracle price data (24 hours to match Chainlink CHF/CAD heartbeat).
uint256 public constant ORACLE_TIMEOUT = 24 hours
SPLITTER
SyntheticSplitter contract.
ISyntheticSplitter public immutable SPLITTER
USDC
USDC stablecoin.
IERC20 public immutable USDC
PLDXY_BEAR
plDXY-BEAR token.
IERC20 public immutable PLDXY_BEAR
PLDXY_BULL
plDXY-BULL token.
IERC20 public immutable PLDXY_BULL
STAKED_BEAR
Staked plDXY-BEAR vault.
StakedToken public immutable STAKED_BEAR
STAKED_BULL
Staked plDXY-BULL vault.
StakedToken public immutable STAKED_BULL
CURVE_POOL
Curve USDC/plDXY-BEAR pool.
ICurvePool public immutable CURVE_POOL
ZAP_ROUTER
ZapRouter for acquiring plDXY-BULL.
ZapRouter public immutable ZAP_ROUTER
ORACLE
BasketOracle for theoretical price.
AggregatorV3Interface public immutable ORACLE
PYTH_ADAPTER
Optional PythAdapter for SEK/USD price updates (address(0) if not used).
PythAdapter public immutable PYTH_ADAPTER
INVAR_COIN
Optional InvarCoin vault that receives a share of BEAR rewards (address(0) if not used).
IInvarCoin public immutable INVAR_COIN
CAP
Protocol CAP price (8 decimals).
uint256 public immutable CAP
State Variables
lastDistributionTime
Timestamp of last distribution.
uint256 public lastDistributionTime
Functions
constructor
Deploys RewardDistributor with all required dependencies.
constructor(
address _splitter,
address _usdc,
address _plDxyBear,
address _plDxyBull,
address _stakedBear,
address _stakedBull,
address _curvePool,
address _zapRouter,
address _oracle,
address _pythAdapter,
address _invarCoin
) ;
Parameters
| Name | Type | Description |
|---|---|---|
_splitter | address | SyntheticSplitter contract address. |
_usdc | address | USDC token address. |
_plDxyBear | address | plDXY-BEAR token address. |
_plDxyBull | address | plDXY-BULL token address. |
_stakedBear | address | StakedToken vault for BEAR. |
_stakedBull | address | StakedToken vault for BULL. |
_curvePool | address | Curve USDC/plDXY-BEAR pool. |
_zapRouter | address | ZapRouter for BULL acquisition. |
_oracle | address | BasketOracle for price data. |
_pythAdapter | address | Optional PythAdapter for SEK/USD updates (address(0) if not used). |
_invarCoin | address | Optional InvarCoin vault for BEAR reward split (address(0) if not used). |
receive
receive() external payable;
distributeRewards
Permissionless function to distribute accumulated USDC rewards.
Calculates price discrepancy, acquires tokens, and donates to vaults.
function distributeRewards() external nonReentrant returns (uint256 callerReward);
Returns
| Name | Type | Description |
|---|---|---|
callerReward | uint256 | Amount of USDC sent to caller as incentive. |
_distributeRewardsInternal
Internal implementation of reward distribution.
function _distributeRewardsInternal() internal returns (uint256 callerReward);
distributeRewardsWithPriceUpdate
Distributes rewards after updating the Pyth oracle price.
Bundles Pyth price update with reward distribution to save gas. Caller receives the same reward as distributeRewards().
function distributeRewardsWithPriceUpdate(
bytes[] calldata pythUpdateData
) external payable nonReentrant returns (uint256 callerReward);
Parameters
| Name | Type | Description |
|---|---|---|
pythUpdateData | bytes[] | Price update data from Pyth Hermes API. |
Returns
| Name | Type | Description |
|---|---|---|
callerReward | uint256 | Amount of USDC sent to caller as incentive. |
previewDistribution
Preview the distribution without executing.
function previewDistribution()
external
view
returns (uint256 bearPct, uint256 bullPct, uint256 usdcBalance, uint256 callerReward);
Returns
| Name | Type | Description |
|---|---|---|
bearPct | uint256 | Expected percentage to BEAR stakers (basis points). |
bullPct | uint256 | Expected percentage to BULL stakers (basis points). |
usdcBalance | uint256 | Current USDC balance available for distribution. |
callerReward | uint256 | Expected caller reward. |
_calculateSplit
Calculates reward split based on price discrepancy.
function _calculateSplit(
uint256 absBasketPrice
) internal view returns (uint256 bearPct, uint256 bullPct);
Parameters
| Name | Type | Description |
|---|---|---|
absBasketPrice | uint256 | Validated basket price (8 decimals, positive). |
Returns
| Name | Type | Description |
|---|---|---|
bearPct | uint256 | Percentage for BEAR stakers (basis points). |
bullPct | uint256 | Percentage for BULL stakers (basis points). |
_acquireTokens
Acquires tokens using optimal combination of minting and swapping/zapping.
function _acquireTokens(
uint256 bearUsdc,
uint256 bullUsdc,
uint256 absBasketPrice
) internal returns (uint256 bearAmount, uint256 bullAmount);
Parameters
| Name | Type | Description |
|---|---|---|
bearUsdc | uint256 | USDC allocated to BEAR acquisition. |
bullUsdc | uint256 | USDC allocated to BULL acquisition. |
absBasketPrice | uint256 | Validated basket price (8 decimals, positive). |
Returns
| Name | Type | Description |
|---|---|---|
bearAmount | uint256 | Amount of plDXY-BEAR acquired. |
bullAmount | uint256 | Amount of plDXY-BULL acquired. |
rescueUsdc
Rescues USDC trapped after protocol liquidation.
Only callable when Splitter is SETTLED. Sends full USDC balance to Splitter treasury.
function rescueUsdc() external;
_splitBearAllocation
Splits BEAR-side USDC allocation between StakedBear and InvarCoin proportional to BEAR exposure.
function _splitBearAllocation(
uint256 bearUsdc,
uint256 absBasketPrice
) internal view returns (uint256 stakedUsdc, uint256 invarUsdc);
Parameters
| Name | Type | Description |
|---|---|---|
bearUsdc | uint256 | Total USDC allocated to the BEAR side. |
absBasketPrice | uint256 | Validated basket price (8 decimals, positive). |
_calculateMintAmount
Converts USDC amount to 18-decimal mint amount.
function _calculateMintAmount(
uint256 usdcAmount
) internal view returns (uint256 mintAmount);
Parameters
| Name | Type | Description |
|---|---|---|
usdcAmount | uint256 | USDC amount (6 decimals). |
Returns
| Name | Type | Description |
|---|---|---|
mintAmount | uint256 | Token amount to mint (18 decimals). |
StakedToken
Inherits: ERC4626
Title: StakedToken
ERC4626 vault for staking plDXY-BEAR or plDXY-BULL tokens.
Used as Morpho collateral. Exchange rate increases via yield donations. Implements 1000x virtual share offset to prevent inflation attacks. Implements streaming rewards (1-hour linear vest) to prevent reward sniping.
Note: security-contact: contact@plether.com
Constants
STREAM_DURATION
Duration over which donated rewards are streamed.
uint256 public constant STREAM_DURATION = 1 hours
State Variables
_trackedBalance
uint256 private _trackedBalance
rewardRate
Current reward streaming rate (tokens per second, scaled by 1e18).
uint256 public rewardRate
streamEndTime
Timestamp when current reward stream ends.
uint256 public streamEndTime
Functions
constructor
Creates a new staking vault for a synthetic token.
constructor(
IERC20 _asset,
string memory _name,
string memory _symbol
) ERC4626(_asset) ERC20(_name, _symbol);
Parameters
| Name | Type | Description |
|---|---|---|
_asset | IERC20 | The underlying plDXY token to stake (plDXY-BEAR or plDXY-BULL). |
_name | string | Vault share name (e.g., “Staked plDXY-BEAR”). |
_symbol | string | Vault share symbol (e.g., “splDXY-BEAR”). |
totalAssets
Returns total assets including only vested streamed rewards.
Overrides ERC4626 to exclude unvested rewards from share price calculation.
function totalAssets() public view override returns (uint256);
donateYield
Donates yield that streams to stakers over STREAM_DURATION.
Rewards vest linearly. New donations extend the stream proportionally to the donation size, preventing griefing via zero-amount timer resets.
function donateYield(
uint256 amount
) external;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | The amount of underlying tokens to donate. |
depositWithPermit
Deposit assets with a permit signature (gasless approval).
Combines EIP-2612 permit with ERC-4626 deposit in a single transaction.
function depositWithPermit(
uint256 assets,
address receiver,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external returns (uint256 shares);
Parameters
| Name | Type | Description |
|---|---|---|
assets | uint256 | Amount of underlying tokens to deposit |
receiver | address | Address to receive the vault shares |
deadline | uint256 | Permit signature expiration timestamp |
v | uint8 | Signature recovery byte |
r | bytes32 | Signature r component |
s | bytes32 | Signature s component |
Returns
| Name | Type | Description |
|---|---|---|
shares | uint256 | Amount of vault shares minted |
_deposit
function _deposit(
address caller,
address receiver,
uint256 assets,
uint256 shares
) internal override;
_withdraw
function _withdraw(
address caller,
address receiver,
address owner,
uint256 assets,
uint256 shares
) internal override;
_unvestedRewards
Calculates unvested rewards from the current stream.
function _unvestedRewards() internal view returns (uint256);
_decimalsOffset
Virtual share offset (10^3 = 1000x) to prevent inflation attacks.
function _decimalsOffset() internal pure override returns (uint8);
Events
YieldDonated
Emitted when yield is donated and streaming begins/extends.
event YieldDonated(address indexed donor, uint256 amount, uint256 newStreamEndTime);
Errors
StakedToken__PermitFailed
error StakedToken__PermitFailed();
SyntheticSplitter
Inherits: ISyntheticSplitter, Ownable2Step, Pausable, ReentrancyGuard
Title: SyntheticSplitter
Core protocol contract for minting/burning synthetic plDXY tokens.
Accepts USDC collateral to mint equal amounts of plDXY-BEAR + plDXY-BULL tokens. Minted USDC stays local; a permissionless deployToAdapter() pushes excess above the 10% buffer into the yield adapter. Three lifecycle states: ACTIVE → PAUSED → SETTLED.
Note: security-contact: contact@plether.com
Constants
BEAR
SyntheticToken public immutable BEAR
BULL
SyntheticToken public immutable BULL
USDC
IERC20 public immutable USDC
ORACLE
AggregatorV3Interface public immutable ORACLE
CAP
uint256 public immutable CAP
USDC_DECIMALS
uint256 private constant USDC_DECIMALS = 6
USDC_MULTIPLIER
uint256 public constant USDC_MULTIPLIER = 10 ** (18 + 8 - USDC_DECIMALS)
BUFFER_PERCENT
uint256 public constant BUFFER_PERCENT = 10
ORACLE_TIMEOUT
uint256 public constant ORACLE_TIMEOUT = 24 hours
TIMELOCK_DELAY
uint256 public constant TIMELOCK_DELAY = 7 days
HARVEST_REWARD_BPS
uint256 public constant HARVEST_REWARD_BPS = 10
MIN_SURPLUS_THRESHOLD
uint256 public constant MIN_SURPLUS_THRESHOLD = 50 * 10 ** USDC_DECIMALS
MIN_DEPLOY_AMOUNT
uint256 public constant MIN_DEPLOY_AMOUNT = 100 * 10 ** USDC_DECIMALS
SEQUENCER_UPTIME_FEED
AggregatorV3Interface public immutable SEQUENCER_UPTIME_FEED
SEQUENCER_GRACE_PERIOD
uint256 public constant SEQUENCER_GRACE_PERIOD = 1 hours
State Variables
yieldAdapter
IERC4626 public yieldAdapter
pendingAdapter
address public pendingAdapter
adapterActivationTime
uint256 public adapterActivationTime
treasury
address public treasury
staking
address public staking
pendingFees
FeeConfig public pendingFees
feesActivationTime
uint256 public feesActivationTime
lastUnpauseTime
uint256 public lastUnpauseTime
isLiquidated
bool public isLiquidated
liquidationTimestamp
uint256 public liquidationTimestamp
Functions
constructor
Deploys the SyntheticSplitter and creates plDXY-BEAR and plDXY-BULL tokens.
constructor(
address _oracle,
address _usdc,
address _yieldAdapter,
uint256 _cap,
address _treasury,
address _sequencerUptimeFeed
) Ownable(msg.sender);
Parameters
| Name | Type | Description |
|---|---|---|
_oracle | address | Chainlink-compatible price feed for plDXY basket. |
_usdc | address | USDC token address (6 decimals). |
_yieldAdapter | address | ERC4626-compliant yield adapter for USDC deposits. |
_cap | uint256 | Maximum plDXY price (8 decimals). Triggers liquidation when breached. |
_treasury | address | Treasury address for fee distribution. |
_sequencerUptimeFeed | address | L2 sequencer uptime feed (address(0) for L1/testnets). |
previewMint
Simulates a mint to see required USDC input
function previewMint(
uint256 mintAmount
) external view returns (uint256 usdcRequired, uint256 depositToAdapter, uint256 keptInBuffer);
Parameters
| Name | Type | Description |
|---|---|---|
mintAmount | uint256 | The amount of TokenA/B the user wants to mint |
Returns
| Name | Type | Description |
|---|---|---|
usdcRequired | uint256 | Total USDC needed from user |
depositToAdapter | uint256 | Always 0 (deployment is separate via deployToAdapter()) |
keptInBuffer | uint256 | Amount that will stay in Splitter contract (equals usdcRequired) |
mint
Mint plDXY-BEAR and plDXY-BULL tokens by depositing USDC collateral.
function mint(
uint256 amount
) external nonReentrant whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | The amount of token pairs to mint (18 decimals). |
mintWithPermit
Mint plDXY-BEAR and plDXY-BULL tokens with a USDC permit signature (gasless approval).
function mintWithPermit(
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external nonReentrant whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | The amount of token pairs to mint (18 decimals). |
deadline | uint256 | Unix timestamp after which the permit and transaction revert. |
v | uint8 | Signature recovery byte. |
r | bytes32 | Signature r component. |
s | bytes32 | Signature s component. |
_mintCore
function _mintCore(
uint256 amount
) internal;
previewBurn
Simulates a burn to see USDC return
function previewBurn(
uint256 burnAmount
) external view returns (uint256 usdcRefund, uint256 withdrawnFromAdapter);
Parameters
| Name | Type | Description |
|---|---|---|
burnAmount | uint256 | The amount of TokenA/B the user wants to burn |
Returns
| Name | Type | Description |
|---|---|---|
usdcRefund | uint256 | Total USDC user will receive |
withdrawnFromAdapter | uint256 | Amount pulled from Yield Source to cover shortage |
burn
Burn plDXY-BEAR and plDXY-BULL tokens to redeem USDC collateral.
function burn(
uint256 amount
) external nonReentrant;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | The amount of token pairs to burn (18 decimals). |
_withdrawFromAdapter
Withdraws USDC from yield adapter with redeem fallback.
function _withdrawFromAdapter(
uint256 amount
) internal;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | USDC amount to withdraw (6 decimals). |
triggerLiquidation
Locks the protocol into liquidated state when price >= CAP.
Permissionless. Prevents system revival if price drops after breach.
function triggerLiquidation() external nonReentrant;
emergencyRedeem
Emergency redemption when protocol is liquidated (price >= CAP).
Only burns plDXY-BEAR tokens at CAP price. plDXY-BULL becomes worthless.
function emergencyRedeem(
uint256 amount
) external nonReentrant;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | The amount of plDXY-BEAR tokens to redeem (18 decimals). |
ejectLiquidity
Emergency exit: withdraws all funds from yield adapter.
Bypasses timelock. Auto-pauses protocol. Use if adapter is compromised.
function ejectLiquidity() external onlyOwner;
withdrawFromAdapter
Withdraws a specific amount from yield adapter while paused.
Requires protocol to be paused. Use for gradual liquidity extraction when full ejectLiquidity() fails due to adapter liquidity constraints.
function withdrawFromAdapter(
uint256 amount
) external nonReentrant onlyOwner;
Parameters
| Name | Type | Description |
|---|---|---|
amount | uint256 | Desired USDC amount to withdraw. Capped by adapter’s maxWithdraw. |
deployToAdapter
Deploys excess local USDC to the yield adapter.
Permissionless keeper function. Pushes USDC above the 10% buffer target into the adapter. Returns 0 silently when nothing to deploy (keeper-friendly).
function deployToAdapter() external nonReentrant whenNotPaused returns (uint256 deployed);
Returns
| Name | Type | Description |
|---|---|---|
deployed | uint256 | Amount of USDC deployed to the adapter. |
_deployExcess
Pushes local USDC above the 10% buffer target into the yield adapter.
function _deployExcess() internal;
previewHarvest
Previews yield harvest amounts and eligibility.
function previewHarvest()
external
view
returns (
bool canHarvest,
uint256 totalSurplus,
uint256 callerReward,
uint256 treasuryShare,
uint256 stakingShare
);
Returns
| Name | Type | Description |
|---|---|---|
canHarvest | bool | True if surplus exceeds MIN_SURPLUS_THRESHOLD. |
totalSurplus | uint256 | Available surplus (total assets - liabilities). |
callerReward | uint256 | Caller incentive (0.1% of harvest). |
treasuryShare | uint256 | Treasury allocation (20% of remaining). |
stakingShare | uint256 | Staking allocation (80% of remaining). |
harvestYield
Permissionless yield harvesting with automatic redeployment.
Pays surplus from local buffer first, only hits adapter for the remainder. After distribution, deploys any remaining excess to the adapter. Distributes: 0.1% to caller, 20% of remaining to treasury, 80% of remaining to staking.
function harvestYield() external nonReentrant whenNotPaused;
_checkLiveness
Enforces 7-day cooldown after unpause for governance actions.
function _checkLiveness() internal view;
proposeFeeReceivers
Propose new fee receiver addresses (7-day timelock).
function proposeFeeReceivers(
address _treasury,
address _staking
) external onlyOwner;
Parameters
| Name | Type | Description |
|---|---|---|
_treasury | address | New treasury address. |
_staking | address | New staking address (can be zero to send all to treasury). |
finalizeFeeReceivers
Finalize pending fee receiver change after timelock expires.
function finalizeFeeReceivers() external onlyOwner;
proposeAdapter
Propose a new yield adapter (7-day timelock).
function proposeAdapter(
address _newAdapter
) external onlyOwner;
Parameters
| Name | Type | Description |
|---|---|---|
_newAdapter | address | Address of the new ERC4626-compliant adapter. |
finalizeAdapter
Finalize adapter migration after timelock. Migrates all funds atomically.
function finalizeAdapter() external nonReentrant onlyOwner;
pause
Pause the protocol. Blocks minting and harvesting.
function pause() external onlyOwner;
unpause
Unpause the protocol. Starts 7-day governance cooldown.
function unpause() external onlyOwner;
rescueToken
Rescue accidentally sent tokens. Cannot rescue core assets.
function rescueToken(
address token,
address to
) external onlyOwner;
Parameters
| Name | Type | Description |
|---|---|---|
token | address | The ERC20 token to rescue. |
to | address | The recipient address. |
currentStatus
Returns the current protocol lifecycle status.
function currentStatus() external view override returns (Status);
Returns
| Name | Type | Description |
|---|---|---|
<none> | Status | The current Status enum value (ACTIVE, PAUSED, or SETTLED). |
getSystemStatus
Returns comprehensive system metrics for dashboards.
function getSystemStatus() external view returns (SystemStatus memory status);
Returns
| Name | Type | Description |
|---|---|---|
status | SystemStatus | Struct containing price, collateral ratio, and liquidity data. |
_getTotalLiabilities
Calculates total liabilities based on BEAR supply at CAP price.
function _getTotalLiabilities() internal view returns (uint256);
_getTotalAssets
Calculates total assets (local USDC + adapter value).
function _getTotalAssets() internal view returns (uint256);
_requireSolventIfPaused
Reverts if paused and insolvent (assets < liabilities).
function _requireSolventIfPaused() internal view;
_getOraclePrice
Oracle price validation using OracleLib.
function _getOraclePrice() internal view returns (uint256);
Events
Minted
event Minted(address indexed user, uint256 amount);
Burned
event Burned(address indexed user, uint256 amount);
AdapterProposed
event AdapterProposed(address indexed newAdapter, uint256 activationTime);
AdapterMigrated
event AdapterMigrated(address indexed oldAdapter, address indexed newAdapter, uint256 transferredAmount);
LiquidationTriggered
event LiquidationTriggered(uint256 price);
EmergencyRedeemed
event EmergencyRedeemed(address indexed user, uint256 amount);
YieldHarvested
event YieldHarvested(uint256 totalSurplus, uint256 treasuryAmt, uint256 stakingAmt);
FeesProposed
event FeesProposed(address indexed treasury, address indexed staking, uint256 activationTime);
FeesUpdated
event FeesUpdated(address indexed treasury, address indexed staking);
EmergencyEjected
event EmergencyEjected(uint256 amountRecovered);
DeployedToAdapter
event DeployedToAdapter(address indexed caller, uint256 amount);
AdapterWithdrawn
event AdapterWithdrawn(uint256 requested, uint256 withdrawn);
TokenRescued
event TokenRescued(address indexed token, address indexed to, uint256 amount);
Errors
Splitter__ZeroAddress
error Splitter__ZeroAddress();
Splitter__InvalidCap
error Splitter__InvalidCap();
Splitter__ZeroAmount
error Splitter__ZeroAmount();
Splitter__ZeroRefund
error Splitter__ZeroRefund();
Splitter__AdapterNotSet
error Splitter__AdapterNotSet();
Splitter__LiquidationActive
error Splitter__LiquidationActive();
Splitter__NotLiquidated
error Splitter__NotLiquidated();
Splitter__TimelockActive
error Splitter__TimelockActive();
Splitter__InvalidProposal
error Splitter__InvalidProposal();
Splitter__NoSurplus
error Splitter__NoSurplus();
Splitter__GovernanceLocked
error Splitter__GovernanceLocked();
Splitter__InsufficientHarvest
error Splitter__InsufficientHarvest();
Splitter__AdapterWithdrawFailed
error Splitter__AdapterWithdrawFailed();
Splitter__Insolvent
error Splitter__Insolvent();
Splitter__NotPaused
error Splitter__NotPaused();
Splitter__CannotRescueCoreAsset
error Splitter__CannotRescueCoreAsset();
Splitter__MigrationLostFunds
error Splitter__MigrationLostFunds();
Splitter__InvalidAdapter
error Splitter__InvalidAdapter();
Splitter__PermitFailed
error Splitter__PermitFailed();
Structs
FeeConfig
struct FeeConfig {
address treasuryAddr;
address stakingAddr;
}
SystemStatus
struct SystemStatus {
uint256 currentPrice;
uint256 capPrice;
bool liquidated;
bool isPaused;
uint256 totalAssets; // Local + Adapter
uint256 totalLiabilities; // Bear Supply * CAP
uint256 collateralRatio; // Basis points
uint256 adapterAssets; // USDC value held in yield adapter
}
SyntheticToken
Inherits: ERC20, ERC20Permit, ERC20FlashMint
Title: SyntheticToken
ERC20 token with flash mint and permit capability, controlled by SyntheticSplitter.
Used for plDXY-BEAR and plDXY-BULL tokens. Only the Splitter can mint/burn. Inherits ERC20FlashMint for fee-free flash loans used by routers.
Note: security-contact: contact@plether.com
Constants
SPLITTER
The SyntheticSplitter contract that controls minting and burning.
address public immutable SPLITTER
Functions
onlySplitter
Restricts function access to the Splitter contract only.
modifier onlySplitter() ;
constructor
Creates a new SyntheticToken.
constructor(
string memory _name,
string memory _symbol,
address _splitter
) ERC20(_name, _symbol) ERC20Permit(_name);
Parameters
| Name | Type | Description |
|---|---|---|
_name | string | Token name (e.g., “Plether Dollar Index Bear”). |
_symbol | string | Token symbol (e.g., “plDXY-BEAR”). |
_splitter | address | Address of the SyntheticSplitter contract. |
mint
Mint tokens to an address. Only callable by Splitter.
function mint(
address to,
uint256 amount
) external onlySplitter;
Parameters
| Name | Type | Description |
|---|---|---|
to | address | Recipient address. |
amount | uint256 | Amount to mint. |
burn
Burn tokens from an address. Only callable by Splitter.
function burn(
address from,
uint256 amount
) external onlySplitter;
Parameters
| Name | Type | Description |
|---|---|---|
from | address | Address to burn from. |
amount | uint256 | Amount to burn. |
Errors
SyntheticToken__Unauthorized
Thrown when a non-Splitter address attempts to mint or burn.
error SyntheticToken__Unauthorized();
SyntheticToken__ZeroAddress
Thrown when zero address provided for splitter.
error SyntheticToken__ZeroAddress();
VaultAdapter
Inherits: ERC4626, Ownable2Step, IYieldAdapter
Title: VaultAdapter
ERC4626-compliant wrapper that deposits into a Morpho vault for yield.
Interchangeable with other yield adapters. Only accepts deposits from SyntheticSplitter.
Note: security-contact: contact@plether.com
Constants
VAULT
Morpho vault (ERC4626) that generates yield.
IERC4626 public immutable VAULT
SPLITTER
SyntheticSplitter authorized to deposit.
address public immutable SPLITTER
Functions
constructor
Deploys adapter targeting a Morpho vault.
constructor(
IERC20 _asset,
address _vault,
address _owner,
address _splitter
) ERC4626(_asset) ERC20("Vault Yield Wrapper", "vyUSDC") Ownable(_owner);
Parameters
| Name | Type | Description |
|---|---|---|
_asset | IERC20 | Underlying asset (USDC). |
_vault | address | Morpho vault address (must have same underlying asset). |
_owner | address | Admin address for rescue operations. |
_splitter | address | SyntheticSplitter authorized to deposit. |
maxWithdraw
Maximum USDC withdrawable by owner.
Does not cap by VAULT.maxWithdraw() — Morpho Vault V2 returns 0 for all max* functions due to its gate system. Actual liquidity is enforced at withdrawal time.
function maxWithdraw(
address owner
) public view override returns (uint256);
maxRedeem
Maximum adapter shares redeemable by owner.
See maxWithdraw for rationale on not delegating to vault.
function maxRedeem(
address owner
) public view override returns (uint256);
maxDeposit
Maximum USDC depositable.
See maxWithdraw for rationale on not delegating to vault.
function maxDeposit(
address
) public view override returns (uint256);
maxMint
Maximum adapter shares mintable.
See maxWithdraw for rationale on not delegating to vault.
function maxMint(
address
) public view override returns (uint256);
totalAssets
Returns total USDC value of this adapter’s vault position.
function totalAssets() public view override returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Total assets held in the Morpho vault, converted from vault shares. |
_deposit
Deposits assets to Morpho vault after ERC4626 share minting.
function _deposit(
address caller,
address receiver,
uint256 assets,
uint256 shares
) internal override;
Parameters
| Name | Type | Description |
|---|---|---|
caller | address | Must be SPLITTER. |
receiver | address | Receiver of adapter shares. |
assets | uint256 | Amount of USDC to deposit. |
shares | uint256 | Amount of adapter shares minted. |
_withdraw
Withdraws assets from Morpho vault before ERC4626 share burning.
function _withdraw(
address caller,
address receiver,
address owner,
uint256 assets,
uint256 shares
) internal override;
Parameters
| Name | Type | Description |
|---|---|---|
caller | address | Caller requesting withdrawal. |
receiver | address | Receiver of withdrawn USDC. |
owner | address | Owner of adapter shares being burned. |
assets | uint256 | Amount of USDC to withdraw. |
shares | uint256 | Amount of adapter shares burned. |
accrueInterest
No-op — Morpho vault accrues interest on underlying markets during deposit/withdraw.
View functions (totalAssets, convertToAssets) may lag by a few blocks of unaccrued interest across the vault’s markets. This is negligible for an actively-used vault and self-corrects on the next state-changing interaction.
function accrueInterest() external;
rescueToken
Recovers stuck tokens (excluding the underlying asset and vault shares).
function rescueToken(
address token,
address to
) external onlyOwner;
Parameters
| Name | Type | Description |
|---|---|---|
token | address | Token to rescue. |
to | address | Recipient address. |
claimRewards
Claims rewards from an external distributor (Merkl, URD, etc.).
Reward tokens land in this contract; use rescueToken() to extract them.
function claimRewards(
address target,
bytes calldata data
) external onlyOwner;
Parameters
| Name | Type | Description |
|---|---|---|
target | address | Distributor contract to call. |
data | bytes | ABI-encoded call data for the claim function. |
Errors
VaultAdapter__OnlySplitter
Thrown when caller is not the SyntheticSplitter.
error VaultAdapter__OnlySplitter();
VaultAdapter__InvalidAddress
Thrown when a zero address is provided.
error VaultAdapter__InvalidAddress();
VaultAdapter__InvalidVault
Thrown when vault’s underlying asset doesn’t match this adapter’s asset.
error VaultAdapter__InvalidVault();
VaultAdapter__CannotRescueUnderlying
Thrown when attempting to rescue the underlying asset.
error VaultAdapter__CannotRescueUnderlying();
VaultAdapter__CannotRescueVaultShares
Thrown when attempting to rescue vault share tokens.
error VaultAdapter__CannotRescueVaultShares();
VaultAdapter__CallFailed
Thrown when a reward claim call fails.
error VaultAdapter__CallFailed();
VaultAdapter__ForbiddenTarget
Thrown when claimRewards targets a forbidden address.
error VaultAdapter__ForbiddenTarget();
ZapRouter
Inherits: FlashLoanBase, Ownable2Step, Pausable, ReentrancyGuard
Title: ZapRouter
Efficient router for acquiring plDXY-BULL tokens using flash mints.
Primary path: flash mints plDXY-BEAR → swaps to USDC via Curve → mints pairs → keeps plDXY-BULL. Fallback (direct) path: when BEAR is overpriced on Curve and the flash path can’t close, mints pairs with user USDC → sells all BEAR on Curve → sends BULL + USDC refund to user. For plDXY-BEAR, users should swap directly on Curve instead.
Note: security-contact: contact@plether.com
Constants
MAX_SLIPPAGE_BPS
Maximum allowed slippage in basis points (1% = 100 bps).
uint256 public constant MAX_SLIPPAGE_BPS = 100
SAFETY_BUFFER_BPS
Safety buffer for flash loan repayment calculations (0.5% = 50 bps).
uint256 public constant SAFETY_BUFFER_BPS = 50
USDC_INDEX
USDC index in the Curve USDC/plDXY-BEAR pool.
uint256 public constant USDC_INDEX = 0
PLDXY_BEAR_INDEX
plDXY-BEAR index in the Curve USDC/plDXY-BEAR pool.
uint256 public constant PLDXY_BEAR_INDEX = 1
SPLITTER
SyntheticSplitter contract for minting/burning pairs.
ISyntheticSplitter public immutable SPLITTER
PLDXY_BEAR
plDXY-BEAR token (flash minted for swaps).
IERC20 public immutable PLDXY_BEAR
PLDXY_BULL
plDXY-BULL token (output of zap operations).
IERC20 public immutable PLDXY_BULL
USDC
USDC stablecoin.
IERC20 public immutable USDC
CURVE_POOL
Curve pool for USDC/plDXY-BEAR swaps.
ICurvePool public immutable CURVE_POOL
CAP
Protocol CAP price (8 decimals, oracle format).
uint256 public immutable CAP
CAP_PRICE
CAP price scaled for Curve comparison (6 decimals).
uint256 public immutable CAP_PRICE
ACTION_MINT
Flash loan action: mint plDXY-BULL.
uint256 private constant ACTION_MINT = 0
ACTION_BURN
Flash loan action: burn plDXY-BULL.
uint256 private constant ACTION_BURN = 1
Functions
constructor
Deploys ZapRouter with required protocol dependencies.
constructor(
address _splitter,
address _plDxyBear,
address _plDxyBull,
address _usdc,
address _curvePool
) Ownable(msg.sender);
Parameters
| Name | Type | Description |
|---|---|---|
_splitter | address | SyntheticSplitter contract address. |
_plDxyBear | address | plDXY-BEAR token address. |
_plDxyBull | address | plDXY-BULL token address. |
_usdc | address | USDC token address. |
_curvePool | address | Curve USDC/plDXY-BEAR pool address. |
zapMint
Buy plDXY-BULL using USDC with flash mint efficiency.
For plDXY-BEAR, swap directly on Curve instead.
function zapMint(
uint256 usdcAmount,
uint256 minAmountOut,
uint256 maxSlippageBps,
uint256 deadline
) external nonReentrant whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
usdcAmount | uint256 | The amount of USDC the user is sending. |
minAmountOut | uint256 | Minimum amount of plDXY-BULL tokens to receive. |
maxSlippageBps | uint256 | Maximum slippage tolerance in basis points (e.g., 100 = 1%). Capped at MAX_SLIPPAGE_BPS (1%) to limit MEV extraction. |
deadline | uint256 | Unix timestamp after which the transaction reverts. |
zapMintWithPermit
Buy plDXY-BULL using USDC with a permit signature (gasless approval).
function zapMintWithPermit(
uint256 usdcAmount,
uint256 minAmountOut,
uint256 maxSlippageBps,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external nonReentrant whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
usdcAmount | uint256 | The amount of USDC the user is sending. |
minAmountOut | uint256 | Minimum amount of plDXY-BULL tokens to receive. |
maxSlippageBps | uint256 | Maximum slippage tolerance in basis points. |
deadline | uint256 | Unix timestamp after which the permit and transaction revert. |
v | uint8 | Signature recovery byte. |
r | bytes32 | Signature r component. |
s | bytes32 | Signature s component. |
_zapMintCore
function _zapMintCore(
uint256 usdcAmount,
uint256 minAmountOut,
uint256 maxSlippageBps,
uint256 deadline
) internal;
zapBurn
Sell plDXY-BULL tokens for USDC using flash mint efficiency.
function zapBurn(
uint256 bullAmount,
uint256 minUsdcOut,
uint256 deadline
) external nonReentrant whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
bullAmount | uint256 | Amount of plDXY-BULL to sell. |
minUsdcOut | uint256 | Minimum USDC to receive (slippage protection). |
deadline | uint256 | Unix timestamp after which the transaction reverts. |
zapBurnWithPermit
Sell plDXY-BULL tokens for USDC with a permit signature (gasless approval).
function zapBurnWithPermit(
uint256 bullAmount,
uint256 minUsdcOut,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external nonReentrant whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
bullAmount | uint256 | Amount of plDXY-BULL to sell. |
minUsdcOut | uint256 | Minimum USDC to receive (slippage protection). |
deadline | uint256 | Unix timestamp after which the transaction reverts. |
v | uint8 | Signature recovery byte. |
r | bytes32 | Signature r component. |
s | bytes32 | Signature s component. |
_zapBurnCore
function _zapBurnCore(
uint256 bullAmount,
uint256 minUsdcOut,
uint256 deadline
) internal;
onFlashLoan
ERC-3156 flash loan callback. Routes to mint or burn handler.
function onFlashLoan(
address initiator,
address,
uint256 amount,
uint256 fee,
bytes calldata data
) external override returns (bytes32);
Parameters
| Name | Type | Description |
|---|---|---|
initiator | address | Address that initiated the flash loan (must be this contract). |
<none> | address | |
amount | uint256 | Amount of plDXY-BEAR borrowed. |
fee | uint256 | Flash loan fee (always 0 for SyntheticToken). |
data | bytes | Encoded operation parameters. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bytes32 | CALLBACK_SUCCESS on successful execution. |
onMorphoFlashLoan
Morpho flash loan callback - not used by ZapRouter.
Always reverts as ZapRouter only uses ERC-3156 flash mints.
function onMorphoFlashLoan(
uint256,
bytes calldata
) external pure override;
_handleMint
Executes mint operation within flash loan callback.
function _handleMint(
uint256 loanAmount,
uint256 fee,
bytes calldata data
) internal;
Parameters
| Name | Type | Description |
|---|---|---|
loanAmount | uint256 | Amount of plDXY-BEAR borrowed. |
fee | uint256 | Flash loan fee (always 0). |
data | bytes | Encoded mint parameters (action, user, usdcAmount, minSwapOut, minAmountOut, maxSlippageBps). |
_handleBurn
Executes burn operation within flash loan callback.
function _handleBurn(
uint256 loanAmount,
uint256 fee,
bytes calldata data
) internal;
Parameters
| Name | Type | Description |
|---|---|---|
loanAmount | uint256 | Amount of plDXY-BEAR borrowed. |
fee | uint256 | Flash loan fee (always 0). |
data | bytes | Encoded burn parameters (action, user, bullAmount, minUsdcOut). |
_zapMintDirect
Mints pairs, sells BEAR on Curve, sends BULL + USDC refund to user.
Used when the flash path can’t close (BEAR overpriced or high price impact).
function _zapMintDirect(
uint256 usdcAmount,
uint256 minAmountOut,
uint256 maxSlippageBps
) private;
previewZapMint
Preview the result of a zapMint operation.
When flashAmount == 0, the direct path is used: expectedSwapOut is the USDC refund from selling minted BEAR, and totalUSDC equals usdcAmount (no flash leverage).
function previewZapMint(
uint256 usdcAmount
)
external
view
returns (
uint256 flashAmount,
uint256 expectedSwapOut,
uint256 totalUSDC,
uint256 expectedTokensOut,
uint256 flashFee
);
Parameters
| Name | Type | Description |
|---|---|---|
usdcAmount | uint256 | The amount of USDC the user will send. |
Returns
| Name | Type | Description |
|---|---|---|
flashAmount | uint256 | Amount of plDXY-BEAR to flash mint (0 signals direct path). |
expectedSwapOut | uint256 | Expected USDC from swap (flash: contributes to minting; direct: refund to user). |
totalUSDC | uint256 | Total USDC for minting pairs (flash: user + swap; direct: user only). |
expectedTokensOut | uint256 | Expected plDXY-BULL tokens to receive. |
flashFee | uint256 | Flash mint fee (0 for direct path). |
previewZapBurn
Preview the result of a zapBurn operation.
function previewZapBurn(
uint256 bullAmount
)
external
view
returns (uint256 expectedUsdcFromBurn, uint256 usdcForBearBuyback, uint256 expectedUsdcOut, uint256 flashFee);
Parameters
| Name | Type | Description |
|---|---|---|
bullAmount | uint256 | The amount of plDXY-BULL tokens to sell. |
Returns
| Name | Type | Description |
|---|---|---|
expectedUsdcFromBurn | uint256 | USDC received from burning pairs via Splitter. |
usdcForBearBuyback | uint256 | USDC needed to buy back plDXY-BEAR for flash loan repayment. |
expectedUsdcOut | uint256 | Net USDC the user will receive. |
flashFee | uint256 | Flash mint fee (if any). |
_estimateUsdcForBearBuyback
function _estimateUsdcForBearBuyback(
uint256 bearAmount
) private view returns (uint256);
pause
Pause the router. Blocks zapMint and zapBurn.
function pause() external onlyOwner;
unpause
Unpause the router.
function unpause() external onlyOwner;
Events
ZapMint
Emitted when user acquires plDXY-BULL via zapMint.
event ZapMint(
address indexed user,
uint256 usdcIn,
uint256 tokensOut,
uint256 maxSlippageBps,
uint256 actualSwapOut,
uint256 usdcRefund
);
ZapBurn
Emitted when user sells plDXY-BULL via zapBurn.
event ZapBurn(address indexed user, uint256 tokensIn, uint256 usdcOut);
Errors
ZapRouter__ZeroAddress
error ZapRouter__ZeroAddress();
ZapRouter__ZeroAmount
error ZapRouter__ZeroAmount();
ZapRouter__Expired
error ZapRouter__Expired();
ZapRouter__SlippageExceedsMax
error ZapRouter__SlippageExceedsMax();
ZapRouter__SplitterNotActive
error ZapRouter__SplitterNotActive();
ZapRouter__InsufficientOutput
error ZapRouter__InsufficientOutput();
ZapRouter__SolvencyBreach
error ZapRouter__SolvencyBreach();
ZapRouter__PermitFailed
error ZapRouter__PermitFailed();