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
}