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: 46/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 | Instances |
---|---|---|
[G-01] | Use constants instead of type(uintx).max or type(uintx).min | 7 |
[G-02] | Non efficient zero initialization | 6 |
[G-03] | Using ternary operator instead of if else saves gas | 4 |
[G-04] | Make 3 event parameters indexed when possible | 7 |
[G-05] | x + y is more efficient than using += for state variables (likewise for -=) | 1 |
[G-06] | Use calldata instead of memory for function arguments that do not get mutated | 37 |
[G-07] | Do not calculate constants variables | 8 |
[G-08] | Use unchecked{} whenever underflow/overflow not possible | 1 |
[G-09] | Use storage instead of memory for structs/arrays | 2 |
[G-10] | The use of a logical AND in place of double if is slightly less gas efficient in instances where there isn't a corresponding else statement for the given if statement | 2 |
[G-11] | <= or >= is more efficient than < or > | 5 |
[G-12] | Calling .length in a for loop wastes gas | 1 |
[G-13] | Use assembly to check for the zero address | 19 |
Total 13 issues
type(uint120).max or type(uint112).max, etc. it uses more gas in the distribution process and also for each transaction than constant usage.
Total 7 instances - 1 file:
Findings are marked with <=FOUND
File: contracts/its/interchain-token/InterchainToken.sol 43:function interchainTransfer( 44: string calldata destinationChain, 45: bytes calldata recipient, 46: uint256 amount, 47: bytes calldata metadata 48: ) external payable { 49: address sender = msg.sender; 50: ITokenManager tokenManager = getTokenManager(); 51: /** 52: * @dev if you know the value of `tokenManagerRequiresApproval()` you can just skip the if statement and just do nothing or _approve. 53: */ 54: if (tokenManagerRequiresApproval()) { 55: uint256 allowance_ = allowance[sender][address(tokenManager)]; 56: if (allowance_ != type(uint256).max) {//<=FOUND 57: if (allowance_ > type(uint256).max - amount) {//<=FOUND 58: allowance_ = type(uint256).max - amount;//<=FOUND 59: } 60: 61: _approve(sender, address(tokenManager), allowance_ + amount); 62: } 63: } 64: 65: // Metadata semantics are defined by the token service and thus should be passed as-is. 66: tokenManager.transmitInterchainTransfer{ value: msg.value }(sender, destinationChain, recipient, amount, metadata); 67: }
Findings are marked with <=FOUND
File: contracts/its/interchain-token/InterchainToken.sol 88:if (_allowance != type(uint256).max) {//<=FOUND 89: _approve(sender, msg.sender, _allowance - amount); 90: } 91: 92: ITokenManager tokenManager = getTokenManager(); 93: if (tokenManagerRequiresApproval()) { 94: uint256 allowance_ = allowance[sender][address(tokenManager)]; 95: if (allowance_ != type(uint256).max) {//<=FOUND 96: if (allowance_ > type(uint256).max - amount) {//<=FOUND 97: allowance_ = type(uint256).max - amount;//<=FOUND 98: }
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.
Total 6 instances - 4 file:
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; ) {
findings are marked with <=FOUND
File: contracts/interchain-governance-executor/InterchainProposalSender.sol 105: uint256 totalGas = 0;//<=FOUND 106: for (uint256 i = 0; i < interchainCalls.length; ) {//<=FOUND
File: contracts/its/token-manager/TokenManager.sol 118:uint32 version = 0;
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
Total 4 instances - 3 file:
File: contracts/interchain-governance-executor/InterchainProposalExecutor.sol 78:if (!success) { 79: _onTargetExecutionFailed(call, result); 80: } else { 81: _onTargetExecuted(call, result); 82: }
File: contracts/interchain-governance-executor/InterchainProposalExecutor.sol 161:if (result.length > 0) { 162: // The failure data is a revert reason string. 163: assembly { 164: revert(add(32, result), mload(result)) 165: } 166: } else { 167: // There is no failure data, just revert with no reason. 168: revert ProposalExecuteFailed(); 169: }
File: contracts/its/token-manager/TokenManager.sol 68:if (operatorBytes.length == 0) { 69: operator_ = address(interchainTokenService); 70: } else { 71: operator_ = operatorBytes.toAddress(); 72: }
File: contracts/its/interchain-token-service/InterchainTokenService.sol 609:if (expressCaller == address(0)) { 610: amount = tokenManager.giveToken(destinationAddress, amount); 611: emit TokenReceived(tokenId, sourceChain, destinationAddress, amount); 612: } else { 613: amount = tokenManager.giveToken(expressCaller, amount); 614: }
Using the indexed keyword for value types such as uint, bool, and address saves gas costs, as seen in the example below. However, this is only the case for value types, whereas indexing bytes and strings are more expensive than their unindexed version.
Total 7 instances - 3 file:
File: contracts/cgp/interfaces /IAxelarServiceGovernance.sol 15: event MultisigApproved(bytes32 indexed proposalHash, address indexed targetContract, bytes callData, uint256 nativeValue); 16:event MultisigCancelled(bytes32 indexed proposalHash, address indexed targetContract, bytes callData, uint256 nativeValue); 17:event MultisigExecuted(bytes32 indexed proposalHash, address indexed targetContract, bytes callData, uint256 nativeValue);
Finding are marked with <=FOUND
File: contracts/interchain-governance-executor/interfaces/IInterchainProposalExecutor.sol 7: event WhitelistedProposalCallerSet(string indexed sourceChain, address indexed sourceCaller, bool whitelisted);//<=FOUND 8: 9: // An event emitted when the proposal sender is whitelisted 10: event WhitelistedProposalSenderSet(string indexed sourceChain, address indexed sourceSender, bool whitelisted);//<=FOUND
Finding are marked with <=FOUND
File: contracts/its/interfaces/IInterchainTokenService.sol 32: event TokenSent(bytes32 tokenId, string destinationChain, bytes destinationAddress, uint256 indexed amount);//<=FOUND 33: event TokenSentWithData(//<=FOUND 34: bytes32 tokenId, 35: string destinationChain, 36: bytes destinationAddress, 37: uint256 indexed amount, 38: address indexed sourceAddress, 39: bytes data 40: );
Finding are marked with <=FOUND
File: contracts/its/interfaces/IInterchainTokenService.sol 50:event RemoteTokenManagerDeploymentInitialized(//<=FOUND 51: bytes32 indexed tokenId, 52: string destinationChain, 53: uint256 indexed gasValue, 54: TokenManagerType indexed tokenManagerType, 55: bytes params 56: );
Finding are marked with <=FOUND
File: contracts/its/interfaces/IInterchainTokenService.sol 67:event TokenManagerDeployed(bytes32 indexed tokenId, TokenManagerType indexed tokenManagerType, bytes params); 68: event StandardizedTokenDeployed(//<=FOUND 69: bytes32 indexed tokenId, 70: string name, 71: string symbol, 72: uint8 decimals, 73: uint256 mintAmount, 74: address mintTo 75: );
In instances found where either += or -= are used against state variables use x = x + y instead
Total 1 instances - 1 file:
File: contracts/interchain-governance-executor/InterchainProposalSender.sol 107: totalGas += interchainCalls[i].gas;
It is generally cheaper to load variables directly from calldata, rather than copying them to memory. Only use memory if the variable needs to be modified.
Total 37 instances - 9 file:
File: contracts/gmp-sdk/deploy/ConstAddressDeployer.sol 24:function deploy(bytes memory bytecode, bytes32 salt) external returns (address deployedAddress_) {
File: contracts/gmp-sdk/deploy/ConstAddressDeployer.sol 43:bytes memory bytecode,
File: contracts/gmp-sdk/deploy/ConstAddressDeployer.sol 80:function _deploy(bytes memory bytecode, bytes32 salt) internal returns (address deployedAddress_) {
File: contracts/gmp-sdk/deploy/Create3Deployer.sol 50:bytes memory bytecode,
File: contracts/gmp-sdk/deploy/Create3.sol 21: function deploy(bytes memory bytecode) external {
File: contracts/gmp-sdk/deploy/Create3.sol 51: function deploy(bytes32 salt, bytes memory bytecode) internal returns (address deployed) {
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/deploy/Create3.sol#L51
Findings are marked as <=FOUND
File: contracts/gmp-sdk/upgradable/InitProxy.sol 35:function init( 36: address implementationAddress, 37: address newOwner, 38: bytes memory params//<=FOUND 39: ) external {
File: contracts/gmp-sdk/upgradable/FinalProxy.sol 72:function finalUpgrade(bytes memory bytecode, bytes calldata setupParams) public returns (address finalImplementation_) {
File: contracts/its/libraries/AddressBytesUtils.sol 17:function toAddress(bytes memory bytesAddress) internal pure returns (address addr) {
File: contracts/its/remote-address-validator/RemoteAddressValidator.sol 83:function addTrustedAddress(string memory chain, string memory addr) public onlyOwner {
Findings are marked as <=FOUND
File: contracts/its/utils/ExpressCallHandler.sol 46:function _getExpressReceiveTokenWithDataSlot( 47: bytes32 tokenId, 48: string memory sourceChain,//<=FOUND 49: bytes memory sourceAddress,//<=FOUND 50: address destinationAddress, 51: uint256 amount, 52: bytes memory data,//<=FOUND 53: bytes32 commandId 54: ) internal pure returns (uint256 slot) {
Findings are marked as <=FOUND
File: contracts/its/utils/ExpressCallHandler.sol 110:function _setExpressReceiveTokenWithData( 111: bytes32 tokenId, 112: string memory sourceChain,//<=FOUND 113: bytes memory sourceAddress,//<=FOUND 114: address destinationAddress, 115: uint256 amount, 116: bytes calldata data, 117: bytes32 commandId, 118: address expressCaller 119: ) internal {
Findings are marked as <=FOUND
File: contracts/its/utils/ExpressCallHandler.sol 171: function getExpressReceiveTokenWithData( 172: bytes32 tokenId, 173: string memory sourceChain,//<=FOUND 174: bytes memory sourceAddress,//<=FOUND 175: address destinationAddress, 176: uint256 amount, 177: bytes calldata data, 178: bytes32 commandId 179: ) public view returns (address expressCaller) {
Findings are marked as <=FOUND
File: contracts/its/utils/ExpressCallHandler.sol 231: function _popExpressReceiveTokenWithData( 232: bytes32 tokenId, 233: string memory sourceChain,//<=FOUND 234: bytes memory sourceAddress,//<=FOUND 235: address destinationAddress, 236: uint256 amount, 237: bytes memory data,//<=FOUND 238: bytes32 commandId 239: ) internal returns (address expressCaller) {
File: contracts/its/interchain-token-service/InterchainTokenService.sol 241:function getParamsLockUnlock(bytes memory operator, address tokenAddress) public pure returns (bytes memory params) {
File: contracts/its/interchain-token-service/InterchainTokenService.sol 251:function getParamsMintBurn(bytes memory operator, address tokenAddress) public pure returns (bytes memory params) {
Findings are marked as <=FOUND
File: contracts/its/interchain-token-service/InterchainTokenService.sol 262:function getParamsLiquidityPool( 263: bytes memory operator,//<=FOUND 264: address tokenAddress, 265: address liquidityPoolAddress 266: ) public pure returns (bytes memory params) {
Findings are marked as <=FOUND
File: contracts/its/interchain-token-service/InterchainTokenService.sol 417:function deployAndRegisterRemoteStandardizedToken( 418: bytes32 salt, 419: string calldata name, 420: string calldata symbol, 421: uint8 decimals, 422: bytes memory distributor,//<=FOUND 423: bytes memory operator,//<=FOUND 424: string calldata destinationChain, 425: uint256 gasValue 426: ) external payable notPaused {
Findings are marked as <=FOUND
File: contracts/its/interchain-token-service/InterchainTokenService.sol 502: function transmitSendToken( 503: bytes32 tokenId, 504: address sourceAddress, 505: string calldata destinationChain, 506: bytes memory destinationAddress,//<=FOUND 507: uint256 amount, 508: bytes calldata metadata 509: ) external payable onlyTokenManager(tokenId) notPaused {
Findings are marked as <=FOUND
File: contracts/its/interchain-token-service/InterchainTokenService.sol 707:function _callContract( 708: string calldata destinationChain, 709: bytes memory payload,//<=FOUND 710: uint256 gasValue, 711: address refundTo 712: ) internal {
Findings are marked as <=FOUND
File: contracts/its/interchain-token-service/InterchainTokenService.sol 748:function _deployRemoteTokenManager( 749: bytes32 tokenId, 750: string calldata destinationChain, 751: uint256 gasValue, 752: TokenManagerType tokenManagerType, 753: bytes memory params//<=FOUND 754: ) internal {
Findings are marked as <=FOUND
File: contracts/its/interchain-token-service/InterchainTokenService.sol 770:function _deployRemoteStandardizedToken( 771: bytes32 tokenId, 772: string memory name,//<=FOUND 773: string memory symbol,//<=FOUND 774: uint8 decimals, 775: bytes memory distributor,//<=FOUND 776: bytes memory operator,//<=FOUND 777: string calldata destinationChain, 778: uint256 gasValue 779: ) internal {
Findings are marked as <=FOUND
File: contracts/its/interchain-token-service/InterchainTokenService.sol 808: function _deployTokenManager( 809: bytes32 tokenId, 810: TokenManagerType tokenManagerType, 811: bytes memory params//<=FOUND 812: ) internal {
Findings are marked as <=FOUND
File: contracts/its/interchain-token-service/InterchainTokenService.sol 841: function _deployStandardizedToken( 842: bytes32 tokenId, 843: address distributor, 844: string memory name,//<=FOUND 845: string memory symbol,//<=FOUND 846: uint8 decimals, 847: uint256 mintAmount, 848: address mintTo 849: ) internal {
Findings are marked as <=FOUND
File: contracts/its/interchain-token-service/InterchainTokenService.sol 880: function _expressExecuteWithInterchainTokenToken( 881: bytes32 tokenId, 882: address destinationAddress, 883: string memory sourceChain,//<=FOUND 884: bytes memory sourceAddress,//<=FOUND 885: bytes calldata data, 886: uint256 amount 887: ) internal {
Due to how constant variables are implemented (replacements at compile-time), an expression assigned to a constant variable is recomputed each time that the variable is used, which wastes some gas.
Total 12 instances - 8 file:
File: contracts/gmp-sdk/upgradable/FinalProxy.sol 18: bytes32 internal constant FINAL_IMPLEMENTATION_SALT = keccak256('final-implementation');
File: contracts/its/proxies/InterchainTokenServiceProxy.sol 12: bytes32 private constant CONTRACT_ID = keccak256('interchain-token-service');
File: contracts/its/proxies/RemoteAddressValidatorProxy.sol 12: bytes32 private constant CONTRACT_ID = keccak256('remote-address-validator');
File: contracts/its/proxies/StandardizedTokenProxy.sol 14: bytes32 private constant CONTRACT_ID = keccak256('standardized-token');
File: contracts/its/remote-address-validator/RemoteAddressValidator.sol 21: bytes32 private constant CONTRACT_ID = keccak256('remote-address-validator');
File: contracts/its/token-implementations/StandardizedToken.sol 27: bytes32 private constant CONTRACT_ID = keccak256('standardized-token');
Findings are marked as <=FOUND
File: contracts/its/utils/FlowLimit.sol 15: uint256 internal constant PREFIX_FLOW_OUT_AMOUNT = uint256(keccak256('prefix-flow-out-amount'));//<=FOUND 16: uint256 internal constant PREFIX_FLOW_IN_AMOUNT = uint256(keccak256('prefix-flow-in-amount'));////<=FOUND
Findings are marked as <=FOUND
File: contracts/its/interchain-token-service/InterchainTokenService.sol 62: bytes32 internal constant PREFIX_CUSTOM_TOKEN_ID = keccak256('its-custom-token-id');//<=FOUND 63: bytes32 internal constant PREFIX_STANDARDIZED_TOKEN_ID = keccak256('its-standardized-token-id');//<=FOUND 64: bytes32 internal constant PREFIX_STANDARDIZED_TOKEN_SALT = keccak256('its-standardized-token-salt');//<=FOUND
File: contracts/its/interchain-token-service/InterchainTokenService.sol 71: bytes32 private constant CONTRACT_ID = keccak256('interchain-token-service');
Using unchecked increments in Solidity can save on gas fees by bypassing built-in overflow checks, thus optimizing gas usage, but requires careful assessment of potential risks and edge cases to avoid unintended consequences.
Total 1 instances - 1 file:
File: contracts/cgp/auth/MultisigBase.sol 70:for (uint256 i; i < count; ++i) {
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/auth/MultisigBase.sol#L70
In Solidity, using storage instead of memory for structs and arrays in function parameters can result in gas savings. When data is passed as a storage reference, the function operates directly on the original data stored in contract storage, without needing to create a copy. Conversely, when memory is used, a copy of the data is created, which incurs additional gas costs. However, it's essential to note that while using storage references can save gas, it also means that any changes made to the data inside the function will modify the original data stored in the contract, which may not always be the desired behavior. Therefore, developers should carefully consider the implications of using storage versus memory based on the specific requirements of their functions.
Total 2 instances - 1 file:
File: contracts/its/interchain-token-service/InterchainTokenService.sol 534:function setFlowLimit(bytes32[] calldata tokenIds, uint256[] calldata flowLimits) external onlyOperator {
Using a double if statement instead of logical AND (&&) can provide similar short-circuiting behavior whereas double if is slightly more efficient.
Total 2 instances - 2 file:
File: contracts/cgp/AxelarGateway.sol 87: modifier onlyMintLimiter() { 88: if (msg.sender != getAddress(KEY_MINT_LIMITER) && msg.sender != getAddress(KEY_GOVERNANCE)) revert NotMintLimiter(); 89: 90: _; 91: }
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/AxelarGateway.sol#L87C1-L91C6
File: contracts/gmp-sdk/upgradable/Proxy.sol 33: if (id != bytes32(0) && IUpgradable(implementationAddress).contractId() != id) revert InvalidImplementation();
Make such found comparisons to the <=/>= equivalent when comparing against integer literals
Total 5 instances - 4 file:
File: contracts/interchain-governance-executor/InterchainProposalSender.sol 91:if (interchainCall.gas > 0) {
File: contracts/gmp-sdk/upgradable/Upgradable.sol 60:if (params.length > 0) {
File: contracts/its/token-implementations/StandardizedToken.sol 64:if (mintAmount > 0) {
File:contracts/its/interchain-token-service/InterchainTokenService.sol 519: if (version > 0) revert InvalidMetadataVersion(version);
File:contracts/its/interchain-token-service/InterchainTokenService.sol 714:if (gasValue > 0) {
Rather than calling .length for an array in a for loop declaration, it is far more gas efficient to cache this length before and use that instead. This will prevent the array length from being called every loop iteration
Total 1 instance - 1 file:
File: contracts/interchain-governance-executor/InterchainProposalSender.sol 106:for (uint256 i = 0; i < interchainCalls.length; ) {
ing assembly for address comparisons in Solidity can save gas because it allows for more direct access to the Ethereum Virtual Machine (EVM), reducing the overhead of higher-level operations. Solidity's high-level abstraction simplifies coding but can introduce additional gas costs. Using assembly for simple operations like address comparisons can be more gas-efficient.
Total 19 instances -9 file:
File: contracts/gmp-sdk/deploy/ConstAddressDeployer.sol 88:if (deployedAddress_ == address(0)) revert FailedDeploy();
File: contracts/gmp-sdk/upgradable/Proxy.sol 30:if (owner == address(0)) revert InvalidOwner();
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/upgradable/Proxy.sol#L30
File: contracts/gmp-sdk/upgradable/InitProxy.sol 47: if (implementation() != address(0)) revert AlreadyInitialized();
File: contracts/its/remote-address-validator/RemoteAddressValidator.sol 28: if (_interchainTokenServiceAddress == address(0)) revert ZeroAddress();
File: contracts/its/token-manager/TokenManager.sol 28: if (interchainTokenService_ == address(0)) revert TokenLinkerZeroAddress();
File: contracts/its/utils/ExpressCallHandler.sol 91: if (prevExpressCaller != address(0)) revert AlreadyExpressCalled();
File: contracts/its/utils/ExpressCallHandler.sol 133: if (prevExpressCaller != address(0)) revert AlreadyExpressCalled();
File: contracts/its/utils/ExpressCallHandler.sol 212: if (expressCaller != address(0)) {
File: contracts/its/utils/ExpressCallHandler.sol 252: if (expressCaller != address(0)) {
File: contracts/its/utils/StandardizedTokenDeployer.sol 31: if (deployer_ == address(0) || implementationLockUnlockAddress_ == address(0) || implementationMintBurnAddress_ == address(0))
File: contracts/its/utils/TokenManagerDeployer.sol 23: if (deployer_ == address(0)) revert AddressZero();
Findings are marked as <=FOUND
File: contracts/its/interchain-token-service/InterchainTokenService.sol 92:if ( 93: remoteAddressValidator_ == address(0) ||//<=FOUND 94: gasService_ == address(0) ||//<=FOUND 95: tokenManagerDeployer_ == address(0) ||//<=FOUND 96: standardizedTokenDeployer_ == address(0)//<=FOUND 97: ) revert ZeroAddress();
File: contracts/its/interchain-token-service/InterchainTokenService.sol 609: if (expressCaller == address(0)) {
File: contracts/its/interchain-token-service/InterchainTokenService.sol 652:if (expressCaller != address(0)) {
#0 - deanamiel
2023-08-31T00:11:37Z
Addressed gas optimization G-12. See PR here
#1 - c4-judge
2023-09-04T19:24:05Z
berndartmueller marked the issue as grade-b