Lunarys
Privacy First DEX using FHENIX.
The problem Lunarys solves
Financial Privacy on Public Blockchains
Today, every transaction on Ethereum is fully transparent. Anyone can see:
- Your wallet balance
- Who you're trading with
- How much you're swapping
- Your liquidity positions
This creates serious problems:
- Front-running & MEV attacks exploit visible pending transactions
- Competitive intelligence leaks expose trading strategies
- Personal security risks from publicly visible wealth
- Regulatory concerns for institutions handling client data
Lunarys DEX: Private DeFi with FHE
Lunarys is a confidential decentralized exchange powered by Fully Homomorphic Encryption (FHE). It enables:
| Feature | What It Does |
|---|---|
| Private Swaps | Trade encrypted tokens without revealing amounts or counterparties |
| Confidential Liquidity | Provide liquidity to pools where reserves stay encrypted |
| Hidden Balances | Your token holdings are encrypted on-chain—only you can decrypt them |
| Encrypted Positions | LP positions represented as NFTs with confidential data |
How It Makes DeFi Safer
Traditional DEX: Lunarys DEX:
┌─────────────────────┐ ┌─────────────────────┐
│ Swap 10,000 USDC │ │ Swap ████████ eUSDC │
│ for 9,200 EURC │ → │ for ████████ eEURC │
│ Wallet: 0x1234... │ │ Wallet: 0x1234... │
│ Balance: $50,000 │ │ Balance: ██████████ │
└─────────────────────┘ └─────────────────────┘
Everyone can see Only you can see
Key Benefits:
- Zero MEV Exposure — Bots can't front-run trades they can't see
- Institutional Ready — Trade without exposing client positions
- Personal Security — No one can target you based on visible wealth
- Compliance Compatible — Audit trails exist without public exposure
Use Cases
- Private Token Swaps — Convert between encrypted stablecoins (eUSDC ↔ eEURC) without revealing trade size
- Confidential Yield Farming — Earn APY on liquidity pools while keeping position sizes private
- Secure Portfolio Management — View and manage encrypted balances, transfer tokens privately
- Institutional Trading — Execute large trades without market impact from front-runners
Technology Stack
- FHE (Fhenix CoFHE) — Enables computation on encrypted data without decryption
- Encrypted Tokens — Encrypted token standard for confidential balances
- Constant Product AMM — Standard x*y=k formula with encrypted reserves
- Position NFTs — Liquidity positions as transferable encrypted NFTs
Challenges I ran into
The most frustrating issue we encountered was with balance decryption after swaps. After executing a successful swap transaction:
User swaps eUSDC → eEURC
Transaction confirmed ✅
Try to decrypt new eEURC balance... ❌ FAILS
Wait ~2 minutes...
Try again... ✅ Works perfectly
The Problem:
When a user receives encrypted tokens from a swap, attempting to decrypt the new balance immediately fails. The cofhejs.unseal() call either returns an error or stale data for approximately 2 minutes after
the transaction confirms.
What We Tried:
- Invalidating local FHE permit cache
- Re-fetching the encrypted balance handle
- Adding retry logic with exponential backoff
Current Workaround:
We implemented a polling mechanism with user feedback:
// Show "Balance updating..." state
// Retry decryption every 15 seconds
// After ~2 min, decryption succeeds
Uncertainty:
We're still not 100% sure if this is:
- A CoFHE SDK limitation — the SDK caches something that needs to invalidate
- On-chain propagation delay — FHE ciphertext needs a few blocks to be "readable"
- Fhenix network latency — decryption nodes need time to sync new encrypted state
This doesn't break functionality, but it impacts UX. Users see their swap succeed but can't immediately verify their new balance.
- Encrypted Arithmetic Precision
FHE operations on euint64 don't support decimals. Implementing the AMM's constant product formula with proper precision required careful handling:
// Problem: k = reserveA * reserveB can overflow euint64
// Solution: Use euint128 for intermediate calculations
euint128 k = FHE.mul(
FHE.asEuint128(reserveA),
FHE.asEuint128(reserveB)
);
Fee calculations with basis points (0.3% = 3000/1000000) needed RAY scaling (10^18) to maintain precision without decimals.
- Async Decryption Pattern
Unlike regular Solidity where you can read values synchronously, FHE decryption is asynchronous:
// Step 1: Request decryption (transaction 1)
FHE.decrypt(encryptedValue);
// Step 2: Wait for Fhenix network to process...
// Step 3: Retrieve result (transaction 2 or view call)
(uint256 result, bool ready) = FHE.getDecryptResultSafe(handle);
This required restructuring our entire flow for features like "mid-price oracle" — users must wait between requesting and receiving the price.
- Permission Management Complexity
FHE values require explicit permission grants. When transferring Position NFTs, we had to ensure the new owner gets FHE access:
function _update(address to, uint256 tokenId, address auth) internal override {
super._update(to, tokenId, auth);
// Grant FHE permissions to new owner Position storage pos = _positions[tokenId]; FHE.allow(pos.liquidity, to); FHE.allow(pos.token0Amount, to); FHE.allow(pos.token1Amount, to);
}
Forgetting a single FHE.allow() meant users couldn't access their own encrypted data.
Technologies used
Cheer Project
Cheering for a project means supporting a project you like with as little as 0.0025 ETH. Right now, you can Cheer using ETH on Arbitrum, Optimism and Base.
