As of 3 Nov 2026, Derify protocol has 3 audit reports by 1 security firm on Trustblock; the latest is by CertiK, dated 7 Dec 2023. Those reports list no open critical findings.
Record last updated . It holds audits, findings and contracts up to Nov 2026, and incidents up to Oct 2026. Trustblock does not review code.
Newest first
| Audit | Security firm | Date | Scope | Critical findings | Report |
|---|---|---|---|---|---|
| Derify Protocol v2 | Security firm: CertiK | Date: | Scope: Files named in the report | No open critical findings | Open · skynet.certik.com |
| Derify protocol Ⅱ | Security firm: CertiK | Date: | Scope: Files named in the report | No open critical findings | Open · skynet.certik.com |
| Derify Protocol | 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 | 1 | 11 | 1 |
| Medium | 2 | 3 | 4 |
| Low | 11 | 0 | 14 |
"Calling a `nonReentrant` function from another `nonReentrant` function is not supported." See https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.4.2/contracts/security/ReentrancyGuard.sol.
Open the reportAccording to the Derify Protocol’s [insurance pool mechanism](https://derify.medium.com/how-does-the-insurance-pool-in-derify-works-c8887ceda8c5), when users withdraw their funds but the system is in a net loss, funds from the insurance pool will be used as a supplement to ensure the normal settlement of the fund withdrawal; when users withdraw their funds but the system is in a net profit, funds from the liquidity pool will be transferred to the insurance pool. See the code snippet provided below: ```solidity function withdraw(uint256 amount) external override nonReentrant whenNotPaused { ... if (compensation > 0) { uint256 profitCompensation = uint256(compensation); if (insuranceFundPool >= profitCompensation) { _transferPoolFrom( PoolUpdateType.ProfitCompensation, AccountType.InsuranceFundPool, AccountType.LiquidityPool, profitCompensation ); } else { // handle the case that InsuranceFundPool not enough to pay => pay bond uint256 pnlBond = profitCompensation - insuranceFundPool; executeAmount = amount - pnlBond; _transferPoolFrom( PoolUpdateType.ProfitCompensation, AccountType.InsuranceFundPool, AccountType.LiquidityPool, insuranceFundPool ); IDerifyRewards(rewards).payBond(trader, pnlBond); } } else if (compensation < 0){ uint256 lossCompensation = uint256(compensation * -1); _transferPoolFrom( PoolUpdateType.LossCompensation, AccountType.LiquidityPool, AccountType.InsuranceFundPool, lossCompensation ); } } ``` However, the smart contract's deposit function possesses a logical inconsistency in the compensation mechanism. When the system is in a net loss, funds from the liquidity pool will be transferred to the insurance pool. This misalignment in fund transfer logic could create discrepancies in compensation, affecting the protocol's reliability.
Open the reportThe function `deposit()` is called by the user to deposit assets. When the `sysNetPnl` of the system is positive and the user attempts to deposit more assets, and the number of assets in the current pool `LiquidityPool` is less than `profitCompensation`, then the function will fail due to the underflow of `uint256`. In other words, the user cannot deposit more assets at that time. File:DerifyExchange.sol ```solidity=80 uint256 public override liquidityPool; ``` ```solidity=149 _transferPoolFrom( PoolUpdateType.ProfitCompensation, AccountType.LiquidityPool, AccountType.InsuranceFundPool, profitCompensation ); ``` ```solidity=161 liquidityPool += amount; ``` Since the amount of `liquidityPool` is accrued on line 161, there may be insufficient assets when transferring the token on line 149 of the contract.
Open the reportThe above-mentioned function has two modifiers `nonRentrant`.
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. Reference: https://thoreum-finance.medium.com/what-exploit-happened-today-for-gocerberus-and-garuda-also-for-lokum-ybear-piggy-caramelswap-3943ee23a39f ```solidity=81 require(IERC20(token).transferFrom(msg.sender, address(this), amount), "CVV_LT: TOKEN_TRANSFER_ERR"); ``` - Transferring tokens by `amount`. ```solidity=79 totalVestingAmount = totalVestingAmount + amount; ``` - The `amount` appears to be used for bookkeeping purposes without compensating the potential transfer fees. --- ```solidity=82 require(IERC20(token).transferFrom(msg.sender, address(this), amount), "PVV_LT: TOKEN_TRANSFER_ERR"); ``` - Transferring tokens by `amount`. ```solidity=80 totalVestingAmount = totalVestingAmount + amount; ``` - The `amount` appears to be used for bookkeeping purposes without compensating the potential transfer fees. --- ```solidity=81 require(IERC20(token).transferFrom(msg.sender, address(this), amount), "TVV_LT: TOKEN_TRANSFER_ERR"); ``` - Transferring tokens by `amount`. ```solidity=79 totalVestingAmount = totalVestingAmount + amount; ``` - The `amount` appears to be used for bookkeeping purposes without compensating the potential transfer fees.
Open the reportThe value of the public constant CONTRACT_DECIMAL is 10e8 in the DerifyBroker contract, hence all the accounts' decimals should be 8. While USD, DRF, and EDRF are with decimals 18. So the protocol has to interact with USD contract and handle the different decimals.
Open the reportIf the account is liquidated, his/her account balance will be cleared.
Open the reportThe amount of the fee is not relevant to the actual `operatedSize`. As long as the position is processed, there will be a flat fee.
Open the reportIf the updates of gasFeePool are always together with the updates of gasBalance, then the two events can be merged for better simplicity.
Open the reportThe function `transferFrom()` in an ERC20 contract is used for receiving the user's payment to the contract. When the contract needs to transfer money out, it's better to use the function `transfer()` because there is the potential that the target token does not support transferring from users selves.
Open the reportThe position change fee is calculated with kRatio, gRatio, and roRatio. The three ratios are set by the owner and it's hard to understand their meanings.
Open the reportThe function that affects the status of sensitive variables should be able to emit events as notifications to users.
Open the reportThe function name should be `cancelAllOrderedPositions` rather than `cancleAllOrderedPositions`.
Open the reportThe linked `transfer()`/`transferFrom()` invocations do not check the return value of the function call which should yield a `true` result in case of a proper ERC-20 implementation.
Open the reportThe contract is serving as the underlying entity to interact with `communityVestingVault`, which is an external dependency in this audit. The scope of the audit treats 3rd party entities as black boxes and assumes their functional correctness. However, in the real world, 3rd parties can be compromised and this may lead to lost or stolen assets. Additionally, the value `amountVested` is calculated by the function `communityVestingVault.calculateGrantClaim()` and is added to the variable `drfRewardPool`. It's recommended to return the actual value in the function `claimVestedTokens` in case the accounts and funds are not reconciled.
Open the reportThe following functions are declared as `public`, contain array function arguments, and are not invoked in any of the contracts contained within the project's scope. The functions that are never called internally within the contract should have external visibility.
Open the reportFunctions that affect the status of sensitive variables should be able to emit events as notifications to customers.
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 different compiler versions. 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 linked variables could be declared as `constant` since these state variables are never modified.
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="derify-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