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