Introduction
The Graph team asked us to audit a bugfix of an issue regarding the Staking
contract.
The pull request that we have audited is PR#457 at commit 01c891829d39e1d6adc30bf13a2c8bf64504f808
and the audited files are the following:
contracts/staking/Staking.sol
contracts/staking/libs/MathUtils.sol
contracts/staking/libs/Stakes.sol
Overview
The issue being fixed is that when indexers were used to call the stakeTo
function (internally calling the _stake
function) to stake from an external address, the delegation parameters of the delegation pool were incorrectly set. This was occurring because setDelegationParameters
was always using msg.sender
to initialize the delegation pool.
The solution adopted is to have an internal _setDelegationParameters
that takes an address as input and that can be used either by the stake
function or the stakeTo
function without encountering the issue anymore.
Apart from the bugfix, the pull request introduces some minor changes in the style and format of the codebase. In particular:
- A new
MathUtils
library has been introduced providing thediff
andweightedAverage
functions. -
The
Stakes
library has been refactored, removing thehasTokens
function and thegetLockingPeriod
. This last function has been replaced by the use of theMathUtils
library. -
Two auxiliary functions,
_pushTokens
and_pullTokens
have been introduced to better modularize the code base.
Summary
The bugfix is quite small and clear. However, other unrelated changes have been added to the same pull request. We strongly recommend using atomic pull requests for bugfix in order to avoid confusing future readers about the explicit purpose of the pull request. We must also notice that the PR in question is still not merged; we assume that The Graph team will merge it as it is and that no other bugs are introduced in eventual changes. The code has been audited by two auditors during the course of three days, with the findings presented below.
Update: All of the following issues have been either fixed or acknowledged by the Graph team. Our analysis of the mitigations is limited to the specific changes made to cover the issues, and disregards all other unrelated changes in the codebase.
Critical Severity
None.
High Severity
None.
Medium Severity
None.
Low Severity
[L01] Lack of input validation
The collect
function of the Staking
contract does not validate whether the _tokens
parameter is non-zero.
When the _tokens
parameter is zero, the collect
function can run without error, and emit its AllocationCollected
event. This provides no useful feedback in the form of a revert message if the parameter was malformed by the client. Furthermore, the emission of the trivial AllocationCollected
event may confuse off-chain services.
In the case that allowing collect
to be called on zero _tokens
was a design choice, consider properly documenting this in the code and other public-facing documentation. Otherwise, consider adding proper checks that the _tokens
parameter is non-zero.
Update: Fixed in commit bd06a61e1055a5e0585e8ea64e618a8d6ce65d7c
where the collect
function now includes documentation describing that zero values of _tokens
are allowed.
[L02] Unclean code
In the lockTokens
function of the Stakes
contract the weightedAverage
function is called, but it is not clear what function parameters each of its four inputs denote. This is because as the inputs are passed in, their names are not suggestive of, or similar to, the parameters belonging to the function signature of the weightedAverage
function.
This hinders readability and understanding of the code by auditors or other stakeholders.
Consider either documenting as comments in the code the correspondence of each input to parameter or defining and using intermediate variables with suggestive names as inputs.
Update: Fixed in PR465 at commit bead8f1e9f248764eec8f4ae5f627c86da33c78d
.
Notes & Additional Information
[N01] Misleading naming
The diff
function of the MathUtils
library is performing a difference of the two numbers or returning zero if the result is negative.
Consider changing the name of the function to diffOrZero
or some other name that can be more explicit on the purpose of the function.
Update: Fixed in commit 3ef27446c7eef64590e133358119d2cbbb0cad65
.
[N02] Erroneous docstrings
The private _setDelegationParameters
function of the Staking
contract is copy/pasting the docstrings from the above setDelegationParameters
function.
Consider giving this private function appropriate docstrings or avoid duplicating them to improve code readability and clarity.
Update: Fixed in commit c8255c05f2dca0a4c7f1574c730255a8c0d39d8c
.
[N03] Inconsistent style
The _sendRewards
function of the Staking
contract makes an ERC20 Graph Token transfer and checks its success with a require statement. Within this same pull request, the _pushTokens
helper function was introduced, making the same transfer and checking its success. This helper function is used in all the Staking
contract’s functions, making a Graph Token transfer, except for the _sendRewards
function.
Taking into consideration how much value a consistent coding style adds to the project’s readability, enforcing a standard coding style is recommended. For consistency, consider using the _pushTokens
function for the token transfer within the _sendRewards
function.
Update: Fixed in commit e11d04183d98f06276cd3fd38c4ad90c4cfd9b65
.
Conclusions
2 Low severity issues and other notes have been reported with recommended changes to improve the codebase.