mirror of
https://github.com/luxfi/precompile.git
synced 2026-09-24 00:26:21 +00:00
315 lines
14 KiB
Solidity
315 lines
14 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.24;
|
|
|
|
import {Currency} from "./Types.sol";
|
|
|
|
/// @title ILXVault (LP-9030)
|
|
/// @notice Clearinghouse interface for LX - custody, margin, positions, liquidations
|
|
/// @dev Precompile address: LP-9030 (0x0000000000000000000000000000000000009030)
|
|
/// @dev This is the ONLY contract that mutates user balances and positions
|
|
/// @dev LXBook calls applyFills() to settle trades atomically
|
|
interface ILXVault {
|
|
// =========================================================================
|
|
// Custom Types
|
|
// =========================================================================
|
|
|
|
/// @notice Market identifier (perp markets, spot pairs, etc.)
|
|
type MarketId is uint32;
|
|
|
|
/// @notice Account margin mode
|
|
enum MarginMode {
|
|
ISOLATED, // Each position has isolated margin
|
|
CROSS, // All positions share margin (default)
|
|
PORTFOLIO // Portfolio margin with cross-asset netting
|
|
}
|
|
|
|
/// @notice Position side
|
|
enum Side {
|
|
LONG,
|
|
SHORT
|
|
}
|
|
|
|
// =========================================================================
|
|
// Structs - Settlement
|
|
// =========================================================================
|
|
|
|
/// @notice Fill from LXBook to be applied to accounts
|
|
/// @dev All amounts are X18 (18 decimal fixed point)
|
|
struct Fill {
|
|
MarketId marketId; // Market this fill belongs to
|
|
address maker; // Maker address
|
|
address taker; // Taker address
|
|
bool takerIsBuy; // True if taker is buying (going long)
|
|
uint128 pxX18; // Execution price (X18)
|
|
uint128 szX18; // Size in base asset (X18)
|
|
uint128 makerFeeX18; // Maker fee (negative = rebate)
|
|
uint128 takerFeeX18; // Taker fee
|
|
uint8 flags; // Bit flags: 0x01=liquidation, 0x02=reduce-only, 0x04=ADL
|
|
}
|
|
|
|
/// @notice Result of applying fills
|
|
struct ApplyResult {
|
|
uint128 appliedFills; // Number of fills successfully applied
|
|
uint128 appliedSzX18; // Total size applied
|
|
uint8 status; // 0=rejected, 1=ok, 2=partial
|
|
}
|
|
|
|
// =========================================================================
|
|
// Structs - Account State
|
|
// =========================================================================
|
|
|
|
/// @notice User's margin account state
|
|
struct AccountState {
|
|
int256 equityX18; // Total equity (collateral + unrealized PnL)
|
|
uint256 totalCollateralX18; // Total collateral value
|
|
int256 unrealizedPnlX18; // Unrealized PnL across all positions
|
|
uint256 maintenanceReqX18; // Maintenance margin requirement
|
|
uint256 initialReqX18; // Initial margin requirement
|
|
MarginMode mode; // Current margin mode
|
|
bool isLiquidatable; // True if below maintenance margin
|
|
}
|
|
|
|
/// @notice Position in a specific market
|
|
struct Position {
|
|
MarketId marketId; // Market ID
|
|
Side side; // LONG or SHORT
|
|
uint128 sizeX18; // Position size (X18)
|
|
uint128 avgEntryPxX18; // Average entry price (X18)
|
|
int128 unrealizedPnlX18;// Current unrealized PnL (X18)
|
|
uint128 marginX18; // Margin allocated (isolated mode)
|
|
int128 fundingAccrued; // Accumulated funding (X18)
|
|
}
|
|
|
|
/// @notice Market configuration for risk parameters
|
|
struct MarketConfig {
|
|
MarketId marketId; // Market ID
|
|
uint128 maxLeverageX18; // Max leverage (e.g., 50e18 = 50x)
|
|
uint128 maintenanceMarginX18; // Maintenance margin ratio (e.g., 0.03e18 = 3%)
|
|
uint128 initialMarginX18; // Initial margin ratio (e.g., 0.05e18 = 5%)
|
|
uint128 liquidationFeeX18; // Liquidation penalty (e.g., 0.025e18 = 2.5%)
|
|
uint128 maxPositionSzX18; // Max position size per account
|
|
uint128 maxOIX18; // Max open interest for market
|
|
bool active; // Market is active
|
|
}
|
|
|
|
// =========================================================================
|
|
// Errors
|
|
// =========================================================================
|
|
|
|
error NotBook(); // Caller is not LXBook
|
|
error NotOracle(); // Caller is not LXOracle/LXFeed
|
|
error RiskRejected(); // Risk check failed
|
|
error InvalidFill(); // Fill parameters invalid
|
|
error InsufficientMargin(); // Not enough margin
|
|
error InsufficientBalance(); // Not enough balance
|
|
error MarketNotActive(); // Market is paused or disabled
|
|
error PositionNotFound(); // No position exists
|
|
error NotLiquidatable(); // Account above maintenance margin
|
|
error MaxOIExceeded(); // Would exceed max open interest
|
|
error WithdrawalLocked(); // Withdrawal cooldown active
|
|
|
|
// =========================================================================
|
|
// Events
|
|
// =========================================================================
|
|
|
|
event Deposit(address indexed user, Currency indexed currency, uint256 amount);
|
|
event Withdraw(address indexed user, Currency indexed currency, uint256 amount);
|
|
event FillApplied(
|
|
MarketId indexed marketId,
|
|
address indexed maker,
|
|
address indexed taker,
|
|
uint128 pxX18,
|
|
uint128 szX18,
|
|
bool takerIsBuy
|
|
);
|
|
event PositionOpened(address indexed user, MarketId indexed marketId, Side side, uint128 szX18, uint128 pxX18);
|
|
event PositionClosed(address indexed user, MarketId indexed marketId, int256 realizedPnlX18);
|
|
event PositionModified(address indexed user, MarketId indexed marketId, uint128 newSzX18, uint128 avgPxX18);
|
|
event Liquidation(address indexed user, MarketId indexed marketId, address indexed liquidator, uint128 szX18);
|
|
event ADLExecuted(MarketId indexed marketId, address indexed winner, address indexed loser, uint128 szX18);
|
|
event FundingAccrued(MarketId indexed marketId, int256 fundingRateX18, uint256 timestamp);
|
|
|
|
// =========================================================================
|
|
// Settlement Interface (called by LXBook only)
|
|
// =========================================================================
|
|
|
|
/// @notice Apply a batch of fills atomically from LXBook
|
|
/// @dev ONLY callable by LXBook (LP-9020)
|
|
/// @dev Validates risk, updates positions, applies fees
|
|
/// @param fills Array of fills to apply
|
|
/// @return result Summary of applied fills
|
|
function applyFills(Fill[] calldata fills) external returns (ApplyResult memory result);
|
|
|
|
/// @notice Pre-check if fills would be accepted (for order validation)
|
|
/// @dev Does NOT modify state - used for pre-trade risk checks
|
|
/// @param fills Array of fills to validate
|
|
/// @return valid True if all fills would be accepted
|
|
/// @return reason Rejection reason if not valid
|
|
function preCheckFills(Fill[] calldata fills) external view returns (bool valid, string memory reason);
|
|
|
|
// =========================================================================
|
|
// Custody Interface (Spot Balances)
|
|
// =========================================================================
|
|
|
|
/// @notice Deposit collateral into vault
|
|
/// @param currency Token to deposit (address(0) for native LUX)
|
|
/// @param amount Amount to deposit
|
|
function deposit(Currency currency, uint256 amount) external payable;
|
|
|
|
/// @notice Withdraw collateral from vault
|
|
/// @dev Subject to margin requirements and withdrawal cooldown
|
|
/// @param currency Token to withdraw
|
|
/// @param amount Amount to withdraw
|
|
/// @param to Recipient address
|
|
function withdraw(Currency currency, uint256 amount, address to) external;
|
|
|
|
/// @notice Get user's balance of a specific currency
|
|
/// @param user Account to query
|
|
/// @param currency Token address
|
|
/// @return balance Available balance (not locked in margin)
|
|
function balanceOf(address user, Currency currency) external view returns (uint256 balance);
|
|
|
|
/// @notice Get user's total collateral value in USD
|
|
/// @param user Account to query
|
|
/// @return valueX18 Total collateral value (X18)
|
|
function totalCollateralValue(address user) external view returns (uint256 valueX18);
|
|
|
|
// =========================================================================
|
|
// Margin Account Interface
|
|
// =========================================================================
|
|
|
|
/// @notice Get full account state
|
|
/// @param user Account to query
|
|
/// @return state Complete account state
|
|
function getAccountState(address user) external view returns (AccountState memory state);
|
|
|
|
/// @notice Get account equity (collateral + unrealized PnL)
|
|
/// @param user Account to query
|
|
/// @return equityX18 Account equity (X18, can be negative)
|
|
function accountEquityX18(address user) external view returns (int256 equityX18);
|
|
|
|
/// @notice Get maintenance margin requirement
|
|
/// @param user Account to query
|
|
/// @return reqX18 Maintenance margin requirement (X18)
|
|
function maintenanceMarginReqX18(address user) external view returns (uint256 reqX18);
|
|
|
|
/// @notice Get initial margin requirement
|
|
/// @param user Account to query
|
|
/// @return reqX18 Initial margin requirement (X18)
|
|
function initialMarginReqX18(address user) external view returns (uint256 reqX18);
|
|
|
|
/// @notice Get margin ratio (equity / maintenance requirement)
|
|
/// @param user Account to query
|
|
/// @return ratioX18 Margin ratio (X18), <1.0 = liquidatable
|
|
function marginRatioX18(address user) external view returns (uint256 ratioX18);
|
|
|
|
/// @notice Set margin mode for account
|
|
/// @param mode New margin mode
|
|
function setMarginMode(MarginMode mode) external;
|
|
|
|
// =========================================================================
|
|
// Position Interface
|
|
// =========================================================================
|
|
|
|
/// @notice Get user's position in a market
|
|
/// @param user Account to query
|
|
/// @param marketId Market ID
|
|
/// @return position Position details (zero if no position)
|
|
function getPosition(address user, MarketId marketId) external view returns (Position memory position);
|
|
|
|
/// @notice Get all positions for a user
|
|
/// @param user Account to query
|
|
/// @return positions Array of all positions
|
|
function getAllPositions(address user) external view returns (Position[] memory positions);
|
|
|
|
/// @notice Add margin to isolated position
|
|
/// @param marketId Market ID
|
|
/// @param amount Additional margin amount
|
|
function addMargin(MarketId marketId, uint256 amount) external;
|
|
|
|
/// @notice Remove margin from isolated position (if excess)
|
|
/// @param marketId Market ID
|
|
/// @param amount Margin to remove
|
|
function removeMargin(MarketId marketId, uint256 amount) external;
|
|
|
|
// =========================================================================
|
|
// Liquidation Interface
|
|
// =========================================================================
|
|
|
|
/// @notice Check if account is liquidatable
|
|
/// @param user Account to check
|
|
/// @return liquidatable True if below maintenance margin
|
|
function isLiquidatable(address user) external view returns (bool liquidatable);
|
|
|
|
/// @notice Liquidate an undercollateralized position
|
|
/// @dev Anyone can call - liquidator receives bonus
|
|
/// @param user Account to liquidate
|
|
/// @param marketId Market to liquidate
|
|
/// @param maxSzX18 Maximum size to liquidate
|
|
/// @return liqSzX18 Actual size liquidated
|
|
function liquidate(address user, MarketId marketId, uint128 maxSzX18) external returns (uint128 liqSzX18);
|
|
|
|
/// @notice Run Auto-Deleveraging for a market
|
|
/// @dev Called when insurance fund insufficient - socializes losses
|
|
/// @param marketId Market to ADL
|
|
/// @param maxAccounts Maximum accounts to process
|
|
function runADL(MarketId marketId, uint32 maxAccounts) external;
|
|
|
|
/// @notice Get insurance fund balance
|
|
/// @return balance Insurance fund balance (X18)
|
|
function insuranceFundBalance() external view returns (uint256 balance);
|
|
|
|
// =========================================================================
|
|
// Funding Interface
|
|
// =========================================================================
|
|
|
|
/// @notice Accrue funding for a market
|
|
/// @dev Called periodically (e.g., every 8 hours)
|
|
/// @param marketId Market to accrue funding for
|
|
function accrueFunding(MarketId marketId) external;
|
|
|
|
/// @notice Get current funding rate for a market
|
|
/// @param marketId Market ID
|
|
/// @return rateX18 Funding rate (X18, positive = longs pay shorts)
|
|
function fundingRateX18(MarketId marketId) external view returns (int256 rateX18);
|
|
|
|
/// @notice Get next funding time
|
|
/// @param marketId Market ID
|
|
/// @return timestamp Next funding timestamp
|
|
function nextFundingTime(MarketId marketId) external view returns (uint256 timestamp);
|
|
|
|
// =========================================================================
|
|
// Market Configuration
|
|
// =========================================================================
|
|
|
|
/// @notice Get market configuration
|
|
/// @param marketId Market ID
|
|
/// @return config Market configuration
|
|
function getMarketConfig(MarketId marketId) external view returns (MarketConfig memory config);
|
|
|
|
/// @notice Get current open interest for a market
|
|
/// @param marketId Market ID
|
|
/// @return longOIX18 Long open interest
|
|
/// @return shortOIX18 Short open interest
|
|
function openInterest(MarketId marketId) external view returns (uint128 longOIX18, uint128 shortOIX18);
|
|
}
|
|
|
|
// =========================================================================
|
|
// Fill Flags (for Fill.flags field)
|
|
// =========================================================================
|
|
|
|
/// @notice Fill is a liquidation fill
|
|
uint8 constant FILL_FLAG_LIQUIDATION = 0x01;
|
|
|
|
/// @notice Fill is reduce-only (cannot increase position)
|
|
uint8 constant FILL_FLAG_REDUCE_ONLY = 0x02;
|
|
|
|
/// @notice Fill is from ADL (auto-deleverage)
|
|
uint8 constant FILL_FLAG_ADL = 0x04;
|
|
|
|
/// @notice Fill is from a market order
|
|
uint8 constant FILL_FLAG_MARKET = 0x08;
|
|
|
|
/// @notice Fill is post-only violation (should reject)
|
|
uint8 constant FILL_FLAG_POST_ONLY_FAIL = 0x10;
|