Skip to content

Plether-Fi/plether-core

Repository files navigation

Plether Protocol

CI Coverage License: AGPL v3 Solidity

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.

How It Works

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).

Deposit Flow

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.

Withdraw Flow

LP deposit: Advanced path for depositing USDC + BEAR directly into Curve LP.

LP Deposit Flow

LP withdraw: Balanced exit returning pro-rata USDC + BEAR. lpWithdraw intentionally works when paused, serving as the emergency exit.

LP Withdraw Flow

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.

Keeper operations:

  • deployToCurve() — pushes excess USDC buffer (>2% target) into single-sided Curve LP
  • replenishBuffer() — burns Curve LP to restore the 2% USDC buffer
  • harvest() — 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
  • _harvestSafe() wraps all Curve + oracle calls in try/catch to guarantee withdrawal liveness
  • setEmergencyMode() resets LP tracking without touching Curve, enabling exits even if the pool is fully bricked
  • L2 sequencer uptime validation on all oracle reads including harvest

Ecosystem Integrations

Token Flow

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

Bear Leverage

Flash loan USDC from Morpho → swap to plDXY-BEAR on Curve → stake → deposit splDXY-BEAR as Morpho collateral.

Bull Leverage

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

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:

  1. LeverageRouter (Bear): Morpho flash loan USDC → Swap to plDXY-BEAR → Stake → Deposit to Morpho as collateral → Borrow USDC to repay flash loan
  2. 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:

  1. 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.
  2. PAUSED - Emergency pause (minting and reward distribution blocked, burning allowed so users can exit, gradual adapter withdrawal enabled)
  3. 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:

  1. Import a test private key (Settings → Import Account)
  2. Add network: RPC http://127.0.0.1:8545, Chain ID 31337

Format

forge fmt               # Format code
forge fmt --check       # Check formatting

Documentation

View Reference 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

AGPL-3.0

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.

About

Plether protocol

Resources

License

Security policy

Stars

Watchers

Forks

Contributors

Languages