اسناد حسابداری بروکر

همان فایل‌های مخزن؛ سیاست تازه نیست

معماری حسابداری بروکر A-Book با TigerBeetle

نام سند: Broker Accounting Architecture
زبان مرجع: فارسی
نسخه سند: 0.021
وضعیت: Architecture Baseline / local ops console + named commands
آخرین بروزرسانی: 2026-09-12
دامنه: A-Book Derivatives + Real Spot + On-Chain Deposit/Withdrawal + Custody + Venue/LP Reconciliation
Financial Source of Truth: TigerBeetle
Operational Source of Truth: PostgreSQL


0. قرارداد اجباری برای Agent توسعه‌دهنده

این فایل فقط یک توضیح معماری نیست. این فایل Specification زنده پروژه و مرجع تصمیم‌های معماری و حسابداری است.

توضیح کوتاه اصطلاحات کنسول و مرحله‌ها (custody، lock/broadcast/confirm، …) در BROKER_ACCOUNTING_GLOSSARY_FA.md است. آن فایل سیاست تازه نمی‌سازد؛ اگر تعارض بود همین سند مقدم است.

Agent توسعه‌دهنده موظف است قبل از هر تغییر مرتبط با حسابداری، معاملات، پوزیشن، واریز/برداشت، کیف پول، LP/Venue، reconciliation یا مدل داده، این فایل را بخواند.

0.1. قانون Document-First

ترتیب هر تغییر باید دقیقاً به شکل زیر باشد:

  1. نیاز یا تغییر مورد نظر مشخص شود.
  2. بخش یا بخش‌های متاثر در همین سند مشخص شوند.
  3. قبل از تغییر کد، سند به وضعیت جدید بروزرسانی شود.
  4. نسخه سند افزایش پیدا کند.
  5. در Change Log سراسری، تغییر ثبت شود.
  6. در Change History محلی بخش مربوطه نیز تغییر ثبت شود.
  7. سپس کد نوشته یا تغییر داده شود.
  8. تست‌های مرتبط نوشته یا بروزرسانی شوند.
  9. در پایان بررسی شود که implementation دقیقاً با سند مطابقت دارد.
  10. اگر implementation با طراحی مستندشده متفاوت شد، ابتدا سند اصلاح شود و بعد task نهایی شود.

تغییر رفتار سیستم بدون بروزرسانی این سند مجاز نیست.


0.2. Versioning

نسخه اولیه:

0.001

نسخه‌ها در فاز اولیه به صورت افزایشی نگهداری می‌شوند:

0.001
0.002
0.003
...
0.010
...

هر تغییر معماری، رفتار مالی، schema، state-machine، invariant، settlement rule یا integration contract باید نسخه سند را افزایش دهد.

تغییرات صرفاً cosmetic که هیچ اثری روی رفتار سیستم ندارند الزامی به افزایش نسخه ندارند.


0.3. Versioning هر بخش

هر بخش اصلی باید metadata زیر را داشته باشد:

Introduced: 0.001
Last Changed: 0.001
Status: Designed | Implementing | Implemented | Deprecated

نمونه:

Position Accounting
Introduced: 0.001
Last Changed: 0.003
Status: Implemented

و پایین همان بخش:

### Change History — Position Accounting

| Version | Date | Change |
|---|---|---|
| 0.001 | 2026-09-12 | تعریف اولیه مدل Position و Margin |
| 0.003 | 2026-09-20 | Unrealized PnL از Ledger حذف و فقط Realized PnL settle شد |

Agent حق حذف history قبلی را ندارد. History باید append-only باشد.


0.4. زبان Documentation

تمام توضیحات معماری، reasoning، flowها، changelog و تصمیمات باید به فارسی نوشته شوند.

نام‌های فنی، identifierها، enumها، table nameها، API nameها، class nameها و code snippetها باید انگلیسی باقی بمانند.

مثال:

موجودی قابل استفاده کاربر در Account Type با نام USER_AVAILABLE نگهداری می‌شود.

0.5. ثبت دلیل تغییر

هر تغییر غیر trivial باید مشخص کند:


0.6. ممنوعیت تغییر تاریخچه مالی

در TigerBeetle:


1. هدف سیستم

Introduced: 0.001
Last Changed: 0.001
Status: Designed

هدف، ساخت لایه حسابداری یک Broker است که موارد زیر را پشتیبانی کند:

  1. حساب چنددارایی برای هر User
  2. معاملات Spot واقعی
  3. معاملات Leveraged / Derivatives به صورت A-Book
  4. Margin و Collateral
  5. Realized PnL
  6. Funding
  7. Trading Fee
  8. Spread
  9. Execution Fee
  10. Deposit روی Blockchain
  11. Withdrawal روی Blockchain
  12. Internal Transfer
  13. Hot / Warm / Cold Wallet
  14. انتقال سرمایه بین Broker و Venue/LP
  15. چند Venue/LP
  16. چند Network
  17. Reconciliation با Blockchain
  18. Reconciliation با Venue/LP
  19. Idempotent financial operations
  20. Audit trail کامل

1.1. اصل کلیدی

TigerBeetle جواب این سؤال را می‌دهد:

چه کسی مالک/طلبکار چه مقدار ارزش است و این ارزش چگونه جابه‌جا شده است؟

PostgreSQL جواب این سؤال‌ها را می‌دهد:

Order چیست؟
Position چیست؟
Fill چیست؟
TxHash چیست؟
Hedge روی کدام Venue انجام شد؟
Position با چه Entry Price باز شده است؟

بنابراین:

Money / Asset / Financial Settlement
                ↓
           TigerBeetle

Order / Position / Fill / Blockchain Tx / Venue Execution
                ↓
            PostgreSQL

2. مفروضات نسخه 0.001

Introduced: 0.001
Last Changed: 0.001
Status: Designed

نسخه اولیه بر اساس این assumptions ساخته می‌شود:

Trading

Custody

Database

Amount

مثال:

BTC:
scale = 8
1 BTC = 100_000_000 units

USDT:
scale = 6
1 USDT = 1_000_000 units

3. معماری سطح بالا

Introduced: 0.001
Last Changed: 0.001
Status: Designed

                              ┌──────────────────┐
                              │       User       │
                              └────────┬─────────┘
                                       │
                                       ▼
                              ┌──────────────────┐
                              │    Broker API    │
                              └────────┬─────────┘
                                       │
        ┌──────────────────────────────┼──────────────────────────────┐
        │                              │                              │
        ▼                              ▼                              ▼
┌────────────────┐            ┌────────────────┐             ┌────────────────┐
│ Order / OMS    │            │ Wallet Service │             │ Accounting Svc │
└───────┬────────┘            └───────┬────────┘             └───────┬────────┘
        │                              │                              │
        ▼                              ▼                              ▼
┌────────────────┐            ┌────────────────┐             ┌────────────────┐
│ Position/Risk  │            │ Blockchain     │             │ TigerBeetle    │
└───────┬────────┘            │ Integration    │             │ Ledger         │
        │                     └────────────────┘             └────────────────┘
        ▼
┌────────────────┐
│ Execution /    │
│ Hedge Service  │
└───────┬────────┘
        │
        ▼
┌────────────────┐
│ LP / Exchange  │
│ Binance/...    │
└────────────────┘


                    ┌────────────────────────────┐
                    │         PostgreSQL         │
                    │                            │
                    │ orders                     │
                    │ fills                      │
                    │ positions                  │
                    │ hedge_orders               │
                    │ hedge_fills                │
                    │ hedge_allocations          │
                    │ deposits                   │
                    │ withdrawals                │
                    │ blockchain_transactions    │
                    │ accounting_events          │
                    │ reconciliation_runs        │
                    └────────────────────────────┘

4. تفکیک مسئولیت سرویس‌ها

Introduced: 0.001
Last Changed: 0.001
Status: Designed

4.1. Accounting Service

تنها سرویس مجاز برای ایجاد Financial Transfer در TigerBeetle.

وظایف:

هیچ سرویس دیگری نباید مستقیم TigerBeetle Transfer ایجاد کند.


4.2. Order Management System — OMS

مسئول:

OMS مالک balance نیست.


4.3. Position / Risk Service

مسئول:

Unrealized PnL در TigerBeetle ثبت نمی‌شود.


4.4. Execution / Hedge Service

مسئول:


4.5. Wallet Service

مسئول:

Private Key نباید در Accounting Service یا API Service نگهداری شود.


5. مدل Ledger در TigerBeetle

Introduced: 0.001
Last Changed: 0.002
Status: Designed / Pre-Provisioning Baseline

هر Asset یک ledger مجزا دارد. برای جلوگیری از اشتباه انسانی بین ledger و account code، فضای عددی ledger از Account Codeها جدا نگه داشته می‌شود.

Registry اولیه:

10_001 = USDT
10_002 = USDC

20_001 = BTC
20_002 = ETH

مقادیر واقعی از assets.tigerbeetle_ledger_id خوانده می‌شوند و نباید در business code به شکل پراکنده hard-code شوند.

5.1. قانون Ledger

Transfer معمولی TigerBeetle فقط بین دو Account روی یک ledger یکسان انجام می‌شود.

برای Exchange بین دو Asset، چند Transfer روی ledgerهای متفاوت ساخته می‌شود. اگر این legها یک accounting operation واحد هستند، باید در همان create_transfers request به صورت یک linked chain پیوسته ارسال شوند.

5.2. Asset Scale یک تصمیم immutable است

Scale هر Asset باید قبل از اولین Account/Transfer آن ledger نهایی شود.

Baseline اولیه:

USDT scale = 6
USDC scale = 6
BTC  scale = 8
ETH  scale = 18

قواعد:

Change History — Ledger & Scale

Version Date Change
0.001 2026-09-12 تعریف اولیه ledger-per-asset
0.002 2026-09-12 جداسازی فضای Ledger ID از Account Code و تثبیت scaleهای اولیه

6. Asset Registry

Introduced: 0.001
Last Changed: 0.002
Status: Designed / Pre-Provisioning Baseline

PostgreSQL باید Asset Registry داشته باشد.

نمونه:

assets

id
symbol
tigerbeetle_ledger_id
scale
status
economic_asset_group
created_at
updated_at

نمونه داده:

BTC   ledger=20_001 scale=8
USDT  ledger=10_001 scale=6
USDC  ledger=10_002 scale=6
ETH   ledger=20_002 scale=18

6.1. Network با Asset یکی نیست

مثلاً:

USDT ERC20
USDT TRC20

اگر از نظر business balance داخلی fungible باشد:

Ledger = USDT

ولی custody accountهای جدا دارند:

CUSTODY_USDT_ETHEREUM
CUSTODY_USDT_TRON

اما:

BTC != WBTC

WBTC باید Asset/Ledger جدا داشته باشد مگر تصمیم معماری مستند دیگری گرفته شود.


7. طبقه‌بندی Accountها

Introduced: 0.001
Last Changed: 0.003
Status: Designed / Pre-Provisioning Baseline

7.1. Liability Accounts — بدهی Broker به User

برای هر User و Asset:

USER_AVAILABLE
USER_SPOT_LOCKED
USER_MARGIN
USER_WITHDRAWAL_LOCKED

Balance اقتصادی Liability:

balance = credits_posted - debits_posted

Constraint:

flags.debits_must_not_exceed_credits = true

7.2. Broker Asset Accounts

CUSTODY_HOT
CUSTODY_WARM
CUSTODY_COLD
CUSTODY_DEPOSIT
ONCHAIN_OUTBOUND_IN_FLIGHT

VENUE_ASSET
VENUE_IN_TRANSIT

CLIENT_BAD_DEBT_RECEIVABLE

Balance اقتصادی Asset:

balance = debits_posted - credits_posted

برای Asset Accountهایی که نباید منفی شوند:

flags.credits_must_not_exceed_debits = true

CLIENT_BAD_DEBT_RECEIVABLE یک Asset/Receivable است و فقط از مسیر Liquidation/Bad Debt ایجاد می‌شود.


7.3. Revenue Accounts

TRADING_FEE_REVENUE
SPREAD_REVENUE
WITHDRAWAL_FEE_REVENUE

CLIENT_FUNDING_REVENUE
VENUE_FUNDING_REVENUE

CLIENT_LOSS_REVENUE
HEDGE_PNL_REVENUE
LIQUIDATION_FEE_REVENUE

Revenue به صورت credit-balance مدل می‌شود.


7.4. Expense Accounts

NETWORK_FEE_EXPENSE
VENUE_EXECUTION_FEE_EXPENSE
CLIENT_PROFIT_EXPENSE
HEDGE_PNL_EXPENSE

CLIENT_FUNDING_EXPENSE
VENUE_FUNDING_EXPENSE

BAD_DEBT_EXPENSE
SPOT_PRICE_IMPROVEMENT_EXPENSE

Expense به صورت debit-balance مدل می‌شود.


7.5. Clearing / Exception Accounts

SPOT_SETTLEMENT_CLEARING

DEPOSIT_SUSPENSE
WITHDRAWAL_SUSPENSE
RECONCILIATION_SUSPENSE
CHAIN_REORG_RECEIVABLE
MANUAL_ADJUSTMENT_CLEARING

SPOT_SETTLEMENT_CLEARING برای هر Asset ساخته می‌شود و client settlement را از venue execution جدا می‌کند.

این حساب عمداً می‌تواند موقتاً debit یا credit balance داشته باشد و نباید flag محدودکننده‌ی debits_must_not_exceed_credits یا credits_must_not_exceed_debits داشته باشد.

Target steady-state:

SPOT_SETTLEMENT_CLEARING balance = 0

هر balance غیر صفر خارج از settlement SLA باید alert ایجاد کند.

این alert یک کنترل عملیاتی سخت است، نه صرفاً observability. در Production، عبور clearing unexplained از hard threshold زمانی یا مقداری باید circuit breaker مربوط به Spot را فعال کند؛ settlement/reconciliation/correction ادامه پیدا می‌کنند ولی ایجاد Client Spot Settlement جدید برای دامنه متاثر متوقف می‌شود تا exposure توضیح داده یا با override حسابرسی‌شده رفع شود.


7.6. Broker Equity / Reserve Accounts

INSURANCE_FUND_RESERVE
BROKER_SPOT_INVENTORY_RESERVE

هر دو credit-balance reserve/equity هستند.

INSURANCE_FUND_RESERVE برای جذب loss طبق Risk Specification استفاده می‌شود. مدل مصرف و replenishment آن قبل از فعال‌شدن liquidation production باید نهایی شود.

BROKER_SPOT_INVENTORY_RESERVE برای هر Asset، مالکیت اقتصادی خود Broker روی inventory آن Asset را نمایش می‌دهد و location custody نیست. محل فیزیکی Asset همچنان در CUSTODY_* یا VENUE_ASSET نگهداری می‌شود.

Seed کردن inventory متعلق به Broker به صورت مفهومی:

Debit:  CUSTODY_HOT_BTC
Credit: BROKER_SPOT_INVENTORY_RESERVE_BTC

در نتیجه Asset فیزیکی و Equity/Inventory اقتصادی Broker هر دو قابل ردیابی‌اند. Internal Spot Fill می‌تواند این reserve را مصرف یا افزایش دهد بدون اینکه Venue جعلی ساخته شود.

Change History — Account Classification

Version Date Change
0.001 2026-09-12 تعریف اولیه User/Broker/Revenue/Expense/Clearing
0.002 2026-09-12 افزودن Spot Clearing، Funding accounts، Liquidation Fee، Bad Debt و یکسان‌سازی HEDGE_PNL naming
0.003 2026-09-12 افزودن Broker Spot Inventory Reserve و تبدیل Spot Clearing alert به hard operational control

8. Account Code Registry

Introduced: 0.001
Last Changed: 0.003
Status: Frozen for Initial Provisioning

Account Codeها از v0.002 به بعد append-only هستند. تغییر معنای code موجود ممنوع است.

# User liabilities
1001 USER_AVAILABLE
1002 USER_SPOT_LOCKED
1003 USER_MARGIN
1004 USER_WITHDRAWAL_LOCKED

# Broker assets
2001 CUSTODY_HOT
2002 CUSTODY_WARM
2003 CUSTODY_COLD
2004 CUSTODY_DEPOSIT
2005 ONCHAIN_OUTBOUND_IN_FLIGHT
2006 CLIENT_BAD_DEBT_RECEIVABLE

2101 VENUE_ASSET
2102 VENUE_IN_TRANSIT

# Revenue / income
3001 TRADING_FEE_REVENUE
3002 SPREAD_REVENUE
3003 WITHDRAWAL_FEE_REVENUE
3004 CLIENT_FUNDING_REVENUE
3005 CLIENT_LOSS_REVENUE
3006 HEDGE_PNL_REVENUE
3007 VENUE_FUNDING_REVENUE
3008 LIQUIDATION_FEE_REVENUE

