Axelar Network - 0xn006e7's results

Decentralized interoperability network.

General Information

Platform: Code4rena

Start Date: 12/07/2023

Pot Size: $80,000 USDC

Total HM: 11

Participants: 47

Period: 9 days

Judge: berndartmueller

Total Solo HM: 1

Id: 260

League: ETH

Axelar Network

Findings Distribution

Researcher Performance

Rank: 19/47

Findings: 1

Award: $189.74

Gas:
grade-a

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

189.7433 USDC - $189.74

Labels

bug
G (Gas Optimization)
grade-a
high quality report
G-15

External Links

Table of contents

S.N.IssuesInstancesGas Savings
G-01Varaibles which are assigned only once in the contract and then never changed thereafter should be declared constant480000 + additional gas
G-02bytes32 constants are more efficient than string constants6-
G-03Don't calculate constants3-
G-04Using XOR (^) and OR (|) bitwise equivalents can save gas2106
G-05Use do while loops instead of for loops12782 + additional gas
G-06Avoid explicit initialization of counter variable in for loop432
G-07Fail as early as possible8-
G-08Save a storage variable reference instead of repeatedly fetching the value in a mapping or an array280
G-09Avoid caching the result of a function call when that result is being used only once42321 + additional gas
G-10Use assembly to write contract type/address storage varibales3-
G-11Cache calldata/memory pointers for complex types to avoid complex offset calculations630
G-12Use a positive conditional flow to save a NOT opcode13 * no. of iterations in for loop

[G-01] : Varaibles which are assigned only once in the contract and then never changed thereafter should be declared constant

Constant variables are stored directly in bytecode rather than storage unlike state variables and thus are much cheaper to read from.Declaring a variable as constant will avoid a Gsset (20000 gas) and replace the first access in each transaction (Gcoldsload - 2100 gas) and each access thereafter (Gwarmacces - 100 gas) with a PUSH32 (3 gas).

Note : These are the instances missed by automated finding(the exact issue found in automated finding is "State variables which are not modified within functions should be set as constants or immutable for values set at deployment" which is a bit different from this one)

4 instances in 1 file

1) contracts/its/token-implementations/StandardizedToken.sol :: Line 22-25

Estimated gas savings : minimum ( 4 * 200000 = 800000) + additional gas due to subsequent SLOADS(Gcoldsload and Gwarmaccess)
File: contracts/its/token-implementations/StandardizedToken.sol
22:     address public tokenManager;
23:     string public name;
24:     string public symbol;
25:     uint8 public decimals;

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/token-implementations/StandardizedToken.sol#L22-L25

[G-02] : bytes32 constants are more efficient than string constants

Use bytes32 to store values that can fit into 32 bytes instead of string as string is more robust in comparison to bytes32.Generally,in Solidity fixed types cost less gas than dynamic types.

6 instances in 3 files

1) contracts/cgp/governance/InterchainGovernance.sol :: Line 21-22

Note : These variables are not declared constants in the original contract but as suggested by the automated finding's GAS-21 issue,they should be declared as constants.

File: contracts/cgp/governance/InterchainGovernance.sol
21:     string public governanceChain;
22:     string public governanceAddress;

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/governance/InterchainGovernance.sol#L21-L22

2) contracts/interchain-governance-executor/lib/InterchainCalls.sol :: struct InterchainCall

File: contracts/interchain-governance-executor/lib/InterchainCalls.sol
14:         string destinationChain;
15:         string destinationContract;

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/interchain-governance-executor/lib/InterchainCalls.sol#L14-L15

3) contracts/its/token-implementations/StandardizedToken.sol :: Line 23-24

Note : These variables are not declared constants in the original contract but they should be as suggested here

File: contracts/its/token-implementations/StandardizedToken.sol
23:     string public name;
24:     string public symbol;

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/token-implementations/StandardizedToken.sol#L23-L24

[G-03] : Don't calculate constants

Assigning constant variables based on the result of a mathematical calculation/function call/casting wastes gas as constant variables need to be revaluated each time they are accessed.

3 instances in 1 file

1) contracts/cgp/AxelarGateway.sol :: Line 34-41

File: contracts/cgp/AxelarGateway.sol
34:     /// @dev Storage slot with the address of the current implementation. `keccak256('eip1967.proxy.implementation') - 1`.
35:     bytes32 internal constant KEY_IMPLEMENTATION = bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc);
36: 
37:     /// @dev Storage slot with the address of the current governance. `keccak256('governance') - 1`.
38:     bytes32 internal constant KEY_GOVERNANCE = bytes32(0xabea6fd3db56a6e6d0242111b43ebb13d1c42709651c032c7894962023a1f909);
39: 
40:     /// @dev Storage slot with the address of the current governance. `keccak256('mint-limiter') - 1`.
41:     bytes32 internal constant KEY_MINT_LIMITER = bytes32(0x627f0c11732837b3240a2de89c0b6343512886dd50978b99c76a68c6416a4d92);

Mitigation : Either use hardcoded values for these constants or chnage them into immutables as immutable variables are evaluated only once i.e during contract creation time and thus can save some gas in comparison to constants.

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/AxelarGateway.sol#L35-L41

[G-04] : Using XOR (^) and OR (|) bitwise equivalents can save gas

On Remix, given only uint256 types, the following are logical equivalents, but don’t cost the same amount of gas:

(a != b || c != d || e != f) costs 571
((a ^ b) | (c ^ d) | (e ^ f)) != 0 costs 498 // (saving 73 gas)

Logic POC

Given 4 variables a, b, c and d represented as such:

0 0 0 0 0 1 1 0 <- a 0 1 1 0 0 1 1 0 <- b 0 0 0 0 0 0 0 0 <- c 1 1 1 1 1 1 1 1 <- d

To have a == b means that every 0 and 1 match on both variables. Meaning that a XOR (operator ^) would evaluate to 0 ((a ^ b) == 0), as it excludes by definition any equalities.Now, if a != b, this means that there’s at least somewhere a 1 and a 0 not matching between a and b, making (a ^ b) != 0.

Both formulas are logically equivalent and using the XOR bitwise operator costs actually the same amount of gas:

  function xOrEquivalence(uint a, uint b) external returns (bool) {
    //return a != b; //370
    //return a ^ b != 0; //370
  }

However, it is much cheaper to use the bitwise OR operator (|) than comparing the truthy or falsy values:

function xOrOrEquivalence(uint a, uint b, uint c, uint d) external returns (bool) {
    //return (a != b || c != d); // 495
    //return (a ^ b | c ^ d) != 0; // 442
}

These are logically equivalent too, as the OR bitwise operator (|) would result in a 1 somewhere if any value is not 0 between the XOR (^) statements, meaning if any XOR (^) statement verifies that its arguments are different.

Coded Proof of Concept :

This little POC also proves that the formulas are equivalent:

function test_XorEq(uint a, uint b, uint c, uint d, uint e, uint f) external {
    assert((a != b || c != d || e != f) == ((a ^ b) | (c ^ d) | (e ^ f)) != 0);
}

Consider applying the suggested equivalence and add a comment mentioning what this is equivalent to, as this is less human-readable, but still understandable once it’s been taught.

2 instances in 2 files

1) contracts/cgp/governance/InterchainGovernance.sol :: _execute()

Estimated gas savings according to remix : 53
File: contracts/cgp/governance/InterchainGovernance.sol
92:         if (keccak256(bytes(sourceChain)) != governanceChainHash || keccak256(bytes(sourceAddress)) != governanceAddressHash)

recommended code :

