Axelar Network - hunter_w3b'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: 41/47

Findings: 1

Award: $19.28

Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

19.2767 USDC - $19.28

Labels

bug
G (Gas Optimization)
grade-b
edited-by-warden
G-11

External Links

Gas Optimization

Summary

NumberOptimization DetailsContext
[G-00]IInterchainTokenService::getChainName function that returns the name of the current chain as a string introduce higher gas costs and increase storage1
[G-01]Using calldata instead of memory for read-only arguments in external functions saves gas10
[G-02]TokenManagerLockUnlock.sol::_takeToken SafeTokenTransfer we should check amount for possible gas save1
[G-03]Using storage instead of memory for structs/arrays saves gas7
[G-04]Keccak256() should only need to be called on a specific string literal once4
[G-05]Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if statement4
[G-06]Functions guaranteed to when called by normal users can be marked Payable16
[G-07]Empty blocks should be removed or emit something8
[G-08]With assembly, .call (bool success)  transfer can be done gas-optimized9
[G-09]Can make the variable outside the loop to save gas4
[G-10]Use assembly to write address storage values3
[G-11]Duplicated if() checks should be refactored to a modifier or function8
[G-12]Should use arguments instead of state variable1
[G-13]Not using the named return variable when a function returns, wastes deployment gas3
[G-14]Expressions for constant values such as a call to keccak256(), should use immutable rather than constant1
[G-15]Multiple address /ID mappings can be combined into a single mapping of an address/ID to a struct , where appropriate1
[G-16]Don’t Initialize Variables with Default Value4
[G-17]abi.encode() is less efficient than abi.encodePacked()31
[G-18]Use hardcode address instead address(this)25
[G-19]Amounts should be checked for 0 before calling a transfer9
[G-20]Using bools for storage incurs overhead6
[G-21]Make 3 event parameters indexed when possible8
[G-22]Use constants instead of type(uintx).max10
[G-23]Using a positive conditional flow to save a NOT opcode18
[G-24]Using XOR (^) and OR () bitwise equivalents3
[G-25]If-statements that use && can be refactored into nested if statements6
[G-26]x += y costs more gas than x = x + y for state variables1
[G-27]Use custom errors rather than revert()/require() strings to save gas-
[G-28]Cache state variables outside of loop to avoid reading storage on every iteration4
[G-29]Use do while loop instead of for loop1

[G-0] IInterchainTokenService::getChainName function that returns the name of the current chain as a string introduce higher gas costs and increase storage

Using strings for chain names can introduce higher gas costs and increase storage requirements compared to using more efficient data types like enums or bytes32. The getChainName() function's use of a string may lead to unintended consequences in certain scenarios.

impact

The potential impact of using strings for chain names is relatively low, but it can become a concern in certain situations where gas efficiency and storage optimization are crucial. For instance, if the contract frequently performs string manipulations or comparisons, it can lead to increased transaction costs and might not be ideal for performance-critical applications.

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interfaces/IInterchainTokenService.sol#L94-L95

File: contracts/its/interfaces/IInterchainTokenService.sol

94    function getChainName() external view returns (string memory name);

To potentially improve gas efficiency and reduce storage costs, consider using an enum or bytes32 data type to represent the chain name, depending on the specific requirements of the contract. If the chain names are a fixed and well-defined set of options, using an enum would be an efficient choice. On the other hand, if dynamic chain names are required, using bytes32 might offer a more efficient alternative to strings.

[G-01] Using calldata instead of memory for read-only arguments in external functions saves gas

When you specify a data location as memory, that value will be copied into memory. When you specify the location as calldata, the value will stay static within calldata. If the value is a large, complex type, using memory may result in extra memory expansion costs.

file:  contracts/cgp/auth/MultisigBase.sol

-142   function rotateSigners(address[] memory newAccounts, uint256 newThreshold) external virtual onlySigners
+142   function rotateSigners(address[] calldata newAccounts, uint256 newThreshold) external virtual onlySigners

