Quantstamp reviewed Chromia (Chromia - Postchain) on 14 Oct 2024: 16 findings, 0 critical; 11 marked fixed, 4 acknowledged, 1 open.
Report hosted on certificate.quantstamp.com.
Trustblock does not review code.
Statuses are the security firm’s own, from its report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 0 | 0 | 1 |
| Medium | 0 | 1 | 4 |
| Low | 1 | 3 | 6 |
In `withdrawBySnapshot()`, the `ERC20StateHeader` is validated to confirm that the withdrawal snapshot is intended for the bridge contract. A snapshot can either be authorized for any bridge on a given network, or a specific bridge on the network. The relevant code is reproduced below: ```js // assume networkId must fit in 96 bits uint256 allowedDiscriminator1 = networkId << 160; // discriminator allows any bridge contract on the network uint256 allowedDiscriminator2 = allowedDiscriminator1 + uint160(address(this)); require((header.discriminator == allowedDiscriminator1) || (header.discriminator == allowedDiscriminator2), "TokenBridge: invalid bridge contract"); ``` <br> `hbridge/snapshot.rell` indicates that the open discriminator is used as a default: ```js function network_id_to_discriminator(network_id: integer): big_integer { return big_integer(network_id) * big_integer.from_hex("1" + "00".repeat(20)); } function maybe_create_local_erc20_state_slot(account: ft4.accounts.account, erc20_asset): state_slot? { .... if (not exists(existing_slot)) { existing_slot = create state_slot ( id = get_next_state_slot_id(), network_id, recipient_address = account_address, type = state_slot_type.erc20, discriminator = network_id_to_discriminator(network_id), // Open discriminator local = true ); } .... } ``` <br> If a mass exit is triggered across multiple bridges on the same network, then `header.discriminator == allowedDiscriminator1` would allow users to call `withdrawBySnapshot()` across different bridges.
In `TokenBridgeWithSnapshotWithdraw.triggerMassExit()`, the owner assigns the `massExitBlock` as the last valid block from which users can call `withdrawBySnapshot()`. The block is required to have occurred within the last three days: ```js require(header.timestamp >= (block.timestamp - 3 days) * 1000, "TokenBridge: mass exit block is too old"); ``` <br> Given that the requirement is hardcoded to 3 days, this duration may be longer than the dispute period if `withdrawOffset` is shorter than 3 days. This would allow users who executed their withdrawals to withdraw additional funds via the older snapshot.
The entity `evm_block` has three keys: 1. `network_id` 1. `evm_block_height` 1. `evm_block_hash` <br> This would allow a given `<network_id, evm_block_height>` pair to be associated with multiple `evm_block_hashes`, which could cause inconsistencies when processing blocks.
The `TokenBridge` contract has had its storage layout refactored since the previous audit, with some variables removed/added across the `ChromiaTokenBridge` and `TokenBridgeWithSnapshotWithdraw` contracts. This makes the new `TokenBridge` incompatible for upgrading previous versions. After speaking with the Chromia team, none of the deployed instances of the `TokenBridge` contract are scheduled for upgrades, and thus the severity of the issue has been marked as Informational.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (2)
236 projects reviewed · 332 reports · latest Oct 2026
See Quantstamp’s auditsSomething 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