# PERP DEX on Robinhood Chain

Minimal perpetual futures DEX for Robinhood Chain stock tokens.

## Network Info

| | Mainnet | Testnet |
|--|---------|---------|
| Chain ID | 4663 | 46630 |
| RPC | Set `ROBINHOOD_RPC_URL` in `.env` (private endpoint — never commit) | `https://rpc.testnet.chain.robinhood.com` |
| Explorer | https://robinhoodchain.blockscout.com | https://explorer.testnet.chain.robinhood.com |
| Gas Token | ETH | ETH |

## Canonical Token Addresses

### Stablecoin
- **USDG**: `0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168`
- **WETH**: `0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73`

### Stock Tokens (20 available)
| Symbol | Address |
|--------|---------|
| NVDA | `0xd0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC'` |
| AAPL | `0xaF3D76f1834A1d425780943C99Ea8A608f8a93f9'` |
| GOOGL | `0x2e0847E8910a9732eB3fb1bb4b70a580ADAD4FE3'` |
| TSLA | `0x322F0929c4625eD5bAd873c95208D54E1c003b2d'` |
| MSFT | `0xe93237C50D904957Cf27E7B1133b510C669c2e74'` |
| META | `0xc0D6457C16Cc70d6790Dd43521C899C87ce02f35'` |
| AMZN | `0x12f190a9F9d7D37a250758b26824B97CE941bF54'` |
| AMD | `0x86923f96303D656E4aa86D9d42D1e57ad2023fdC'` |
| COIN | `0x6330D8C3178a418788dF01a47479c0ce7CCF450b'` |
| PLTR | `0x894E1EC2D74FFE5AEF8Dc8A9e84686acCB964F2A'` |

### ETFs
| Symbol | Address |
|--------|---------|
| SPY | `0x117cc2133c37B721F49dE2A7a74833232B3B4C0C` |
| QQQ | `0xD5f3879160bc7c32ebb4dC785F8a4F505888de68` |

## Quick Start (V2 — current)

### 1. Install
```bash
npm install
```

### 2. Configure
```bash
cp .env.example .env
# Edit .env — minimum required fields:
#   PRIVATE_KEY          deployer / keeper wallet private key
#   ROBINHOOD_RPC_URL    RPC endpoint (or set NEXT_PUBLIC_RPC_URL as fallback)
```

### 3. Deploy V2 Contracts

**Testnet (MANUAL price mode):**
```bash
npx hardhat run scripts/deploy-v2.js --network robinhoodTestnet
```

**Mainnet (Chainlink Data Streams):**
```bash
# Provide feed IDs via JSON or per-asset env vars (see below), then:
npx hardhat run scripts/deploy-v2.js --network robinhood -- --streams
```

After deployment the script prints contract addresses and writes a manifest to
`deployments/<network>.json`.  Copy the printed env-var block into your keeper
`.env`:

```
V2_PRICE_FEED_ADDRESS=<printed>
V2_EXCHANGE_ADDRESS=<printed>
V2_VAULT_ADDRESS=<printed>
V2_ORDER_MANAGER_ADDRESS=<printed>
V2_INSURANCE_ADDRESS=<printed>
USDG_ADDRESS=<printed>
```

### 4. Start the Keeper

```bash
# Requires V2_PRICE_FEED_ADDRESS, V2_EXCHANGE_ADDRESS, V2_ORDER_MANAGER_ADDRESS
node scripts/keeper-v2.js
```

The keeper runs one pass every `KEEPER_INTERVAL_MS` milliseconds (default 30 s).
A pass takes longer than the interval at high order volume — the built-in
in-flight guard automatically skips the next tick rather than running two passes
concurrently.

**One-shot / testing:**
```bash
KEEPER_ONCE=true node scripts/keeper-v2.js
```

### 5. Start the Monitor

```bash
# Requires V2_PRICE_FEED_ADDRESS, V2_EXCHANGE_ADDRESS, V2_VAULT_ADDRESS, V2_ORDER_MANAGER_ADDRESS
node scripts/monitor-v2.js
```

Designed to run as a cron job every 1–5 minutes.  Exits 0 on pass, 1 on any
failure.  POSTs a JSON alert to `WEBHOOK_URL` on failure.

**Recommended cron (every 2 minutes):**
```
*/2 * * * * /usr/bin/node /path/to/scripts/monitor-v2.js >> /var/log/monitor-v2.log 2>&1
```

### 6. Deploy Frontend

```bash
# Set Vercel env vars first (see below), then:
vercel --prod
```

---

## V2 Environment Variable Reference

### Required for keeper-v2.js

| Variable | Description |
|----------|-------------|
| `PRIVATE_KEY` | Keeper wallet (needs Keeper role on PriceFeedV2 + OrderManager) |
| `ROBINHOOD_RPC_URL` | RPC endpoint (fallback: `NEXT_PUBLIC_RPC_URL`) |
| `V2_PRICE_FEED_ADDRESS` | PriceFeedV2 contract address |
| `V2_EXCHANGE_ADDRESS` | PerpExchangeV2 contract address |
| `V2_ORDER_MANAGER_ADDRESS` | OrderManager contract address |

