DerivaDEX security record

As of 3 Nov 2026, DerivaDEX has 1 audit report on Trustblock, by Quantstamp, dated 4 Sep 2020. That report lists no open critical findings.

Critical findings
No open critical findings
In 1 report.
Open the latest report
Your label, live
Get the label for your site

Record last updated . It holds audits, findings and contracts up to Nov 2026, and incidents up to Oct 2026. Trustblock does not review code.

Audits1

Newest first

AuditSecurity firmDateScopeCritical findingsReport
DerivaDEXSecurity firm: QuantstampDate: Scope: Files named in the reportNo open critical findingsOpen · certificate.quantstamp.com

Findings27

Statuses are the security firm’s own, from each report.

SeverityOpenAcknowledgedFixed
Critical000
High102
Medium014
Low3511
HighCloneable wallet can be re-initializedDerivaDEX · Quantstamp · 4 Sep 2020Fixed

The `initialize()` function member of `DDXWalletCloneable` is used to set the wallets that hold the traders DDX fund. When creating the clone contract in Trader the constructor is not called, however, the `initialize()` function can be called later on to set the approval and delegation. Once the clone created and initialized, any attacker can re-initialize the clone since initialize does not require to be intialized only once. As a result all DDX funds can be taken since the attacker can approve the tokens to his own address.

Open the report
HighDoS Attack in `getPriorVotes()`DerivaDEX · Quantstamp · 4 Sep 2020Fixed

The function `getPriorVotes()` load an entire Checkpoints structure `cp = checkpoints[_account]` to memory from storage. The checkpoint structure contains two dynamic arrays, thus, an attacker can start an attack where he delegates his voting power to an important voting address with high weight, causing the delegatee Checkpoint arrays toincrease in size when the attacker make transfers back and forth between his addresses. If the array sizes increase enough the attacker can render voting inacessible for the delegatee address, since `getPriorVotes()` will consume excessive gas when loading the arrays into the memory and when the while loop will execute, and therefore increase his chances to pass a proposal. `getPriorVotes()` checks the most recent balance before loading the storage to the memory and executing the fetching algorithm, however, this can be bypassed by making a token transfer from the attacker address that delegates its voting power to the attacked address when the proposal voting starts. A more sophisticated attack can involve a malicous contract that own sub-contracts that attack multiple delegees addresses in a single transaction; please note that each token transfer transaction has to be mined in a different block to increase the arrays length.

Open the report
Medium`permit()` does not validate ECDSA parametersDerivaDEX · Quantstamp · 4 Sep 2020Fixed

