# Dexly Platform Teknik Dokümantasyonu (Genişletilmiş) ## Giriş ### Problem Statement Hyperliquid DEX, her order için wallet signature gerektirir. Bu, automated copy trading için iki temel constraint oluşturur: 1. **Signature requirement**: Her trade için user interaction gerekli → real-time execution imkansız 2. **Custody dilemma**: Custodial çözümler (CEX-style) user funds'ı merkezi kontrol altına alır ### Çözüm Mimarisi Platform, **delegated signing** yaklaşımı kullanır: - User, platform-generated **agent wallet**'a trading yetkisi verir (Hyperliquid native feature) - Agent private key encrypted olarak backend'de saklanır - Worker servisleri agent key ile user adına order imzalar - Funds her zaman user'ın Hyperliquid account'unda kalır (non-custodial) ### Sistem Bileşenleri **Platform Bileşenleri:** | Bileşen | Teknoloji | Açıklama | |---------|-----------|----------| | dexly-core | Fastify, TypeScript | Backend API ve worker servisleri | | dexly-web | Next.js 16, React 19 | Web frontend | | dexly-app | Expo, React Native | Mobile uygulama | | Supabase | PostgreSQL | Veritabanı | | Hyperliquid | DEX | Trade execution | --- ## 1. Authentication Sistemi (Detaylı) ### 1.1 Genel Mimari **Dual Authentication Strategy:** - **Privy**: Managed wallet infrastructure → mobile-first, embedded wallet generation - **SIWE**: External wallet support → MetaMask, WalletConnect, hardware wallets **Design Decision**: İki yöntem farklı user segment'leri hedefler. Privy onboarding friction'ı azaltır (no seed phrase management), SIWE existing wallet holder'lar için native experience sağlar. **Token Architecture**: Her iki flow da aynı JWT-based session'a converge eder: - Access token (15 dk): Stateless API authentication - Refresh token (30 gün): Session persistence, single-use rotation ``` ┌─────────────────────────────────────────────────────────────────┐ │ AUTHENTICATION FLOW │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ Mobile │ │ Web │ │ │ │ (dexly-app) │ │ (dexly-web) │ │ │ └──────┬───────┘ └──────┬───────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ Privy SDK │ │ WalletConnect│ │ │ │ (embedded) │ │ / MetaMask │ │ │ └──────┬───────┘ └──────┬───────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌─────────────────────────────────────────┐ │ │ │ dexly-core API │ │ │ │ POST /v1/auth/privy │ POST /v1/auth/siwe/verify │ │ └─────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────┐ │ │ │ JWT Token Generation │ │ │ │ • Access Token (15 dk) │ │ │ │ • Refresh Token (30 gün) │ │ │ └─────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 1.2 Privy Authentication (Detaylı) **Architecture**: Privy, third-party identity provider olarak JWT-based authentication sağlar. Embedded wallet key management Shamir Secret Sharing ile Privy infrastructure'da tutulur. **Trade-off**: Key custody Privy'de → user seed phrase yönetmez, ancak full self-custody değil. Export özelliği ile migration path mevcut. **Integration Flow**: - Privy SDK client-side JWT üretir - Backend JWKS endpoint üzerinden signature verify eder - Token validation: issuer, audience, maxAge (5 dk) kontrolleri **Akış:** 1. Kullanıcı Privy SDK ile giriş yapar (email, social login, veya passkey) 2. Privy bir JWT token üretir 3. Frontend bu token'ı `/v1/auth/privy` endpoint'ine gönderir 4. Backend, Privy'nin JWKS endpoint'i üzerinden token'ı doğrular 5. Kullanıcı ve cüzdan kaydı oluşturulur/güncellenir 6. Dexly JWT token'ları (access + refresh) döndürülür **Privy Token Doğrulama:** ```typescript // src/auth/privyVerifier.ts async verifyPrivyToken(token: string): Promise<PrivyPayload> { // JWKS üzerinden public key çekiliyor const jwks = await fetchJwks(config.privy.jwksUrl); // JWT signature doğrulanıyor const decoded = await jwt.verify(token, jwks, { issuer: config.privy.issuer, // Beklenen issuer audience: config.privy.audience, // Beklenen audience maxTokenAge: 300, // Max 5 dakika eski olabilir }); return { privy_user_id: decoded.sub, embedded_wallet_address: decoded.wallet?.address, privy_wallet_id: decoded.wallet?.id, }; } ``` **Güvenlik Kontrolleri:** | Kontrol | Açıklama | |---------|----------| | JWKS Doğrulama | Public key Privy'den dinamik çekiliyor | | Issuer Check | Token'ın Privy tarafından üretildiği doğrulanıyor | | Audience Check | Token'ın Dexly için üretildiği doğrulanıyor | | Token Age | 5 dakikadan eski token'lar reddediliyor | ### 1.3 SIWE Authentication (Detaylı) **Protocol**: EIP-4361 standardı. Off-chain signature verification, gas-free authentication. **Security Model**: - **Nonce**: Server-generated, single-use, 10 dk TTL → replay attack prevention - **Two-phase flow**: Nonce request → signature verification. Server-side nonce generation zorunlu, client-generated nonce kabul edilmez - **Double verification**: `siwe.verify()` + `ethers.verifyMessage()` redundant check **Whitelist Controls**: - Domain/Origin: `DEXLY_SIWE_ALLOWED_DOMAINS`, `DEXLY_SIWE_ALLOWED_ORIGINS` - ChainId: `DEXLY_SIWE_ALLOWED_CHAIN_IDS` - Message freshness: `issuedAt` max age validation **İki Aşamalı Akış:** ``` ┌─────────────────────────────────────────────────────────────────┐ │ SIWE FLOW │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ADIM 1: Nonce Talebi │ │ ──────────────────── │ │ Frontend ──GET /v1/auth/siwe/nonce?address=0x...──► Backend │ │ │ │ Backend: │ │ 1. Rastgele nonce üretir (siwe.generateNonce()) │ │ 2. Nonce'u DB'ye kaydeder (10 dk geçerli) │ │ 3. { nonce, expires_at } döndürür │ │ │ │ ADIM 2: İmza Doğrulama │ │ ────────────────────── │ │ Frontend ──POST /v1/auth/siwe/verify──► Backend │ │ Body: { address, message, signature, connector } │ │ │ │ Backend: │ │ 1. Nonce kontrolü (exists, not used, not expired) │ │ 2. Domain/Origin/ChainId whitelist kontrolü │ │ 3. Message freshness kontrolü (issuedAt) │ │ 4. Signature doğrulama (siwe lib + ethers backup) │ │ 5. Nonce atomic consume (tek kullanım) │ │ 6. User/Wallet oluştur veya bul │ │ 7. JWT token'ları döndür │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` **SIWE Güvenlik Kontrolleri:** | Kontrol | Açıklama | Config | |---------|----------|--------| | Nonce Single-Use | Her nonce sadece bir kez kullanılabilir | Atomic DB consume | | Nonce Expiry | Nonce 10 dakika sonra geçersiz | Hardcoded | | Domain Whitelist | Sadece izinli domain'lerden gelen istekler | `DEXLY_SIWE_ALLOWED_DOMAINS` | | Origin Whitelist | URI origin kontrolü | `DEXLY_SIWE_ALLOWED_ORIGINS` | | ChainId Whitelist | Sadece izinli chain'ler | `DEXLY_SIWE_ALLOWED_CHAIN_IDS` | | Message Freshness | issuedAt çok eski olamaz | `DEXLY_SIWE_MAX_ISSUED_AT_AGE` | | Double Verification | siwe lib + ethers.verifyMessage | Redundant check | **Çift İmza Doğrulama:** ```typescript // src/services/authService.ts // Birincil doğrulama const siweMessage = new SiweMessage(message); await siweMessage.verify({ signature }); // Yedek doğrulama (paranoid mode) const recoveredAddress = ethers.verifyMessage(message, signature); if (recoveredAddress.toLowerCase() !== address.toLowerCase()) { throw new Error('Signature address mismatch'); } ``` ### 1.4 Token Sistemi (Detaylı) **Dual Token Strategy**: | Token | TTL | Storage | Purpose | |-------|-----|---------|---------| | Access | 15 dk | Memory/Header | Stateless API auth, short exposure window | | Refresh | 30 gün | DB (hashed) | Session persistence, rotation-based security | **Refresh Token Rotation**: Her kullanımda token invalidate edilir, yenisi issue edilir. Concurrent kullanım (stolen token) tespit mekanizması sağlar. **Security Trade-off**: Short access TTL → frequent refresh calls, ancak token theft impact window minimize. **Access Token:** ```typescript // src/auth/jwt.ts interface AccessTokenPayload { app_user_id: string; // Kullanıcı ID auth_method: 'privy' | 'siwe'; session_id: string; // Oturum ID wallet_address?: string; // Aktif cüzdan (bilgi amaçlı) iss: string; // Issuer: 'dexly-core' iat: number; // Issued at timestamp exp: number; // Expiry timestamp } // TTL: 15 dakika (900 saniye) // Algorithm: HS256 // Secret: DEXLY_JWT_SECRET env var ``` **Refresh Token:** ```typescript // Rastgele 32-byte token (base64url encoded) const refreshToken = crypto.randomBytes(32).toString('base64url'); // DB'de hash olarak saklanır const hash = crypto.createHmac('sha256', pepper) .update(refreshToken) .digest('hex'); // TTL: 30 gün (2,592,000 saniye) // Single-use: Her kullanımda yenisiyle değiştirilir ``` **Token Yenileme Akışı:** ``` POST /v1/auth/refresh Body: { refresh_token: "abc123..." } Backend: 1. Token hash'ini hesapla 2. DB'de bul ve atomic consume et 3. Eğer bulunamadı/kullanılmış → 401 4. Yeni access + refresh token üret 5. Yeni refresh token'ı DB'ye kaydet 6. Token'ları döndür ``` ### 1.5 Auth Database Şeması **Data Model**: - `app_user` → `user_wallet`: One-to-many. Multi-wallet support per user. - `wallet_address`: Unique constraint. Bir adres tek user'a bound. - `active_wallet_id`: Current session context için aktif wallet referansı. **Session Storage**: - Refresh token DB'de hash olarak saklanır (HMAC-SHA256 + pepper) - DB leak durumunda token recovery imkansız - `revoked_at`: Explicit session invalidation (logout, security kill-switch) **app_users Tablosu:** ```sql CREATE TABLE app_users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), last_login_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), terms_accepted_at TIMESTAMPTZ, active_wallet_id UUID REFERENCES user_wallets(id), is_flagged BOOLEAN DEFAULT FALSE, flag_reason TEXT ); ``` **user_wallets Tablosu:** ```sql CREATE TABLE user_wallets ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), app_user_id UUID NOT NULL REFERENCES app_users(id), wallet_address TEXT NOT NULL, -- 0x... (lowercase) wallet_kind TEXT NOT NULL, -- 'EOA' | 'PRIVY_EMBEDDED' connector TEXT NOT NULL, -- 'PRIVY' | 'REOWN' | 'WALLETCONNECT' privy_wallet_id TEXT, privy_user_id TEXT, is_active BOOLEAN DEFAULT FALSE, last_seen_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(wallet_address) -- Bir adres tek kullanıcıya ait ); ``` **auth_sessions Tablosu:** ```sql CREATE TABLE auth_sessions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), app_user_id UUID NOT NULL REFERENCES app_users(id), refresh_token_hash TEXT NOT NULL UNIQUE, -- SHA256/HMAC hash created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), expires_at TIMESTAMPTZ NOT NULL, revoked_at TIMESTAMPTZ -- NULL = aktif ); ``` --- ## 2. Agent Wallet Sistemi (Detaylı) ### 2.1 Agent Wallet Nedir? **Problem**: Hyperliquid order execution cryptographic signature gerektirir. Automated trading için user interaction blocker. **Solution**: Hyperliquid native agent delegation feature: - User, secondary wallet'a trading authority verir - Agent wallet platform tarafından generate edilir - Private key encrypted olarak backend'de saklanır - Worker agent key ile user adına sign eder **Scope Control**: | Scope | Capability | |-------|------------| | `TRADE_ONLY` | Order placement/cancellation. Withdrawal disabled. | | `TRADE_AND_WITHDRAW` | Full access (not used in copy trading) | **Non-Custodial Guarantee**: - Funds Hyperliquid account'ta kalır (user's master address) - Agent sadece signing authority'ye sahip - User istediğinde revoke edebilir (on-chain action) ### 2.2 Agent Wallet Oluşturma Süreci **Three-Phase Flow**: | Phase | Actor | Action | |-------|-------|--------| | Prepare | Backend | `ethers.Wallet.createRandom()` → AES-256-GCM encrypt → DB save (PENDING) | | Sign | User | Master wallet ile iki action imzala: `approveAgent` + `approveBuilderFee` | | Confirm | Backend | Signatures → Hyperliquid API submit → status = ACTIVE | **Required Signatures**: - `approveAgent`: Agent delegation on-chain registration - `approveBuilderFee`: Platform fee approval (builder address + rate) **State Machine**: `PENDING` → `ACTIVE` → `REVOKED` ``` ┌─────────────────────────────────────────────────────────────────┐ │ AGENT WALLET CREATION FLOW │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. PREPARE (Backend) │ │ ───────────────────── │ │ POST /v1/agents/enable │ │ Body: { scope: 'TRADE_ONLY', agent_name: 'MyAgent' } │ │ │ │ Backend: │ │ • ethers.Wallet.createRandom() → yeni cüzdan │ │ • Private key AES-256-GCM ile şifrelenir │ │ • DB'ye PENDING status ile kaydedilir │ │ • İmzalanacak action'lar döndürülür │ │ │ │ 2. SIGN (Frontend) │ │ ───────────────────── │ │ Kullanıcı iki action imzalar: │ │ • approveAgent: Agent'a trade yetkisi ver │ │ • approveBuilderFee: Builder fee'yi onayla │ │ │ │ 3. CONFIRM (Backend) │ │ ───────────────────── │ │ POST /v1/agents/confirm │ │ Body: { signatures: [...] } │ │ │ │ Backend: │ │ • İmzaları Hyperliquid'e gönderir │ │ • Agent status = 'ACTIVE' olur │ │ • upstream_confirmed_at set edilir │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 2.3 Database'de Agent Wallet Saklama **trade_agents Tablosu:** ```sql CREATE TABLE trade_agents ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), app_user_id UUID NOT NULL REFERENCES app_users(id), user_wallet_id UUID NOT NULL REFERENCES user_wallets(id), -- Adresler master_address TEXT NOT NULL, -- Kullanıcının cüzdanı (0x...) agent_address TEXT NOT NULL, -- Agent cüzdanı (0x...) -- Şifreli Private Key agent_private_key_enc TEXT, -- AES-256-GCM encrypted -- Hyperliquid Integration builder_address TEXT, -- Builder fee adresi builder_fee_rate TEXT, -- Fee oranı referral_code TEXT, hl_chain TEXT, -- Arbitrum chain info -- Status scope TEXT DEFAULT 'TRADE_ONLY', -- TRADE_ONLY | TRADE_AND_WITHDRAW status TEXT DEFAULT 'PENDING', -- PENDING | ACTIVE | REVOKED agent_name TEXT, -- Max 17 karakter -- Timestamps approve_agent_confirmed_at TIMESTAMPTZ, approve_builder_fee_confirmed_at TIMESTAMPTZ, upstream_confirmed_at TIMESTAMPTZ, revoked_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(user_wallet_id, agent_address) ); ``` ### 2.4 Private Key Encryption (Detaylı) **Encryption Scheme**: AES-256-GCM (Authenticated Encryption with Associated Data) **Algorithm Choice Rationale**: - AES-256: Industry standard, quantum-resistant key size - GCM mode: Provides confidentiality + integrity (auth tag). Tamper detection built-in. **Key Management**: - Master key: `AGENT_ENCRYPTION_KEY` env var - Key derivation: SHA-256 hash → 256-bit AES key (deterministic) - Per-encryption IV: Random 12-byte, pattern analysis prevention **Ciphertext Format**: `base64(IV).base64(ciphertext).base64(authTag)` **Şifreleme Süreci:** ```typescript // src/utils/crypto.ts // 1. Encryption key derivation function deriveAes256Key(secret: string): Buffer { // AGENT_ENCRYPTION_KEY → SHA256 → 256-bit key const hash = crypto.createHash('sha256') .update(secret) .digest(); return hash; // 32 bytes = 256 bits } // 2. Encryption function encryptWithAesGcm(plaintext: string, key: Buffer): string { const iv = crypto.randomBytes(12); // 96-bit IV (GCM standard) const cipher = crypto.createCipheriv('aes-256-gcm', key, iv); const encrypted = Buffer.concat([ cipher.update(plaintext, 'utf8'), cipher.final() ]); const tag = cipher.getAuthTag(); // 128-bit authentication tag // Format: iv.ciphertext.tag (base64 encoded) return [ iv.toString('base64'), encrypted.toString('base64'), tag.toString('base64') ].join('.'); } // Örnek output: // "dGVzdGl2MTIzNDU2.YWJjZGVmZ2hpams=.dGFnMTIzNDU2Nzg5MA==" ``` **Deşifreleme Süreci (Trade Execution Sırasında):** ```typescript // 3. Decryption function decryptWithAesGcm(payload: string, key: Buffer): string { const [ivB64, encryptedB64, tagB64] = payload.split('.'); const iv = Buffer.from(ivB64, 'base64'); const encrypted = Buffer.from(encryptedB64, 'base64'); const tag = Buffer.from(tagB64, 'base64'); const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv); decipher.setAuthTag(tag); // Tag kontrolü (tamper detection) const decrypted = Buffer.concat([ decipher.update(encrypted), decipher.final() ]); return decrypted.toString('utf8'); // Private key } ``` **Encryption Özellikleri:** | Özellik | Değer | Açıklama | |---------|-------|----------| | Algoritma | AES-256-GCM | Authenticated encryption | | Key Size | 256-bit | SHA256(AGENT_ENCRYPTION_KEY) | | IV Size | 96-bit (12 byte) | Her encryption için random | | Tag Size | 128-bit | Tamper detection | | Key Derivation | SHA-256 | Deterministic | ### 2.5 Agent Wallet ile Trade Execution **Key Lifecycle**: Decrypt → Sign → Garbage Collect. Memory-only, no disk persistence. **Security Constraints**: - No key caching (short memory exposure window) - No logging of decrypted keys - Per-operation decrypt (AES-256 decrypt overhead: ~µs, acceptable) **Trade-off**: Performance vs Security. Key caching faster ama memory exposure riski. Current: security-first. **Akış:** ``` ┌─────────────────────────────────────────────────────────────────┐ │ TRADE EXECUTION WITH AGENT WALLET │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. Worker lider trade'ini algılar │ │ │ │ 2. Follower için order hazırlanır │ │ • Risk kontrolleri yapılır │ │ • Order size hesaplanır │ │ │ │ 3. Agent wallet DB'den çekilir │ │ const agent = await tradeAgentsRepo │ │ .getActiveByWalletId(follow.follower_wallet_id); │ │ │ │ 4. Private key decrypt edilir (SADECE MEMORY'DE) │ │ const key = deriveAes256Key(AGENT_ENCRYPTION_KEY); │ │ const privateKey = decryptWithAesGcm( │ │ agent.agent_private_key_enc, │ │ key │ │ ); │ │ │ │ 5. Hyperliquid'e order gönderilir │ │ const wallet = new ethers.Wallet(privateKey); │ │ await hyperliquidClient.placeOrder({ │ │ wallet, // Agent wallet imzalıyor │ │ orders: [{ asset, isBuy, price, size }] │ │ }); │ │ │ │ 6. Private key garbage collected (memory'den siliniyor) │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 2.6 User Wallet → Agent Wallet İlişkisi ``` ┌─────────────────────────────────────────────────────────────────┐ │ WALLET HIERARCHY │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ User (app_user) │ │ │ │ │ ├── User Wallet 1 (master_address: 0xABC...) │ │ │ │ │ │ │ └── Agent Wallet (agent_address: 0xDEF...) │ │ │ • Status: ACTIVE │ │ │ • Scope: TRADE_ONLY │ │ │ • Bu agent ile trade yapılabilir │ │ │ │ │ └── User Wallet 2 (master_address: 0x123...) │ │ │ │ │ └── Agent Wallet (agent_address: 0x456...) │ │ • Status: PENDING (henüz onaylanmamış) │ │ │ │ Hyperliquid'de: │ │ • Pozisyonlar master_address'e ait görünür │ │ • Agent sadece imzalama yetkisine sahip │ │ • Fonlar her zaman user'ın kontrolünde │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## 3. Copy Trading Sistemi ### 3.1 Copy Trading Akışı **Architecture**: Event-driven, job queue based processing. **Detection Pipeline**: 1. WebSocket: Hyperliquid global trades stream → leader address filter 2. Leader marked "dirty" → `leader_sync` job enqueue 3. REST API: Leader fills fetch → per-fill `follower_execute` jobs 4. Execution: Risk check → order sizing → agent sign → Hyperliquid submit **Latency Target**: Leader fill detection to follower order: <500ms (realtime mode) **Scaling Model**: Her stage bağımsız scale edilebilir. Job queue partitioning ile horizontal scaling. ``` ┌─────────────────────────────────────────────────────────────────┐ │ COPY TRADING FLOW │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. FOLLOW OLUŞTURMA │ │ POST /v1/copy/follows │ │ Body: { │ │ leader_address: "0xLEADER...", │ │ copy_budget_usdc: 1000, │ │ cost_per_order_usdc: 100, │ │ risk: { max_leverage: 10, ... } │ │ } │ │ Status: INACTIVE │ │ │ │ 2. FOLLOW BAŞLATMA │ │ POST /v1/copy/follows/:id/start │ │ • Agent wallet kontrolü (ACTIVE olmalı) │ │ • Status: ACTIVE │ │ • Reconcile job enqueue │ │ │ │ 3. TRADE ALGILAMA (Worker) │ │ • WebSocket: Hyperliquid global trades │ │ • Lider adresi görülünce "dirty" mark │ │ • leader_sync job enqueue │ │ │ │ 4. LEADER SYNC │ │ • REST API ile lider fill'leri çekiliyor │ │ • Her fill için follower_execute job oluşturuluyor │ │ │ │ 5. FOLLOWER EXECUTE │ │ • Risk kontrolleri │ │ • Order sizing │ │ • Agent wallet ile imzalama │ │ • Hyperliquid'e order gönderme │ │ │ │ 6. RECONCILIATION (60 sn loop) │ │ • Live pozisyon sync │ │ • PnL hesaplama │ │ • Drawdown kontrolü │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 3.2 Copy Trading Ayarları **Risk Parameter Design**: | Parameter | Function | Default Rationale | |-----------|----------|-------------------| | `copy_budget_usdc` | Total allocation | User-defined capital commitment | | `cost_per_order_usdc` | Per-trade limit | Position sizing cap, diversification | | `max_total_leverage` | Leverage ceiling (10x) | Leader leverage override, risk normalization | | `stop_copy_drawdown_pct` | Auto-pause trigger (30%) | Capital preservation, emotional trading prevention | | `slippage_bps` | Limit price tolerance (50bps) | Fill rate vs price impact trade-off | **Budget Accounting**: `remaining = budget - used + realized_pnl + unrealized_pnl` **Follow Risk Config Tablosu:** | Parametre | Tip | Default | Min | Max | Açıklama | |-----------|-----|---------|-----|-----|----------| | `copy_budget_usdc` | number | - | 10 | ∞ | Toplam bütçe (USD) | | `cost_per_order_usdc` | number | - | 10 | budget | Order başına bütçe | | `max_total_leverage` | number | 10 | 1 | 50* | Maksimum kaldıraç | | `max_open_positions` | number | 3 | 1 | 20 | Max açık sembol sayısı | | `max_symbol_allocation_pct` | number | 50 | 10 | 100 | Sembol başına max % | | `stop_copy_drawdown_pct` | number | 30 | 5 | 100 | Drawdown stop % | | `slippage_bps` | number | 50 | 0 | 500 | Slippage toleransı (bps) | | `margin_mode` | string | 'cross' | - | - | 'cross' \| 'isolated' | | `mode` | string | 'realtime' | - | - | Trade algılama modu | | `sync_interval_seconds` | number | 10 | 5 | 60 | Sync intervali | *\*Hyperliquid asset limiti ile sınırlı* **Trade Algılama Modları:** | Mod | Latency | Kullanım | Rate Limit Impact | |-----|---------|----------|-------------------| | `realtime` | 0ms | Normal liderler | Yüksek | | `throttled` | 500-1500ms | Aktif liderler | Orta | | `safe_poll` | 10s polling | HFT liderler | Düşük | ### 3.3 Order Sizing Algoritması **Sizing Formula**: ``` perOrderBudget = min(cost_per_order_usdc, remainingBudget, symbolAllocationRemaining) notional = perOrderBudget × leverage size = notional / fillPrice limitPrice = fillPrice × (1 ± slippage_bps/10000) // + for buy, - for sell ``` **Constraint Cascade**: 1. Global remaining budget check 2. Per-symbol allocation limit (`max_symbol_allocation_pct`) 3. Per-order budget cap (`cost_per_order_usdc`) 4. Min notional threshold ($10 USD) **Partial Close Handling**: Leader close ratio hesaplanır, follower position aynı ratio ile reduce edilir. ```typescript // src/features/copytrade/riskEngine.ts function computeOrderPlan(fill: LeaderFill, ctx: RiskContext): OrderPlan { // 1. Kalan bütçe hesapla const remaining = ctx.copy_budget_usdc - ctx.budget_used_usdc + ctx.copied_pnl_realized_usdc + ctx.copied_pnl_unrealized_usdc; // 2. Sembol allocation kontrolü const symbolUsed = Math.abs(ctx.current_symbol_exposure_usdc); const symbolMax = ctx.copy_budget_usdc * (ctx.max_symbol_allocation_pct / 100); const symbolRemaining = Math.max(0, symbolMax - symbolUsed); // 3. Per-order bütçe const perOrderBudget = Math.min( ctx.cost_per_order_usdc, remaining, symbolRemaining ); // 4. Notional ve size hesapla const notional = perOrderBudget * ctx.max_total_leverage; const size = notional / fill.px; // 5. Slippage ekle const slippage = ctx.slippage_bps / 10_000; const limitPrice = fill.side === 'buy' ? fill.px * (1 + slippage) : fill.px * (1 - slippage); return { symbol: fill.coin, isBuy: fill.side === 'buy', limitPrice, size, notional_usdc: notional, per_order_budget: perOrderBudget, }; } ``` --- ## 4. Risk Engine (Detaylı) ### 4.1 Risk Engine Çalışma Prensipleri **Two-Phase Validation Architecture**: | Phase | Latency | Data Source | Purpose | |-------|---------|-------------|---------| | Quick Guard | ~µs | In-memory state | Fail-fast, obvious violations | | Full Validation | ~50-200ms | Hyperliquid API | Authoritative state, margin check | **Optimization Rationale**: API calls expensive. Quick guard filters ~70% of rejections without API hit. **Fail-Fast Checks** (Quick Guard): - Budget exhausted - Max positions reached - Below min notional - Symbol allocation exceeded **Authoritative Checks** (Full Validation): - Live margin availability - Asset-specific leverage limits - Position state verification Risk engine iki aşamada çalışır: **Aşama 1: Quick Guard Check (Pre-Execution)** ```typescript // API çağrısı yapmadan hızlı kontroller function quickGuardCheck(params): { canProceed: boolean; reason?: string } { // 1. Bütçe kontrolü const remaining = budget - used + realized_pnl + unrealized_pnl; if (remaining <= 0) { return { canProceed: false, reason: 'BUDGET_EXHAUSTED' }; } // 2. Max açık pozisyon kontrolü if (!isReducing && openPositions >= max_open_positions) { return { canProceed: false, reason: 'MAX_POSITIONS_REACHED' }; } // 3. Min notional kontrolü if (estimatedNotional < min_order_notional_usd) { return { canProceed: false, reason: 'BELOW_MIN_NOTIONAL' }; } // 4. Sembol allocation kontrolü const symbolMax = budget * (max_symbol_allocation_pct / 100); if (currentSymbolExposure >= symbolMax) { return { canProceed: false, reason: 'SYMBOL_ALLOCATION_EXCEEDED' }; } return { canProceed: true }; } ``` **Aşama 2: Full Validation (With API Data)** ```typescript // Live veri ile detaylı kontroller async function computeOrderPlan(fill, ctx): Promise<RiskDecision> { // 1. Live pozisyon ve margin çek const position = await fetchLivePosition(symbol); const margin = await fetchMarginSummary(); // 2. Margin yeterli mi? if (margin.margin_used >= margin.account_value) { return { status: 'reject', reason: 'INSUFFICIENT_MARGIN' }; } // 3. Leverage asset limiti içinde mi? const assetMaxLeverage = getAssetMaxLeverage(symbol); const effectiveLeverage = Math.min(max_leverage, assetMaxLeverage); // 4. Close ratio hesapla (kısmi kapama için) let closeRatio = 1.0; if (isLeaderClosing && position.size > 0) { closeRatio = fill.closedSize / leaderStartPosition; } return { status: 'accept', plan: { ... } }; } ``` ### 4.2 Drawdown Protection **Mechanism**: Realized PnL based auto-pause. **Formula**: ``` threshold = -budget × (stop_copy_drawdown_pct / 100) if realized_pnl <= threshold → pause follow ``` **Design Decision**: Realized-only (unrealized excluded). Rationale: Temporary price fluctuations trigger false positives. Realized = confirmed loss. **Reconciliation Loop**: 60s interval. Checks: 1. Live position sync 2. PnL calculation 3. Drawdown threshold comparison 4. Auto-pause if breached **Nasıl Çalışır:** ```typescript // Her 60 saniyede reconciliation loop async function handleReconcile(job: CopyJob) { const follow = await getFollow(job.follow_id); // Live pozisyonları çek const positions = await fetchOpenPositions(follow.follower_wallet_address); // Realized PnL hesapla (kapalı pozisyonlardan) const realizedPnL = follow.copied_pnl_realized_usdc; // Drawdown threshold const threshold = -follow.copy_budget_usdc * (follow.stop_copy_drawdown_pct / 100); // Kontrol if (realizedPnL <= threshold) { // Otomatik pause await copyFollowsRepo.pauseFollow(follow.id, 'drawdown_stop'); // Security event log await logSecurityEvent({ type: 'COPY_DRAWDOWN_STOP', follow_id: follow.id, realized_pnl: realizedPnL, threshold: threshold, }); } } ``` **Drawdown Örnek Senaryolar:** | Budget | Drawdown % | Threshold | Realized PnL | Sonuç | |--------|------------|-----------|--------------|-------| | $1,000 | 30% | -$300 | -$250 | ✅ Devam | | $1,000 | 30% | -$300 | -$300 | ⚠️ PAUSE | | $1,000 | 30% | -$300 | -$400 | ⚠️ PAUSE | | $5,000 | 20% | -$1,000 | -$800 | ✅ Devam | | $5,000 | 20% | -$1,000 | -$1,100 | ⚠️ PAUSE | ### 4.3 Position Tracking **Purpose**: Internal state vs Hyperliquid state consistency. **Use Cases**: - Max open positions enforcement - Symbol allocation calculation - Partial close ratio computation - Drift detection (phantom positions) **Phantom Position Problem**: Tracked ≠ Live state divergence scenarios: - Liquidation on Hyperliquid side - Network failure during close - Manual intervention **Resolution**: Reconciliation loop detects `tracked.notional > 0 && live.size == 0` → cleanup. **copy_follow_positions Tablosu:** ```sql CREATE TABLE copy_follow_positions ( id UUID PRIMARY KEY, follow_id UUID REFERENCES copy_follows(id), symbol TEXT NOT NULL, target_notional NUMERIC, -- Hedef exposure USD is_active BOOLEAN DEFAULT TRUE, updated_at TIMESTAMPTZ, UNIQUE(follow_id, symbol) ); ``` **Phantom Position Cleanup:** ```typescript // Tracked var ama live yok → sıfırla if (trackedPosition.target_notional !== 0 && livePosition.size === 0) { logger.info({ event: 'PHANTOM_POSITION_CLEANUP', symbol }); await copyFollowPositionsRepo.upsert({ follow_id, symbol, target_notional: 0, is_active: false, }); } ``` --- ## 5. Güvenlik ve Potansiyel Zafiyetler ### 5.1 Güvenlik Önlemleri **Security Principles**: | Principle | Implementation | |-----------|----------------| | Defense in Depth | Multi-layer: encryption + memory-only + short lifetime + audit | | Fail-Closed | DB unavailable → assume blacklisted, deny operation | | Least Privilege | Agent: TRADE_ONLY scope. Workers: partition-scoped. DB: RLS policies | **Key Security Layers**: - **Cryptographic**: AES-256-GCM (keys), HMAC-SHA256 (tokens), ECDSA (signatures) - **Access Control**: JWT validation, rate limiting, blacklist checks - **Operational**: Audit logging, anomaly detection hooks | Kategori | Önlem | Açıklama | |----------|-------|----------| | **Private Key** | AES-256-GCM | Authenticated encryption, tamper detection | | **Private Key** | Memory-only decrypt | Disk'e yazılmaz, log'lanmaz | | **Private Key** | Short lifetime | Decrypt → sign → garbage collect | | **Auth** | JWT + Refresh rotation | Access token kısa, refresh tek kullanımlık | | **Auth** | SIWE nonce single-use | Replay attack koruması | | **Auth** | Double signature verification | siwe lib + ethers backup | | **API** | Rate limiting | Token bucket (100 req, 10/sn refill) | | **API** | JWT validation | Her request'te token doğrulama | | **Copy Trade** | Blacklist | Her order öncesi leader/follower kontrolü | | **Copy Trade** | Drawdown protection | Otomatik pause | | **Copy Trade** | Fail-closed design | DB unavailable → blacklisted varsay | | **DB** | RLS (Row Level Security) | Supabase policies | | **DB** | Service role only | Direct DB access yok | ### 5.2 Potansiyel Zafiyetler ve Mitigasyonlar **Threat Model Categories**: - **Critical**: Direct fund access (key leak, agent compromise) - **High**: Broad unauthorized access (JWT secret, DB breach) - **Medium/Low**: Limited scope, already mitigated (rate limit bypass, replay) **Risk Matrix**: `Impact × Probability`. High-impact low-probability (key leak) → proactive mitigation. High-probability low-impact → operational controls. | Zafiyet | Risk | Mevcut Durum | Mitigation | |---------|------|--------------|------------| | **AGENT_ENCRYPTION_KEY leak** | Kritik | Env var | HSM/KMS kullanımı, key rotation | | **Worker compromise** | Kritik | Memory'de key | Process isolation, audit logging | | **DB SQL injection** | Orta | Parameterized queries | Supabase RPC, input validation | | **JWT secret leak** | Yüksek | Env var | Secret rotation, short TTL | | **Race condition (nonce)** | Düşük | Atomic consume | ✅ Mitigated | | **Replay attack (refresh)** | Düşük | Single-use token | ✅ Mitigated | | **HFT leader manipulation** | Orta | HFT detection | Threshold: ≥60 fills/min block | | **Blacklist bypass** | Düşük | Fail-closed | ✅ Mitigated | | **DoS via copy jobs** | Orta | Rate limiting | Per-follower: 10 order/dk | ### 5.3 Önerilen Gelecek İyileştirmeler | İyileştirme | Öncelik | Açıklama | |-------------|---------|----------| | HSM/KMS entegrasyonu | Yüksek | Agent key'leri hardware'de sakla | | Key rotation | Yüksek | Periyodik AGENT_ENCRYPTION_KEY değişimi | | Audit logging | Orta | Tüm sensitive operasyonları logla | | IP allowlist | Orta | Worker'lar için IP kısıtlaması | | 2FA for sensitive ops | Düşük | Agent revoke için ek doğrulama | | Anomaly detection | Orta | Unusual trading pattern algılama | --- ## 6. Scaling ve Kapasite ### 6.1 Tek Worker Kapasitesi **Worker Model**: Event loop based job processor. Partitioned queue for ordering guarantees. **Partition Strategy**: - Job → partition assignment (follow_id based hash) - Same-follow jobs processed sequentially (order guarantee) - Different partitions processed in parallel **Concurrency Model**: `jobConcurrency` parallel partition processing. Non-blocking I/O. **Mevcut Konfigürasyon:** ```typescript // src/config/index.ts copyTrade: { jobDequeueBatch: 20, // Her loop'ta çekilen job jobConcurrency: 5, // Paralel partition sayısı processLoopMs: 300, // Loop interval maxOrdersPerSec: 10, // Global order limiti maxLeaderSyncsPerSec: 5, // Leader sync limiti } ``` **Tek Worker Kapasite Tablosu:** | Metrik | Değer | Hesaplama | |--------|-------|-----------| | Loop/saniye | 3.33 | 1000 / 300ms | | Job/loop | 20 | jobDequeueBatch | | Teorik job/sn | 66.6 | 3.33 × 20 | | Paralel partition | 5 | jobConcurrency | | Max order/sn | 10 | maxOrdersPerSec | | Max order/dk | 600 | 10 × 60 | **Aktif Follow Kapasitesi (Tek Worker):** | Senaryo | Follow Sayısı | Açıklama | |---------|---------------|----------| | Conservative | 100-200 | Her follow 1-2 trade/dk | | Normal | 300-400 | Ortalama aktivite | | Optimized (batching) | 500-700 | High-volume batching aktif | | Peak handling | 200-300 | Yoğun piyasa koşulları | ### 6.2 Horizontal Scaling **Scaling Strategy**: Stateless workers, shared job queue. Add workers → linear capacity increase. **Partition Rebalancing**: Worker join/leave → partitions redistributed automatically. **Practical Limits**: - Hyperliquid API: ~100 req/s (shared across workers) - Supabase connections: Connection pool per worker - Network: Cross-AZ latency considerations **Multi-Worker Kapasite Tablosu:** | Worker Sayısı | Teorik Job/sn | Max Order/sn | Tahmini Follow | |---------------|---------------|--------------|----------------| | 1 | 66 | 10 | 200-400 | | 2 | 132 | 20 | 400-800 | | 3 | 198 | 30 | 600-1,200 | | 5 | 330 | 50 | 1,000-2,000 | | 10 | 660 | 100* | 2,000-4,000 | *\*Hyperliquid API limiti (~100 req/sn) ile sınırlı* ### 6.3 Darboğaz Analizi **Bottleneck Hierarchy** (typical order): 1. Hyperliquid API rate limits (external, hard limit) 2. Database connection pool exhaustion 3. Worker CPU/memory 4. Job queue throughput (DB IOPS) **Monitoring Metrics**: API latency p99, queue depth, connection pool utilization, worker CPU. | Darboğaz | Limit | Çözüm | |----------|-------|-------| | Hyperliquid API | ~100 req/sn | Multiple API keys, batching | | Supabase connections | ~100 concurrent | Connection pooling | | Worker memory | ~512MB/worker | Memory cleanup, caching | | Job queue throughput | DB IOPS | Read replicas, partitioning | ### 6.4 Ölçeklenebilirlik Yol Haritası **Scaling Tiers**: Infrastructure grows with user base. Avoid premature optimization. **Tier Progression**: - Tier 1 (100-500): Single worker, basic DB - Tier 2 (500-2K): Multi-worker, connection pooling - Tier 3 (2K-10K): Dedicated DB, Redis caching - Tier 4 (10K+): K8s orchestration, multi-region | Kullanıcı Sayısı | Gerekli Altyapı | Tahmini Maliyet | |------------------|-----------------|-----------------| | 100-500 | 1 worker, basic DB | $50-100/ay | | 500-2,000 | 2-3 worker, pro DB | $200-400/ay | | 2,000-5,000 | 5 worker, dedicated DB | $500-1,000/ay | | 5,000-10,000 | 10 worker, cluster DB, Redis | $2,000-4,000/ay | | 10,000+ | Kubernetes, multi-region | $5,000+/ay | --- ## 7. Özet Metrikler | Kategori | Metrik | Değer | |----------|--------|-------| | **Auth** | Access Token TTL | 15 dakika | | **Auth** | Refresh Token TTL | 30 gün | | **Auth** | Nonce TTL | 10 dakika | | **Agent** | Key Encryption | AES-256-GCM | | **Agent** | Scopes | TRADE_ONLY, TRADE_AND_WITHDRAW | | **Copy** | Trade Detection | 0-1500ms (moda göre) | | **Copy** | Reconciliation | 60 saniye | | **Copy** | Min Order Notional | $10 USD | | **Copy** | Default Drawdown | 30% | | **Risk** | Max Retry | 8 attempt | | **Risk** | Max Leverage | 50x (asset'e göre) | | **Rate** | API Limit | 100 req, 10/sn refill | | **Rate** | Order Limit | 10/sn global | | **Scale** | Single Worker | 200-500 follow | | **Scale** | Multi Worker (5x) | 1,000-2,000 follow | --- ## 8. Sonuç Dexly, güvenlik ve performansı ön planda tutan bir copy trading platformudur: - **Non-custodial**: Kullanıcı fonları her zaman kendi Hyperliquid hesabında - **Güvenli**: AES-256-GCM encrypted agent keys, JWT auth, rate limiting - **Ölçeklenebilir**: Horizontal scaling, 10,000+ kullanıcıya kadar büyüyebilir - **Risk-aware**: Drawdown protection, position limits, HFT detection Sistem tamamen açık kaynak değildir ancak enterprise-grade güvenlik standartlarını takip etmektedir.