92:         if (
93:             (
94:                (uint256(keccak256(bytes(sourceChain))) ^ uint256(governanceChainHash)) | (uint256(keccak256(bytes(sourceAddress))) ^ uint256(governanceAddressHash)) 
95:             ) != 0
96:         )

Note : As XOR(^) operators work only with the integer type values,the bytes32 value in above if statement must be safely casted into unint256 value

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/governance/InterchainGovernance.sol#L92

2) contracts/cgp/AxelarGateway.sol :: execute()

Estimated gas savings according to remix : 53
File: contracts/cgp/AxelarGateway.sol
342:         if (commandsLength != commands.length || commandsLength != params.length) revert InvalidCommands();

recommended code :

342:         if (
343:             (
344:                 (commandsLength ^ commands.length) | (commandsLength ^ params.length)
345:             ) != 0
346:         ) revert InvalidCommands();

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/AxelarGateway.sol#L342

[G-05] : Use do while loops instead of for loops

A do while loop costs less gas in comparison to a for loop as the condition is not checked for the first iteration in a do while

12 instances in 7 files

Note : The gas savings calculated in this section have been calculated using protocol's provided test.There are other instances too in this section where gas savings have not been demonstrated and that is due to unavailability of the tests for the functions involved in these instances or even if there are tests available for those functions, REPORT_GAS = true npm run test command in hardhat didn't produce gas reports for them.But as it can be analyzed from other similar instances,applying the recommended mitigation will definitely save significant amount of gas

1 ) contracts/cgp/auth/MultisigBase.sol :: modifier onlySigners(),the gas saving calculated in this instance has been calculated using rotateSigners function as this modifier is being used by rotateSigners

avg. gas saving obtained : 13
Functionminmaxavgcalls
BeforerotateSigners750101400049057121
AfterrotateSigners750101399489055821
File: contracts/cgp/auth/MultisigBase.sol
70:         for (uint256 i; i < count; ++i) {
71:             voting.hasVoted[signers.accounts[i]] = false;
72:         }

recommended code :

70:         uint256 i;
71:         do {
72:             voting.hasVoted[signers.accounts[i]] = false;
73:             ++i;
74:         } while (i < count);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/auth/MultisigBase.sol#L70-L72

2) contracts/cgp/auth/MultisigBase.sol :: getSignerVotesCount

File: contracts/cgp/auth/MultisigBase.sol
122:         for (uint256 i; i < length; ++i) {
123:             if (votingPerTopic[signerEpoch][topic].hasVoted[signers.accounts[i]]) {
124:                 voteCount++;
125:             }
126:         }

recommended code :

122:         uint256 i;
123:         do {
124:             if (votingPerTopic[signerEpoch][topic].hasVoted[signers.accounts[i]]) {
125:                 voteCount++;
126:             }
127:             ++i;
128:         } while (i < length);
129: 

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/auth/MultisigBase.sol#L122-L126

3) contracts/cgp/AxelarGateway.sol :: setTokenMintLimits()

avg. gas saving obtained : 54
Functionminmaxavgcalls
BeforesetTokenMintLimits5848487280800814
AftersetTokenMintLimits5845287219800274
File: contracts/cgp/AxelarGateway.sol
270:         for (uint256 i; i < length; ++i) {
271:             string memory symbol = symbols[i];
272:             uint256 limit = limits[i];
273: 
274:             if (tokenAddresses(symbol) == address(0)) revert TokenDoesNotExist(symbol);
275: 
276:             _setTokenMintLimit(symbol, limit);
277:         }

recommended code :

270:         uint256 i;
271:         do {
272:             string memory symbol = symbols[i];
273:             uint256 limit = limits[i];
274: 
275:             if (tokenAddresses(symbol) == address(0)) revert TokenDoesNotExist(symbol);
276: 
277:             _setTokenMintLimit(symbol, limit);
278:             ++i;
279:         } while (i < length);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/AxelarGateway.sol#L270-L277

4) contracts/interchain-governance-executor/InterchainProposalSender.sol :: sendProposals()

avg. gas saving obtained : 79
Functionminmaxavgcalls
BeforesendProposals--1150058
AftersendProposals--1149268
File: contracts/interchain-governance-executor/InterchainProposalSender.sol
63:         for (uint256 i = 0; i < interchainCalls.length; ) {
64:             _sendProposal(interchainCalls[i]);
65:             unchecked {
66:                 ++i;
67:             }
68:         }

recommended code :

62:         uint256 i = 0;
63:         do {
64:             _sendProposal(interchainCalls[i]);
65:             unchecked {
66:                 ++i;
67:             }
68:         } while (i < interchainCalls.length);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/interchain-governance-executor/InterchainProposalSender.sol#L63-L68

5) contracts/interchain-governance-executor/InterchainProposalSender.sol :: revertIfInvalidFee(), this function gas saving has been calculated using sendProposals function as it is a private function used by sendProposals

avg. gas saving obtained : 55
Functionminmaxavgcalls
BeforesendProposals--1150058
AftersendProposals--1149508
File: contracts/interchain-governance-executor/InterchainProposalSender.sol
106:         for (uint256 i = 0; i < interchainCalls.length; ) {
107:             totalGas += interchainCalls[i].gas;
108:             unchecked {
109:                 ++i;
110:             }
111:         }

recommended code :

106:         uint256 i = 0;
107:         do {
108:             totalGas += interchainCalls[i].gas;
109:             unchecked {
110:                 ++i;
111:             }
112:         } while(i < interchainCalls.length);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/interchain-governance-executor/InterchainProposalSender.sol#L106-L111

6) contracts/interchain-governance-executor/InterchainProposalExecutor.sol :: _executeProposal()

No test has been provided for the specific function involved in this instance from protocol's side(using which actual gas saving could be calculated).But,this function has been mimiced by forceExecute function in the required test script using which the demonstrated gas saving has been calculated

avg. gas saving obtained : 47
Functionminmaxavgcalls
BeforeforceExecute7665594306825396
AfterforceExecute7660894259824926
File: contracts/interchain-governance-executor/InterchainProposalExecutor.sol
74:         for (uint256 i = 0; i < calls.length; i++) {
75:             InterchainCalls.Call memory call = calls[i];
76:             (bool success, bytes memory result) = call.target.call{ value: call.value }(call.callData);
77: 
78:             if (!success) {
79:                 _onTargetExecutionFailed(call, result);
80:             } else {
81:                 _onTargetExecuted(call, result);
82:             }
83:         }

recommended code :

74:         uint256 i = 0;
75:         do {
76:             InterchainCalls.Call memory call = calls[i];
77:             (bool success, bytes memory result) = call.target.call{ value: call.value }(call.callData);
78: 
79:             if (!success) {
80:                 _onTargetExecutionFailed(call, result);
81:             } else {
82:                 _onTargetExecuted(call, result);
83:             }
84:             i++;
85:         } while (i < calls.length);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/interchain-governance-executor/InterchainProposalExecutor.sol#L74-L83

7) contracts/its/interchain-token-service/InterchainTokenService.sol :: setFlowLimit()

File: contracts/its/interchain-token-service/InterchainTokenService.sol
537:         for (uint256 i; i < length; ++i) {
538:             ITokenManager tokenManager = ITokenManager(getValidTokenManagerAddress(tokenIds[i]));
539:             tokenManager.setFlowLimit(flowLimits[i]);
540:         }

recommended code :

537:         uint256 i;
538:         do {
539:             ITokenManager tokenManager = ITokenManager(getValidTokenManagerAddress(tokenIds[i]));
540:             tokenManager.setFlowLimit(flowLimits[i]);
541:             ++i;
542:         } while (i < length);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L537-L540

