The Graph L2 bridge contest - IllIllI's results

A protocol for indexing and querying blockchain data.

General Information

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

The Graph

Findings Distribution

Researcher Performance

Rank: 9/62

Findings: 2

Award: $954.97

🌟 Selected for report: 1

šŸš€ Solo Findings: 0

Awards

602.1213 USDC - $602.12

Labels

bug
QA (Quality Assurance)

External Links

Summary

Low Risk Issues

IssueInstances
[L‑01]Upgradeable contract not initialized1
[L‑02]initialize() function does not use the initializer modifier1
[L‑03]require() should be used instead of assert()3
[L‑04]Missing checks for address(0x0) when assigning values to address state variables1
[L‑05]abi.encodePacked() should not be used with dynamic types when passing the result to a hash function such as keccak256()1
[L‑06]Upgradeable contract is missing a __gap[50] storage variable to allow for new storage variables in later versions4

Total: 11 instances over 6 issues

Non-critical Issues

IssueInstances
[N‑01]Missing initializer modifier on constructor1
[N‑02]Missing initializer modifier1
[N‑03]The nonReentrant modifier should occur before all other modifiers2
[N‑04]require()/revert() statements should have descriptive reason strings4
[N‑05]public functions not called by the contract should be declared external instead6
[N‑06]Non-assembly method available1
[N‑07]constants should be defined rather than using magic numbers2
[N‑08]Cast is more restrictive than the type of the variable being assigned1
[N‑09]Use a more recent version of solidity3
[N‑10]Use a more recent version of solidity2
[N‑11]Expressions for constant values such as a call to keccak256(), should use immutable rather than constant4
[N‑12]Variable names that consist of all capital letters should be reserved for constant/immutable variables1
[N‑13]Non-library/interface files should use fixed compiler versions, not floating ones12
[N‑14]File is missing NatSpec6
[N‑15]NatSpec is incomplete6
[N‑16]Event is missing indexed fields18
[N‑17]Duplicated require()/revert() checks should be refactored to a modifier or function5

Total: 74 instances over 17 issues

Low Risk Issues

[L‑01] Upgradeable contract not initialized

Upgradeable contracts are initialized via an initializer function rather than by a constructor. Leaving such a contract uninitialized may lead to it being taken over by a malicious user

There is 1 instances of this issue:

File: contracts/l2/token/GraphTokenUpgradeable.sol

/// @audit missing __ERC20Burnable_init()
28:   contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgradeable {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/GraphTokenUpgradeable.sol#L28

[L‑02] initialize() function does not use the initializer modifier

Without the modifier, the function may be called multiple times, overwriting prior initializations

There is 1 instance of this issue:

File: contracts/l2/gateway/L2GraphTokenGateway.sol

87:       function initialize(address _controller) external onlyImpl {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/gateway/L2GraphTokenGateway.sol#L87

[L‑03] require() should be used instead of assert()

Prior to solidity version 0.8.0, hitting an assert consumes the remainder of the transaction's available gas rather than returning it, as require()/revert() do. assert() should be avoided even past solidity version 0.8.0 as its documentation states that "The assert function creates an error of type Panic(uint256). ... Properly functioning code should never create a Panic, not even on invalid external input. If this happens, then there is a bug in your contract which you should fix".

There are 3 instances of this issue:

File: contracts/upgrades/GraphProxy.sol

47:           assert(ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));

48            assert(
49                IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)
50:           );

51            assert(
52                PENDING_IMPLEMENTATION_SLOT ==
53                    bytes32(uint256(keccak256("eip1967.proxy.pendingImplementation")) - 1)
54:           );

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxy.sol#L47

[L‑04] Missing checks for address(0x0) when assigning values to address state variables

There is 1 instance of this issue:

File: contracts/governance/Governed.sol

32:           governor = _initGovernor;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Governed.sol#L32

[L‑05] abi.encodePacked() should not be used with dynamic types when passing the result to a hash function such as keccak256()

Use abi.encode() instead which will pad items to 32 bytes, which will prevent hash collisions (e.g. abi.encodePacked(0x123,0x456) => 0x123456 => abi.encodePacked(0x1,0x23456), but abi.encode(0x123,0x456) => 0x0...1230...456). "Unless there is a compelling reason, abi.encode should be preferred". If there is only one argument to abi.encodePacked() it can often be cast to bytes() or bytes32() instead.

If all arguments are strings and or bytes, bytes.concat() should be used instead

There is 1 instance of this issue:

File: contracts/governance/Managed.sol

174:          bytes32 nameHash = keccak256(abi.encodePacked(_name));

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Managed.sol#L174

[L‑06] Upgradeable contract is missing a __gap[50] storage variable to allow for new storage variables in later versions

See this link for a description of this storage variable. While some contracts may not currently be sub-classed, adding the variable now protects against forgetting to add it in the future.

There are 4 instances of this issue:

File: contracts/gateway/BridgeEscrow.sol