# Expenses
4001 NETWORK_FEE_EXPENSE
4002 VENUE_EXECUTION_FEE_EXPENSE
4003 CLIENT_PROFIT_EXPENSE
4004 HEDGE_PNL_EXPENSE
4005 CLIENT_FUNDING_EXPENSE
4006 VENUE_FUNDING_EXPENSE
4007 BAD_DEBT_EXPENSE
4008 SPOT_PRICE_IMPROVEMENT_EXPENSE

# Clearing / exception
5001 DEPOSIT_SUSPENSE
5002 WITHDRAWAL_SUSPENSE
5003 RECONCILIATION_SUSPENSE
5004 CHAIN_REORG_RECEIVABLE
5005 MANUAL_ADJUSTMENT_CLEARING
5006 SPOT_SETTLEMENT_CLEARING

# Equity / reserve
6001 INSURANCE_FUND_RESERVE
6002 BROKER_SPOT_INVENTORY_RESERVE

Naming Rule

نام canonical این پروژه:

HEDGE_PNL_REVENUE
HEDGE_PNL_EXPENSE

نام HEDGE_LOSS_EXPENSE از v0.002 deprecated است و نباید در code جدید استفاده شود.

Change History — Account Codes

Version Date Change
0.001 2026-09-12 Registry اولیه
0.002 2026-09-12 تکمیل Funding/Liquidation/Bad-Debt/Spot-Clearing accounts و رفع naming inconsistency
0.003 2026-09-12 افزودن BROKER_SPOT_INVENTORY_RESERVE برای internal inventory spot fills

9. TigerBeetle Account Provisioning & Immutable Mapping

Introduced: 0.001
Last Changed: 0.006
Status: Frozen for Initial Provisioning

Accountهای TigerBeetle بعد از creation از نظر schema fields immutable هستند؛ بنابراین موارد این بخش قبل از اولین create_accounts قرارداد محسوب می‌شوند.

9.1. Account Fields

id
ledger
code
flags
user_data_128
user_data_64
user_data_32

Mapping:

id
  = TigerBeetle time-based u128 ID
  = generated once and persisted in accounting_accounts before create_accounts

ledger
  = immutable asset ledger from Asset Registry

code
  = immutable Account Code Registry

user_data_128
  = canonical owning/related entity ID
    User Account      -> user_id_u128
    Venue Account     -> venue_id_u128
    Custody Account   -> custody_wallet_group_id_u128
    System Account    -> stable system_entity_id_u128

user_data_64
  = 0 in schema v0.002

user_data_32
  = 0 in schema v0.002

استفاده از user_data_64 یا user_data_32 در آینده نیازمند account migration برای Accountهای قدیمی است؛ Agent حق ندارد معنای پنهانی به این فیلدها اضافه کند.


9.2. flags.history اجباری است

تمام Accountهای production این پروژه باید در زمان creation دارای:

flags.history = true

باشند.

دلیل:

Constraint flags بر اساس نوع Account علاوه بر history تنظیم می‌شوند:

User Liability:
  history
  debits_must_not_exceed_credits

Non-negative Broker Asset:
  history
  credits_must_not_exceed_debits

Clearing / Revenue / Expense / Reserve:
  history
  + constraint only if explicitly defined by account type

9.3. flags.closed و Freeze

flags.closed در TigerBeetle انتقال‌های جدید را رد می‌کند. با این حال، User Freeze عمومی نباید صرفاً با closed مدل شود زیرا freeze عملیاتی ممکن است همچنان نیازمند correction، deposit handling یا settlement کنترل‌شده باشد.

Baseline:

users.financial_status:
ACTIVE
FROZEN
CLOSED

9.4. Historical Import قبل از Go-Live

اگر پروژه historical financial data دارد، migration باید قبل از live traffic انجام شود.

قواعد baseline:

migration_mode =
  FRESH_START
  | HISTORICAL_IMPORT

برای HISTORICAL_IMPORT:

انتخاب migration_mode یک Go-Live blocker است و نمی‌تواند به Phaseهای پایانی موکول شود.

9.5. Flag Matrix قطعی برای Provisioning

این جدول قرارداد immutable provisioning است. Agent حق ندارد بر اساس «نوع کلی Account» flag را حدس بزند؛ برای هر code دقیقاً مجموعه زیر ساخته می‌شود.

Legend:

H   = history
D<=C = debits_must_not_exceed_credits
C<=D = credits_must_not_exceed_debits
Code Account Required Flags
1001 USER_AVAILABLE H + D<=C
1002 USER_SPOT_LOCKED H + D<=C
1003 USER_MARGIN H + D<=C
1004 USER_WITHDRAWAL_LOCKED H + D<=C
2001 CUSTODY_HOT H + C<=D
2002 CUSTODY_WARM H + C<=D
2003 CUSTODY_COLD H + C<=D
2004 CUSTODY_DEPOSIT H + C<=D
2005 ONCHAIN_OUTBOUND_IN_FLIGHT H + C<=D
2006 CLIENT_BAD_DEBT_RECEIVABLE H + C<=D
2101 VENUE_ASSET H + C<=D
2102 VENUE_IN_TRANSIT H + C<=D
3001 TRADING_FEE_REVENUE H only
3002 SPREAD_REVENUE H only
3003 WITHDRAWAL_FEE_REVENUE H only
3004 CLIENT_FUNDING_REVENUE H only
3005 CLIENT_LOSS_REVENUE H only
3006 HEDGE_PNL_REVENUE H only
3007 VENUE_FUNDING_REVENUE H only
3008 LIQUIDATION_FEE_REVENUE H only
4001 NETWORK_FEE_EXPENSE H only
4002 VENUE_EXECUTION_FEE_EXPENSE H only
4003 CLIENT_PROFIT_EXPENSE H only
4004 HEDGE_PNL_EXPENSE H only
4005 CLIENT_FUNDING_EXPENSE H only
4006 VENUE_FUNDING_EXPENSE H only
4007 BAD_DEBT_EXPENSE H only
4008 SPOT_PRICE_IMPROVEMENT_EXPENSE H only
5001 DEPOSIT_SUSPENSE H only
5002 WITHDRAWAL_SUSPENSE H only
5003 RECONCILIATION_SUSPENSE H only
5004 CHAIN_REORG_RECEIVABLE H only
5005 MANUAL_ADJUSTMENT_CLEARING H only
5006 SPOT_SETTLEMENT_CLEARING H only
6001 INSURANCE_FUND_RESERVE H + D<=C
6002 BROKER_SPOT_INVENTORY_RESERVE H + D<=C

نکات الزام‌آور:

هر تغییر در این جدول به دلیل immutable بودن Account flagها، برای Accountهای ساخته‌شده نیازمند migration/reprovisioning و Document-First version bump است.


9.6. هزینه history و Sharding Accountهای داغ

تصمیم history=true آگاهانه پذیرفته شده است، حتی برای Accountهای پرترافیک مانند TRADING_FEE_REVENUE.

اگر حجم history یک system account به مشکل عملیاتی تبدیل شود، راه‌حل baseline خاموش‌کردن history نیست. می‌توان چند Account با همان Account Code و ownership/partition metadata روشن ایجاد کرد، مثلاً:

TRADING_FEE_REVENUE:BINANCE:BTCUSDT
TRADING_FEE_REVENUE:BYBIT:BTCUSDT

Partitioning باید قبل از provisioning آن accountها مستند شود و aggregation گزارش مالی بر اساس code + external metadata انجام شود.

مکانیزم فیزیکی این partition label از 0.005 صریح است: ستون accounting_accounts.partition_key طبق §26.2. مقدار پیش‌فرض '' است و accountهای shardشده label پایدار می‌گیرند.

این metadata فقط در PostgreSQL نگهداری می‌شود. user_data_64 و user_data_32 طبق §9.1 صفر می‌مانند و partitioning اجازه ندارد از آن فیلدها استفاده کند.

از دید TigerBeetle چند shard صرفاً چند Account با code یکسان و id متفاوت هستند؛ هیچ تغییری در flag matrix §9.5 لازم نیست.

Change History — Account Provisioning

Version Date Change
0.001 2026-09-12 Mapping اولیه
0.002 2026-09-12 تثبیت history flag، user_data mapping، freeze semantics و historical import strategy
0.003 2026-09-12 افزودن flag matrix قطعی per-code و ثبت آگاهانه هزینه history/sharding policy
0.005 2026-09-12 تثبیت مکانیزم sharding روی accounting_accounts.partition_key و تاکید بر صفر ماندن user_data_64/32

10. Transfer ID و Idempotency

Introduced: 0.001
Last Changed: 0.003
Status: Frozen for Initial Implementation

هر Financial Operation قبل از ارسال به TigerBeetle یک accounting_event_id پایدار و persisted دارد.

accounting_event_id نیز یک u128 time-sortable مستقل است و قبل از ساخت legها persist می‌شود. این ID برای grouping/audit است و با Transfer.id یکی نیست.

10.1. ID Scheme

برای Account و Transfer از TigerBeetle time-based identifier scheme استفاده می‌شود:

high 48 bits = millisecond timestamp
low 80 bits  = random / monotonic-within-ms component

Random UUID/hash به عنوان ID اصلی TigerBeetle استفاده نمی‌شود.

Transfer Leg ID

هر leg یک TigerBeetle time-based ID مستقل دارد و قبل از submit در accounting_event_legs persist می‌شود.

Accounting Event E

Leg 1 -> transfer_id = TB_TIME_ID_A
Leg 2 -> transfer_id = TB_TIME_ID_B
Leg 3 -> transfer_id = TB_TIME_ID_C

Retry همان persisted ID را استفاده می‌کند.

تصمیم v0.002: از schemeهایی مانند hash(E,n) یا mask کردن بیت‌های ID رسمی برای ساخت base_id | leg_index استفاده نمی‌شود. Grouping توسط user_data_128 انجام می‌شود، نه با دستکاری ID.


10.2. Transfer user_data_*

Mapping Transferها:

user_data_128
  = accounting_event_id_u128

user_data_64
  = real_world_occurred_at_ms
  = 0 only if no meaningful external occurrence time exists

user_data_32
  = source_system_code

source_system_code یک enum append-only است، مثلاً:

1 API
2 OMS
3 WALLET
4 EXECUTION
5 RISK
6 RECONCILIATION
7 ADMIN_CORRECTION

10.3. Result Handling

موفقیت semantic:

created / ok
exists

exists در retry همانند success تلقی می‌شود فقط وقتی object با همان محتوا وجود دارد.

تمام resultهای زیر Critical Data Integrity Error هستند:

exists_with_different_flags
exists_with_different_pending_id
exists_with_different_timeout
exists_with_different_debit_account_id
exists_with_different_credit_account_id
exists_with_different_amount
exists_with_different_user_data_128
exists_with_different_user_data_64
exists_with_different_user_data_32
exists_with_different_ledger
exists_with_different_code

رفتار:

event -> REQUIRES_REVIEW
automatic retry -> STOP
critical alert -> FIRE

این وضعیت یعنی یک ID یکسان با payload متفاوت تولید شده و یک bug منطقی/داده‌ای است.


10.4. Retry Rule و Burn شدن Transfer ID

حالت A — Outcome نامعلوم است

اگر application response را دریافت نکرده است و نمی‌داند request commit شده یا نه:

retry same request semantics
reuse exactly the same persisted transfer IDs

در این حالت ساخت ID جدید ممنوع است، چون ممکن است request قبلی در cluster commit شده باشد.

حالت B — TigerBeetle صریحاً state-dependent failure برگردانده است

از client release 0.16.4 به بعد، هفت result زیر transient/state-dependent هستند ولی ID همان attempt برای همیشه سوخته است:

debit_account_not_found
credit_account_not_found
pending_transfer_not_found
exceeds_credits
exceeds_debits
debit_account_already_closed
credit_account_already_closed

Retry همان ID بعداً به id_already_failed می‌رسد، حتی اگر state اصلاح شده باشد. دریافت خودِ id_already_failed نیز terminal محسوب می‌شود: worker نباید همان ID را دوباره retry کند و در صورت نیاز business باید از successor event استفاده کند.

این نکته برای business rejection عادی مثل insufficient funds نیز اعمال می‌شود. مثال:

USER_AVAILABLE insufficient
    ↓
exceeds_credits
    ↓
that transfer_id is permanently unusable

اگر business بعداً بعد از تامین موجودی دوباره همان intent را submit کند، باید successor Accounting Event با Transfer IDهای کاملاً جدید بسازد.

Linked Chain Rule

اگر یکی از legهای یک linked chain یکی از هفت خطای بالا را بگیرد، سایر legها معمولاً می‌توانند linked_event_failed بگیرند. linked_event_failed خودش در لیست ID-burning بالا نیست، اما در این معماری هیچ ID از آن Accounting Event برای business re-attempt مجدد استفاده نمی‌شود.

قاعده اجباری:

one explicit ID-burning failure in event
               ↓
current accounting_event = PERMANENT_FAILED
failure_class = TB_TRANSIENT_ID_BURN
               ↓
NO leg ID from this event may be reused
               ↓
create successor accounting_event
supersedes_event_id = failed_event.id
               ↓
generate/persist ALL fresh leg IDs
               ↓
submit the entire new event

مخلوط‌کردن IDهای سالم chain قبلی با ID جدید leg سوخته ممنوع است. این قاعده audit lineage را ساده نگه می‌دارد و مانع eventهایی با ancestry مبهم می‌شود.

PERMANENT_FAILED در اینجا یعنی این attempt و IDهای آن terminal هستند؛ business intent در صورت مجاز بودن می‌تواند با successor event جدید دوباره اجرا شود.

هدف:

durable intent
+
persisted idempotency IDs
+
explicit burn semantics
+
superseding event lineage
=
safe financial retry

10.5. TigerBeetle Client Compatibility Floor

Implementation language این پروژه برای Accounting Service و TigerBeetle integration در baseline 0.004:

.NET / C#

Client رسمی:

NuGet package: tigerbeetle
namespace:     TigerBeetle

حداقل prerequisite فعلی client رسمی برای runtime:

.NET >= 8.0

پروژه می‌تواند .NET جدیدتری pin کند و باید TFM دقیق را در CI ثبت کند.

حداقل semantic version مجاز:

TigerBeetle client >= 0.16.4

استفاده از client پایین‌تر از 0.16.4 ممنوع است، چون semantics مربوط به permanently-failed idempotency IDs در نسخه‌های قدیمی متفاوت است. این floor مستقل از زبان است و مبنای §10.4 و D-017 محسوب می‌شود.

الزامات build:

10.5.1. Client Lifetime در .NET

TigerBeetle.Client نوع IDisposable است و thread-safe؛ یک instance باید بین taskها به اشتراک گذاشته شود تا automatic batching کار کند.

Host / Process
      │
      └── singleton TigerBeetle.Client per cluster/config

قواعد:


10.5.2. Type Mapping و ذخیره‌سازی u128

TigerBeetle u128  -> System.UInt128
Account flags     -> TigerBeetle.AccountFlags
Transfer flags    -> TigerBeetle.TransferFlags
time-based ID     -> TigerBeetle.ID.Create()

Flagهای §9.5 در .NET با bitwise-or ترکیب می‌شوند، مثلاً:

H + D<=C  ->  AccountFlags.History | AccountFlags.DebitsMustNotExceedCredits
H + C<=D  ->  AccountFlags.History | AccountFlags.CreditsMustNotExceedDebits
H only    ->  AccountFlags.History

Baseline ذخیره‌سازی u128 در PostgreSQL:

tigerbeetle_account_id   -> uuid
tigerbeetle_transfer_id  -> uuid
accounting_event_id      -> uuid

uuid در PostgreSQL دقیقاً 128 بیت و index-friendly است و client رسمی .NET نیز تبدیل UInt128 به Guid را فراهم می‌کند.

الزام: تبدیل UInt128 ↔ Guid ↔ uuid باید در یک helper مرکزی با endianness ثابت و تست‌شده انجام شود. تکرار پراکنده این تبدیل در سرویس‌ها ممنوع است، چون layout باینری Guid در .NET mixed-endian است و یک تبدیل ناهمگون می‌تواند ID مالی را به شکل غیرقابل‌بازگشت خراب کند.

مقادیر رزرو‌شده: id = 0 و id = 2^128 - 1 مجاز نیستند و generator باید آن‌ها را قبل از submit reject کند.


10.5.3. Deployment Note

محیط production پشتیبانی‌شده TigerBeetle فقط Linux >= 5.6 است. macOS و Windows فقط برای توسعه پشتیبانی می‌شوند.

بنابراین محیط توسعه باید TigerBeetle را در container اجرا کند تا با production divergence نداشته باشد.

