Tokemak

QuantstampFiles named in the report

Quantstamp reviewed Tokemak on 31 Aug 2021: 20 findings, 0 critical; 9 marked fixed, 10 acknowledged, 1 open.

No open critical findings
Open the reportOpen Tokemak’s security record

Report hosted on certificate.quantstamp.com.

Trustblock does not review code.

Findings20

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

SeverityOpenAcknowledgedFixed
Critical000
High011
Medium010
Low188
HighManager's Decisions Are Made Off the ChainAcknowledged

The core flow of the system (i.e., supplying liquidity) is currently carried out by the admin team in an off-chain fashion. Precisely put, the calculations of slashing, reward distributions, and asset allocations take place of the chain. Based upon those calculations, the manager submits the transactions to the blockchain. This design assumes a high trust in the manager. The manager can be compromised or go rogue in which case users can lose funds.

MediumStake Without Locking FundsAcknowledged

An attacker can obtain staking power if there is any `schedule` with a passed `hardStart`. The code never checks if funds are being deposited to an already expired `schedule`. Moreover, the code does not unset the `isActive` flag for expired schedules. This could result in a malicious user staking a large amount of funds in order to vote for their favorable target controller followed with an immediate withdraw of their funds. This scenario could be used as a utility to conduct a more complex attack.

Low`schedule.setup` Checks Always PassAcknowledged

Upon addition (`_addSchedule`) of schedules the setup property is set to true. The require statements in the `slash` and `depositFor` functions seem to be useless.

LowAllowance Double-Spend ExploitAcknowledged

