Skip to content

Trustless Bridge Architecture (TVM ↔ TVM)

Overview

Trustless bridge is a decentralized cross-chain system for token transfers between TVM-compatible networks, such as TON and Tetra L2. Unlike relay-based bridge (used for transfers between EVM and TVM networks), Trustless bridge does not rely on relay nodes for transfer confirmation. Instead, the system uses cryptographic verification based on Merkle proofs and specialized LiteClient contracts.

Key architectural feature

Mathematical proof of correctness for each transaction, which can be independently verified by any network participant. This approach eliminates the need to trust centralized operators or bridge validator groups.

Principal Scheme

Trustless Bridge Principal Scheme

Glossary

Native and Alien Tokens

The system operates with two token types depending on their origin relative to the current network:

TermDescription
Native tokenA token originally created and existing in this network. When transferred to another network, it is locked in the source network's Proxy contract, remaining there as collateral. Ownership rights to these locked tokens are represented by corresponding alien tokens in the destination network.
Alien tokenA wrapped representation of a token from another network. Created (mint) in the destination network during incoming transfer, fully backed by locked native tokens in the source network. During reverse transfer, it is burned, releasing the corresponding native tokens.

Example:

  • USDT on TON network is a Native token for TON
  • When transferring USDT from TON to Tetra L2:
    • On TON: USDT is locked in NativeProxy (remains Native, physically located in the source network)
    • On Tetra L2: USDT is minted (Alien token) — a wrapped version, fully backed by locked tokens in TON

Verification Components

The verification system consists of several interconnected components ensuring cryptographic verification of cross-chain transactions:

TermDescription
LiteClientLight client smart contract deployed in each network, storing current information about key blocks of the other network. Contains the current validator set, their weights (influence in consensus), epoch timestamps. Automatically updated by Sync Service during validator epoch changes. Acts as a trusted source of truth about the other network's state.
TransactionCheckerContract for transaction verification via Merkle proofs. Verifies transaction inclusion in a block through Merkle proof, then validates block signatures via LiteClient. Works as an independent arbiter, mathematically confirming transaction existence.
Proof ChainChain of cryptographic proofs linking transaction to validator consensus: transaction Merkle proof → block Merkle proof → validator signature verification via LiteClient. Each chain link is verified independently, ensuring the entire construction's reliability.
Key BlockTVM network key block containing validator set change information (validator set rotation). Synchronized between networks via Sync Service, ensuring validator information currency in LiteClient contracts.

Configuration and Management

Transfer management is performed through a system of configuration contracts and specialized proxy contracts:

TermDescription
EventConfigurationEvent configuration contract managing Event contract lifecycle. Deploys Event contracts for processing incoming transfers, verifies event parameters (chainId, timestamps, event source). Acts as event factory and validator.
NativeProxyProxy contract for managing native tokens. Performs token locking during outgoing transfers, preserving them as collateral, and unlocking during incoming transfers after verification. Manages fees, daily limits, and liquidity.
AlienProxyProxy contract for managing alien tokens. Performs minting (creation) of new alien tokens during incoming transfers and burning during outgoing transfers. Manages deployment of new alien token types and integration with MergePool for consolidating tokens from different sources.
MergePoolContract for consolidating alien tokens from different networks into a single canonical representation (canon token). Allows using tokens of the same type but from different sources (e.g., USDT from TON and USDT from Ethereum) as interchangeable assets. Handles conversion between different decimals.
Predeployed TokenPre-registered token for native-native scenario when the token is native in both networks. Used for special tokens (e.g., wrapped native tokens like wTON) that must exist as native in multiple networks simultaneously.

Transfer Types

The system supports three main transfer types depending on direction and token nature:

FlowSource Network ActionDestination Network ActionEvent Contract
Lock Native → Mint AlienNativeProxy locks native tokensAlienProxy creates corresponding alien tokensMultiVaultTvmTvmEventAlien
Burn Alien → Unlock NativeAlienProxy burns alien tokensNativeProxy unlocks corresponding native tokensMultiVaultTvmTvmEventNative
Lock Native → Unlock NativeNativeProxy locks native tokensAlienProxy checks predeployed token and transfers control to NativeProxy for unlockingMultiVaultTvmTvmEventAlien (!)

Transfer Structure