Change History — IDs & Idempotency

Version Date Change
0.001 2026-09-12 تعریف Accounting Event و retry با ID ثابت
0.002 2026-09-12 جایگزینی deterministic hash/base-leg scheme با persisted TigerBeetle time IDs و تعریف result handling
0.003 2026-09-12 تعریف کامل ID-burning از 0.16.4، linked-chain superseding rule و انتخاب Go/client floor
0.004 2026-09-12 زبان پیاده‌سازی از Go به .NET تغییر کرد؛ افزودن client lifetime، type mapping، u128↔uuid storage و deployment note

11. Accounting Event و Leg Model

Introduced: 0.001
Last Changed: 0.003
Status: Designed

PostgreSQL باید قبل از هر Financial Operation یک intent پایدار ایجاد کند.

accounting_events

id                      u128-compatible
event_type
aggregate_type
aggregate_id
user_id nullable
status

payload

created_at
occurred_at
submitted_at
completed_at
failed_at

attempt_count
next_retry_at
locked_by
locked_until
last_error_code
last_error_message
failure_class

supersedes_event_id nullable

supersedes_event_id یک self-reference به event قبلی است. Reverse relation یعنی superseded_by از query روی successor به دست می‌آید و به صورت ستون تکراری ذخیره نمی‌شود.

Status:

CREATED
READY
SUBMITTING
COMPLETED
RETRYABLE_FAILED
PERMANENT_FAILED
REQUIRES_REVIEW

Transfer IDها به شکل array داخل accounting_events نگهداری نمی‌شوند.

هر leg row مستقل دارد:

accounting_event_legs

id
accounting_event_id
leg_index

tigerbeetle_transfer_id

ledger_id
debit_account_id
credit_account_id
amount
transfer_code
flags

status
tigerbeetle_result_code

submitted_at
completed_at
created_at

Constraints:

UNIQUE(accounting_event_id, leg_index)
UNIQUE(tigerbeetle_transfer_id)

برای accounting_events:

FOREIGN KEY (supersedes_event_id) REFERENCES accounting_events(id)
UNIQUE (supersedes_event_id) WHERE supersedes_event_id IS NOT NULL

بنابراین یک failed attempt حداکثر یک successor مستقیم canonical دارد. Successor یک accounting_event_id جدید دارد و همه legهایش Transfer ID جدید می‌گیرند؛ در نتیجه constraint (accounting_event_id, leg_index) بدون تغییر باقی می‌ماند.

مزایا:

Change History — Accounting Event

Version Date Change
0.001 2026-09-12 Event record اولیه
0.002 2026-09-12 افزودن worker lease/retry fields و جدول accounting_event_legs
0.003 2026-09-12 افزودن failure_class و supersedes_event_id برای lineage تلاش‌های دارای ID سوخته

12. Transfer Code Registry

Introduced: 0.001
Last Changed: 0.003
Status: Frozen for Initial Implementation

Transfer Codeها append-only هستند و معنای code موجود تغییر نمی‌کند.

100 DEPOSIT_CONFIRMED

110 WITHDRAWAL_LOCK
111 WITHDRAWAL_RELEASE
112 WITHDRAWAL_BROADCAST
113 WITHDRAWAL_CONFIRMED
114 WITHDRAWAL_FEE

120 INTERNAL_TRANSFER

200 SPOT_ORDER_LOCK
201 SPOT_ORDER_RELEASE
202 SPOT_CLIENT_SETTLEMENT
203 SPOT_VENUE_SETTLEMENT
204 SPOT_TRADING_FEE
205 SPOT_SPREAD
206 SPOT_PRICE_IMPROVEMENT
207 SPOT_INVENTORY_SETTLEMENT

300 MARGIN_LOCK
301 MARGIN_RELEASE

310 CLIENT_REALIZED_PROFIT
311 CLIENT_REALIZED_LOSS

320 HEDGE_REALIZED_PROFIT
321 HEDGE_REALIZED_LOSS

330 CLIENT_FUNDING_PAYMENT
331 CLIENT_FUNDING_RECEIPT
332 VENUE_FUNDING_PAYMENT
333 VENUE_FUNDING_RECEIPT

340 LIQUIDATION_COLLECTIBLE_LOSS
341 LIQUIDATION_FEE
342 BAD_DEBT_RECOGNITION
343 BAD_DEBT_WRITEOFF
344 INSURANCE_FUND_ADJUSTMENT

400 CUSTODY_MOVE

410 VENUE_DEPOSIT
411 VENUE_WITHDRAWAL

420 NETWORK_FEE
421 VENUE_EXECUTION_FEE

900 CORRECTION
901 MANUAL_ADJUSTMENT

12.1. Zero-Amount Transfer Policy

TigerBeetle از release 0.16.0 مقدار صفر را برای Transfer می‌پذیرد، اما baseline این پروژه برای posted business legs معمولی چنین است:

computed amount == 0
    => do not emit the transfer leg

در این حالت row اضافی در accounting_event_legs ساخته نمی‌شود.

استثنا فقط زمانی مجاز است که مقدار صفر در یک API operation معنای خاص و مستند TigerBeetle داشته باشد، مانند برخی post_pending_transfer / void_pending_transfer flowها. چنین استثنایی باید در همان feature صریح مستند شود.

هدف: جلوگیری از history noise و تفاوت رفتاری بین producerها.

Change History — Transfer Codes

Version Date Change
0.001 2026-09-12 Registry اولیه
0.002 2026-09-12 افزودن price-improvement و مسیر صریح bad-debt/liquidation
0.003 2026-09-12 افزودن SPOT_INVENTORY_SETTLEMENT و zero-amount omission policy

13. مدل User Balance

Introduced: 0.001
Last Changed: 0.001
Status: Designed

User از دید UI ممکن است فقط یک Trading Account داشته باشد.

اما از دید Ledger:

User
│
├── USDT
│   ├── AVAILABLE
│   ├── SPOT_LOCKED
│   ├── MARGIN
│   └── WITHDRAWAL_LOCKED
│
├── BTC
│   ├── AVAILABLE
│   ├── SPOT_LOCKED
│   └── WITHDRAWAL_LOCKED
│
└── ETH
    ├── AVAILABLE
    ├── SPOT_LOCKED
    └── WITHDRAWAL_LOCKED

Total User Entitlement

مثلاً USDT:

Total User USDT =
AVAILABLE
+ SPOT_LOCKED
+ MARGIN
+ WITHDRAWAL_LOCKED

جابجایی بین این Accountها مالکیت User را عوض نمی‌کند؛ فقط وضعیت استفاده از دارایی را تغییر می‌دهد.


14. Deposit روی Blockchain

Introduced: 0.001
Last Changed: 0.002
Status: Designed

14.1. Lifecycle

DETECTED
   ↓
CONFIRMING
   ↓
CONFIRMED
   ↓
ACCOUNTED

PostgreSQL:

deposits

id
user_id
asset_id
network_id
address
amount

tx_hash
tx_index / vout / log_index

confirmation_count
required_confirmations

status
accounting_event_id

detected_at
confirmed_at
accounted_at

14.2. Credit User

بعد از رسیدن به confirmation policy:

مثلاً:

0.5 BTC

Transfer:

Deposit همیشه ابتدا به Account متناظر با مقصد واقعی on-chain ثبت می‌شود:

Debit:
CUSTODY_DEPOSIT_BTC:<custody_wallet_group_id>

Credit:
USER_AVAILABLE_BTC

Amount:
0.5 BTC

استفاده مستقیم از HOT_WALLET_BTC برای Deposit Credit مجاز نیست، مگر اینکه مقصد واقعی تراکنش همان Hot Wallet باشد و آن wallet group در Asset Registry/Custody Registry صریحاً همان Account باشد.

اگر بعداً deposit funds sweep شوند:

CUSTODY_DEPOSIT_BTC
        ↓  CUSTODY_MOVE
CUSTODY_HOT_BTC

این sweep یک Accounting Event جداست تا Blockchain reconciliation بر اساس wallet/address group ممکن بماند.

اثر اقتصادی:

Broker Asset      +0.5 BTC
User Liability    +0.5 BTC

14.3. Duplicate Deposit Event

Deposit identifier باید chain-specific و unique باشد.

برای UTXO chain معمولاً چیزی مشابه:

(tx_hash, vout)

برای Token Event معمولاً:

(chain_id, tx_hash, log_index)

Duplicate detection قبل از accounting انجام می‌شود.

همچنین Accounting Event و Transfer ID idempotent هستند.


14.4. Reorg

Credit تنها بعد از confirmation threshold انجام می‌شود.

اگر با وجود آن deep reorg رخ داد:

Policy دقیق Reorg باید per-network تعریف شود.


Change History — Deposit

Version Date Change
0.001 2026-09-12 تعریف lifecycle اولیه Deposit، confirmation و accounting
0.002 2026-09-12 تثبیت CUSTODY_DEPOSIT به عنوان منبع Deposit Credit و جداسازی Sweep به CUSTODY_MOVE

15. Internal Transfer بین Userها

Introduced: 0.001
Last Changed: 0.001
Status: Designed

مثال:

Ali → Sara
0.5 BTC

Transfer:

Debit:
ALI_BTC_AVAILABLE

Credit:
SARA_BTC_AVAILABLE

Custody Asset تغییر نمی‌کند.

Ali Liability   -0.5
Sara Liability  +0.5
Broker Asset     unchanged

Blockchain transaction ساخته نمی‌شود.


16. Custody: Hot / Warm / Cold

Introduced: 0.001
Last Changed: 0.001
Status: Designed

مثال انتقال 20 BTC از Hot به Cold:

Debit:
COLD_WALLET_BTC

Credit:
HOT_WALLET_BTC

Amount:
20 BTC

این فقط location دارایی Broker را تغییر می‌دهد.

User Liability هیچ تغییری نمی‌کند.

Network Fee باید leg جدا باشد:

Debit:
NETWORK_FEE_EXPENSE_BTC

Credit:
HOT_WALLET_BTC

17. انتقال Asset به Venue / LP

Introduced: 0.001
Last Changed: 0.001
Status: Designed

برای A-Book باید Broker روی Venue/LP دارایی داشته باشد.

مثال:

Hot Wallet → Binance
100,000 USDT

مرحله Broadcast:

Debit:
VENUE_IN_TRANSIT_USDT

Credit:
HOT_WALLET_USDT

پس از credit شدن توسط Venue:

Debit:
BINANCE_USDT_ASSET

Credit:
VENUE_IN_TRANSIT_USDT

VENUE_IN_TRANSIT باید در حالت عادی پس از settlement به صفر برگردد.


18. Spot Trading

Introduced: 0.001
Last Changed: 0.003
Status: Designed

Spot واقعی است.

اگر User:

BUY 1 BTC

انجام دهد، در پایان:

USER_BTC_AVAILABLE += 1 BTC

به عنوان Liability Broker به User.


18.1. Lock کردن Buy Order

Ali:

USDT Available = 100,000
BUY 1 BTC
Limit = 50,000 USDT

در زمان Place Order:

Debit:
USER_USDT_AVAILABLE

Credit:
USER_USDT_SPOT_LOCKED

Amount:
max_required_amount

برای Buy Order با quote asset، مقدار lock باید حد بالای واقعی مصرف User باشد:

max_quote_notional = round_up(price_cap * quantity, quote_asset_scale)

max_required_amount =
    max_quote_notional
  + worst_case_client_fee_in_quote
  + explicit_safety_buffer_if_configured

قواعد:

برای Sell Order، حداقل base_quantity و در صورت charge شدن fee از base asset، worst-case base fee نیز lock می‌شود.

این Single-Phase Transfer است.


18.2. چرا Spot Order Lock با Pending Transfer مدل نمی‌شود؟

در نسخه 0.001، Spot Order Reservation با جابه‌جایی واقعی:

AVAILABLE → SPOT_LOCKED

مدل می‌شود.

دلیل:

یک Pending Transfer در TigerBeetle فقط یک بار resolve می‌شود.

اگر بخشی از Pending Amount post شود، remainder آزاد می‌شود.

اما Order می‌تواند چندین Partial Fill مستقل داشته باشد:

Fill 1
Fill 2
Fill 3
Fill 4
...

بنابراین Account مجزای SPOT_LOCKED برای lifecycle سفارش مناسب‌تر است.


18.3. Partial Fill

فرض:

Order reserve = 50,000 USDT

Fill اول:

Client fill = 10,000 USDT

فقط 10,000 از SPOT_LOCKED مصرف می‌شود.

باقی‌مانده:

40,000 USDT

برای Fillهای بعدی locked می‌ماند.


18.4. Cancel Order

Unused reserve:

Debit:
USER_USDT_SPOT_LOCKED

Credit:
USER_USDT_AVAILABLE

Change History — Spot Order Reservation

Version Date Change
0.001 2026-09-12 تعریف اولیه AVAILABLE → SPOT_LOCKED
0.003 2026-09-12 تعریف دقیق max_required_amount، fee buffer و price protection برای Market Order

19. Spot Settlement — تفکیک Client Side از Venue Side

Introduced: 0.001
Last Changed: 0.003
Status: Designed

19.1. اصل معماری

از v0.002 Spot Settlement نباید فرض کند:

1 Client Fill = 1 Venue Fill

Client ممکن است:

بنابراین بین Client و Venue یک clearing account per-asset وجود دارد:

SPOT_SETTLEMENT_CLEARING_USDT
SPOT_SETTLEMENT_CLEARING_BTC
SPOT_SETTLEMENT_CLEARING_ETH
...

هدف steady-state:

clearing balance = 0

Client Settlement و Venue Settlement دو Accounting Event مستقل‌اند و با allocation در PostgreSQL به هم مرتبط می‌شوند.


19.2. Client Buy Settlement

مثال:

Client buys 1 BTC
Client price = 50,000 USDT

Client-side linked chain:

USDT

Debit:
USER_USDT_SPOT_LOCKED

Credit:
SPOT_SETTLEMENT_CLEARING_USDT

Amount:
50,000 USDT

Code:
SPOT_CLIENT_SETTLEMENT

BTC

Debit:
SPOT_SETTLEMENT_CLEARING_BTC

Credit:
USER_BTC_AVAILABLE

Amount:
1 BTC

Code:
SPOT_CLIENT_SETTLEMENT

این operation وابسته به یک Venue مشخص نیست.


19.3. Venue Execution برای Buy

فرض:

Venue cost = 49,990 USDT
Venue BTC acquired = 1 BTC

Venue-side linked chain:

Quote Asset

Debit:
SPOT_SETTLEMENT_CLEARING_USDT

Credit:
BINANCE_USDT_ASSET

Amount:
49,990 USDT

Code:
SPOT_VENUE_SETTLEMENT

Base Asset

Debit:
BINANCE_BTC_ASSET

Credit:
SPOT_SETTLEMENT_CLEARING_BTC

Amount:
1 BTC

Code:
SPOT_VENUE_SETTLEMENT

بعد از allocation کامل:

BTC clearing = 0
USDT clearing = +10 credit balance

19.4. Fill از Inventory خود Broker

Spot ممکن است بدون Venue Fill فوری از inventory اقتصادی خود Broker تامین شود. در این حالت Venue جعلی ایجاد نمی‌شود.

Account مورد استفاده:

BROKER_SPOT_INVENTORY_RESERVE_<ASSET>

این Account location دارایی نیست؛ custody location همچنان در CUSTODY_* / VENUE_ASSET است.

Client Buy از Broker Inventory

بعد از Client-side settlement در §19.2، clearing چنین exposureی دارد:

Quote clearing = credit balance
Base clearing  = debit balance

یک linked accounting event با code SPOT_INVENTORY_SETTLEMENT ثبت می‌شود:

Quote leg:

Debit:  SPOT_SETTLEMENT_CLEARING_USDT
Credit: BROKER_SPOT_INVENTORY_RESERVE_USDT
Amount: internal_quote_amount

Base leg:

Debit:  BROKER_SPOT_INVENTORY_RESERVE_BTC
Credit: SPOT_SETTLEMENT_CLEARING_BTC
Amount: internal_base_quantity

در نتیجه Broker بخشی از BTC inventory خود را به entitlement مشتری تبدیل کرده و در مقابل USDT inventory/equity خود را افزایش داده است.

Client Sell به Broker Inventory

برای Sell، جهت برعکس است:

Base leg:

Debit:  SPOT_SETTLEMENT_CLEARING_BTC
Credit: BROKER_SPOT_INVENTORY_RESERVE_BTC
Amount: internal_base_quantity

Quote leg:

Debit:  BROKER_SPOT_INVENTORY_RESERVE_USDT
Credit: SPOT_SETTLEMENT_CLEARING_USDT
Amount: internal_quote_amount

