Trade perps without revealing a thing — Open positions. Zero exposure
co-Perp is a privacy-first perpetual futures DEX built on Fhenix. Traders open long or short positions with up to 20x leverage against a liquidity pool — with take-profit and stop-loss orders, PnL tracking, and liquidation protection — but none of that data is ever visible on-chain.
Position size, direction, leverage, margin, entry price, PnL, and liquidation level are encrypted from the moment a position opens to the moment it settles. The protocol performs every computation it needs — PnL calculation, liquidation checks, funding rate aggregation — directly on encrypted data, without ever decrypting it mid-execution.
This is made possible by Fhenix CoFHE: a coprocessor for Fully Homomorphic Encryption. The smart contract never holds or processes plaintext values. It works exclusively with handles — opaque on-chain references to encrypted values living in the coprocessor. When the contract needs to compute PnL, it calls FHE.sub(markPrice, entryPrice). The coprocessor executes the encrypted subtraction off-chain. The result is written back on-chain as a new handle. The plaintext never surfaces.
Only the trader can decrypt their own position data, through a permit-gated flow processed by a Threshold Network where no single node ever holds the full decryption key.
On every existing perpetual DEX — GMX, dYdX, Hyperliquid, and others — all trader data is public. Position size, direction, leverage, entry price, and liquidation level are readable by anyone with an RPC connection. This creates three compounding problems:
Front-running. When a large order enters the mempool, bots read the size and direction and trade ahead of it. The trader fills at a worse price. The bot extracts the difference.
Liquidation hunting. Every open position has a calculable liquidation price. Market participants — and automated bots — monitor these levels and apply targeted price pressure to trigger liquidations, collecting the liquidation bonus at the expense of the position holder.
Copy-trading without consent. Sophisticated traders build alpha through research and risk management. On a transparent chain, anyone can replicate that strategy by watching on-chain activity in real time, eroding the edge of the original trader.
These are not edge cases — they are structural consequences of transparent blockchains. co-Perp removes them at the protocol level. Encrypting position data means there is nothing for a front-runner to read, no liquidation level to hunt, and no position to copy. The mempool sees an encrypted blob. The chain stores handles. The coprocessor computes on ciphertexts. At no point does the strategy become visible.
co-Perp operates across three layers. The trader's browser encrypts all inputs using the cofhejs SDK, generates ZK proofs validating input ranges, and handles decryption of the trader's own position data through a permit system. The smart contracts never hold plaintext — they work exclusively with handles, dispatching encrypted operations through FHE.sol to the Task Manager, which queues them for off-chain execution. The CoFHE coprocessor picks up those events, executes the actual homomorphic math on ciphertexts via the FHEOS Server, and writes encrypted results back on-chain.
Position state — size, direction, leverage, margin, PnL — lives entirely as encrypted handles. PnL is computed using unsigned double-branch arithmetic with FHE.select. Liquidations and TP/SL run as two-phase transactions: Phase 1 dispatches the encrypted comparison and stores the pending handle, Phase 2 is executed by a keeper bot after the coprocessor resolves it. Funding rate uses two persistent running OI handles updated on every open and close, keeping computation cost constant regardless of position count. Settlement triggers a decryptForTx flow through the Threshold Network, which produces a signed plaintext payout the contract verifies on-chain before releasing tokens. Everything else stays encrypted permanently.
TRADER (browser)
│
│ cofhejs SDK encrypts trade inputs locally using FHE public key
│ generates ZK proofs:
│ - size > minimum notional
│ - leverage between 1x and 20x
│ - direction is a valid enum value (0 or 1)
│ ZK Verifier checks proofs on-chain before contract accepts inputs
│ returns signed encrypted handles to the contract
│
│ sends tx with encrypted handles
│
▼
SMART CONTRACTS (Fhenix EVM)
│
│ co-Perp contracts work ONLY with handles — no plaintext position data
│
│ openPosition(encSize, encDirection, encLeverage):
│ FHE.asEuint128(encSize) → handle: positionSize
│ FHE.asEuint8(encDirection) → handle: direction (0=short, 1=long)
│ FHE.asEuint128(encLeverage) → handle: leverage
│ FHE.asEuint128(oraclePrice) → handle: entryPrice (plaintext oracle cast)
│ FHE.asEuint128(collateral) → handle: margin
│ FHE.allowThis(all handles) → contract retains access across txs
│ FHE.allow(all handles, trader) → trader can decrypt via Threshold Network
│ store all handles in Position struct
│ FHE.add(encLongOI, encSize) if long → update running long OI handle
│ FHE.add(encShortOI, encSize) if short → update running short OI handle
│
│ closePosition / computePnL:
│ (no signed integers — both branches computed as valid unsigned ops)
│
│ euint128 priceUp = FHE.sub(markPrice, entryPrice)
│ → profit magnitude if price increased
│ euint128 priceDown = FHE.sub(entryPrice, markPrice)
│ → profit magnitude if price decreased
│ ebool priceWentUp = FHE.lt(entryPrice, markPrice)
│ → encrypted: did price go up?
│
│ euint128 longPnl = FHE.select(priceWentUp, priceUp, priceDown)
│ → long profits when price goes up
│ euint128 shortPnl = FHE.select(priceWentUp, priceDown, priceUp)
│ → short profits when price goes down
│ ebool isLong = FHE.eq(direction, FHE.asEuint8(1))
│ euint128 rawPnl = FHE.select(isLong, longPnl, shortPnl)
│ → correct PnL magnitude for this position's direction
│ euint128 payout = FHE.add(margin, FHE.mul(rawPnl, positionSize))
│
│ FHE.sub(encLongOI, positionSize) or FHE.sub(encShortOI, positionSize)
│ → decrement running OI on close
│
│ liquidationCheck (two-phase — required by async coprocessor model):
│
│ Phase 1 — checkLiquidation(positionId):
│ euint128 maintenanceMargin = FHE.mul(entryPrice, maintenanceRatio)
│ ebool isLiquidatable = FHE.lt(currentMargin, maintenanceMargin)
│ FHE.allowThis(isLiquidatable)
│ store { positionId, isLiquidatable handle, block.number } as pending
│ emit LiquidationCheckPending(positionId)
│ → coprocessor begins resolving isLiquidatable handle off-chain
│
│ Phase 2 — settleLiquidation(positionId) [called by keeper after delay]:
│ load isLiquidatable handle from pending store
│ euint128 remaining = FHE.select(isLiquidatable, 0, currentMargin)
│ → wipes margin to 0 if liquidatable, else no-op
│ update position margin handle
│ clear pending state
│ caller never learns the margin value or whether liquidation executed
│
│ takeProfitStopLoss (same two-phase pattern as liquidation):
│
│ Phase 1 — on each oracle price update:
│ ebool hitSL = FHE.lt(markPrice, slPrice)
│ ebool hitTP = FHE.gt(markPrice, tpPrice)
│ store handles as pending
│ emit TPSLCheckPending(positionId)
│
│ Phase 2 — settleTPSL(positionId):
│ close position if either encrypted bool resolves true
│ same decryptForTx settlement flow as manual close
│
│ fundingRate (running OI totals — constant cost regardless of position count):
│ encLongOI and encShortOI are single handles maintained across all positions
│ ebool longsPayShorts = FHE.lt(encLongOI, encShortOI)
│ euint128 imbalance = FHE.select(longsPayShorts,
│ FHE.sub(encShortOI, encLongOI),
│ FHE.sub(encLongOI, encShortOI))
│ funding rate applied per interval to each position's encrypted margin handle
│
│ CoFHE on-chain contracts (deployed by Fhenix, called by FHE.sol):
│ Task Manager — validates ops, issues handles, emits events
│ ZK Verifier — checks client ZK proofs on input submission
│ ACL — enforces handle access permissions
│ CTRegistry — maps handles to resolved ciphertexts
│
▼
CoFHE COPROCESSOR (off-chain)
│
│ Slim Listener
│ monitors chain for Task Manager events
│ forwards FHE operation requests to FHEOS Server
│ ↓
│ FHEOS Server
│ executes actual FHE arithmetic on real ciphertexts:
│ FHE.sub, FHE.mul, FHE.add, FHE.lt, FHE.eq, FHE.select
│ resolves chained operation graphs in dependency order
│ (e.g. payout = add(margin, mul(pnl, size)) resolved leaf-first)
│ ↓
│ Result Processor
│ writes resolved ciphertext results to CTRegistry on-chain
│ handle is now populated — Phase 2 transactions can proceed
│
│ Threshold Network (decryption only — never touches computation)
│
│ decryptForView (trader viewing their own position):
│ trader submits permit with encrypted handle
│ → Threshold Network verifies ACL on-chain (trader has allow permission)
│ → multi-party decryption: no single node sees plaintext
│ → decrypted value returned to trader's browser via cofhejs SDK
│ → position size, leverage, PnL visible only to the trader
│
│ decryptForTx (settlement — closing position, receiving payout):
│ same verification and MPC decryption flow
│ → additionally produces ECDSA signature over decrypted payout amount
│ → contract verifies signature on-chain before releasing tokens
│ → payout amount becomes public at this point
│ → all position details that produced it remain encrypted permanently
co-perp/
│
├── contracts/ # All Solidity smart contracts
│ │
│ ├── core/
│ │ ├── CoPerpVault.sol # Encrypted collateral deposits and withdrawals
│ │ │ # Accepts euint128 handles from cofhejs SDK
│ │ │ # Triggers decryptForTx on withdrawal for token release
│ │ │
│ │ ├── CoPerpEngine.sol # Main trading engine — openPosition, closePosition
│ │ │ # Stores Position structs containing only handles
│ │ │ # Dispatches FHE.sub/mul/eq/select for PnL computation
│ │ │ # Calls ACL: allowThis + allow(trader) on all handles
│ │ │ # Updates running encLongOI / encShortOI on every open/close
│ │ │
│ │ ├── CoPerpLiquidation.sol # Two-phase liquidation logic
│ │ │ # Phase 1: checkLiquidation — dispatches FHE.lt(margin, threshold)
│ │ │ # stores pending { positionId, handle, block } on-chain
│ │ │ # emits LiquidationCheckPending event
│ │ │ # Phase 2: settleLiquidation — called by keeper after delay
│ │ │ # reads resolved handle, applies FHE.select to wipe margin
│ │ │
│ │ ├── CoPerpTPSL.sol # Two-phase take-profit and stop-loss logic
│ │ │ # Mirrors liquidation two-phase pattern
│ │ │ # Phase 1: dispatches FHE.lt(mark, sl) and FHE.gt(mark, tp)
│ │ │ # Phase 2: settleTPSL — keeper closes position if bool resolves true
│ │ │
│ │ └── CoPerpFunding.sol # Periodic funding rate computation
│ │ # Reads encLongOI and encShortOI handles from Engine
│ │ # FHE.lt to determine which side pays
│ │ # FHE.select + FHE.sub to get encrypted imbalance
│ │ # Applies funding payment to each position margin handle
│ │
│ ├── pool/
│ │ └── LiquidityPool.sol # LP collateral pool — counterparty to all trades
│ │ # Tracks aggregate pool exposure in plaintext (public)
│ │ # Receives liquidation proceeds and losing position payouts
│ │ # Pays out winning position settlements
│ │
│ ├── oracle/
│ │ └── OracleAdapter.sol # Plaintext price feed adapter (Chainlink / Pyth)
│ │ # Fetches uint256 mark price
│ │ # Exposes FHE.asEuint128(price) cast for Engine consumption
│ │ # Only plaintext data source in the entire system
│ │
│ ├── interfaces/
│ │ ├── ICoPerpEngine.sol # Engine interface — openPosition, closePosition signatures
│ │ ├── ICoPerpLiquidation.sol # Liquidation interface — check and settle signatures
│ │ ├── ICoPerpFunding.sol # Funding interface — applyFunding signature
│ │ └── IOracle.sol # Oracle interface — getPrice signature
│ │
│ └── libraries/
│ ├── FHEPositionLib.sol # Shared Position struct definition
│ │ # Fields: euint128 size, euint8 direction, euint128 leverage
│ │ # euint128 entryPrice, euint128 margin
│ │ # euint128 pnlHandle, bool pendingLiquidation
│ │
│ └── FHEMathLib.sol # Reusable unsigned PnL arithmetic helpers
│ # computePnL(entryPrice, markPrice, direction, size)
│ # Uses double-branch unsigned pattern with FHE.select
│ # Handles euint128 upcasting at all mul boundaries
│
├── keepers/ # Off-chain keeper bot infrastructure
│ │
│ ├── LiquidationKeeper.ts # Watches LiquidationCheckPending events on-chain
│ │ # Waits for coprocessor to resolve handle (polls CTRegistry)
│ │ # Calls settleLiquidation(positionId) on Phase 2
│ │
│ ├── TPSLKeeper.ts # Watches TPSLCheckPending events on-chain
│ │ # Same resolution pattern as LiquidationKeeper
│ │ # Calls settleTPSL(positionId) after handle resolves
│ │
│ └── FundingKeeper.ts # Triggers periodic funding rate application
│ # Calls CoPerpFunding.applyFunding() on interval
│ # Monitors encLongOI vs encShortOI handle state
│
├── frontend/ # React trading interface
│ │
│ ├── src/
│ │ │
│ │ ├── sdk/
│ │ │ └── cofhe.ts # cofhejs SDK wrapper
│ │ │ # encryptInput(value) — encrypts trade inputs locally
│ │ │ # generateZKProof(value, range) — proves input validity
│ │ │ # requestPermit(handle) — gets time-bound decrypt permit
│ │ │ # decryptForView(handle, permit) — decrypts position data locally
│ │ │
│ │ ├── hooks/
│ │ │ ├── usePosition.ts # Fetches and decrypts trader's open position handles
│ │ │ │ # Calls decryptForView, returns plaintext to UI only
│ │ │ │
│ │ │ ├── useOpenPosition.ts # Encrypts inputs, generates ZK proofs, submits openPosition tx
│ │ │ │
│ │ │ ├── useClosePosition.ts # Triggers closePosition tx, handles decryptForTx settlement flow
│ │ │ │
│ │ │ └── usePermit.ts # Manages permit lifecycle — requests, caches, refreshes on expiry
│ │ │
│ │ ├── components/
│ │ │ ├── TradePanel.tsx # Size, direction, leverage input form
│ │ │ │ # Encrypts all inputs before they leave the browser
│ │ │ │
│ │ │ ├── PositionCard.tsx # Displays decrypted position: size, PnL, margin, liq estimate
│ │ │ │ # All values decrypted locally via decryptForView
│ │ │ │
│ │ │ ├── VaultPanel.tsx # Deposit and withdraw collateral UI
│ │ │ │
│ │ │ └── PriceChart.tsx # Plaintext oracle price feed display (public data)
│ │ │
│ │ └── config/
│ │ └── contracts.ts # Contract addresses, ABIs, network config
│ │
│ └── public/
│
├── scripts/ # Hardhat deployment and setup scripts
│ ├── deploy.ts # Deploys all contracts in dependency order
│ │ # Vault, Engine, Liquidation, TPSL, Funding, Pool, Oracle
│ │
│ └── setup.ts # Seeds LiquidityPool, sets oracle adapter address
│
├── test/ # Hardhat tests using Fhenix mock CoFHE contracts
│ ├── vault.test.ts # Tests deposit, withdrawal, decryptForTx flow
│ ├── engine.test.ts # Tests openPosition, closePosition, unsigned PnL correctness
│ ├── liquidation.test.ts # Tests two-phase liquidation, keeper settlement
│ ├── tpsl.test.ts # Tests TP and SL two-phase execution
│ └── funding.test.ts # Tests running OI update and funding payment application
│
├── hardhat.config.ts # Hardhat config with Fhenix plugin and network settings
├── package.json
└── README.md
Step 1 — Encrypted Vault
Implement deposit and withdrawal of collateral. Deposits are encrypted by cofhejs SDK with ZK proof in the browser, stored as euint128 handles on-chain, computed by the FHEOS Server. Withdrawal triggers decryptForTx through the Threshold Network for token release. This proves the full CoFHE pipeline end-to-end — ZK proof → encrypt → handle → coprocessor compute → Threshold Network decryption → on-chain verification → token transfer — before any trading logic is introduced.
Step 2 — Position Open and Close with Correct Unsigned PnL
Implement openPosition and closePosition with encrypted size, direction, leverage, and entry price. PnL uses the double-branch unsigned pattern: compute both the profit branch and the loss branch as valid unsigned subtractions, select between them using encrypted direction and price movement booleans. Settlement via decryptForTx. Running OI handles updated on every open and close.
Step 3 — Two-Phase Liquidation Engine
Implement checkLiquidation (Phase 1: dispatch margin comparison, store pending handle, emit event) and settleLiquidation (Phase 2: read resolved handle, apply FHE.select to wipe or preserve margin). Deploy keeper infrastructure to monitor LiquidationCheckPending events and call settleLiquidation after coprocessor resolution.
Step 4 — TP/SL as Position-Local Two-Phase Checks Implement take-profit and stop-loss using the same two-phase pattern. Trader submits encrypted TP and SL price handles at position open. On oracle price updates, dispatch encrypted comparisons. Keeper bots call settle phase to close positions if either check resolves true.
Step 5 — Funding Rate via Running OI Totals
Implement periodic funding rate computation using the maintained encLongOI and encShortOI handles. Apply encrypted funding payments to each position's margin handle per interval. Funding computation cost is constant — two handles — regardless of the number of open positions.
Step 6 — Frontend
Build the trading interface using cofhejs SDK. Encrypt inputs and generate ZK proofs in the browser before submission. Handle permit expiry gracefully — re-request permits before calling decryptForView if expired. Display decrypted position data — size, leverage, PnL, margin, liquidation estimate — locally in the trader's browser. No position data is sent to any server.
Fhenix CoFHE The core infrastructure. CoFHE is not on-chain FHE execution. The smart contract does not perform homomorphic computation. Instead, FHE.sol calls dispatch operation requests to an off-chain coprocessor (the FHEOS Server) that executes encrypted math on real ciphertexts and writes results back on-chain as new handles. The contract only ever touches handles.
The six CoFHE components co-Perp relies on:
-
cofhejs SDK — TypeScript library running in the trader's browser. Encrypts trade inputs locally using the FHE public key, generates ZK proofs proving inputs are valid and in range, manages time-bound permits for decryption, and runs the
decryptForViewflow for the trading UI. -
Task Manager — on-chain contract deployed by Fhenix. The gateway for every FHE operation request. Validates requests, checks ACL permissions, generates a result handle, and emits an event for the off-chain infrastructure. Returns the handle synchronously so the contract can chain multiple operations within a single transaction.
-
ZK Verifier — on-chain contract. Verifies ZK proofs attached to client-submitted encrypted inputs before the Task Manager accepts them. The enforcement layer ensuring that encrypted values entering the system are well-formed and within protocol-defined ranges.
-
FHEOS Server — off-chain. Performs the actual FHE computation on ciphertexts. Receives operation events from the Slim Listener, executes the encrypted arithmetic and comparisons, resolves chained operation graphs in dependency order, and passes results to the Result Processor.
-
Threshold Network — off-chain distributed network. Handles all decryption through multi-party computation. No single node ever holds or sees the plaintext. For
decryptForView, verifies ACL permission on-chain and returns decrypted data to the requesting trader. FordecryptForTx, additionally produces an ECDSA signature the contract verifies before releasing tokens. -
ACL (Access Control Layer) — on-chain contract. Tracks which addresses have read permission over which encrypted handles.
FHE.allow(handle, traderAddress)grants a trader decryption access.FHE.allowThis(handle)grants the contract itself permission to use a handle in future transactions. WithoutallowThis, handles cannot be referenced across transaction boundaries. -
Ciphertext Registry (CTRegistry) — on-chain contract. Maps handles to the computed ciphertexts produced by the FHEOS Server. The on-chain source of truth for all encrypted state.
Solidity + FHE.sol
Contracts are standard Solidity. FHE.sol provides the encrypted operation API: FHE.add(), FHE.sub(), FHE.mul(), FHE.lt(), FHE.eq(), FHE.select(), and ACL management functions. These calls do not execute FHE math — they dispatch to the Task Manager and return handles.
Hardhat + Fhenix Hardhat Plugin Development and local testing environment. The plugin deploys mock CoFHE contracts locally, simulating FHE operations in plaintext for fast iteration without requiring the real coprocessor.
Chainlink / Pyth Network
Plaintext price oracle feeds. Oracle prices are the only plaintext data in the system. They enter the contract as standard uint256 values and are cast into the encrypted domain via FHE.asEuint128(price) to participate in encrypted computations alongside position data. Entry price confidentiality is weaker than other position fields — the oracle price at the block a position opens is public — but size, direction, leverage, and margin remain fully private.
React + cofhejs SDK Frontend trading interface. The SDK handles client-side encryption, ZK proof generation, permit lifecycle management, and the decryption flow for displaying position data to the trader.