A fully on-chain perpetuals exchange with zero off-chain dependencies.
Chainlink price oracles · ERC-4337 gasless smart accounts · CCIP cross-chain margin · 50× leverage.
💻 Source Code · 📜 Docs · 🔗 PositionManager · 🚰 Get Testnet USDC
Most DeFi perp protocols rely on off-chain matching engines, centralized price feeds, or custodial bridges. Nexus Perps eliminates every one of these dependencies — every price fetch, every liquidation, every settlement runs entirely on-chain with no external coordination layer.
| Problem With Existing Protocols | Nexus Solution |
|---|---|
| Oracle manipulation via thin markets | Chainlink aggregators with per-asset heartbeat staleness guards |
| Gas costs kill small traders | ERC-4337 smart accounts + NexusPaymaster sponsors 100% of gas |
| Liquidity fragmented across chains | Chainlink CCIP cross-chain margin relay with nonce replay protection |
| Custodial bridges introduce counterparty risk | All collateral lives in PerpsVault.sol — non-custodial, on-chain |
| LP inflation attacks on first deposit | MINIMUM_LIQUIDITY = 1000 shares permanently burned on genesis deposit |
| Dust sweep / precision drain | scaledAmount % DECIMALS_SCALAR != 0 enforced on every withdrawal |
- 🏛️ Architecture
- ✅ Deployed Contracts
- 🧩 Contract Reference
- 💻 Frontend Stack
- 🧪 Test Suite & Coverage
- 🛠️ Local Setup
- 🔐 Security Model
Five isolated protocol layers. A failure in cross-chain routing cannot affect vault solvency. The oracle layer is fully stateless with zero write access to core contracts.
┌──────────────────────────────────────────────────────────────────┐
│ USER / DAPP │
│ RainbowKit · Wagmi v2 · Viem · Next.js 14 App Router │
└───────────────────────────┬──────────────────────────────────────┘
│
┌───────────────────────────▼──────────────────────────────────────┐
│ ACCOUNT ABSTRACTION (ERC-4337) │
│ SmartAccount.sol ── AccountFactory.sol ── NexusPaymaster.sol │
│ Gasless UserOps · EIP-712 signing · CREATE2 deterministic AA │
└───────────────────────────┬──────────────────────────────────────┘
│
┌───────────────────────────▼──────────────────────────────────────┐
│ TRADING ENGINE │
│ PositionManager.sol · PnLCalculator.sol · LiquidationEngine│
│ Market & limit orders · Isolated/Cross margin · Batch keepers │
└────────────┬─────────────────────────────────┬───────────────────┘
│ │
┌────────────▼────────────┐ ┌────────────▼────────────────── ┐
│ VAULT LAYER │ │ ORACLE LAYER │
│ PerpsVault.sol │ │ PriceOracle.sol │
│ 18-dec precision │ │ Chainlink BTC/USD + ETH/USD │
│ LP share system │ │ Heartbeat staleness guard │
│ settleTrade / PnL │ │ Normalized to 1e18 │
└─────────────────────────┘ └─────────────────────────────────┘
│
┌────────────▼────────────────────────────────────────────────────┐
│ CROSS-CHAIN LAYER (CCIP) │
│ CrossChainRouter.sol ── encodes & sends trade requests │
│ MessageReceiver.sol ── decodes, deduplicates nonce, executes │
│ Source chain + sender whitelist · try/catch pipeline safety │
└─────────────────────────────────────────────────────────────────┘
1. No off-chain trust — Price discovery, execution, liquidation, settlement — all fully on-chain.
2. 18-decimal precision throughout — DECIMALS_SCALAR = 10^(18 - tokenDecimals) normalizes USDC (6 dec) to 1e18 internally, eliminating dust-sweep and rounding bugs.
3. Vault solvency is an invariant — 128 runs × 50 calls = 6,400 randomized state mutations, zero reverts against totalLiquidity + totalLockedCollateral + totalTraderFreeCollateral == ASSET.balanceOf(vault).
4. Isolated margin by default — Cross-margin mode uses _calculateGlobalPnL iterating all active positions for holistic equity checks before liquidation.
All contracts deployed on Ethereum Sepolia and verified on Etherscan.
| Contract | Address | Etherscan |
|---|---|---|
| PositionManager | 0x6952144C5dfb64DF54a64b61B3321Fd2C24cB42A |
↗ |
| PerpsVault | 0x891FBf3C860333FB05f3f80526C3a1919de2d83c |
↗ |
| LiquidationEngine | 0xEE17eAF240c6b7C566E7431088FfC99551472669 |
↗ |
| PriceOracle | 0x4Ca4A6fa3763b1AE2F3a09B17189152a608920f5 |
↗ |
| Contract | Address | Etherscan |
|---|---|---|
| SmartAccount (Impl) | 0x1e821F5796bc833FE020c05007f84dF040878d81 |
↗ |
| AccountFactory | 0xb6445BF0F856FDF2Fd261A5c32409d226D134221 |
↗ |
| NexusPaymaster | 0x20e302881494F79eF5E536d5533be04F913eE652 |
↗ |
| EntryPoint (Standard) | 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789 |
↗ |
| Contract | Address | Etherscan |
|---|---|---|
| CrossChainRouter | 0xE9b7f8F6c78054fb8d0D97585F32e7e026F5dd24 |
↗ |
| MessageReceiver | 0x5A371254b7e69d83C3aA4823D0e6ec4de91e95ec |
↗ |
| CCIP Router (Sepolia) | 0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59 |
↗ |
| Sepolia Chain Selector | 16015286601757825753 |
CCIP config |
| Asset | Address | Notes |
|---|---|---|
| ETH/USD Feed | 0x694AA1769357215DE4FAC081bf1f309aDC325306 |
Chainlink Sepolia |
| BTC/USD Feed | 0x1b44F3514812d835EB1BDB0acB33d3fA3351Ee43 |
Chainlink Sepolia |
| USDC (Collateral) | 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238 |
Circle Testnet USDC |
| WETH | 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14 |
Sepolia Testnet |
| WBTC | 0x29f2D40B0605204364af54EC677bD022dA425d03 |
Sepolia Testnet |
nexus-perps/
├── src/
│ ├── core/
│ │ ├── PositionManager.sol # Trading engine: market/limit/liquidate/cross-chain
│ │ ├── PerpsVault.sol # Collateral & LP vault (18-dec precision)
│ │ └── LiquidationEngine.sol # Keeper-compatible batch liquidator
│ ├── math/
│ │ └── PnLCalculator.sol # Pure library: PnL, liquidation health, overflow guards
│ ├── oracles/
│ │ └── PriceOracle.sol # Chainlink aggregator + heartbeat staleness
│ ├── account-abstraction/
│ │ ├── SmartAccount.sol # ERC-4337: EIP-712 signing, nonce, batch execution
│ │ ├── AccountFactory.sol # CREATE2 deterministic EIP-1167 clone factory
│ │ └── NexusPaymaster.sol # Verifying paymaster, chain-ID bound, packed storage
│ ├── cross-chain/
│ │ ├── CrossChainRouter.sol # CCIP message sender + fee estimation
│ │ └── MessageReceiver.sol # CCIP receiver + nonce dedup + try/catch execution
│ ├── interfaces/
│ │ ├── IPerpsCore.sol # Position struct, MarginMode enum, events
│ │ ├── IPriceOracle.sol
│ │ ├── ICrossChain.sol
│ │ └── IEntryPoint.sol
│ └── errors/
│ └── PerpsErrors.sol # Centralized custom error library (29 errors)
└── web3-app/
└── src/
├── app/ # Next.js 14 App Router
│ ├── page.tsx # Landing page (Server Component)
│ ├── trade/page.tsx # Trading interface
│ ├── vaults/page.tsx # LP vault interface
│ ├── portfolio/page.tsx # Position dashboard
│ ├── docs/page.tsx # Protocol docs
│ └── api/sign/route.ts # AA session signing endpoint
├── components/
│ ├── AccountVerification.tsx # Smart account login flow ('use client')
│ ├── TradingViewWidget.tsx
│ ├── layout/Navbar.tsx
│ └── providers/Web3Provider.tsx
├── hooks/
│ ├── useNexusAccount.ts # AA session state machine
│ ├── useVaultOperations.ts # deposit/withdraw/addLiquidity/removeLiquidity
│ ├── useLPOperations.ts # LP share tracking
│ ├── useVaultStats.ts # totalLiquidity, totalLocked, APY
│ └── usePortfolioData.ts # Per-trader positions + collateral
└── constants/
├── contracts.ts # All deployed addresses
└── abis/ # 100+ auto-generated ABI JSON files
Full position lifecycle: open → update → close → liquidate.
- Market Orders —
openPosition()validates oracle price, locks collateral in vault, stores position at current Chainlink price - Limit Orders —
placeLimitOrder()locks collateral optimistically. Keeper callsexecuteLimitOrder()when price condition met, earnskeeperRewardBpsdeducted from collateral - Cross-Chain Trades —
executeCrossChainTrade()gated byonlyCrossChainReceiver. Positions flaggedisCrossChain = truebypass local vault settlement on close - Liquidations — Isolated mode uses
PnLCalculator.isLiquidatable(). Cross-margin computestotalEquity = vaultCollateral + globalPnLacross all active positions via_calculateGlobalPnL - Asset Tracking — Swap-and-pop O(1) removal. Capped at
maxActiveAssetsper trader
Single contract holding all trader collateral and LP liquidity with strict dual accounting.
- Dual accounting —
traderCollateral(free) andlockedCollateral(in positions) tracked separately.totalTraderFreeCollateralglobal invariant maintained on every state change - LP Shares — First deposit burns
MINIMUM_LIQUIDITY = 1000permanently preventing inflation attacks. Subsequent deposits:sharesToMint = scaledAmount * totalLpShares / totalLiquidity settleTrade()— Atomically unlocks collateral → adjuststotalLiquidityfor PnL → credits payout to trader. Physical token balance checked as safety floor before payout- Dust prevention —
withdraw()enforcesscaledAmount % DECIMALS_SCALAR == 0
Pure Solidity library, zero state, no imports except interfaces.
positionSize = (collateral × leverage) / 1e18
PnL = (priceDelta × positionSize) / entryPrice
isLiquidatable = equity ≤ maintenanceMargin
= (collateral + PnL) ≤ (collateral × liquidationThresholdBps / 10000)
Overflow guard inside unchecked {}: checks priceDelta * size <= type(uint256).max before multiplication. Reverts with PerpsErrors.InvalidAmount() on overflow.
EIP-1167 clone-compatible, EIP-712 structured signing.
validateUserOp()— verifies nonce, recovers signer fromUSER_OP_TYPEHASHstruct hash viaECDSA.tryRecover. PaysmissingAccountFundsto EntryPoint. Returns0(success) or1(failure)execute()/executeBatch()— callable only by EntryPoint. Batch validates array length parity. Uses inlineassembly { revert(add(result, 32), mload(result)) }to bubble downstream errors_disableInitializers()in constructor prevents the implementation contract itself from being initialized as an attack vector
Packed storage: verifyingSigner (20 bytes) + maxCostLimit (12 bytes) fits in 1 storage slot.
paymasterAndDatamust be exactly85 bytes(20 addr + 65 sig) — any other length rejected- Signs
keccak256(userOpHash, block.chainid, address(this))— chain + contract binding prevents cross-chain replay
- Encodes
(trader, nonce, token, isLong, margin, leverage)as ABI payload, sent viaCCIP_ROUTER.ccipSend - Refunds surplus ETH via
call{value: refundAmount, gas: 3000}— intentionally ignores return (no reentrancy vector, documented in code) rescueFunds()differentiates ETH rescue (address(this).balance - operationalBalance) from ERC20 rescue to prevent double-counting
- Source chain whitelist + sender address whitelist — double-gated access control
- Per-trader nonce deduplication:
processedNonces[trader][nonce]prevents replay before execution try/catchwrapspositionManager.executeCrossChainTrade()— failed trades emitTradeFailedbut never block the CCIP pipeline
Next.js 14 App Router with zero backend dependency for read operations. All contract reads use Wagmi v2 + Viem directly from the browser.
| Layer | Technology |
|---|---|
| Framework | Next.js 14 (TypeScript, App Router) |
| Blockchain | Wagmi v2 + Viem |
| Wallet UI | RainbowKit (MetaMask, WalletConnect, Coinbase Wallet) |
| Queries | TanStack Query v5 |
| Styling | Tailwind CSS |
| Fonts | Syne (display) · Space Mono (mono) |
| RPC | Alchemy primary → PublicNode → Sepolia.org → Infura public (fallback chain) |
AccountVerification.tsx ('use client') drives a 5-step state machine via useNexusAccount:
idle
└─▶ fetching_nonce GET /api/sign/route.ts → server generates EIP-712 nonce
└─▶ awaiting_signature wallet.signTypedData()
└─▶ verifying POST /api/sign → server verifies signature + issues JWT
└─▶ deploying_account AccountFactory.createAccount() if new wallet
└─▶ done 24h session active · gas sponsored by NexusPaymaster
Each step renders its own UI: spinner, progress bar, error retry. page.tsx (Server Component) simply imports <AccountVerification /> — no 'use client' needed on the page, no server/client boundary error.
Unit Tests — Every function in isolation. Success paths, revert conditions, access control, edge cases (zero amounts, stale prices, unauthorized callers, zero addresses).
Integration Tests:
AAFlowIntegrationTest— Smart account deploy → UserOp validation → batch trade execution → unauthorized blockCrossChainIntegrationFlowTest— Full CCIP encode → router send → receiver decode → PositionManager execute → invalid asset revert → direct call blockLiquidationFlowIntegrationTest— Stale price oracle protection, precise math solvency, mixed-state batch resilience
Fuzz Tests (256 runs each):
testFuzz_OpenRandomPositions— random collateral + leverage, validates no panic or unexpected reverttestFuzz_LiquidationMathSolvency— validates vault never insolvent under any liquidation scenario
Invariant Tests (128 runs × 50 calls = 6,400 state mutations, 0 reverts):
╭─────────────────────┬────────────────────┬───────┬─────────┬──────────╮
│ Contract │ Selector │ Calls │ Reverts │ Discards │
╞═════════════════════╪════════════════════╪═══════╪═════════╪══════════╡
│ PositionHandler │ changeOraclePrice │ 1,541 │ 0 │ 0 │
│ PositionHandler │ createTrader │ 1,603 │ 0 │ 1 │
│ PositionHandler │ openRandomPosition │ 1,659 │ 0 │ 0 │
│ PositionHandler │ tryLiquidation │ 1,598 │ 0 │ 0 │
╰─────────────────────┴────────────────────┴───────┴─────────┴──────────╯
invariant_VaultIsSolvent—totalLiquidity ≥ 0holds across all state mutationsinvariant_InternalAccountingConsistent— sum of all internal balances matches physicalASSET.balanceOf(vault)invariant_MaxActiveAssetsRespected— no trader ever exceedsmaxActiveAssets
- Foundry (forge, cast, anvil)
- Node.js ≥ 18
- Alchemy API key for Sepolia RPC
git clone https://github.com/NexTechArchitect/Nexus-Protocol.git
cd Nexus-Protocol
# Install Foundry dependencies
forge install
# Run full test suite (95 tests, ~3s)
forge test -vv
# Generate coverage report
forge coverage
# Deploy to Sepolia
cp .env.example .env
# Fill: PRIVATE_KEY, RPC_URL, ETHERSCAN_API_KEY
forge script script/deploy/05_FullDeploy.s.sol --rpc-url sepolia --broadcast --verifycd web3-app
npm install
cp .env.local.example .env.local
# NEXT_PUBLIC_ALCHEMY_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY
npm run dev
# → http://localhost:3000- Go to faucet.circle.com
- Select Ethereum Sepolia (not mainnet)
- Enter wallet address → receive USDC
- Deposit via Vaults page → start trading
| Attack Vector | Mitigation |
|---|---|
| Oracle price manipulation | Chainlink latestRoundData() + block.timestamp - updatedAt > heartbeat staleness revert |
| Reentrancy | ReentrancyGuard on all vault state-changing functions: settleTrade, transferByManager, batchLiquidate |
| LP share inflation attack | MINIMUM_LIQUIDITY = 1000 permanently burned on genesis deposit |
| Dust sweep / precision drain | scaledAmount % DECIMALS_SCALAR != 0 reverts on withdrawal |
| Cross-chain replay | Per-trader nonce map in MessageReceiver + block.chainid binding in NexusPaymaster |
| Unauthorized cross-chain calls | onlyCrossChainReceiver + source chain whitelist + sender whitelist (3 layers) |
| Over-withdrawal during active position | lockedCollateral tracking prevents withdrawing margin from open positions |
| Paymaster signature forgery | keccak256(userOpHash, block.chainid, address(this)) — chain + contract bound |
| CCIP pipeline blocking | try/catch in _ccipReceive — failed trades emit TradeFailed, never block the pipeline |
| Keeper reward rug pull | rescueTokens() explicitly blocks PROTOCOL_ASSET from owner withdrawal |
| Implementation contract initialization | _disableInitializers() in SmartAccount constructor |
⚠️ No formal external security audit has been conducted. Deployed on Sepolia testnet with testnet assets only. Do not use with real funds.
Nexus Perps runs exclusively on Ethereum Sepolia. All USDC is Circle testnet USDC with zero real-world value. Faucet at faucet.circle.com — select Ethereum Sepolia only. This is not financial advice.