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
Rank: 41/47
Findings: 1
Award: $19.28
đ Selected for report: 0
đ Solo Findings: 0
đ Selected for report: Sathish9098
Also found by: 0x11singh99, 0xAnah, 0xn006e7, Arz, DavidGiladi, K42, Raihan, ReyAdmirado, Rolezn, SAQ, SM3_SS, SY_S, Walter, dharma09, flutter_developer, hunter_w3b, matrix_0wl, naman1778, petrichor, ybansal2403
19.2767 USDC - $19.28
Number | Optimization Details | Context |
---|---|---|
[G-00] | IInterchainTokenService::getChainName function that returns the name of the current chain as a string introduce higher gas costs and increase storage | 1 |
[G-01] | Using calldata instead of memory for read-only arguments in external functions saves gas | 10 |
[G-02] | TokenManagerLockUnlock.sol::_takeToken SafeTokenTransfer we should check amount for possible gas save | 1 |
[G-03] | Using storage instead of memory for structs/arrays saves gas | 7 |
[G-04] | Keccak256() should only need to be called on a specific string literal once | 4 |
[G-05] | Add unchecked {}  for subtractions where the operands cannot underflow because of a previous require() or if statement | 4 |
[G-06] | Functions guaranteed to when called by normal users can be marked Payable | 16 |
[G-07] | Empty blocks should be removed or emit something | 8 |
[G-08] | With assembly , .call (bool success)  transfer can be done gas-optimized | 9 |
[G-09] | Can make the variable outside the loop to save gas | 4 |
[G-10] | Use assembly  to write address storage values | 3 |
[G-11] | Duplicated if() checks should be refactored to a modifier or function | 8 |
[G-12] | Should use arguments instead of state variable | 1 |
[G-13] | Not using the named return variable when a function returns, wastes deployment gas | 3 |
[G-14] | Expressions for constant values such as a call to keccak256(), should use immutable rather than constant | 1 |
[G-15] | Multiple address /ID mappings can be combined into a single mapping of an address/ID to a struct , where appropriate | 1 |
[G-16] | Donât Initialize Variables with Default Value | 4 |
[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 transfer | 9 |
[G-20] | Using bools for storage incurs overhead | 6 |
[G-21] | Make 3 event parameters indexed when possible | 8 |
[G-22] | Use constants instead of type(uintx).max | 10 |
[G-23] | Using a positive conditional flow to save a NOT opcode | 18 |
[G-24] | Using XOR (^) and OR () bitwise equivalents | 3 |
[G-25] | If-statements that use && can be refactored into nested if statements | 6 |
[G-26] | x += y costs more gas than x = x + y for state variables | 1 |
[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 iteration | 4 |
[G-29] | Use do while loop instead of for loop | 1 |
IInterchainTokenService::getChainName
function that returns the name of the current chain as a string introduce higher gas costs and increase storageUsing 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.
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.
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.
calldata
instead of memory
for read-only arguments in external functions saves gasWhen 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;
file: contracts/gmp-sdk/deploy/ConstAddressDeployer.sol 42 function deployAndInit( bytes memory bytecode, bytes32 salt, bytes calldata init ) external returns
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
file: contracts/interchain-governance-executor/interfaces/IInterchainProposalSender.sol 15 function sendProposals(InterchainCalls.InterchainCall[] memory calls) external payable;
file: contracts/its/interchain-token-service/InterchainTokenService.sol 559 function _sanitizeTokenManagerImplementation(address[] memory implementaions, TokenManagerType tokenManagerType)
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
TokenManagerLockUnlock.sol::_takeToken
SafeTokenTransfer we should check amount for possible gas saveBefore 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);
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;
storage
instead of memory
for structs/arrays saves gasWhen 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[]));
file: /contracts/its/remote-address-validator/RemoteAddressValidator.sol 41 (string[] memory trustedChainNames, string[] memory trustedAddresses) = abi.decode(params, (string[], string[]));
file: /contracts/cgp/governance/AxelarServiceGovernance.sol 38 address[] memory cosigners,
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
Keccak256()
should only need to be called on a specific string literal onceIt 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');
file: /contracts/its/proxies/InterchainTokenServiceProxy.sol 12 bytes32 private constant CONTRACT_ID = keccak256('interchain-token-service');
file: /contracts/its/proxies/StandardizedTokenProxy.sol 14 bytes32 private constant CONTRACT_ID = keccak256('standardized-token');
file: /contracts/its/token-implementations/StandardizedToken.sol#L27 27 bytes32 private constant CONTRACT_ID = keccak256('standardized-token');
unchecked {}
 for subtractions where the operands cannot underflow because of a previous require() or if statementrequire(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);
