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: 26/47
Findings: 1
Award: $94.77
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Jeiwan
Also found by: 0xkazim, Emmanuel, KrisApostolov, T1MOH, Toshii, UniversalCrypto, Viktor_Cortess, immeas, libratus, nobody2018, qpzm
94.7708 USDC - $94.77
The external function execute()
from AxelarExecutable contracts calls the internal function _execute
from InterchainProposalExecutor
contract that executes the proposal. But these functions are not payable and can't receive native tokens during the call making it impossible to send native tokens to the target contracts during proposal execution.
The execute()
external function calls the crucial internal function _execute
:
function execute( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes calldata payload ) external { bytes32 payloadHash = keccak256(payload); if (!gateway.validateContractCall(commandId, sourceChain, sourceAddress, payloadHash)) revert NotApprovedByGateway(); _execute(sourceChain, sourceAddress, payload); }
The _execute
internal function is responsible for executing the proposal. However, it lacks the ability to receive native tokens, rendering the execution of calls with call.value > 0
impossible.
function _execute( string calldata sourceChain, string calldata sourceAddress, bytes calldata payload ) internal override { _beforeProposalExecuted(sourceChain, sourceAddress, payload); // Check that the source address is whitelisted if (!whitelistedSenders[sourceChain][StringToAddress.toAddress(sourceAddress)]) { revert NotWhitelistedSourceAddress(); } // Decode the payload (address interchainProposalCaller, InterchainCalls.Call[] memory calls) = abi.decode(payload, (address, InterchainCalls.Call[])); // Check that the caller is whitelisted if (!whitelistedCallers[sourceChain][interchainProposalCaller]) { revert NotWhitelistedCaller(); } // Execute the proposal with the given arguments _executeProposal(calls); _onProposalExecuted(sourceChain, sourceAddress, interchainProposalCaller, payload); emit ProposalExecuted(keccak256(abi.encode(sourceChain, sourceAddress, interchainProposalCaller, payload))); }
The last internal function, _executeProposal()
, is supposed to send some native tokens with the call, but due to the non-payable nature of the contract and its functions, it is unable to receive native tokens making sending impossible.
function _executeProposal(InterchainCalls.Call[] memory calls) internal { for (uint256 i = 0; i < calls.length; i++) { InterchainCalls.Call memory call = calls[i]; (bool success, bytes memory result) = call.target.call{ value: call.value }(call.callData); if (!success) { _onTargetExecutionFailed(call, result); } else { _onTargetExecuted(call, result); } } }
Test from InterchainProposalExecutor reverts with an error if we try to send some msg.value
with the call:
describe('_execute', function () { it.only('should be able to call target contract', async function () { // whitelist caller and sender await executor.setWhitelistedProposalCaller('ethereum', signerAddress, true); await executor.setWhitelistedProposalSender('ethereum', signerAddress, true); const callData = dummy.interface.encodeFunctionData('setState', ['Hello World']); const calls = [ { target: dummy.address, value: 0, callData, }, ]; const payload = ethers.utils.defaultAbiCoder.encode( ['address', 'tuple(address target, uint256 value, bytes callData)[]'], [signerAddress, calls], ); const broadcast = () => executor.forceExecute('ethereum', signerAddress, payload, { value: '100' });
Error:
1) Interchain Proposal Executor _execute should be able to call target contract: Error: non-payable method cannot override value (operation="overrides.value", value="100", code=UNSUPPORTED_OPERATION, version=contracts/5.7.0)
vs
To address this issue, I recommend adding either the payable
keyword to the external execute()
function or implementing a receive()
fallback function in the contract. This way, the contract will be able to receive native tokens, allowing successful execution of calls with call.value > 0
.
Payable
#0 - c4-pre-sort
2023-07-29T00:04:13Z
0xSorryNotSorry marked the issue as duplicate of #319
#1 - c4-judge
2023-09-08T10:59:18Z
berndartmueller marked the issue as satisfactory