As of 3 Nov 2026, Kroma has 3 audit reports by 1 security firm on Trustblock; the latest is by CertiK, dated 3 Dec 2024. Those reports list no open critical findings. It also holds 1 deployed contract on 1 chain.
Record last updated . It holds audits and findings, contracts and incidents up to Nov 2026. Trustblock does not review code.
Newest first
| Audit | Security firm | Date | Scope | Critical findings | Report |
|---|---|---|---|---|---|
| Kroma Network - Token Audit | Security firm: CertiK | Date: | Scope: 1 address on Ethereum | No open critical findings | Open · skynet.certik.com |
| Spectrum Protocol - Audit(Addendum) | Security firm: CertiK | Date: | Scope: Files named in the report | No open critical findings | Open · skynet.certik.com |
| Spectrum Protocol - Audit | Security firm: CertiK | Date: | Scope: Files named in the report | No open critical findings | Open · skynet.certik.com |
Statuses are the security firm’s own, from each report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 0 | 6 | 0 |
| Medium | 1 | 3 | 4 |
| Low | 14 | 0 | 10 |
In the `stake()` and `unstake()` functions, there is no check to ensure that the asset is a supported asset. As a result, a user can stake any asset for the first time (`spTotalSupply == 0`) and receive the `spETH` token. However, if another user attempts to stake with the correct asset, the `stake()` function will revert due to a division by zero error because the contract's asset balance is zero. ```solidity =166 function _getSpectrumFromAsset( SnapGlobal memory gSnap, address asset, uint256 assetAmount ) internal view returns (uint256 spAmount) { CoreSupportAssetInfo memory assetInfo = _getSpectrumCoreStorage().assetInfos[asset]; uint256 spTotalSupply = gSnap.info.spETH.totalSupply(); if (spTotalSupply == 0) return assetAmount; spAmount = (spTotalSupply * _switchAssetEval(assetInfo.id)(assetInfo.assetEvaluator, assetAmount)) / gSnap.spSnap.evalValue; } ```
Open the reportIn the function `setIndexEvalRate()`, if the `evalRate` is not zero, the function will revert. This means the function only sets the `evalRate` to 0. However, according to the function `setIndexToken()`, if the `evalRate` is set to 0, the `idxAddrs` should be removed from `idxAssetList` and `feeConfigs` should be deleted. ```solidity=158 function setIndexEvalRate(address[] memory idxAddrs, Ubp[] memory evalRates) public onlyAdmin { SpectrumCoreStorage storage $ = _getSpectrumCoreStorage(); if (idxAddrs.length != evalRates.length) revert Spectrum(SpectrumErrors.Length); for (uint256 i; i < idxAddrs.length; i++) { if (!evalRates[i].isZero()) revert Spectrum(SpectrumErrors.RateZero); $.idxAssetConfigs[idxAddrs[i]].evalRate = evalRates[i]; } } ```
Open the reportIn the function `_stakeReceiveFrom()`, if the `requestedAsset` is `address(0)`, `idxAsset` is assigned the value of `$.underlyingToIdx[$.info.fundUnderlying]`, the function `wrap()` transfers `fundUnderlying` to the `idxAsset` contract while minting `idxAsset` tokens to the contract. Therefore, the contract should increase the allowance of `fundUnderlying` token instead of `idxAsset` token for the `idxAsset` contract. Additionally, the user does not transfer any tokens to the contract. ```solidity=153 if (requestedAsset == address(0)) { idxAsset = $.underlyingToIdx[$.info.fundUnderlying]; IEcoERC20(idxAsset).safeIncreaseAllowance(idxAsset, amount); idxAmount = IidxAssetWrapper(idxAsset).wrap(amount); } ```
Open the reportIn the function `_stakeReceiveFrom()`, if the `requestedAsset` is `address(0)`, `idxAsset` is assigned the value of `$.underlyingToIdx[$.info.fundUnderlying]` and `idxAmount` is assigned the value of the `wrap` function of the `idxAsset`'. However, the function is not terminated and returned. Therefore, `idxAsset` may be overridden with `address(0)` if the `$.underlyingToIdx[requestedAsset]` returns `address(0)`. Once the `$.idxAssetList` does not contain `address(0)`, the function will revert. ```solidity=146 function _stakeReceiveFrom( address requestedAsset, address from, uint256 amount ) internal returns (address idxAsset, uint256 idxAmount) { SpectrumCoreStorage storage $ = _getSpectrumCoreStorage(); if (requestedAsset == address(0)) { idxAsset = $.underlyingToIdx[$.info.fundUnderlying]; IEcoERC20(idxAsset).safeIncreaseAllowance(idxAsset, amount); idxAmount = IidxAssetWrapper(idxAsset).wrap(amount); } idxAsset = $.underlyingToIdx[requestedAsset]; if (idxAsset == address(0)) { idxAsset = requestedAsset; idxAmount = amount; if (!$.idxAssetList.contains(idxAsset)) revert Spectrum(SpectrumErrors.NotExist); IEcoERC20(idxAsset).safeTransferFrom(from, address(this), idxAmount); } else { // should wrap IEcoERC20(requestedAsset).safeTransferFrom(from, address(this), amount); IEcoERC20(requestedAsset).safeIncreaseAllowance(idxAsset, amount); idxAmount = IidxAssetWrapper(idxAsset).wrap(amount); } } ```
Open the reportThe cited address input is missing a check that it is not `address(0)`.
Open the reportThe function `updateCoreSupportAsset()` updates asset info, but there is no check to ensure the asset exists. Therefore, if the asset does not exist, it is not added to the `supportAssets`, leading to inconsistency.
Open the reportThe contract `SpectrumCoreEntry` has a `receive()` function to receive ETH, but there is no function to withdraw them. Thus, every ETH sent to the contract will be lost.
Open the reportWhen transferring deflationary ERC20 tokens, the input amount may not be equal to the received amount due to the charged transaction fee. For example, if a user sends 100 deflationary tokens (with a 10% transaction fee), only 90 tokens actually arrived to the contract. However, a failure to discount such fees may allow the same user to withdraw 100 tokens from the contract, which causes the contract to lose 10 tokens in such a transaction.
Open the reportOne or more parameters with `memory` data location are never modified in their functions and those functions are never called internally within the contract. Thus, their data location can be changed to `calldata` to avoid gas consumption copying from `calldata` to memory. ```solidity=13 function functionCallWithValue( ``` `functionCallWithValue` has memory location parameters: `data`. --- ```solidity=25 function functionDelegateCall(address target, bytes memory data) external payable returns (bytes memory); ``` `functionDelegateCall` has memory location parameters: `data`.
Open the reportThe `initialize()` functions below are not called by another contract atomically after the contract is deployed, so it's possible for a malicious user to call `initialize()` which, if it's noticed in time, would require the project to re-deploy the contract in order to properly initialize.
Open the reportAddresses are not validated before assignment or external calls, potentially allowing the use of zero addresses and leading to unexpected behavior or vulnerabilities. For example, transferring tokens to a zero address can result in a permanent loss of those tokens. ```solidity=74 (success, data) = to.call{ value: value }(data); ``` - `to` is not zero-checked before being used.
Open the reportIn the function `checkAscendingAddresses()`, if the `addresses` array is empty, it would cause an underflow error. ```solidity=168 function checkAscendingAddresses(address[] memory addresses) public pure { unchecked { uint256 len = addresses.length - 1; for (uint256 i; i < len; i++) { if (!(addresses[i] < addresses[i + 1])) revert Spectrum(SpectrumErrors.Sort); } } } ```
Open the reportThe contracts `TestProxyAdminFail` and `TestEcoOwnable` are named with a prefix of `Test`, which suggests that they may be test codes.
Open the reportType casting refers to changing an variable of one data type into another. The code contains an unsafe cast between integer types, which may result in unexpected truncation or sign flipping of the value. ```solidity=32 return Ubp.wrap(uint32(Ibp.unwrap(bp))); ``` Casted expression `Ibp.unwrap(bp)` has estimated range [-2147483648, 2147483647] but target type `uint32` has range [0, 4294967295]. --- ```solidity=105 return Ibp.wrap(int32(uint32(underlying))); ``` Casted expression `uint32(underlying)` has estimated range [0, 4294967295] but target type `int32` has range [-2147483648, 2147483647]. --- ```solidity=105 return Ibp.wrap(int32(uint32(underlying))); ``` Casted expression `underlying` has estimated range [0, 115792089237316195423570985008687907853269984665640564039457584007913129639935] but target type `uint32` has range [0, 4294967295]. --- ```solidity=109 return Ibp.wrap(int32(underlying)); ``` Casted expression `underlying` has estimated range [-57896044618658097711785492504343953926634992332820282019728792003956564819968, 57896044618658097711785492504343953926634992332820282019728792003956564819967] but target type `int32` has range [-2147483648, 2147483647]. --- ```solidity=117 return int32(Ubp.unwrap(bp)); ``` Casted expression `Ubp.unwrap(bp)` has estimated range [0, 4294967295] but target type `int32` has range [-2147483648, 2147483647]. --- ```solidity=146 return (unwrap(a) * int256(b)) / IbpBase; ``` Casted expression `b` has estimated range [0, 115792089237316195423570985008687907853269984665640564039457584007913129639935] but target type `int256` has range [-57896044618658097711785492504343953926634992332820282019728792003956564819968, 57896044618658097711785492504343953926634992332820282019728792003956564819967].
Open the report| Address | Chain | Source code |
|---|---|---|
| 0xf1af…c8970xf1af9d897956434e5d7bd875fd231e044890c897 | Chain: Ethereum | Source code: Verified source |
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="kroma-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