8) contracts/its/remote-address-validator/RemoteAddressValidator.sol :: _setup()

File: contracts/its/remote-address-validator/RemoteAddressValidator.sol
44:         for (uint256 i; i < length; ++i) {
45:             addTrustedAddress(trustedChainNames[i], trustedAddresses[i]);
46:         }

recommended code :

44:         uint256 i;
45:         do {
46:             addTrustedAddress(trustedChainNames[i], trustedAddresses[i]);
47:             ++i;
48:         } while (i < length);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/remote-address-validator/RemoteAddressValidator.sol#L44-L46

9) contracts/its/remote-address-validator/RemoteAddressValidator.sol :: _lowerCase()

avg. gas saving obtained : 363
Functionminmaxavgcalls
BeforeaddTrustedAddress--847422
AfteraddTrustedAddress--843792
File: contracts/its/remote-address-validator/RemoteAddressValidator.sol
56:         for (uint256 i; i < length; i++) {
57:             uint8 b = uint8(bytes(s)[i]);
58:             if ((b >= 65) && (b <= 70)) bytes(s)[i] = bytes1(b + uint8(32));
59:         }

recommended code :

56:         uint256 i;
57:         do {
58:             uint8 b = uint8(bytes(s)[i]);
59:             if ((b >= 65) && (b <= 70)) bytes(s)[i] = bytes1(b + uint8(32));
60:             i++;
61:         } while (i < length);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/remote-address-validator/RemoteAddressValidator.sol#L56-L59

10) contracts/its/remote-address-validator/RemoteAddressValidator.sol :: addGatewaySupportedChains()

avg. gas saving obtained : 44
Functionminmaxavgcalls
BeforeaddGatewaySupportedChains--539132
AfteraddGatewaySupportedChains--538692
File: contracts/its/remote-address-validator/RemoteAddressValidator.sol
108:         for (uint256 i; i < length; ++i) {
109:             string calldata chainName = chainNames[i];
110:             supportedByGateway[chainName] = true;
111:             emit GatewaySupportedChainAdded(chainName);
112:         }

recommended code :

108:         uint256 i;
109:         do {
110:             string calldata chainName = chainNames[i];
111:             supportedByGateway[chainName] = true;
112:             emit GatewaySupportedChainAdded(chainName);
113:             ++i;
114:         } while (i < length);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/remote-address-validator/RemoteAddressValidator.sol#L108-L112

11) contracts/its/remote-address-validator/RemoteAddressValidator.sol :: removeGatewaySupportedChains()

avg. gas saving obtained : 44
Functionminmaxavgcalls
BeforeremoveGatewaySupportedChains--320132
AfterremoveGatewaySupportedChains--319692
File: contracts/its/remote-address-validator/RemoteAddressValidator.sol
121:         for (uint256 i; i < length; ++i) {
122:             string calldata chainName = chainNames[i];
123:             supportedByGateway[chainName] = false;
124:             emit GatewaySupportedChainRemoved(chainName);
125:         }

recommended code :

121:         uint256 i;
122:         do {
123:             string calldata chainName = chainNames[i];
124:             supportedByGateway[chainName] = false;
125:             emit GatewaySupportedChainRemoved(chainName);
126:             ++i;
127:         } while (i < length);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/remote-address-validator/RemoteAddressValidator.sol#L121-L125

12) contracts/its/utils/Multicall.sol :: multicall()

avg. gas saving obtained : 83
Functionminmaxavgcalls
Beforemulticall--568682
Aftermulticall--567852
File: contracts/its/utils/Multicall.sol
24:         for (uint256 i = 0; i < data.length; ++i) {
25:             (bool success, bytes memory result) = address(this).delegatecall(data[i]);
26: 
27:             if (!success) {
28:                 revert(string(result));
29:             }
30: 
31:             results[i] = result;
32:         }

recommended code :

24:         uint256 i = 0;
25:         do {
26:             (bool success, bytes memory result) = address(this).delegatecall(data[i]);
27: 
28:             if (!success) {
29:                 revert(string(result));
30:             }
31: 
32:             results[i] = result;
33:             ++i;
34:         } while (i < data.length);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/utils/Multicall.sol#L24-L32

[G-06] : Avoid explicit initialization of counter variable in for loop

In solidity,concept of null value for a variable doesn't exist i.e when variables are declared in solidity,they are initialized with the default value of their type rather than being set to null.Thus,explicitly initializing variables(counter varaible for which generally i is used) in for loops wastes gas.

According to remix,around 8 gas per loop can be saved by avoiding the explicit initialization of counter varaible in a for loop.Here is a little POC experimented in remix to demonstrate the gas saving that can be obtained via the recommended mitigation.

// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;

contract AvoidExplicitInitializationGasTest {

// @audit : call to test in CASE 1 costs 3090 gas wheras it costs 3082 gas in CASE 2

function test(uint256 a) public pure returns(uint256) {

    // CASE 1

    // for(uint256 i = 0; i < 10;) {
    //     a = a + i;

    //     unchecked {
    //         ++i;
    //     }
    // }

    // return a;

    // CASE 2

    for(uint256 i; i < 10;) {
        a = a + i;

        unchecked {
            ++i;
        }
    }

    return a;
}
}

4 instances in 3 files

Estimated gas saving : 4 * 8 = 32

1)contracts/interchain-governance-executor/InterchainProposalSender.sol :: sendProposals()

File: contracts/interchain-governance-executor/InterchainProposalSender.sol
63:         for (uint256 i = 0; i < interchainCalls.length; ) {

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/interchain-governance-executor/InterchainProposalSender.sol#L63

2)contracts/interchain-governance-executor/InterchainProposalSender.sol :: revertIfInvalidFee()

File: contracts/interchain-governance-executor/InterchainProposalSender.sol
106:         for (uint256 i = 0; i < interchainCalls.length; ) {

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/interchain-governance-executor/InterchainProposalSender.sol#L106

3) contracts/interchain-governance-executor/InterchainProposalExecutor.sol :: _executeProposal()

File: contracts/interchain-governance-executor/InterchainProposalExecutor.sol
74:         for (uint256 i = 0; i < calls.length; i++) {

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/interchain-governance-executor/InterchainProposalExecutor.sol#L74

4) contracts/its/utils/Multicall.sol :: multicall()

File: contracts/its/utils/Multicall.sol
24:         for (uint256 i = 0; i < data.length; ++i) {

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/utils/Multicall.sol#L24-L32

[G-07] : Fail as early as possible

Move revert statements to the top of the function as it's logic is not influenced by the code above it in any way and in the current code design,users will have to pay more gas if function reverts.Thus by moving revert statements to the top,users gas can be saved if they call the function and the function reverts as early as possible

8 instances in 4 files

1) contracts/cgp/auth/MultisigBase.sol :: _rotateSigners(), move lines 159-161 to the top of the function by refactoring the code as per the expected functionality

File: contracts/cgp/auth/MultisigBase.sol
159:         if (newThreshold > length) revert InvalidSigners();
160: 
161:         if (newThreshold == 0) revert InvalidSignerThreshold();

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/auth/MultisigBase.sol#L159-L161

2) contracts/cgp/AxelarGateway.sol :: setTokenMintLimits(), swap line 272 with 274

File: contracts/cgp/AxelarGateway.sol
271:             string memory symbol = symbols[i];
272:             uint256 limit = limits[i];
273: 
274:             if (tokenAddresses(symbol) == address(0)) revert TokenDoesNotExist(symbol);

