Cudos contest - IllIllI's results

Decentralised cloud computing for Web3.

General Information

Platform: Code4rena

Start Date: 03/05/2022

Pot Size: $75,000 USDC

Total HM: 6

Participants: 55

Period: 7 days

Judge: Albert Chon

Total Solo HM: 2

Id: 116

League: COSMOS

Cudos

Findings Distribution

Researcher Performance

Rank: 5/55

Findings: 3

Award: $5,500.94

🌟 Selected for report: 1

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: p_crypt0

Also found by: 0x1337, GermanKuber, IllIllI, WatchPug, csanuragjain, danb, dirk_y, kirk-baird

Labels

bug
duplicate
2 (Med Risk)

Awards

502.4722 USDC - $502.47

External Links

Lines of code

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L632-L638

Vulnerability details

The Cudos Network is a special-purpose blockchain designed to provide high-performance, trustless, and permissionless cloud computing for all.

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/README.md?plain=1#L14

To be considered trustless, both the incentives and the code must be aligned to prevent the possibility of maliciousness, especially by actors of the system given specific powers.

Impact

Administrators are able to rug all funds in the gravity bridge, which requires a lot of trust

Proof of Concept

File: solidity/contracts/Gravity.sol   #1

632      function withdrawERC20(
633         address _tokenAddress) 
634         external {
635         require(cudosAccessControls.hasAdminRole(msg.sender), "Recipient is not an admin");
636         uint256 totalBalance = IERC20(_tokenAddress).balanceOf(address(this));
637         IERC20(_tokenAddress).safeTransfer(msg.sender , totalBalance);
638      }

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L632-L638

If this function were able to withdraw to a pre-defined address, or the function required a specific amount to be specified, then under the right circumstances, maybe it would be ok, but the fact that it goes directly to the message sender, and only the full balance can be transferred, and the bridge's token can be taken, makes it seem like this is just a rug vector waiting to be applied. This amount of privilege is excessive.

Tools Used

Code inspection

Only allow withdrawal to a multisig after a timelock has expired, and only of specific amounts. Also, consider making the bridge pausable to mitigate attacks

#0 - mlukanova

2022-05-11T17:04:40Z

Duplicate of #14

Awards

1110.8314 USDC - $1,110.83

Labels

bug
QA (Quality Assurance)

External Links

Low Risk Issues

Summary

TitleInstances
1Validator signing address of zero not rejected, allowing anyone to sign1
2Unbounded loops may run out of gas1
3deployERC20() does not have a reentrancy guard1
4Comment does not match the behavior of the code2
5abi.encodePacked() should not be used with dynamic types when passing the result to a hash function such as keccak256()1

Total: 6 instances over 5 classes (see lower down in this report for the summary table of the Non-critical findings)

1. Validator signing address of zero not rejected, allowing anyone to sign

ecrecover() returns 0 when the signature does not match. If the validators approve a valset including an address of 0, then anyone will be able to sign messages for that signer, since invalid sigatures will return zero, and will match the zero address.

File: solidity/contracts/Gravity.sol   #1

185  		return _signer == ecrecover(messageDigest, _v, _r, _s);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L185

2. Unbounded loops may run out of gas

The call to ecrecover() costs 3000 gas per call, and if there are too many validators, the update of the validator set may pass, but large batches will fail

File: solidity/contracts/Gravity.sol   #1