As it presently is constructed, the contract is vulnerable to the [allowance double-spend exploit](https://github.com/OpenZeppelin/openzeppelin-solidity/blob/b4f87bb8fc25fb07f73099701e39e167a3d36465/contracts/token/ERC20/ERC20.sol#L71-L78), as with other ERC20 tokens.

LowFunds Held in `BalancerController` Could be DrainedAcknowledged

The visibility of function `deploy` and `withdraw` are set to `external` and without any permission check. With this, an attacker can withdraw all the funds that are in the other pools first, then drain the funds returned to the contract by deploying funds into a malicious pool under the attacker’s control. Right now, the manager uses the vulnerable controller through delegate calls. Therefore, the manager’s storage is used for the platform funds. If someone directly calls the controller it would net no funds.

LowGas Usage / `for` Loop ConcernsAcknowledged

Some loops are not capped at a max number of supported iterations. This has certain implications on the gas usage. > 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. Listed are the loops that we deem as unsafe with respect to gas usage: 1. `contracts/manager/Manager.sol:executeMaintenance:L125` function: ```solidity=125 for (uint256 x = 0; x < params.cycleSteps.length; x++) ``` 2. `contracts/manager/Manager.sol:executeRollover:L147` function: ```solidity=147 for (uint256 x = 0; x < params.cycleSteps.length; x++) { _executeControllerCommand(params.cycleSteps[x]); } ``` 3. `contracts/manager/Manager.sol:L152` ```solidity=152 for (uint256 y = 0; y < params.poolsForWithdraw.length; y++) ``` 4. contracts/staking/Staking.sol:102~104: ```solidity=102 for(uint256 i = 0; i < length; i++) { retSchedules[i] = StakingScheduleInfo(schedules[scheduleIdxs.at(i)], scheduleIdxs.at(i)); } ```

LowPrivileged Roles and OwnershipAcknowledged

Smart contracts will often have `owner` variables to designate the person with special privileges to make modifications to the smart contract. The owner can pause contracts and perform privileged operations. For instance, the owner can withdraw the diff of already withdrawn and initially vested tokens to the treasury. Users can lose funds if the owner account gets compromised or they go rogue.

LowSandwich Attacks Susceptibility in Sushiswap and Uniswap ControllersAcknowledged

> A common attack in DeFi is the sandwich attack. Upon observing a trade of asset X for asset Y, an attacker frontruns the victim trade by also buying asset Y, lets the victim execute the trade, and then backruns (executes after) the victim by trading back the amount gained in the first trade. Intuitively, one uses the knowledge that someone’s going to buy an asset, and that this trade will increase its price, to make a profit. The attacker’s plan is to buy this asset cheap, let the victim buy at an increased price, and then sell the received amount again at a higher price afterwards. In both stated contracts, `minAmountA` and `minAmountB` are provided in the `deploy` function. However, they are not checked after addLiquidity is called.

LowUnlocked PragmaAcknowledged

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". Different versions of Solidity is used: ``` - Version used: ['0.6.11', '>=0.4.24<0.8.0', '>=0.5.0', '>=0.6.0', '>=0.6.0<0.8.0', '>=0.6.2', '>=0.6.2<0.8.0', '^0.6.0', '^0.6.5'] - ^0.6.5 (node_modules/@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol#20) - ^0.6.5 (node_modules/@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol#20) - ^0.6.5 (node_modules/@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol#20) - ^0.6.5 (node_modules/@0x/contracts-utils/contracts/src/v06/errors/LibSafeMathRichErrorsV06.sol#20) - ^0.6.5 (node_modules/@0x/contracts-zero-ex/contracts/src/errors/LibNativeOrdersRichErrors.sol#20) - ^0.6.5 (node_modules/@0x/contracts-zero-ex/contracts/src/errors/LibSignatureRichErrors.sol#20) - ^0.6.5 (node_modules/@0x/contracts-zero-ex/contracts/src/features/interfaces/INativeOrdersEvents.sol#20) - ABIEncoderV2 (node_modules/@0x/contracts-zero-ex/contracts/src/features/interfaces/INativeOrdersEvents.sol#21) - ^0.6.5 (node_modules/@0x/contracts-zero-ex/contracts/src/features/interfaces/INativeOrdersFeature.sol#20) - ABIEncoderV2 (node_modules/@0x/contracts-zero-ex/contracts/src/features/interfaces/INativeOrdersFeature.sol#21) - ^0.6.5 (node_modules/@0x/contracts-zero-ex/contracts/src/features/libs/LibNativeOrder.sol#20) - ABIEncoderV2 (node_modules/@0x/contracts-zero-ex/contracts/src/features/libs/LibNativeOrder.sol#21) - ^0.6.5 (node_modules/@0x/contracts-zero-ex/contracts/src/features/libs/LibSignature.sol#20) - ABIEncoderV2 (node_modules/@0x/contracts-zero-ex/contracts/src/features/libs/LibSignature.sol#21) - >=0.6.0 (node_modules/@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol#2) - ^0.6.0 (node_modules/@gnosis.pm/mock-contract/contracts/MockContract.sol#1) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts-upgradeable/math/MathUpgradeable.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol#3) - >=0.4.24<0.8.0 (node_modules/@openzeppelin/contracts-upgradeable/proxy/Initializable.sol#4) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol#3) - >=0.6.2<0.8.0 (node_modules/@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts-upgradeable/utils/EnumerableSetUpgradeable.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts/access/AccessControl.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts/access/Ownable.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts/cryptography/ECDSA.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts/cryptography/MerkleProof.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts/math/Math.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts/math/SafeMath.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts/presets/ERC20PresetMinterPauser.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts/token/ERC20/ERC20Pausable.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts/token/ERC20/SafeERC20.sol#3) - >=0.6.2<0.8.0 (node_modules/@openzeppelin/contracts/utils/Address.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts/utils/Context.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts/utils/EnumerableSet.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts/utils/Pausable.sol#3) - >=0.6.0<0.8.0 (node_modules/@openzeppelin/contracts/utils/SafeCast.sol#3) - >=0.5.0 (node_modules/@sushiswap/core/contracts/uniswapv2/interfaces/IUniswapV2ERC20.sol#3) - >=0.5.0 (node_modules/@sushiswap/core/contracts/uniswapv2/interfaces/IUniswapV2Factory.sol#3) - >=0.6.2 (node_modules/@sushiswap/core/contracts/uniswapv2/interfaces/IUniswapV2Router01.sol#3) - >=0.6.2 (node_modules/@sushiswap/core/contracts/uniswapv2/interfaces/IUniswapV2Router02

LowUnsafe Cycle Durations Can be SetAcknowledged

The `cycleDuration`, which is set in the `initialize` and `setCycleDuration` functions, can be set to an unsafe small value. Since miners can manipulate block numbers within a small range, the miners might be able to trigger the `completeRollover` earlier or later than the intended `cycleDuration`.

Contracts in scope

No deployed address is recorded for this audit.

The report names a repository or files. Open the report to see its scope.

Repository and files the report names (18)

  • contracts/interfaces/ILiquidityPool.sol
  • contracts/controllers/BalancerController.sol
  • contracts/interfaces/IWallet.sol
  • contracts/wallet/ZeroExTradeWallet.sol
  • contracts/manager/Manager.sol
  • contracts/pools/EthPool.sol
  • contracts/interfaces/IWETH.sol
  • contracts/pools/Pool.sol
  • contracts/rewards/Rewards.sol
  • contracts/controllers/SushiswapController.sol
  • contracts/interfaces/IStaking.sol
  • contracts/controllers/ZeroExController.sol
  • contracts/interfaces/ILiquidityEthPool.sol
  • contracts/interfaces/balancer/IBalancerRegistry.sol
  • contracts/token/Toke.sol
  • contracts/staking/Staking.sol
  • contracts/interfaces/IManager.sol
  • contracts/controllers/UniswapController.sol

Part of Tokemak’s security record

Tokemak
Audits6 audits by 2 firmsLast audit Mar 2025, by Hexens.
Critical findingsNo open critical findingsAcross the 6 reports, 2 critical findings were reported. Each is marked fixed by the firm that reported it.
Open Tokemak’s security record

Security firm

Quantstamp

236 projects reviewed · 332 reports · latest Oct 2026

See Quantstamp’s audits

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