CertiK reviewed TON (TON Stablecoin) on 7 Dec 2022: 9 findings, 0 critical; 8 marked fixed, 1 open.
Report hosted on skynet.certik.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 | 1 | 0 | 0 |
| Medium | 0 | 0 | 3 |
| Low | 0 | 0 | 5 |
`jetton-minter` during processing of `op::mint` message uses `calculate_user_jetton_wallet_address()` to prepare the `jetton-wallet` address. However, even if `to_address` is not in basechain, the generated wallet address is in `workchain = 0`. As a result, the minted jettons will not be accessible.
When `op::internal_transfer` message is bounced from one `jetton-wallet` to another one, the `balance` is updated (increased back). However, if `op::internal_transfer` message is bounced from `jetton-wallet` to `jetton-minter`, the `total_supply` is not updated. `master_msg` provided to `op::mint` is not validated and passed as is to `jetton-wallet`. Underflow can happen. As a result the total circulating supply will be less than `total_supply`.
During processing of `op::upgrade` the inconsistent `new_data` can be assigned. That will invalidate all the deployed wallets or `total_supply` value. `new_code` is not validated for correctness. Inconsistent storage can be also set during `jetton-minter` deployment. During processing of `op::call_to` and `op::mint` the `master_msg` and `amount` are not validated. During processing of `op::change_content` it is not validated that updated `content` follows [TIP-64 (Token Data Standard)](https://github.com/ton-blockchain/TIPs/issues/64). Incorrect data can be assigned. `symbol`, `name`, `decimals` can be changed. Well known Jetton can be renamed to something else or to another well known Jetton. This can influence the Jetton price. During processing of `op::change_admin` the `new_admin_address` is not validated. Assigning the wrong admin can prevent further minting.
The function `load_data()` reads all the fields of Storage, however, it does not ensure that the slice is empty after that.
According to the code zero `status` is considered "unlocked". Any other `status` is considered "locked". Locked status forbids performing `send_tokens()` but allows to `burn_tokens()` and `receive_tokens()`.
In `calculate_jetton_wallet_state_init()` `StateInit` structure is used: ```c _ split_depth:(Maybe (## 5)) special:(Maybe TickTock) code:(Maybe ^Cell) data:(Maybe ^Cell) library:(HashmapE 256 SimpleLib) = StateInit; ``` In `calculate_jetton_wallet_address()` `MsgAddressInt` structure is used: ```c addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 address:bits256 = MsgAddressInt; ``` `send_raw_message()` arguments preparation is not documented. `send_raw_message()` `mode` argument is not documented in comments. No corresponding constants introduced.
`Storage` comment in `jetton-wallet` doesn't mention `status` field. `transfer` and `internal_transfer` comments don't contain the structure tags. Other structures: `transfer_notification`, `excesses`, `burn`, `burn_notification`, - should be described. `founds` is supposed to be `funds`. `steel` is supposed to be `still`. `uin4` is supposed to be `uint4`. `is only decreases` is supposed to be `only decreases`.
According to [TIP-74 standard (TON fungible tokens)](https://github.com/ton-blockchain/TIPs/issues/74) "After processing the request, the receiver's jetton-wallet **must** send at least `in_msg_value - forward_amount - 2 * max_tx_gas_price` to the `response_destination` address. If the sender jetton-wallet cannot guarantee this, it must immediately stop executing the request and throw error." So, the `op::transfer` is allowed to do two processings only. However, these values can be deducted: 1. Up to `min_tons_for_storage` can be left on the `receiver_wallet` 2. Up to 2 forward fees can be spent: one for `op::internal_transfer` from `sender_wallet` to `receiver_wallet`, one for `op::transfer_notification` from `receiver_wallet` to `receiver_owner` 3. 2 processing fees can be spent: one for `op::transfer` processing, one for `op::internal_transfer` processing In fact `op::excesses` message will get only `op::transfer::in_msg_value - forward_ton_amount - min_tons_for_storage - 2 * fwd_fee - 2 * max_tx_gas_price`. Also, `gas_consumption` constant is hardcoded, not obtained from `ConfigParam21 gas_limit`. This makes the calculations inaccurate for different workchains.
The report names a repository or files. Open the report to see its scope.
1,045 projects reviewed · 1,376 reports · latest May 2025
See CertiK’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