App
N
Nexus Docs
Live · Polkadot Hub Testnet

Nexus Protocol

Technical Documentation · v1.0 · Chain ID 420420417

Nexus is a fully on-chain perpetuals exchange on Polkadot Hub Testnet. Trade BTC and ETH up to 50× leverage — no off-chain order books, no trusted operators.

Five layers: Vault for capital · Trading Engine for positions · Risk Engine for liquidations · Oracle for prices · Cross-Chain via CCIP.

Network

Polkadot Hub Testnet

Chain ID

420420417

RPC

polkadothub-rpc.com

Explorer

blockscout-passet-hub

🏦

Single-Vault

All LP liquidity and trader collateral in PerpsVault.

Open Keepers

Any address calls batchLiquidate and earns 10% reward.

🔗

Polkadot Native

Deployed on Polkadot Hub EVM-compatible parachain.

📊

Live Prices

Oracle auto-updated every 2 min via Binance WebSocket.

🔐

18-Dec Math

DECIMALS_SCALAR normalises USDC (6 dec) to 1e18.

⛓️

Cross-Chain

CrossChainRouter + MessageReceiver for full CCIP.

System Architecture

Five-layer composable design

Capital deposits into PerpsVault, locking on position open via PositionManager.PriceOracle validates freshness every call. PnLCalculator checks position health. Below liquidation threshold, LiquidationEngine is open to any caller in the world.

Core Invariants

No off-chain trustPrice, execution, liquidation, settlement — all fully on-chain.
18-decimal precisionDECIMALS_SCALAR = 10^(18−tokenDecimals) normalises USDC to 1e18.
Vault solvency128 runs × 50 calls = 6,400 state mutations, zero reverts.
Isolated defaultCross-margin uses _calculateGlobalPnL across all positions.

Vault Layer

PerpsVault.sol

└─ LP Shares

└─ Collateral Lock

└─ settleTrade

Trading Engine

PositionManager.sol

└─ ISOLATED / CROSS

└─ Market + Limit

└─ Cross-Chain

Risk Engine

LiquidationEngine.sol

└─ Batch (max 20)

└─ Keeper Rewards

└─ Rescue Guard

Oracle & Math

PriceOracle.sol

└─ PnLCalculator.sol

└─ MockAggregatorV3

└─ Heartbeat Guard

Cross-Chain

CrossChainRouter.sol

└─ MessageReceiver.sol

└─ CCIP Pipeline

└─ Nonce Dedup

Price Keeper

PriceKeeper.sol

└─ Binance feed

└─ Auto 2-min push

└─ 60s cooldown

Contract Addresses

Polkadot Hub Testnet · Chain ID 420420417

MockWETHAsset
0xE357951677F61E
MockWBTCAsset
0x20e9D3EfB1A119
MockUSDCCollateral
0xDFdb1843C3Afab
PriceOracleOracle
0x7C002F51A52803
PriceKeeperOracle
0x481EC5937871C2
PerpsVaultCore
0x9495fE47c67173
PositionManagerCore
0xd16150d051fB53
LiquidationEngineCore
0x01721d6507f58B
CrossChainRouterCCIP
0x8768d7477312e9
MessageReceiverCCIP
0xdcd169caE10A64
ℹ️Block Explorer
blockscout-passet-hub.parity-testnet.parity.io — all contracts verified on-chain.

PerpsVault

The capital layer — all funds live here

PerpsVault.sol holds every dollar — LP liquidity and trader margin. Single vault, dual accounting, auditable solvency from one slot. LP funds cannot silently cover trader losses.

OwnableReentrancyGuardPausableMINIMUM_LIQUIDITY = 1000DECIMALS_SCALAR = 1e12
01

LP Liquidity Engine

LPs call addLiquidity(). Formula: shares = (amount × totalSupply) / totalAssets. First deposit permanently burns MINIMUM_LIQUIDITY (1,000) to address(0) — blocks share-price inflation attacks forever.

PerpsVault.sol — addLiquidity
SOL
function addLiquidity(uint256 amount) external nonReentrant whenNotPaused {
  uint256 normalised = amount * DECIMALS_SCALAR;
  uint256 supply     = totalLpShares;

  uint256 shares = supply == 0
    ? normalised - MINIMUM_LIQUIDITY   // genesis: burn 1000 permanently
    : (normalised * supply) / totalLiquidity;

  lpShares[msg.sender] += shares;
  totalLpShares        += shares;
  totalLiquidity       += normalised;
}
02

Trader Collateral

Traders call deposit(). USDC scaled to 18 dec via DECIMALS_SCALAR. All margin checks happen in PositionManager before any capital moves.