Each cross-chain transfer is an atomic operation consisting of two logical parts executed sequentially:

  1. Source network part — user initiates locking (for native) or burning (for alien) tokens, Proxy contract emits an event containing all transfer parameters
  2. Destination network part — system builds cryptographic proof (proof chain), verifies the event through TransactionChecker and LiteClient, then performs minting (for alien) or unlocking (for native) tokens to the recipient

It's important to understand that both parts are cryptographically linked: the second part can only be executed with a mathematically correct proof of the first part.

Event Contract

For each transfer, an individual Event contract is deployed in the destination network — a unique smart contract representing a specific transfer operation. The Event contract performs several critically important functions:

  1. Double-spending protection — the contract address is deterministically computed from the source message's msgHash through a derivation function. This mathematically guarantees that one transaction can only be processed once: a repeat attempt to deploy an Event contract with the same msgHash will fail at the TVM level, as a contract at that address already exists.

  2. Verification process coordination — the Event contract manages interaction with TransactionChecker and LiteClient, tracking verification status and processing results.

  3. Participation in Merge logic — the Event contract interacts with MergePool/MergeRouter to determine which token the user receives: derive-token (source-specific) or canon-token (unified representation) based on MergePool configuration.

  4. Edge case management — handling limit exceeded situations and liquidity shortages, coordinating with liquidity providers.

Verification Mechanism

Trustless verification is based on mathematical proof that a transaction was included in a block and that block was confirmed by source network validators through their cryptographic signatures.

Overview

The verification system consists of three interconnected components:

  1. LiteClient — stores the current validator set of the other network (public keys, weights, epoch parameters), acting as the source of truth about consensus
  2. TransactionChecker — verifies Merkle proofs of transactions and blocks, confirming mathematically correct transaction inclusion in the blockchain
  3. Sync Service — automatically synchronizes key blocks between networks, ensuring validator set currency in LiteClient

LiteClient and Key Blocks

LiteClient is a specialized smart contract deployed in each network, storing information about the current validator set of the other TVM network (from which transfers arrive). It works as a lightweight version of a full node but stores only key block and validator set information, without full transaction history.

Information is automatically updated during validator epoch changes (validator set rotation) through the specialized Sync Service, which monitors key blocks and transmits changes between networks.

Stored data structure:

  • current_seq_no — current key block sequence number (monotonically increasing sequence number)
  • current_epoch_since/until — current validator epoch time boundaries (unix timestamp), defining the validator set validity period
  • current_cutoff_weight — minimum signature threshold for block confirmation: 2/3 + 1 of total validator weight (Byzantine threshold)
  • current_validators_set — validator dictionary: validator_index → (ed25519 public key + consensus weight)

Validator set update process:

Updating the validator set is a critically important operation requiring strict verification:

  1. Key block monitoring: Sync Service continuously monitors the source network blockchain, tracking key block appearances (blocks containing validator set change information)

  2. Proof preparation: When a new key block appears, Sync Service:

    • Downloads the complete block with Merkle proof structure
    • Collects validator signatures for this block
    • Forms a proof chain linking the block to consensus
  3. LiteClient verification: Sync Service calls new_key_block() on the LiteClient contract, passing:

    • Block data
    • Block Merkle proof
    • Validator signature collection
  4. LiteClient checks:

    • Merkle proof correctness: verifying mathematical correctness of block proof structure
    • Block type: verifying the block is a key block (contains ConfigParams with new validator set)
    • Signature validity: verifying block cryptographic signatures via ed25519_check_signature() for each validator from the current set (existing validator set is used to confirm the new one)
    • Threshold achievement: verifying total weight of signing validators ≥ cutoff_weight
  5. State update: On successful verification, LiteClient:

    • Extracts new validator set from key block ConfigParams
    • Updates current_validators_set with new public keys and weights
    • Updates current_epoch_since/until with new epoch time boundaries
    • Computes new cutoff_weight = (total_weight * 2 / 3) + 1
    • Increments current_seq_no

Byzantine Fault Tolerance

The cutoff weight formula (total_weight * 2 / 3) + 1 implements the classic Byzantine threshold, guaranteeing security with up to 1/3 malicious or failed validators.