recommended code :

271:             string memory symbol = symbols[i];
272:             if (tokenAddresses(symbol) == address(0)) revert TokenDoesNotExist(symbol);
273: 
274:             uint256 limit = limits[i];

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/AxelarGateway.sol#L271-L274

3) contracts/cgp/AxelarGateway.sol :: upgrade()

File: contracts/cgp/AxelarGateway.sol
289:         emit Upgraded(newImplementation);
290: 
291:         // AUDIT: If `newImplementation.setup` performs `selfdestruct`, it will result in the loss of _this_ implementation (thereby losing the gateway)
292:         //        if `upgrade` is entered within the context of _this_ implementation itself.
293:         if (setupParams.length != 0) {
294:             (bool success, ) = newImplementation.delegatecall(abi.encodeWithSelector(IAxelarGateway.setup.selector, setupParams));
295: 
296:             if (!success) revert SetupFailed();
297:         }

recommended code :

289:         // AUDIT: If `newImplementation.setup` performs `selfdestruct`, it will result in the loss of _this_ implementation (thereby losing the gateway)
290:         //        if `upgrade` is entered within the context of _this_ implementation itself.
291:         if (setupParams.length != 0) {
292:             (bool success, ) = newImplementation.delegatecall(abi.encodeWithSelector(IAxelarGateway.setup.selector, setupParams));
293: 
294:             if (!success) revert SetupFailed();
295:         }
296: 
297:         emit Upgraded(newImplementation);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/AxelarGateway.sol#L289-L297

4) contracts/cgp/AxelarGateway.sol :: execute()

File: contracts/cgp/AxelarGateway.sol
326:         bytes32 messageHash = ECDSA.toEthSignedMessageHash(keccak256(data));
327: 
328:         // returns true for current operators
329:         bool allowOperatorshipTransfer = IAxelarAuth(AUTH_MODULE).validateProof(messageHash, proof);
330: 
331:         uint256 chainId;
332:         bytes32[] memory commandIds;
333:         string[] memory commands;
334:         bytes[] memory params;
335: 
336:         (chainId, commandIds, commands, params) = abi.decode(data, (uint256, bytes32[], string[], bytes[]));
337: 
338:         if (chainId != block.chainid) revert InvalidChainId();
339: 
340:         uint256 commandsLength = commandIds.length;
341: 
342:         if (commandsLength != commands.length || commandsLength != params.length) revert InvalidCommands();

recommended code :

326:         uint256 chainId;
327:         bytes32[] memory commandIds;
328:         string[] memory commands;
329:         bytes[] memory params;
330: 
331:         (chainId, commandIds, commands, params) = abi.decode(data, (uint256, bytes32[], string[], bytes[]));
332: 
333:         if (chainId != block.chainid) revert InvalidChainId();
334: 
335:         uint256 commandsLength = commandIds.length;
336: 
337:         if (commandsLength != commands.length || commandsLength != params.length) revert InvalidCommands();
338: 
339:         bytes32 messageHash = ECDSA.toEthSignedMessageHash(keccak256(data));
340: 
341:         // returns true for current operators
342:         bool allowOperatorshipTransfer = IAxelarAuth(AUTH_MODULE).validateProof(messageHash, proof);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/AxelarGateway.sol#L326-L342

5) contracts/its/interchain-token-service/InterchainTokenService.sol :: constructor

File: contracts/its/interchain-token-service/InterchainTokenService.sol
092:         if (
093:             remoteAddressValidator_ == address(0) ||
094:             gasService_ == address(0) ||
095:             tokenManagerDeployer_ == address(0) ||
096:             standardizedTokenDeployer_ == address(0)
097:         ) revert ZeroAddress();
098:         remoteAddressValidator = IRemoteAddressValidator(remoteAddressValidator_);
099:         gasService = IAxelarGasService(gasService_);
100:         tokenManagerDeployer = tokenManagerDeployer_;
101:         standardizedTokenDeployer = standardizedTokenDeployer_;
102:         deployer = ITokenManagerDeployer(tokenManagerDeployer_).deployer();
103: 
104:         if (tokenManagerImplementations.length != uint256(type(TokenManagerType).max) + 1) revert LengthMismatch();

recommended code :

092:         if (
093:             remoteAddressValidator_ == address(0) ||
094:             gasService_ == address(0) ||
095:             tokenManagerDeployer_ == address(0) ||
096:             standardizedTokenDeployer_ == address(0)
097:         ) revert ZeroAddress();
098: 
099:         if (tokenManagerImplementations.length != uint256(type(TokenManagerType).max) + 1) revert LengthMismatch();
100:         
101:         remoteAddressValidator = IRemoteAddressValidator(remoteAddressValidator_);
102:         gasService = IAxelarGasService(gasService_);
103:         tokenManagerDeployer = tokenManagerDeployer_;
104:         standardizedTokenDeployer = standardizedTokenDeployer_;
105:         deployer = ITokenManagerDeployer(tokenManagerDeployer_).deployer();

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L92-L104

6) contracts/its/utils/ExpressCallHandler.sol :: _setExpressReceiveToken()

File: contracts/its/utils/ExpressCallHandler.sol
86:         uint256 slot = _getExpressReceiveTokenSlot(tokenId, destinationAddress, amount, commandId);
87:         address prevExpressCaller;
88:         assembly {
89:             prevExpressCaller := sload(slot)
90:         }
91:         if (prevExpressCaller != address(0)) revert AlreadyExpressCalled();

recommended code :

86:         address prevExpressCaller;
87:         assembly {
88:             prevExpressCaller := sload(slot)
89:         }
90:         if (prevExpressCaller != address(0)) revert AlreadyExpressCalled();
91: 
92:         uint256 slot = _getExpressReceiveTokenSlot(tokenId, destinationAddress, amount, commandId);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/utils/ExpressCallHandler.sol#L86-L91

[G-08] : Save a storage variable reference instead of repeatedly fetching the value in a mapping or an array

Caching the value of a particular key of a mapping or the value of a particular index of an array in a local storage variable when the value is accessed(read and written) multiple times saves ~40 gas per access due to not having to perform the same offset calculation every time.Help the Optimizer by saving a storage variable’s reference instead of repeatedly fetching it.

To help the optimizer,declare a storage type variable and use it instead of repeatedly fetching the reference in a map or an array.

As an example, instead of repeatedly calling someMap[someKey] and SomeArray[someIndex], save their references like SomeType storage someVariable = someMap[someIndex] and SomeType storage someVariable = SomeArray[someIndex] and use them.

Note : These are the instances missed by automated finding

2 instance in 1 file

Estimated gas savings : 2 * 40 = 80

1) contracts/cgp/auth/MultisigBase.sol :: _rotateSigners(), signers.isSigner[account] should be cached in locla storage

File: contracts/cgp/auth/MultisigBase.sol
172:             if (signers.isSigner[account]) revert DuplicateSigner(account); // @audit : 1st access
173:             if (account == address(0)) revert InvalidSigners();
174: 
175:             signers.isSigner[account] = true; // @audit : 2nd access

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/auth/MultisigBase.sol#L172-L175

[G-09] : Avoid caching the result of a function call when that result is being used only once

Caching the result of a function call can save gas only when that result is being accessed mutiple times inside a function .There is no point in caching such result when it is being used only once inside a function as it would increase gas costs due to involved memory operations.

42 instances in 10 files