🛡onlyPositionManager
lockCollateral, unlockCollateral, settleTrade, transferByManager — all gated. No backdoor exists.
03

Trade Settlement

settleTrade(trader, locked, pnl) atomically applies PnL and returns net payout. LP absorbs losses, pays profits — zero-sum, single transaction.

PerpsVault.sol — settleTrade
SOL
function settleTrade(address user, uint256 amountLocked, int256 pnl)
  external onlyPositionManager nonReentrant
{
  uint256 payout = amountLocked;
  if (pnl >= 0) {
    payout         += uint256(pnl);
    totalLiquidity -= uint256(pnl);   // LP pays profit
  } else {
    uint256 loss    = uint256(-pnl);
    if (loss > amountLocked) loss = amountLocked;
    payout         -= loss;
    totalLiquidity += loss;           // LP collects loss
  }
  lockedCollateral[user] -= amountLocked;
  traderCollateral[user] += payout;
}
Dust Withdrawal Patched
scaledAmount % DECIMALS_SCALAR != 0 reverts on every withdrawal path. Eliminates fractional-wei drain exploits from the 6→18 decimal boundary.

PositionManager

Sole contract with vault write access

Every trader action routes here. Position struct: collateral, leverage, entryPrice, isLong, isOpen, isCrossChain, marginMode.

OwnableReentrancyGuardPausableliquidationThresholdBps = 8000maxLeverage = 50×
01

Margin Modes

ISOLATED

Each position's margin is ring-fenced. Only that position is at risk on liquidation.

CROSS

All free collateral shared across positions — higher leverage, cascade risk.

02

Market Orders

openPosition(token, collateral, leverage, isLong, mode) — queries oracle, validates leverage, locks collateral, writes Position struct — all atomic and nonReentrant.

PositionManager.sol — openPosition
SOL
function openPosition(
  address _token, uint256 _collateralDelta,
  uint256 _leverage, bool _isLong,
  IPerpsCore.MarginMode _mode
) external nonReentrant whenNotPaused {
  if (!whitelistedOracles[_token])  revert PerpsErrors.InvalidAsset();
  if (positions[msg.sender][_token].isOpen)
    revert PerpsErrors.PositionAlreadyExists();

  uint256 price = _getOraclePrice(_token);
  if (price == 0) revert PerpsErrors.InvalidPrice();

  VAULT.lockCollateral(msg.sender, _collateralDelta);
  _storePosition(msg.sender, _token, _collateralDelta,
    _leverage, _isLong, price, false, _mode);
}
03

Limit Orders

placeLimitOrder() locks full collateral immediately — no unfunded orders possible. Any caller executes at target price and earns 0.1% of collateral as keeper incentive.

04

Cross-Chain Execution

executeCrossChainTrade() gated to crossChainReceiver. If open fails after collateral arrives, full amount credited to trader's free balance — funds never silently lost.

LiquidationEngine

Open keeper model — any address, 10% reward

No privileged admin. Any address calls batchLiquidate() and earns 10% per successful liquidation. Competitive keeper market, no single point of failure.

01

Batch Processing

LiquidationEngine.sol
SOL
for (uint i = 0; i < _traders.length; i++) {
  IPerpsCore.Position memory pos =
    POSITION_MANAGER.getPosition(_traders[i], _tokens[i]);
  if (!pos.isOpen) {
    emit LiquidationFailed(_traders[i], _tokens[i], "not open");
    continue;  // never block the batch
  }
  try POSITION_MANAGER.liquidate(_traders[i], _tokens[i]) {
    successfulLiquidations++;
  } catch Error(string memory reason) {
    emit LiquidationFailed(_traders[i], _tokens[i], reason);
  }
}
if (successfulLiquidations > 0) _transferRewardsToKeeper();
02

Keeper Rewards

Rewards forwarded atomically at batch end — no intermediate custodian.

🛡PROTOCOL_ASSET Guard
rescueTokens() reverts unconditionally for PROTOCOL_ASSET — even a compromised owner cannot drain rewards.

PriceOracle + PriceKeeper

Mock feeds auto-synced via Binance

PriceOracle.sol wraps MockAggregatorV3 for 18-decimal prices with staleness guards.PriceKeeper auto-pushes Binance prices on-chain every 2 minutes from the frontend — no server needed.

PriceOracle.sol — getPrice
SOL
function getPrice(address _token) external view returns (uint256) {
  AggregatorV3Interface feed = feeds[_token];
  if (address(feed) == address(0)) revert PerpsErrors.InvalidAsset();

  (, int256 rawPrice,, uint256 updatedAt,) = feed.latestRoundData();
  if (rawPrice <= 0) revert PerpsErrors.InvalidPrice();
  if (block.timestamp - updatedAt > heartbeats[_token])
    revert PerpsErrors.StalePrice();        // default: 2-hour window

  uint8 dec = feed.decimals();              // 8 → scale to 18
  return uint256(rawPrice) * (10 ** (TARGET_DECIMALS - dec));
}

