Quantstamp reviewed Reach Protocol (/Reach Protocol) on 13 Nov 2023: 15 findings, 0 critical; 4 marked fixed, 7 acknowledged, 4 open.
Report hosted on certificate.quantstamp.com.
Trustblock does not review code.
Statuses are the security firm’s own, from its report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 0 | 1 | 0 |
| Medium | 3 | 2 | 1 |
| Low | 1 | 4 | 3 |
It is a rather centralized design; the reward withdrawal logic depends solely on Merkle roots provided by the off-chain component. Currently the design: 1. Allows the off-chain component (i.e., the admins) to withdraw all the funds supplied by the creators for different missions from the contract at once. 2. Allows an admin to unpause the protocol paused by another admin. This means if an attacker manages to manipulate one of the admins, there will be nothing remaining to stop them.
A leaf in the Reach’s Merkle tree is computed as `leaf = keccak256(abi.encodePacked(msg.sender, _amount))` and can be interpreted as the caller, `msg.sender`, should get `_amount` of rewards. However, it is unclear if the amount should be in ETH or ERC20 tokens. If the variable `tokenDistribution` is wrongly set, the user might get the rewards in the wrong denomination and the original beneficiary of those rewards might not get them at all.
The function `createDistribution()` accepts two input arguments: the `_merkleRoot` containing all the users and the amount each user should get, and the `_amount` input representing the total amount to be distributed. The `_amount` value should be equal to the total amount to be distributed to all the leaves in the Merkle tree, and also less or equal to the current ETH or token balance of the contract. Otherwise, some users would be expecting rewards that are not even available.
The owner of the contracts can call `transferOwnership()` to transfer the ownership to a new address. If an uncontrollable address is accidentally provided as the new owner address then the contract will no longer have an active owner, and functions with the `onlyOwner` modifier can no longer be executed.
The contract allows users to claim rewards in either ETH or an ERC20 token. When initiating a mission through the `createMission()` function, the amount of ETH they send to this function also designates the total rewards that the mission creator will distribute. Furthermore, this function emits the `DistributionCreated(currentVersion, _amount)` event, serving as a trigger for additional processing within the off-chain system. However, it's important to note that there is currently no corresponding function for missions offering rewards in ERC20 tokens. While creators (or the Reach team's Discord bot) can naturally transfer ERC20 tokens directly to the smart contract, there is an equivalent function missing to handle ERC20 token rewards as seen in the `createMission()` for ETH.
If users forget or for some other reason (i.e. sickness) do not claim their rewards before the next Merkle root is updated, those rewards will remain in the contract and some possibilities are in place: 1. The rewards are re-introduced in the tree for the next version. 2. The rewards are diluted between the beneficiaries of the next distribution.
When updating the `erc20token` without updating the Merkle root could force the function `claimRewards()` to fail, if `tokenDistribution` is true, since the contract’s balance of the new token might still be zero. Additionally, it might cause unclaimed rewards to get locked in the contract until the `erc20token` gets updated back to the old one.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (1)
236 projects reviewed · 332 reports · latest Oct 2026
See Quantstamp’s auditsSomething 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