Note : The gas savings calculated in this section have been calculated using protocol's provided test.There are other instances too in this section where gas savings have not been demonstrated and that is due to unavailability of the tests for the functions involoved in these instances or even if there are tests available for those functions, REPORT_GAS = true npm run test command in hardhat didn't produce gas reports for them.But as it can be analyzed from other similar instances,applying the recommended mitigation will definitely save significant amount of gas

1) contracts/cgp/AxelarGateway.sol :: execute()

Here, messageHash cached in line 326 has been used only once in line 329

avg. gas saving obtained : 11
Functionminmaxavgcalls
Beforeexecute809242551246514858101
Afterexecute809112551253514847101
File: contracts/cgp/AxelarGateway.sol
326:         bytes32 messageHash = ECDSA.toEthSignedMessageHash(keccak256(data));
327: 
328:         // returns true for current operators
329:         bool allowOperatorshipTransfer = IAxelarAuth(AUTH_MODULE).validateProof(messageHash, proof);

recommended code :

326:         // returns true for current operators
327:         bool allowOperatorshipTransfer = IAxelarAuth(AUTH_MODULE).validateProof(ECDSA.toEthSignedMessageHash(keccak256(data)), proof);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/AxelarGateway.sol#L326-L329

2) contracts/cgp/AxelarGateway.sol :: deployToken()

Here, salt cached in line 396 has been used only once in line 399

File: contracts/cgp/AxelarGateway.sol
396:             bytes32 salt = keccak256(abi.encodePacked(symbol));
397: 
398:             (bool success, bytes memory data) = TOKEN_DEPLOYER_IMPLEMENTATION.delegatecall(
399:                 abi.encodeWithSelector(ITokenDeployer.deployToken.selector, name, symbol, decimals, cap, salt)
400:             );

recommended code :

396:             (bool success, bytes memory data) = TOKEN_DEPLOYER_IMPLEMENTATION.delegatecall(
397:                 abi.encodeWithSelector(ITokenDeployer.deployToken.selector, name, symbol, decimals, cap, keccak256(abi.encodePacked(symbol)))
398:             );

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/AxelarGateway.sol#L396-L400

3) contracts/cgp/AxelarGateway.sol :: burnToken()

Here, depositHandlerAddress cached in line 435 has been used only once in line 437

File: contracts/cgp/AxelarGateway.sol
435:             address depositHandlerAddress = _getCreate2Address(salt, keccak256(abi.encodePacked(type(DepositHandler).creationCode)));
436: 
437:             if (_hasCode(depositHandlerAddress)) return;

recommended code :

435:             if (_hasCode(_getCreate2Address(salt, keccak256(abi.encodePacked(type(DepositHandler).creationCode))))) return;

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/AxelarGateway.sol#L435-L437

4) contracts/gmp-sdk/deploy/ConstAddressDeployer.sol :: deployedAddress()

Here, newSalt cached in line 63 has been used only once in line 71

File: contracts/gmp-sdk/deploy/ConstAddressDeployer.sol
63:         bytes32 newSalt = keccak256(abi.encode(sender, salt));
64:         deployedAddress_ = address(
65:             uint160(
66:                 uint256(
67:                     keccak256(
68:                         abi.encodePacked(
69:                             hex'ff',
70:                             address(this),
71:                             newSalt,

recommended code :

63:         deployedAddress_ = address(
64:             uint160(
65:                 uint256(
66:                     keccak256(
67:                         abi.encodePacked(
68:                             hex'ff',
69:                             address(this),
70:                             keccak256(abi.encode(sender, salt)),

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/deploy/ConstAddressDeployer.sol#L63-L71

5) contracts/gmp-sdk/deploy/Create3Deployer.sol :: deploy()

Here, deploySalt cached in line 30 has been used only once in line 31

avg. gas saving obtained : 13
Functionminmaxavgcalls
Beforedeploy17904172077064337014
Afterdeploy17902872075764335714
File: contracts/gmp-sdk/deploy/Create3Deployer.sol
30:         bytes32 deploySalt = keccak256(abi.encode(msg.sender, salt));
31:         deployedAddress_ = Create3.deploy(deploySalt, bytecode);

recommended code :

30:         deployedAddress_ = Create3.deploy(keccak256(abi.encode(msg.sender, salt)), bytecode);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/deploy/Create3Deployer.sol#L30-L31

6) contracts/gmp-sdk/deploy/Create3Deployer.sol :: deployAndInit()

Here, deploySalt cached in line 54 has been used only once in line 55

avg. gas saving obtained : 13
Functionminmaxavgcalls
BeforedeployAndInit--8246462
AfterdeployAndInit--8246332
File: contracts/gmp-sdk/deploy/Create3Deployer.sol
54:         bytes32 deploySalt = keccak256(abi.encode(msg.sender, salt));
55:         deployedAddress_ = Create3.deploy(deploySalt, bytecode);

recommended code :

54:         deployedAddress_ = Create3.deploy(keccak256(abi.encode(msg.sender, salt)), bytecode);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/deploy/Create3Deployer.sol#L54-L55

7) contracts/gmp-sdk/deploy/Create3Deployer.sol :: deployedAddress()

Here, deploySalt cached in line 68 has been used only once in line 69

File: contracts/gmp-sdk/deploy/Create3Deployer.sol
68:         bytes32 deploySalt = keccak256(abi.encode(sender, salt));
69:         return Create3.deployedAddress(address(this), deploySalt);

recommended code :

68:         return Create3.deployedAddress(address(this), keccak256(abi.encode(sender, salt)));

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/deploy/Create3Deployer.sol#L68-L69

8) contracts/its/interchain-token-service/InterchainTokenService.sol :: getTokenAddress()

Here, tokenManagerAddress cached in line 181 has been used only once in line 182

File: contracts/its/interchain-token-service/InterchainTokenService.sol
181:         address tokenManagerAddress = getValidTokenManagerAddress(tokenId);
182:         tokenAddress = ITokenManager(tokenManagerAddress).tokenAddress();

recommended code :

181:         tokenAddress = ITokenManager(getValidTokenManagerAddress(tokenId)).tokenAddress();

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L181-L182

9) contracts/its/interchain-token-service/InterchainTokenService.sol :: getFlowLimit()

Here, tokenManager cached in line 276 has been used only once in line 277

File: contracts/its/interchain-token-service/InterchainTokenService.sol
276:         ITokenManager tokenManager = ITokenManager(getValidTokenManagerAddress(tokenId));
277:         flowLimit = tokenManager.getFlowLimit();

recommended code :

276:         flowLimit = (ITokenManager(getValidTokenManagerAddress(tokenId))).getFlowLimit();

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L276-L277

10) contracts/its/interchain-token-service/InterchainTokenService.sol :: getFlowOutAmount()

Here, tokenManager cached in line 286 has been used only once in line 287

File: contracts/its/interchain-token-service/InterchainTokenService.sol
286:         ITokenManager tokenManager = ITokenManager(getValidTokenManagerAddress(tokenId));
287:         flowOutAmount = tokenManager.getFlowOutAmount();

recommended code :

286:         flowOutAmount = (ITokenManager(getValidTokenManagerAddress(tokenId))).getFlowOutAmount();

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L286-L287

11) contracts/its/interchain-token-service/InterchainTokenService.sol :: getFlowInAmount()

Here, tokenManager cached in line 296 has been used only once in line 297

File: contracts/its/interchain-token-service/InterchainTokenService.sol
296:         ITokenManager tokenManager = ITokenManager(getValidTokenManagerAddress(tokenId));
297:         flowInAmount = tokenManager.getFlowInAmount();

recommended code :

296:         flowInAmount = (ITokenManager(getValidTokenManagerAddress(tokenId))).getFlowInAmount();

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L296-L297

12) contracts/its/interchain-token-service/InterchainTokenService.sol :: deployAndRegisterStandardizedToken()

Here, tokenManagerAddress cached in line 398 has been used only once in line 399.And,tokenManagerType and tokenAddress cached in lines 399 and 400 has been used only once in line 401

avg. gas saving obtained : 41
Functionminmaxavgcalls
BeforedeployAndRegisterStandardizedToken91276391284791280310
AfterdeployAndRegisterStandardizedToken91271291282091276210
File: contracts/its/interchain-token-service/InterchainTokenService.sol
398:         address tokenManagerAddress = getTokenManagerAddress(tokenId);
399:         TokenManagerType tokenManagerType = distributor == tokenManagerAddress ? TokenManagerType.MINT_BURN : TokenManagerType.LOCK_UNLOCK;
400:         address tokenAddress = getStandardizedTokenAddress(tokenId);
401:         _deployTokenManager(tokenId, tokenManagerType, abi.encode(msg.sender.toBytes(), tokenAddress));

recommended code :

398:         _deployTokenManager(tokenId, 
399:         distributor == getTokenManagerAddress(tokenId) ? TokenManagerType.MINT_BURN : TokenManagerType.LOCK_UNLOCK, 
400:         abi.encode(msg.sender.toBytes(), 
401:         getStandardizedTokenAddress(tokenId)));

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L398-L401

13) contracts/its/interchain-token-service/InterchainTokenService.sol :: deployAndRegisterRemoteStandardizedToken(), this function gas saving has been calculated using deployAndRegisterRemoteStandardizedToken function as it is an internal function used by deployAndRegisterRemoteStandardizedToken

Here, tokenId cached in line 427 has been used only once in line 428

avg. gas saving obtained : 34
Functionminmaxavgcalls
BeforedeployAndRegisterRemoteStandardizedToken--1073044
AfterdeployAndRegisterRemoteStandardizedToken--1072704
File: contracts/its/interchain-token-service/InterchainTokenService.sol
427:         bytes32 tokenId = getCustomTokenId(msg.sender, salt);
428:         _deployRemoteStandardizedToken(tokenId, name, symbol, decimals, distributor, operator, destinationChain, gasValue);

recommended code :

427:         _deployRemoteStandardizedToken(getCustomTokenId(msg.sender, salt), 
428:         name, symbol, decimals, distributor, operator, destinationChain, gasValue);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L427-L428

14) contracts/its/interchain-token-service/InterchainTokenService.sol :: expressReceiveToken()

