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: 36/47
Findings: 1
Award: $43.33
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: immeas
Also found by: Bauchibred, DavidGiladi, Emmanuel, Jeiwan, MohammedRizwan, MrPotatoMagic, Rolezn, Sathish9098, T1MOH, Udsen, banpaleo5, hals, matrix_0wl, naman1778
43.3267 USDC - $43.33
abi.encode
IN PLACE OF abi.encodePacked
.When concatenating multiple data types to be provided as input to keccak256
function, it is recommended to use abi.encode
instead of abi.encodePacked
since abi.encodePacked
can introduce hash collisions in the event dynamic data types such as bytes
, arrays
or structs
are used.
In the InterchainGovernance.getProposalEta
function the _getProposalHash
function is used to compute the keccak256
hash of the encoded data of target, callData, nativeValue
. But in the InterchainGovernance.executeProposal
function, the same hash is calculated directly inside the function itself as shown below:
bytes32 proposalHash = keccak256(abi.encodePacked(target, callData, nativeValue));
Hence it is recommended to use the _getProposalHash
function to get the hash value in the InterchainGovernance.executeProposal
function as well for the ease of readability and clarity of the code.
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/governance/InterchainGovernance.sol#L57 https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/governance/InterchainGovernance.sol#L73
abstract
KEYWORD IS MISSING IN THE ABSTRACT FUNCTIONIn the Natspec
comments for the InterchainProposalExecutor
contract the following is mentioned:
* This contract is abstract and some of its functions need to be implemented in a derived contract.
When a contract is abstract the abstract
keyword is used to denote the same.
Hence it is recommended to add the abstract
keyword before the function name as follows:
abstract contract InterchainProposalExecutor is IInterchainProposalExecutor, AxelarExecutable, Ownable {
In the InterchainProposalExecutor.constructor
contract, the ownership
of the contract is set as follows inside the constructor
.
_transferOwnership(_owner);
But the _owner
is not checked for address(0)
before calling the _transferOwnership
function in the openzeppelin Ownable.sol
contract. As a result if the owner is set to address(0)
then onlyOwner
modifier controlled critical functions will be inaccessible. This will prompt a redeployment of the contract.
Hence it is required to perform the following check before calling the _transferOwnership
function to make sure the proper input validation is performed.
require(_owner != address(0), "Can not be address(0)");
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/interchain-governance-executor/InterchainProposalExecutor.sol#L29-L31 https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/upgradable/InitProxy.sol#L54 https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/gmp-sdk/upgradable/FixedProxy.sol#L24
Here the exisiting owner
proposes
the new owner
and then new owner should accept the role by calling the respective function as msg.sender
.
Natspec
COMMENT GIVEN FOR THE FUNCTION InterchainToken.tokenManagerRequiresApproval
The following comment is given as the description of the return
value of InterchainToken.tokenManagerRequiresApproval
.
* @return tokenManager the TokenManager called to facilitate cross chain transfers.
But the above Natspec
comment is wrong since the function returns boolean
value which indicates whether the tokenManager
requires approval
for token transfers.
The following Natspec
comment is also wrong since the ExpressCallHandler._setExpressReceiveToken
calls the _getExpressReceiveTokenSlot
function to get the storage slot.
* @notice Stores the address of the express caller at the storage slot determined by _getExpressSendTokenSlot
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/utils/ExpressCallHandler.sol#L72 https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/utils/ExpressCallHandler.sol#L86 https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/utils/ExpressCallHandler.sol#L100 https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/utils/ExpressCallHandler.sol#L120
/** * @title TokenManagerProxy * @dev This contract is a proxy for token manager contracts. It implements ITokenManagerProxy and * inherits from FixedProxy from the gmp sdk repo */ interface ITokenManagerProxy {
* @param amount the amount of tokens to take from msg.sender.
Above should be corrected as follows:
* @param amount the amount of tokens to take from sender.
In the InterchainToken.interchainTransferFrom
function, the existing allowance of the msg.sender is decreased by the amount
as follows:
if (_allowance != type(uint256).max) { _approve(sender, msg.sender, _allowance - amount); }
But there is no conditinal check to verify _allowance >= amount
. If this is not the case the transaction could revert without an error message. Even in the openzeppelin
library for ERC20.sol
, this check is performed to confirm that _allowance >= amount
and if not transaction reverts.
Hence it is recommended to update the above code snippet as follows to inlcude the above check.
if (_allowance != type(uint256).max) { require(_allowance >= amount, "Insufficient Allowance"); _approve(sender, msg.sender, _allowance - amount); }
solidity 0.8.18
Consider moving to solidity version 0.8.18 or later, and use named mappings for better understanding of the purpose of each mapping.
mapping(string => bytes32) public remoteAddressHashes; mapping(string => string) public remoteAddresses; ... mapping(string => bool) public supportedByGateway;
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/remote-address-validator/RemoteAddressValidator.sol#L15-L16 https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/its/remote-address-validator/RemoteAddressValidator.sol#L19
call
FUNCTIONThe Caller._call
internal function is used to exeucte the low level call
function to execute the calldata
payload on the destination address as shown below:
(bool success, ) = target.call{ value: nativeValue }(callData);
But there is no check to make sure that the target
has contract code in the destination address.
If the nativeValue
is sent to a destination contract without contract code the transaction will succeed but the sent native
tokens will be lost. Thus this is loss of funds to the caller of the transaction.
Hence it is recommended to check the contract code size as shown below before calling the low level call
function. The following code snippet should be run before calling the low level call
function inside the Caller._call
function.
uint256 codeSize; assembly { codeSize := extcodesize(contractAddress) } require(codeSize > 0, "Can not transfer to contract with no contract code");
https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/util/Caller.sol#L11-L22 https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/cgp/governance/InterchainGovernance.sol#L100 https://github.com/code-423n4/2023-07-axelar/blob/main/contracts/interchain-governance-executor/InterchainProposalExecutor.sol#L76
#0 - deanamiel
2023-08-31T00:13:36Z
InterchainProposalExecutor is not abstract. We updated the NatSpec documentation. See PR here
#1 - c4-judge
2023-09-08T11:38:10Z
berndartmueller marked the issue as grade-b