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

Glossary
Native and Alien Tokens
The system operates with two token types depending on their origin relative to the current network:
| Term | Description |
|---|---|
| Native token | A 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 token | A 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:
| Term | Description |
|---|---|
| LiteClient | Light 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. |
| TransactionChecker | Contract 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 Chain | Chain 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 Block | TVM 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:
| Term | Description |
|---|---|
| EventConfiguration | Event 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. |
| NativeProxy | Proxy 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. |
| AlienProxy | Proxy 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. |
| MergePool | Contract 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 Token | Pre-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:
| Flow | Source Network Action | Destination Network Action | Event Contract |
|---|---|---|---|
| Lock Native → Mint Alien | NativeProxy locks native tokens | AlienProxy creates corresponding alien tokens | MultiVaultTvmTvmEventAlien |
| Burn Alien → Unlock Native | AlienProxy burns alien tokens | NativeProxy unlocks corresponding native tokens | MultiVaultTvmTvmEventNative |
| Lock Native → Unlock Native | NativeProxy locks native tokens | AlienProxy checks predeployed token and transfers control to NativeProxy for unlocking | MultiVaultTvmTvmEventAlien (!) |
Transfer Structure
Each cross-chain transfer is an atomic operation consisting of two logical parts executed sequentially:
- Source network part — user initiates locking (for native) or burning (for alien) tokens, Proxy contract emits an event containing all transfer parameters
- 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:
Double-spending protection — the contract address is deterministically computed from the source message's
msgHashthrough 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.Verification process coordination — the Event contract manages interaction with TransactionChecker and LiteClient, tracking verification status and processing results.
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.
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:
- LiteClient — stores the current validator set of the other network (public keys, weights, epoch parameters), acting as the source of truth about consensus
- TransactionChecker — verifies Merkle proofs of transactions and blocks, confirming mathematically correct transaction inclusion in the blockchain
- 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 periodcurrent_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:
Key block monitoring: Sync Service continuously monitors the source network blockchain, tracking key block appearances (blocks containing validator set change information)
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
LiteClient verification: Sync Service calls
new_key_block()on the LiteClient contract, passing:- Block data
- Block Merkle proof
- Validator signature collection
LiteClient checks:
- Merkle proof correctness: verifying mathematical correctness of block proof structure
- Block type: verifying the block is a key block (contains
ConfigParamswith 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
State update: On successful verification, LiteClient:
- Extracts new validator set from key block
ConfigParams - Updates
current_validators_setwith new public keys and weights - Updates
current_epoch_since/untilwith new epoch time boundaries - Computes new
cutoff_weight=(total_weight * 2 / 3) + 1 - Increments
current_seq_no
- Extracts new validator set from key block
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):
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)
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)
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_rootthrough hash sequence - Extracts block hash (
block_hash) for further verification
Signature check delegation: TransactionChecker sends
check_block()request to LiteClient with:block_hash— block hash for verificationblock_proof— block proofsignatures— validator signature collection
Signature verification in LiteClient: LiteClient performs critical verification:
- Iterates through
signaturescollection - For each signature:
- Extracts
validator_indexandsignature - Gets
pubkeyandweightof validator fromcurrent_validators_set[validator_index] - Calls
ed25519_check_signature(block_hash, signature, pubkey) - On success:
total_signed_weight += weight
- Extracts
- Final check:
total_signed_weight >= cutoff_weight
- Iterates through
Verification confirmation: On successful verification:
- LiteClient sends callback with verification result
- TransactionChecker receives confirmation
- TransactionChecker sends
response::transaction_checkedto Event contract - Event contract updates status and continues transfer processing
Attack Protection
The system implements multi-level protection against various attack vectors:
| Attack | Protection Mechanism |
|---|---|
| Double-spending | Event contract address is deterministically computed from msgHash. Repeat deployment of contract with identical address is physically impossible in TVM. |
| Replay attack | EventConfiguration verifies chainId and destinationChainId — event from one network cannot be applied in another. |
| Forged proofs | Merkle proof is cryptographically verified. Forgery is mathematically impossible without knowledge of validator private keys. |
| Invalid signatures | LiteClient 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 numbercurrent_epoch_since/until— validator epoch start/end timestampscurrent_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 updatecheck_block— block signature verification against current validator set
TransactionChecker
Contract for verifying transactions from another network via Merkle proofs.
Operations:
check_transaction— transaction verification:- Extracts transaction and block Merkle proofs
- Verifies that transaction exists in block
- Sends request to LiteClient for block signature verification
- 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 transferonTvmEventConfirmedExtended— 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 transferonAcceptTokensBurn— callback from jetton contract after token burningdeployTvmAlienToken— 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 processingderiveEventAddress— computing deterministic Event contract address frommsgHashonTvmEventConfirmedExtended— callback from Event, proxies call to Proxy
MultiVaultTvmTvmEvent (Alien / Native)
Event contracts for processing specific transfers.
Key methods:
processProof— transaction proof processing, calling TransactionCheckeronTrustlessVerify— 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_blockon 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 transactionPOST /transfers/search— search transfers with filteringPOST /transfers/status— get specific transfer status
Event Contract Lifecycle
Event Contract Statuses
| Code | Status | Description |
|---|---|---|
| 0 | Initializing | Initial state after deployment |
| 1 | Pending | Awaiting verification via TransactionChecker |
| 2 | Confirmed | Transfer confirmed, proxy called, tokens delivered |
| 3 | Rejected | Transfer rejected (verification failed) |
| 4 | Cancelled | Transfer cancelled by user |
| 5 | LimitReached | Daily limit exceeded, awaiting approval |
| 6 | LiquidityRequested | Liquidity requested from providers |
| 7 | LiquidityProvided | Liquidity provided |
| 8 | Verified | Transaction verified (trustless), but tokens not yet delivered |
State Transition Diagram
Main Flow (Successful Transfer)
- Initializing — EventConfiguration deploys contract, calls
processProof() - Pending — Event sends
verifyTx()to TransactionChecker, awaits response - Verified — TransactionChecker confirmed transaction via
onTrustlessVerify(true) - Confirmed — Event called
_onConfirm(), Proxy executed mint/unlock
Edge Cases
LimitReached (Daily Limit Exceeded)
When transfer amount exceeds daily limit:
- Proxy calls
Event.dailyLimitReached(limitApprover) - Status → LimitReached
limitApprovercan:approveLimit()→ continue transfer → ConfirmedrejectLimit()→ reject → Rejected
- User can:
cancel()→ Cancelled (reverse transfer)
LiquidityRequested (Insufficient Liquidity)
When NativeProxy cannot unlock tokens (insufficient balance):
- NativeProxy calls
Event.notEnoughLiquidity() - Status → LiquidityRequested
- Event requests
eventTokenWalletfor receiving liquidity - Liquidity provider can send tokens with bounty
- After receiving → LiquidityProvided → tokens delivered to user
- 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
onTvmEventConfirmedExtendedto 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:
- NativeProxy calls
Event.notEnoughLiquidity() - Event → status LiquidityRequested
- Liquidity provider can send tokens with bounty
- 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
| Risk | Cause | Protection Mechanism |
|---|---|---|
| Double-spending | Reprocessing same transaction | Event contract address is deterministically computed from msgHash. Repeat deployment impossible. |
| Forged Merkle proof | Faking transaction proof | Merkle proof is cryptographically verified via extract_merkle_proof() and lookup_tx_in_block() |
| Invalid block signatures | Insufficient signatures or invalid signatures | LiteClient verifies: total signer weight ≥ cutoff_weight (2/3 + 1) |
| Outdated validator set | LiteClient stores outdated validator set | Sync Service periodically updates validator set via new_key_block() |
| Replay attack | Event from one network processed in another | EventConfiguration verifies chainId in proof == networkConfiguration.chainId |
| Wrong destination chain | Event intended for different network | EventConfiguration verifies destinationChainId |
| Unauthorized event emitter | Event emitted not by Proxy contract | EventConfiguration verifies accountAddr == eventEmitter |
| Daily limit exceeded | Transfer amount exceeds daily limit | Proxy checks limits; on exceed → LimitReached status, awaits approval |
| Not enough liquidity | NativeProxy cannot unlock tokens | NativeProxy calls notEnoughLiquidity(); provider can supply liquidity with bounty |
| Wrong predeployed token | Invalid externalNativeProxyWallet | Event verifies match with data from predeployedTokens |
What to do with transfer problems?
- LimitReached — wait for approval from
limitApproveror cancel transfer - LiquidityRequested — wait for liquidity provider or cancel transfer (receive reverse transfer to source network)
- Cancelled — tokens will be returned to source network via reverse transfer
Smart Contract Errors
Event Contracts
Event contract errors (MultiVaultTvmTvmEventAlien, MultiVaultTvmTvmEventNative):
| Code | Condition |
|---|---|
| 2321 | Invalid status for processProof(): status != Initializing && status != Pending && status != Verified |
| 2313 | Call not from EventConfiguration: msg.sender != eventInitData.configuration |
| 2329 | Callback not from TransactionChecker: msg.sender != transactionChecker |
| 2312 | Invalid status for onTrustlessVerify(): status != Pending |
| 2901 | Callback not from Proxy: msg.sender != transitionalData.proxy |
| 2330 | Invalid externalNativeProxyWallet for predeployed token |
| 2326 | Callback not from TokenRoot: msg.sender != transitionalData.token |
| 2327 | Callback not from MergeRouter: msg.sender != transitionalData.router |
| 2328 | Callback not from MergePool: msg.sender != transitionalData.pool |
| 2335 | Call not from limitApprover: msg.sender != limitApprover |
| 2332 | Call not from recipient/sender (for cancel/setBounty) |
| 2324 | Invalid status for operation (approveLimit, rejectLimit, cancel, setBounty, retry) |
| 2333 | Bounty exceeds amount: bounty > eventData.amount |
| 2334 | Callback not from eventTokenWallet: msg.sender != eventTokenWallet |
| 2331 | Callback not from Proxy (Native Event) or not from proxy/native_proxy (Alien Event) |
EventConfiguration
TvmTvmEventConfiguration contract errors:
| Code | Condition |
|---|---|
| 2218 | endTimestamp already set: networkConfiguration.endTimestamp != 0 |
| 2216 | endTimestamp less than startTimestamp |
| 2221 | Proof from different network: chainId != networkConfiguration.chainId |
| 2222 | Invalid msgHash: tvm.hash(message) != _eventVoteData.msgHash |
| 2223 | Event not for target network: destinationChainId != TON_GLOBAL_ID |
| 2220 | Event not from Proxy: accountAddr != eventEmitter.value |
| 2211 | Event before startTimestamp: txTimestamp < networkConfiguration.startTimestamp |
| 2215 | Event after endTimestamp (if endTimestamp != 0) |
| 2212 | Callback not from Event contract: deriveEventAddress(_eventInitData) != msg.sender |
Proxy Contracts
ProxyMultiVaultAlien and ProxyMultiVaultNative contract errors:
| Code | Condition |
|---|---|
| 2710 | Callback not from allowed EventConfiguration: configuration not in tvmConfiguration.incomingConfigurations |
TransactionChecker
TransactionChecker contract errors:
| Code | Condition |
|---|---|
| 200 | Transaction not found in block |
| 201 | Invalid transaction hash |
| 202 | Block is not masterchain block |
| 203 | Callback not from LiteClient: sender_address != ctx_lite_client_addr |
LiteClient
LiteClient contract errors:
| Code | Condition |
|---|---|
| 111 | Block is not key block |
| 112 | Key block from same epoch |
| 113 | Key block from old epoch |
| 114 | Invalid validator signature |
| 115 | Insufficient signature weight: total_signed_weight < ctx_current_cutoff_weight |
| 116 | Invalid validator epoch parameters |
Common Merkle Proof Errors
| Code | Condition |
|---|---|
| 101 | Cell is not exotic cell |
| 102 | Cell is not Merkle proof |
| 103 | Cell is not Merkle update |