Withdrawal Flow Architecture¶
Детальный поток вывода средств с админской авторизацией через HD Wallet мастер-адрес.
Withdrawal Process Flow¶
sequenceDiagram
participant User as 👤 User
participant UserApp as 📱 User App
participant API as 🌐 API
participant WithdrawalService as 💸 Withdrawal Service
participant BalanceService as 💰 Balance Service
participant Admin as 🔧 Admin
participant AdminApp as 🛠️ Admin App
participant HDService as 🏦 HD Wallet Service
participant MetaMask as 🦊 MetaMask
participant Blockchain as ⛓️ Blockchain
participant Database as 🗄️ Database
Note over User,Database: Step 1: User Requests Withdrawal
User->>UserApp: Enter withdrawal details
UserApp->>API: POST /user/withdrawal/request
API->>WithdrawalService: ValidateWithdrawalRequest()
WithdrawalService->>BalanceService: CheckUserBalance()
BalanceService->>Database: Query user balance
Database->>BalanceService: Balance data
BalanceService->>WithdrawalService: Balance sufficient
WithdrawalService->>Database: Create withdrawal request
Database->>WithdrawalService: Request created
WithdrawalService->>BalanceService: LockFunds()
BalanceService->>Database: Update balance (lock amount)
WithdrawalService->>API: Request submitted
API->>UserApp: Withdrawal request pending
UserApp->>User: Show pending status
Note over User,Database: Step 2: Admin Reviews Request
Admin->>AdminApp: Open withdrawal management
AdminApp->>API: GET /admin/withdrawal/pending
API->>Database: Query pending withdrawals
Database->>API: Pending withdrawal list
API->>AdminApp: Display pending requests
AdminApp->>Admin: Show requests for review
Admin->>AdminApp: Review and approve request
AdminApp->>API: POST /admin/withdrawal/approve
API->>WithdrawalService: ApproveWithdrawal()
WithdrawalService->>Database: Update status to "approved"
Note over User,Database: Step 3: Admin Processes Withdrawal
Admin->>AdminApp: Click "Process Withdrawal"
AdminApp->>API: POST /admin/withdrawal/process
API->>HDService: PrepareWithdrawalTransaction()
HDService->>HDService: Generate transaction to user address
HDService->>API: Return transaction details
API->>AdminApp: Transaction details
Note over User,Database: Step 4: Admin Signs Transaction
AdminApp->>MetaMask: Request transaction signature
MetaMask->>Admin: Show transaction details
Admin->>MetaMask: Approve transaction
MetaMask->>AdminApp: Signed transaction
AdminApp->>API: POST /admin/withdrawal/submit
API->>Blockchain: Submit signed transaction
Blockchain->>API: Transaction hash
API->>Database: Update withdrawal with tx_hash
Database->>API: Updated
Note over User,Database: Step 5: Transaction Monitoring
API->>API: Monitor transaction status
API->>Blockchain: Check transaction status
Blockchain->>API: Transaction confirmed
API->>WithdrawalService: TransactionConfirmed()
WithdrawalService->>Database: Update status to "completed"
WithdrawalService->>BalanceService: UnlockFunds()
BalanceService->>Database: Update balance (remove locked amount)
Note over User,Database: Step 6: User Notification
API->>UserApp: WebSocket notification
UserApp->>User: Withdrawal completed notification
Withdrawal State Machine¶
stateDiagram-v2
[*] --> Requested: User submits withdrawal request
Requested --> Pending: Initial validation passed
Requested --> Rejected: Validation failed
Pending --> Approved: Admin approval
Pending --> Rejected: Admin rejection
Approved --> Executing: Admin initiates transaction
Executing --> Submitted: Transaction submitted to blockchain
Executing --> Failed: Transaction submission failed
Submitted --> Confirmed: Transaction confirmed on blockchain
Submitted --> Failed: Transaction failed on blockchain
Confirmed --> Completed: Funds unlocked, user notified
Failed --> Approved: Admin retry
Rejected --> [*]: Final rejection
Completed --> [*]: Successful completion
Admin Withdrawal Management Dashboard¶
graph TB
subgraph "Admin Dashboard"
PENDING[📋 Pending Requests]
APPROVED[✅ Approved Requests]
EXECUTING[⚙️ Executing Requests]
HISTORY[📚 Withdrawal History]
end
subgraph "Withdrawal Actions"
REVIEW[👀 Review Request]
APPROVE[✅ Approve]
REJECT[❌ Reject]
EXECUTE[⚡ Execute]
RETRY[🔄 Retry Failed]
end
subgraph "Validation Checks"
BALANCE[💰 Balance Check]
AMOUNT[💵 Amount Validation]
ADDRESS[📍 Address Validation]
LIMITS[📏 Limits Check]
end
PENDING --> REVIEW
REVIEW --> BALANCE
REVIEW --> AMOUNT
REVIEW --> ADDRESS
REVIEW --> LIMITS
BALANCE --> APPROVE
AMOUNT --> APPROVE
ADDRESS --> APPROVE
LIMITS --> APPROVE
BALANCE --> REJECT
AMOUNT --> REJECT
ADDRESS --> REJECT
LIMITS --> REJECT
APPROVE --> APPROVED
REJECT --> HISTORY
APPROVED --> EXECUTE
EXECUTE --> EXECUTING
EXECUTING --> RETRY
classDef dashboard fill:#E8F5E8,stroke:#4CAF50,color:#000
classDef actions fill:#FFF3E0,stroke:#FF9800,color:#000
classDef validation fill:#F3E5F5,stroke:#9C27B0,color:#000
class PENDING,APPROVED,EXECUTING,HISTORY dashboard
class REVIEW,APPROVE,REJECT,EXECUTE,RETRY actions
class BALANCE,AMOUNT,ADDRESS,LIMITS validation
Balance Management During Withdrawal¶
graph TB
subgraph "User Balance States"
AVAILABLE[💰 Available Balance]
LOCKED[🔒 Locked Balance]
PENDING[⏳ Pending Balance]
end
subgraph "Withdrawal Process"
REQUEST[📝 Withdrawal Request]
LOCK[🔒 Lock Funds]
EXECUTE[⚡ Execute Transaction]
COMPLETE[✅ Complete Withdrawal]
FAIL[❌ Transaction Failed]
end
AVAILABLE --> REQUEST
REQUEST --> LOCK
LOCK --> LOCKED
LOCKED --> EXECUTE
EXECUTE --> COMPLETE
EXECUTE --> FAIL
COMPLETE --> AVAILABLE
FAIL --> AVAILABLE
REQUEST -.-> PENDING
PENDING -.-> AVAILABLE
classDef balance fill:#E3F2FD,stroke:#2196F3,color:#000
classDef process fill:#FFF8E1,stroke:#FFC107,color:#000
class AVAILABLE,LOCKED,PENDING balance
class REQUEST,LOCK,EXECUTE,COMPLETE,FAIL process
Security and Validation Flow¶
flowchart TD
START[📝 Withdrawal Request] --> BASIC_VAL[✅ Basic Validation]
BASIC_VAL --> USER_AUTH{User Authenticated?}
USER_AUTH -->|No| AUTH_ERROR[❌ Authentication Error]
USER_AUTH -->|Yes| AMOUNT_VAL[💵 Amount Validation]
AMOUNT_VAL --> AMOUNT_OK{Amount Valid?}
AMOUNT_OK -->|No| AMOUNT_ERROR[❌ Invalid Amount]
AMOUNT_OK -->|Yes| BALANCE_CHECK[💰 Balance Check]
BALANCE_CHECK --> BALANCE_OK{Sufficient Balance?}
BALANCE_OK -->|No| BALANCE_ERROR[❌ Insufficient Balance]
BALANCE_OK -->|Yes| ADDRESS_VAL[📍 Address Validation]
ADDRESS_VAL --> ADDRESS_OK{Valid Address?}
ADDRESS_OK -->|No| ADDRESS_ERROR[❌ Invalid Address]
ADDRESS_OK -->|Yes| LIMITS_CHECK[📏 Limits Check]
LIMITS_CHECK --> LIMITS_OK{Within Limits?}
LIMITS_OK -->|No| LIMITS_ERROR[❌ Exceeds Limits]
LIMITS_OK -->|Yes| ADMIN_QUEUE[📋 Admin Review Queue]
ADMIN_QUEUE --> ADMIN_REVIEW[👨💼 Admin Review]
ADMIN_REVIEW --> ADMIN_DECISION{Admin Decision}
ADMIN_DECISION -->|Reject| ADMIN_REJECT[❌ Admin Rejection]
ADMIN_DECISION -->|Approve| ADMIN_APPROVE[✅ Admin Approval]
ADMIN_APPROVE --> EXECUTE_TX[⚡ Execute Transaction]
EXECUTE_TX --> TX_SUCCESS{Transaction Success?}
TX_SUCCESS -->|No| TX_ERROR[❌ Transaction Failed]
TX_SUCCESS -->|Yes| SUCCESS[✅ Withdrawal Completed]
%% Error handling
AUTH_ERROR --> RETRY[🔄 User Can Retry]
AMOUNT_ERROR --> RETRY
BALANCE_ERROR --> RETRY
ADDRESS_ERROR --> RETRY
LIMITS_ERROR --> RETRY
ADMIN_REJECT --> FINAL_REJECT[❌ Final Rejection]
TX_ERROR --> ADMIN_RETRY[🔄 Admin Can Retry]
ADMIN_RETRY --> EXECUTE_TX
classDef validation fill:#E8F5E8,stroke:#4CAF50,color:#000
classDef error fill:#FFEBEE,stroke:#F44336,color:#000
classDef success fill:#E0F2F1,stroke:#4CAF50,color:#000
classDef process fill:#FFF3E0,stroke:#FF9800,color:#000
class BASIC_VAL,AMOUNT_VAL,BALANCE_CHECK,ADDRESS_VAL,LIMITS_CHECK validation
class AUTH_ERROR,AMOUNT_ERROR,BALANCE_ERROR,ADDRESS_ERROR,LIMITS_ERROR,ADMIN_REJECT,TX_ERROR,FINAL_REJECT error
class SUCCESS success
class START,ADMIN_QUEUE,ADMIN_REVIEW,EXECUTE_TX,RETRY,ADMIN_RETRY process
Database Schema for Withdrawals¶
erDiagram
USERS {
uuid id PK
string email
string status
timestamp created_at
timestamp updated_at
}
TRANSACTION_REQUESTS {
uuid id PK
uuid user_id FK
uuid transaction_id FK
string type
string from_address
string to_address
decimal amount
string currency
string status
jsonb metadata
timestamp created_at
timestamp processed_at
}
TRANSACTIONS {
uuid id PK
uuid user_id FK
integer network_id
string type
string from_address
string to_address
decimal amount
string currency
string tx_hash
string status
integer confirmations
timestamp created_at
timestamp confirmed_at
}
USERS ||--o{ TRANSACTION_REQUESTS : requests
TRANSACTION_REQUESTS ||--o| TRANSACTIONS : creates
USERS ||--o{ TRANSACTIONS : has
АРХИТЕКТУРНЫЙ ПРИНЦИП: Балансы рассчитываются динамически из transactions таблицы (единый источник истины). Кеширование финансовых данных в отдельных таблицах запрещено согласно CLAUDE.md принципам.
Конфигурация лимитов¶
Все лимиты withdrawal хранятся в unified_config.go:
financial:
limits:
min_withdrawal: "0.01"
max_withdrawal: "100000.00"
daily_withdrawal_limit: "10000.00"
weekly_withdrawal_limit: "50000.00"
monthly_withdrawal_limit: "200000.00"
Implementation Details¶
Withdrawal Request Validation¶
func (s *WithdrawalService) ValidateWithdrawalRequest(req *WithdrawalRequest) error {
// Basic validation
if req.Amount.IsZero() || req.Amount.IsNegative() {
return errors.New("invalid withdrawal amount")
}
// Address validation
if !s.validateAddress(req.ToAddress) {
return errors.New("invalid withdrawal address")
}
// Balance check (calculated from transactions)
currentBalance, err := s.balanceService.CalculateUserBalance(req.UserID, req.Currency)
if err != nil {
return err
}
if currentBalance.LessThan(req.Amount) {
return errors.New("insufficient balance")
}
// Limits check using unified config
cfg := s.config
if req.Amount.LessThan(cfg.GetMinWithdrawal()) {
return errors.New("amount below minimum withdrawal limit")
}
if req.Amount.GreaterThan(cfg.GetMaxWithdrawal()) {
return errors.New("amount exceeds maximum withdrawal limit")
}
// Check temporal limits (daily/weekly/monthly)
if err := s.checkTemporalLimits(req.UserID, req.Amount, req.Currency); err != nil {
return err
}
return nil
}
Fund Locking¶
func (s *BalanceService) LockFunds(userID string, amount decimal.Decimal, currency string) error {
return s.db.Transaction(func(tx *sql.Tx) error {
// Calculate current balance from transactions (single source of truth)
currentBalance, err := s.calculateUserBalance(tx, userID, currency)
if err != nil {
return err
}
if currentBalance.LessThan(amount) {
return errors.New("insufficient available balance")
}
// Create a "lock" transaction to reserve funds
lockTransaction := &models.Transaction{
UserID: userID,
Type: "lock",
Amount: amount.Neg(), // Negative amount to reduce available balance
Currency: currency,
Status: "confirmed",
Metadata: map[string]interface{}{"lock_type": "withdrawal_pending"},
}
return s.createTransaction(tx, lockTransaction)
})
}
// Calculate user balance from transactions table (CLAUDE.md compliant)
func (s *BalanceService) calculateUserBalance(tx *sql.Tx, userID string, currency string) (decimal.Decimal, error) {
query := `
SELECT COALESCE(SUM(amount), 0) as balance
FROM transactions
WHERE user_id = $1 AND currency = $2 AND status = 'confirmed'
`
var balance decimal.Decimal
err := tx.QueryRow(query, userID, currency).Scan(&balance)
return balance, err
}
Transaction Processing¶
func (s *WithdrawalService) ProcessWithdrawal(requestID string, adminID string) error {
// Get withdrawal request from transaction_requests
request, err := s.getTransactionRequest(requestID)
if err != nil {
return err
}
if request.Type != "withdrawal" {
return errors.New("invalid request type")
}
// Prepare transaction
tx, err := s.hdService.PrepareWithdrawalTransaction(
request.ToAddress,
request.Amount,
request.Currency,
)
if err != nil {
return err
}
// Update request status
request.Status = "executing"
request.ProcessedAt = time.Now()
// Create transaction record
transaction := &models.Transaction{
UserID: request.UserID,
NetworkID: s.config.GetDefaultNetworkID(),
Type: "withdrawal",
FromAddress: s.config.GetHDWalletMasterAddress(),
ToAddress: request.ToAddress,
Amount: request.Amount,
Currency: request.Currency,
Status: "pending",
}
// Save both records
if err := s.updateTransactionRequest(request); err != nil {
return err
}
if err := s.createTransaction(transaction); err != nil {
return err
}
return nil
}
Security Considerations¶
- Multi-level Approval: Требует админского одобрения для всех withdrawal
- Balance Locking: Средства блокируются во время обработки
- Address Validation: Проверка корректности адреса получателя
- Unified Config Limits: Все лимиты централизованы в unified_config.go
- Минимальные и максимальные суммы withdrawal
- Дневные, недельные и месячные лимиты
-
Комиссии за операции
-
Audit Trail: Полная история в transaction_requests и transactions
- HD Wallet Security: Только мастер-адрес может отправлять средства
- Confirmation Requirements: Ожидание подтверждений блокчейна
- Retry Logic: Автоматическое повторение при временных ошибках
- Admin Authentication: Проверка админских прав через wallet signature
- Real-time Monitoring: Мониторинг статуса транзакций в реальном времени
Error Recovery Scenarios¶
Failed Transaction Recovery¶
flowchart TD
TX_FAIL[❌ Transaction Failed] --> ANALYZE[🔍 Analyze Failure]
ANALYZE --> TEMP_FAIL{Temporary Failure?}
TEMP_FAIL -->|Yes| AUTO_RETRY[🔄 Auto Retry]
AUTO_RETRY --> MAX_RETRIES{Max Retries Reached?}
MAX_RETRIES -->|No| RETRY_TX[⚡ Retry Transaction]
MAX_RETRIES -->|Yes| ADMIN_MANUAL[👨💼 Admin Manual Review]
TEMP_FAIL -->|No| PERM_FAIL[❌ Permanent Failure]
PERM_FAIL --> UNLOCK_FUNDS[🔓 Unlock Funds]
UNLOCK_FUNDS --> NOTIFY_USER[📢 Notify User]
RETRY_TX --> TX_SUCCESS{Success?}
TX_SUCCESS -->|Yes| COMPLETE[✅ Complete Withdrawal]
TX_SUCCESS -->|No| TX_FAIL
ADMIN_MANUAL --> ADMIN_DECISION{Admin Decision}
ADMIN_DECISION -->|Retry| RETRY_TX
ADMIN_DECISION -->|Cancel| UNLOCK_FUNDS
classDef fail fill:#FFEBEE,stroke:#F44336,color:#000
classDef process fill:#FFF3E0,stroke:#FF9800,color:#000
classDef success fill:#E0F2F1,stroke:#4CAF50,color:#000
class TX_FAIL,PERM_FAIL fail
class ANALYZE,AUTO_RETRY,RETRY_TX,ADMIN_MANUAL,UNLOCK_FUNDS,NOTIFY_USER process
class COMPLETE success