The function does not validate either of the `s` and `v`. values. See [ECDSA.sol](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/cryptography/ECDSA.sol#L46).

Open the report
MediumGas Usage / `for` Loop ConcernsDerivaDEX · Quantstamp · 4 Sep 2020Fixed

Gas usage is a main concern for smart contract developers and users, since high gas costs may prevent users from wanting to use the smart contract. Even worse, some gas usage issues may prevent the contract from providing services entirely. For example, if a `for` loop requires too much gas to exit, then it may prevent the contract from functioning correctly entirely. It is best to break such loops into individual functions as possible. In particular: * If the number of checkpoints is very large, `advanceInsuranceMineInterval()` may run into gas issues. * In `claimDDXFromInsuranceMining()`, if the staker has not claimed rewards in many intervals, there may be gas issues.

Open the report
MediumMissing input validationDerivaDEX · Quantstamp · 4 Sep 2020Fixed

Several functions do not fully sanitize for faulty arguments. Constructors and initializer functions should ensure that all relevant address arguments are non-zero to avoid faulty deployments. Setter functions should ensure that the underlying state of the contract remains consistent, e.g., variables that constitute lower bounds should be less than variables that denote upper bounds. In the function `DDX.transferOwnershipToDerivaDEXProxy()` is allowed to be called only once and can be subject to input error. We recommend ensuring that `_derivaDEXProxy != address(0)`. When setting `DDX` address in the `DerivaDEX` constructor, `DDX` is not checked to be different than `address(0)`. In `Governance.sol`: * `skipRemainingVotingThreshold` has to be higher than `quorumVotes` otherwise all proposal can eventually reach a defeated state following the `state()` function implementation. If the contract is self governed and a change is made where the `skipRemainingVotingThreshold` is lower than the `quorumVotes`, there will be a single way for reverting this situation since all future proposals will fail except if the last vote is high enough to reach both `skipRemainingVotingThreshold` and `quorumVotes` at the same time when the vote is casted. * When setting governance parameters, some rules should be enforced programmatically such as the `minimumDelay` and `maximumDelay`. As an example, if `maximumDelay` is set lower than `minimumDelay`, all proposal will be rejected. * The function `castVote()` does not revert if the `msg.sender` address votes are equal to zero. In `InsuranceFund.sol`: * The function `setAdvanceIntervalReward()` sets the weight drop between intervals. This should likely be bounded above by 100 through a require-statement. * Similarly for `setWithdrawalFactor()`, this should be capped to 1000. * The address parameters to `addInsuranceFundCollateral()` should at least be ensured to be non-zero. In `Trader.sol`, `setRewardCliff()` may require a bound (possibly 100).

Open the report
MediumStaking may result in loss of funds when different multipliers are usedDerivaDEX · Quantstamp · 4 Sep 2020Fixed

When staking, `stakeToInsuranceFund()` will issue an amount of `DIFundToken` for the `msg.sender` equal to `_amount.proportion96(multiplier, 10)` (please note that the minted amount of `DIFundToken` is used to track the user deposited stake) where the multiplier is used as incentive. Users with higher multiplier value will be issued a higher value of `DIFundToken` and the opposite is true. When withdrawing, `withdrawFromInsuranceFund()` will burn the `DIFundToken` input `_amount` and calculate the collateral amount to give back to the `msg.sender` (`underlyingToTransferNoFee`). If different multiplier values are used when the users stake, the users that get a higher multiplier can get a higher collateral amount withdrawal than his initial deposit (the opposite is also true for lower multiplier value), since they get the proportion of `claimCheckpoint.cap` divided by `stakeCollateral.diFundToken.getTotalPriorValues(block.number.sub(1))` of the input `DIFundToken` to be burned. Please note that a users that stake `X` amount of collateral, in a normal scenario, it is expected that they get the same value of collateral returned when burning the totality of his issued `DIFundToken`.

Open the report
Low`skipRemainingVotingThreshold` may cause voting "races" if set below 50%DerivaDEX · Quantstamp · 4 Sep 2020Fixed

Setting `skipRemainingVotingThreshold` value lower than 51% of the `DDX` supply or the total voting power will incentivize the voters to race against each other since the first group to reach that value will most probably win by either defeating the proposal of succeeding, since the voting deadline won't be necessary as seen [here](https://github.com/apalepu23/derivadex_v1/blob/6bb9ac918e87bc416966b385cc5800cd7c310953/packages/protocol/contracts/facets/governance/Governance.sol#L754). Following the importance of the proposal or if the proposal contains malicous code or actions, using `skipRemainingVotingThreshold` can help attackers reach their goal faster.

Open the report
Low`totalSupply()` does not denote the current total supplyDerivaDEX · Quantstamp · 4 Sep 2020Fixed

Total Supply is the total amount of coins in existence at any moment. The DDX token `totalSupply()` function returns the `TOTAL_SUPPLY` state variable (please note that `TOTAL_SUPPLY` is the max cap and not the existing supply and the state variable is set without being fully minted); `circulatingSupply` is used instead to count the existing token supply that is minted progressively. When burning tokens, the amount is deducted only from `circulatingSupply` and not from `TOTAL_SUPPLY` making the contract return wrong estimation of the existing tokens when calling `totalSupply()`.

Open the report
LowCannot retrieved ether deposited through `receive()`DerivaDEX · Quantstamp · 4 Sep 2020Fixed

The `DerivaDEX` contract implement receive method to handle transaction with empty `msg.data` to receive ether instead of the fallback function. However, no function is implemented to handle ether in the contract balance; any funds sent by mistake will be frozen except if a facet with a `withdraw()` function is added.

Open the report
LowFlashloan VoteDerivaDEX · Quantstamp · 4 Sep 2020Fixed

Even if `getCurrentVotes()` is not used by the implemented Governance facet, using it in future iteration may make the system vulnerable to a flashloans vote, since it includes the user last CheckPoints even if it was created in the same block.

Open the report
Show 7 more
LowIgnored exception cases in transfer functionsDerivaDEX · Quantstamp · 4 Sep 2020Fixed

In `transferTokensAave()` and `transferTokensCompound()`, it is not clear why exceptions are ignored. If the transfer fails the transaction won't get reverted.

Open the report
LowNonstandard Naming in `transferFrom()`DerivaDEX · Quantstamp · 4 Sep 2020Fixed

The function `transferFrom()` always allows the transfer of funds if the `msg.sender` is equal to the `_sender ` param.

Open the report
LowRedundant RequirementsDerivaDEX · Quantstamp · 4 Sep 2020Fixed

Checking `msg.sender` to be different than `address(0)` is unecessary and consume extra gas only. This occurs in the functions `approve()`, `increaseAllowance()`, and `decreaseAllowance()`. In `_transferTokens()`, the function already requires that the `_spender` and `_recipient` addresses must be different than `address(0)`; adding any extra requirements in the `transfer()` function is unecessary.

Open the report
LowStorage data packing can be optimizedDerivaDEX · Quantstamp · 4 Sep 2020Fixed

All data packing for `DiamondStorageContract` is done manually when structures can be used instead. The compiler will tightly pack any ordered state variable by group of 32 bytes and use only one storage slot (also applicable for struct members). Please note that all necessary operation for reading and writing the variables will be created and optimized automatically by the compiler. The `DiamondFacet ` contract handles all the data packing logic, however, the implemented code can be simplified for better reading and acceptance by the developers community.

Open the report
LowUnclear if-case in `claimDDXFromInsuranceMining()`DerivaDEX · Quantstamp · 4 Sep 2020Fixed

On L678, it is not clear why `(stakeCollateral.flavor == InsuranceFundDefs.Flavor.Vanilla)` is mentioned, since the only case handled in the code is for Aave, due to the if-statement on L680. It is not clear if additional code is required here for vanilla cases.

Open the report
LowUnlocked PragmaDerivaDEX · Quantstamp · 4 Sep 2020Fixed

Every Solidity file specifies in the header a version number of the format `pragma solidity (^)0.4.*`. The caret (`^`) before the version number implies an unlocked pragma, meaning that the compiler will use the specified version _and above_, hence the term "unlocked."

Open the report
LowVariable type inconsistencyDerivaDEX · Quantstamp · 4 Sep 2020Fixed

`facets` should store `address facet`, `uint32 slotIndex` and `uint64 slotsIndex` in a single storage slot, however, the values saved here are loaded as `uint128`. Even if the total value will likely not be higher than `2^32`, the developers should keep a certain consistency when implementing their solutions. The possibility of an overflow happening is very low, but it should always be considered.

Open the report

Contracts

No deployed address is recorded for these audits.

The reports name a repository or files. Open a report to see its scope.

The repositories and files the reports name

Show this on your site

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.

Content

Facts

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.

Fixed as
Language

Style

Layout
Theme
Accent
Corners

Format

Size
Date
Embed as
HTML
<head>
<script src="https://widget.trustblock.run/project-widget.min.js" defer></script>
</head>
<body>
<div
data-tb-project-widget
data-tb-project-slug="derivadao"
></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