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: 18/47
Findings: 1
Award: $189.74
🌟 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
189.7433 USDC - $189.74
no | Issue | Instances | |
---|---|---|---|
[G-01] | Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if statement | 4 | - |
[G-02] | Keccak256() should only need to be called on a specific string literal once | 4 | - |
[G-03] | Functions guaranteed to when called by normal users can be marked Payable | 17 | - |
[G-04] | Expressions for constant values such as a call to keccak256(), should use immutable rather than constant | 1 | - |
[G-05] | Multiple address/ID mappings can be combined into a single mapping of an address/ID to a struct , where appropriate | 1 | - |
[G-06] | Not using the named return variable when a function returns, wastes deployment gas | 3 | - |
[G-07] | Can make the variable outside the loop to save gas | 4 | - |
[G-08] | Using calldata instead of memory for read-only arguments in external functions saves gas | 5 | - |
[G-09] | Should use arguments instead of state variable | 1 | - |
[G-10] | Before transfer of some functions, we should check some variables for possible gas save | 1 | - |
[G-11] | Empty blocks should be removed or emit something | 8 | - |
[G-12] | Using storage instead of memory for structs/arrays saves gas | 7 | - |
[G-13] | With assembly, .call (bool success) transfer can be done gas-optimized | 9 | - |
[G-14] | Duplicated require()/if() checks should be refactored to a modifier or function | 3 | - |
[G-15] | Use assembly to write address storage values | 3 | - |
[G-16] | Caching global variables is more expensive than using the actual variable (use msg.sender instead of caching it) | 6 | - |
[G-17] | Use selfbalance() instead of address(this).balance | 1 | - |
[G-18] | abi.encode() is less efficient than abi.encodepacked() | 9 | - |
[G-19] | Using delete statement can save gas | 3 | - |
[G-20] | Use hardcode address instead of address(this) | 5 | - |
[G-21] | Use assembly for math (add, sub, mul, div) | 2 | - |
[G-22] | Sort Solidity operations using short-circuit mode | 4 | - |
[G-23] | Non efficient zero initialization | 4 | - |
[G-24] | Expression `` is cheaper than new bytes(0) | 1 | - |
[G-25] | Use uint256(1)/uint256(2) instead for true and false boolean states | 4 | - |
[G-26] | Using Bools For Storage Incurs Overhead | 1 | - |
[G-27] | Avoid contract existence checks by using low level calls | 2 | - |
[G-28] | Don't apply the same value to state variables | 2 | - |
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);
File: /contracts/its/token-manager/implementations/TokenManagerLockUnlock.sol 66 return IERC20(token).balanceOf(to) - balance;
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');
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');
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 { 547 function setPaused(bool paused) external onlyOwner { 579 ) internal override onlyRemoteService(sourceChain, sourceAddress) notPaused {
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 {
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
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;
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
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
When a function with a memory array is called externally, the abi.decode () step has to use a for-loop to copy each index of the calldata to the memory index. Each iteration of this for-loop costs at least 60 gas (i.e. 60 * <mem_array>.length). Using calldata directly, obliviates the need for such a loop in the contract code and runtime execution.
file: /contracts/cgp/interfaces/IMultisigBase.sol 73 function rotateSigners(address[] memory newAccounts, uint256 newThreshold) external;
file: /contracts/interchain-governance-executor/InterchainProposalSender.sol 81 string memory destinationChain, 82 string memory destinationContract,
file: /contracts/interchain-governance-executor/interfaces/IInterchainProposalSender.sol 15 function sendProposals(InterchainCalls.InterchainCall[] memory calls) external payable;
file: /contracts/gmp-sdk/deploy/Create3.sol 21 function deploy(bytes memory bytecode) external {
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/deploy/Create3.sol#L21
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
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);
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 {}
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 {}
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/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
file: /contracts/cgp/AxelarGateway.sol 332 bytes32[] memory commandIds; 334 string[] memory commands; 334 bytes[] memory 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[]));
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);
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
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();
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_;
file: /contracts/its/utils/StandardizedTokenDeployer.sol 34 implementationLockUnlockAddress = implementationLockUnlockAddress_; 35 implementationMintBurnAddress = implementationMintBurnAddress_;
file: /contracts/its/interchain-token/InterchainToken.sol 49 address sender = msg.sender;
file: /contracts/its/interchain-token-service/InterchainTokenService.sol 372 address deployer_ = msg.sender; 447 address caller = msg.sender; 448 address caller = msg.sender;
file: /contracts/its/token-manager/TokenManager.sol 89 address sender = msg.sender; 115 address sender = msg.sender;
file: /contracts/cgp/util/Caller.sol 16 if (nativeValue > address(this).balance) revert InsufficientBalance();
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/util/Caller.sol#L16
In terms of efficiency, abi.encodePacked() is generally considered to be more gas-efficient than abi.encode(), because it skips the step of adding function signatures and other metadata to the encoded data. However, this comes at the cost of reduced safety, as abi.encodePacked() does not perform any type checking or padding of data.
Refference: https://github.com/ConnorBlockchain/Solidity-Encode-Gas-Comparison
file: /contracts/interchain-governance-executor/InterchainProposalSender.sol 89 bytes memory payload = abi.encode(msg.sender, interchainCall.calls);
file: /contracts/interchain-governance-executor/InterchainProposalExecutor.sol 66 emit ProposalExecuted(keccak256(abi.encode(sourceChain, sourceAddress, interchainProposalCaller, payload)));
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 54 bytes32 deploySalt = keccak256(abi.encode(msg.sender, salt)); 68 bytes32 deploySalt = keccak256(abi.encode(sender, salt));
file: /contracts/its/utils/StandardizedTokenDeployer.sol 62 bytes memory params = abi.encode(tokenManager, distributor, name, symbol, decimals, mintAmount, mintTo);
file: /contracts/its/utils/TokenManagerDeployer.sol 38 bytes memory args = abi.encode(address(this), implementationType, tokenId, params);
file: /contracts/interchain-governance-executor/InterchainProposalSender.sol 105 uint256 totalGas = 0;
file: /contracts/cgp/auth/MultisigBase.sol 66 voting.voteCount = 0;
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/auth/MultisigBase.sol#L66
file: /contracts/its/token-manager/TokenManager.sol 118 uint32 version = 0;
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. References: https://book.getfoundry.sh/reference/forge-std/compute-create-address
Here's an example :
contract MyContract { address constant public CONTRACT_ADDRESS = 0x1234567890123456789012345678901234567890; function getContractAddress() public view returns (address) { return CONTRACT_ADDRESS; } }
file: /contracts/gmp-sdk/upgradable/Upgradable.sol 23 implementationAddress = address(this);
file: /contracts/cgp/AxelarGateway.sol 73 if (msg.sender != address(this)) revert NotSelf();
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/AxelarGateway.sol#L73
file: /contracts/gmp-sdk/deploy/Create3Deployer.sol 69 return Create3.deployedAddress(address(this), deploySalt);
file: /contracts/its/token-manager/implementations/TokenManagerLockUnlock.sol 51 return IERC20(token).balanceOf(address(this)) - balance;
file: /contracts/its/utils/Implementation.sol 19 implementationAddress = address(this);
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/utils/Implementation.sol#L19
Use assembly for math instead of Solidity. You can check for overflow/underflow in assembly to ensure safety. If using Solidity versions < 0.8.0 and you are using Safemath, you can gain significant gas savings by using assembly to calculate values and checking for overflow/underflow.
file: /contracts/gmp-sdk/util/TimeLock.sol 54 uint256 minimumEta = block.timestamp + _minimumTimeLockDelay;
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/util/TimeLock.sol#L54
file: /contracts/cgp/auth/MultisigBase.sol 56 uint256 voteCount = voting.voteCount + 1;
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/auth/MultisigBase.sol#L56
Short-circuiting is a solidity contract development model that uses OR/AND logic to sequence different cost operations. It puts low gas cost operations in the front and high gas cost operations in the back, so that if the front is low If the cost operation is feasible, you can skip (short-circuit) the subsequent high-cost Ethereum virtual machine operation.
//f(x) is a low gas cost operation //g(y) is a high gas cost operation //Sort operations with different gas costs as follows f(x) || g(y) f(x) && g(y)
file: /contracts/its/interchain-token-service/InterchainTokenService.sol 92 if ( remoteAddressValidator_ == address(0) || gasService_ == address(0) || tokenManagerDeployer_ == address(0) || standardizedTokenDeployer_ == address(0) 97 ) revert ZeroAddress();
file: /contracts/its/utils/StandardizedTokenDeployer.sol 31 if (deployer_ == address(0) || implementationLockUnlockAddress_ == address(0) || implementationMintBurnAddress_ == address(0))
file: /contracts/cgp/governance/InterchainGovernance.sol 92 if (keccak256(bytes(sourceChain)) != governanceChainHash || keccak256(bytes(sourceAddress)) != governanceAddressHash)
file: /contracts/cgp/AxelarGateway.sol 342 if (commandsLength != commands.length || commandsLength != params.length) revert InvalidCommands();
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/AxelarGateway.sol#L342
Solidity does not recognize null as a value, so uint variables are initialized to zero. Setting a uint variable to zero is redundant and can waste gas.
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/interchain-governance-executor/InterchainProposalExecutor.sol 74 for (uint256 i = 0; i < calls.length; i++) {
file: /contracts/its/utils/Multicall.sol 24 for (uint256 i = 0; i < data.length; ++i) {
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/utils/Multicall.sol#L24
file: /contracts/its/libraries/AddressBytesUtils.sol# 31 bytesAddress = new bytes(20);
If you don't use boolean for storage you will avoid Gwarmaccess 100 gas. In addition, state changes of boolean from true to false can cost up to ~20000 gas rather than uint256(2) to uint256(1) that would cost significantly less.
file: /contracts/cgp/governance/AxelarServiceGovernance.sol 57 multisigApprovals[proposalHash] = false; 102 multisigApprovals[proposalHash] = false;
file: /contracts/cgp/governance/AxelarServiceGovernance.sol 22 mapping(bytes32 => bool) public multisigApprovals;
file: /contracts/cgp/auth/MultisigBase.sol 71 voting.hasVoted[signers.accounts[i]] = false;
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/auth/MultisigBase.sol#L71
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/governance/AxelarServiceGovernance.sol 22 mapping(bytes32 => bool) public multisigApprovals;
Prior to 0.8.10 the compiler inserted extra code, including EXTCODESIZE (100 gas), to check for contract existence for external function calls. In more recent solidity versions, the compiler will not insert these checks if the external call has a return value. Similar behavior can be achieved in earlier versions by using low-level calls, since low level calls never check for contract existence.
file: /contracts/cgp/AxelarGateway.sol 329 bool allowOperatorshipTransfer = IAxelarAuth(AUTH_MODULE).validateProof(messageHash, proof);
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/AxelarGateway.sol#L329
file: /contracts/gmp-sdk/upgradable/Upgradable.sol 57 if (IUpgradable(newImplementation).contractId() != IUpgradable(this).contractId()) revert InvalidImplementation();
Assigning the same value to a state variable repeatedly can cause unnecessary gas costs, because each assignment operation incurs a gas cost. Additionally, if the value being assigned is the same as the current value of the state variable, the assignment operation does not actually change anything, which can waste gas.
file: /contracts/cgp/governance/InterchainGovernance.sol /// @audit governanceChain and governanceAddress state variabls but assign same value 39 governanceChain = governanceChain_; 40 governanceAddress = governanceAddress_;
#0 - c4-judge
2023-09-04T19:36:36Z
berndartmueller marked the issue as grade-a