File: /contracts/its/token-manager/implementations/TokenManagerLockUnlock.sol 66 return IERC20(token).balanceOf(to) - balance;
guaranteed
to when called by normal users can be marked PayableThe 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 {
file: /contracts/its/token-manager/implementations/TokenManagerLiquidityPool.sol 67 function setLiquidityPool(address newLiquidityPool) external onlyOperator {
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 {
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 {
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 {
file: /contracts/gmp-sdk/upgradable/Upgradable.sol 56 ) external override onlyOwner { 78 function setup(bytes calldata data) external override onlyProxy {
Empty blocks
should be removed or emit somethingThe 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 {}
file: /contracts/its/proxies/TokenManagerProxy.sol 66 function setup(bytes calldata setupParams) external {}
file: /contracts/cgp/governance/InterchainGovernance.sol 168 receive() external payable {}
file: /contracts/cgp/governance/AxelarServiceGovernance.sol 40 ) InterchainGovernance(gateway, governanceChain, governanceAddress, minimumTimeDelay) MultisigBase(cosigners, threshold) {}
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 {}
assembly
, .call (bool success)  transfer can be done gas-optimizedreturn 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);
file: /contracts/gmp-sdk/deploy/ConstAddressDeployer.sol 50 (bool success, ) = deployedAddress_.call(init);
file: /contracts/gmp-sdk/deploy/Create3Deployer.sol 57 (bool success, ) = deployedAddress_.call(init);
file: /contracts/gmp-sdk/upgradable/FinalProxy.sol 81 (bool success, ) = finalImplementation_.delegatecall(abi.encodeWithSelector(BaseProxy.setup.selector, setupParams));
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
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
assembly
 to write address storage valuesBy 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_;
file: /contracts/its/utils/StandardizedTokenDeployer.sol 34 implementationLockUnlockAddress = implementationLockUnlockAddress_; 35 implementationMintBurnAddress = implementationMintBurnAddress_;
if()
checks should be refactored to a modifier or functionto 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();
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
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
constant
values such as a call to keccak256(), should use immutable rather than constantWhile 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
address /ID
mappings can be combined into a single mapping
of an address/ID to a struct , where appropriateSaves 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;
Initialize
Variables with Default ValueUninitialized 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++)
file: contracts/interchain-governance-executor/InterchainProposalSender.sol 63 for (uint256 i = 0; i < interchainCalls.length; ) 106 for (uint256 i = 0; i < interchainCalls.length; )
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
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));
file: contracts/its/utils/ExpressCallHandler.sol 32 slot = uint256(keccak256(abi.encode(PREFIX_EXPRESS_RECEIVE_TOKEN, tokenId, destinationAddress, amount, commandId))) 57 abi.encode(
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));
file: contracts/its/utils/TokenManagerDeployer.sol 38 bytes memory args = abi.encode(address(this), implementationType, tokenId, params);
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));
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));
file: contracts/interchain-governance-executor/InterchainProposalExecutor.sol 66 emit ProposalExecuted(keccak256(abi.encode(sourceChain, sourceAddress, interchainProposalCaller, payload)));
file: contracts/interchain-governance-executor/InterchainProposalSender.sol 89 bytes memory payload = abi.encode(msg.sender, interchainCall.calls);
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();
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);
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);
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),
file: contracts/its/token-manager/TokenManager.sol 205 tokenId = ITokenManagerProxy(address(this)).tokenId();
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;
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);
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);
file: contracts/its/interchain-token-service/InterchainTokenService.sol 451 SafeTokenTransferFrom.safeTransferFrom(token, caller, destinationAddress, amount); 482 SafeTokenTransferFrom.safeTransferFrom(token, caller, destinationAddress, amount);
file: contracts/its/token-manager/implementations/TokenManagerLiquidityPool.sol 82 SafeTokenTransferFrom.safeTransferFrom(token, from, liquidityPool_, amount); 98 SafeTokenTransferFrom.safeTransferFrom(token, liquidityPool(), to, amount);
file: contracts/its/token-manager/implementations/TokenManagerLockUnlock.sol 48 SafeTokenTransferFrom.safeTransferFrom(token, from, address(this), amount); 64 SafeTokenTransfer.safeTransfer(token, to, amount);
// 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;
file: contracts/interchain-governance-executor/InterchainProposalExecutor.sol 24 mapping(string => mapping(address => bool)) public whitelistedCallers; 27 mapping(string => mapping(address => bool)) public whitelistedSenders;
file: contracts/its/remote-address-validator/RemoteAddressValidator.sol 19 mapping(string => bool) public supportedByGateway;
3 event
parameters indexed when possibleItâ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);
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);
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))
file: 120 if (commandId > uint256(type(GovernanceCommand).max))
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;
file: contracts/its/interchain-token-service/InterchainTokenService.sol 104 if (tokenManagerImplementations.length != uint256(type(TokenManagerType).max) + 1) revert LengthMismatch();
conditional
flow to save a NOT opcodefile: 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();
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();
file: contracts/gmp-sdk/deploy/ConstAddressDeployer.sol 51 if (!success) revert FailedInit();
file: contracts/gmp-sdk/deploy/Create3Deployer.sol 58 if (!success) revert FailedInit();
file: contracts/gmp-sdk/upgradable/FinalProxy.sol 82 if (!success) revert SetupFailed();
file: contracts/interchain-governance-executor/InterchainProposalExecutor.sol 49 if (!whitelistedSenders[sourceChain][StringToAddress.toAddress(sourceAddress)]) 57 if (!whitelistedCallers[sourceChain][interchainProposalCaller]) 78 if (!success)
file: contracts/its/interchain-token-service/InterchainTokenService.sol 124 if (!remoteAddressValidator.validateSender(sourceChain, sourceAddress)) revert NotRemoteService(); 816 if (!success) 866 if (!success)
XOR (^)
and OR (|)
bitwise equivalentsEstimated 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)
If-statements
that use &&
can be refactored into nested if statementsfile: 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();
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));
x += y
costs more gas than x = x + y
for state variablesfile: contracts/interchain-governance-executor/InterchainProposalSender.sol 107 totalGas += interchainCalls[i].gas;
custom errors
rather than revert()/require()
strings to save gasUsing 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();
file: contracts/cgp/governance/InterchainGovernance.sol 93 revert NotGovernance(); 100 if (target == address(0)) revert InvalidTarget(); 121 revert InvalidCommand(); 162 revert TokenNotSupported();
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();
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();
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();
file: contracts/gmp-sdk/upgradable/FinalProxy.sol 77 if (msg.sender != owner) revert NotOwner(); 82 if (!success) revert SetupFailed();
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();
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();
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();
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();
file: contracts/its/libraries/AddressBytesUtils.sol 18 if (bytesAddress.length != 20) revert InvalidBytesLength(bytesAddress);
file: contracts/its/proxies/StandardizedTokenProxy.sol 22 if (IStandardizedToken(implementationAddress).contractId() != CONTRACT_ID) revert InvalidImplementation(); 25 if (!success) revert SetupFailed();if (!success) revert SetupFailed();
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();
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();
file: contracts/its/utils/ExpressCallHandler.sol 91 if (prevExpressCaller != address(0)) revert AlreadyExpressCalled(); 133 if (prevExpressCaller != address(0)) revert AlreadyExpressCalled();
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
state variables
outside of loop to avoid reading storage on every iterationReading 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); } }
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
do while
loop instead of for loopfile: 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
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