BROKER_SPOT_INVENTORY_RESERVE دارای debits_must_not_exceed_credits است؛ بنابراین سیستم نمی‌تواند بیش از inventory اقتصادی متعلق به Broker را به مشتری بفروشد.

Replenishment بعدی Broker از Venue یک event مستقل است و نباید retrospectively به عنوان همان Client Fill جعل شود.

19.5. Spread Settlement

اگر residual به نفع Broker باشد:

Debit:
SPOT_SETTLEMENT_CLEARING_USDT

Credit:
SPREAD_REVENUE_USDT

Amount:
10 USDT

اگر Venue execution از Client price بدتر باشد و residual به ضرر Broker باشد:

Debit:
SPOT_PRICE_IMPROVEMENT_EXPENSE_USDT

Credit:
SPOT_SETTLEMENT_CLEARING_USDT

Amount:
absolute_residual

هیچ residual نباید بدون classification دائماً در clearing باقی بماند.


19.6. Multi-Venue / Aggregate Execution

یک Client Fill می‌تواند به چند Venue Fill allocate شود:

Client Fill C1 = 1.0 BTC

Venue Fill V1 = 0.4 BTC @ Binance
Venue Fill V2 = 0.3 BTC @ Bybit
Venue Fill V3 = 0.3 BTC @ Binance

PostgreSQL باید allocation صریح داشته باشد:

spot_execution_allocations

id
client_fill_id

source_type                # VENUE_EXECUTION | BROKER_INVENTORY
venue_execution_id nullable
source_accounting_event_id nullable

base_quantity
quote_amount

created_at

Client settlement فقط یک بار انجام می‌شود؛ هر Venue Fill settlement خودش را دارد. Allocation با source_type=BROKER_INVENTORY نیز مجاز است و نباید venue_execution_id جعلی داشته باشد.


19.7. Atomicity الزام‌آور

هر accounting event چند-leg که نیاز به atomicity دارد باید:

  1. تمام legها را در یک create_transfers call بفرستد.
  2. legها در request contiguous باشند.
  3. تمام legها به جز آخر flags.linked = true داشته باشند.
  4. آخرین leg flags.linked = false داشته باشد.

تقسیم یک linked chain بین چند request ممنوع است.

در default configuration، create_transfers حداکثر 8189 event در یک request می‌پذیرد. اگر یک business operation از این سقف عبور کند، نمی‌توان atomicity کامل TigerBeetle را ادعا کرد و طراحی باید قبل از implementation تغییر کند.


20. Spot Sell Settlement

Introduced: 0.001
Last Changed: 0.003
Status: Designed

فرض:

Client sells 1 BTC
Client proceeds = 50,000 USDT
Venue proceeds  = 50,010 USDT

20.1. Client Side

BTC:

Debit:
USER_BTC_SPOT_LOCKED

Credit:
SPOT_SETTLEMENT_CLEARING_BTC

Amount:
1 BTC

USDT:

Debit:
SPOT_SETTLEMENT_CLEARING_USDT

Credit:
USER_USDT_AVAILABLE

Amount:
50,000 USDT

این دو leg یک linked chain هستند.


20.2. Venue Side

BTC:

Debit:
SPOT_SETTLEMENT_CLEARING_BTC

Credit:
BINANCE_BTC_ASSET

Amount:
1 BTC

USDT:

Debit:
BINANCE_USDT_ASSET

Credit:
SPOT_SETTLEMENT_CLEARING_USDT

Amount:
50,010 USDT

در این مثال USDT clearing دارای 10 USDT credit residual است که با SPOT_SPREAD به SPREAD_REVENUE_USDT منتقل می‌شود.


20.3. Invariantهای Spot Clearing

برای هر Asset:

expected long-lived balance = 0

Non-zero balance فقط وقتی مجاز است که حداقل یکی از این وضعیت‌ها وجود داشته باشد:

UNALLOCATED_CLIENT_FILL
UNSETTLED_VENUE_FILL
PARTIAL_VENUE_EXECUTION
PRICE_RESIDUAL_PENDING_CLASSIFICATION
INCIDENT_REQUIRES_REVIEW

هر balance باید از روی spot_execution_allocations و accounting events قابل توضیح باشد.


20.4. Hard Clearing Guard

چون SPOT_SETTLEMENT_CLEARING عمداً must_not_exceed ندارد، TigerBeetle به تنهایی solvency این account را enforce نمی‌کند. بنابراین کنترل زیر Production Requirement است:

unexplained clearing age > configured_max_age
OR
abs(unexplained clearing exposure) > configured_hard_limit
        ↓
CRITICAL ALERT
        ↓
HALT NEW CLIENT SPOT SETTLEMENTS
for affected asset/instrument scope

در حالت halt، این عملیات همچنان باید مجاز بمانند:

رفع circuit breaker فقط پس از reconciliation موفق یا override صریح و audit‌شده مجاز است.

configured_max_age و configured_hard_limit قبل از Spot Production باید مقدار غیر-null داشته باشند.

اگر در آینده hard bound کاملاً داخل TigerBeetle لازم شود، recipe رسمی Balance Bounds می‌تواند بررسی شود؛ baseline 0.003 به دلیل هزینه و پیچیدگی پنج linked transfer آن را برای Spot Clearing فعال نمی‌کند.

Change History — Spot Settlement

Version Date Change
0.001 2026-09-12 تعریف اولیه Spot Buy/Sell، lock account، partial fill، spread و fee
0.002 2026-09-12 حذف فرض 1:1 بین Client Fill و Venue Fill؛ افزودن SPOT_SETTLEMENT_CLEARING و multi-venue allocation
0.003 2026-09-12 افزودن internal broker inventory settlement، source_type allocation و hard clearing guard/circuit breaker

21. Spot Trading Fee

Introduced: 0.001
Last Changed: 0.002
Status: Designed

اگر از User:

20 USDT

fee گرفته شود:

Debit:
USER_USDT_SPOT_LOCKED / USER_USDT_AVAILABLE

Credit:
TRADING_FEE_REVENUE_USDT

برای Buy Order، max_required_amount در زمان lock باید در صورت charge شدن fee از quote asset شامل worst-case fee/buffer نیز باشد تا settlement در زمان fill به overdraft نخورد.

Venue execution fee مستقل است:

Debit:
VENUE_EXECUTION_FEE_EXPENSE_USDT

Credit:
BINANCE_USDT_ASSET

Client Fee و Venue Fee هیچ‌وقت با هم net و مخفی نمی‌شوند.


Change History — Spot Trading Fee

Version Date Change
0.001 2026-09-12 تعریف اولیه Trading Fee و Venue Fee
0.002 2026-09-12 الزام worst-case fee داخل max_required_amount برای feeهای quote-asset

22. Position / Derivatives — A-Book

Introduced: 0.001
Last Changed: 0.002
Status: Designed

Position داخل PostgreSQL نگهداری می‌شود.

نمونه:

positions

id
user_id
instrument_id

side
quantity

entry_price
mark_price

leverage
initial_margin
maintenance_margin

realized_pnl
unrealized_pnl

status

opened_at
closed_at

TigerBeetle Position را نگهداری نمی‌کند.


22.1. Margin Lock

مثال:

Notional = 10,000 USDT
Leverage = 10x
Margin = 1,000 USDT

Transfer:

Debit:
USER_USDT_AVAILABLE

Credit:
USER_USDT_MARGIN

Amount:
1,000 USDT

User هنوز مالک 1,000 USDT است ولی amount برای trading lock شده است.


22.2. Unrealized PnL

Unrealized PnL در TigerBeetle ثبت نمی‌شود.

مثال:

Entry = 50,000
Mark  = 52,000
Unrealized PnL = +2,000

این مقدار توسط Position/Risk Engine محاسبه می‌شود.

دلیل:


22.3. Realized PnL

هنگام Close یا Realization، PnL وارد TigerBeetle می‌شود.

User Profit

مثال:

User Realized Profit = 500 USDT

Transfer:

Debit:
CLIENT_PROFIT_EXPENSE_USDT

Credit:
USER_USDT_AVAILABLE

User Loss

مثال:

User Realized Loss = 500 USDT

Loss نباید با یک transfer که ممکن است از موجودی User بیشتر باشد ثبت شود.

Baseline v0.002:

  1. collectible_loss از balance واقعی USER_MARGIN و سپس طبق Risk Policy از USER_AVAILABLE محاسبه می‌شود.
  2. فقط مقدار collectible از User debit می‌شود.
  3. shortfall با مسیر Bad Debt §24 ثبت می‌شود.
  4. هیچ transfer نباید constraint حساب User را دور بزند.

برای بخش قابل وصول:

Debit:
USER_USDT_MARGIN / USER_USDT_AVAILABLE

Credit:
CLIENT_LOSS_REVENUE_USDT

Amount:
collectible_loss

Policy دقیق اولویت consumption بین MARGIN و AVAILABLE هنوز باید قبل از فعال‌شدن derivatives production نهایی شود، ولی shortfall هرگز نباید liquidation را block کند.


22.4. Margin Release

پس از بسته شدن Position یا کاهش margin:

Debit:
USER_USDT_MARGIN

Credit:
USER_USDT_AVAILABLE

22.5. A-Book Hedge

Client Exposure:

Client LONG BTCUSDT

Broker exposure مقابل Client دارد و آن را روی Venue hedge می‌کند.

مفهومی:

Client           +10k BTC exposure

Broker vs Client -10k

Broker at Venue  +10k
----------------------
Broker Net ≈ 0

Position و Hedge Position در PostgreSQL نگهداری می‌شوند.


22.6. Hedge Mapping

سیستم نباید فرض کند:

1 Client Fill = 1 Hedge Fill

PostgreSQL باید قابلیت allocation داشته باشد:

hedge_allocations

id
client_fill_id / exposure_id
hedge_fill_id

allocated_quantity
allocated_notional
created_at

این design امکان موارد زیر را می‌دهد:


22.7. Hedge Realized PnL

اگر Venue hedge:

+490 USDT

سود بسازد:

Debit:
BINANCE_USDT_ASSET

Credit:
HEDGE_PNL_REVENUE_USDT

اگر hedge ضرر کند:

Debit:
HEDGE_PNL_EXPENSE_USDT

Credit:
BINANCE_USDT_ASSET

22.8. Client و Hedge PnL جدا ثبت می‌شوند

مثال:

Client Profit Expense = 500
Hedge Profit Revenue  = 490

Net A-Book result:

490 - 500 = -10 USDT

سپس Fee/Spread می‌تواند نتیجه نهایی Broker را تغییر دهد.

هرگز Client PnL و Hedge PnL را قبل از ثبت accounting با هم net نکن.


Change History — Position / Derivatives

Version Date Change
0.001 2026-09-12 تعریف اولیه Position، Margin، Realized/Unrealized PnL و A-Book Hedge
0.001 2026-09-12 تصمیم: Unrealized PnL داخل TigerBeetle ثبت نشود
0.001 2026-09-12 تصمیم: Client PnL و Hedge PnL جداگانه ثبت شوند
0.001 2026-09-12 تصمیم: Mapping بین Client Fill و Hedge Fill از مدل many-to-many پشتیبانی کند
0.002 2026-09-12 مسیر Client Loss به collectible + explicit bad-debt shortfall تفکیک شد

23. Funding

Introduced: 0.001
Last Changed: 0.002
Status: Designed

Client Funding و Venue Funding مستقل ثبت می‌شوند و قبل از accounting net نمی‌شوند.

23.1. Client pays funding

Debit:
USER_USDT_MARGIN / USER_USDT_AVAILABLE

Credit:
CLIENT_FUNDING_REVENUE_USDT

Code:
CLIENT_FUNDING_PAYMENT

23.2. Client receives funding

Debit:
CLIENT_FUNDING_EXPENSE_USDT

Credit:
USER_USDT_AVAILABLE

Code:
CLIENT_FUNDING_RECEIPT

23.3. Venue pays Broker funding

Debit:
VENUE_USDT_ASSET

Credit:
VENUE_FUNDING_REVENUE_USDT

Code:
VENUE_FUNDING_RECEIPT

23.4. Broker pays Venue funding

Debit:
VENUE_FUNDING_EXPENSE_USDT

Credit:
VENUE_USDT_ASSET

Code:
VENUE_FUNDING_PAYMENT

Client Funding و Venue Funding حتی اگر از نظر اقتصادی قابل offset باشند، در Ledger مستقل باقی می‌مانند.

Change History — Funding

Version Date Change
0.001 2026-09-12 تفکیک Client و Venue funding به عنوان اصل معماری
0.002 2026-09-12 افزودن accountهای revenue/expense صریح برای هر چهار جهت funding

24. Liquidation و Bad Debt

Introduced: 0.001
Last Changed: 0.002
Status: Designed / Risk Policy Partially Open

Liquidation decision توسط Risk Engine گرفته می‌شود. Accounting Service نتیجه مالی را ثبت می‌کند.

24.1. قانون جلوگیری از Deadlock

User Liability accountها constrained هستند:

debits_must_not_exceed_credits = true

بنابراین Liquidation نباید یک linked transfer برای کل theoretical loss بسازد اگر loss از MARGIN + AVAILABLE بیشتر است؛ در غیر این صورت TigerBeetle leg را reject می‌کند و chain می‌تواند liquidation را متوقف کند.

Baseline:

realized_loss =
collectible_loss
+ shortfall

24.2. Collectible Loss

فقط مقدار قابل وصول:

Debit:
USER_MARGIN / USER_AVAILABLE

Credit:
CLIENT_LOSS_REVENUE

Amount:
collectible_loss

Code:
LIQUIDATION_COLLECTIBLE_LOSS

24.3. Shortfall / Bad Debt Recognition

اگر:

realized_loss = 1,200
collectible    = 1,000
shortfall      =   200

shortfall به عنوان receivable ثبت می‌شود:

Debit:
CLIENT_BAD_DEBT_RECEIVABLE

Credit:
CLIENT_LOSS_REVENUE

Amount:
200

Code:
BAD_DEBT_RECOGNITION

این باعث می‌شود User balance منفی نشود و liquidation قابل تکمیل بماند.


24.4. Write-off

اگر receivable غیرقابل وصول تشخیص داده شد:

Debit:
BAD_DEBT_EXPENSE

Credit:
CLIENT_BAD_DEBT_RECEIVABLE

Amount:
writeoff_amount

Code:
BAD_DEBT_WRITEOFF

24.5. Insurance Fund

اگر Insurance Fund فعال باشد، recovery باید Accounting Event جداگانه باشد و نباید completion liquidation را block کند.

در v0.002 فقط حساب پایه زیر reserve شده است:

INSURANCE_FUND_RESERVE

مدل دقیق funding/replenishment/recovery و اثر equity آن قبل از production liquidation باید در Risk Specification نهایی شود.


24.6. Liquidation Fee

Debit:
USER_MARGIN / USER_AVAILABLE

Credit:
LIQUIDATION_FEE_REVENUE

Code:
LIQUIDATION_FEE

Fee فقط تا حد balance قابل وصول گرفته می‌شود مگر policy دیگری به صورت صریح مستند شود.

Change History — Liquidation

Version Date Change
0.001 2026-09-12 Skeleton اولیه liquidation
0.002 2026-09-12 افزودن collectible-loss، bad-debt receivable/writeoff و جلوگیری از deadlock به علت account constraint

25. Withdrawal روی Blockchain

Introduced: 0.001
Last Changed: 0.003
Status: Designed

25.1. Lifecycle

REQUESTED
   ↓
LOCKED
   ↓
APPROVED
   ↓
SIGNED
   ↓
BROADCASTED
   ↓
CONFIRMING
   ↓
CONFIRMED
   ↓
ACCOUNTED / COMPLETED

Failure stateها:

REJECTED
FAILED_BEFORE_BROADCAST
REQUIRES_REVIEW
REPLACED

25.2. Lock User Balance

در v0.002 فرض می‌شود requested_amount همان amount دریافتی مقصد است و withdrawal fee در صورت وجود علاوه بر آن از User دریافت می‌شود.

مثال:

recipient amount = 0.1 BTC
user withdrawal fee = 0.0002 BTC

total lock = 0.1002 BTC

Transfer:

Debit:
USER_BTC_AVAILABLE

Credit:
USER_BTC_WITHDRAWAL_LOCKED

25.3. Broadcast

فرض:

principal = 0.1 BTC
actual network fee = 0.00015 BTC

در زمان Broadcast:

Debit:
ONCHAIN_OUTBOUND_IN_FLIGHT_BTC

Credit:
HOT_WALLET_BTC

Amount:
0.10015 BTC

این نشان می‌دهد Asset از Wallet خارج شده ولی هنوز settlement نهایی نشده است.


25.3.1. Ordering Invariant اجباری