Mathematically: for a successful attack, an adversary needs to compromise >1/3 of all validators by weight. If ≤1/3 are compromised, they physically cannot accumulate the required cutoff weight to confirm a forged block — they lack sufficient total weight even if all compromised validators act in coordination.

This property ensures cryptographic safety: even if a third of the network is controlled by attackers, the honest majority (≥2/3) maintains control over consensus.

TransactionChecker and Merkle Proofs

TransactionChecker is a specialized verification contract performing cryptographic verification that a specific transaction was included in a specific block, and that block was signed by a sufficient number of validators.

The mechanism works based on Merkle proofs — mathematical data structures enabling efficient proof of element inclusion in a set without transmitting the entire set. In blockchain context: one can prove a transaction is in a block without transmitting all block transactions.

Verification algorithm (step-by-step):

  1. Check initiation: Event contract calls check_transaction() on TransactionChecker, passing:

    • Address of the account that executed the transaction
    • Transaction logical time (lt)
    • Encoded proof (proof data)
  2. Proof structure decoding: TransactionChecker parses proof data, extracting:

    • tx_proof — Merkle proof linking transaction to block (hash chain from transaction to block Merkle tree root)
    • block_proof — block Merkle proof (proof that block belongs to masterchain)
    • signatures — validator signature collection for this block (validator_index → signature)
  3. Transaction-in-block verification: TransactionChecker calls lookup_tx_in_block():

    • Verifies Merkle path from transaction hash to block root
    • Verifies that hash(transaction) ∈ block.transactions_root through hash sequence
    • Extracts block hash (block_hash) for further verification
  4. Signature check delegation: TransactionChecker sends check_block() request to LiteClient with:

    • block_hash — block hash for verification
    • block_proof — block proof
    • signatures — validator signature collection
  5. Signature verification in LiteClient: LiteClient performs critical verification:

    • Iterates through signatures collection
    • For each signature:
      • Extracts validator_index and signature
      • Gets pubkey and weight of validator from current_validators_set[validator_index]
      • Calls ed25519_check_signature(block_hash, signature, pubkey)
      • On success: total_signed_weight += weight
    • Final check: total_signed_weight >= cutoff_weight
  6. Verification confirmation: On successful verification:

    • LiteClient sends callback with verification result
    • TransactionChecker receives confirmation
    • TransactionChecker sends response::transaction_checked to Event contract
    • Event contract updates status and continues transfer processing

Attack Protection

The system implements multi-level protection against various attack vectors:

AttackProtection Mechanism
Double-spendingEvent contract address is deterministically computed from msgHash. Repeat deployment of contract with identical address is physically impossible in TVM.
Replay attackEventConfiguration verifies chainId and destinationChainId — event from one network cannot be applied in another.
Forged proofsMerkle proof is cryptographically verified. Forgery is mathematically impossible without knowledge of validator private keys.
Invalid signaturesLiteClient verifies that total signature weight ≥ 2/3 + 1 of total weight.

System Components

On-chain Components (Smart Contracts)

LiteClient

Light client contract storing information about key blocks of the other network.

Stored data:

  • current_seq_no — current key block sequence number
  • current_epoch_since/until — validator epoch start/end timestamps
  • current_cutoff_weight — minimum signature weight (2/3 + 1 of total validator weight)
  • current_validators_set — validator set with public keys and weights

Operations:

  • new_key_block — processing new key block: signature verification, validator set update
  • check_block — block signature verification against current validator set

TransactionChecker

Contract for verifying transactions from another network via Merkle proofs.

Operations:

  • check_transaction — transaction verification:
    1. Extracts transaction and block Merkle proofs
    2. Verifies that transaction exists in block
    3. Sends request to LiteClient for block signature verification
    4. On successful verification sends response::transaction_checked

ProxyMultiVaultNative (NativeProxy)

Proxy contract for working with native tokens (jettons).

Main functions:

  • Receives native tokens (lock) during outgoing transfers
  • Sends native tokens (unlock) during incoming transfers
  • Emits events for transfers to other networks
  • Manages fees and daily limits

Key methods:

  • transferNotification — callback when receiving jetton tokens, initiates outgoing transfer
  • onTvmEventConfirmedExtended — processing confirmed event, completing incoming transfer

ProxyMultiVaultAlien (AlienProxy)

Proxy contract for working with alien tokens.