PnLCalculator

Pure library — no state, no ownership

Stateless pure library. Centralised arithmetic prevents rounding divergence between PositionManager and LiquidationEngine. All inputs validated before multiplication.

Overflow Safety
Both operands range-checked before multiplication. An unchecked overflow silently bypasses all health checks.

PerpsErrors

Centralised custom error registry

InvalidAssetToken not whitelisted in oracle registry
ZeroAmountDeposit, withdrawal, or order amount is zero
InsufficientCollateralMargin below minimum to open position
InvalidLeverageExceeds asset maximum (50×)
PositionAlreadyExistsOpen position already exists for this token
NoPositionFoundPosition does not exist in storage
PositionHealthyHealth above liquidation threshold — cannot liquidate
StalePriceOracle not updated within heartbeat window
InvalidPriceOracle returned zero, negative, or malformed price
UnauthorizedCaller is not registered PositionManager

CrossChainRouter

Source chain — initiates trade flow

Encodes trade params + collateral into CCIP message, validates target chain allowlist, sends to MessageReceiver.

MessageReceiver

Executes on Polkadot Hub

Decodes payload → deposits USDC → calls executeCrossChainTrade. Sender allowlist keyed by (sourceChain, senderAddress).

🛡Sender Allowlist Critical
Skipping this check allows arbitrary trade execution from any source chain address.

Test Suite

95 tests · 0 failures · Foundry

ContractSelectorCallsRevertsDiscards
PositionHandlerchangeOraclePrice1,54100
PositionHandlercreateTrader1,60301
PositionHandleropenRandomPosition1,65900
PositionHandlertryLiquidation1,59800
Terminal
SHELL
forge test        # 95 tests, ~3 seconds
forge coverage    # detailed coverage report
forge test -vvv   # full call traces

Security Model

Defense-in-depth on every surface

Oracle manipulationAuto-updated every 2 min. Staleness heartbeat reverts stale reads.
ReentrancyReentrancyGuard on settleTrade, transferByManager, batchLiquidate, deposit, withdraw.
LP inflation attackMINIMUM_LIQUIDITY = 1000 permanently burned to address(0) on genesis deposit.
Dust sweep / drainscaledAmount % DECIMALS_SCALAR != 0 reverts on every withdrawal path.
Unauthorised mutationsonlyPositionManager blocks all direct vault state changes from other callers.
Over-withdrawallockedCollateral tracking prevents withdrawing margin from open positions.
Keeper rug pullrescueTokens() reverts unconditionally for PROTOCOL_ASSET address.
Wrong decimal pricesAll prices normalised to 18-dec. Feed decimals validated on whitelist.
No External Audit
No formal audit. Polkadot Hub testnet only — testnet assets, zero real-world value.

Local Setup

Foundry + Next.js 16

Smart Contracts

Terminal
SHELL
git clone https://github.com/NexTechArchitect/nexus-protocol-v2.git
cd nexus-protocol-v2 && forge install && forge test -vv

# Deploy — fill PRIVATE_KEY in .env
forge script script/deploy/01_DeployMocks.s.sol  --rpc-url polkadot-testnet --broadcast --legacy
forge script script/deploy/02_DeployOracle.s.sol --rpc-url polkadot-testnet --broadcast --legacy
forge script script/deploy/03_DeployVault.s.sol  --rpc-url polkadot-testnet --broadcast --legacy
forge script script/deploy/04_DeployCore.s.sol   --rpc-url polkadot-testnet --broadcast --legacy

Frontend

Terminal
SHELL
cd web3-app && npm install --legacy-peer-deps
npm run dev   # → http://localhost:3000

Network config

foundry.toml
TOML
[rpc_endpoints]
polkadot-testnet = "https://services.polkadothub-rpc.com/testnet"

[etherscan]
polkadot-testnet = { key = "no-key",
  url = "https://blockscout-passet-hub.parity-testnet.parity.io/api" }

Frontend Stack

Next.js 16 · zero backend reads

Framework

Next.js 16 · App Router

Blockchain

Wagmi v2 · Viem

Wallet UI

RainbowKit · MetaMask · OKX

Charts

TradingView · Binance WS

Animations

Framer Motion · CSS

Network

Polkadot Hub (420420417)

N
NEXUS PROTOCOL● Polkadot Hub

Deterministic perpetuals. Auto price feeds.
Open-keeper liquidations. Built for Polkadot 2026.

© 2026 Nexus Protocol · MITGitHub Live App DoraHacks