Platform: Code4rena
Start Date: 01/12/2022
Pot Size: $26,900 USDC
Total HM: 3
Participants: 19
Period: 4 days
Judge: GalloDaSballo
Id: 188
League: ETH
Rank: 18/19
Findings: 1
Award: $53.42
🌟 Selected for report: 0
🚀 Solo Findings: 0
53.4153 USDC - $53.42
The processCalls()
function in the EthereumToArbitrumRelayer
contract does not reset the value in the relayed
mapping to false
after processing the calls.
Doing so provides a gas refund, and so implementing this results in a gas saving.
Simply alter the processCalls()
function slightly as follows:
function processCalls(...) external payable returns (uint256) { bytes32 _txHash = _getTxHash(_nonce, _calls, _sender, _gasLimit); require(relayed[_txHash], "Relayer/calls-not-relayed"); delete relayed[_txHash]; // ... return _ticketID; }
This results in a gas saving of 1319 (16%):
Function Name | min | avg | median | max | # calls |
---|---|---|---|---|---|
processCalls | 8066 | 8066 | 8066 | 8066 | 1 |
processCallsCheaper | 6744 | 6744 | 6744 | 6744 | 1 |
Instances: 1
Use unchecked{ ++nonce }
instead of nonce++
. The automated gas report mentioned that ++nonce
is cheaper than nonce++
, however did not mention to use unchecked{}
.
This is safe because the operation cannot overflow.
Gas saving per call of relayCalls()
:
EthereumToArbitrumRelayer.sol
- 130 gasEthereumToOptimismRelayer.sol
- 124 gasEthereumToPolygonRelayer.sol
- 146 gasInstances: 3
https://github.com/pooltogether/ERC5164/blob/5647bd84f2a6d1a37f41394874d567e45a97bf48/src/ethereum-arbitrum/EthereumToArbitrumRelayer.sol#L78 https://github.com/pooltogether/ERC5164/blob/5647bd84f2a6d1a37f41394874d567e45a97bf48/src/ethereum-optimism/EthereumToOptimismRelayer.sol#L60 https://github.com/pooltogether/ERC5164/blob/5647bd84f2a6d1a37f41394874d567e45a97bf48/src/ethereum-polygon/EthereumToPolygonRelayer.sol#L56
msg.sender
variablefunction relayCalls(...) external payable returns (uint256) { // ... address _msgSender = msg.sender; _sendMessageToChild(abi.encode(_nonce, msg.sender, _calls)); emit RelayedCalls(_nonce, msg.sender, _calls, _gasLimit); return _nonce; }
Gas saving per call of relayCalls()
:
EthereumToArbitrumRelayer.sol
- 14 gasEthereumToOptimismRelayer.sol
- 11 gasEthereumToPolygonRelayer.sol
- 36 gasInstances: 3
https://github.com/pooltogether/ERC5164/blob/5647bd84f2a6d1a37f41394874d567e45a97bf48/src/ethereum-arbitrum/EthereumToArbitrumRelayer.sol#L82-L84 https://github.com/pooltogether/ERC5164/blob/5647bd84f2a6d1a37f41394874d567e45a97bf48/src/ethereum-optimism/EthereumToOptimismRelayer.sol#L64-L75 https://github.com/pooltogether/ERC5164/blob/5647bd84f2a6d1a37f41394874d567e45a97bf48/src/ethereum-polygon/EthereumToPolygonRelayer.sol#L60-L62
Gas saving per call to view executed[nonce]
:
EthereumToArbitrumExecutor.sol
- 77 gasEthereumToOptimismExecutor.sol
- 77 gasEthereumToPolygonExecutor.sol
- 99 gasInstances: 4
https://github.com/pooltogether/ERC5164/blob/5647bd84f2a6d1a37f41394874d567e45a97bf48/src/ethereum-arbitrum/EthereumToArbitrumExecutor.sol#L26 https://github.com/pooltogether/ERC5164/blob/5647bd84f2a6d1a37f41394874d567e45a97bf48/src/ethereum-optimism/EthereumToOptimismExecutor.sol#L29 https://github.com/pooltogether/ERC5164/blob/5647bd84f2a6d1a37f41394874d567e45a97bf48/src/ethereum-polygon/EthereumToPolygonExecutor.sol#L31 https://github.com/pooltogether/ERC5164/blob/5647bd84f2a6d1a37f41394874d567e45a97bf48/src/ethereum-arbitrum/EthereumToArbitrumRelayer.sol#L47
unchecked { _callIndex++ }
in for loop body in CallLib.executeCalls()
For loop index cannot overflow and so it is safe to use unchecked to save gas.
Also, replace _callIndex < _callsLength
with _callIndex != _callsLength
.
Using !=
instead of <
was mentioned in the automated report, however this instance was missed.
function executeCalls(...) internal { //... for (uint256 _callIndex; _callIndex != _callsLength; ) { //... unchecked{ ++_callIndex; } } }
Gas saved: - 69 gas per iteration
Deployment saving - 47 gas
Instances: 1
require()
statements in constructorThe team has mentioned the willingness to re-deploy the contracts if setExecutor()
or setRelayer()
are front-run.
Similarly, they could do the same if they mistakenly pass the zero address/zero uint to the constructor.
Removing the require()
statement will save gas on deployment and sacrifice very little (misclicking constructor arguments is very unlikely).
Alternatively, the checks could be performed off chain prior to deployment.
However if they are to be kept, replacing them with custom errors would also save gas.
Deployment gas savings:
EthereumToArbitrumRelayer.sol
- 171EthereumToOptimismExecutor.sol
- 98EthereumToOptimismRelayer.sol
- 171EthereumToPolygonRelayer.sol
- 86Instances: 4
https://github.com/pooltogether/ERC5164/blob/851d1fe82f301ebe0e35e6f1f98bf8113135a9d2/src/ethereum-arbitrum/EthereumToArbitrumRelayer.sol#L57-L58 https://github.com/pooltogether/ERC5164/blob/851d1fe82f301ebe0e35e6f1f98bf8113135a9d2/src/ethereum-optimism/EthereumToOptimismRelayer.sol#L39-L40 https://github.com/pooltogether/ERC5164/blob/851d1fe82f301ebe0e35e6f1f98bf8113135a9d2/src/ethereum-optimism/EthereumToOptimismExecutor.sol#L38 https://github.com/pooltogether/ERC5164/blob/851d1fe82f301ebe0e35e6f1f98bf8113135a9d2/src/ethereum-polygon/EthereumToPolygonRelayer.sol#L38
nonce
to 1
instead of 0
to save gas on the first callIncrementing from zero to non-zero is more expensive than from non-zero to non-zero.
Gas saving on first call to relayCalls()
:
EthereumToArbitrumExecutor.sol
- 17100 gasEthereumToOptimismExecutor.sol
- 21900 gasEthereumToPolygonExecutor.sol
- 21900 gasInstances: 3
https://github.com/pooltogether/ERC5164/blob/851d1fe82f301ebe0e35e6f1f98bf8113135a9d2/src/ethereum-arbitrum/EthereumToArbitrumRelayer.sol#L40 https://github.com/pooltogether/ERC5164/blob/851d1fe82f301ebe0e35e6f1f98bf8113135a9d2/src/ethereum-optimism/EthereumToOptimismRelayer.sol#L29 https://github.com/pooltogether/ERC5164/blob/851d1fe82f301ebe0e35e6f1f98bf8113135a9d2/src/ethereum-polygon/EthereumToPolygonRelayer.sol#L23
#0 - GalloDaSballo
2022-12-26T21:31:26Z
Accepted benchmark
1319
Rest will save 100 gas at most (ignoring setting to 1 as ultimately it's one off)
1419
#1 - c4-judge
2022-12-26T23:13:36Z
GalloDaSballo marked the issue as grade-b