Main functions:

  • Mints alien tokens during incoming transfers
  • Processes callback after burning alien tokens and initiates outgoing transfer
  • Manages deployment of new alien tokens
  • Works with MergePool/MergeRouter for token consolidation

Key methods:

  • onTvmEventConfirmedExtended — minting alien tokens during incoming transfer
  • onAcceptTokensBurn — callback from jetton contract after token burning
  • deployTvmAlienToken — deploying new alien token

EventConfiguration (TvmTvmEventConfiguration)

Event configuration contract managing event contract deployment.

Stored data:

  • TransactionChecker address
  • Event contract code
  • Proxy contract address
  • Configuration parameters (chainId, startTimestamp, endTimestamp)

Key methods:

  • deployEvent — deploying new event contract for incoming transfer processing
  • deriveEventAddress — computing deterministic Event contract address from msgHash
  • onTvmEventConfirmedExtended — callback from Event, proxies call to Proxy

MultiVaultTvmTvmEvent (Alien / Native)

Event contracts for processing specific transfers.

Key methods:

  • processProof — transaction proof processing, calling TransactionChecker
  • onTrustlessVerify — callback from TransactionChecker on successful verification
  • _onConfirm — calling proxy to complete transfer

MergePool / MergeRouter

Contracts for consolidating alien tokens from different networks into single representation (canon token). For example, USDT transferred from TON and USDT transferred from Ethereum can be consolidated into one canon USDT.

MergeRouter:

  • Routing to corresponding MergePool for token

MergePool:

  • Stores derive-token to canon-token mapping
  • Conversion between decimals of different representations

Off-chain Components (Backend)

Sync Service

Background service for synchronizing key blocks between networks.

Important

Sync Service runs in background mode and synchronizes key blocks periodically, not for each transfer. This is necessary to maintain current validator set in LiteClient.

Functions:

  • Monitors key blocks in source network
  • Uploads current validator set information to destination network's LiteClient contract
  • Calls new_key_block on LiteClient to update validator set

Update frequency:

  • Key blocks appear during validator epoch changes
  • Usually 1-2 times per day (depends on network)

Proof API

Light node synchronizing blocks and providing API for building proof chain.

Functions:

  • Synchronizes all blocks in real-time
  • Builds Merkle tree proof for transaction
  • Builds block proof
  • Endpoint: GET /v1/proof_chain/{address}/{lt} — returns BOC with proof chain

Bridge API

Event indexer for specific TVM network.

Functions:

  • Event indexing from proxy contracts
  • Transfer status tracking
  • Providing data for event contract deployment

Note: One Bridge API instance serves one TVM network.

Bridge Aggregator API

Aggregator over all APIs for working with transfers.

Functions:

  • Preparing payload for transfers
  • Combining transfer history from all networks
  • Transfer status tracking regardless of networks

Key endpoints:

  • POST /payload/build — build payload for transfer transaction
  • POST /transfers/search — search transfers with filtering
  • POST /transfers/status — get specific transfer status

Event Contract Lifecycle

Event Contract Statuses

CodeStatusDescription
0InitializingInitial state after deployment
1PendingAwaiting verification via TransactionChecker
2ConfirmedTransfer confirmed, proxy called, tokens delivered
3RejectedTransfer rejected (verification failed)
4CancelledTransfer cancelled by user
5LimitReachedDaily limit exceeded, awaiting approval
6LiquidityRequestedLiquidity requested from providers
7LiquidityProvidedLiquidity provided
8VerifiedTransaction verified (trustless), but tokens not yet delivered

State Transition Diagram

Main Flow (Successful Transfer)

  1. Initializing — EventConfiguration deploys contract, calls processProof()
  2. Pending — Event sends verifyTx() to TransactionChecker, awaits response
  3. Verified — TransactionChecker confirmed transaction via onTrustlessVerify(true)
  4. Confirmed — Event called _onConfirm(), Proxy executed mint/unlock

Edge Cases

LimitReached (Daily Limit Exceeded)

When transfer amount exceeds daily limit:

  1. Proxy calls Event.dailyLimitReached(limitApprover)
  2. Status → LimitReached
  3. limitApprover can:
    • approveLimit() → continue transfer → Confirmed
    • rejectLimit() → reject → Rejected
  4. User can: cancel()Cancelled (reverse transfer)