219  	function checkValidatorSignatures(
220  		// The current validator set and their powers
221  		address[] memory _currentValidators,
222  		uint256[] memory _currentPowers,
223  		// The current validator's signatures
224  		uint8[] memory _v,
225  		bytes32[] memory _r,
226  		bytes32[] memory _s,
227  		// This is what we are checking they have signed
228  		bytes32 _theHash,
229  		uint256 _powerThreshold
230  	) private pure {
231  		uint256 cumulativePower = 0;
232  
233  		for (uint256 i = 0; i < _currentValidators.length; i++) {
234  			// If v is set to 0, this signifies that it was not possible to get a signature from this validator and we skip evaluation
235  			// (In a valid signature, it is either 27 or 28)
236  			if (_v[i] != 0) {
237  				// Check that the current validator has signed off on the hash
238  				require(
239  					verifySig(_currentValidators[i], _theHash, _v[i], _r[i], _s[i]),

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L219-L239

3. deployERC20() does not have a reentrancy guard

deployERC20() increments the state_lastEventNonce so it's possible for the nonce to be incremented by a transfer hook. I don't see a way to exploit this given the code in scope, but perhaps some other area relies on event nonces happening in a specific order in relation to the other events.

File: solidity/contracts/Gravity.sol   #1

611  	function deployERC20(
612  		string memory _cosmosDenom,
613  		string memory _name,
614  		string memory _symbol,
615  		uint8 _decimals
616  	) public {
617  		// Deploy an ERC20 with entire supply granted to Gravity.sol
618  		CosmosERC20 erc20 = new CosmosERC20(address(this), _name, _symbol, _decimals);
619  
620  		// Fire an event to let the Cosmos module know
621  		state_lastEventNonce = state_lastEventNonce.add(1);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L611-L621

4. Comment does not match the behavior of the code

Both of the functions below have require(isOrchestrator(msg.sender)), and orchestrators are the first signer, so not just anyone can call these

File: solidity/contracts/Gravity.sol   #1

362  	// Anyone can call this function, but they must supply valid signatures of state_powerThreshold of the current valset over
363  	// the batch.
364  	function submitBatch (

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L362-L364

File: solidity/contracts/Gravity.sol   #2

274  	// Anyone can call this function, but they must supply valid signatures of state_powerThreshold of the current valset over
275  	// the new valset.
276  	function updateValset(

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L274-L276

5. 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.

File: solidity/contracts/Gravity.sol   #1

182   		bytes32 messageDigest = keccak256(
183   			abi.encodePacked("\x19Ethereum Signed Message:\n32", _theHash)
184   		);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L182-L184

Non-critical Issues

Summary

TitleInstances
1Best practice is to prevent signature malleability1
2Inconsistent variable naming convention2
3Inconsistent tabs vs spaces3
4if( should be if ( to match other lines in the file1
5Misleading function name1
6Avoid the use of sensitive terms in favor of neutral ones4
7public functions not called by the contract should be declared external instead10
82**<n> - 1 should be re-written as type(uint<n>).max1
9constants should be defined rather than using magic numbers3
10Use a more recent version of solidity1
11Variable names that consist of all capital letters should be reserved for const/immutable variables1
12Non-library/interface files should use fixed compiler versions, not floating ones2
13Typos1
14File does not contain an SPDX Identifier2
15File is missing NatSpec2
16Event is missing indexed fields5
17Consider making the bridge 'pausable'1

Total: 41 instances over 17 classes

1. Best practice is to prevent signature malleability

Use OpenZeppelin's ECDSA contract rather than calling ecrecover() directly

File: solidity/contracts/Gravity.sol   #1

182  		bytes32 messageDigest = keccak256(
183  			abi.encodePacked("\x19Ethereum Signed Message:
32", _theHash)
184  		);
185  		return _signer == ecrecover(messageDigest, _v, _r, _s);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L182-L185

2. Inconsistent variable naming convention

Most state variables use the state_ prefix in their variable name. There are some that don't. Use the prefix everywhere, and manually add public getters where necessary

File: solidity/contracts/Gravity.sol   #1

63  	CudosAccessControls public cudosAccessControls;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L63

File: solidity/contracts/Gravity.sol   #2

65  	mapping(address => bool) public whitelisted;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L65

3. Inconsistent tabs vs spaces

Most lines use tabs, but some use spaces, which leads to alignment issues

File: solidity/contracts/Gravity.sol   #1

128  		 for (uint256 i = 0; i < _users.length; i++) {
129              require(
130                  _users[i] != address(0),
131                  "User is the zero address"
132              );
133              whitelisted[_users[i]] = _isWhitelisted;
134          }

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L128-L134

File: solidity/contracts/Gravity.sol   #2

117  		 require(
118              whitelisted[msg.sender] || cudosAccessControls.hasAdminRole(msg.sender) ,
119              "The caller is not whitelisted for this operation"
120          );
121  		_;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L117-L121

File: solidity/contracts/Gravity.sol   #3

647  		address[] memory _validators,
648      uint256[] memory _powers,
649  		CudosAccessControls _cudosAccessControls

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L647-L649

4. if( should be if ( to match other lines in the file

File: solidity/contracts/Gravity.sol   #1

264  			if(_newValset.validators[i] == _sender) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L264

5. Misleading function name

onlyWhitelisted() should be onlyWhitelistedOrAdmin()

File: solidity/contracts/Gravity.sol   #1

116  	modifier onlyWhitelisted() {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L116

6. Avoid the use of sensitive terms in favor of neutral ones

Use allowlist rather than whitelist

File: solidity/contracts/Gravity.sol   #1

116  	modifier onlyWhitelisted() {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L116

File: solidity/contracts/Gravity.sol   #2

65  	mapping(address => bool) public whitelisted;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L65

File: solidity/contracts/Gravity.sol   #3

109  	event WhitelistedStatusModified(

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L109

File: solidity/contracts/Gravity.sol   #4

124  	function manageWhitelist(

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L124

7. 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.

File: solidity/contracts/Gravity.sol   #1

124   	function manageWhitelist(
125   		address[] memory _users,
126   		bool _isWhitelisted
127   		) public onlyWhitelisted {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L124-L127

File: solidity/contracts/Gravity.sol   #2

140   	function testMakeCheckpoint(ValsetArgs memory _valsetArgs, bytes32 _gravityId) public pure {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L140

File: solidity/contracts/Gravity.sol   #3

144   	function testCheckValidatorSignatures(
145   		address[] memory _currentValidators,
146   		uint256[] memory _currentPowers,
147   		uint8[] memory _v,
148   		bytes32[] memory _r,
149   		bytes32[] memory _s,
150   		bytes32 _theHash,
151   		uint256 _powerThreshold

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L144-L151

File: solidity/contracts/Gravity.sol   #4

166   	function lastBatchNonce(address _erc20Address) public view returns (uint256) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L166

File: solidity/contracts/Gravity.sol   #5

170   	function lastLogicCallNonce(bytes32 _invalidation_id) public view returns (uint256) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L170

File: solidity/contracts/Gravity.sol   #6

276   	function updateValset(
277   		// The new version of the validator set
278   		ValsetArgs memory _newValset,
279   		// The current validators that approve the change
280   		ValsetArgs memory _currentValset,
281   		// These are arrays of the parts of the current validator's signatures
282   		uint8[] memory _v,
283   		bytes32[] memory _r,
284   		bytes32[] memory _s
285   	) public nonReentrant {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L276-L285

File: solidity/contracts/Gravity.sol   #7

364   	function submitBatch (
365   		// The validators that approve the batch
366   		ValsetArgs memory _currentValset,
367   		// These are arrays of the parts of the validators signatures
368   		uint8[] memory _v,
369   		bytes32[] memory _r,
370   		bytes32[] memory _s,
371   		// The batch of transactions
372   		uint256[] memory _amounts,
373   		address[] memory _destinations,
374   		uint256[] memory _fees,
375   		uint256 _batchNonce,
376   		address _tokenContract,
377   		// a block height beyond which this batch is not valid
378   		// used to provide a fee-free timeout
379   		uint256 _batchTimeout
380   	) public nonReentrant {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L364-L380

File: solidity/contracts/Gravity.sol   #8

479   	function submitLogicCall(
480   		// The validators that approve the call
481   		ValsetArgs memory _currentValset,
482   		// These are arrays of the parts of the validators signatures
483   		uint8[] memory _v,
484   		bytes32[] memory _r,
485   		bytes32[] memory _s,
486   		LogicCallArgs memory _args
487   	) public nonReentrant {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L479-L487

File: solidity/contracts/Gravity.sol   #9

595   	function sendToCosmos(
596   		address _tokenContract,
597   		bytes32 _destination,
598   		uint256 _amount
599   	) public nonReentrant  {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L595-L599

File: solidity/contracts/Gravity.sol   #10

611   	function deployERC20(
612   		string memory _cosmosDenom,
613   		string memory _name,
614   		string memory _symbol,
615   		uint8 _decimals

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L611-L615

8. 2**<n> - 1 should be re-written as type(uint<n>).max

Earlier versions of solidity can use uint<n>(-1) instead. Expressions not including the - 1 can often be re-written to accomodate the change (e.g. by using a > rather than a >=, which will also save some gas)

File: solidity/contracts/CosmosToken.sol   #1

5   	uint256 MAX_UINT = 2**256 - 1;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/CosmosToken.sol#L5

9. constants should be defined rather than using magic numbers

File: solidity/contracts/Gravity.sol   #1

202   		bytes32 methodName = 0x636865636b706f696e7400000000000000000000000000000000000000000000;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L202

File: solidity/contracts/Gravity.sol   #2

433   						0x7472616e73616374696f6e426174636800000000000000000000000000000000,

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L433

File: solidity/contracts/Gravity.sol   #3

535   				0x6c6f67696343616c6c0000000000000000000000000000000000000000000000,

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L535

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>)

File: solidity/contracts/Gravity.sol   #1

1   pragma solidity ^0.6.6;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L1

11. Variable names that consist of all capital letters should be reserved for const/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).

File: solidity/contracts/CosmosToken.sol   #1

5   	uint256 MAX_UINT = 2**256 - 1;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/CosmosToken.sol#L5

12. Non-library/interface files should use fixed compiler versions, not floating ones

File: solidity/contracts/CosmosToken.sol   #1

1   pragma solidity ^0.6.6;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/CosmosToken.sol#L1

File: solidity/contracts/Gravity.sol   #2

1   pragma solidity ^0.6.6;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L1

13. Typos

File: solidity/contracts/Gravity.sol   #1

564   		// Update invaldiation nonce

invaldiation https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L564

14. File does not contain an SPDX Identifier

File: solidity/contracts/CosmosToken.sol   #1

0   pragma solidity ^0.6.6;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/CosmosToken.sol#L0

File: solidity/contracts/Gravity.sol   #2

0   pragma solidity ^0.6.6;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L0

15. File is missing NatSpec

File: solidity/contracts/CosmosToken.sol (various lines)   #1

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/CosmosToken.sol

File: solidity/contracts/Gravity.sol (various lines)   #2

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol

16. Event is missing indexed fields

Each event should use three indexed fields if there are three or more fields

File: solidity/contracts/Gravity.sol   #1

73   	event TransactionBatchExecutedEvent(
74   		uint256 indexed _batchNonce,
75   		address indexed _token,
76   		uint256 _eventNonce
77   	);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L73-L77

File: solidity/contracts/Gravity.sol   #2

85   	event ERC20DeployedEvent(
86   		// FYI: Can't index on a string without doing a bunch of weird stuff
87   		string _cosmosDenom,
88   		address indexed _tokenContract,
89   		string _name,
90   		string _symbol,
91   		uint8 _decimals,
92   		uint256 _eventNonce
93   	);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L85-L93

File: solidity/contracts/Gravity.sol   #3

94   	event ValsetUpdatedEvent(
95   		uint256 indexed _newValsetNonce,
96   		uint256 _eventNonce,
97   		uint256 _rewardAmount,
98   		address _rewardToken,
99   		address[] _validators,
100   		uint256[] _powers
101   	);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L94-L101

File: solidity/contracts/Gravity.sol   #4

102   	event LogicCallEvent(
103   		bytes32 _invalidationId,
104   		uint256 _invalidationNonce,
105   		bytes _returnData,
106   		uint256 _eventNonce
107   	);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L102-L107

File: solidity/contracts/Gravity.sol   #5

109   	event WhitelistedStatusModified(
110   		address _sender,
111   		address[] _users,
112   		bool _isWhitelisted
113   	);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L109-L113

17. Consider making the bridge 'pausable'

Having this ability would help to mitigate attacks and would ameleorate the need for this withdrawERC20() to be all-or-nothing

File: solidity/contracts/Gravity.sol   #1

632  	function withdrawERC20(
633  		address _tokenAddress) 
634  		external {
635  		require(cudosAccessControls.hasAdminRole(msg.sender), "Recipient is not an admin");
636  		uint256 totalBalance = IERC20(_tokenAddress).balanceOf(address(this));
637  		IERC20(_tokenAddress).safeTransfer(msg.sender , totalBalance);
638  	}

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L632-L638

#0 - V-Staykov

2022-05-10T15:22:31Z

This is a particularly high quality

Awards

385.8235 USDC - $385.82

Labels

bug
G (Gas Optimization)

External Links

Gas Optimizations

Summary

TitleInstances
1State variables only set in the constructor should be declared immutable3
2State variables should be cached in stack variables rather than re-reading them from storage8
3<array>.length should not be looked up in every loop of a for-loop7
4require()/revert() strings longer than 32 bytes cost extra gas16
5Using bools for storage incurs overhead1
6Use a more recent version of solidity2
7It costs more gas to initialize variables to zero than to let the default of zero be applied9
8++i costs less gas than ++i, especially when it's used in for-loops (--i/i-- too)7
9Splitting require() statements that use && saves gas4
10Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead4
11Duplicated require()/revert() checks should be refactored to a modifier or function4
12Functions guaranteed to revert when called by normal users can be marked payable1
13public functions not called by the contract should be declared external instead10
14Return from function rather than breaking out of loop1
15require() or revert() statements that check input arguments should be at the top of the function3
16Remove test code to save deployment gas1

Total: 81 instances over 16 classes

1. State variables only set in the constructor should be declared immutable

Avoids a Gsset (20000 gas) in the constructor, and replaces each Gwarmacces (100 gas) with a PUSH32 (3 gas).

File: solidity/contracts/Gravity.sol   #1

60   	bytes32 public state_gravityId;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L60

File: solidity/contracts/Gravity.sol   #2

61   	uint256 public state_powerThreshold;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L61

File: solidity/contracts/Gravity.sol   #3

63   	CudosAccessControls public cudosAccessControls;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L63

2. 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 will replace each Gwarmaccess (100 gas) with a much cheaper stack read. Less obvious fixes/optimizations include having local storage variables of mappings within state variable mappings or mappings within state variable structs, having local storage variables of structs within mappings, having local memory caches of state variable structs, or having local caches of state variable contracts/addresses.

File: solidity/contracts/Gravity.sol   #1

352   			state_lastEventNonce,

state_lastEventNonce https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L352

File: solidity/contracts/Gravity.sol   #2

466   			emit TransactionBatchExecutedEvent(_batchNonce, _tokenContract, state_lastEventNonce);

state_lastEventNonce https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L466

File: solidity/contracts/Gravity.sol   #3

590   				state_lastEventNonce

state_lastEventNonce https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L590

File: solidity/contracts/Gravity.sol   #4

607   			state_lastEventNonce

state_lastEventNonce https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L607

File: solidity/contracts/Gravity.sol   #5

628   			state_lastEventNonce

state_lastEventNonce https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L628

File: solidity/contracts/Gravity.sol   #6

321   		bytes32 newCheckpoint = makeCheckpoint(_newValset, state_gravityId);

state_gravityId https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L321

File: solidity/contracts/Gravity.sol   #7

431   						state_gravityId,

state_gravityId https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L431

File: solidity/contracts/Gravity.sol   #8

533   				state_gravityId,

state_gravityId https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L533

3. <array>.length should not be looked up in every loop of a for-loop

The overheads outlined below are PER LOOP, excluding the first loop

  • storage arrays incur a Gwarmaccess (100 gas)
  • memory arrays use MLOAD (3 gas)
  • calldata arrays use CALLDATALOAD (3 gas)

Caching the length changes each of these to a DUP<N> (3 gas), and gets rid of the extra DUP<N> needed to store the stack offset

File: solidity/contracts/Gravity.sol   #1

128   		 for (uint256 i = 0; i < _users.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L128

File: solidity/contracts/Gravity.sol   #2

233   		for (uint256 i = 0; i < _currentValidators.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L233

File: solidity/contracts/Gravity.sol   #3

263   		for (uint256 i = 0; i < _newValset.validators.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L263

File: solidity/contracts/Gravity.sol   #4

453   				for (uint256 i = 0; i < _amounts.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L453

File: solidity/contracts/Gravity.sol   #5

568   		for (uint256 i = 0; i < _args.transferAmounts.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L568

File: solidity/contracts/Gravity.sol   #6

579   		for (uint256 i = 0; i < _args.feeAmounts.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L579

File: solidity/contracts/Gravity.sol   #7

660   		for (uint256 i = 0; i < _powers.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L660

4. require()/revert() strings longer than 32 bytes cost extra gas

File: solidity/contracts/Gravity.sol   #1

117   		 require(
118               whitelisted[msg.sender] || cudosAccessControls.hasAdminRole(msg.sender) ,
119               "The caller is not whitelisted for this operation"
120           );

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L117-L120

File: solidity/contracts/Gravity.sol   #2

238   				require(
239   					verifySig(_currentValidators[i], _theHash, _v[i], _r[i], _s[i]),
240   					"Validator signature does not match."
241   				);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L238-L241

File: solidity/contracts/Gravity.sol   #3

254   		require(
255   			cumulativePower > _powerThreshold,
256   			"Submitted validator set signatures do not have enough power."
257   		);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L254-L257

File: solidity/contracts/Gravity.sol   #4

289   		require(
290   			_newValset.valsetNonce > _currentValset.valsetNonce,
291   			"New valset nonce must be greater than the current nonce"
292   		);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L289-L292

File: solidity/contracts/Gravity.sol   #5

310   		require(
311   			makeCheckpoint(_currentValset, state_gravityId) == state_lastValsetCheckpoint,
312   			"Supplied current validators and powers do not match checkpoint."
313   		);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L310-L313

File: solidity/contracts/Gravity.sol   #6

315   		require(
316   			isOrchestrator(_currentValset, msg.sender),
317   			"The sender of the transaction is not validated orchestrator"
318   		);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L315-L318

File: solidity/contracts/Gravity.sol   #7

384   			require(
385   				state_lastBatchNonces[_tokenContract] < _batchNonce,
386   				"New batch nonce must be greater than the current nonce"
387   			);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L384-L387

File: solidity/contracts/Gravity.sol   #8

390   			require(
391   				block.number < _batchTimeout,
392   				"Batch timeout must be greater than the current block height"
393   			);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L390-L393

File: solidity/contracts/Gravity.sol   #9

405   			require(
406   				makeCheckpoint(_currentValset, state_gravityId) == state_lastValsetCheckpoint,
407   				"Supplied current validators and powers do not match checkpoint."
408   			);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L405-L408

File: solidity/contracts/Gravity.sol   #10

416   			require(
417   				isOrchestrator(_currentValset, msg.sender),
418   				"The sender of the transaction is not validated orchestrator"
419   			);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L416-L419

File: solidity/contracts/Gravity.sol   #11

494   			require(
495   				state_invalidationMapping[_args.invalidationId] < _args.invalidationNonce,
496   				"New invalidation nonce must be greater than the current nonce"
497   			);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L494-L497

File: solidity/contracts/Gravity.sol   #12

509   			require(
510   				makeCheckpoint(_currentValset, state_gravityId) == state_lastValsetCheckpoint,
511   				"Supplied current validators and powers do not match checkpoint."
512   			);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L509-L512

File: solidity/contracts/Gravity.sol   #13

515   			require(
516   				_args.transferAmounts.length == _args.transferTokenContracts.length,
517   				"Malformed list of token transfers"
518   			);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L515-L518

File: solidity/contracts/Gravity.sol   #14

525   			require(
526   				isOrchestrator(_currentValset, msg.sender),
527   				"The sender of the transaction is not validated orchestrator"
528   			);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L525-L528

File: solidity/contracts/Gravity.sol   #15

655   		require(address(_cudosAccessControls) != address(0), "Access control contract address is incorrect");

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L655

File: solidity/contracts/Gravity.sol   #16

666   		require(
667   			cumulativePower > _powerThreshold,
668   			"Submitted validator set signatures do not have enough power."
669   		);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L666-L669

5. 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

File: solidity/contracts/Gravity.sol   #1

65   	mapping(address => bool) public whitelisted;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L65

6. 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 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

File: solidity/contracts/CosmosToken.sol   #1

1   pragma solidity ^0.6.6;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/CosmosToken.sol#L1

File: solidity/contracts/Gravity.sol   #2

1   pragma solidity ^0.6.6;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L1

7. It costs more gas to initialize variables to zero than to let the default of zero be applied

File: solidity/contracts/Gravity.sol   #1

128   		 for (uint256 i = 0; i < _users.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L128

File: solidity/contracts/Gravity.sol   #2

231   		uint256 cumulativePower = 0;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L231

File: solidity/contracts/Gravity.sol   #3

233   		for (uint256 i = 0; i < _currentValidators.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L233

File: solidity/contracts/Gravity.sol   #4

263   		for (uint256 i = 0; i < _newValset.validators.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L263

File: solidity/contracts/Gravity.sol   #5

453   				for (uint256 i = 0; i < _amounts.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L453

File: solidity/contracts/Gravity.sol   #6

568   		for (uint256 i = 0; i < _args.transferAmounts.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L568

File: solidity/contracts/Gravity.sol   #7

579   		for (uint256 i = 0; i < _args.feeAmounts.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L579

File: solidity/contracts/Gravity.sol   #8

659   		uint256 cumulativePower = 0;

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L659

File: solidity/contracts/Gravity.sol   #9

660   		for (uint256 i = 0; i < _powers.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L660

8. ++i costs less gas than ++i, especially when it's used in for-loops (--i/i-- too)

Saves 6 gas PER LOOP

File: solidity/contracts/Gravity.sol   #1

128   		 for (uint256 i = 0; i < _users.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L128

File: solidity/contracts/Gravity.sol   #2

233   		for (uint256 i = 0; i < _currentValidators.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L233

File: solidity/contracts/Gravity.sol   #3

263   		for (uint256 i = 0; i < _newValset.validators.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L263

File: solidity/contracts/Gravity.sol   #4

453   				for (uint256 i = 0; i < _amounts.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L453

File: solidity/contracts/Gravity.sol   #5

568   		for (uint256 i = 0; i < _args.transferAmounts.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L568

File: solidity/contracts/Gravity.sol   #6

579   		for (uint256 i = 0; i < _args.feeAmounts.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L579

File: solidity/contracts/Gravity.sol   #7

660   		for (uint256 i = 0; i < _powers.length; i++) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L660

9. 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

File: solidity/contracts/Gravity.sol   #1

301   		require(
302   			_currentValset.validators.length == _currentValset.powers.length &&
303   				_currentValset.validators.length == _v.length &&
304   				_currentValset.validators.length == _r.length &&
305   				_currentValset.validators.length == _s.length,
306   			"Malformed current validator set"
307   		);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L301-L307

File: solidity/contracts/Gravity.sol   #2

396   			require(
397   				_currentValset.validators.length == _currentValset.powers.length &&
398   					_currentValset.validators.length == _v.length &&
399   					_currentValset.validators.length == _r.length &&
400   					_currentValset.validators.length == _s.length,
401   				"Malformed current validator set"
402   			);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L396-L402

File: solidity/contracts/Gravity.sol   #3

411   			require(
412   				_amounts.length == _destinations.length && _amounts.length == _fees.length,
413   				"Malformed batch of transactions"
414   			);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L411-L414

File: solidity/contracts/Gravity.sol   #4

500   			require(
501   				_currentValset.validators.length == _currentValset.powers.length &&
502   					_currentValset.validators.length == _v.length &&
503   					_currentValset.validators.length == _r.length &&
504   					_currentValset.validators.length == _s.length,
505   				"Malformed current validator set"
506   			);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L500-L506

10. Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead

When using elements that are smaller than 32 bytes, your contract’s gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.

https://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.html Use a larger size then downcast where needed

File: solidity/contracts/CosmosToken.sol   #1

11   		uint8 _decimals

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/CosmosToken.sol#L11

File: solidity/contracts/Gravity.sol   #2

91   		uint8 _decimals,

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L91

File: solidity/contracts/Gravity.sol   #3

178   		uint8 _v,

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L178

File: solidity/contracts/Gravity.sol   #4

615   		uint8 _decimals

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L615

11. Duplicated require()/revert() checks should be refactored to a modifier or function

Saves deployment costs

File: solidity/contracts/Gravity.sol   #1

666   		require(
667   			cumulativePower > _powerThreshold,
668   			"Submitted validator set signatures do not have enough power."
669   		);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L666-L669

File: solidity/contracts/Gravity.sol   #2

396   			require(
397   				_currentValset.validators.length == _currentValset.powers.length &&
398   					_currentValset.validators.length == _v.length &&
399   					_currentValset.validators.length == _r.length &&
400   					_currentValset.validators.length == _s.length,
401   				"Malformed current validator set"
402   			);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L396-L402

File: solidity/contracts/Gravity.sol   #3

405   			require(
406   				makeCheckpoint(_currentValset, state_gravityId) == state_lastValsetCheckpoint,
407   				"Supplied current validators and powers do not match checkpoint."
408   			);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L405-L408

File: solidity/contracts/Gravity.sol   #4

416   			require(
417   				isOrchestrator(_currentValset, msg.sender),
418   				"The sender of the transaction is not validated orchestrator"
419   			);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L416-L419

12. 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

File: solidity/contracts/Gravity.sol   #1

124   	function manageWhitelist(
125   		address[] memory _users,
126   		bool _isWhitelisted
127   		) public onlyWhitelisted {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L124-L127

13. 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 and can save gas by doing so.

File: solidity/contracts/Gravity.sol   #1

124   	function manageWhitelist(
125   		address[] memory _users,
126   		bool _isWhitelisted
127   		) public onlyWhitelisted {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L124-L127

File: solidity/contracts/Gravity.sol   #2

140   	function testMakeCheckpoint(ValsetArgs memory _valsetArgs, bytes32 _gravityId) public pure {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L140

File: solidity/contracts/Gravity.sol   #3

144   	function testCheckValidatorSignatures(
145   		address[] memory _currentValidators,
146   		uint256[] memory _currentPowers,
147   		uint8[] memory _v,
148   		bytes32[] memory _r,
149   		bytes32[] memory _s,
150   		bytes32 _theHash,
151   		uint256 _powerThreshold

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L144-L151

File: solidity/contracts/Gravity.sol   #4

166   	function lastBatchNonce(address _erc20Address) public view returns (uint256) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L166

File: solidity/contracts/Gravity.sol   #5

170   	function lastLogicCallNonce(bytes32 _invalidation_id) public view returns (uint256) {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L170

File: solidity/contracts/Gravity.sol   #6

276   	function updateValset(
277   		// The new version of the validator set
278   		ValsetArgs memory _newValset,
279   		// The current validators that approve the change
280   		ValsetArgs memory _currentValset,
281   		// These are arrays of the parts of the current validator's signatures
282   		uint8[] memory _v,
283   		bytes32[] memory _r,
284   		bytes32[] memory _s
285   	) public nonReentrant {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L276-L285

File: solidity/contracts/Gravity.sol   #7

364   	function submitBatch (
365   		// The validators that approve the batch
366   		ValsetArgs memory _currentValset,
367   		// These are arrays of the parts of the validators signatures
368   		uint8[] memory _v,
369   		bytes32[] memory _r,
370   		bytes32[] memory _s,
371   		// The batch of transactions
372   		uint256[] memory _amounts,
373   		address[] memory _destinations,
374   		uint256[] memory _fees,
375   		uint256 _batchNonce,
376   		address _tokenContract,
377   		// a block height beyond which this batch is not valid
378   		// used to provide a fee-free timeout
379   		uint256 _batchTimeout
380   	) public nonReentrant {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L364-L380

File: solidity/contracts/Gravity.sol   #8

479   	function submitLogicCall(
480   		// The validators that approve the call
481   		ValsetArgs memory _currentValset,
482   		// These are arrays of the parts of the validators signatures
483   		uint8[] memory _v,
484   		bytes32[] memory _r,
485   		bytes32[] memory _s,
486   		LogicCallArgs memory _args
487   	) public nonReentrant {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L479-L487

File: solidity/contracts/Gravity.sol   #9

595   	function sendToCosmos(
596   		address _tokenContract,
597   		bytes32 _destination,
598   		uint256 _amount
599   	) public nonReentrant  {

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L595-L599

File: solidity/contracts/Gravity.sol   #10

611   	function deployERC20(
612   		string memory _cosmosDenom,
613   		string memory _name,
614   		string memory _symbol,
615   		uint8 _decimals

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L611-L615

14. Return from function rather than breaking out of loop

Using return rather than break saves gas because the require() outside of the for-loop has the same condition that caused the loop to be broken out of

File: solidity/contracts/Gravity.sol   #1

246  				// Break early to avoid wasting gas
247  				if (cumulativePower > _powerThreshold) {
248  					break;
249  				}
250  			}
251  		}
252  
253  		// Check that there was enough power
254  		require(
255  			cumulativePower > _powerThreshold,
256  			"Submitted validator set signatures do not have enough power."
257  		);
258  		// Success
259  	}

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L246-L259

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

File: solidity/contracts/Gravity.sol   #1

411  			require(
412  				_amounts.length == _destinations.length && _amounts.length == _fees.length,
413  				"Malformed batch of transactions"
414  			);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L411-L414

File: solidity/contracts/Gravity.sol   #2

514  			// Check that the token transfer list is well-formed
515  			require(
516  				_args.transferAmounts.length == _args.transferTokenContracts.length,
517  				"Malformed list of token transfers"
518  			);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L514-L518

File: solidity/contracts/Gravity.sol   #3

520  			// Check that the fee list is well-formed
521  			require(
522  				_args.feeAmounts.length == _args.feeTokenContracts.length,
523  				"Malformed list of fees"
524  			);

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L520-L524

16. Remove test code to save deployment gas

File: solidity/contracts/Gravity.sol   #1

138  	// TEST FIXTURES
139  	// These are here to make it easier to measure gas usage. They should be removed before production
140  	function testMakeCheckpoint(ValsetArgs memory _valsetArgs, bytes32 _gravityId) public pure {
141  		makeCheckpoint(_valsetArgs, _gravityId);
142  	}
143  
144  	function testCheckValidatorSignatures(
145  		address[] memory _currentValidators,
146  		uint256[] memory _currentPowers,
147  		uint8[] memory _v,
148  		bytes32[] memory _r,
149  		bytes32[] memory _s,
150  		bytes32 _theHash,
151  		uint256 _powerThreshold
152  	) public pure {
153  		checkValidatorSignatures(
154  			_currentValidators,
155  			_currentPowers,
156  			_v,
157  			_r,
158  			_s,
159  			_theHash,
160  			_powerThreshold
161  		);
162  	}
163  
164  	// END TEST FIXTURES

https://github.com/code-423n4/2022-05-cudos/blob/de39cf3cd1f1e1cf211819b06d4acf6a043acda0/solidity/contracts/Gravity.sol#L138-L164

#0 - V-Staykov

2022-05-10T11:11:43Z

I find this report particularly 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