Platform: Code4rena
Start Date: 07/10/2022
Pot Size: $50,000 USDC
Total HM: 4
Participants: 62
Period: 5 days
Judge: 0xean
Total Solo HM: 2
Id: 169
League: ETH
Rank: 25/62
Findings: 2
Award: $71.07
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xSmartContract
Also found by: 0x1f8b, 0x4non, 0xNazgul, Bnke0x0, Chom, IllIllI, Josiah, Rahoz, RaymondFam, Trust, Waze, ajtra, bobirichman, brgltd, bulej93, c3phas, cccz, chrisdior4, delfin454000, fatherOfBlocks, gogo, ladboy233, mcwildy, mics, nicobevi, oyc_109, rbserver, rotcivegaf, zzzitron
50.2765 USDC - $50.28
external
visibility modifier for function that are not being invoked by the contractline#L30: function getProxyImplementation(IGraphProxy _proxy) public view returns (address) { line#L43: function getProxyPendingImplementation(IGraphProxy _proxy) public view returns (address) { line#L68: function changeProxyAdmin(IGraphProxy _proxy, address _newAdmin) public onlyGovernor { line#L77: function upgrade(IGraphProxy _proxy, address _implementation) public onlyGovernor {
line#L18: * public `syncAllContracts()` function whenever a contract changes in the controller.
Optimizations, bug fixes and additional features come with each solidity patch
version update
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity >=0.6.12 <0.8.0;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L26: pragma solidity ^0.7.6;
line#L26: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L26: pragma solidity ^0.7.6;
line#L3: pragma solidity >=0.6.12 <0.8.0;
line#L9: pragma solidity ^0.7.6;
line#L3: pragma solidity >=0.6.12 <0.8.0;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L5: import "./IGraphCurationToken.sol";
https://docs.soliditylang.org/en/develop/natspec-format.html
line#L1: // SPDX-License-Identifier: GPL-2.0-or-later
line#L1: // SPDX-License-Identifier: GPL-2.0-or-later
line#L1: // SPDX-License-Identifier: GPL-2.0-or-later
line#L1: // SPDX-License-Identifier: GPL-2.0-or-later
line#L1: // SPDX-License-Identifier: GPL-2.0-or-later
line#L1: // SPDX-License-Identifier: GPL-2.0-or-later
Emmiting events is recommended each time when a state variable's value is being changed or just some critical event for the contract has occurred. It also helps off-chain monitoring of the contract's state.
line#L150: function _initialize(address _owner, uint256 _initialSupply) internal {
line#L31: function _initialize(address _initGovernor) internal {
line#L26: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L9: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L26: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
line#L3: pragma solidity ^0.7.6;
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0xNazgul, 0xSmartContract, 0xdeadbeef, B2, Bnke0x0, Deivitto, ElKu, Jujic, KoKo, Pheonix, RaymondFam, RedOneN, RockingMiles, Rolezn, Saintcode_, Shinchan, TomJ, Tomio, __141345__, ajtra, aysha, c3phas, carlitox477, catchup, delfin454000, emrekocak, erictee, fatherOfBlocks, gerdusx, gianganhnguyen, gogo, martin, mcwildy, medikko, oyc_109, pedr02b2, rbserver, ret2basic, rotcivegaf, saian, sakman, zishansami
20.7905 USDC - $20.79
1
and 2
instead of true
and false
EVM word size is 32 bytes. The uint256 1
and uint256 2
will be cheaper to use than the boolean true
and boolean false
since the first are 32 bytes long and the last are only 1 byte
line#L8: bool internal _partialPaused; line#L10: bool internal _paused;
If a function is called only once it can be inlined in order to save 20 gas
line#L195: function _getChainID() private pure returns (uint256) {
line#L290: function parseOutboundData(bytes memory _data)
line#L286: function parseOutboundData(bytes memory _data) private view returns (address, bytes memory) {
line#L8: /// @audit Currently storage variables are packed in 4 slots but could fit in 3
payable
to avoid the low-level evm is-a-payment-provided
checkThe EVM checks whether a payment is provided to the function and this check costs additional gas. If the function is marked as payable
there is no such check
line#L52: function _onlyGovernor() internal view { line#L56: function _onlyController() internal view {
line#L40: function transferOwnership(address _newGovernor) external onlyGovernor {
line#L50: function acceptProxy(IGraphProxy _proxy) external onlyProxyAdmin(_proxy) {
line#L80: function bridgeMint(address _account, uint256 _amount) external override onlyGateway { line#L90: function bridgeBurn(address _account, uint256 _amount) external override onlyGateway {
constant
and immutable
keywords for storage varibles if they doesn't change through contract's lifecycleThat way the variable will go to the contract's bytecode and will not allocate a storage slot
line#L29: uint256[10] private __gap;
calldata
where possible instead of memory
to save gasline#L290: function parseOutboundData(bytes memory _data) line#L331: bytes memory _data
line#L266: bytes memory _data /// @audit Store `_data` in calldata. line#L286: function parseOutboundData(bytes memory _data) private view returns (address, bytes memory) {
line#L41: bytes memory _data
line#L173: function _syncContract(string memory _name) internal {
line#L48: L2GasParams memory _l2GasParams, line#L49: bytes memory _data line#L75: bytes memory _data
x < y + 1
is cheaper than x <= y
line#L224: require(msg.value >= expectedEth, "WRONG_ETH_VALUE"); line#L275: require(_amount <= escrowBalance, "BRIDGE_OUT_OF_FUNDS");
line#L95: require(_deadline == 0 || block.timestamp <= _deadline, "GRT: expired permit");
uint256
instead of the smaller uints where possibleThe word-size in ethereum is 32 bytes which means that every variables which is sized with less bytes will cost more to operate with
line#L79: uint8 _v,
line#L33: uint8 _v,
line#L32: function setThawingPeriod(uint32 _thawingPeriod) external; line#L34: function setCurationPercentage(uint32 _percentage) external; line#L38: function setChannelDisputeEpochs(uint32 _channelDisputeEpochs) external; line#L40: function setMaxAllocationEpochs(uint32 _maxAllocationEpochs) external; line#L44: function setDelegationRatio(uint32 _delegationRatio) external; line#L47: uint32 _indexingRewardCut, line#L49: uint32 _cooldownBlocks line#L52: function setDelegationParametersCooldown(uint32 _blocks) external; line#L56: function setDelegationTaxPercentage(uint32 _percentage) external;
line#L37: uint32 cooldownBlocks; line#L38: uint32 indexingRewardCut; line#L39: uint32 queryFeeCut;
line#L29: uint160 constant offset = uint160(0x1111000000000000000000000000000000001111);
line#L10: function setDefaultReserveRatio(uint32 _defaultReserveRatio) external; line#L14: function setCurationTaxPercentage(uint32 _percentage) external;
require
stringsline#L24: require(msg.sender == governor, "Only Governor can call"); line#L41: require(_newGovernor != address(0), "Governor must be set"); line#L54: require( line#L55: pendingGovernor != address(0) && msg.sender == pendingGovernor, line#L56: "Caller must be pending governor" line#L57: );
line#L60: require(isMinter(msg.sender), "Only minter can call"); line#L94: require(_owner == recoveredAddress, "GRT: invalid permit"); line#L106: require(_account != address(0), "INVALID_MINTER"); line#L115: require(_minters[_account], "NOT_A_MINTER");
line#L24: require(msg.sender == _proxy.admin(), "Caller must be the proxy admin"); line#L32: require(msg.sender == _implementation(), "Caller must be the implementation");
line#L62: require(msg.sender == _admin(), "Caller must be admin");
line#L100: require(l2ToL1Sender != address(0), "NO_SENDER");
line#L36: require(msg.sender == gateway, "NOT_GATEWAY"); line#L49: require(_owner != address(0), "Owner must be set"); line#L60: require(_gw != address(0), "INVALID_GATEWAY"); line#L70: require(_addr != address(0), "INVALID_L1_ADDRESS");
line#L69: require( line#L70: msg.sender == AddressAliasHelper.applyL1ToL2Alias(l1Counterpart), line#L71: "ONLY_COUNTERPART_GATEWAY" line#L72: ); line#L98: require(_l2Router != address(0), "INVALID_L2_ROUTER"); line#L118: require(_l1Counterpart != address(0), "INVALID_L1_COUNTERPART"); line#L145: require(_l1Token == l1GRT, "TOKEN_NOT_GRT"); line#L147: require(msg.value == 0, "INVALID_NONZERO_VALUE"); line#L148: require(_to != address(0), "INVALID_DESTINATION"); line#L233: require(_l1Token == l1GRT, "TOKEN_NOT_GRT"); line#L234: require(msg.value == 0, "INVALID_NONZERO_VALUE");
line#L44: require(!controller.paused(), "Paused"); line#L45: require(!controller.partialPaused(), "Partial-paused"); line#L53: require(msg.sender == controller.getGovernor(), "Caller must be Controller governor"); line#L57: require(msg.sender == address(controller), "Caller must be Controller");
line#L105: require(_newAdmin != address(0), "Cannot change the admin of a proxy to the zero address"); line#L141: require(Address.isContract(_pendingImplementation), "Implementation must be a contract"); line#L142: require( line#L143: _pendingImplementation != address(0) && msg.sender == _pendingImplementation, line#L144: "Caller must be the pending implementation" line#L145: ); line#L157: require(msg.sender != _admin(), "Cannot fallback to proxy target");
line#L19: require( line#L20: msg.sender == controller.getGovernor() || msg.sender == pauseGuardian, line#L21: "Only Governor or Guardian can call" line#L22: ); line#L31: require(_newPauseGuardian != address(0), "PauseGuardian must be set"); line#L40: require(!_paused, "Paused (contract)");
line#L74: require(inbox != address(0), "INBOX_NOT_SET"); line#L78: require(msg.sender == address(bridge), "NOT_FROM_BRIDGE"); line#L110: require(_inbox != address(0), "INVALID_INBOX"); line#L111: require(_l1Router != address(0), "INVALID_L1_ROUTER"); line#L132: require(_l2Counterpart != address(0), "INVALID_L2_COUNTERPART"); line#L142: require(_escrow != address(0) && Address.isContract(_escrow), "INVALID_ESCROW"); line#L154: require(!callhookWhitelist[_newWhitelisted], "ALREADY_WHITELISTED"); line#L165: require(_notWhitelisted != address(0), "INVALID_ADDRESS"); line#L200: require(_l1Token == address(token), "TOKEN_NOT_GRT"); line#L201: require(_amount > 0, "INVALID_ZERO_AMOUNT"); line#L213: require( line#L214: extraData.length == 0 || callhookWhitelist[msg.sender] == true, line#L215: "CALL_HOOK_DATA_NOT_ALLOWED" line#L216: ); line#L217: require(maxSubmissionCost > 0, "NO_SUBMISSION_COST"); line#L271: require(_l1Token == address(token), "TOKEN_NOT_GRT"); line#L275: require(_amount <= escrowBalance, "BRIDGE_OUT_OF_FUNDS");
if (x != 0)
instead of if (x > 0)
for uints comparisonSaves 3 gas per if
-check
line#L146: require(_amount > 0, "INVALID_ZERO_AMOUNT");
line#L201: require(_amount > 0, "INVALID_ZERO_AMOUNT"); line#L217: require(maxSubmissionCost > 0, "NO_SUBMISSION_COST");
line#L105: require(_newAdmin != address(0), "Cannot change the admin of a proxy to the zero address"); line#L141: require(Address.isContract(_pendingImplementation), "Implementation must be a contract"); line#L142: require( line#L143: _pendingImplementation != address(0) && msg.sender == _pendingImplementation, line#L144: "Caller must be the pending implementation" line#L145: );
line#L19: require( line#L20: msg.sender == controller.getGovernor() || msg.sender == pauseGuardian, line#L21: "Only Governor or Guardian can call" line#L22: );
line#L53: require(msg.sender == controller.getGovernor(), "Caller must be Controller governor");
line#L32: require(msg.sender == _implementation(), "Caller must be the implementation");
&&
in require()
or if()
statementsUse multiple require()
/if
statements instead
line#L142: require( line#L143: _pendingImplementation != address(0) && msg.sender == _pendingImplementation, line#L144: "Caller must be the pending implementation" line#L145: );
line#L142: require(_escrow != address(0) && Address.isContract(_escrow), "INVALID_ESCROW");
line#L54: require( line#L55: pendingGovernor != address(0) && msg.sender == pendingGovernor, line#L56: "Caller must be pending governor" line#L57: );
Optimization comes from using MSTORE and MLOAD instead of SSTORE and SLOAD
/// @audit Cache `pendingGovernor`. Used 4 times in `acceptOwnership` line#L55: pendingGovernor != address(0) && msg.sender == pendingGovernor, line#L60: address oldPendingGovernor = pendingGovernor; line#L62: governor = pendingGovernor; line#L66: emit NewPendingOwnership(oldPendingGovernor, pendingGovernor);
/// @audit Cache `_partialPaused`. Used 3 times in `_setPartialPaused` line#L27: if (_toPause == _partialPaused) { line#L31: if (_partialPaused) { line#L34: emit PartialPauseChanged(_partialPaused); /// @audit Cache `_paused`. Used 3 times in `_setPaused` line#L41: if (_toPause == _paused) { line#L45: if (_paused) { line#L48: emit PauseChanged(_paused);
constant
variable initialization includes keccak256()
, sha256()
, etc. it should be marked as immutable
insteadThe result of the calculation of the hash (or whatever the expression is) is expected to be saved once compiled, but that's not the case. If marked as constant
the calculation will be executed every time the variable is accessed.
line#L38: bytes32 private constant DOMAIN_NAME_HASH = keccak256("Graph Token"); line#L39: bytes32 private constant DOMAIN_VERSION_HASH = keccak256("0");
true
or false
Directly use the boolean
line#L214: extraData.length == 0 || callhookWhitelist[msg.sender] == true,