### Required for monitor-v2.js

| Variable | Description |
|----------|-------------|
| `ROBINHOOD_RPC_URL` | RPC endpoint (fallback: `NEXT_PUBLIC_RPC_URL`) |
| `V2_PRICE_FEED_ADDRESS` | PriceFeedV2 contract address |
| `V2_EXCHANGE_ADDRESS` | PerpExchangeV2 contract address |
| `V2_VAULT_ADDRESS` | PerpVault contract address |
| `V2_ORDER_MANAGER_ADDRESS` | OrderManager contract address |

### Required for deploy-v2.js (--streams / mainnet)

| Variable | Description |
|----------|-------------|
| `VERIFIER_PROXY` | Chainlink Data Streams verifier proxy address |
| `STREAMS_CONFIG` | Path to JSON file mapping symbol → feedId bytes32 |
| `FEED_ID_<SYMBOL>` | Per-asset feed ID override (e.g. `FEED_ID_NVDA`) |

### Optional / tuning

| Variable | Default | Description |
|----------|---------|-------------|
| `DATA_STREAMS_URL` | unset | Base URL for Chainlink Data Streams REST API (keeper) |
| `DATA_STREAMS_CLIENT_ID` | unset | HMAC client ID (keeper) |
| `DATA_STREAMS_CLIENT_SECRET` | unset | HMAC client secret (keeper) |
| `KEEPER_INTERVAL_MS` | 30000 | Keeper loop interval in ms |
| `KEEPER_FROM_BLOCK` | 0 | Block to start event scans from |
| `KEEPER_ONCE` | false | Run one pass and exit (set to `"true"` for testing) |
| `WEBHOOK_URL` | unset | HTTP(S) endpoint for alert POSTs on failure |
| `MONITOR_SOLVENCY_BUFFER_BPS` | 0 | Min free-assets as % of totalAssets in bps |
| `MONITOR_STALE_PRICE_THRESHOLD_S` | contract value | Override stale-price threshold |
| `MONITOR_ORDER_BACKLOG_THRESHOLD` | 50 | Max acceptable pending orders |
| `MONITOR_SKEW_THRESHOLD_BPS` | 8000 | Max acceptable \|skew\|/totalOI in bps |
| `MONITOR_FROM_BLOCK` | 0 | Block to start event scans from |

### Frontend (Next.js)

| Variable | Description |
|----------|-------------|
| `NEXT_PUBLIC_RPC_URL` | Public RPC endpoint for the frontend |
| `NEXT_PUBLIC_CHAIN_ID` | Chain ID (46630 testnet / 4663 mainnet) |
| `NEXT_PUBLIC_USDG_ADDRESS` | USDG token address |
| `NEXT_PUBLIC_USDG_DECIMALS` | USDG decimal count (default 6) |
| `NEXT_PUBLIC_EXCHANGE_ADDRESS` | PerpExchangeV2 address |
| `NEXT_PUBLIC_VAULT_ADDRESS` | PerpVault address |
| `NEXT_PUBLIC_ORDER_MANAGER_ADDRESS` | OrderManager address |
| `NEXT_PUBLIC_PRICE_FEED_ADDRESS` | PriceFeedV2 address |

---

## V2 Architecture

| Contract | Purpose |
|----------|---------|
| PriceFeedV2.sol | Oracle — Chainlink Data Streams (mainnet) or manual (testnet) |
| PerpVault.sol | ERC-4626 LP vault — counterparty capital |
| InsuranceFund.sol | Bad-debt backstop |
| PerpExchangeV2.sol | Core position engine (funding, borrowing, liquidations) |
| OrderManager.sol | Two-step order execution with keeper network |

### Governance / Security

Critical address changes (vault, priceFeed, insuranceFund, orderManager) on
PerpExchangeV2 use a **24-hour two-phase timelock**:

```
owner calls proposeVault(newAddr)       → starts 24 h countdown
... wait 24 h ...
owner calls acceptVault()               → finalises the change
```

This prevents an instant single-transaction takeover of core access controls
even if the owner key is compromised. The orderManager may be set directly once
(during initial deployment when the slot is still zero); all subsequent changes
require the propose/accept flow.

---

## V1 Quick Start (legacy)

The original V1 contracts are still in `contracts/` for reference.

```bash
npx hardhat run scripts/deploy.js --network robinhood
npx hardhat run scripts/authorize-exchange.js --network robinhood
npx hardhat run scripts/set-prices.js --network robinhood
npx hardhat run scripts/seed-liquidity.js --network robinhood
```

## Features

- Long/short perps on 12 stock tokens + ETFs
- Up to 10x leverage
- USDG collateral
- Chainlink Data Streams oracle (V2)
- Skew-based funding rates + utilization-based borrowing
- Two-step order execution (limit, stop, market orders)
- Auto-liquidation with insurance fund backstop
- 24-hour governance timelock on critical address changes
