Skip to content

agntdev/orderbook-protocol

Repository files navigation

order-book

A decentralized order book system on the TON blockchain for peer-to-peer token exchanges (jetton ↔ jetton, TON ↔ jetton) with slippage protection, fee management, and order matching capabilities.

Audit

Status: Passed ✅

The smart contracts have been audited and verified. Full audit report is available here:

Audit Report | PDF

Table of Contents

Project Structure

contracts/              Smart contracts (Tolk)
  utils/                Shared utilities (errors, opcodes, fees, types, messages, fee_calc)
wrappers/               TypeScript wrappers implementing Contract from @ton/core
tests/                  Test suites (Jest + @ton/sandbox)
scripts/                Deployment and interaction scripts (Blueprint)
  helpers/              Shared script utilities (API client)

Architecture

Exchange Contracts Architecture

The system consists of four core contracts with a modular utility-based architecture:

VaultFactory ──creates──► Vault (VaultJetton / VaultTon)
                              │
                              ├── creates ──► Order
                              ├── sends fees ──► FeeCollector
                              └── transfers tokens to matched orders

Two separate VaultFactory instances are deployed — one compiled with VaultJetton code (for jetton vaults) and one with VaultTon code (for TON vaults). Each factory produces vaults of its respective type.

Core Contracts

Order (order.tolk)

Represents a single trading order with exchange parameters.

Storage:

Field Type Description
owner address Order creator
vault address Vault holding funds for this order
oppositeVault address Vault of the counter-party token
feeInfo Cell<FeeInfo>? Provider and matcher fee configuration
exchangeInfo Cell<ExchangeInfo> Amount, priceRate, slippage, jetton info
createdAt uint32 Creation timestamp
closeFlag bool Whether the order has been closed

Operations:

Opcode Name Description
0x2d0e1e1b InitOrder Initialize with amount, priceRate, slippage, feeInfo
0x47ff7e25 MatchOrder Initiate matching (called by matcher)
0xdfe29f63 InternalMatchOrder Internal matching with slippage/fee validation (from vault)
0x55feb42a SuccessMatch Confirm match and update state
0x52e80bac CloseOrder Close order and return remaining funds

VaultJetton (vault_jetton.tolk)

Holds jetton tokens and manages jetton-based order creation and transfers.

Storage:

Field Type Description
jettonWalletAddress address? Associated jetton wallet address
vault_factory address Parent factory
codesInfo Cell<CodesInfo> Order + FeeCollector compiled code
fromJetton Cell<JettonInfo>? Jetton minter info
randomHash uint256 Random hash for vault uniqueness
amount coins Total deposited amount

VaultTon (vault_ton.tolk)

Holds native TON and manages TON-based order creation and transfers.

Storage:

Field Type Description
vault_factory address Parent factory
codesInfo Cell<CodesInfo> Order + FeeCollector compiled code
randomHash uint256 Random hash for vault uniqueness
amount coins Total deposited amount

VaultFactory (vault_factory.tolk)

Factory for deploying vault instances. Each factory is compiled with a specific vault code (VaultJetton or VaultTon).

Operations:

Opcode Name Description
0x2717c4a2 CreateVault Deploy a new vault

Parameters for sendCreateVault:

  • jettonMaster: Address | null — jetton minter address (null for TON vaults)
  • jettonWalletAddress: Address | null — pre-computed jetton wallet address for the vault
  • randomHash: bigint | null — random hash for uniqueness

FeeCollector (fee_collector.tolk)

Per-vault, per-matcher contract that accumulates matcher fees from successful matches.

Storage:

Field Type Description
vault address Associated vault
owner address Matcher (fee recipient)
amount coins Accumulated fees

Operations:

Opcode Name Description
0xfc7532f4 AddFee Add fee (called by vault only)
0xec9a92f6 WithDraw Withdraw all fees (called by owner only)

Address derivation: FeeCollector addresses are deterministic, computed from contractAddress(workchain, { code: feeCollectorCode, data: cell(vault, owner, 0) }). This allows off-chain pre-computation.

Utility Modules

All contracts share a set of utility modules in contracts/utils/:

Module Purpose
messages.tolk Message sending: sendSimpleMessage, sendBouncedMessage, sendJettonTransfer, sendFeeToCollector, sendFeeToProvider
fee_calc.tolk calculateFees (provider + matcher fee deduction), addFeesToRate (adjust rate for slippage validation)
types.tolk Shared structs: JettonInfo, FeeInfo, TransferAddresses
generate_addresses.tolk Deterministic address generation: generateOrder, generateFeeCollector
fees.tolk Gas constants for all operations
errors.tolk Error codes
op_codes.tolk Operation codes

Error Codes

Code Name Description
403 ERR_INVALID_SENDER Unauthorized sender
406 ERR_AMOUNT_NOT_AVAILABLE Insufficient order amount
407 ERR_INVALID_AMOUNT Invalid amount value
422 ERR_INSUFFICIENT_GAS Not enough gas
427 ERR_FORWARD_PAYLOAD_REQUIRED Missing forward payload
428 ERR_FROM_JETTON_REQUIRED From-jetton info required
429 ERR_INVALID_JETTON_WALLET Jetton wallet mismatch
430 ERR_INVALID_SLIPPAGE Slippage check failed
431 ERR_ORDER_ALREADY_INITIALIZED Double initialization attempt
432 ERR_VAULT_ALREADY_HAS_JETTON Vault jetton already set
433 ERR_CLOSE_FLAG Order already closed

Gas Constants

Constant Value (TON)
GAS_STORAGE 0.01
GAS_MIN_ORDER_STORAGE 0.003
GAS_ORDER_FULL_MATCH 1.0
GAS_JETTON_WALLET_TRANSFER 0.05
GAS_VAULT_FACTORY_CREATE_VAULT 0.03
GAS_VAULT_FACTORY_INIT 0.01
GAS_VAULT_JETTON_TRANSFER 0.01883
GAS_VAULT_TON_TRANSFER 0.1
GAS_VAULT_WITHDRAW 0.004154
GAS_ORDER_CLOSE_ORDER 0.05
GAS_ORDER_INIT 0.04
GAS_CREATE_ORDER_JETTON 0.1
GAS_FEE_COLLECTOR_WITHDRAW 0.05
GAS_FEE_COLLECTOR_ADD_FEE 0.001

Precision Constants

The system uses two precision bases for on-chain fixed-point arithmetic:

Constant Value Usage
PRICE_PRECISION 10^18 (1e18) Base unit for priceRate. A rate of PRICE_PRECISION means 1:1 exchange. 2 * PRICE_PRECISION means 1 token = 2 counter-tokens.
SLIPPAGE_PRECISION 10^9 (1e9 = 100%) Base unit for slippage tolerance. SLIPPAGE_PRECISION * 2 / 100 = 2% slippage.

Exchange math:

anotherGetAmount = mulDivFloor(matchAmount, priceRate, PRICE_PRECISION)
getAmount        = mulDivFloor(anotherGetAmount, PRICE_PRECISION, priceRate)

Fee Structure

Dual fee model with provider and matcher fees:

  • Provider Fee: Charged by the vault provider (feeNum / feeDenom ratio)
  • Matcher Fee: Charged by the order matcher (matcherFeeNum / matcherFeeDenom ratio)

Fee calculation (calculateFees from fee_calc.tolk):

  1. Calculate provider fee and matcher fee from the transfer amount
  2. Return net transfer amount after fee deductions
  3. Handle zero fees gracefully

Fees are deducted during order matching. Matcher fees are accumulated in per-vault, per-matcher FeeCollector contracts. The matcher can withdraw accumulated fees at any time via sendWithDraw.

Slippage Protection

Orders include slippage tolerance parameters. During matching:

  1. Price rates are adjusted to include fees using addFeesToRate from fee_calc.tolk
  2. Adjusted rates are compared against original rates with slippage tolerance using isWithinSlippage
  3. Both orders must validate each other's rates — the match proceeds only if prices are within acceptable ranges

Bounce Handling

The Order contract implements a two-path bounce handler for InternalMatchOrder failures:

Penalty Path (fatal errors)

Triggered by errors that indicate a malformed or malicious match attempt. The order burns matcher gas by sending to the zero address:

  • computePhase == null — transaction ran out of gas
  • ERR_INVALID_SLIPPAGE (430) — price out of range
  • ERR_CLOSE_FLAG (433) — order already closed

Non-Penalty Path (recoverable errors)