15:   contract BridgeEscrow is GraphUpgradeable, Managed {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/BridgeEscrow.sol#L15

File: contracts/l2/gateway/L2GraphTokenGateway.sol

23:   contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, ReentrancyGuardUpgradeable {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/gateway/L2GraphTokenGateway.sol#L23

File: contracts/l2/token/GraphTokenUpgradeable.sol

28:   contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgradeable {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/GraphTokenUpgradeable.sol#L28

File: contracts/l2/token/L2GraphToken.sol

15:   contract L2GraphToken is GraphTokenUpgradeable, IArbToken {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/L2GraphToken.sol#L15

Non-critical Issues

[N‑01] Missing initializer modifier on constructor

OpenZeppelin recommends that the initializer modifier be applied to constructors in order to avoid potential griefs, social engineering, or exploits. Ensure that the modifier is applied to the implementation contract. If the default constructor is currently being used, it should be changed to be an explicit one with the modifier applied.

There is 1 instance of this issue:

File: contracts/l2/gateway/L2GraphTokenGateway.sol

23:   contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, ReentrancyGuardUpgradeable {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/gateway/L2GraphTokenGateway.sol#L23

[N‑02] Missing initializer modifier

The contract extends Initializable/ReentrancyGuardUpgradeable but does not use the initializer modifier anywhere

There is 1 instance of this issue:

File: contracts/l2/gateway/L2GraphTokenGateway.sol

23:   contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, ReentrancyGuardUpgradeable {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/gateway/L2GraphTokenGateway.sol#L23

[N‑03] The nonReentrant modifier should occur before all other modifiers

This is a best-practice to protect against reentrancy in other modifiers

There are 2 instances of this issue:

File: contracts/l2/gateway/L2GraphTokenGateway.sol

144:      ) public payable override notPaused nonReentrant returns (bytes memory) {

232:      ) external payable override notPaused onlyL1Counterpart nonReentrant {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/gateway/L2GraphTokenGateway.sol#L144

[N‑04] require()/revert() statements should have descriptive reason strings

There are 4 instances of this issue:

File: contracts/upgrades/GraphProxyAdmin.sol

34:           require(success);

47:           require(success);

59:           require(success);

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxyAdmin.sol#L34

File: contracts/upgrades/GraphProxy.sol

133:          require(success);

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxy.sol#L133

[N‑05] public functions not called by the contract should be declared external instead

Contracts are allowed to override their parents' functions and change the visibility from external to public.

There are 6 instances of this issue:

File: contracts/upgrades/GraphProxyAdmin.sol

30:       function getProxyImplementation(IGraphProxy _proxy) public view returns (address) {

43:       function getProxyPendingImplementation(IGraphProxy _proxy) public view returns (address) {

55:       function getProxyAdmin(IGraphProxy _proxy) public view returns (address) {

68:       function changeProxyAdmin(IGraphProxy _proxy, address _newAdmin) public onlyGovernor {

77:       function upgrade(IGraphProxy _proxy, address _implementation) public onlyGovernor {

86:       function acceptProxy(GraphUpgradeable _implementation, IGraphProxy _proxy) public onlyGovernor {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxyAdmin.sol#L30

[N‑06] Non-assembly method available

assembly{ id := chainid() } => uint256 id = block.chainid, assembly { size := extcodesize() } => uint256 size = address().code.length There are some automated tools that will flag a project as having higher complexity if there is inline-assembly, so it's best to avoid using it where it's not necessary

There is 1 instance of this issue:

File: contracts/l2/token/GraphTokenUpgradeable.sol

199:              id := chainid()

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/GraphTokenUpgradeable.sol#L199

[N‑07] constants should be defined rather than using magic numbers

Even assembly can benefit from using readable constants instead of hex/numeric literals

There are 2 instances of this issue:

File: contracts/upgrades/GraphProxy.sol

/// @audit 0x40
161:              let ptr := mload(0x40)

/// @audit 0xffffffffffffffffffffffffffffffffffffffff
164:              let impl := and(sload(IMPLEMENTATION_SLOT), 0xffffffffffffffffffffffffffffffffffffffff)

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxy.sol#L161

[N‑08] Cast is more restrictive than the type of the variable being assigned

If address foo is being used in an expression such as IERC20 token = FooToken(foo), then the more specific cast to FooToken is a waste because the only thing the compiler will check for is that FooToken extends IERC20 - it won't check any of the function signatures. Therefore, it makes more sense to do IERC20 token = IERC20(token) or better yet FooToken token = FooToken(foo). The former may allow the file in which it's used to remove the import for FooToken

There is 1 instance of this issue:

File: contracts/governance/Managed.sol

/// @audit IController vs address
105:          controller = IController(_controller);

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Managed.sol#L105

[N‑09] Use a more recent version of solidity

Use a solidity version of at least 0.8.13 to get the ability to use using for with a list of free functions

There are 3 instances of this issue:

File: contracts/gateway/L1GraphTokenGateway.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/L1GraphTokenGateway.sol#L3

File: contracts/l2/gateway/L2GraphTokenGateway.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/gateway/L2GraphTokenGateway.sol#L3

File: contracts/l2/token/L2GraphToken.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/L2GraphToken.sol#L3

[N‑10] Use a more recent version of solidity

Use a solidity version of at least 0.8.4 to get bytes.concat() instead of abi.encodePacked(<bytes>,<bytes>) Use a solidity version of at least 0.8.12 to get string.concat() instead of abi.encodePacked(<str>,<str>)

There are 2 instances of this issue:

File: contracts/governance/Managed.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Managed.sol#L3

File: contracts/l2/token/GraphTokenUpgradeable.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/GraphTokenUpgradeable.sol#L3

[N‑11] Expressions for constant values such as a call to keccak256(), should use immutable rather than constant

While it doesn't save any gas because the compiler knows that developers often make this mistake, it's still best to use the right tool for the task at hand. There is a difference between constant variables and immutable variables, and they should each be used in their appropriate contexts. constants should be used for literal values written into the code, and immutable variables should be used for expressions, or values calculated in, or passed into the constructor.

There are 4 instances of this issue:

File: contracts/l2/token/GraphTokenUpgradeable.sol

34        bytes32 private constant DOMAIN_TYPE_HASH =
35            keccak256(
36                "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)"
37:           );

38:       bytes32 private constant DOMAIN_NAME_HASH = keccak256("Graph Token");

39:       bytes32 private constant DOMAIN_VERSION_HASH = keccak256("0");

42        bytes32 private constant PERMIT_TYPEHASH =
43            keccak256(
44                "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
45:           );

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/GraphTokenUpgradeable.sol#L34-L37

[N‑12] Variable names that consist of all capital letters should be reserved for constant/immutable variables

If the variable needs to be different based on which class it comes from, a view/pure function should be used instead (e.g. like this).

There is 1 instance of this issue:

File: contracts/l2/token/GraphTokenUpgradeable.sol

50:       bytes32 private DOMAIN_SEPARATOR;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/GraphTokenUpgradeable.sol#L50

[N‑13] Non-library/interface files should use fixed compiler versions, not floating ones

There are 12 instances of this issue:

File: contracts/gateway/BridgeEscrow.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/BridgeEscrow.sol#L3

File: contracts/gateway/L1GraphTokenGateway.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/L1GraphTokenGateway.sol#L3

File: contracts/governance/Governed.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Governed.sol#L3

File: contracts/governance/Managed.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Managed.sol#L3

File: contracts/governance/Pausable.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Pausable.sol#L3

File: contracts/l2/gateway/L2GraphTokenGateway.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/gateway/L2GraphTokenGateway.sol#L3

File: contracts/l2/token/GraphTokenUpgradeable.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/GraphTokenUpgradeable.sol#L3

File: contracts/l2/token/L2GraphToken.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/L2GraphToken.sol#L3

File: contracts/upgrades/GraphProxyAdmin.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxyAdmin.sol#L3

File: contracts/upgrades/GraphProxy.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxy.sol#L3

File: contracts/upgrades/GraphProxyStorage.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxyStorage.sol#L3

File: contracts/upgrades/GraphUpgradeable.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphUpgradeable.sol#L3

[N‑14] File is missing NatSpec

There are 6 instances of this issue:

File: contracts/curation/ICuration.sol

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/curation/ICuration.sol

File: contracts/curation/IGraphCurationToken.sol

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/curation/IGraphCurationToken.sol

File: contracts/epochs/IEpochManager.sol

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/epochs/IEpochManager.sol

File: contracts/governance/IController.sol

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/IController.sol

File: contracts/token/IGraphToken.sol

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/token/IGraphToken.sol

File: contracts/upgrades/IGraphProxy.sol

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/IGraphProxy.sol

[N‑15] NatSpec is incomplete

There are 6 instances of this issue:

File: contracts/gateway/L1GraphTokenGateway.sol

/// @audit Missing: '@param bytes'
252       /**
253        * @notice Receives withdrawn tokens from L2
254        * The equivalent tokens are released from escrow and sent to the destination.
255        * @dev can only accept transactions coming from the L2 GRT Gateway.
256        * The last parameter is unused but kept for compatibility with Arbitrum gateways,
257        * and the encoded exitNum is assumed to be 0.
258        * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router)
259        * @param _from Address of the sender
260        * @param _to Recepient address on L1
261        * @param _amount Amount of tokens transferred
262        */
263       function finalizeInboundTransfer(
264           address _l1Token,
265           address _from,
266           address _to,
267           uint256 _amount,
268           bytes calldata // _data, contains exitNum, unused by this contract
269:      ) external payable override notPaused onlyL2Counterpart {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/L1GraphTokenGateway.sol#L252-L269

File: contracts/governance/Managed.sol

/// @audit Missing: '@param _nameHash'
157       /**
158        * @dev Resolve a contract address from the cache or the Controller if not found.
159        * @return Address of the contract
160        */
161:      function _resolveContract(bytes32 _nameHash) internal view returns (address) {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Managed.sol#L157-L161

File: contracts/l2/gateway/L2GraphTokenGateway.sol

/// @audit Missing: '@param uint256'
123       /**
124        * @notice Burns L2 tokens and initiates a transfer to L1.
125        * The tokens will be available on L1 only after the wait period (7 days) is over,
126        * and will require an Outbox.executeTransaction to finalize.
127        * Note that the caller must previously allow the gateway to spend the specified amount of GRT.
128        * @dev no additional callhook data is allowed. The two unused params are needed
129        * for compatibility with Arbitrum's gateway router.
130        * The function is payable for ITokenGateway compatibility, but msg.value must be zero.
131        * @param _l1Token L1 Address of GRT (needed for compatibility with Arbitrum Gateway Router)
132        * @param _to Recipient address on L1
133        * @param _amount Amount of tokens to burn
134        * @param _data Contains sender and additional data (always empty) to send to L1
135        * @return ID of the withdraw transaction
136        */
137       function outboundTransfer(
138           address _l1Token,
139           address _to,
140           uint256 _amount,
141           uint256, // unused on L2
142           uint256, // unused on L2
143           bytes calldata _data
144:      ) public payable override notPaused nonReentrant returns (bytes memory) {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/gateway/L2GraphTokenGateway.sol#L123-L144

File: contracts/upgrades/GraphProxyAdmin.sol

/// @audit Missing: '@param _proxy'
25        /**
26         * @dev Returns the current implementation of a proxy.
27         * This is needed because only the proxy admin can query it.
28         * @return The address of the current implementation of the proxy.
29         */
30:       function getProxyImplementation(IGraphProxy _proxy) public view returns (address) {

/// @audit Missing: '@param _proxy'
38        /**
39         * @dev Returns the pending implementation of a proxy.
40         * This is needed because only the proxy admin can query it.
41         * @return The address of the pending implementation of the proxy.
42         */
43:       function getProxyPendingImplementation(IGraphProxy _proxy) public view returns (address) {

/// @audit Missing: '@param _proxy'
51        /**
52         * @dev Returns the admin of a proxy. Only the admin can query it.
53         * @return The address of the current admin of the proxy.
54         */
55:       function getProxyAdmin(IGraphProxy _proxy) public view returns (address) {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxyAdmin.sol#L25-L30

[N‑16] Event is missing indexed fields

Index event fields make the field more quickly accessible to off-chain tools that parse events. However, note that each index field costs extra gas during emission, so it's not necessarily best to index the maximum allowed per event (three fields). Each event should use three indexed fields if there are three or more fields, and gas usage is not particularly of concern for the events in question. If there are fewer than three fields, all of the fields should be indexed.

There are 18 instances of this issue:

File: contracts/gateway/L1GraphTokenGateway.sol

56:       event ArbitrumAddressesSet(address inbox, address l1Router);

58:       event L2TokenAddressSet(address l2GRT);

60:       event L2CounterpartAddressSet(address l2Counterpart);

62:       event EscrowAddressSet(address escrow);

64:       event AddedToCallhookWhitelist(address newWhitelisted);

66:       event RemovedFromCallhookWhitelist(address notWhitelisted);

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/L1GraphTokenGateway.sol#L56

File: contracts/governance/Managed.sol

33:       event ParameterUpdated(string param);

34:       event SetController(address controller);

39:       event ContractSynced(bytes32 indexed nameHash, address contractAddress);

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Managed.sol#L33

File: contracts/governance/Pausable.sol

19:       event PartialPauseChanged(bool isPaused);

20:       event PauseChanged(bool isPaused);

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Pausable.sol#L19

File: contracts/l2/gateway/L2GraphTokenGateway.sol

58:       event L2RouterSet(address l2Router);

60:       event L1TokenAddressSet(address l1GRT);

62:       event L1CounterpartAddressSet(address l1Counterpart);

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/gateway/L2GraphTokenGateway.sol#L58

File: contracts/l2/token/L2GraphToken.sol

24:       event BridgeMinted(address indexed account, uint256 amount);

26:       event BridgeBurned(address indexed account, uint256 amount);

28:       event GatewaySet(address gateway);

30:       event L1AddressSet(address l1Address);

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/L2GraphToken.sol#L24

[N‑17] Duplicated require()/revert() checks should be refactored to a modifier or function

The compiler will inline the function, which will avoid JUMP instructions usually associated with functions

There are 5 instances of this issue:

File: contracts/gateway/L1GraphTokenGateway.sol

271:          require(_l1Token == address(token), "TOKEN_NOT_GRT");

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/L1GraphTokenGateway.sol#L271

File: contracts/governance/Managed.sol

49:           require(!controller.paused(), "Paused");

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Managed.sol#L49

File: contracts/l2/gateway/L2GraphTokenGateway.sol

233:          require(_l1Token == l1GRT, "TOKEN_NOT_GRT");

234:          require(msg.value == 0, "INVALID_NONZERO_VALUE");

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/gateway/L2GraphTokenGateway.sol#L233

File: contracts/upgrades/GraphProxyAdmin.sol

47:           require(success);

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxyAdmin.sol#L47

#0 - trust1995

2022-10-21T22:13:04Z

L-02 is dup of #149

Awards

352.8492 USDC - $352.85

Labels

bug
G (Gas Optimization)
primary issue
selected for report

External Links

Summary

Gas Optimizations

IssueInstancesTotal Gas Saved
[G‑01]State variables can be packed into fewer storage slots1-
[G‑02]Avoid contract existence checks by using low level calls3300
[G‑03]State variables should be cached in stack variables rather than re-reading them from storage10970
[G‑04]internal functions only called once can be inlined to save gas360
[G‑05]require()/revert() strings longer than 32 bytes cost extra gas6-
[G‑06]keccak256() should only need to be called on a specific string literal once6252
[G‑07]Optimize names to save gas19418
[G‑08]Using bools for storage incurs overhead468400
[G‑09]Use a more recent version of solidity20-
[G‑10]Use a more recent version of solidity3-
[G‑11]Using > 0 costs more gas than != 0 when used on a uint in a require() statement318
[G‑12]Splitting require() statements that use && saves gas39
[G‑13]Don't compare boolean expressions to boolean literals19
[G‑14]Stack variable used as a cheaper cache for a state variable is only used once412
[G‑15]require() or revert() statements that check input arguments should be at the top of the function1-
[G‑16]Use custom errors rather than revert()/require() strings to save gas60-
[G‑17]Functions guaranteed to revert when called by normal users can be marked payable32672

Total: 179 instances over 17 issues with 7120 gas saved

Gas totals use lower bounds of ranges and count two iterations of each for-loop. All values above are runtime, not deployment, values; deployment values are listed in the individual issue descriptions

Gas Optimizations

[G‑01] State variables can be packed into fewer storage slots

If variables occupying the same slot are both written the same function or by the constructor, avoids a separate Gsset (20000 gas). Reads of the variables can also be cheaper

There is 1 instance of this issue:

File: contracts/governance/Pausable.sol

/// @audit Variable ordering with 3 slots instead of the current 4:
///           uint256(32):lastPausePartialTime, uint256(32):lastPauseTime, address(20):pauseGuardian, bool(1):_partialPaused, bool(1):_paused
8:        bool internal _partialPaused;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Pausable.sol#L8

[G‑02] Avoid contract existence checks by using low level calls

Prior to 0.8.10 the compiler inserted extra code, including EXTCODESIZE (100 gas), to check for contract existence for external function calls. In more recent solidity versions, the compiler will not insert these checks if the external call has a return value. Similar behavior can be achieved in earlier versions by using low-level calls, since low level calls never check for contract existence

There are 3 instances of this issue:

File: contracts/gateway/BridgeEscrow.sol

/// @audit approve()
29:           graphToken().approve(_spender, type(uint256).max);

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/BridgeEscrow.sol#L29

File: contracts/gateway/L1GraphTokenGateway.sol

/// @audit bridge()
77:           IBridge bridge = IInbox(inbox).bridge();

/// @audit l2ToL1Sender()
81:           address l2ToL1Sender = IOutbox(bridge.activeOutbox()).l2ToL1Sender();

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/L1GraphTokenGateway.sol#L77

[G‑03] State variables should be cached in stack variables rather than re-reading them from storage

The instances below point to the second+ access of a state variable within a function. Caching of a state variable replaces each Gwarmaccess (100 gas) with a much cheaper stack read. Other less obvious fixes/optimizations include having local memory caches of state variable structs, or having local caches of state variable contracts/addresses.

There are 10 instances of this issue:

File: contracts/governance/Governed.sol

/// @audit governor on line 62
65:           emit NewOwnership(oldGovernor, governor);

/// @audit pendingGovernor on line 44
46:           emit NewPendingOwnership(oldPendingGovernor, pendingGovernor);

/// @audit pendingGovernor on line 55
55:               pendingGovernor != address(0) && msg.sender == pendingGovernor,

/// @audit pendingGovernor on line 55
60:           address oldPendingGovernor = pendingGovernor;

/// @audit pendingGovernor on line 60
62:           governor = pendingGovernor;

/// @audit pendingGovernor on line 63
66:           emit NewPendingOwnership(oldPendingGovernor, pendingGovernor);

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Governed.sol#L65

File: contracts/governance/Pausable.sol

/// @audit _partialPaused on line 30
31:           if (_partialPaused) {

/// @audit _partialPaused on line 31
34:           emit PartialPauseChanged(_partialPaused);

/// @audit _paused on line 44
45:           if (_paused) {

/// @audit _paused on line 45
48:           emit PauseChanged(_paused);

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Pausable.sol#L31

[G‑04] internal functions only called once can be inlined to save gas

Not inlining costs 20 to 40 gas because of two extra JUMP instructions and additional stack operations needed for function calls.

There are 3 instances of this issue:

File: contracts/governance/Managed.sol

43        function _notPartialPaused() internal view {
44:           require(!controller.paused(), "Paused");

52        function _onlyGovernor() internal view {
53:           require(msg.sender == controller.getGovernor(), "Caller must be Controller governor");

56        function _onlyController() internal view {
57:           require(msg.sender == address(controller), "Caller must be Controller");

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Managed.sol#L43-L44

[G‑05] require()/revert() strings longer than 32 bytes cost extra gas

Each extra memory word of bytes past the original 32 incurs an MSTORE which costs 3 gas

There are 6 instances of this issue:

File: contracts/gateway/GraphTokenGateway.sol

19            require(
20                msg.sender == controller.getGovernor() || msg.sender == pauseGuardian,
21                "Only Governor or Guardian can call"
22:           );

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/GraphTokenGateway.sol#L19-L22

File: contracts/governance/Managed.sol

53:           require(msg.sender == controller.getGovernor(), "Caller must be Controller governor");

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Managed.sol#L53

File: contracts/upgrades/GraphProxy.sol

105:          require(_newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");

141:          require(Address.isContract(_pendingImplementation), "Implementation must be a contract");

142           require(
143               _pendingImplementation != address(0) && msg.sender == _pendingImplementation,
144               "Caller must be the pending implementation"
145:          );

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxy.sol#L105

File: contracts/upgrades/GraphUpgradeable.sol

32:           require(msg.sender == _implementation(), "Caller must be the implementation");

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphUpgradeable.sol#L32

[G‑06] keccak256() should only need to be called on a specific string literal once

It should be saved to an immutable variable, and the variable used instead. If the hash is being used as a part of a function selector, the cast to bytes4 should also only be done once

There are 6 instances of this issue:

File: contracts/governance/Managed.sol

114:          return ICuration(_resolveContract(keccak256("Curation")));

122:          return IEpochManager(_resolveContract(keccak256("EpochManager")));

130:          return IRewardsManager(_resolveContract(keccak256("RewardsManager")));

138:          return IStaking(_resolveContract(keccak256("Staking")));

146:          return IGraphToken(_resolveContract(keccak256("GraphToken")));

154:          return ITokenGateway(_resolveContract(keccak256("GraphTokenGateway")));

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Managed.sol#L114

[G‑07] Optimize names to save gas

public/external function names and public member variable names can be optimized to save gas. See this link for an example of how it works. Below are the interfaces/abstract contracts that can be optimized so that the most frequently-called functions use the least amount of gas possible during method lookup. Method IDs that have two leading zero bytes can save 128 gas each during deployment, and renaming functions to have lower method IDs will save 22 gas per call, per sorted position shifted

There are 19 instances of this issue:

File: contracts/curation/ICuration.sol

/// @audit setDefaultReserveRatio(), setMinimumCurationDeposit(), setCurationTaxPercentage(), setCurationTokenMaster(), mint(), burn(), collect(), isCurated(), getCuratorSignal(), getCurationPoolSignal(), getCurationPoolTokens(), tokensToSignal(), signalToTokens(), curationTaxPercentage()
7:    interface ICuration {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/curation/ICuration.sol#L7

File: contracts/curation/IGraphCurationToken.sol

/// @audit initialize(), burnFrom(), mint()
7:    interface IGraphCurationToken is IERC20Upgradeable {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/curation/IGraphCurationToken.sol#L7

File: contracts/epochs/IEpochManager.sol

/// @audit setEpochLength(), runEpoch(), isCurrentEpochRun(), blockNum(), blockHash(), currentEpoch(), currentEpochBlock(), currentEpochBlockSinceStart(), epochsSince(), epochsSinceUpdate()
5:    interface IEpochManager {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/epochs/IEpochManager.sol#L5

File: contracts/gateway/BridgeEscrow.sol

/// @audit initialize(), approveAll(), revokeAll()
15:   contract BridgeEscrow is GraphUpgradeable, Managed {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/BridgeEscrow.sol#L15

File: contracts/gateway/GraphTokenGateway.sol

/// @audit setPauseGuardian(), setPaused(), paused()
14:   abstract contract GraphTokenGateway is GraphUpgradeable, Pausable, Managed, ITokenGateway {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/GraphTokenGateway.sol#L14

File: contracts/gateway/ICallhookReceiver.sol

/// @audit onTokenTransfer()
11:   interface ICallhookReceiver {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/ICallhookReceiver.sol#L11

File: contracts/gateway/L1GraphTokenGateway.sol

/// @audit initialize(), setArbitrumAddresses(), setL2TokenAddress(), setL2CounterpartAddress(), setEscrowAddress(), addToCallhookWhitelist(), removeFromCallhookWhitelist(), getOutboundCalldata()
21:   contract L1GraphTokenGateway is GraphTokenGateway, L1ArbitrumMessenger {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/L1GraphTokenGateway.sol#L21

File: contracts/governance/IController.sol

/// @audit getGovernor(), setContractProxy(), unsetContractProxy(), updateController(), getContractProxy(), setPartialPaused(), setPaused(), setPauseGuardian(), paused(), partialPaused()
5:    interface IController {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/IController.sol#L5

File: contracts/governance/Managed.sol

/// @audit syncAllContracts()
23:   contract Managed {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Managed.sol#L23

File: contracts/l2/gateway/L2GraphTokenGateway.sol

/// @audit initialize(), setL2Router(), setL1TokenAddress(), setL1CounterpartAddress(), outboundTransfer(), getOutboundCalldata()
23:   contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, ReentrancyGuardUpgradeable {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/gateway/L2GraphTokenGateway.sol#L23

File: contracts/l2/token/GraphTokenUpgradeable.sol

/// @audit addMinter(), removeMinter(), renounceMinter(), mint(), isMinter()
28:   contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgradeable {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/GraphTokenUpgradeable.sol#L28

File: contracts/l2/token/L2GraphToken.sol

/// @audit initialize(), setGateway(), setL1Address()
15:   contract L2GraphToken is GraphTokenUpgradeable, IArbToken {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/L2GraphToken.sol#L15

File: contracts/rewards/IRewardsManager.sol

/// @audit setIssuanceRate(), setMinimumSubgraphSignal(), setSubgraphAvailabilityOracle(), setDenied(), setDeniedMany(), isDenied(), getNewRewardsPerSignal(), getAccRewardsPerSignal(), getAccRewardsForSubgraph(), getAccRewardsPerAllocatedToken(), getRewards(), updateAccRewardsPerSignal(), takeRewards(), onSubgraphSignalUpdate(), onSubgraphAllocationUpdate()
5:    interface IRewardsManager {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/rewards/IRewardsManager.sol#L5

File: contracts/staking/IStaking.sol

/// @audit setMinimumIndexerStake(), setThawingPeriod(), setCurationPercentage(), setProtocolPercentage(), setChannelDisputeEpochs(), setMaxAllocationEpochs(), setRebateRatio(), setDelegationRatio(), setDelegationParameters(), setDelegationParametersCooldown(), setDelegationUnbondingPeriod(), setDelegationTaxPercentage(), setSlasher(), setAssetHolder(), setOperator(), isOperator(), stake(), stakeTo(), unstake(), slash(), withdraw(), setRewardsDestination(), delegate(), undelegate(), withdrawDelegated(), allocate(), allocateFrom(), closeAllocation(), closeAllocationMany(), closeAndAllocate(), collect(), claim(), claimMany(), hasStake(), getIndexerStakedTokens(), getIndexerCapacity(), getAllocation(), getAllocationState(), isAllocation(), getSubgraphAllocatedTokens(), getDelegation(), isDelegator()
8:    interface IStaking is IStakingData {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/staking/IStaking.sol#L8

File: contracts/token/IGraphToken.sol

/// @audit burn(), burnFrom(), mint(), addMinter(), removeMinter(), renounceMinter(), isMinter()
7:    interface IGraphToken is IERC20 {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/token/IGraphToken.sol#L7

File: contracts/upgrades/GraphProxyAdmin.sol

/// @audit getProxyImplementation(), getProxyPendingImplementation(), getProxyAdmin(), changeProxyAdmin(), upgrade(), acceptProxy(), acceptProxyAndCall()
17:   contract GraphProxyAdmin is Governed {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxyAdmin.sol#L17

File: contracts/upgrades/GraphProxy.sol

/// @audit implementation(), pendingImplementation(), setAdmin(), upgradeTo(), acceptUpgrade(), acceptUpgradeAndCall()
16:   contract GraphProxy is GraphProxyStorage {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxy.sol#L16

File: contracts/upgrades/GraphUpgradeable.sol

/// @audit acceptProxy(), acceptProxyAndCall()
11:   contract GraphUpgradeable {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphUpgradeable.sol#L11

File: contracts/upgrades/IGraphProxy.sol

/// @audit setAdmin(), implementation(), pendingImplementation(), upgradeTo(), acceptUpgrade(), acceptUpgradeAndCall()
5:    interface IGraphProxy {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/IGraphProxy.sol#L5

[G‑08] Using bools for storage incurs overhead

    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/58f635312aa21f947cae5f8578638a85aa2519f5/contracts/security/ReentrancyGuard.sol#L23-L27 Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas) for the extra SLOAD, and to avoid Gsset (20000 gas) when changing from false to true, after having been true in the past

There are 4 instances of this issue:

File: contracts/gateway/L1GraphTokenGateway.sol

35:       mapping(address => bool) public callhookWhitelist;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/L1GraphTokenGateway.sol#L35

File: contracts/governance/Pausable.sol

8:        bool internal _partialPaused;

10:       bool internal _paused;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Pausable.sol#L8

File: contracts/l2/token/GraphTokenUpgradeable.sol

51:       mapping(address => bool) private _minters;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/GraphTokenUpgradeable.sol#L51

[G‑09] Use a more recent version of solidity

Use a solidity version of at least 0.8.0 to get overflow protection without SafeMath Use a solidity version of at least 0.8.2 to get simple compiler automatic inlining Use a solidity version of at least 0.8.3 to get better struct packing and cheaper multiple storage reads Use a solidity version of at least 0.8.4 to get custom errors, which are cheaper at deployment than revert()/require() strings Use a solidity version of at least 0.8.10 to have external calls skip contract existence checks if the external call has a return value

There are 20 instances of this issue:

File: contracts/curation/ICuration.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/curation/ICuration.sol#L3

File: contracts/curation/IGraphCurationToken.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/curation/IGraphCurationToken.sol#L3

File: contracts/epochs/IEpochManager.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/epochs/IEpochManager.sol#L3

File: contracts/gateway/BridgeEscrow.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/BridgeEscrow.sol#L3

File: contracts/gateway/GraphTokenGateway.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/GraphTokenGateway.sol#L3

File: contracts/gateway/ICallhookReceiver.sol

9:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/ICallhookReceiver.sol#L9

File: contracts/gateway/L1GraphTokenGateway.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/L1GraphTokenGateway.sol#L3

File: contracts/governance/Governed.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Governed.sol#L3

File: contracts/governance/Managed.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Managed.sol#L3

File: contracts/governance/Pausable.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Pausable.sol#L3

File: contracts/l2/gateway/L2GraphTokenGateway.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/gateway/L2GraphTokenGateway.sol#L3

File: contracts/l2/token/GraphTokenUpgradeable.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/GraphTokenUpgradeable.sol#L3

File: contracts/l2/token/L2GraphToken.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/L2GraphToken.sol#L3

File: contracts/rewards/IRewardsManager.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/rewards/IRewardsManager.sol#L3

File: contracts/token/IGraphToken.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/token/IGraphToken.sol#L3

File: contracts/upgrades/GraphProxyAdmin.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxyAdmin.sol#L3

File: contracts/upgrades/GraphProxy.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxy.sol#L3

File: contracts/upgrades/GraphProxyStorage.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxyStorage.sol#L3

File: contracts/upgrades/GraphUpgradeable.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphUpgradeable.sol#L3

File: contracts/upgrades/IGraphProxy.sol

3:    pragma solidity ^0.7.6;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/IGraphProxy.sol#L3

[G‑10] Use a more recent version of solidity

Use a solidity version of at least 0.8.2 to get simple compiler automatic inlining Use a solidity version of at least 0.8.3 to get better struct packing and cheaper multiple storage reads Use a solidity version of at least 0.8.4 to get custom errors, which are cheaper at deployment than revert()/require() strings Use a solidity version of at least 0.8.10 to have external calls skip contract existence checks if the external call has a return value

There are 3 instances of this issue:

File: contracts/governance/IController.sol

3:    pragma solidity >=0.6.12 <0.8.0;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/IController.sol#L3

File: contracts/staking/IStakingData.sol

3:    pragma solidity >=0.6.12 <0.8.0;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/staking/IStakingData.sol#L3

File: contracts/staking/IStaking.sol

3:    pragma solidity >=0.6.12 <0.8.0;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/staking/IStaking.sol#L3

[G‑11] Using > 0 costs more gas than != 0 when used on a uint in a require() statement

This change saves 6 gas per instance. The optimization works until solidity version 0.8.13 where there is a regression in gas costs.

There are 3 instances of this issue:

File: contracts/gateway/L1GraphTokenGateway.sol

201:          require(_amount > 0, "INVALID_ZERO_AMOUNT");

217:                  require(maxSubmissionCost > 0, "NO_SUBMISSION_COST");

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/L1GraphTokenGateway.sol#L201

File: contracts/l2/gateway/L2GraphTokenGateway.sol

146:          require(_amount > 0, "INVALID_ZERO_AMOUNT");

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/gateway/L2GraphTokenGateway.sol#L146

[G‑12] Splitting require() statements that use && saves gas

See this issue which describes the fact that there is a larger deployment gas cost, but with enough runtime calls, the change ends up being cheaper by 3 gas

There are 3 instances of this issue:

File: contracts/gateway/L1GraphTokenGateway.sol

142:          require(_escrow != address(0) && Address.isContract(_escrow), "INVALID_ESCROW");

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/L1GraphTokenGateway.sol#L142

File: contracts/governance/Governed.sol

54            require(
55                pendingGovernor != address(0) && msg.sender == pendingGovernor,
56                "Caller must be pending governor"
57:           );

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Governed.sol#L54-L57

File: contracts/upgrades/GraphProxy.sol

142           require(
143               _pendingImplementation != address(0) && msg.sender == _pendingImplementation,
144               "Caller must be the pending implementation"
145:          );

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxy.sol#L142-L145

[G‑13] Don't compare boolean expressions to boolean literals

if (<x> == true) => if (<x>), if (<x> == false) => if (!<x>)

There is 1 instance of this issue:

File: contracts/gateway/L1GraphTokenGateway.sol

214:                      extraData.length == 0 || callhookWhitelist[msg.sender] == true,

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/L1GraphTokenGateway.sol#L214

[G‑14] Stack variable used as a cheaper cache for a state variable is only used once

If the variable is only accessed once, it's cheaper to use the state variable directly that one time, and save the 3 gas the extra stack assignment would spend

There are 4 instances of this issue:

File: contracts/governance/Governed.sol

43:           address oldPendingGovernor = pendingGovernor;

59:           address oldGovernor = governor;

60:           address oldPendingGovernor = pendingGovernor;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Governed.sol#L43

File: contracts/governance/Pausable.sol

56:           address oldPauseGuardian = pauseGuardian;

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Pausable.sol#L56

[G‑15] require() or revert() statements that check input arguments should be at the top of the function

Checks that involve constants should come before checks that involve state variables, function calls, and calculations. By doing these checks first, the function is able to revert before wasting a Gcoldsload (2100 gas*) in a function that may ultimately revert in the unhappy case.

There is 1 instance of this issue:

File: contracts/gateway/L1GraphTokenGateway.sol

/// @audit expensive op on line 199
201:          require(_amount > 0, "INVALID_ZERO_AMOUNT");

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/L1GraphTokenGateway.sol#L201

[G‑16] Use custom errors rather than revert()/require() strings to save gas

Custom errors are available from solidity version 0.8.4. Custom errors save ~50 gas each time they're hit by avoiding having to allocate and store the revert string. Not defining the strings also save deployment gas

There are 60 instances of this issue:

File: contracts/gateway/GraphTokenGateway.sol

19            require(
20                msg.sender == controller.getGovernor() || msg.sender == pauseGuardian,
21                "Only Governor or Guardian can call"
22:           );

31:           require(_newPauseGuardian != address(0), "PauseGuardian must be set");

40:           require(!_paused, "Paused (contract)");

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/GraphTokenGateway.sol#L19-L22

File: contracts/gateway/L1GraphTokenGateway.sol

74:           require(inbox != address(0), "INBOX_NOT_SET");

78:           require(msg.sender == address(bridge), "NOT_FROM_BRIDGE");

82:           require(l2ToL1Sender == l2Counterpart, "ONLY_COUNTERPART_GATEWAY");

110:          require(_inbox != address(0), "INVALID_INBOX");

111:          require(_l1Router != address(0), "INVALID_L1_ROUTER");

122:          require(_l2GRT != address(0), "INVALID_L2_GRT");

132:          require(_l2Counterpart != address(0), "INVALID_L2_COUNTERPART");

142:          require(_escrow != address(0) && Address.isContract(_escrow), "INVALID_ESCROW");

153:          require(_newWhitelisted != address(0), "INVALID_ADDRESS");

154:          require(!callhookWhitelist[_newWhitelisted], "ALREADY_WHITELISTED");

165:          require(_notWhitelisted != address(0), "INVALID_ADDRESS");

166:          require(callhookWhitelist[_notWhitelisted], "NOT_WHITELISTED");

200:          require(_l1Token == address(token), "TOKEN_NOT_GRT");

201:          require(_amount > 0, "INVALID_ZERO_AMOUNT");

202:          require(_to != address(0), "INVALID_DESTINATION");

213                   require(
214                       extraData.length == 0 || callhookWhitelist[msg.sender] == true,
215                       "CALL_HOOK_DATA_NOT_ALLOWED"
216:                  );

217:                  require(maxSubmissionCost > 0, "NO_SUBMISSION_COST");

224:                      require(msg.value >= expectedEth, "WRONG_ETH_VALUE");

271:          require(_l1Token == address(token), "TOKEN_NOT_GRT");

275:          require(_amount <= escrowBalance, "BRIDGE_OUT_OF_FUNDS");

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/L1GraphTokenGateway.sol#L74

File: contracts/governance/Governed.sol

24:           require(msg.sender == governor, "Only Governor can call");

41:           require(_newGovernor != address(0), "Governor must be set");

54            require(
55                pendingGovernor != address(0) && msg.sender == pendingGovernor,
56                "Caller must be pending governor"
57:           );

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Governed.sol#L24

File: contracts/governance/Managed.sol

44:           require(!controller.paused(), "Paused");

45:           require(!controller.partialPaused(), "Partial-paused");

49:           require(!controller.paused(), "Paused");

53:           require(msg.sender == controller.getGovernor(), "Caller must be Controller governor");

57:           require(msg.sender == address(controller), "Caller must be Controller");

104:          require(_controller != address(0), "Controller must be set");

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Managed.sol#L44

File: contracts/l2/gateway/L2GraphTokenGateway.sol

69            require(
70                msg.sender == AddressAliasHelper.applyL1ToL2Alias(l1Counterpart),
71                "ONLY_COUNTERPART_GATEWAY"
72:           );

98:           require(_l2Router != address(0), "INVALID_L2_ROUTER");

108:          require(_l1GRT != address(0), "INVALID_L1_GRT");

118:          require(_l1Counterpart != address(0), "INVALID_L1_COUNTERPART");

145:          require(_l1Token == l1GRT, "TOKEN_NOT_GRT");

146:          require(_amount > 0, "INVALID_ZERO_AMOUNT");

147:          require(msg.value == 0, "INVALID_NONZERO_VALUE");

148:          require(_to != address(0), "INVALID_DESTINATION");

153:          require(outboundCalldata.extraData.length == 0, "CALL_HOOK_DATA_NOT_ALLOWED");

233:          require(_l1Token == l1GRT, "TOKEN_NOT_GRT");

234:          require(msg.value == 0, "INVALID_NONZERO_VALUE");

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/gateway/L2GraphTokenGateway.sol#L69-L72

File: contracts/l2/token/GraphTokenUpgradeable.sol

60:           require(isMinter(msg.sender), "Only minter can call");

94:           require(_owner == recoveredAddress, "GRT: invalid permit");

95:           require(_deadline == 0 || block.timestamp <= _deadline, "GRT: expired permit");

106:          require(_account != address(0), "INVALID_MINTER");

115:          require(_minters[_account], "NOT_A_MINTER");

123:          require(_minters[msg.sender], "NOT_A_MINTER");

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/GraphTokenUpgradeable.sol#L60

File: contracts/l2/token/L2GraphToken.sol

36:           require(msg.sender == gateway, "NOT_GATEWAY");

49:           require(_owner != address(0), "Owner must be set");

60:           require(_gw != address(0), "INVALID_GATEWAY");

70:           require(_addr != address(0), "INVALID_L1_ADDRESS");

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/L2GraphToken.sol#L36

File: contracts/upgrades/GraphProxy.sol

105:          require(_newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");

141:          require(Address.isContract(_pendingImplementation), "Implementation must be a contract");

142           require(
143               _pendingImplementation != address(0) && msg.sender == _pendingImplementation,
144               "Caller must be the pending implementation"
145:          );

157:          require(msg.sender != _admin(), "Cannot fallback to proxy target");

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxy.sol#L105

File: contracts/upgrades/GraphProxyStorage.sol

62:           require(msg.sender == _admin(), "Caller must be admin");

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxyStorage.sol#L62

File: contracts/upgrades/GraphUpgradeable.sol

24:           require(msg.sender == _proxy.admin(), "Caller must be the proxy admin");

32:           require(msg.sender == _implementation(), "Caller must be the implementation");

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphUpgradeable.sol#L24

[G‑17] Functions guaranteed to revert when called by normal users can be marked payable

If a function modifier such as onlyOwner is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are CALLVALUE(2),DUP1(3),ISZERO(3),PUSH2(3),JUMPI(10),PUSH1(3),DUP1(3),REVERT(0),JUMPDEST(1),POP(2), which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost

There are 32 instances of this issue:

File: contracts/gateway/BridgeEscrow.sol

20:       function initialize(address _controller) external onlyImpl {

28:       function approveAll(address _spender) external onlyGovernor {

36:       function revokeAll(address _spender) external onlyGovernor {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/BridgeEscrow.sol#L20

File: contracts/gateway/GraphTokenGateway.sol

30:       function setPauseGuardian(address _newPauseGuardian) external onlyGovernor {

47:       function setPaused(bool _newPaused) external onlyGovernorOrGuardian {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/GraphTokenGateway.sol#L30

File: contracts/gateway/L1GraphTokenGateway.sol

99:       function initialize(address _controller) external onlyImpl {

109:      function setArbitrumAddresses(address _inbox, address _l1Router) external onlyGovernor {

121:      function setL2TokenAddress(address _l2GRT) external onlyGovernor {

131:      function setL2CounterpartAddress(address _l2Counterpart) external onlyGovernor {

141:      function setEscrowAddress(address _escrow) external onlyGovernor {

152:      function addToCallhookWhitelist(address _newWhitelisted) external onlyGovernor {

164:      function removeFromCallhookWhitelist(address _notWhitelisted) external onlyGovernor {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/gateway/L1GraphTokenGateway.sol#L99

File: contracts/governance/Governed.sol

40:       function transferOwnership(address _newGovernor) external onlyGovernor {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Governed.sol#L40

File: contracts/governance/Managed.sol

95:       function setController(address _controller) external onlyController {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/governance/Managed.sol#L95

File: contracts/l2/gateway/L2GraphTokenGateway.sol

87:       function initialize(address _controller) external onlyImpl {

97:       function setL2Router(address _l2Router) external onlyGovernor {

107:      function setL1TokenAddress(address _l1GRT) external onlyGovernor {

117:      function setL1CounterpartAddress(address _l1Counterpart) external onlyGovernor {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/gateway/L2GraphTokenGateway.sol#L87

File: contracts/l2/token/GraphTokenUpgradeable.sol

105:      function addMinter(address _account) external onlyGovernor {

114:      function removeMinter(address _account) external onlyGovernor {

132:      function mint(address _to, uint256 _amount) external onlyMinter {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/GraphTokenUpgradeable.sol#L105

File: contracts/l2/token/L2GraphToken.sol

48:       function initialize(address _owner) external onlyImpl {

59:       function setGateway(address _gw) external onlyGovernor {

69:       function setL1Address(address _addr) external onlyGovernor {

80:       function bridgeMint(address _account, uint256 _amount) external override onlyGateway {

90:       function bridgeBurn(address _account, uint256 _amount) external override onlyGateway {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/l2/token/L2GraphToken.sol#L48

File: contracts/upgrades/GraphProxyAdmin.sol

68:       function changeProxyAdmin(IGraphProxy _proxy, address _newAdmin) public onlyGovernor {

77:       function upgrade(IGraphProxy _proxy, address _implementation) public onlyGovernor {

86:       function acceptProxy(GraphUpgradeable _implementation, IGraphProxy _proxy) public onlyGovernor {

96        function acceptProxyAndCall(
97            GraphUpgradeable _implementation,
98            IGraphProxy _proxy,
99            bytes calldata _data
100:      ) external onlyGovernor {

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphProxyAdmin.sol#L68

File: contracts/upgrades/GraphUpgradeable.sol

50:       function acceptProxy(IGraphProxy _proxy) external onlyProxyAdmin(_proxy) {

59        function acceptProxyAndCall(IGraphProxy _proxy, bytes calldata _data)
60            external
61:           onlyProxyAdmin(_proxy)

https://github.com/code-423n4/2022-10-thegraph/blob/48e7c7cf641847e07ba07e01176cb17ba8ad6432/contracts/upgrades/GraphUpgradeable.sol#L50

#0 - tmigone

2022-10-21T20:06:51Z

We've found this submission to be of high quality.

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax Ā© 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter