As of 10 Nov 2026, TON has 6 audit reports by 2 security firms on Trustblock; the latest is by Trail of Bits, dated 25 Mar 2024. Those reports list no open critical findings.
Record last updated . It holds audits and findings, contracts and incidents up to Nov 2026. Trustblock does not review code.
Newest first
1 other security report listed, not counted
| Report | Security firm | Date | Scope | Critical findings | Source |
|---|---|---|---|---|---|
| TON Foundation Multisignature Wallet | Security firm: Trail of Bits | Date: | Scope: —Not recorded; see the report | None open | GitHub ↗ |
| TON-EVM Bridge | Security firm: CertiK | Date: | Scope: Files listed | None open | skynet.certik.com ↗ |
| TON Stablecoin | Security firm: CertiK | Date: | Scope: —Not recorded; see the report | None open | skynet.certik.com ↗ |
| Ton Bridge Web Application Penetration test of off-chain systemsNot counted as an audit report | Security firm: CertiK | Date: | Scope: —Not recorded; see the report | None open | skynet.certik.com ↗ |
| TON Masterchain Contracts Formal VerificationFormal verification | Security firm: CertiK | Date: | Scope: —Not recorded; see the report | None open | skynet.certik.com ↗ |
| TON Vesting | Security firm: CertiK | Date: | Scope: —Not recorded; see the report | None open | skynet.certik.com ↗ |
| The Open Network - Audit 1 (Phase 2) | Security firm: CertiK | Date: | Scope: —Not recorded; see the report | None open | skynet.certik.com ↗ |
Statuses are the security firm’s own, from each report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 3 | 1 | 2 |
| Medium | 2 | 0 | 11 |
| Low | 17 | 0 | 37 |
The report gives no description here.
Open the reportTON contract `bridge_code` can `execute_voting()` received from the Bridge Governance (`multisig-code` owned by a set of oracles). According to the [ETH-TON bridge description](https://github.com/ton-blockchain/TIPs/issues/24#issuecomment-899768466), each oracle puts a significant amount of TON coins (stake) on the governing smart contract (`multisig-code`). The initial stake is 100k TON coins per oracle. Solidity contract `Bridge` can mint TONCOINs via `voteForMinting()`. In case of oracles collusion: 1. They can mint unlimited amount of ERC20 TONCOINs and sell them on DEX/CEX. The profit depends on the available liquidity and order book. 2. They can extract all the locked TONs from `bridge_code` via common "swap" operation. Reasonable if locked amount is bigger than the total stake. At the moment of writing total stake is 900k TONs, `total_locked` is 11M TONs. At the same time 1. The Bridge Governance can't be reassigned to another contract by Validators because that will automatically "unlock" the stakes. 2. Oracles set on ETH side can't be updated because it requires the signatures of old Bridge Governance participants. 3. If stakes unlocked, the oracles majority can steal minority's stakes. So, "out-of-control" Bridge can't be shut down. New Bridge has to be deployed and will work in parallel. The Bridge assumes that the majority of oracles follow the rules, despite the fact they are incentivized to do not. **Each oracle has to be trusted**, otherwise the malicious actor can stake from many accounts to get the majority.
Open the reportThe owner of `admin_address` is allowed to: * `op::mint` any amount of jettons to any wallet * `op::change_admin` to any other one * lock any wallet (prevent funds transferring) * transfer funds from any wallet to any other one or burn them * invalidate all the wallets, invalidate `total_supply` and other data * rename Jetton, change description or `decimals`
Open the reportThe message signed by oracles in one chain (Ethereum mainnet, BSC mainnet, Ethereum testnet, etc.) can be replayed by the attacker in another one, if the Bridge address is the same and oracle keys are the same. The replay attack can also be executed in case of chain fork (like Ethereum/Ethereum Classic).
Open the report```solidity=19 require(signatures.length >= 2 * oraclesSet.length / 3, "Not enough signatures"); ``` The check is expected to pass if at least 2/3 or signatures were obtained. However, due to rounding down it can pass with less. For example, with 4 oracles only 2 signatures required.
Open the report`pending_queries` element has the one bit flag at the beginning. If it is set to 0, the query was already "executed". Otherwise the flag is 1 and other fields are packed in the structure. In `update_pending_queries()` this flag is saved with `store_uint()` and `store_int()`. In `message_signed_by_id?()`, `messages_by_mask()` and `unpack_query_data()` it is loaded by `load_int()`.
Open the report`end_parse()` checks if slice is empty, otherwise throws an exception. It allows to ensure the slice has the expected data structure.
Open the reportAccording to the name the function `get_messages_unsigned_by_id()` should return the messages that are yet unsigned by the owner with `id`. However, the function returns the messages that were signed by that owner. `get_messages_unsigned()` returns all the messages not yet "executed".
Open the report`load_text_hex_number()` doesn't perform some safety checks: 1. It is not checked that `comment.slice_bits()` is divisible by 8 (extra bits are ignored). 2. It is not checked that the result fits into `int` (overflow exception is thrown). 3. It is not checked that `comment` has enough data (underflow exception is thrown).
Open the reportThe expression `if (!condition) revert(message)` can be rewritten in more common form `require(condition, message)`.
Open the reportSome function names do not conform to the [Solidity style guide](https://solidity.readthedocs.io/en/v0.7.3/style-guide.html#naming-conventions) with regards to its naming convention.
Open the report`config_param(71)` stands for Ethereum mainnet bridge. To deploy the bridge for another chain one has to edit the code. This can lead to a confusion. Also, `multisig-code` and `bridge_code` use independent code to access the `config_param` which increases the code complexity.
Open the report`public` functions which are never called internally within the contract should have `external` visibility for gas optimization. Unchanged `memory` arguments of `external` functions should be declared `calldata`.
Open the reportThe function `get_bridge_config()` is defined as `impure`, but it does not modify the storage or throw an exception.
Open the report`allowBurn` field is changed by `voteForSwitchBurn()` but no event is emitted.
Open the report```cpp=15 throw_if(301, wc + 1); ``` The code can be rewritten in a more explicit way: ```cpp=15 throw_unless(301, wc == - 1); ``` ```cpp=67 int should_check = cnt_bits != old_cnt_bits; cnt -= should_check; ``` The code can be rewritten in a more explicit way: ```cpp=67 if (cnt_bits != old_cnt_bits) { cnt += 1; } ``` ```cpp=167 last_cleaned -= last_cleaned == 0; ``` The code can be rewritten in a more explicit way: ```cpp=167 if (last_cleaned == 0) { last_cleaned = 1; } ```
Open the reportThere are many magic numbers used directly in code.
Open the reportThe voting configuration includes the parameters `min_wins` and `max_losses`, which are used to initialize new proposals. As we prove in this report, a proposal needs to win at least `min_wins` times in order to pass. If it loses more than `max_losses` times, it may be discarded. However, the latter is not a hard guarantee. The check for losses happens either when somebody votes for the proposal, or if the proposal is selected by the `scan_random_proposal()` function. If nobody voted on it for a long period of time, where the validator set changed multiple times and the proposal did not happen to be scanned, then the fact that it did not win in that period would not get recorded, so the loss count would be smaller and it could still pass.
Open the report`seqno` and `subwallet_id` are saved into storage via `store_int()` but loaded via `load_uint()`. This can lead to exceptions in case of negative/too big values.
Open the reportLoading data from the storage is done by the identical code several times. `locked_amount` calculation is done by the identical code two times.
Open the reportFunction declarations contain `_` instead of real return type. Argument types are omitted.
Open the report`allow_elector` and `can_use_restricted` are 1-bit uint. However, they are used as part of logical expression. This can lead to accidental errors, like: ```c if (3 & 4) { ;; never happens } ```
Open the reportThe reports name a repository or files. Open a report to see its scope.
Is this your project? Show this record on your own website. The label states what the record holds, links to this page and updates by itself. It is free and needs no sign-in.
Automatic: the three strongest facts of this project’s own record (its last audit and two more), highlighted. They update with the record. Picking a fact turns Automatic off.
<head><script src="https://widget.trustblock.run/project-widget.min.js" defer></script></head><body><divdata-tb-project-widgetdata-tb-project-slug="the-open-network"></div></body>
Paste the code where the label should appear. It uses no API credit and updates by itself.
Something wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.
Tell us