LiquidityRequested (Insufficient Liquidity)

When NativeProxy cannot unlock tokens (insufficient balance):

  1. NativeProxy calls Event.notEnoughLiquidity()
  2. Status → LiquidityRequested
  3. Event requests eventTokenWallet for receiving liquidity
  4. Liquidity provider can send tokens with bounty
  5. After receiving → LiquidityProvided → tokens delivered to user
  6. Or user can: cancel()Cancelled (reverse transfer)

Bounty for Provider

Liquidity provider receives bounty — reward from user for providing liquidity. Bounty amount is set by user or sender during transfer initiation.

Transfer Flows

Lock Native → Mint Alien

This flow describes transferring a native token from source network to destination network where it becomes an alien token.

Step-by-step Description

Step 1-2: Initiation and Locking

User sends jetton tokens to NativeProxy. The payload specifies recipient address and target network. Proxy locks tokens and emits TvmTvmNative event.

Step 3: Event Contract Deployment

Proof API generates transaction Merkle proof. EventConfiguration deploys event contract with proof data in destination network.

Step 4-5: Verification

Event contract calls TransactionChecker for transaction verification. TransactionChecker verifies Merkle proof and requests LiteClient for block signature verification.

Step 6-7: Confirmation

On successful verification, TransactionChecker calls onTrustlessVerify(true) on Event contract. Status changes to Verified. Then Event contract calls _onConfirm(), which:

  • Changes status to Confirmed
  • Sends onTvmEventConfirmedExtended to AlienProxy

Step 8-9: Minting

AlienProxy receives confirmation, checks limits, charges fee and mints alien tokens to recipient.


Burn Alien → Unlock Native

Reverse flow: returning alien token to original network where native tokens are unlocked.

Step-by-step Description

Step 1-2: User burns alien tokens via AlienProxy. Proxy emits TvmTvmAlien event.

Step 3-5: Event contract deployment and verification similar to previous flow.

Step 6-7: Event contract receives confirmation, status changes Verified → Confirmed.

Step 8-9: NativeProxy receives confirmation and unlocks native tokens to recipient.

Edge Case: Insufficient Liquidity

If NativeProxy wallet balance is insufficient for unlock:

  1. NativeProxy calls Event.notEnoughLiquidity()
  2. Event → status LiquidityRequested
  3. Liquidity provider can send tokens with bounty
  4. Or user can cancel transfer via cancel()

Lock Native → Unlock Native

Flow for transferring native token between networks where the token is native in both networks.

Implementation Detail

The transfer is technically executed via the Alien flow, but AlienProxy performs a check: if native-native scenario is configured for the token (entry exists in predeployedTokens), AlienProxy doesn't mint the token but proxies the call to NativeProxy.

Step-by-step Description

Step 1-2: User sends native tokens to NativeProxy in source network. Tokens are locked, TvmTvmNative event is emitted.

Step 3-5: In destination network, MultiVaultTvmTvmEventAlien (not Native!) is deployed via AlienEventConfiguration. Verification happens via TransactionChecker.

Step 6-7: Event contract calls receivePredeployedToken on AlienProxy. If token is registered in predeployedTokens, PredeployedTokenData with nativeProxyTokenWallet address is returned.

Step 8-9: After verification, Event contract calls onTvmEventConfirmedExtended on AlienProxy, passing nativeProxyTokenWallet in metadata.

Step 10: AlienProxy checks nativeProxyTokenWallet.hasValue(). If true — doesn't mint, but proxies call to NativeProxy via proxyMultiVaultNative.onTvmEventConfirmedExtended().

Step 11: NativeProxy receives call (accepts from AlienProxy or directly from EventConfig), unlocks native tokens and sends to recipient.

When is this flow used?

Lock Native → Unlock Native is used when token is configured as native in both networks via predeployedTokens in AlienProxy. This is possible for:

  • Wrapped native tokens (e.g., wTON)
  • Tokens specifically configured by administrator

Configuration happens via registering PredeployedTokenData in tvmConfiguration.predeployedTokens mapping.

Risks and Protection Mechanisms