Here, tokenManager and token cached in lines 448 and 449 has been used only once in line 449 and 451 respectively

avg. gas saving obtained : 26
Functionminmaxavgcalls
BeforeexpressReceiveToken1099201195201131779
AfterexpressReceiveToken1098941194941131519
File: contracts/its/interchain-token-service/InterchainTokenService.sol
448:         ITokenManager tokenManager = ITokenManager(getValidTokenManagerAddress(tokenId));
449:         IERC20 token = IERC20(tokenManager.tokenAddress());
450: 
451:         SafeTokenTransferFrom.safeTransferFrom(token, caller, destinationAddress, amount);

recommended code :

448:         SafeTokenTransferFrom.safeTransferFrom(IERC20((ITokenManager(getValidTokenManagerAddress(tokenId))).tokenAddress()), 
449:         caller, destinationAddress, amount);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L448-L451

15) contracts/its/interchain-token-service/InterchainTokenService.sol :: expressReceiveTokenWithData()

Here, tokenManager and token cached in lines 479 and 480 has been used only once in line 480 and 482 respectively

avg. gas saving obtained : 26
Functionminmaxavgcalls
BeforeexpressReceiveTokenWithData12864514870414138411
AfterexpressReceiveTokenWithData12861914867814135811
File: contracts/its/interchain-token-service/InterchainTokenService.sol
479:         ITokenManager tokenManager = ITokenManager(getValidTokenManagerAddress(tokenId));
480:         IERC20 token = IERC20(tokenManager.tokenAddress());
481: 
482:         SafeTokenTransferFrom.safeTransferFrom(token, caller, destinationAddress, amount);

recommended code :

479:         SafeTokenTransferFrom.safeTransferFrom(IERC20((ITokenManager(getValidTokenManagerAddress(tokenId))).tokenAddress()), 
480:         caller, destinationAddress, amount);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L479-L482

16) contracts/its/interchain-token-service/InterchainTokenService.sol :: setFlowLimit()

Here, tokenManager cached in line 538 has been used only once in line 539

Note : Gas savings obtained in this instance will actually be very high since the caching is being performed inside a for loop

File: contracts/its/interchain-token-service/InterchainTokenService.sol
538:             ITokenManager tokenManager = ITokenManager(getValidTokenManagerAddress(tokenIds[i]));
539:             tokenManager.setFlowLimit(flowLimits[i]);

recommended code :

538:             (ITokenManager(getValidTokenManagerAddress(tokenIds[i]))).setFlowLimit(flowLimits[i]);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L538-L539

17) contracts/its/interchain-token-service/InterchainTokenService.sol :: _processDeployStandardizedTokenAndManagerPayload()

Here, tokenAddress cached in line 688 has been used only once in line 696

File: contracts/its/interchain-token-service/InterchainTokenService.sol
688:         address tokenAddress = getStandardizedTokenAddress(tokenId);
689:         address tokenManagerAddress = getTokenManagerAddress(tokenId);
690:         address distributor = distributorBytes.length > 0 ? distributorBytes.toAddress() : tokenManagerAddress;
691:         _deployStandardizedToken(tokenId, distributor, name, symbol, decimals, 0, distributor);
692:         TokenManagerType tokenManagerType = distributor == tokenManagerAddress ? TokenManagerType.MINT_BURN : TokenManagerType.LOCK_UNLOCK;
693:         _deployTokenManager(
694:             tokenId,
695:             tokenManagerType,
696:             abi.encode(operatorBytes.length == 0 ? address(this).toBytes() : operatorBytes, tokenAddress)

recommended code :

688:         address tokenManagerAddress = getTokenManagerAddress(tokenId);
689:         address distributor = distributorBytes.length > 0 ? distributorBytes.toAddress() : tokenManagerAddress;
690:         _deployStandardizedToken(tokenId, distributor, name, symbol, decimals, 0, distributor);
691:         TokenManagerType tokenManagerType = distributor == tokenManagerAddress ? TokenManagerType.MINT_BURN : TokenManagerType.LOCK_UNLOCK;
692:         _deployTokenManager(
693:             tokenId,
694:             tokenManagerType,
695:             abi.encode(operatorBytes.length == 0 ? address(this).toBytes() : operatorBytes, getStandardizedTokenAddress(tokenId))

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L688-L696

18) contracts/its/interchain-token-service/InterchainTokenService.sol :: _deployRemoteTokenManager(), this function gas saving has been calculated using deployRemoteCustomTokenManager function as it is an internal function used by deployRemoteCustomTokenManager

Here, payload cached in line 755 has been used only once in line 756

avg. gas saving obtained : 13
Functionminmaxavgcalls
BeforedeployRemoteCustomTokenManager--991328
AfterdeployRemoteCustomTokenManager--991198
File: contracts/its/interchain-token-service/InterchainTokenService.sol
755:         bytes memory payload = abi.encode(SELECTOR_DEPLOY_TOKEN_MANAGER, tokenId, tokenManagerType, params);
756:         _callContract(destinationChain, payload, gasValue, msg.sender);

recommended code :

755:         _callContract(destinationChain, 
756:         abi.encode(SELECTOR_DEPLOY_TOKEN_MANAGER, tokenId, tokenManagerType, params), 
757:         gasValue, msg.sender);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L755-L756

19) contracts/its/interchain-token-service/InterchainTokenService.sol :: _deployRemoteStandardizedToken(), this function gas saving has been calculated using deployRemoteCanonicalToken function as it is an internal function used by deployRemoteCanonicalToken

