---
# System prepended metadata

title: Dexly Platform Teknik Dokümantasyonu (Genişletilmiş)

---

# 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.
