Zero Staking

QuantstampRepository at commit 5f93318

Quantstamp reviewed Zero Staking on 19 Jun 2024: 10 findings, 0 critical; 6 marked fixed, 4 acknowledged.

No open critical findings
Open the reportOpen Zero Staking’s security record

Report hosted on certificate.quantstamp.com.

Trustblock does not review code.

Findings10

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

SeverityOpenAcknowledgedFixed
Critical000
High002
Medium002
Low042
HighLoss of Pending Reward when UnstakingFixed

A user who unstakes his full staked balance with the flag `exit = true` can lose all his potential rewards because of the following lines: ``` // In StakingERC20.sol if (staker.amountStaked - amount == 0) { delete stakers[msg.sender]; } ``` This effectively deletes the accounting of the rewards owed to the staker, causing the user to lose all his rewards even when `staker.owedRewards` is non-zero. A similar vulnerability exists in `StakingERC721.sol`.

HighMalicious User Can Drain Rewards Through Reentrancy in StakingFixed

The `StakingERC721.stake()` function allows a malicious staker to exploit the stale `staker.lastUpdatedTimestamp` due to a reentrancy vulnerability. During the staking process, the `_stake()` function calls `_safeMint()`, which calls `checkOnERC721Received()`. This allows the staker to re-enter the staking contract during the minting process. Since the `staker.lastUpdatedTimestamp` is only updated at the end of the `stake()` function, the staker can re-enter the `stake()` or `unstake()` function to trigger multiple `_checkRewards` calculations, thereby unfairly increasing their pending rewards based on the `staker.lastUpdatedTimestamp`. Similar reentrancy risks exist in the `StakingERC20.stake()` function if ERC777 tokens are accepted as staking tokens. The ERC777 token standard was created to extend the capabilities available in ERC20 tokens and one of the features allows the ERC777 token contract to notify the sender and recipient when ERC777 tokens are sent or received, which a malicious staker can use to re-enter the staking function.

MediumMixing of Staked Tokens and Reward TokensFixed

If the staked token and reward token are identical, the staked tokens may be incorrectly used to pay out rewards to other users. This occurs because there is no separation between the funds that are staked by users and the tokens that are sent to the contract to distribute as rewards. This lack of separation can lead to a situation where users' staked tokens are depleted to fulfill reward claims. Furthermore, if the staked token and reward tokens are identical, the function `withdrawLeftoverRewards()` can be used to withdraw staked funds.

MediumUsers Can Lose Out on Rewards if They Claim Too FrequentlyFixed

If a user calls `claim()` too frequently, he may lose out on rewards due to rounding calculation in `_getPendingRewards()`. In particular, when a user claims at times `A` and `B`, where the `B - A` is less than `periodLength`, then the rewards that should have been accumulated (i.e. `(rewardsPerPeriod * staker.amountStaked * ((B - A) / periodLength))` is lost simply due to rounding. Similarly, when a user claims and then stakes within a period's length, the partially accumulated rewards will be lost due to rounding.

LowIncompatible with Deflationary or Rebasing TokensFixed

The contracts are not designed to accommodate deflationary tokens when accepting token transfers. If a user transfers funds that incur a fee on transfer during order submission, the contract will store that the user has deposited more funds than they sent. This can cause issues during withdrawal or vault deposit.

LowMissing Input ValidationFixed

It is important to validate inputs, even if they only come from trusted addresses, to avoid human error. Specifically, in the following functions arguments of type `address` may be initialized with value `0x0`: Following is the list of places that can potentially benefit from stricter input validation: 1. `StakingERC20`: 1. the pending `rewards` of the `_baseClaim()` should not be zero. 2. the `amount` of the `unstake()` should not be zero. 2. `StakingERC721`: 1. the `tokenIds` length should be greater than zero and the `tokenIds` and `tokenUris` length should match in `stake()` function. 2. the `tokenIds` length should be greater than zero in the `unstake()` function. 3. `Match`: 1. the `matchFee` should be greater than zero in the `startMatch()` function; otherwise the `lockedFunds` will be zero and it will not be possible to end the match. 2. the list of `players` addresses should not contain duplicates; otherwise, a player can be charged `matchFee` more than once. 3. when ending a match via the `endMatch()` function, the authorized caller can specify any amount in `gameFee`. A compromised authorized party can set this value to be as much as 100%, stealing the users’ payouts. Consider adding either a state variable with a fixed and known game fee or add an upper bound validation check. 4. `OwnableOperable`: 1. when adding a new operator, the operator boolean should be previously `false` in the function `addOperator()`. 2. when removing an existing operator, the operator boolean should be previously `true` in the function `removeOperator()`.

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 (12)

  • https://github.com/zer0-os/zModules/contracts/access/IOwnableOperable.sol at commit 5f93318541de502e40d80354f1af21e6e8791f87
  • https://github.com/zer0-os/zModules/contracts/access/OwnableOperable.sol at commit 5f93318541de502e40d80354f1af21e6e8791f87
  • https://github.com/zer0-os/zModules/contracts/staking/ERC20/StakingERC20.sol at commit 5f93318541de502e40d80354f1af21e6e8791f87
  • https://github.com/zer0-os/zModules/contracts/escrow/Escrow.sol at commit 5f93318541de502e40d80354f1af21e6e8791f87
  • https://github.com/zer0-os/zModules/contracts/staking/IStakingBase.sol at commit 5f93318541de502e40d80354f1af21e6e8791f87
  • https://github.com/zer0-os/zModules/contracts/staking/StakingBase.sol at commit 5f93318541de502e40d80354f1af21e6e8791f87
  • https://github.com/zer0-os/zModules/contracts/staking/ERC721/IStakingERC721.sol at commit 5f93318541de502e40d80354f1af21e6e8791f87
  • https://github.com/zer0-os/zModules/contracts/match/Match.sol at commit 5f93318541de502e40d80354f1af21e6e8791f87
  • https://github.com/zer0-os/zModules/contracts/escrow/IEscrow.sol at commit 5f93318541de502e40d80354f1af21e6e8791f87
  • https://github.com/zer0-os/zModules/contracts/match/IMatch.sol at commit 5f93318541de502e40d80354f1af21e6e8791f87
  • https://github.com/zer0-os/zModules/contracts/staking/ERC721/StakingERC721.sol at commit 5f93318541de502e40d80354f1af21e6e8791f87
  • https://github.com/zer0-os/zModules/contracts/staking/ERC20/IStakingERC20.sol at commit 5f93318541de502e40d80354f1af21e6e8791f87

Part of Zero Staking’s security record

Zero Staking
Audits1 audit by 1 firmLast audit Jun 2024, by Quantstamp.
Critical findingsNo open critical findingsIn 1 report.
Open Zero Staking’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