Here, payload cached in line 780 has been used only once in line 789

avg. gas saving obtained : 13
Functionminmaxavgcalls
BeforedeployRemoteCanonicalToken--1321064
AfterdeployRemoteCanonicalToken--1320934
File: contracts/its/interchain-token-service/InterchainTokenService.sol
780:         bytes memory payload = abi.encode(
781:             SELECTOR_DEPLOY_AND_REGISTER_STANDARDIZED_TOKEN,
782:             tokenId,
783:             name,
784:             symbol,
785:             decimals,
786:             distributor,
787:             operator
788:         );
789:         _callContract(destinationChain, payload, gasValue, msg.sender);

recommended code :

780:         _callContract(destinationChain, 
781:         abi.encode(
782:             SELECTOR_DEPLOY_AND_REGISTER_STANDARDIZED_TOKEN,
783:             tokenId,
784:             name,
785:             symbol,
786:             decimals,
787:             distributor,
788:             operator
789:         ), 
790:         gasValue, msg.sender);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L780-L789

20) contracts/its/interchain-token-service/InterchainTokenService.sol :: _deployStandardizedToken(), this function gas saving has been calculated using deployAndRegisterStandardizedToken function as it is an internal function used by deployAndRegisterStandardizedToken

Here, salt and tokenManagerAddress cached in lines 850 and 851 has been used only once in line 856 and 857 respectively

avg. gas saving obtained : 32
Functionminmaxavgcalls
BeforedeployAndRegisterStandardizedToken91276391285991280510
AfterdeployAndRegisterStandardizedToken91273191282791277310
File: contracts/its/interchain-token-service/InterchainTokenService.sol
850:         bytes32 salt = _getStandardizedTokenSalt(tokenId);
851:         address tokenManagerAddress = getTokenManagerAddress(tokenId);
852: 
853:         (bool success, ) = standardizedTokenDeployer.delegatecall(
854:             abi.encodeWithSelector(
855:                 IStandardizedTokenDeployer.deployStandardizedToken.selector,
856:                 salt,
857:                 tokenManagerAddress,

recommended code :

850:         (bool success, ) = standardizedTokenDeployer.delegatecall(
851:             abi.encodeWithSelector(
852:                 IStandardizedTokenDeployer.deployStandardizedToken.selector,
853:                 _getStandardizedTokenSalt(tokenId),
854:                 getTokenManagerAddress(tokenId),

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token-service/InterchainTokenService.sol#L850-L857

21) contracts/its/proxies/TokenManagerProxy.sol :: constructor()

Here, impl cached in line 34 has been used only once in line 36

File: contracts/its/proxies/TokenManagerProxy.sol
34:         address impl = _getImplementation(IInterchainTokenService(interchainTokenServiceAddress_), implementationType_);
35: 
36:         (bool success, ) = impl.delegatecall(abi.encodeWithSelector(TokenManagerProxy.setup.selector, params));

recommended code :

35:         (bool success, ) = _getImplementation(IInterchainTokenService(interchainTokenServiceAddress_), implementationType_)
36:         .delegatecall(abi.encodeWithSelector(TokenManagerProxy.setup.selector, params));

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/proxies/TokenManagerProxy.sol#L34-L36

22) contracts/its/remote-address-validator/RemoteAddressValidator.sol :: validateSender()

Here, sourceAddressLC cached in line 70 has been used only once in line 71

File: contracts/its/remote-address-validator/RemoteAddressValidator.sol
70:         string memory sourceAddressLC = _lowerCase(sourceAddress);
71:         bytes32 sourceAddressHash = keccak256(bytes(sourceAddressLC));

recommended code :

70:         bytes32 sourceAddressHash = keccak256(bytes(_lowerCase(sourceAddress)));

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/remote-address-validator/RemoteAddressValidator.sol#L70-L71

23) contracts/its/token-manager/implementations/TokenManagerMintBurn.sol :: _takeToken()

Here, token cached in line 46 has been used only once in line 48

File: contracts/its/token-manager/implementations/TokenManagerMintBurn.sol
46:         IERC20 token = IERC20(tokenAddress());
47: 
48:         SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20BurnableMintable.burn.selector, from, amount));

recommended code :

46:         SafeTokenCall.safeCall(IERC20(tokenAddress()), abi.encodeWithSelector(IERC20BurnableMintable.burn.selector, from, amount));

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/token-manager/implementations/TokenManagerMintBurn.sol#L46-L48

24) contracts/its/token-manager/implementations/TokenManagerMintBurn.sol :: _giveToken()

Here, token cached in line 60 has been used only once in line 62

File: contracts/its/token-manager/implementations/TokenManagerMintBurn.sol
60:         IERC20 token = IERC20(tokenAddress());
61: 
62:         SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20BurnableMintable.mint.selector, to, amount));

recommended code :

60:         SafeTokenCall.safeCall(IERC20(tokenAddress()), abi.encodeWithSelector(IERC20BurnableMintable.mint.selector, to, amount));

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/token-manager/implementations/TokenManagerMintBurn.sol#L60-L62

25) contracts/its/utils/FlowLimit.sol :: getFlowOutAmount()

Here, epoch cached in line 64 has been used only once in line 65

File: contracts/its/utils/FlowLimit.sol
64:         uint256 epoch = block.timestamp / EPOCH_TIME;
65:         uint256 slot = _getFlowOutSlot(epoch);

recommended code :

64:         uint256 slot = _getFlowOutSlot(block.timestamp / EPOCH_TIME);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/utils/FlowLimit.sol#L64-L65

26) contracts/its/utils/FlowLimit.sol :: getFlowInAmount()

Here, epoch cached in line 76 has been used only once in line 77

File: contracts/its/utils/FlowLimit.sol
76:         uint256 epoch = block.timestamp / EPOCH_TIME;
77:         uint256 slot = _getFlowInSlot(epoch);

recommended code :

76:         uint256 slot = _getFlowInSlot(block.timestamp / EPOCH_TIME);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/utils/FlowLimit.sol#L76-L77

27) contracts/its/utils/FlowLimit.sol :: _addFlowOut()

Here, slotToAdd and slotToCompare cached in lines 115 and 116 has been used only once in line 117

No test has been provided for the specific function involved in this instance from protocol's side(using which actual gas saving could be calculated).But,this function has been mimiced by addFlowOut function in the required test script using which the demonstrated gas saving has been calculated

avg. gas saving obtained : 26
Functionminmaxavgcalls
BeforeaddFlowOut31471485713717112
AfteraddFlowOut31445485453714512
File: contracts/its/utils/FlowLimit.sol
115:         uint256 slotToAdd = _getFlowOutSlot(epoch);
116:         uint256 slotToCompare = _getFlowInSlot(epoch);
117:         _addFlow(flowLimit, slotToAdd, slotToCompare, flowOutAmount);

recommended code :

115:         _addFlow(flowLimit, _getFlowOutSlot(epoch), _getFlowInSlot(epoch), flowOutAmount);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/utils/FlowLimit.sol#L115-L117