با توجه به اینکه ONCHAIN_OUTBOUND_IN_FLIGHT دارای credits_must_not_exceed_debits است، ترتیب state transition حسابداری بخشی از correctness سیستم است:

1. WITHDRAWAL_LOCK must be COMPLETED
2. WITHDRAWAL_BROADCAST accounting must be COMPLETED
3. final chain spend / RBF delta must be reflected in IN_FLIGHT
4. only then CONFIRMATION settlement may run
5. principal + network fee + user fee are finalized

Producer حق ندارد event مربوط به principal/network-fee confirmation را قبل از completed شدن broadcast accounting آماده‌ی submit کند.

برای RBF/replacement، هر افزایش در final on-chain spend باید قبل از confirmation با adjustment همان withdrawal به ONCHAIN_OUTBOUND_IN_FLIGHT اضافه شود. Confirmation نباید بیشتر از debit ثبت‌شده‌ی In-Flight credit کند.

در baseline 0.003، هنگام final confirmation legهای زیر باید در یک linked request settle شوند:

principal settlement
network fee expense
user withdrawal fee (if amount > 0)

leg با amount صفر طبق §12.1 حذف می‌شود.

25.4. Confirmation — Principal

پس از finality:

Debit:
USER_BTC_WITHDRAWAL_LOCKED

Credit:
ONCHAIN_OUTBOUND_IN_FLIGHT_BTC

Amount:
0.1 BTC

User Liability کاهش پیدا می‌کند و principal asset از In-Flight خارج می‌شود.


25.5. Network Fee

Debit:
NETWORK_FEE_EXPENSE_BTC

Credit:
ONCHAIN_OUTBOUND_IN_FLIGHT_BTC

Amount:
0.00015 BTC

25.6. User Withdrawal Fee

مثلاً:

0.0002 BTC

Transfer:

Debit:
USER_BTC_WITHDRAWAL_LOCKED

Credit:
WITHDRAWAL_FEE_REVENUE_BTC

در نتیجه:

User charged fee      0.00020
Actual network cost   0.00015
-----------------------------
Gross fee margin      0.00005 BTC

25.7. Failed Before Broadcast

اگر withdrawal قبل از broadcast شکست خورد:

Debit:
USER_WITHDRAWAL_LOCKED

Credit:
USER_AVAILABLE

کل lock آزاد می‌شود.


25.8. Failure After Broadcast

بعد از Broadcast balance User خودکار release نمی‌شود.

ابتدا chain state باید مشخص شود.

مواردی مثل:

باید توسط Withdrawal state machine مدیریت شوند.


25.9. Withdrawal ID با TxHash یکی نیست

withdrawal_id != tx_hash

مثال:

Withdrawal #123

Tx A → broadcast
Tx A → dropped

Tx B → replacement
Tx B → confirmed

در Accounting هنوز فقط یک business withdrawal وجود دارد.


25.10. چرا Withdrawal با Pending Transfer مدل نمی‌شود؟

این یک تصمیم صریح معماری است.

Withdrawal می‌تواند بعد از Broadcast دارای:

RBF
replacement transaction
dropped transaction
delayed confirmation
multiple tx_hash for one withdrawal_id

باشد.

Pending Transfer در TigerBeetle حداکثر یک بار resolve می‌شود و مدل مناسبی برای این state machine نیست.

بنابراین lifecycle مالی Withdrawal با Accountهای posted زیر مدل می‌شود:

USER_AVAILABLE
        ↓
USER_WITHDRAWAL_LOCKED
        ↓
ONCHAIN_OUTBOUND_IN_FLIGHT
        ↓
confirmed settlement

این تصمیم نباید بدون Decision Log جدید به pending transfer تبدیل شود.


Change History — Withdrawal

Version Date Change
0.001 2026-09-12 تعریف lifecycle اولیه Withdrawal
0.001 2026-09-12 تصمیم: withdrawal_id مستقل از tx_hash باشد
0.001 2026-09-12 تصمیم: پس از Broadcast release خودکار موجودی ممنوع است
0.002 2026-09-12 تصمیم صریح: Withdrawal state machine با posted accounts مدل شود، نه Pending Transfer
0.003 2026-09-12 الزام ordering بین lock/broadcast/in-flight/confirmation و RBF delta قبل از final settlement

26. PostgreSQL Schema — حداقل مدل مورد نیاز

Introduced: 0.001
Last Changed: 0.006
Status: Designed

حداقل tableها:

assets
networks
asset_networks
instruments

users

accounting_accounts
accounting_events
accounting_event_legs
balance_cache

orders
fills
spot_execution_allocations

positions

hedge_orders
hedge_fills
hedge_allocations

deposit_addresses
deposits

withdrawals
blockchain_transactions

venues
venue_accounts
venue_executions
venue_balance_snapshots

reconciliation_runs
reconciliation_items

manual_adjustments

26.1. instruments

positions.instrument_id و trading orderها باید به registry صریح instrument متصل باشند.

instruments

id
symbol

base_asset_id
quote_asset_id
settlement_asset_id

instrument_type
price_scale
quantity_scale

status
created_at
updated_at

price_scale و quantity_scale با TigerBeetle asset scale یکی نیستند و باید جدا مدیریت شوند.


26.2. accounting_accounts

Mapping بین domain و TigerBeetle:

accounting_accounts

id

owner_type
owner_id

asset_id
account_type
partition_key

tigerbeetle_account_id

history_enabled
status

created_at

Unique constraint مفهومی:

(owner_type, owner_id, asset_id, account_type, partition_key)

history_enabled در production باید همیشه true باشد و صرفاً برای audit/debug mapping ذخیره می‌شود؛ source واقعی flag خود TigerBeetle است.

partition_key تنها مکانیزم مجاز برای sharding accountهای داغ طبق §9.6 است:


26.3. accounting_events

id

event_type
aggregate_type
aggregate_id

user_id nullable

status
payload

occurred_at
source_system

attempt_count
next_retry_at

locked_by
locked_until

last_error_code
last_error_message
failure_class

supersedes_event_id nullable

created_at
submitted_at
completed_at
failed_at

source_system مقدار source_system_code از §10.2 است که به user_data_32 هر Transfer این event نوشته می‌شود.

ذخیره‌سازی آن اجباری است و قابل استنتاج در زمان submit نیست. هر مقداری که به user_data_* می‌رود باید persist شود تا retry بایت‌به‌بایت همان payload قبلی را بفرستد؛ در غیر این صورت یک attempt دوم با source_system متفاوت به exists_with_different_user_data_32 می‌خورد که طبق §10.3 یک Critical Data Integrity Error است و event را به REQUIRES_REVIEW می‌برد.

همین استدلال دربارهٔ occurred_at (که به user_data_64 می‌رود) و accounting_event_legs.flags طبق §26.9 اعمال می‌شود.

Worker فقط eventی را claim می‌کند که:

status in (READY, RETRYABLE_FAILED)
AND next_retry_at <= now()
AND (locked_until IS NULL OR locked_until < now())

Claim باید transactional/atomic باشد.


Constraints:

FOREIGN KEY (supersedes_event_id) REFERENCES accounting_events(id)
UNIQUE (supersedes_event_id) WHERE supersedes_event_id IS NOT NULL

Successor فقط پس از explicit terminal attempt ساخته می‌شود؛ timeout/unknown outcome مجوز ساخت successor نیست.

26.4. accounting_event_legs

id
accounting_event_id
leg_index

tigerbeetle_transfer_id

ledger_id
debit_account_id
credit_account_id
amount

transfer_code
flags

status
tigerbeetle_result_code

submitted_at
completed_at
created_at

Constraints:

UNIQUE(accounting_event_id, leg_index)
UNIQUE(tigerbeetle_transfer_id)

Array از transfer ID داخل accounting_events جایگزین این table نمی‌شود.


26.5. Spot Allocation

spot_execution_allocations

id
client_fill_id

source_type                  # VENUE_EXECUTION | BROKER_INVENTORY
venue_execution_id nullable
source_accounting_event_id nullable

base_quantity
quote_amount

created_at

این table امکان چند Venue Fill برای یک Client Fill، aggregation و همچنین تامین بخشی/تمام fill از Broker Inventory را می‌دهد. برای BROKER_INVENTORY، venue_execution_id باید null باشد و source_accounting_event_id به event داخلی اشاره کند.


26.6. Balance Cache

balance_cache فقط projection است:

NOT SOURCE OF TRUTH

Schema مفهومی:

balance_cache

tigerbeetle_account_id

debits_posted
credits_posted
debits_pending
credits_pending

economic_balance

last_accounting_event_id
refreshed_at

Refresh Rule

بعد از COMPLETED شدن Accounting Event:

  1. affected Account IDها استخراج شوند.
  2. lookup_accounts به شکل batch اجرا شود.
  3. cache با cumulative values دریافتی overwrite شود.
  4. refreshed_at ثبت شود.

Staleness Baseline

normal target <= 2 seconds
hard stale threshold = 5 seconds

Cache قدیمی‌تر از 5 ثانیه:

STALE

محسوب می‌شود و نباید برای authorization مالی مثل withdrawal، margin lock یا order lock استفاده شود.

Financial authorization در نهایت توسط TigerBeetle transfer constraints enforce می‌شود.


26.7. User Financial Status

users.financial_status

ACTIVE
FROZEN
CLOSED

FROZEN gate عملیاتی است؛ TigerBeetle closed فقط طبق §9.3 استفاده می‌شود.


26.8. Physical Storage Contract

بخش‌های قبلی §26 schema را در سطح مفهومی تعریف می‌کنند. این بخش نوع فیزیکی PostgreSQL را برای هر مفهوم TigerBeetle تثبیت می‌کند تا producerهای مختلف نوع متفاوت انتخاب نکنند.

مفهوم TigerBeetle عرض نوع PostgreSQL
id (account / transfer) u128 uuid
accounting_event_id u128 uuid
user_data_128 u128 uuid
amount u128 numeric(39,0)
user_data_64 / timestamp ms u64 numeric(20,0)
ledger u32 bigint + CHECK BETWEEN 1 AND 4294967295
user_data_32 u32 bigint + CHECK BETWEEN 0 AND 4294967295
code u16 integer + CHECK BETWEEN 1 AND 65535
flags u16 integer + CHECK BETWEEN 0 AND 65535

قاعده اجباری Amount

مقدار مالی هرگز در bigint ذخیره نمی‌شود.

bigint علامت‌دار ۶۴ بیتی است و سقف آن 9_223_372_036_854_775_807 است. با ETH scale = 18 طبق §5.2:

1 ETH   = 10^18 units          -> در bigint جا می‌شود
10 ETH  = 10^19 units          -> سرریز bigint

بنابراین یک withdrawal ساده ۱۰ ETH با bigint قابل ذخیره نیست. تمام ستون‌های amount و تمام cumulative counterهای TigerBeetle باید numeric(39,0) باشند؛ 39 رقم برای بازه کامل u128 یعنی 2^128 - 1 کافی است.

numeric(39,0) نوع دقیق و integer-only است و نقض §2 یا §34.3 محسوب نمی‌شود. ممنوعیت floating point همچنان کامل برجاست: real، double precision و money در هیچ ستون مالی مجاز نیستند.

قاعده علامت

Persistence Stack

driver:             Npgsql
financial mapping:  command/parameter و reader مستقیم Npgsql با نوع صریح
other mapping:      Dapper برای جداول غیرمالی مجاز است
schema evolution:   versioned raw SQL migration files

قواعد:


26.9. Status و Enumeration Contract

§11 وضعیت‌های accounting_events را شماره‌گذاری کرده است، ولی accounting_event_legs.status و accounting_events.failure_class تا 0.004 مقدار مجاز مشخصی نداشتند. این بخش آنها را می‌بندد تا producerهای مختلف رشته دلخواه ننویسند.

Leg Status

PENDING       # ID تولید و persist شده، هنوز submit نشده
SUBMITTING    # داخل یک request در حال پرواز
COMPLETED     # نتیجه ok یا exists با محتوای یکسان
FAILED        # نتیجه terminal برای همین leg

PENDING قبل از اولین submit الزامی است، چون §10.1 حکم می‌کند Transfer ID قبل از submit persist شود.

Failure Class

مقدار failure_class فقط زمانی پر می‌شود که event به RETRYABLE_FAILED، PERMANENT_FAILED یا REQUIRES_REVIEW برود.

مقدار منشأ Event Status Retry همان ID
INFRASTRUCTURE outcome نامعلوم، §10.4 حالت A RETRYABLE_FAILED بله، الزامی
TB_TRANSIENT_ID_BURN یکی از هفت result §10.4 حالت B PERMANENT_FAILED خیر، successor لازم است
TB_ID_ALREADY_FAILED دریافت id_already_failed PERMANENT_FAILED خیر، successor لازم است
TB_CONTENT_MISMATCH هر exists_with_different_* §10.3 REQUIRES_REVIEW خیر، retry خودکار ممنوع
TB_VALIDATION rejection قطعی و deterministic مانند ledger_must_not_be_zero PERMANENT_FAILED خیر، این یک bug است

قواعد:

Leg Flags

accounting_event_legs.flags دقیقاً همان مقداری است که به TigerBeetle submit می‌شود، شامل بیت linked.

دلیل: exists_with_different_flags طبق §10.3 یک Critical Data Integrity Error است. اگر flags هنگام submit به صورت derived محاسبه شود، دو نسخه از کد می‌توانند برای همان leg مقدار متفاوتی بسازند و retry به این خطا برخورد کند. با persist کردن مقدار نهایی، retry بایت‌به‌بایت همان چیزی را می‌فرستد که بار اول فرستاده شد.

نتیجه برای chain: طبق §27.4 هر Accounting Event یک linked chain واحد در یک create_transfers request است، بنابراین در زمان ساخت legها:

leg[0 .. n-2]  ->  flags شامل linked
leg[n-1]       ->  flags بدون linked      # closing leg زنجیره
n == 1         ->  flags بدون linked

submitter باید این ساختار را قبل از ارسال verify کند و در صورت نقض، event را بدون submit به REQUIRES_REVIEW ببرد؛ یک chain نیمه‌باز linked_event_chain_open می‌گیرد و کل event را بی‌دلیل می‌سوزاند.

Owner Type

مقادیر مجاز accounting_accounts.owner_type بر اساس چهار دسته user_data_128 در §9.1:

USER
VENUE
CUSTODY_GROUP
SYSTEM

نگاشت per-code از 0.007 طبق D-026 بسته است. AccountProvisioner می‌تواند owner_type را از کد بخواند؛ caller همچنان حق دارد همان مقدار را صریح بدهد ولی حق ندارد مقدار دیگری بنویسد.

26.10. Account Provisioning Sequence

ترتیب اجباری برای ساخت یک Account:

1. caller supplies owner_type, owner_id, asset_id, account_type, partition_key
2. generate TigerBeetle time-based account id
3. persist accounting_accounts row FIRST
4. create_accounts with:
     flags          = exact §9.5 matrix for that code
     user_data_128  = owner_id
     user_data_64   = 0
     user_data_32   = 0
5. Created or identical Exists => success
6. exists_with_different_*     => stop; do not recreate; treat as integrity incident

دلیل persist-before-create: §9.1 id را قبل از create_accounts در accounting_accounts می‌خواهد. اگر cluster پاسخ ندهد، retry همان id را می‌فرستد؛ ساخت id جدید بعد از یک create مبهم همان خطر §10.4 case A برای transfer است.

Idempotency: ردیف موجود با همان (owner_type, owner_id, asset_id, account_type, partition_key) دوباره با همان tigerbeetle_account_id submit می‌شود، نه با id تازه.

Change History — PostgreSQL Schema

Version Date Change
0.001 2026-09-12 Schema list اولیه
0.002 2026-09-12 افزودن instruments، accounting_event_legs، worker lease، spot allocations و cache refresh contract
0.003 2026-09-12 افزودن supersedes_event_id/failure_class و allocation source_type برای Broker Inventory
0.005 2026-09-12 افزودن partition_key به accounting_accounts و بخش 26.8 physical storage contract با الزام numeric(39,0) برای amount
0.006 2026-09-12 §26.10 توالی persist-then-create_accounts؛ owner_type ورودی صریح provisioning است

27. Cross-System Consistency و TigerBeetle Client Model

Introduced: 0.001
Last Changed: 0.002
Status: Designed

Distributed transaction واقعی بین این سیستم‌ها وجود ندارد:

PostgreSQL
TigerBeetle
LP / Venue
Blockchain

بنابراین نباید فرض شود یک ACID transaction همه آن‌ها را پوشش می‌دهد.


27.1. Durable Financial Intent