Triggered by non-fatal errors (e.g., ERR_AMOUNT_NOT_AVAILABLE 406). The order restores its amount and sends OP_CODE_RETURN_MATCHER_FAILED back to the matcher, allowing retry.

Exchange Flow

1. Deploy VaultFactory    ──►  VaultFactory (with VaultJetton or VaultTon code)
2. Create Vault           ──►  VaultFactory.sendCreateVault(jettonMaster, jettonWalletAddress, randomHash)
3. Deposit + Create Order ──►  Send jettons/TON to vault → vault auto-creates Order
4. Match Orders           ──►  Matcher calls order1.sendMatchOrder(anotherOrderOwner, createdAt, amount)
5. Internal Match         ──►  order1 → vault → order2 (InternalMatchOrder with slippage validation)
6. Success Match          ──►  order2 → vault → order1 (SuccessMatch with token transfer + fee deduction)
7. Fees                   ──►  Vault sends fees to FeeCollector and provider
8. Close Order            ──►  Owner calls order.sendCloseOrder() → remaining funds returned
9. Withdraw Fees          ──►  Matcher calls feeCollector.sendWithDraw()

TypeScript Wrappers

All wrappers are in wrappers/ and implement the Contract interface from @ton/core:

Wrapper File Description
Order Order.ts Create orders, match, close, getData
VaultJetton VaultJetton.ts Jetton vault interactions
VaultTon VaultTon.ts TON vault interactions
VaultFactory VaultFactory.ts Create vaults via factory
FeeCollector MatcherFeeCollector.ts Withdraw fees, getData
JettonWallet JettonWallet.ts Jetton wallet interactions (createOrder via transfer)
JettonMinter JettonMinter.ts Jetton minter interactions
HighloadWalletV3 HighloadWalletV3.ts Highload wallet for batch operations (up to 254 messages per tx)
HighloadQueryId HighloadQueryId.ts Query ID management for HighloadWalletV3

Scripts

All scripts are in scripts/. Blueprint scripts are run with npx blueprint run <scriptName>.

Deployment & Interaction

Script Run With Description
deployVaultFactory.ts npx blueprint run Deploy VaultFactory (jetton or TON variant)
createVault.ts npx blueprint run Create a vault with pre-computed jettonWalletAddress
createOrder.ts npx blueprint run Create an order with correct PRICE_PRECISION / SLIPPAGE_PRECISION
matchOrder.ts npx blueprint run Match two orders
closeOrder.ts npx blueprint run Close a single order
withDrawMatcherFeeCollector.ts npx blueprint run Withdraw fees from a FeeCollector

Batch Operations (Blueprint Provider)

Script Run With Description
batchCloseOrder.ts npx blueprint run Close all deployed orders for the connected wallet. Fetches orders from the Open4Dev API.
batchWithdraw.ts npx blueprint run Withdraw fees from all FeeCollectors. Computes FeeCollector addresses for each vault + connected wallet, checks balances, sends Withdraw.

Batch Operations (Mnemonic)

Standalone scripts supporting three wallet types: V4R2, V5R1, and HighloadV3.

Script Description
batchCloseOrderMnemonic.ts Batch close orders via mnemonic wallet
batchWithdrawMnemonic.ts Batch withdraw fees via mnemonic wallet

Environment variables:

Variable Required Default Description
MNEMONIC Yes 24 words separated by spaces
WALLET_VERSION No V4R2 V4R2 | V5R1 | HighloadV3
NETWORK No testnet mainnet | testnet
TONCENTER_API_KEY No Toncenter API key (recommended for mainnet)

Usage:

# V4R2 — sends messages in batches of 4, waits for seqno between batches
MNEMONIC="word1 word2 ..." WALLET_VERSION=V4R2 NETWORK=testnet \
  npx ts-node scripts/batchCloseOrderMnemonic.ts

# V5R1 — sends messages in batches of 255
MNEMONIC="word1 word2 ..." WALLET_VERSION=V5R1 NETWORK=mainnet \
  npx ts-node scripts/batchWithdrawMnemonic.ts

# HighloadV3 — sends ALL messages in a single transaction (up to 254 per internal transfer, auto-chained)
MNEMONIC="word1 word2 ..." WALLET_VERSION=HighloadV3 NETWORK=mainnet \
  npx ts-node scripts/batchCloseOrderMnemonic.ts