RiskCauseProtection Mechanism
Double-spendingReprocessing same transactionEvent contract address is deterministically computed from msgHash. Repeat deployment impossible.
Forged Merkle proofFaking transaction proofMerkle proof is cryptographically verified via extract_merkle_proof() and lookup_tx_in_block()
Invalid block signaturesInsufficient signatures or invalid signaturesLiteClient verifies: total signer weight ≥ cutoff_weight (2/3 + 1)
Outdated validator setLiteClient stores outdated validator setSync Service periodically updates validator set via new_key_block()
Replay attackEvent from one network processed in anotherEventConfiguration verifies chainId in proof == networkConfiguration.chainId
Wrong destination chainEvent intended for different networkEventConfiguration verifies destinationChainId
Unauthorized event emitterEvent emitted not by Proxy contractEventConfiguration verifies accountAddr == eventEmitter
Daily limit exceededTransfer amount exceeds daily limitProxy checks limits; on exceed → LimitReached status, awaits approval
Not enough liquidityNativeProxy cannot unlock tokensNativeProxy calls notEnoughLiquidity(); provider can supply liquidity with bounty
Wrong predeployed tokenInvalid externalNativeProxyWalletEvent verifies match with data from predeployedTokens

What to do with transfer problems?

  1. LimitReached — wait for approval from limitApprover or cancel transfer
  2. LiquidityRequested — wait for liquidity provider or cancel transfer (receive reverse transfer to source network)
  3. Cancelled — tokens will be returned to source network via reverse transfer

Smart Contract Errors

Event Contracts

Event contract errors (MultiVaultTvmTvmEventAlien, MultiVaultTvmTvmEventNative):

CodeCondition
2321Invalid status for processProof(): status != Initializing && status != Pending && status != Verified
2313Call not from EventConfiguration: msg.sender != eventInitData.configuration
2329Callback not from TransactionChecker: msg.sender != transactionChecker
2312Invalid status for onTrustlessVerify(): status != Pending
2901Callback not from Proxy: msg.sender != transitionalData.proxy
2330Invalid externalNativeProxyWallet for predeployed token
2326Callback not from TokenRoot: msg.sender != transitionalData.token
2327Callback not from MergeRouter: msg.sender != transitionalData.router
2328Callback not from MergePool: msg.sender != transitionalData.pool
2335Call not from limitApprover: msg.sender != limitApprover
2332Call not from recipient/sender (for cancel/setBounty)
2324Invalid status for operation (approveLimit, rejectLimit, cancel, setBounty, retry)
2333Bounty exceeds amount: bounty > eventData.amount
2334Callback not from eventTokenWallet: msg.sender != eventTokenWallet
2331Callback not from Proxy (Native Event) or not from proxy/native_proxy (Alien Event)

EventConfiguration

TvmTvmEventConfiguration contract errors:

CodeCondition
2218endTimestamp already set: networkConfiguration.endTimestamp != 0
2216endTimestamp less than startTimestamp
2221Proof from different network: chainId != networkConfiguration.chainId
2222Invalid msgHash: tvm.hash(message) != _eventVoteData.msgHash
2223Event not for target network: destinationChainId != TON_GLOBAL_ID
2220Event not from Proxy: accountAddr != eventEmitter.value
2211Event before startTimestamp: txTimestamp < networkConfiguration.startTimestamp
2215Event after endTimestamp (if endTimestamp != 0)
2212Callback not from Event contract: deriveEventAddress(_eventInitData) != msg.sender

Proxy Contracts

ProxyMultiVaultAlien and ProxyMultiVaultNative contract errors:

CodeCondition
2710Callback not from allowed EventConfiguration: configuration not in tvmConfiguration.incomingConfigurations

TransactionChecker

TransactionChecker contract errors:

CodeCondition
200Transaction not found in block
201Invalid transaction hash
202Block is not masterchain block
203Callback not from LiteClient: sender_address != ctx_lite_client_addr

LiteClient

LiteClient contract errors:

CodeCondition
111Block is not key block
112Key block from same epoch
113Key block from old epoch
114Invalid validator signature
115Insufficient signature weight: total_signed_weight < ctx_current_cutoff_weight
116Invalid validator epoch parameters

Common Merkle Proof Errors

CodeCondition
101Cell is not exotic cell
102Cell is not Merkle proof
103Cell is not Merkle update

ChainConnect Bridge Documentation