28) contracts/its/utils/FlowLimit.sol :: _addFlowIn()

Here, slotToAdd and slotToCompare cached in lines 128 and 129 has been used only once in line 130

avg. gas saving obtained : 26

No test has been provided for the specific function involved in this instance from protocol's side(using which actual gas saving could be calculated).But,this function has been mimiced by addFlowIn function in the required test script using which the demonstrated gas saving has been calculated

Functionminmaxavgcalls
BeforeaddFlowIn31428485283712812
AfteraddFlowIn31402485023710212
File: contracts/its/utils/FlowLimit.sol
128:         uint256 slotToAdd = _getFlowInSlot(epoch);
129:         uint256 slotToCompare = _getFlowOutSlot(epoch);
130:         _addFlow(flowLimit, slotToAdd, slotToCompare, flowInAmount);

recommended code :

128:         _addFlow(flowLimit, _getFlowInSlot(epoch), _getFlowOutSlot(epoch), flowInAmount);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/utils/FlowLimit.sol#L128-L130

29) contracts/its/utils/StandardizedTokenDeployer.sol :: deployStandardizedToken()

Here, implementationAddress and params cached in lines 60 and 62 has been used only once in line 63.And bytecode and tokenAddress cached in lines 63 and 65 has been used only once in line 65 and 66respectively

avg. gas saving obtained : 47
Functionminmaxavgcalls
BeforedeployStandardizedToken4804054804074804066
AfterdeployStandardizedToken4803544803644803596
File: contracts/its/utils/StandardizedTokenDeployer.sol
59:         bytes memory bytecode;
60:         address implementationAddress = distributor == tokenManager ? implementationMintBurnAddress : implementationLockUnlockAddress;
61:         {
62:             bytes memory params = abi.encode(tokenManager, distributor, name, symbol, decimals, mintAmount, mintTo);
63:             bytecode = abi.encodePacked(type(StandardizedTokenProxy).creationCode, abi.encode(implementationAddress, params));
64:         }
65:         address tokenAddress = deployer.deploy(bytecode, salt);
66:         if (tokenAddress.code.length == 0) revert TokenDeploymentFailed();

recommended code :

59:         if (
60:             (deployer.deploy(
61:             abi.encodePacked(type(StandardizedTokenProxy).creationCode, abi.encode(
62:                 distributor == tokenManager ? implementationMintBurnAddress : implementationLockUnlockAddress, 
63:                 abi.encode(tokenManager, distributor, name, symbol, decimals, mintAmount, mintTo))), 
64:                 salt))
65:                 .code.length == 0
66:            ) revert TokenDeploymentFailed();

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/utils/StandardizedTokenDeployer.sol#L59-L66

30) contracts/its/utils/TokenManagerDeployer.sol :: deployTokenManager()

Here, args, bytecode and tokenManagerAddress cached in lines 38, 39 and 40 has been used only once in lines 39, 40 and 41 respectively

File: contracts/its/utils/TokenManagerDeployer.sol
38:         bytes memory args = abi.encode(address(this), implementationType, tokenId, params);
39:         bytes memory bytecode = abi.encodePacked(type(TokenManagerProxy).creationCode, args);
40:         address tokenManagerAddress = deployer.deploy(bytecode, tokenId);
41:         if (tokenManagerAddress.code.length == 0) revert TokenManagerDeploymentFailed();

recommended code :

38:         if (
39:             (deployer.deploy(
40:             abi.encodePacked(type(TokenManagerProxy).creationCode, 
41:             abi.encode(address(this), implementationType, tokenId, params)),
42:             tokenId))
43:             .code.length == 0
44:            ) revert TokenManagerDeploymentFailed();

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/utils/TokenManagerDeployer.sol#L38-L41

[G-10] : Use assembly to write contract type/address storage varibales

3 instances in 2 files

1) contracts/interchain-governance-executor/InterchainProposalSender.sol :: constructor()

File: contracts/interchain-governance-executor/InterchainProposalSender.sol
43:         gateway = IAxelarGateway(_gateway);
44:         gasService = IAxelarGasService(_gasService);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/interchain-governance-executor/InterchainProposalSender.sol#L43-L44

2) contracts/its/token-implementations/StandardizedToken.sol :: setup()

File: contracts/its/token-implementations/StandardizedToken.sol
56:             tokenManager = tokenManager_;

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/token-implementations/StandardizedToken.sol#L56

[G-11] : Cache calldata/memory pointers for complex types to avoid complex offset calculations

The function parameters in the following instances are complex types and thus will result in more complex offset calculations to retrieve specific data from calldata. We can avoid peforming some of these offset calculations by instantiating calldata/memory pointers.

6 instance in 1 file

1) contracts/interchain-governance-executor/InterchainProposalSender.sol :: _sendProposal(), cache interchainCall.gas, interchainCall.destinationChain and interchainCall.destinationContract

avg. gas saving obtained : 30
Functionminmaxavgcalls
BeforesendProposals--1150058
AftersendProposals--1149758

Note : The calculated gas saving is applicable only when the condition on line 91 in the original contract is satisfied.

File: contracts/interchain-governance-executor/InterchainProposalSender.sol
091:         if (interchainCall.gas > 0) { // @audit : 1st access
092:             gasService.payNativeGasForContractCall{ value: interchainCall.gas }( // @audit : 2nd access
093:                 address(this),
094:                 interchainCall.destinationChain, // @audit : 1st access
095:                 interchainCall.destinationContract, // @audit : 1st access
097:                 msg.sender
098:             );
099:         }
100: 
101:         gateway.callContract(interchainCall.destinationChain, interchainCall.destinationContract, payload); // @audit : 2nd access

recommended code :

091:         uint256 interchainCallGas = interchainCall.gas;
092:         string memory interchainCallDestinationChain = interchainCall.destinationChain;
093:         string memory interchainCallDestinationContract = interchainCall.destinationContract;
094: 
095:         if (interchainCallGas > 0) {
096:             gasService.payNativeGasForContractCall{ value: interchainCallGas }(
097:                 address(this),
098:                 interchainCallDestinationChain,
099:                 interchainCallDestinationContract,
100:                 payload,
101:                 msg.sender
102:             );
103:         }
104: 
105:         gateway.callContract(interchainCallDestinationChain, interchainCallDestinationContract, payload);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/interchain-governance-executor/InterchainProposalSender.sol#L91-L101

[G-12] : Use a positive conditional flow to save a NOT opcode

By switiching to a positive conditional flow in if statement, a NOT opcode(costing 3 gas) can be saved

1 instance in 1 file

Estimated gas saving : 3 * no. of iterations for which the for loop will be executed as piece of the code where the issue has been identified is being used inside a for loop

1) contracts/interchain-governance-executor/InterchainProposalExecutor.sol :: _executeProposal()

File: contracts/interchain-governance-executor/InterchainProposalExecutor.sol
78:             if (!success) {
79:                 _onTargetExecutionFailed(call, result);
80:             } else {
81:                 _onTargetExecuted(call, result);
82:             }

recommended code :

78:             if (success) {
79:                 _onTargetExecuted(call, result);
80:             } else {
81:                 _onTargetExecutionFailed(call, result);
82:             }

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/interchain-governance-executor/InterchainProposalExecutor.sol#L78-L82

#0 - c4-pre-sort

2023-07-29T01:13:16Z

0xSorryNotSorry marked the issue as high quality report

#1 - c4-judge

2023-09-04T19:38:07Z

berndartmueller marked the issue as grade-a

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