API Helper

scripts/helpers/api.ts provides a shared client for the Open4Dev API:

  • fetchOrders(ownerRawAddress, status?) — fetch orders with pagination (default status: deployed)
  • fetchVaults() — fetch all vaults with pagination

Testing

Run tests:

npx jest --runInBand    # sequential (recommended — avoids compilation timeouts)
npx blueprint test      # or via blueprint

Test suites (93 tests across 5 suites):

Suite File Coverage
Order Order.spec.ts Order creation, matching (full + partial), slippage validation, fee calculations, close order, bounce handling (penalty + non-penalty), PRICE_PRECISION math with non-1:1 rates
VaultFactory VaultFactory.spec.ts Vault creation, order creation through vault, matching via vault, partial matches
VaultJetton VaultJetton.spec.ts Jetton vault initialization, deposits, transfers
VaultTon VaultTon.spec.ts TON vault initialization, deposits, transfers
FeeCollector FeeCollector.spec.ts Fee accumulation, withdrawal, authorization

Key Test Scenarios

  • Partial matches with rate ≠ 1:1 — verifies exact exchange amounts using PRICE_PRECISION math
  • Bounce handling — penalty path (slippage error, close flag, out of gas) and non-penalty path (amount not available → return to matcher)
  • All InternalMatchOrder bounce tests verify order amount restoration
  • Multi-step partial matching with amount verification after each step

Deployed Contracts

Mainnet

VaultFactory

Vault Type Mainnet Address
VaultFactory with VaultJetton EQBcZQhXBU9GfDGLjo5tBKFpnq2tmLEBNLpS8ldqAunaDQUa
VaultFactory with VaultTon EQB20cDxfCIfIw4B4XOqNPAF9pDxheBUbNYUjCYzakbMdMMl

Supported Tokens

TON (Native)
Property Value
Vault Address Created via TON VaultFactory
Jettons
Token Jetton Address Vault Address
BUILD EQBYnUrIlwBrWqp_rl-VxeSBvTR2VmTfC4ManQ657n_BUILD EQDJ-N9sbbh2vNmbJ9DANEWpHdZqW2y8qAAnoBS5rY9fVdLO
cbBTC EQDhyPzbIjJT_WnY3gGprjSYUK9fiGMjWMezxO8MZiUdfb_B EQCYdHD4Pwz5ZtDlyCmSc5XjnfefXeAK2TE1Vz356xr6ILSZ
DOGS EQCvxJy4eG8hyHBFsZ7eePxrRsUQSFE_jpptRAYBmcG_DOGS EQCzqK8_LNpqyXviutwVJUhw30FcAs6YL8HO9cMNCEaAybpt
NOT EQAvlWFDxGF2lXm67y4yzC17wYKD9A0guwPkMs1gOsM__NOT EQD7vaWSbY38DqQ0zY2hNvagO2M-AuL7InUHN4_x2ThceN6J
PX EQB420yQsZobGcy0VYDfSKHpG2QQlw-j1f_tPu1J488I__PX EQA6kzh2-YZbJa5L9PUu7dDCQDs52-uVQDxBIXFFp6b0ATmZ
USDT EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs EQBrozHGTEwumr5ND62CpUXqmfYyi1UucbIj-15ZJnlFLe9U
XAUT EQA1R_LuQCLHlMgOo1S4G7Y7W1cd0FrAkbA10Zq7rddKxi9k EQA9cFoL4hcjOsMTYHHxG0hNyGoikG11oabAQuVQwRKFUhq5

Testnet

To be added

Development

Prerequisites

Dependencies

{
  "@ton/core": "^0.62.0",
  "@ton/ton": "^16.0.0",
  "@ton/crypto": "^3.3.0",
  "@ton/sandbox": "0.41.0",
  "@ton/blueprint": ">=0.38.0"
}

Commands

npx blueprint build          # Compile contracts
npx blueprint test           # Run tests
npx blueprint run            # Run a script (interactive selection)
npx tsc --noEmit             # Type-check TypeScript

Contributing

Adding New Jetton Support

If your jetton is not supported, submit a pull request modifying the calculateJettonWallet function in vault_jetton.tolk.

Warning: If the calculateJettonWallet function in the vault does not match your jetton wallet smart contract, the vault will not create the order.

About

Orderbook protocol

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors