Incident,
29 ETH.
Source: De.Fi incident database.
- Audits before it
- 6.
- Audits since
- 3, the latest dated 21 Sep 2025.
As of 20 Oct 2026, Onyx Protocol has 9 audit reports by 2 security firms on Trustblock; the latest is by PeckShield, dated 21 Sep 2025. Those reports list no open critical findings. It also holds 1 deployed contract on 1 chain and 1 incident (latest 31 Oct 2023).
Record last updated . It holds audits and findings, contracts and incidents up to Oct 2026. Trustblock does not review code.
Newest first
| Audit | Security firm | Date | Scope | Critical findings | Report |
|---|---|---|---|---|---|
| Onyx: Onyx DAO Registry audit | Security firm: PeckShield | Date: | Scope: Not recorded (see the report) | No open critical findings | Open · GitHub |
| Onyx audit | Security firm: PeckShield | Date: | Scope: Not recorded (see the report) | No open critical findings | Open · GitHub |
| XCN Token (Base) - Audit | Security firm: CertiK | Date: | Scope: Not recorded (see the report) | No open critical findings | Open · skynet.certik.com |
| Onyx Protocol - Audit | Security firm: CertiK | Date: | Scope: Files named in the report | No open critical findings | Open · skynet.certik.com |
| XCN Swap - Audit | Security firm: CertiK | Date: | Scope: Files named in the report | No open critical findings | Open · skynet.certik.com |
| Chain Staking | Security firm: CertiK | Date: | Scope: Files named in the report | No open critical findings | Open · skynet.certik.com |
| Chain - XCN Token | Security firm: CertiK | Date: | Scope: Files named in the report | No open critical findings | Open · skynet.certik.com |
| Chain Team Treasury Timelock | Security firm: CertiK | Date: | Scope: Files named in the report | No open critical findings | Open · skynet.certik.com |
| Chain Token | Security firm: CertiK | Date: | Scope: 1 address on Ethereum | 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 | 7 | 2 | 4 |
| Medium | 4 | 0 | 4 |
| Low | 21 | 3 | 17 |
The OTokenEx contract is intended to be an ERC721 NFT token, but it does not conform to the EIP721 standard. The following issues are present: 1. No ownerOf() function: every NFT token must have an owner. 2. No setApprovalForAll()/isApprovedForAll()/getApproved() functions: ERC721 is different from ERC20. ERC721 does not have the concept of allowances - each token must be approved individually or an operator must be approved for all tokens owned by an owner. 3. transferFrom() is not properly implemented: it mistakenly uses the ERC20 allowance concept, and does not follow the ERC721 individual token or operator approval design. 4. Improper implementation of balanceOf(): the number of tokens owned by a user is the length of the array userTokens, not the value in accountTokens. For reference, see the ERC721 standard interface at https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.1/contracts/token/ERC721/IERC721.sol, and a proper implementation at https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.1/contracts/token/ERC721/ERC721.sol.
Open the reportIn the contracts `BaseJumpRateModelV2` and `DAIInterestRateModelV3` the role `owner` has authority over the function below. Any compromise to the `owner` account may allow the hacker to take advantage of this authority and update the parameters of the interest rate model. - updateJumpRateModel() In the contract `ChainlinkOracle` the role `admin` has authority over the functions below. Any compromise to the `admin` account may allow the hacker to take advantage of this authority and set assets to any prices, set Chainlink aggregator as price feed for an asset, and transfer the `admin` role to a new account. - setDirectPrice() - setFeed() - setAdmin() - setUnderlyingPrice() In the contract `Comptroller` the role `admin` has authority over the functions below. Any compromise to the `admin` account may allow the hacker to take advantage of this authority and set price oracle, set properties, change `marketCapGuardian` and `pauseGuardian`, pause OToken transfer, pause OToken minting, pause seizing assets, pause borrowing assets, and transfer XCN to the recipient. - _setPriceOracle() - _setCloseFactor() - _setCollateralFactor() - _setLiquidationIncentive() - _supportMarket() - _setMarketCaps() - _setMarketCapGuardian() - _setPauseGuardian() - _setMintPaused() - _setBorrowPaused() - _setTransferPaused() - _setSeizePaused() - _grantXcn() - _setXcnSpeeds() In the contracts `Comptroller` and `ComptrollerG5` the role `marketCapGuardian` has authority over the function below. Any compromise to the `marketCapGuardian` account may allow the hacker to take advantage of this authority and set market caps. - _setMarketCaps() In the contracts `Comptroller`, `ComptrollerG2`, `ComptrollerG3`, `ComptrollerG4`, and `ComptrollerG5`, the role `pauseGuardian` has authority over the functions below. Any compromise to the `pauseGuardian` account may allow the hacker to take advantage of this authority and pause OToken transfer, pause OToken minting, pause seizing assets, and pause borrowing assets. - _setMintPaused() - _setBorrowPaused() - _setTransferPaused() - _setSeizePaused() In the contract `ComptrollerG1` the role `admin` has authority over the functions below. Any compromise to the `admin` account may allow the hacker to take advantage of this authority. - _setPriceOracle() - _setCloseFactor() - _setCollateralFactor() - _setMaxAssets() - _setLiquidationIncentive() - _supportMarket() In the contract `ComptrollerG2` the role `admin` has authority over the functions below. Any compromise to the `admin` account may allow the hacker to take advantage of this authority. - _setPriceOracle() - _setCloseFactor() - _setCollateralFactor() - _setMaxAssets() - _setLiquidationIncentive() - _supportMarket() - _setPauseGuardian() - _setMintPaused() - _setBorrowPaused() - _setTransferPaused() - _setSeizePaused() In the contracts `ComptrollerG3`, `ComptrollerG4` and `ComptrollerG5` the role `admin` has authority over the functions below. Any compromise to the `admin` account may allow the hacker to take advantage of this authority. - _setPriceOracle() - _setCloseFactor() - _setCollateralFactor() - _setMaxAssets() - _setLiquidationIncentive() - _supportMarket() - _setPauseGuardian() - _setMintPaused() - _setBorrowPaused() - _setTransferPaused() - _setSeizePaused() - _setXcnRate() - _addXcnMarkets() - _dropXcnMarket() In the contracts `OToken` and `OTokenEx` the role `admin` has authority over the functions shown below. Any compromise to the `admin` account may allow the hacker to take advantage of this authority and set properties, and reduce reserves by transferring to the admin. - _setPendingAdmin() - _setComptroller() - _setReserveFactor() - _reduceReserves() - _setInterestRateModel() In the contract `ODaiDelegate` the role `admin` has authority over the functions below. Any compromise to the `admin` account may allow the hacker to take advantage of this authority. - _becomeImplementation() - _resignImplementation() In the contract `OErc20` the role `admin` has authority over the functions shown below. Any compromise to the `admin` account may allow the hacker to take advantage of this authority and withdraw ERC20 tokens except the underlying asset, and delegate the votes of the XCN-like underlying. - sweepToken() - _delegateXcnLikeTo() In the contracts `OErc20Delegator` and `OErc721Delegator` the role `admin` has authority over the function below. Any compromise to the `admin` account may allow the hacker to take advantage of this authority. - _setImplementation() In the contracts `Unitroller` the role `admin` has authority over the functions below. Any compromise to the `admin` account may allow the hacker to take advantage of this authority. - _setPendingImplementation() - _setPendingAdmin() - Function _become() of contracts `Comptroller`, `ComptrollerG1`, `ComptrollerG2`, `ComptrollerG3`, `ComptrollerG4` and `ComptrollerG5`
Open the report`21537311000 * 10 ** 18` tokens are sent to `msg.sender` when deploying the contract. This could be a centralization risk as the deployer can distribute tokens without obtaining the consensus of the community.
Open the report```solidity=36 function grantDAO(address user, uint256 amount) public onlyOwner { rewardToken.safeTransfer(user, amount); emit GrantDAO(user, amount); } ``` The role `_owner` can send arbitrary amount of reward token to any address through the function `grantDAO()`. If the private key of the owner is compromised, reward token can be completely drained from the contract, and users might not be able to call the `claimReward()` function due to insufficient balance.
Open the reportIn the contract `CHNReward` the role `_owner` has authority over the function `changeStakingAddress()`. Any compromise to the `_owner` account may allow the hacker to take advantage of this authority and change the address of staking contract. In the contract `CHNStaking` the role `_owner` has authority over the functions shown in the diagram below. Any compromise to the `_owner` account may allow the hacker to take advantage of this authority and manipulate critical functions. 
Open the reportIn the contract `ChainToken.sol`, the role `Owner` has the authority over the following function: - pause() - unpause() Any compromise to the `Owner` account may allow the hacker to take advantage of this and pause the token contract. Token holders will not be able to perform token transfer when the token is paused.
Open the reportAll of the Chain tokens are sent to the contract deployer when deploying the contract. This could be a centralization risk as the deployer can distribute Chain tokens without obtaining the consensus of the community.
Open the reportThe report gives no description here.
Open the reportThe functions `getAccountSnapshot()/balanceOfUnderlying()` uses the same `exchangeRate` to calculate the value of an owner's all tokens. But each erc721 token is unique and may have different prices.
Open the reportNFT price can possibly be manipulated. For example, a person or a group of people can sell their nft tokens to themselves at artificially high prices. Even if chainlink nft floor price is used, there is still risk. Wash trading will mislead Onyx platform into believing that the fair market value of an NFT is the new floor price when in reality the price is not a result of natural demand. See https://blog.chain.link/what-is-an-nft-floor-price/
Open the reportIf the token being deposited is a deflationary token implementing the ERC20 standard, the input `_amount` may not be equal to the received amount due to the charged transaction fee. In this case, if a user stakes `_amount` deflationary tokens (with a non-zero transaction fee) into a pool, fewer than `_amount` tokens actually arrive in the contract. The contract calculates the amount the user is able to withdraw to be `user.amount = user.amount.add(_amount).sub(depositFee);`, which could be larger than the amount actually received. The user's rewards calculated for the deposit also use the entered `_amount`, not the actual amount received, which can lead to inconsistencies.
Open the reportThe report gives no description here.
Open the reportThe linked functions are equivalent to the compiler-generated getter functions for the respective variables.
Open the reportIn the function `liquidateCalculateSeizeTokens()`, when calculating the number of collateral tokens to be seized, adding an extra repayment amount from the liquidation proxy leads to an increase in the number of seized collateral tokens for the liquidator. As we are aware from the proxy implementation address, the borrower is compensated with the borrowed tokens after deducting fees. However, liquidating with extra repay does not work properly in the following scenario. A borrower deposited two NFTs, NFT_1 and NFT_2, in order to borrow asset X. If the liquidator mistakenly uses this approach and repays assets with extra payments to liquidate NFT_1. The extra payments will be deposited to the market and mint oX tokens for the borrower. This means that the extra payments go to the borrower and the liquidator does not receive NFT_2, leading to a loss of extra payments for the liquidator. Liquidation proxy implementation address: https://goerli.etherscan.io/address/0x68d65662f93ec57fa8b805215e1690c8597e378d#contracts
Open the reportThe underlying token is an ERC721 token, and the EIP20 interface should not be used for ERC721 tokens, as the two types of tokens are fundamentally different.
Open the reportIn the aforementioned line, the formula for the calculation of `exchangeRate` is as follows after oToken is minted: $$ exchangeRate =\\ {totalCash + totalBorrows - totalReserves \over totalSupply } $$ ```solidity=340 function exchangeRateStoredInternal() internal view returns (MathError, uint) { uint _totalSupply = totalSupply; if (_totalSupply == 0) { /* * If there are no tokens minted: * exchangeRate = initialExchangeRate */ return (MathError.NO_ERROR, initialExchangeRateMantissa); } else { /* * Otherwise: * exchangeRate = (totalCash + totalBorrows - totalReserves) / totalSupply */ uint totalCash = getCashPrior(); uint cashPlusBorrowsMinusReserves; Exp memory exchangeRate; MathError mathErr; (mathErr, cashPlusBorrowsMinusReserves) = addThenSubUInt(totalCash, totalBorrows, totalReserves); if (mathErr != MathError.NO_ERROR) { return (mathErr, 0); } (mathErr, exchangeRate) = getExp(cashPlusBorrowsMinusReserves, _totalSupply); if (mathErr != MathError.NO_ERROR) { return (mathErr, 0); } return (MathError.NO_ERROR, exchangeRate.mantissa); } } ``` In solidity, division calculations have truncation problems. The `totalSupply` will be 1 and `exchangeRate` will be much smaller than `initialExchangeRate` in case the last user redeems (`accountTokens[redeemer] - 1`) oToken. As a result, the `exchangeRate` would be extremely small. When the value of `exchangeRate` is much smaller than `initialExchangeRate`, the user can mint oTokens well above normal values, and then the value of `exchangeRate` will be normal with the interest generating. In other words, the users can use this arbitrage to take away the underlying tokens in this pool. For example, the user can mint the amount of 1e8 OToken with one underlying token in case `exchangeRate = 1/1e8`.
Open the reportThe `require()` statements should provide a description of the condition not met.
Open the reportThe contract serves as the underlying entity to interact with `LiquidationProxy` and `CHNInterface` contracts. The scope of the audit treats contract that is out of scope as black boxes and assumes their functional correctness. However, in the real world, those contracts can be compromised.
Open the reportThe contract is serving as the underlying entity to interact with one or more third party protocols. The scope of the audit treats third party entities as black boxes and assume their functional correctness. However, in the real world, third parties can be compromised and this may lead to lost or stolen assets. In addition, upgrades of third parties can possibly create severe impacts, such as increasing fees of third parties, manipulated price, etc. ```solidity=18 EIP20Interface public token; ``` - The contract `Reservoir` interacts with third party contract with `EIP20Interface` interface via `token`. --- ```solidity=86 (, int256 answer, , , ) = feed.latestRoundData(); ``` - The contract `ChainlinkOracle` interacts with third party contract with `AggregatorV2V3Interface` interface via `feed`. --- ```solidity=220 XcnLike(underlying).delegate(xcnLikeDelegatee); ``` - The contract `OErc20` interacts with third party contract with `XcnLike` interface via `underlying`.
Open the reportIn the function `transferFrom()`, tokens and votes should be transferred from `from` account to `to` account. Therefore the function should utilize the delegate of the `from` account instead of using `msg.sender`.
Open the reportThe return value of the transfer()/transferFrom() call is not checked. ```solidity=63 token_.transfer(target_, toDrip_); ``` --- ```solidity=428 IERC20(MINT_BASE_TOKEN).transferFrom(msg.sender, address(this), value); ```
Open the reportSince the token here is a Governance token, the voting power of delegation should be addressed with the token flow (i.e., when `mint` and `transfer`/`transferFrom` functions are triggered). However, the current `mint()` / `constructor()` implementation does not update the `delegate` (i.e., invoking `_moveDelegates()`).
Open the reportFor the deflationary tokens, the transferred amount through `safeTransferFrom()` may not be equal to the amount of token that the contract received due to the transaction fee designed as the nature of the deflationary token.
Open the reportThere should always be events emitted in the sensitive functions that are controlled by centralization roles.
Open the reportContract `CHNStaking` should inherit from `CHNStakingInterface` in`CHNReward.sol`
Open the report```solidity=46 function initialize( IERC20 _rewardToken, uint256 _rewardPerBlock, uint256 _startBlock, uint256 _bonusEndBlock, uint256 _multiplier, address _rewardVault ) public initializer { __Ownable_init(); rewardToken = _rewardToken; rewardPerBlock = _rewardPerBlock; startBlock = _startBlock; bonusEndBlock = _bonusEndBlock; BONUS_MULTIPLIER = _multiplier; rewardVault = _rewardVault; } ``` Function `initialize()` lacks input validation: - Zero-address checks on `rewardVault` and `rewardToken` - Upper boundaries on `rewardPerBlock` and `BONUS_MULTIPLIER` - Lower boundaries on `startBlock` and `bonusEndBlock`
Open the reportThe return values of functions `safeTransfer()` and `safeTransferFrom()` are not handled.
Open the reportSince the token here is a Governance token, the voting power of delegation should be addressed with the token flow (i.e., when `mint` and `transfer`/`transferFrom` functions are triggered). However, the current `mint()` implementation does not update the `delegate` (i.e., invoking `_moveDelegates()`).
Open the reportSolidity integer division might truncate. As a result, performing multiplication before division can sometimes avoid loss of precision.
Open the reportLiterals with many digits are difficult to read and review.
Open the reportThe contract has unlocked compiler version. An unlocked compiler version in the source code of the contract permits the user to compile it at or above a particular version. This, in turn, leads to differences in the generated bytecode between compilations due to differing compiler version numbers. This can lead to an ambiguity when debugging as compiler specific bugs may occur in the codebase that would be hard to identify over a span of multiple compiler versions rather than a specific one.
Open the reportThe contract has unlocked compiler version. An unlocked compiler version in the source code of the contract permits the user to compile it at or above a particular version. This, in turn, leads to differences in the generated bytecode between compilations due to differing compiler version numbers. This can lead to an ambiguity when debugging as compiler specific bugs may occur in the codebase that would be hard to identify over a span of multiple compiler versions rather than a specific one.
Open the reportAttached through a shared contract address. Our incident data ends in Oct 2026.
29 ETH.
Source: De.Fi incident database.
| Address | Chain | Source code |
|---|---|---|
| 0x41c3…3ca90x41c37a4683d6a05adb31c39d71348a8403b13ca9 | 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="onyx-protocol"></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