برای Financial Event:

  1. Business event دریافت شود.
  2. accounting_event در PostgreSQL persist شود.
  3. تمام accounting_event_legs و TigerBeetle IDها persist شوند.
  4. Worker event را با lease claim کند.
  5. تمام legهای نیازمند atomicity در یک create_transfers call ساخته شوند.
  6. operation به TigerBeetle ارسال شود.
  7. response مطابق §10.3 classify شود.
  8. created/ok یا valid exists => leg success.
  9. بعد از success همه legها => event COMPLETED.
  10. cache refresh schedule شود.
  11. crash در هر مرحله recoverable باشد.

Network ambiguity با retry همان ID حل می‌شود.


27.2. Venue Fill

Venue Fill باید identifier یکتا داشته باشد:

UNIQUE(venue_id, venue_execution_id)

Duplicate venue event نباید Accounting Event جدید ایجاد کند.

Client Fill و Venue Fill الزاماً 1:1 نیستند.


27.3. TigerBeetle Client Concurrency

TigerBeetle client باید shared باشد.

ممنوع:

HTTP request
   ↓
new TigerBeetle Client()

Baseline:

Accounting Service Process
        │
        └── one shared TigerBeetle client per cluster/config

Client بین thread/taskها share می‌شود تا automatic batching انجام شود.

TigerBeetle client در هر لحظه حداکثر یک request در-flight دارد؛ throughput از batching eventها در request حاصل می‌شود، نه ساخت تعداد زیادی client per request.


27.4. Batch و Linked Chain

Default maximum برای create_transfers:

8189 events / request

برای operation atomic:

one accounting event
    ↓
all linked legs
    ↓
one create_transfers request

Agent حق ندارد chain را بین چند request split کند و همچنان آن را atomic بنامد.


27.5. Worker Recovery

locked_until lease باید finite باشد.

اگر worker crash کند:

locked_until expires
        ↓
another worker claims event
        ↓
same persisted transfer IDs submitted/reconciled

SUBMITTING به معنای «حتماً ثبت نشده» نیست؛ بعد از crash باید response/idempotency semantics در نظر گرفته شود.

Change History — Cross-System Consistency

Version Date Change
0.001 2026-09-12 Durable accounting event و retry اولیه
0.002 2026-09-12 افزودن client sharing، automatic batching، one in-flight request، 8189 limit و worker lease recovery

28. Reconciliation

Introduced: 0.001
Last Changed: 0.003
Status: Designed

Reconciliation یک feature جانبی نیست؛ بخشی از طراحی اصلی سیستم مالی است.

تمام Accountهای production باید flags.history داشته باشند تا historical balance inspection ممکن باشد.


28.1. TigerBeetle Invariant

برای هر ledger:

Σ debits_posted = Σ credits_posted

28.2. Economic Balance Formula

Asset account:

asset_balance =
debits_posted - credits_posted

Liability account:

liability_balance =
credits_posted - debits_posted

Revenue:

revenue_balance =
credits_posted - debits_posted

Expense:

expense_balance =
debits_posted - credits_posted

28.3. Blockchain Reconciliation

برای هر custody wallet/group:

TigerBeetle Custody Asset
             VS
Blockchain observed balance

CUSTODY_DEPOSIT باید به wallet/address group واقعی متصل باشد؛ Deposit مستقیماً به Hot Wallet نسبت داده نمی‌شود مگر مقصد واقعی chain همان Hot Wallet باشد.

نمونه:

TB CUSTODY_DEPOSIT_BTC:W1 = 1.25000000
Chain wallet group W1     = 1.25000000
difference                = 0

Sweep:

CUSTODY_DEPOSIT -> CUSTODY_HOT

یک transfer مستقل است و باید با chain movement قابل تطبیق باشد.

هر Difference خارج از tolerance تعریف‌شده باید Alert ایجاد کند.


28.4. Venue Reconciliation

برای هر Venue و Asset:

TigerBeetle BINANCE_USDT_ASSET
             VS
Binance API balance

حداقل:


28.5. User Liability Reconciliation

برای هر Asset:

Σ User Available
+ Σ User Spot Locked
+ Σ User Margin
+ Σ User Withdrawal Locked

باید با total customer liability reconcile شود.


28.6. Spot Settlement Clearing Reconciliation

برای هر Asset:

SPOT_SETTLEMENT_CLEARING

باید در steady-state صفر باشد.

هر non-zero balance باید به یکی از موارد زیر trace شود:

client fill not yet allocated
venue fill not yet allocated
partial venue execution
price residual pending classification
incident under review

Reconciliation باید بتواند از spot_execution_allocations به Client Fill و Venue Fill برگردد.

Aging برای clearing itemها باید اندازه‌گیری شود. عبور از hard threshold طبق §20.4 باید علاوه بر Critical Alert، circuit breaker ایجاد Client Spot Settlement جدید را فعال کند؛ این کنترل optional نیست.


28.7. Operational Solvency View

برای هر Asset:

Custody Assets
+ Venue Assets
+ In-Transit Assets
+ Other Receivables
--------------------------------
Total Controlled / Claimed Assets

Customer Liabilities
--------------------------------
Net Broker Inventory / Equity

CLIENT_BAD_DEBT_RECEIVABLE باید جداگانه نمایش داده شود و با liquid asset اشتباه گرفته نشود.

P&L، Expense، Revenue و Reserve accountها باید در financial view نهایی لحاظ شوند.

Change History — Reconciliation

Version Date Change
0.001 2026-09-12 Blockchain/Venue/User reconciliation اولیه
0.002 2026-09-12 افزودن historical-balance requirement، deposit-wallet mapping و Spot Clearing reconciliation
0.003 2026-09-12 ارتقای Spot Clearing alert به hard reconciliation/circuit-breaker control

29. Manual Adjustment و Correction

Introduced: 0.001
Last Changed: 0.001
Status: Designed

هیچ admin endpoint نباید مستقیماً balance را تغییر دهد.

Adjustment همیشه Transfer است.

الزام metadata:

adjustment_id
original_event_id
reason
ticket_id
requested_by
approved_by
created_at

برای مبلغ‌های حساس، Maker/Checker approval پیشنهاد می‌شود.


30. Observability و Audit Logging

Introduced: 0.001
Last Changed: 0.002
Status: Designed

هر log مالی باید در صورت relevant بودن این فیلدها را داشته باشد:

accounting_event_id
supersedes_event_id
failure_class
tigerbeetle_transfer_id
accounting_leg_index
source_system_code

user_id

order_id
fill_id
position_id

hedge_order_id
hedge_fill_id

deposit_id
withdrawal_id

tx_hash
network_id

venue_id
venue_execution_id

asset_id
amount

service
trace_id

نباید در log ذخیره شوند:

private_key
seed_phrase
API secret
signing secret
full sensitive credential

31. Security Rules

Introduced: 0.001
Last Changed: 0.003
Status: Designed


32. API / Service Contract پیشنهادی Accounting Service

Introduced: 0.001
Last Changed: 0.002
Status: Designed

API دقیق می‌تواند RPC/HTTP/Event-driven باشد، اما use-caseهای زیر باید وجود داشته باشند:

ensureUserAssetAccounts()

creditConfirmedDeposit()
moveCustodyAsset()

lockSpotOrder()
releaseSpotOrderLock()

settleSpotClientBuyFill()
settleSpotClientSellFill()

settleSpotVenueBuyExecution()
settleSpotVenueSellExecution()
settleSpotInventoryFill()

recordSpotExecutionAllocation()
settleSpotResidual()

chargeTradingFee()
recordVenueExecutionFee()

lockMargin()
releaseMargin()

settleClientRealizedPnl()
settleHedgeRealizedPnl()

settleClientFunding()
settleVenueFunding()

settleLiquidation()
recognizeBadDebt()
writeOffBadDebt()

lockWithdrawal()
releaseWithdrawal()
markWithdrawalBroadcast()
settleConfirmedWithdrawal()

moveAssetToVenue()
moveAssetFromVenue()

createCorrection()

getUserBalance()
getAccountBalance()

runReconciliation()

تمام write commandها باید:

operation_id
occurred_at
source_system_code

داشته باشند.

operation_id باید قبل از Financial Submit پایدار و idempotent باشد.

از 0.007 این مقدار در PostgreSQL به صورت (aggregate_type, aggregate_id) ذخیره می‌شود و unique است. تکرار همان command همان event را برمی‌گرداند، نه یک posting دوم.

32.1. HTTP Command API (local / integration)

پروتکل local برای تست دستی: JSON روی HTTP. هیچ endpoint عمومی حق ندارد debit/credit دلخواه بسازد؛ فقط commandهای زیر مجازند.

POST /v1/users/{userId}/ensure
POST /v1/deposits
POST /v1/transfers
POST /v1/custody/moves
POST /v1/margin/locks
POST /v1/margin/releases
POST /v1/pnl/client
POST /v1/positions
POST /v1/positions/{id}/close
GET  /v1/users/{userId}/balances?asset=USDT
GET  /v1/events/{id}
GET  /v1/positions/{id}

Amountها integer خرد دارایی‌اند (مثلاً USDT scale 6: 1000000 = 1 USDT).

POST /v1/positions فقط USER_AVAILABLE → USER_MARGIN را می‌زند و ردیف positions را در PostgreSQL می‌نویسد. Unrealized PnL به ledger نمی‌رود.

از 0.008 commandهای باقی‌ماندهٔ §32 هم HTTP دارند. Spot client/venue settlement یک Accounting Event است ولی هر leg ledger خودش را دارد؛ TigerBeetle فقط داخل یک transfer همان ledger را اجباری می‌کند، نه برای کل chain.

POST /v1/withdrawals/lock
POST /v1/withdrawals/release
POST /v1/withdrawals/broadcast
POST /v1/withdrawals/confirm
POST /v1/spot/locks
POST /v1/spot/releases
POST /v1/spot/client-buy
POST /v1/spot/client-sell
POST /v1/spot/venue-buy
POST /v1/spot/venue-sell
POST /v1/spot/inventory-buy
POST /v1/spot/inventory-sell
POST /v1/spot/spread
POST /v1/fees/trading
POST /v1/fees/venue
POST /v1/venue/in
POST /v1/venue/out
POST /v1/hedge/pnl
POST /v1/funding/client
POST /v1/funding/venue
POST /v1/liquidations
POST /v1/bad-debt/writeoff
POST /v1/corrections
GET  /v1/reconciliation/{asset}
GET  /ops
GET  /ops/snapshot
GET  /ops/stream
GET  /docs
GET  /docs/glossary
GET  /docs/architecture

/ops کنسول عملیاتی local است. Snapshot زنده PostgreSQL و TigerBeetle را نشان می‌دهد و از 0.010 همان commandهای نام‌دار §32 را از فرم‌های همین صفحه صدا می‌زند. Debit/credit دلخواه همچنان ممنوع است؛ فقط همان endpointهای نام‌دار. پاک کردن داده از کنسول نیست. معنی نام فرمان‌ها و حساب‌ها: BROKER_ACCOUNTING_GLOSSARY_FA.md.

/docs همان دو فایل markdown را به‌صورت HTML سرو می‌کند.

سرویس compose به‌نام accounting ایمیج API را بالا می‌آورد و روی 127.0.0.1:5080 publish می‌کند. Nginx میزبان accounting.m0oo0m.xyz کل / را به http://accounting/ می‌فرستد (deploy/nginx-accounting.conf). این hostname کنسول و /v1 را هم نشان می‌دهد. آدرس TigerBeetle می‌تواند hostname باشد؛ قبل از ساخت client به IPv4 resolve می‌شود چون native client فقط IP می‌پذیرد. native client هم io_uring می‌خواهد؛ کانتینر accounting مثل replica با seccomp:unconfined اجرا می‌شود. اگر هسته kernel.io_uring_disabled را روشن کرده باشد باید روی host خاموش شود.

شروع دوبارهٔ local: volumeهای compose را دور بریز و stack را بالا بیاور.

docker compose down -v && docker compose up -d

Venue پیش‌فرض local: 33333333-3333-3333-3333-333333333333.

POST /v1/positions/{id}/close با realizedPnl علامت‌دار:

realizedPnl > 0  -> CLIENT_PROFIT_EXPENSE → USER_AVAILABLE
                    سپس USER_MARGIN → USER_AVAILABLE  (آزادسازی مانده)
realizedPnl < 0  -> collectible از USER_MARGIN سپس USER_AVAILABLE → CLIENT_LOSS_REVENUE
                    سپس ماندهٔ margin آزاد می‌شود
                    shortfall → CLIENT_BAD_DEBT_RECEIVABLE + BAD_DEBT_EXPENSE طبق §24
realizedPnl = 0  -> فقط آزادسازی margin

اولویت مصرف ضرر از 0.007: اول USER_MARGIN بعد USER_AVAILABLE (baseline §22.3).

32.2. Local identity defaults

برای محیط dev، در نبود caller صریح:

BROKER_SYSTEM_ID     = 11111111-1111-1111-1111-111111111111
DEFAULT_CUSTODY_ID   = 22222222-2222-2222-2222-222222222222
DEFAULT_VENUE_ID     = 33333333-3333-3333-3333-333333333333

user_data_128 حساب‌های SYSTEM همان BROKER_SYSTEM_ID است؛ حساب‌های custody پیش‌فرض همان DEFAULT_CUSTODY_ID؛ حساب‌های venue پیش‌فرض همان DEFAULT_VENUE_ID.

Spot Client Settlement و Spot Venue Settlement API جدا هستند؛ API واحدی که implicitly یک Client Fill را به یک Venue Fill bind کند مجاز نیست.

Change History — Accounting API

Version Date Change
0.001 2026-09-12 Use-case list اولیه
0.002 2026-09-12 تفکیک Client/Venue Spot settlement و افزودن bad-debt/liquidation operations
0.007 2026-09-12 HTTP JSON برای deposit/transfer/margin/position
0.008 2026-09-12 HTTP برای withdrawal، spot، venue، hedge، funding، liquidation، correction، reconciliation؛ DEFAULT_VENUE_ID
0.009 2026-09-12 کنسول /ops فقط‌خواندنی با snapshot زنده PostgreSQL + TigerBeetle
0.010 2026-09-12 فرم commandهای نام‌دار §32 داخل /ops
0.011 2026-09-12 POST /ops/reset برای شروع دوباره تست local
0.012 2026-09-12 reset محلی فایل data تایگر را هم از نو format می‌کند
0.013 2026-09-12 reset از /ops حذف شد؛ wipe محلی با docker compose down -v
0.014 2026-09-12 واژه‌نامهٔ کنسول در BROKER_ACCOUNTING_GLOSSARY_FA.md
0.015 2026-09-12 /docs HTML برای واژه‌نامه و معماری؛ دامنه فقط همین مسیر
0.016 2026-09-12 ایمیج API و make push به رجیستری داکر
0.017 2026-09-12 edge دامنه با Nginx میزبان؛ Caddy compose حذف شد
0.018 2026-09-12 سرویس compose accounting و Nginx accounting.m0oo0m.xyz
0.019 2026-09-12 resolve hostname تایگر به IPv4؛ libgssapi در ایمیج runtime
0.020 2026-09-12 seccomp:unconfined برای کانتینر accounting به‌خاطر io_uring کلاینت
0.021 2026-09-12 کانتینر accounting: IPC_LOCK/SYS_ADMIN و listen روی 0.0.0.0

33. State Ownership

Introduced: 0.001
Last Changed: 0.002
Status: Designed

Data Source of Truth
User financial balance TigerBeetle
Available balance TigerBeetle
Spot locked amount TigerBeetle
Margin locked amount TigerBeetle
Financial transfer history TigerBeetle
Order state PostgreSQL
Fill PostgreSQL
Position PostgreSQL
Entry Price PostgreSQL
Mark Price Market/Risk System
Unrealized PnL Risk/Position Service
Hedge position PostgreSQL/Venue projection
Blockchain transaction PostgreSQL + Blockchain
Tx finality Blockchain Integration
Venue execution PostgreSQL + Venue
Reconciliation result PostgreSQL
Spot settlement clearing balance TigerBeetle
Balance cache PostgreSQL projection only

34. Forbidden Patterns

Introduced: 0.001
Last Changed: 0.003
Status: Designed

این موارد ممنوع هستند:

34.1. Direct balance mutation

user.balance -= 100

بدون Financial Transfer.

34.2. PostgreSQL balance as source of truth

استفاده از PostgreSQL balance/cache به عنوان Financial Source of Truth ممنوع است.

34.3. Floating point money

استفاده از float / double برای money/asset ممنوع است.

34.4. Mutating history

Delete یا update کردن Transfer مالی برای اصلاح اشتباه ممنوع است.

34.5. Unrealized PnL posting

ثبت Unrealized PnL با هر market tick در TigerBeetle ممنوع است.

34.6. PnL netting before accounting

Net کردن Client PnL و Hedge PnL قبل از Accounting ممنوع است.

34.7. Fee netting before accounting

Net کردن Client Fee، Venue Fee، Funding یا Network Fee قبل از Accounting ممنوع است.

34.8. Withdrawal identity mistake

فرض:

withdrawal_id == tx_hash

ممنوع است.

34.9. New ID on ambiguous retry

ساخت Transfer ID جدید برای retry یک submission با outcome نامعلوم ممنوع است.

34.10. Financial write without durable intent

اجرای تغییر مالی بدون accounting_event_id و persisted leg IDs ممنوع است.

34.11. Direct Client-to-Venue Spot dependency

Spot client settlement نباید مستقیماً وابسته به یک Venue Fill مشخص باشد.

ممنوع:

1 client fill -> must equal exactly 1 venue fill

34.12. Splitting linked chains

تقسیم linked chain بین دو create_transfers request و ادعای atomicity ممنوع است.

34.13. Client-per-request

ساخت TigerBeetle client برای هر HTTP/RPC request ممنوع است.

34.14. Ignoring content mismatch

تبدیل exists_with_different_* به success/retry عادی ممنوع است.

این خطا Critical Data Integrity Incident است.

34.15. Provisioning without history

ساخت Account production بدون:

flags.history = true

ممنوع است مگر یک Decision Log جدید صریحاً استثنا تعریف کند.

34.16. Changing immutable semantics silently

تغییر موارد زیر بدون ledger/account migration و Document-First change ممنوع است:

asset scale
account code meaning
ledger meaning
user_data mapping
account flags baseline

34.17. Reusing partial IDs after a linked-chain burn

اگر یک leg explicit ID-burning error بگیرد، استفاده مجدد از ID سایر legهای همان failed Accounting Event برای successor ممنوع است.

Successor باید event جدید با تمام Transfer IDهای جدید باشد.

34.18. Normal zero-value posted legs

Emit کردن posted business transfer با amount = 0 صرفاً برای ثابت نگه‌داشتن تعداد legها ممنوع است. Zero فقط در API flowهایی که معنای خاص مستند دارد مجاز است.

34.19. Self-transfer / zero registry values

ارسال Transfer با debit و credit یکسان یا تولید ledger=0 / code=0 ممنوع است و باید قبل از TigerBeetle reject شود.

Change History — Forbidden Patterns

Version Date Change
0.001 2026-09-12 Forbidden list اولیه
0.002 2026-09-12 افزودن client/venue decoupling، linked-request atomicity، history flag و integrity mismatch rules
0.003 2026-09-12 ممنوعیت reuse بخشی از linked IDs پس از burn، zero-value normal legs و self-transfer/zero registry values

35. تست‌های اجباری

Introduced: 0.001
Last Changed: 0.003
Status: Designed

حداقل integration/chaos testها:

Account Provisioning

Idempotency

Balance

Deposit

Spot

Position / A-Book

Funding

Withdrawal

TigerBeetle Client Compatibility

Worker Recovery

Balance Cache

Reconciliation

Historical Import

اگر migration mode = HISTORICAL_IMPORT:

Change History — Tests

Version Date Change
0.001 2026-09-12 Test matrix اولیه
0.002 2026-09-12 افزودن multi-venue spot، ID mismatch، client batching, bad debt, history و migration tests

36. Crash / Retry Scenarios

Introduced: 0.001
Last Changed: 0.002
Status: Designed

Agent باید حداقل این crash pointها را تست کند:

1. accounting_event persisted ولی legs هنوز کامل persist نشده‌اند
2. event + legs persisted، قبل از TigerBeetle submit
3. بعد از submit ولی قبل از response
4. بعد از TigerBeetle success ولی قبل از PostgreSQL status update
5. بعد از event COMPLETED ولی قبل از balance_cache refresh
6. Venue fill دریافت شده ولی قبل از accounting
7. Client Spot Settlement انجام شده ولی Venue Settlement هنوز نرسیده
8. Venue Settlement انجام شده ولی allocation update crash کرده
9. Blockchain tx broadcast شده ولی قبل از ذخیره tx_hash
10. Withdrawal confirmed شده ولی قبل از accounting completion
11. Worker در حالت SUBMITTING crash کرده و lease منقضی شده

در تمام موارد:

durable event
+
durable leg IDs
+
TigerBeetle idempotency
+
worker lease
=
recoverable operation

36.1. Ambiguous Response

اگر درخواست ارسال شده ولی response قابل acknowledge نیست:

36.2. Existing Object

اگر result:

exists

و محتوا یکسان است، recovery success است.

اگر result:

exists_with_different_*

باشد:

REQUIRES_REVIEW
critical alert
no blind retry

Change History — Crash & Retry

Version Date Change
0.001 2026-09-12 crash pointهای اولیه
0.002 2026-09-12 افزودن lease recovery، Spot decoupling crash points و exists mismatch handling

37. Definition of Done برای هر Feature مالی

Introduced: 0.001
Last Changed: 0.001
Status: Designed

یک feature مالی Done نیست مگر اینکه:


38. قالب ثبت تغییر توسط Agent

هر تغییر باید در Global Change Log با این قالب ثبت شود:

## Version 0.00X — YYYY-MM-DD

### Changed
- ...

### Reason
- ...

### Affected Components
- ...

### Database / Migration
- ...

### Accounting Impact
- ...

### Reconciliation Impact
- ...

### Tests
- ...

### Compatibility
- ...

39. قالب Change History محلی

برای هر بخش:

### Change History — <Section Name>

| Version | Date | Change |
|---|---|---|
| 0.001 | 2026-09-12 | Initial design |
| 0.002 | 2026-09-15 | ... |

Agent نباید entry قدیمی را overwrite کند.

اگر تصمیم قبلی غلط یا منسوخ شد:

Deprecated in 0.00X
Replaced by ...

ثبت شود.


40. Decision Log

Introduced: 0.001
Last Changed: 0.003
Status: Active

D-001 — TigerBeetle Financial Source of Truth

Version: 0.001

تصمیم:

تمام balanceهای مالی authoritative در TigerBeetle هستند.

دلیل:


D-002 — PostgreSQL برای Trading State

Version: 0.001

Position، Order، Fill، Blockchain Tx و Venue Execution در PostgreSQL نگهداری می‌شوند.


D-003 — Unrealized PnL خارج از Ledger

Version: 0.001

Unrealized PnL توسط Position/Risk Engine محاسبه می‌شود و فقط Realized PnL settle می‌شود.


D-004 — Spot Lock Account

Version: 0.001

Spot reservation با:

USER_AVAILABLE → USER_SPOT_LOCKED

انجام می‌شود، نه یک Pending Transfer بلندمدت.

دلیل اصلی: پشتیبانی طبیعی از multiple partial fills.


D-005 — A-Book Client/Hedge Separation

Version: 0.001

Client financial result و Hedge financial result به صورت مستقل ثبت می‌شوند.


D-006 — Withdrawal Business ID

Version: 0.001

withdrawal_id مستقل از tx_hash است.


D-007 — Fee Transparency

Version: 0.001

Spread، Client Fee، Venue Fee، Funding و Network Fee legهای مستقل حسابداری هستند و نباید در exchange rate یا net value پنهان شوند.


D-008 — Spot Client/Venue Decoupling

Version: 0.002

Client Spot Settlement و Venue Settlement از طریق:

SPOT_SETTLEMENT_CLEARING

از یکدیگر جدا هستند.

سیستم هیچ فرض 1:1 بین Client Fill و Venue Fill ندارد.


D-009 — Production Accounts Require History

Version: 0.002

تمام Accountهای production با:

flags.history = true

ساخته می‌شوند.

ساخت account بدون history نیازمند Decision Log صریح جدید است.


D-010 — TigerBeetle Time-Based IDs + Persisted Leg IDs

Version: 0.002

Account و Transfer IDها از time-based scheme توصیه‌شده TigerBeetle استفاده می‌کنند.

هر Transfer ID قبل از submit در accounting_event_legs persist می‌شود.

Grouping توسط:

Transfer.user_data_128 = accounting_event_id

انجام می‌شود.


D-011 — Withdrawal Uses Posted State Accounts, Not Pending Transfer

Version: 0.002

Withdrawal lifecycle با:

USER_AVAILABLE
USER_WITHDRAWAL_LOCKED
ONCHAIN_OUTBOUND_IN_FLIGHT

مدل می‌شود.

دلیل: RBF/replacement و چند tx_hash برای یک withdrawal با one-shot pending resolution سازگار نیست.


D-012 — Historical Migration Is Pre-Go-Live

Version: 0.002

Migration mode باید قبل از live writes مشخص شود:

FRESH_START
HISTORICAL_IMPORT

Historical import بعد از شروع ترافیک عادی به عنوان migration معمولی مجاز نیست.


D-013 — Shared TigerBeetle Client

Version: 0.002

هر process یک TigerBeetle client shared برای cluster/config دارد.

Client-per-request ممنوع است.

Throughput بر batching تکیه می‌کند.


D-014 — Immutable user_data Mapping

Version: 0.002

Account:

user_data_128 = canonical entity id
user_data_64  = 0
user_data_32  = 0

Transfer:

user_data_128 = accounting_event_id
user_data_64  = occurred_at_ms
user_data_32  = source_system_code

تغییر این معنا برای objectهای جدید بدون migration/versioned schema ممنوع است.


D-015 — Explicit Bad Debt Path

Version: 0.002

Loss بیشتر از User funds نباید با debit نامعتبر به User account ثبت شود.

realized_loss =
collectible_loss
+ bad_debt_shortfall

Shortfall از مسیر:

CLIENT_BAD_DEBT_RECEIVABLE

عبور می‌کند و liquidation را block نمی‌کند.


D-016 — Go + TigerBeetle Client Compatibility Floor

Version: 0.003
Status: Deprecated in 0.004
Replaced by: D-023

Accounting Service و TigerBeetle integration با Go پیاده‌سازی می‌شوند.

module: github.com/tigerbeetle/tigerbeetle-go
minimum supported client: 0.16.4
minimum Go toolchain: 1.21

Dependency باید exact در go.mod/go.sum pin شود و downgrade زیر 0.16.4 ممنوع است.

منسوخ در 0.004: زبان پیاده‌سازی به .NET تغییر کرد. بخش مربوط به client floor یعنی >= 0.16.4 منسوخ نشده و در D-023 عیناً حفظ شده است.


D-017 — Burned ID Requires Superseding Accounting Event

Version: 0.003

اگر TigerBeetle یکی از هفت state-dependent ID-burning result را صریحاً برگرداند، attempt جاری terminal است.

برای linked event نیز هیچ Transfer ID از event شکست‌خورده reuse نمی‌شود.

Business re-attempt فقط با:

new accounting_event_id
supersedes_event_id = previous_event_id
all-new persisted transfer IDs

مجاز است.


D-018 — Per-Code Account Flag Matrix Is Immutable Baseline

Version: 0.003

Provisioning فقط بر اساس جدول §9.5 انجام می‌شود؛ inference runtime از category ممنوع است.


D-019 — Broker Inventory Is an Explicit Spot Settlement Source

Version: 0.003

Spot Client Fill می‌تواند از BROKER_SPOT_INVENTORY_RESERVE تامین شود و نباید برای این حالت Venue جعلی ساخته شود.


D-020 — Omit Zero-Amount Normal Business Legs

Version: 0.003

Normal posted leg با computed amount صفر ساخته نمی‌شود. Zero فقط در TigerBeetle operationهایی با semantics خاص و مستند مجاز است.


D-021 — Spot Clearing Guard Is a Hard Production Control

Version: 0.003

SPOT_SETTLEMENT_CLEARING unconstrained باقی می‌ماند، ولی breach در age/exposure threshold باید circuit breaker ایجاد Client Spot Settlement جدید را فعال کند.


D-022 — Withdrawal In-Flight Ordering Is Mandatory

Version: 0.003

Broadcast accounting و هر RBF spend delta باید قبل از confirmation settlement در ONCHAIN_OUTBOUND_IN_FLIGHT ثبت شده باشند.


D-023 — .NET به عنوان زبان پیاده‌سازی

Version: 0.004
Supersedes: D-016

Accounting Service و TigerBeetle integration با .NET پیاده‌سازی می‌شوند.

language:                 .NET / C#
official client package:  tigerbeetle (NuGet)
namespace:                TigerBeetle
minimum runtime:          .NET >= 8.0
minimum supported client: 0.16.4

دلیل: انتخاب پلتفرم تیم توسعه.

مواردی که این تصمیم تغییر نمی‌دهد:

الزامات:


D-024 — Hot Account Sharding Uses partition_key, Not user_data

Version: 0.005

sharding accountهای داغ §9.6 با ستون accounting_accounts.partition_key انجام می‌شود. مقدار پیش‌فرض '' و بخشی از unique constraint است:

UNIQUE (owner_type, owner_id, asset_id, account_type, partition_key)

دلیل: §9.6 اجازه چند Account با یک code را می‌داد ولی unique constraint §26.2 آن را غیرممکن می‌کرد. استفاده از user_data_64/32 برای partition label نیز گزینه نبود، چون §9.1 آن فیلدها را روی صفر freeze کرده و D-014 افزودن معنای پنهانی را ممنوع می‌کند.

نتیجه: partition metadata فقط operational و در PostgreSQL است؛ contract مالی TigerBeetle دست‌نخورده می‌ماند.


D-025 — Physical Storage Contract و Persistence Stack

Version: 0.005

u128 id / user_data_128  -> uuid
u128 amount / counter    -> numeric(39,0)
u64                      -> numeric(20,0)
u32                      -> bigint + CHECK
u16                      -> integer + CHECK

driver                   -> Npgsql
mapping                  -> Dapper
schema evolution         -> versioned immutable raw SQL migrations

دلیل اصلی انتخاب numeric(39,0) برای amount: سقف bigint برابر 9.22 * 10^18 است و با ETH scale = 18 مقدار ۱۰ ETH یعنی 10^19 سرریز می‌کند. بنابراین bigint برای amount مالی این پروژه نادرست است.

numeric(39,0) دقیق و integer-only است؛ ممنوعیت floating point در §34.3 دست‌نخورده است و real / double precision / money در هیچ ستون مالی مجاز نیستند.

دلیل نبودن ORM با change-tracking روی جداول مالی: نوشتن مالی باید صریح و قابل بازبینی باشد و lazy loading یا flush خودکار می‌تواند side effect مالی غیرمنتظره تولید کند.


D-026 — Per-code owner_type mapping

Version: 0.007

1001-1004 user liabilities          -> USER
2006 CLIENT_BAD_DEBT_RECEIVABLE     -> USER
2001-2005 custody / outbound        -> CUSTODY_GROUP
2101-2102 venue                     -> VENUE
3xxx revenue, 4xxx expense,
5xxx clearing, 6xxx equity          -> SYSTEM

دلیل: user_data_128 در زمان create_accounts immutable است. بدون نگاشت ثابت، دو caller می‌توانند برای یک code دو owner_type بنویسند و identity unique §26.2 را بشکنند.


41. Global Change Log

Version 0.021 — 2026-09-12

Changed

Reason

curl به host:5080 با RST برمی‌گشت در حالی که ASPNETCORE_URLS درست بود: یا process با io_uring می‌مرد، یا Kestrel فقط loopback داخل کانتینر را می‌گرفت.

Affected Components

Database / Migration

هیچ.

Accounting Impact

هیچ.

Reconciliation Impact

هیچ.

Tests

هیچ.

Compatibility

ایمیج باید rebuild/push شود. اگر باز io_uring PermissionDenied بود: sysctl -w kernel.io_uring_disabled=0 روی host.


Version 0.020 — 2026-09-12

Changed

Reason

seccomp پیش‌فرض Docker syscallهای io_uring را می‌بندد و tb_client با PermissionDenied panic می‌کند.

Affected Components

Database / Migration

هیچ.

Accounting Impact

هیچ.

Reconciliation Impact

هیچ.

Tests

هیچ.

Compatibility

سازگار با 0.019. فقط recreate کانتینر لازم است، نه rebuild ایمیج.


Version 0.019 — 2026-09-12

Changed

Reason

Native client تایگر tigerbeetle:3000 را رد می‌کند. Npgsql بدون libgssapi در کانتینر شروع را خراب می‌کند.

Affected Components

Database / Migration

هیچ.

Accounting Impact

هیچ.

Reconciliation Impact

هیچ.

Tests

Compatibility

سازگار با 0.018. ایمیج باید از نو build/push شود.


Version 0.018 — 2026-09-12

Changed

Reason

سرور تست از قبل Nginx و گواهی این دامنه را دارد؛ API باید یک سرویس compose با نام accounting باشد.