-149   function _rotateSigners(address[] memory newAccounts, uint256 newThreshold) internal {
+      function _rotateSigners(address[] calldata newAccounts, uint256 newThreshold) internal {

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

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

file:   contracts/cgp/interfaces/IMultisigBase.sol

73     function rotateSigners(address[] memory newAccounts, uint256 newThreshold) external;

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/cgp/interfaces/IMultisigBase.sol#L73

file:   contracts/gmp-sdk/deploy/ConstAddressDeployer.sol

42        function deployAndInit(
        bytes memory bytecode,
        bytes32 salt,
        bytes calldata init
    ) external returns

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/gmp-sdk/deploy/ConstAddressDeployer.sol#L42-L46

file:   contracts/gmp-sdk/deploy/Create3.sol

21    function deploy(bytes memory bytecode) external

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/gmp-sdk/deploy/Create3.sol#L21

file:   contracts/interchain-governance-executor/InterchainProposalExecutor.sol

73    function _executeProposal(InterchainCalls.Call[] memory calls) internal

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

file:   contracts/interchain-governance-executor/interfaces/IInterchainProposalSender.sol

15      function sendProposals(InterchainCalls.InterchainCall[] memory calls) external payable;

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/interchain-governance-executor/interfaces/IInterchainProposalSender.sol#L15

file:     contracts/its/interchain-token-service/InterchainTokenService.sol

559      function _sanitizeTokenManagerImplementation(address[] memory implementaions, TokenManagerType tokenManagerType)

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

file:    contracts/its/interfaces/IMulticall.sol

18      function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/its/interfaces/IMulticall.sol#L18

file:   contracts/its/utils/Multicall.sol

22     function multicall(bytes[] calldata data) public payable returns (bytes[] memory results)

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

[G-02] TokenManagerLockUnlock.sol::_takeToken SafeTokenTransfer we should check amount for possible gas save

Before transfer, we should check for amount being 0 so the function doesn't run when its not gonna do anything:

file: /contracts/its/token-manager/implementations/TokenManagerLockUnlock.sol

48        SafeTokenTransferFrom.safeTransferFrom(token, from, address(this), amount);

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

diff --git a/TokenManagerLockUnlock.org.sol b/TokenManagerLockUnlock.sol
index cb5b187..2888bb0 100644
--- a/TokenManagerLockUnlock.org.sol
+++ b/TokenManagerLockUnlock.sol
@@ -2,7 +2,7 @@
         IERC20 token = IERC20(tokenAddress());
         uint256 balance = token.balanceOf(address(this));

-        SafeTokenTransferFrom.safeTransferFrom(token, from, address(this), amount);
+        if (amount != 0) SafeTokenTransferFrom.safeTransferFrom(token, from, address(this), amount);

         // Note: This allows support for fee-on-transfer tokens
         return IERC20(token).balanceOf(address(this)) - balance;

[G-03] Using storage instead of memory for structs/arrays saves gas

When fetching data from a storage location, assigning the data to a memory variable causes all fields of the struct/array to be read from storage, which incurs a Gcoldsload (2100 gas) for each field of the struct/array. If the fields are read from the new memory variable, they incur an additional MLOAD rather than a cheap stack read. Instead of declearing the variable with the memory keyword, declaring the variable with the storage keyword and caching any fields that need to be re-read in stack variables, will be much cheaper, only incuring the Gcoldsload for the fields actually read. The only time it makes sense to read the whole struct/array into a memory variable, is if the full struct/array is being returned by the function, is being passed to a function that requires memory, or if the array/struct is being read from another memory array/struct

file: /contracts/cgp/AxelarGateway.sol

-332        bytes32[] memory commandIds;
+           bytes32[] storage commandIds;

-334        string[] memory commands;
+           string[] storage commands;


-334        bytes[] memory params;
+           bytes[] storage params;

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

file: /contracts/interchain-governance-executor/InterchainProposalExecutor.sol

34        (address interchainProposalCaller, InterchainCalls.Call[] memory calls) = abi.decode(payload, (address, InterchainCalls.Call[]));

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

file: /contracts/its/remote-address-validator/RemoteAddressValidator.sol

41        (string[] memory trustedChainNames, string[] memory trustedAddresses) = abi.decode(params, (string[], string[]));

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

file: /contracts/cgp/governance/AxelarServiceGovernance.sol

38        address[] memory cosigners,

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

file: /contracts/cgp/auth/MultisigBase.sol

35    constructor(address[] memory accounts, uint256 threshold) {

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

[G-04] Keccak256() should only need to be called on a specific string literal once

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

file: /contracts/its/interchain-token-service/InterchainTokenService.sol

71     bytes32 private constant CONTRACT_ID = keccak256('interchain-token-service');

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

file: /contracts/its/proxies/InterchainTokenServiceProxy.sol

12     bytes32 private constant CONTRACT_ID = keccak256('interchain-token-service');

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

file: /contracts/its/proxies/StandardizedTokenProxy.sol

14     bytes32 private constant CONTRACT_ID = keccak256('standardized-token');

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

file: /contracts/its/token-implementations/StandardizedToken.sol#L27

27     bytes32 private constant CONTRACT_ID = keccak256('standardized-token');

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

[G-05] Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if statement

require(a <= b); x = b - a => require(a <= b); unchecked { x = b - a } if(a <= b); x = b - a => if(a <= b); unchecked { x = b - a } This will stop the check for overflow and underflow so it will save gas

file: /contracts/its/interchain-token/InterchainToken.sol

57                if (allowance_ > type(uint256).max - amount) {

58                allowance_ = type(uint256).max - amount;

89            _approve(sender, msg.sender, _allowance - amount);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/interchain-token/InterchainToken.sol#L57

File: /contracts/its/token-manager/implementations/TokenManagerLockUnlock.sol

66        return IERC20(token).balanceOf(to) - balance;

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

[G-06] Functions guaranteed to when called by normal users can be marked Payable

The onlyOwner modifier makes a function revert if not called by the address registered as the owner

file: /contracts/its/interchain-token-service/InterchainTokenService.sol

-534    function setFlowLimit(bytes32[] calldata tokenIds, uint256[] calldata flowLimits) external onlyOperator {
+534    function setFlowLimit(bytes32[] calldata tokenIds, uint256[] calldata flowLimits) external payable onlyOperator {


-547    function setPaused(bool paused) external onlyOwner {
+       function setPaused(bool paused) external payable onlyOwner {

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

file: /contracts/its/token-manager/implementations/TokenManagerLiquidityPool.sol

67    function setLiquidityPool(address newLiquidityPool) external onlyOperator {

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

file: /contracts/its/utils/Distributable.sol

51    function setDistributor(address distr) external onlyDistributor {

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

file: /contracts/its/token-implementations/StandardizedToken.sol

49    function setup(bytes calldata params) external override onlyProxy {

76    function mint(address account, uint256 amount) external onlyDistributor {

86    function burn(address account, uint256 amount) external onlyDistributor {

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

file: /contracts/its/remote-address-validator/RemoteAddressValidator.sol

83     function addTrustedAddress(string memory chain, string memory addr) public onlyOwner {

95     function removeTrustedAddress(string calldata chain) external onlyOwner {

106    function addGatewaySupportedChains(string[] calldata chainNames) external onlyOwner {

119    function removeGatewaySupportedChains(string[] calldata chainNames) external onlyOwner {

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

file: /contracts/cgp/auth/MultisigBase.sol

142     function rotateSigners(address[] memory newAccounts, uint256 newThreshold) external virtual onlySigners {

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

file: /contracts/interchain-governance-executor/InterchainProposalExecutor.sol

96     ) external override onlyOwner {

111    ) external override onlyOwner {

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

file: /contracts/gmp-sdk/upgradable/Upgradable.sol

56    ) external override onlyOwner {

78    function setup(bytes calldata data) external override onlyProxy {

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/upgradable/Upgradable.sol#L56

[G-07] Empty blocks should be removed or emit something

The code should be refactored such that they no longer exist, or the block should do something useful, such as emitting an event or reverting. If the contract is meant to be extended, the contract should be abstract and the function signatures be added without any default implementation.

file: /contracts/gmp-sdk/upgradable/BaseProxy.sol

32    function setup(bytes calldata params) external {}

67    receive() external payable virtual {}

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/upgradable/BaseProxy.sol#L32

file: /contracts/its/proxies/TokenManagerProxy.sol

66    function setup(bytes calldata setupParams) external {}

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

file: /contracts/cgp/governance/InterchainGovernance.sol

168    receive() external payable {}

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

file: /contracts/cgp/governance/AxelarServiceGovernance.sol

40    ) InterchainGovernance(gateway, governanceChain, governanceAddress, minimumTimeDelay) MultisigBase(cosigners, threshold) {}

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

file: /contracts/cgp/governance/Multisig.sol

20    constructor(address[] memory accounts, uint256 threshold) MultisigBase(accounts, threshold) {}

41    receive() external payable {}

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

file: /contracts/gmp-sdk/upgradable/Upgradable.sol

87    function _setup(bytes calldata data) internal virtual {}

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/upgradable/Upgradable.sol#L87

[G-08] With assembly, .call (bool success)  transfer can be done gas-optimized

return data (bool success,) has to be stored due to EVM architecture, but in a usage like below, ‘out’ and ‘outsize’ values are given (0,0),this storage disappears and gas optimization is provided.

file: /contracts/cgp/util/Caller.sol

18        (bool success, ) = target.call{ value: nativeValue }(callData);

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/util/Caller.sol#L18

file: /contracts/cgp/AxelarGateway.sol

374            (bool success, ) = address(this).call(abi.encodeWithSelector(commandSelector, params[i], commandId));

398            (bool success, bytes memory data) = TOKEN_DEPLOYER_IMPLEMENTATION.delegatecall(

441            (bool success, bytes memory returnData) = depositHandler.execute(

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

file: /contracts/interchain-governance-executor/InterchainProposalExecutor.sol

76            (bool success, bytes memory result) = call.target.call{ value: call.value }(call.callData);

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

file: /contracts/gmp-sdk/deploy/ConstAddressDeployer.sol

50        (bool success, ) = deployedAddress_.call(init);

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

file: /contracts/gmp-sdk/deploy/Create3Deployer.sol

57        (bool success, ) = deployedAddress_.call(init);

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

file: /contracts/gmp-sdk/upgradable/FinalProxy.sol

81            (bool success, ) = finalImplementation_.delegatecall(abi.encodeWithSelector(BaseProxy.setup.selector, setupParams));

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/upgradable/FinalProxy.sol#L81

file: /contracts/its/utils/Multicall.sol

25            (bool success, bytes memory result) = address(this).delegatecall(data[i]);

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

[G-09] Can make the variable outside the loop to save gas

Consider making the stack variables before the loop which gonna save gas

file: /contracts/cgp/auth/MultisigBase.sol

169            address account = newAccounts[i];

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

file: /contracts/cgp/AxelarGateway.sol

271            string memory symbol = symbols[i];

272            uint256 limit = limits[i];

345            bytes32 commandId = commandIds[i];

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

[G-10] Use assembly to write address storage values

By using assembly to write to address storage values, you can bypass some of these operations and lower the gas cost of writing to storage. Assembly code allows you to directly access the Ethereum Virtual Machine (EVM) and perform low-level operations that are not possible in Solidity.

example of using assembly to write to address storage values:

contract MyContract {
    address private myAddress;

    function setAddressUsingAssembly(address newAddress) public {
        assembly {
            sstore(0, newAddress)
        }
    }
}
file: /contracts/its/to6en-implementations/StandardizedToken.sol

56            tokenManager = tokenManager_;

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

file: /contracts/its/utils/StandardizedTokenDeployer.sol

34        implementationLockUnlockAddress = implementationLockUnlockAddress_;

35        implementationMintBurnAddress = implementationMintBurnAddress_;

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

[G-11] Duplicated if() checks should be refactored to a modifier or function

to reduce code duplication and improve readability. •  Modifiers can be used to perform additional checks on the function inputs or state before it is executed. By defining a modifier to perform a specific check, we can reuse it across multiple functions that require the same check. • A function can also be used to perform a specific check and return a boolean value indicating whether the check has passed or failed. This can be useful when the check is more complex and cannot be performed easily in a modifier.

file: /contracts/gmp-sdk/util/TimeLock.sol

51        if (hash == 0) revert InvalidTimeLockHash();

68        if (hash == 0) revert InvalidTimeLockHash();

https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/util/TimeLock.sol#L51

file: /contracts/cgp/AxelarGateway.sol

432        if (tokenAddress == address(0)) revert TokenDoesNotExist(symbol);

519        if (tokenAddress == address(0)) revert TokenDoesNotExist(symbol);

537        if (tokenAddress == address(0)) revert TokenDoesNotExist(symbol);

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

file: /contracts/its/utils/ExpressCallHandler.sol

91        if (prevExpressCaller != address(0)) revert AlreadyExpressCalled();

133       if (prevExpressCaller != address(0)) revert AlreadyExpressCalled();

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

[G-12] Should use arguments instead of state variable

state variables should not used in emit , This will save near 97 gas

file: /contracts/cgp/AxelarGateway.sol

///@audit  the ' KEY_MINT_LIMITER ' is state variable , first should cached to stack and after that should emit.

688        emit MintLimiterTransferred(getAddress(KEY_MINT_LIMITER), newMintLimiter);

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

[G-13] Not using the named return variable when a function returns, wastes deployment gas

file: /contracts/gmp-sdk/deploy/Create3.sol

74        deployed = address(uint160(uint256(keccak256(abi.encodePacked(hex'd6_94', deployer, hex'01')))));

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

file: /contracts/its/utils/FlowLimit.sol

47        slot = uint256(keccak256(abi.encode(PREFIX_FLOW_OUT_AMOUNT, epoch)));

56        slot = uint256(keccak256(abi.encode(PREFIX_FLOW_IN_AMOUNT, epoch)));

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

[G-14] Expressions for constant values such as a call to keccak256(), should use immutable rather than constant

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

file: /contracts/gmp-sdk/deploy/Create3.sol

41     bytes32 internal constant DEPLOYER_BYTECODE_HASH = keccak256(type(CreateDeployer).creationCode);

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

[G-15] Multiple address /ID mappings can be combined into a single mapping of an address/ID to a struct , where appropriate

Saves a storage slot for the mapping. Depending on the circumstances and sizes of types, can avoid a Gsset (20000 gas) per mapping combined. Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot. Finally, if both fields are accessed in the same function, can save ~42 gas per access due to not having to re the key’s keccak256 hash (Gkeccak256 - 30 gas) and that calculation’s associated stack operations.

file: /contracts/interchain-governance-executor/InterchainProposalExecutor.sol

24    mapping(string => mapping(address => bool)) public whitelistedCallers;


27    mapping(string => mapping(address => bool)) public whitelistedSenders;

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

[G-16] Don’t Initialize Variables with Default Value

Uninitialized variables are assigned with the types default value. Explicitly initializing a variable with it’s default value costs unnecesary gas.

file:    contracts/interchain-governance-executor/InterchainProposalExecutor.sol

74      for (uint256 i = 0; i < calls.length; i++)

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

file:  contracts/interchain-governance-executor/InterchainProposalSender.sol

63     for (uint256 i = 0; i < interchainCalls.length; )

106    for (uint256 i = 0; i < interchainCalls.length; )

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

file:   contracts/its/utils/Multicall.sol

24     for (uint256 i = 0; i < data.length; ++i)

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

[G-17] abi.encode() is less efficient than abi.encodePacked()

use abi.encodePacked() where possible to save gas

file:   contracts/its/interchain-token-service/InterchainTokenService.sol

203    tokenId = keccak256(abi.encode(PREFIX_STANDARDIZED_TOKEN_ID, chainNameHash, tokenAddress));

214    tokenId = keccak256(abi.encode(PREFIX_CUSTOM_TOKEN_ID, sender, salt));

242    params = abi.encode(operator, tokenAddress);

252    params = abi.encode(operator, tokenAddress);

267    params = abi.encode(operator, tokenAddress, liquidityPoolAddress);

313    _deployTokenManager(tokenId, TokenManagerType.LOCK_UNLOCK, abi.encode(address(this).toBytes(),
tokenAddress));

401    _deployTokenManager(tokenId, tokenManagerType, abi.encode(msg.sender.toBytes(), tokenAddress));

512    payload = abi.encode(SELECTOR_SEND_TOKEN, tokenId, destinationAddress, amount);

520    payload = abi.encode(SELECTOR_SEND_TOKEN_WITH_DATA, tokenId, destinationAddress, amount, sourceAddress.toBytes(), metadata);

696    abi.encode(operatorBytes.length == 0 ? address(this).toBytes() : operatorBytes, tokenAddress)

755    bytes memory payload = abi.encode(SELECTOR_DEPLOY_TOKEN_MANAGER, tokenId, tokenManagerType, params);

780    bytes memory payload = abi.encode(

828    return keccak256(abi.encode(PREFIX_STANDARDIZED_TOKEN_SALT, tokenId));

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

file:  contracts/its/utils/ExpressCallHandler.sol

32      slot = uint256(keccak256(abi.encode(PREFIX_EXPRESS_RECEIVE_TOKEN, tokenId, destinationAddress, amount, commandId)))

57      abi.encode(

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

file:   contracts/its/utils/FlowLimit.sol

47      slot = uint256(keccak256(abi.encode(PREFIX_FLOW_OUT_AMOUNT, epoch)));

56      slot = uint256(keccak256(abi.encode(PREFIX_FLOW_IN_AMOUNT, epoch)));

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

file:     contracts/its/utils/StandardizedTokenDeployer.sol

62        bytes memory params = abi.encode(tokenManager, distributor, name, symbol, decimals, mintAmount, mintTo);

63        bytecode = abi.encodePacked(type(StandardizedTokenProxy).creationCode, abi.encode(implementationAddress, params));

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


file:   contracts/its/utils/TokenManagerDeployer.sol

38     bytes memory args = abi.encode(address(this), implementationType, tokenId, params);

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

file:   contracts/cgp/AxelarGateway.sol

562     return keccak256(abi.encode(PREFIX_TOKEN_MINT_AMOUNT, symbol, day));

584     return keccak256(abi.encode(PREFIX_CONTRACT_CALL_APPROVED, commandId, sourceChain, sourceAddress, contractAddress, payloadHash));

597               keccak256(
                abi.encode(
                    PREFIX_CONTRACT_CALL_APPROVED_WITH_MINT,
                    commandId,
                    sourceChain,
                    sourceAddress,
                    contractAddress,
                    payloadHash,
                    symbol,
                    amount
                )
            );

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

file:    contracts/gmp-sdk/deploy/ConstAddressDeployer.sol
25       deployedAddress_ = _deploy(bytecode, keccak256(abi.encode(msg.sender, salt)));

47       deployedAddress_ = _deploy(bytecode, keccak256(abi.encode(msg.sender, salt)));

63       bytes32 newSalt = keccak256(abi.encode(sender, salt));

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

file: contracts/gmp-sdk/deploy/Create3Deployer.sol

30    bytes32 deploySalt = keccak256(abi.encode(msg.sender, salt));

54    bytes32 deploySalt = keccak256(abi.encode(msg.sender, salt));

68    bytes32 deploySalt = keccak256(abi.encode(sender, salt));

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

file:  contracts/interchain-governance-executor/InterchainProposalExecutor.sol

66     emit ProposalExecuted(keccak256(abi.encode(sourceChain, sourceAddress, interchainProposalCaller, payload)));

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

file:  contracts/interchain-governance-executor/InterchainProposalSender.sol
89     bytes memory payload = abi.encode(msg.sender, interchainCall.calls);

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

[G-18] Use hardcode address instead address(this)

Instead of using address(this), it is more gas-efficient to pre-calculate and use the hardcoded address. Foundry’s script.sol and solmate’s LibRlp.sol contracts can help achieve this. In Solidity, the address(this) expression returns the address of the current contract instance. This expression is commonly used to send or receive Ether to or from the contract.

The statement "Use hardcoded address instead of address(this) to save gas cost" means that using a hardcoded address instead of the address(this) expression can be more gas-efficient when sending or receiving Ether to or from the contract. This is because using the address(this) expression requires additional gas to be consumed to retrieve the address of the current contract, while using a hardcoded address does not.

For example, consider the following code:

contract MyContract {
  address public myAddress = 0x1234567890123456789012345678901234567890;

  function receiveEther() public payable {
    // Receive Ether using address(this)
    // msg.value is the value of the Ether being sent
    address(this).transfer(msg.value);
  }

  function receiveEtherHardcoded() public payable {
    // Receive Ether using hardcoded address
    // msg.value is the value of the Ether being sent
    myAddress.transfer(msg.value);
  }
}

In this code, the receiveEther() function receives Ether using address(this), while the receiveEtherHardcoded() function receives Ether using a hardcoded address. Although both functions achieve the same result, the receiveEtherHardcoded() function is more gas-efficient, as it does not require additional gas to retrieve the address of the current contract.

Therefore, using a hardcoded address instead of the address(this) expression can be a gas-efficient way to send or receive Ether to or from a smart contract, as it reduces the gas cost of the contract. However, it is important to ensure that the hardcoded address is correct and does not change during the contract's lifetime.

file:   contracts/gmp-sdk/upgradable/Upgradable.sol

23      implementationAddress = address(this);

31      if (address(this) == implementationAddress) revert NotProxy();

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/gmp-sdk/upgradable/Upgradable.sol#L23

file:  contracts/cgp/util/Caller.sol

16     if (nativeValue > address(this).balance) revert InsufficientBalance();

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/cgp/util/Caller.sol#L16

file:   contracts/gmp-sdk/deploy/Create3.sol

52      deployed = deployedAddress(address(this), salt);

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/gmp-sdk/deploy/Create3.sol#L52

file:   contracts/gmp-sdk/deploy/Create3Deployer.sol

69      return Create3.deployedAddress(address(this), deploySalt);

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

file:   contracts/cgp/AxelarGateway.sol

73      if (msg.sender != address(this)) revert NotSelf();

374     (bool success, ) = address(this).call(abi.encodeWithSelector(commandSelector, params[i], commandId));

443     abi.encodeWithSelector(IERC20.transfer.selector, address(this), IERC20(tokenAddress).balanceOf(address(depositHandler)))

449     depositHandler.destroy(address(this));

543     IERC20(tokenAddress).safeTransferFrom(sender, address(this), amount);

616     return address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, codeHash)))));

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

file:  contracts/gmp-sdk/upgradable/FinalProxy.sol

61     implementation_ = Create3.deployedAddress(address(this), FINAL_IMPLEMENTATION_SALT);

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/gmp-sdk/upgradable/FinalProxy.sol#L61

file:    contracts/its/interchain-token-service/InterchainTokenService.sol

162      tokenManagerAddress = deployer.deployedAddress(address(this), tokenId);

193      tokenAddress = deployer.deployedAddress(address(this), tokenId);

313      _deployTokenManager(tokenId, TokenManagerType.LOCK_UNLOCK, abi.encode(address(this).toBytes(), tokenAddress));

696       abi.encode(operatorBytes.length == 0 ? address(this).toBytes() : operatorBytes, tokenAddress)

716       address(this),

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

file:   contracts/its/token-manager/TokenManager.sol

205     tokenId = ITokenManagerProxy(address(this)).tokenId();

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/its/token-manager/TokenManager.sol#L205

file:    contracts/its/token-manager/implementations/TokenManagerLockUnlock.sol

46       uint256 balance = token.balanceOf(address(this));

48       SafeTokenTransferFrom.safeTransferFrom(token, from, address(this), amount);

51       return IERC20(token).balanceOf(address(this)) - balance;

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

file:   contracts/its/utils/Implementation.sol

19      implementationAddress = address(this);

27     if (implementationAddress == address(this)) revert NotProxy();

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/its/utils/Implementation.sol#L19

file:   contracts/its/utils/Multicall.sol
25     (bool success, bytes memory result) = address(this).delegatecall(data[i]);

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

file:  contracts/its/utils/TokenManagerDeployer.sol

38     bytes memory args = abi.encode(address(this), implementationType, tokenId, params);

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

[G-19] Amounts should be checked for 0 before calling a transfer

Before transfer, we should check for amount being 0 so the function doesnt run when its not gonna do anything:

file:    contracts/cgp/AxelarGateway.sol

524      IERC20(tokenAddress).safeTransfer(account, amount);

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

file:   contracts/its/interchain-token/InterchainToken.sol

66      tokenManager.transmitInterchainTransfer{ value: msg.value }(sender, destinationChain, recipient, amount, metadata);

104     tokenManager.transmitInterchainTransfer{ value: msg.value }(sender, destinationChain, recipient, amount, metadata);

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/its/interchain-token/InterchainToken.sol#L66

file:   contracts/its/interchain-token-service/InterchainTokenService.sol

451     SafeTokenTransferFrom.safeTransferFrom(token, caller, destinationAddress, amount);

482     SafeTokenTransferFrom.safeTransferFrom(token, caller, destinationAddress, amount);

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

file:  contracts/its/token-manager/implementations/TokenManagerLiquidityPool.sol

82     SafeTokenTransferFrom.safeTransferFrom(token, from, liquidityPool_, amount);

98     SafeTokenTransferFrom.safeTransferFrom(token, liquidityPool(), to, amount);

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/its/token-manager/implementations/TokenManagerLiquidityPool.sol#L82

file:   contracts/its/token-manager/implementations/TokenManagerLockUnlock.sol

48     SafeTokenTransferFrom.safeTransferFrom(token, from, address(this), amount);

64     SafeTokenTransfer.safeTransfer(token, to, amount);

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

[G‑20] 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.

file:   contracts/cgp/auth/MultisigBase.sol

15      mapping(address => bool) hasVoted;

21      mapping(address => bool) isSigner;

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

file:  contracts/cgp/governance/AxelarServiceGovernance.sol

22     mapping(bytes32 => bool) public multisigApprovals;

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

file:   contracts/interchain-governance-executor/InterchainProposalExecutor.sol

24      mapping(string => mapping(address => bool)) public whitelistedCallers;

27      mapping(string => mapping(address => bool)) public whitelistedSenders;

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

file:  contracts/its/remote-address-validator/RemoteAddressValidator.sol

19     mapping(string => bool) public supportedByGateway;

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

[G-21] Make 3 event parameters indexed when possible

It’s the most gas efficient to make up to 3 event parameters indexed. If there are less than 3 parameters, you need to make all parameters indexed


file:   contracts/its/interfaces/IDistributable.sol

8       event DistributorChanged(address distributor);

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/its/interfaces/IDistributable.sol#L8

file:  contracts/its/interfaces/IFlowLimit.sol

8      event FlowLimitSet(uint256 flowLimit);

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/its/interfaces/IFlowLimit.sol#L8

file:   contracts/its/interfaces/IOperatable.sol

8      event OperatorChanged(address operator);

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/its/interfaces/IOperatable.sol#L8

file:   contracts/its/interfaces/IPausable.sol

11       event PausedSet(bool paused);

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/its/interfaces/IPausable.sol#L11


file:    contracts/its/interfaces/IRemoteAddressValidator.sol

14      event TrustedAddressAdded(string souceChain, string sourceAddress);

15      event TrustedAddressRemoved(string souceChain);

16      event GatewaySupportedChainAdded(string chain);

17      event GatewaySupportedChainRemoved(string chain);

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/its/interfaces/IRemoteAddressValidator.sol#L14

[G-22] Use constants instead of type(uintx).max

type(uint256).max etc. it uses more gas in the distribution process and also for each transaction than constant usage.

file:  contracts/cgp/governance/AxelarServiceGovernance.sol

79    if (commandId > uint256(type(ServiceGovernanceCommand).max))

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/cgp/governance/AxelarServiceGovernance.sol#L79

file:

120     if (commandId > uint256(type(GovernanceCommand).max))

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


file:   contracts/its/interchain-token/InterchainToken.sol

56      if (allowance_ != type(uint256).max) {

57      if (allowance_ > type(uint256).max - amount) {

58      allowance_ = type(uint256).max - amount;

88      if (_allowance != type(uint256).max)

95      if (allowance_ != type(uint256).max) {

96      if (allowance_ > type(uint256).max - amount) {

97      allowance_ = type(uint256).max - amount;

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/its/interchain-token/InterchainToken.sol#L56

file:     contracts/its/interchain-token-service/InterchainTokenService.sol

104      if (tokenManagerImplementations.length != uint256(type(TokenManagerType).max) + 1) revert LengthMismatch();

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

[G-23] Using a positive conditional flow to save a NOT opcode

file:    contracts/cgp/AxelarGateway.sol

296      if (!success) revert SetupFailed();

363      if (!allowOperatorshipTransfer) continue;

402      if (!success) revert TokenDeployFailed(symbol);

446      if (!success || (returnData.length != uint256(0) && !abi.decode(returnData, (bool)))) revert BurnFailed(symbol);

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

file:    contracts/cgp/auth/MultisigBase.sol

45     if (!signers.isSigner[msg.sender]) revert NotSigner();

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

file:  contracts/cgp/governance/AxelarServiceGovernance.sol

55    if (!multisigApprovals[proposalHash]) revert NotApproved();

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/cgp/governance/AxelarServiceGovernance.sol#L55

file:    contracts/cgp/util/Caller.sol

19    if (!success)

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/cgp/util/Caller.sol#L19

file:  contracts/gmp-sdk/upgradable/Upgradable.sol

51    if (!success) revert SetupFailed();

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/gmp-sdk/upgradable/Upgradable.sol#L51

file:   contracts/gmp-sdk/deploy/ConstAddressDeployer.sol

51     if (!success) revert FailedInit();

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

file:    contracts/gmp-sdk/deploy/Create3Deployer.sol

58      if (!success) revert FailedInit();

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

file:   contracts/gmp-sdk/upgradable/FinalProxy.sol

82    if (!success) revert SetupFailed();

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/gmp-sdk/upgradable/FinalProxy.sol#L82

file:  contracts/interchain-governance-executor/InterchainProposalExecutor.sol

49     if (!whitelistedSenders[sourceChain][StringToAddress.toAddress(sourceAddress)])

57     if (!whitelistedCallers[sourceChain][interchainProposalCaller])

78     if (!success)

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

file:    contracts/its/interchain-token-service/InterchainTokenService.sol

124     if (!remoteAddressValidator.validateSender(sourceChain, sourceAddress)) revert NotRemoteService();

816     if (!success)

866      if (!success)

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

[G-24] Using XOR (^) and OR (|) bitwise equivalents

Estimated savings: 73 gas

file:   contracts/cgp/AxelarGateway.sol

342     if (commandsLength != commands.length || commandsLength != params.length) revert InvalidCommands();

446     if (!success || (returnData.length != uint256(0) && !abi.decode(returnData, (bool)))) revert BurnFailed(symbol);

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

file:   contracts/cgp/governance/InterchainGovernance.sol

92    if (keccak256(bytes(sourceChain)) != governanceChainHash || keccak256(bytes(sourceAddress)) != governanceAddressHash)

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

[G-25] If-statements that use && can be refactored into nested if statements

file:    contracts/cgp/AxelarGateway.sol

88      if (msg.sender != getAddress(KEY_MINT_LIMITER) && msg.sender != getAddress(KEY_GOVERNANCE)) revert NotMintLimiter();

446     if (!success || (returnData.length != uint256(0) && !abi.decode(returnData, (bool)))) revert BurnFailed(symbol);

635     if (limit > 0 && amount > limit) revert ExceedMintLimit(symbol);

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

file:   contracts/gmp-sdk/upgradable/InitProxy.sol

50     if (id != bytes32(0) && IUpgradable(implementationAddress).contractId() != id) revert InvalidImplementation();

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/gmp-sdk/upgradable/InitProxy.sol#L50

file:   contracts/gmp-sdk/upgradable/Proxy.sol

33     if (id != bytes32(0) && IUpgradable(implementationAddress).contractId() != id) revert InvalidImplementation();

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/gmp-sdk/upgradable/Proxy.sol#L33

file:   contracts/its/remote-address-validator/RemoteAddressValidator.sol

58     if ((b >= 65) && (b <= 70)) bytes(s)[i] = bytes1(b + uint8(32));

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

[G-26] x += y costs more gas than x = x + y for state variables

file:    contracts/interchain-governance-executor/InterchainProposalSender.sol

107     totalGas += interchainCalls[i].gas;

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

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

Using custom errors rather than revert() or require() strings can indeed save gas in Solidity smart contracts. When you use revert() or require() with string messages, the entire string message is stored in the transaction's revert message, which consumes unnecessary gas.

Custom errors, on the other hand, use predefined error codes (usually enum) to represent specific error conditions. These error codes are stored more efficiently in the transaction's revert message, resulting in reduced gas consumption.

Let's see an example to illustrate this gas-saving technique:

// Using custom errors
contract CustomErrorsExample {
    error InvalidSetMintLimitsParams();
    error TokenDoesNotExist(string symbol);

    function setTokenMintLimits(string[] calldata symbols, uint256[] calldata limits) external {
        uint256 length = symbols.length;
        if (length != limits.length) revert InvalidSetMintLimitsParams();

        for (uint256 i = 0; i < length; ++i) {
            string memory symbol = symbols[i];
            uint256 limit = limits[i];

            if (tokenAddresses(symbol) == address(0)) revert TokenDoesNotExist(symbol);

            _setTokenMintLimit(symbol, limit);
        }
    }

    // Other contract functions...
}

file:    contracts/cgp/AxelarGateway.sol

65       if (authModule_.code.length == 0) revert InvalidAuthModule();

66       if (tokenDeployerImplementation_.code.length == 0) revert InvalidTokenDeployer();

73       if (msg.sender != address(this)) revert NotSelf();

79       if (msg.sender != getAddress(KEY_GOVERNANCE)) revert NotGovernance();

255      if (newGovernance == address(0)) revert InvalidGovernance();

261      if (newMintLimiter == address(0)) revert InvalidMintLimiter();

268      if (length != limits.length) revert InvalidSetMintLimitsParams();

274      if (tokenAddresses(symbol) == address(0)) revert TokenDoesNotExist(symbol);

285      if (newImplementationCodeHash != newImplementation.codehash) revert InvalidCodeHash();

296      if (!success) revert SetupFailed();

309      if (implementation() == address(0)) revert NotProxy();

338      if (chainId != block.chainid) revert InvalidChainId();

342      if (commandsLength != commands.length || commandsLength != params.length) revert InvalidCommands();

392      if (tokenAddresses(symbol) != address(0)) revert TokenAlreadyExists(symbol);

402      if (!success) revert TokenDeployFailed(symbol);

409      if (tokenAddress.code.length == uint256(0)) revert TokenContractDoesNotExist(tokenAddress);

432      if (tokenAddress == address(0)) revert TokenDoesNotExist(symbol);

446      if (!success || (returnData.length != uint256(0) && !abi.decode(returnData, (bool)))) revert BurnFailed(symbol);

519      if (tokenAddress == address(0)) revert TokenDoesNotExist(symbol);

537      if (tokenAddress == address(0)) revert TokenDoesNotExist(symbol);

538      if (amount == 0) revert InvalidAmount();

635      if (limit > 0 && amount > limit) revert ExceedMintLimit(symbol);

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

file:  contracts/cgp/auth/MultisigBase.sol

45    if (!signers.isSigner[msg.sender]) revert NotSigner();

51    if (voting.hasVoted[msg.sender]) revert AlreadyVoted();

159   if (newThreshold > length) revert InvalidSigners();

161   if (newThreshold == 0) revert InvalidSignerThreshold();
172   if (signers.isSigner[account]) revert DuplicateSigner(account);
173   if (account == address(0)) revert InvalidSigners();

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

file:    contracts/cgp/governance/AxelarServiceGovernance.sol
55      if (!multisigApprovals[proposalHash]) revert NotApproved();
80      revert InvalidCommand();

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/cgp/governance/AxelarServiceGovernance.sol#L55

file:  contracts/cgp/governance/InterchainGovernance.sol

93    revert NotGovernance();

100   if (target == address(0)) revert InvalidTarget();

121   revert InvalidCommand();

162    revert TokenNotSupported();

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

file:   contracts/cgp/util/Caller.sol

16      if (nativeValue > address(this).balance) revert InsufficientBalance();

20      revert ExecutionFailed();

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/cgp/util/Caller.sol#L16

file:    contracts/gmp-sdk/upgradable/Upgradable.sol

14       if (owner() != msg.sender) revert NotOwner();

25       if (newOwner == address(0)) revert InvalidOwner();
46       if (newImplementationCodeHash != newImplementation.codehash) revert InvalidCodeHash();
51       if (!success) revert SetupFailed();
63       if (implementation() == address(0)) revert NotProxy();

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/gmp-sdk/upgradable/Upgradable.sol#L14

file:   contracts/gmp-sdk/deploy/ConstAddressDeployer.sol
51      if (!success) revert FailedInit();
81      if (bytecode.length == 0) revert EmptyBytecode();
88      if (deployedAddress_ == address(0)) revert FailedDeploy();

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

file:    contracts/gmp-sdk/deploy/Create3.sol

54       if (deployed.isContract()) revert AlreadyDeployed();

55       if (bytecode.length == 0) revert EmptyBytecode();

60       if (address(deployer) == address(0)) revert DeployFailed();

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

file:   contracts/gmp-sdk/deploy/Create3Deployer.sol

58     if (!success) revert FailedInit();

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

file:   contracts/gmp-sdk/upgradable/FinalProxy.sol

77     if (msg.sender != owner) revert NotOwner();

82     if (!success) revert SetupFailed();

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/gmp-sdk/upgradable/FinalProxy.sol#L77

file:     contracts/gmp-sdk/upgradable/InitProxy.sol

46        if (msg.sender != owner) revert NotOwner();

47        if (implementation() != address(0)) revert AlreadyInitialized();

50        if (id != bytes32(0) && IUpgradable(implementationAddress).contractId() != id) revert
InvalidImplementation();

59         if (!success) revert SetupFailed();

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/gmp-sdk/upgradable/InitProxy.sol#L46

file:  contracts/gmp-sdk/upgradable/Proxy.sol

30     if (owner == address(0)) revert InvalidOwner();

33     if (id != bytes32(0) && IUpgradable(implementationAddress).contractId() != id) revert InvalidImplementation();

42     if (!success) revert SetupFailed();

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/gmp-sdk/upgradable/Proxy.sol#L30

file:   contracts/gmp-sdk/upgradable/Upgradable.sol

31      if (address(this) == implementationAddress) revert NotProxy();

57      if (IUpgradable(newImplementation).contractId() != IUpgradable(this).contractId()) revert InvalidImplementation();

58      if (newImplementationCodeHash != newImplementation.codehash) revert InvalidCodeHash();

63      if (!success) revert SetupFailed();

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/gmp-sdk/upgradable/Upgradable.sol#L31

file:     contracts/gmp-sdk/util/TimeLock.sol

51        if (hash == 0) revert InvalidTimeLockHash();

52        if (_getTimeLockEta(hash) != 0) revert TimeLockAlreadyScheduled();

68        if (hash == 0) revert InvalidTimeLockHash();

82        if (hash == 0 || eta == 0) revert InvalidTimeLockHash();

84        if (block.timestamp < eta) revert TimeLockNotReady();

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/gmp-sdk/util/TimeLock.sol#L51

file:  contracts/interchain-governance-executor/InterchainProposalExecutor.sol

50     revert NotWhitelistedSourceAddress();

58     revert NotWhitelistedCaller();

164    revert(add(32, result), mload(result))

168    revert ProposalExecuteFailed();

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

file: its/interchain-token-service/InterchainTokenService.sol

97      revert ZeroAddress();

104     revert LengthMismatch();

124     revert NotRemoteService();

133     revert NotTokenManager();

172     revert TokenManagerDoesNotExist(tokenId);

311     if (gateway.tokenAddresses(tokenSymbol) == tokenAddress) revert GatewayToken();

332     if (getCanonicalTokenId(tokenAddress) != tokenId) revert NotCanonicalTokenManager();

445     if (gateway.isCommandExecuted(commandId)) revert AlreadyExecuted(commandId);

476     if (gateway.isCommandExecuted(commandId)) revert AlreadyExecuted(commandId);

519     if (version > 0) revert InvalidMetadataVersion(version);

536     if (length != flowLimits.length) revert LengthMismatch();

565     if (implementation == address(0)) revert ZeroAddress();

566        if (ITokenManager(implementation).implementationType() != uint256(tokenManagerType)) revert InvalidTokenManagerImplementation();

590    revert SelectorUnknown();

817    revert TokenManagerDeploymentFailed();

867    revert StandardizedTokenDeploymentFailed();

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

file:  contracts/its/libraries/AddressBytesUtils.sol

18     if (bytesAddress.length != 20) revert InvalidBytesLength(bytesAddress);

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/its/libraries/AddressBytesUtils.sol#L18

file:  contracts/its/proxies/StandardizedTokenProxy.sol

22    if (IStandardizedToken(implementationAddress).contractId() != CONTRACT_ID) revert InvalidImplementation();

25    if (!success) revert SetupFailed();if (!success) revert SetupFailed();

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/its/proxies/StandardizedTokenProxy.sol#L22

file:   contracts/its/remote-address-validator/RemoteAddressValidator.sol

28      if (_interchainTokenServiceAddress == address(0)) revert ZeroAddress();

43      if (length != trustedAddresses.length) revert LengthMismatch();

84      if (bytes(chain).length == 0) revert ZeroStringLength();

85      if (bytes(addr).length == 0) revert ZeroStringLength();

96      if (bytes(chain).length == 0) revert ZeroStringLength();

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

file:   contracts/its/token-manager/TokenManager.sol

28      if (interchainTokenService_ == address(0)) revert TokenLinkerZeroAddress();

36      if (msg.sender != address(interchainTokenService)) revert NotService();

44      if (msg.sender != tokenAddress()) revert NotToken();

https://github.com/code-423n4/2023-07-axelar/tree/main/contracts/its/token-manager/TokenManager.sol#L28

file:   contracts/its/utils/ExpressCallHandler.sol

91     if (prevExpressCaller != address(0)) revert AlreadyExpressCalled();

133    if (prevExpressCaller != address(0)) revert AlreadyExpressCalled();

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

file: contracts/its/utils/FlowLimit.sol

101     if (flowToAdd + flowAmount > flowToCompare + flowLimit) revert FlowLimitExceeded();

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

[G-28] Cache state variables outside of loop to avoid reading storage on every iteration

Reading from storage should always try to be avoided within loops. In the following instances, we are able to cache state variables outside of the loop to save a Gwarmaccess (100 gas) per loop iteration.

file:     contracts/its/remote-address-validator/RemoteAddressValidator.sol


108             for (uint256 i; i < length; ++i) {
            string calldata chainName = chainNames[i];
            supportedByGateway[chainName] = true;
            emit GatewaySupportedChainAdded(chainName);
        }


119        function removeGatewaySupportedChains(string[] calldata chainNames) external onlyOwner {
        uint256 length = chainNames.length;
        for (uint256 i; i < length; ++i) {
            string calldata chainName = chainNames[i];
            supportedByGateway[chainName] = false;
            emit GatewaySupportedChainRemoved(chainName);
        }
    }

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

file:      contracts/cgp/auth/MultisigBase.sol

122          for (uint256 i; i < length; ++i) {
            if (votingPerTopic[signerEpoch][topic].hasVoted[signers.accounts[i]]) {
                voteCount++;
            }
        }

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

file:    contracts/cgp/auth/MultisigBase.sol

70             for (uint256 i; i < count; ++i) {
            voting.hasVoted[signers.accounts[i]] = false;
        }

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

[G-29] Use do while loop instead of for loop

file: contracts/cgp/AxelarGateway.sol

270         for (uint256 i; i < length; ++i) {
            string memory symbol = symbols[i];
            uint256 limit = limits[i];

            if (tokenAddresses(symbol) == address(0)) revert TokenDoesNotExist(symbol);

            _setTokenMintLimit(symbol, limit);
        }

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

Recommanded code

diff --git a/AxelarGateway.org.sol b/AxelarGateway.sol
index e688c08..dff73eb 100644
--- a/AxelarGateway.org.sol
+++ b/AxelarGateway.sol
@@ -1,9 +1,8 @@
-   for (uint256 i; i < length; ++i) {
+   uint256 i;
+   do{
             string memory symbol = symbols[i];
             uint256 limit = limits[i];

             if (tokenAddresses(symbol) == address(0)) revert TokenDoesNotExist(symbol);

             _setTokenMintLimit(symbol, limit);
-            }
+        }while(i < length);

#0 - deanamiel

2023-09-06T23:21:08Z

Implemented the suggestion from G-21. See PR here

#1 - c4-judge

2023-09-08T12:00:38Z

berndartmueller marked the issue as grade-b

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