Affected Components

Database / Migration

هیچ.

Accounting Impact

هیچ.

Reconciliation Impact

هیچ.

Tests

هیچ.

Compatibility

جایگزین محدودیت «فقط /docs» در 0.017 برای این hostname.


Version 0.017 — 2026-09-12

Changed

Reason

سرور تست از قبل Nginx دارد؛ پورت ۸۰/۴۴۳ را نباید Caddy بگیرد.

Affected Components

Database / Migration

هیچ.

Accounting Impact

هیچ.

Reconciliation Impact

هیچ.

Tests

هیچ.

Compatibility

جایگزین edge Caddy در 0.015.


Version 0.016 — 2026-09-12

Changed

Reason

سرور تست باید ایمیج ساخته‌شده را از رجیستری بکشد، نه اینکه روی خود سرور dotnet run شود.

Affected Components

Database / Migration

هیچ.

Accounting Impact

هیچ.

Reconciliation Impact

هیچ.

Tests

هیچ.

Compatibility

سازگار با 0.015. فقط packaging.


Version 0.015 — 2026-09-12

Changed

Reason

همکاران باید سند را روی سرور تست با hostname ببینند، بدون اینکه کنسول یا commandهای /v1 روی اینترنت باز شوند.

Affected Components

Database / Migration

هیچ.

Accounting Impact

هیچ.

Reconciliation Impact

هیچ.

Tests

هیچ.

Compatibility

سازگار با 0.014. فقط hosting.


Version 0.014 — 2026-09-12

Changed

Reason

نام فرمان‌های /ops برای خوانندهٔ غیر از سند کامل مبهم است (مثلاً custody در برابر بدهی کاربر، یا چهار مرحلهٔ withdrawal).

Affected Components

Database / Migration

هیچ.

Accounting Impact

هیچ.

Reconciliation Impact

هیچ.

Tests

هیچ.

Compatibility

سازگار با 0.013.


Version 0.013 — 2026-09-12

Changed

Reason

Wipe از داخل process API session تایگر را evict می‌کند و کنسول را می‌شکند. down -v هر دو volume را تمیز برمی‌گرداند.

Affected Components

Database / Migration

volumeهای postgres-data و tigerbeetle-data حذف می‌شوند. migration و seed دارایی با استارت بعدی API برمی‌گردند.

Accounting Impact

کلاستر TB و جدول‌های عملیاتی هر دو از صفر شروع می‌شوند.

Reconciliation Impact

snapshot خالی است تا اولین command بعد از migrate.

Tests

هیچ.

Compatibility

جایگزین 0.011 / 0.012 reset HTTP. فقط local.


Version 0.012 — 2026-09-12

Changed

Reason

بدون wipe کلاستر، حساب‌های orphan در TB می‌ماند و تست «از صفر» تمیز نبود.

Affected Components

Database / Migration

هیچ. volume محلی /data/0_0.tigerbeetle از نو ساخته می‌شود.

Accounting Impact

بعد از reset هیچ transfer/accountی در TB نیست تا اولین provision.

Reconciliation Impact

snapshot خالی است.

Tests

Compatibility

سازگار با 0.011. فقط local.


Version 0.011 — 2026-09-12

Changed

Reason

در کنسول تست لازم است بشود از صفر دوباره داده گذاشت، بدون drop کردن دیتابیس و migration.

Affected Components

Database / Migration

هیچ جدول تازه‌ای نیست. TRUNCATE روی جدول‌های عملیاتی.

Accounting Impact

حساب‌های قبلی TigerBeetle orphan می‌شوند و دیگر map نمی‌شوند. account جدید با id تازه provision می‌شود.

Reconciliation Impact

بعد از reset، snapshot خالی است تا اولین command.

Tests

Compatibility

سازگار با 0.010. فقط برای local.


Version 0.010 — 2026-09-12

Changed

Reason

دیدن مانده بدون توانایی زدن همان commandها هنوز به ابزار جدا نیاز داشت.

Affected Components

Database / Migration

هیچ.

Accounting Impact

هیچ مسیر posting تازه‌ای اضافه نشد؛ UI همان API موجود را صدا می‌زند.

Reconciliation Impact

بدون تغییر.

Tests

Compatibility

سازگار با 0.009.


Version 0.009 — 2026-09-12

Changed

Reason

در تست دستی نباید برای دیدن اثر هر command به REPL تایگر یا SQL جدا رفت. Phase 6 tooling در لایه Accounting Service یعنی همین گزارش‌ها؛ comparator چین/venue همچنان به observation خارجی نیاز دارد.

Affected Components

Database / Migration

هیچ جدول تازه‌ای اضافه نشد.

Accounting Impact

هیچ. کنسول write مالی ندارد.

Reconciliation Impact

همان گزارش §0.008 حالا در UI زنده دیده می‌شود.

Tests

Compatibility

سازگار با 0.008.


Version 0.008 — 2026-09-12

Changed

Reason

پیاده‌سازی «همه فازها» در لایه Accounting Service یعنی همان use-caseهای §32، نه ناظر بلاکچین یا adapter صرافی. آن‌ها سرویس جدا هستند و از همین commandها مصرف می‌کنند.

Affected Components

Database / Migration

جدول عملیاتی spot_execution_allocations طبق §19.6 اضافه شد. Ledger همچنان فقط در TigerBeetle است.

Accounting Impact

هیچ account/transfer code تازه‌ای اضافه نشد. مسیرها همان §14–§25 هستند.

Reconciliation Impact

GET /v1/reconciliation/{asset} فقط گزارش می‌دهد: جمع liability کاربر، clearing، in-transit، و ردیف‌های stale cache. comparator چین/venue هنوز به API خارجی وصل نیست.

Tests

Compatibility

سازگار با 0.007.


Version 0.007 — 2026-09-12

Changed

Reason

تست دستی open/close پوزیشن و transfer بدون حدس debit/credit دلخواه ممکن نبود. HTTP فقط commandهای سند را expose می‌کند.

Affected Components

Database / Migration

Accounting Impact

هیچ account code یا flag تازه‌ای اضافه نشد. فقط مسیر HTTP روی transfer codeهای موجود §12 سوار شد.

Reconciliation Impact

هیچ.

Tests

Compatibility

سازگار با 0.006.


Version 0.005 — 2026-09-12

Changed

Reason

پیاده‌سازی دیتا مدل نیاز به نوع فیزیکی قطعی داشت و دو تناقض در baseline قبلی آشکار شد:

۱. §9.6 اجازه چند Account با یک code را می‌داد ولی unique constraint §26.2 آن را غیرممکن می‌کرد و §9.1 هم user_data_64/32 را روی صفر freeze کرده بود، بنابراین هیچ محل مجازی برای partition label وجود نداشت.

۲. سند هیچ‌جا نوع فیزیکی amount را تعیین نکرده بود. انتخاب طبیعی bigint با ETH scale = 18 نادرست است، چون سقف bigint برابر 9.22 * 10^18 است و ۱۰ ETH یعنی 10^19 سرریز می‌کند. کشف این مورد بعد از go-live نیازمند migration نوع ستون روی جداول مالی بود.

۳. سه ستون schema هیچ مجموعه مقدار مجازی نداشتند: accounting_event_legs.status، accounting_events.failure_class و accounting_accounts.owner_type. §26.9 آنها را از رفتار مستندشده §10.3، §10.4 و §9.1 استخراج و تثبیت کرد.

Affected Components

Database / Migration

این نسخه baseline اولین migration است و روی هیچ داده production اعمال نمی‌شود.

Accounting Impact

هیچ. هیچ ledger، account code، transfer code، flag matrix، flow یا invariant حسابداری تغییر نکرده است. تغییرات صرفاً representation در PostgreSQL هستند.

Reconciliation Impact

هیچ تغییر قاعده‌ای. §9.6 از قبل aggregation بر اساس code + external metadata را الزام کرده بود و اکنون آن metadata نام ستون مشخص دارد.

Tests

Compatibility

سازگار با 0.004. هیچ تصمیم immutable قبلی نقض نشده است. user_data_64/32 طبق §9.1 و D-014 روی صفر باقی می‌مانند و floating point طبق §34.3 همچنان ممنوع است.


Version 0.004 — 2026-09-12

Changed

Reason

تغییر پلتفرم انتخابی تیم توسعه. تغییر در سطح client/runtime است و هیچ تصمیم حسابداری را جابه‌جا نمی‌کند.

Affected Components

Accounting Service
TigerBeetle Client Integration
Worker Runtime
Dependency/Build/CI
PostgreSQL ID column types

Database / Migration

هیچ migration مالی لازم نیست. چون هنوز هیچ Account/Transfer production ساخته نشده است، تثبیت نوع ستون‌های ID روی uuid تغییر baseline است نه migration.

Accounting Impact

هیچ. Ledger registry، Account Code registry، Transfer Code registry، flag matrix §9.5، user_data mapping، تمام accounting flowها و تمام invariantها بدون تغییر باقی مانده‌اند.

Reconciliation Impact

هیچ.

Tests

§35 بخش TigerBeetle Client Compatibility گسترش یافت.

Compatibility

0.004 نسبت به 0.003 از نظر مدل مالی کاملاً backward-compatible است. تنها ناسازگاری در سطح toolchain/dependency است.


Version 0.003 — 2026-09-12

Changed

Reason

بستن رفتارهای غیرقابل‌بازگشت و retry semantics قبل از Phase 1/provisioning، و حذف آخرین فرض‌های پنهان در Spot/Withdrawal flows.

Affected Components

Accounting Service
TigerBeetle Client Layer
Accounting Worker
Account Provisioning
Spot Settlement
Spot Reconciliation
Wallet/Withdrawal
PostgreSQL Schema
CI/Test Harness

Database / Migration

اگر Accountهای production قبلاً با flag متفاوت ساخته شده‌اند، in-place edit مجاز نیست و migration/reprovisioning لازم است.

Accounting Impact

Reconciliation Impact

Tests

§35 و §36 بروزرسانی شدند.

Compatibility

0.003 حداقل TigerBeetle client 0.16.4 را فرض می‌کند. Client پایین‌تر supported نیست.

اگر provisioning واقعی بر اساس v0.002 انجام شده باشد، flag matrix باید با Accountهای موجود compare شود؛ هر mismatch نیازمند migration است.


Version 0.002 — 2026-09-12

Changed

Reason

رفع تناقض‌های معماری و تصمیم‌های immutable قبل از شروع provisioning و implementation.

Affected Components

Accounting Service
Spot Settlement
Execution Service
Position/Risk
Wallet Service
Reconciliation
PostgreSQL
TigerBeetle Provisioning
Worker Runtime

Database / Migration

Accounting Impact

Breaking architecture refinement قبل از go-live.

Reconciliation Impact

Tests

§35 و §36 بروزرسانی شدند.

Compatibility

0.002 نسبت به هر provisioning واقعی مبتنی بر account flags/mapping نسخه 0.001 backward-compatible فرض نمی‌شود.

اگر هنوز هیچ Account production ساخته نشده، 0.002 baseline جدید است.


Version 0.001 — 2026-09-12

Changed

Reason

ایجاد baseline معماری قابل پیاده‌سازی برای:

A-Book Derivatives
+
Real Spot
+
Blockchain Custody
+
On-Chain Deposit/Withdrawal

Affected Components

Accounting Service
OMS
Position Service
Risk Service
Execution/Hedge Service
Wallet Service
PostgreSQL
TigerBeetle
Reconciliation

Database / Migration

Initial schema required.

Accounting Impact

Initial accounting model.

Reconciliation Impact

Initial reconciliation model.

Tests

Test matrix در بخش 35 تعریف شد.

Compatibility

Initial version; backward compatibility موضوعیت ندارد.


42. Open Decisions و Go-Live Blockers

این بخش فقط شامل تصمیم‌هایی است که واقعاً هنوز باز هستند. موارد immutable مربوط به history, ID scheme, user_data, initial scale, account flags و TigerBeetle client semantics از v0.003 دیگر «بعداً تصمیم می‌گیریم» نیستند.

42.1. Pre-Provisioning Gate

قبل از اولین create_accounts واقعی باید این موارد verify شوند:

اگر migration_mode = HISTORICAL_IMPORT باشد، import و reconciliation باید قبل از live writer تکمیل شوند.


42.2. قبل از Spot Production


42.3. قبل از Derivatives Production

مسیر accounting برای shortfall از v0.002 مشخص است، ولی business/risk policy هنوز باید نهایی شود.


42.4. قبل از Blockchain Production


42.5. Operational Decisions

هر تصمیم باز هنگام نهایی‌شدن باید:

  1. در بخش اصلی مربوطه نوشته شود.
  2. Decision Log داشته باشد.
  3. Version bump شود.
  4. Global Change Log بروزرسانی شود.
  5. سپس implement شود.

43. دستور شروع کار برای Agent

Agent باید قبل از شروع کدنویسی Pre-Provisioning Gate در §42.1 را بررسی کند.

Phase 0 — Architecture Gate
Validate document version = 0.005+
Select migration_mode
Verify implementation language = .NET
Verify tigerbeetle NuGet package >= 0.16.4 and exact version pin
Freeze initial asset/ledger registry
Verify exact per-code account flag matrix + user_data mapping
Run design-level accounting invariant tests

Phase 1 — Foundation
Asset Registry
Instrument Registry
TigerBeetle shared client
Account code registry
Transfer code registry
Accounting account provisioning

Phase 2 — Durable Accounting Runtime
Accounting Event
Accounting Event Legs
ID generation + persistence
Worker lease
Retry/recovery
Result classification
Balance query
Balance cache

Phase 3 — Custody
Deposit
Internal transfer
Withdrawal
Custody movement
Venue funding

Phase 4 — Spot
Order lock/release
Client spot settlement
Venue spot settlement
Execution allocation
Partial fill
Fees/spread/price-improvement
Spot clearing reconciliation

Phase 5 — Derivatives
Margin
Position realized pnl
A-Book hedge accounting
Funding
Liquidation
Bad debt

Phase 6 — Reconciliation & Operations
Blockchain reconciliation
Venue reconciliation
User liability reconciliation
Spot clearing reconciliation
Alerts
Manual corrections
Operational tooling

از 0.008 commandهای حسابداری Phase 3–6 در Accounting Service پیاده شده‌اند. این به معنی ناظر بلاکچین، client صرافی، یا matching OMS نیست؛ آن‌ها سرویس جدا هستند و فقط همین commandها را صدا می‌زنند.

در شروع هر Phase:

  1. همین document review شود.
  2. Open decisionهای آن Phase بسته شوند.
  3. document version/change log در صورت تغییر bump شود.
  4. سپس code تغییر کند.

44. منابع فنی TigerBeetle

Implementation باید با documentation رسمی نسخه client/server مورد استفاده تطبیق داده شود.

Agent باید هنگام implementation API details را با documentation رسمی version مورد استفاده verify کند.

اگر documentation version مورد استفاده با این سند تفاوت دارد:

Update Document
        ↓
Version / Change Log
        ↓
Then Code

نه برعکس.


45. خلاصه معماری

                         USER
                          │
              ┌───────────┴────────────┐
              │                        │
             SPOT                  DERIVATIVES
              │                        │
      Available / Locked        Available / Margin
              │                        │
              ▼                        ▼
       Client Settlement         Client Position
              │                        │
              ▼                        ▼
   SPOT_SETTLEMENT_CLEARING        Hedge Engine
              │                        │
      ┌───────┼────────┐               │
      │       │        │               │
      ▼       ▼        ▼               ▼
 Inventory Venue A Venue B          LP / Venue
      │                │               │
      └───────┬────────┘               │
              ▼                        ▼
          Venue Assets             Venue Assets
              │                        │
              └──────────┬─────────────┘
                         ▼
                    In Transit
                         │
                         ▼
                Hot / Warm / Cold
                         │
                         ▼
                    Blockchain
TigerBeetle:
    Financial ownership
    Balances
    Account history
    Transfers
    Spot clearing
    Fees
    Realized PnL
    Margin
    Funding
    Settlement
    Bad-debt receivable/writeoff

PostgreSQL:
    Asset/Instrument registry
    Orders
    Client fills
    Spot execution allocations
    Positions
    Hedge orders/fills/allocations
    Deposits
    Withdrawals
    Blockchain transactions
    Accounting event intents/legs
    Worker leases
    Reconciliation state
    Non-authoritative balance cache

اصول نهایی

Balance نتیجه Transferها است.

Transfer نتیجه تغییر دستی Balance نیست.
Client execution
!=
Venue execution
Unrealized PnL
!=
Ledger posting
Ambiguous retry
=
same persisted ID
exists_with_different_*
=
critical integrity incident
هر تغییر معماری:
Document First
Code Second
Change Log Always