Skip to main content

Overview

The Executor contract provides secure batch execution of intent calls with comprehensive safety checks and authorization controls. It is deployed by the Inbox contract and executes the calldata specified in fulfilled intents. Contract Location: contracts/Executor.sol Implements: IExecutor

Security Features

  • Authorization: Only the portal contract can execute calls (onlyPortal modifier)
  • EOA Protection: Prevents malicious calls through EOA validation
  • Batch Execution: Supports multiple calls in a single transaction
  • Phishing Protection: Blocks calls to EOAs with calldata to prevent signature phishing

State Variables

portal

address private immutable portal
Address of the portal contract authorized to call execute. Set during contract deployment.

Constructor

Executor()

constructor()
Initializes the Executor contract. Sets the deploying address (portal) as the only authorized caller.

State-Changing Functions

execute

function execute(
    Call[] calldata calls
) external payable override onlyPortal returns (bytes[] memory)
Executes multiple intent calls with comprehensive safety checks.
calls
Call[]
Array of call data containing target addresses, values, and calldata
bytes[]
bytes[]
Array of return data from the successfully executed calls
This function performs validation and execution for each call in the batch:
  1. Prevents calls to EOAs that include calldata (potential phishing protection)
  2. Executes each call and returns results or reverts on any failure

Call Structure

Each call in the array must follow this structure:
struct Call {
    address target;  // The contract address to call
    bytes data;      // ABI-encoded function call data
    uint256 value;   // Amount of native tokens to send with the call
}

Receive Function

receive()

receive() external payable
Allows the contract to receive ETH. Required for handling ETH transfer for intent execution.

Errors

NonPortalCaller

error NonPortalCaller(address caller)
Reverts with this error if caller is not the portal contract.
caller
address
The address that attempted to call the function

CallToEOA

error CallToEOA(address target)
Reverts when attempting to call an Externally Owned Account (EOA) with calldata.
target
address
The EOA address that was targeted
This error prevents potential phishing attacks where calldata might be misinterpreted. Calls to EOAs are only allowed if the calldata is empty.

CallFailed

error CallFailed(Call call, bytes result)
Reverts when a call execution fails.
call
Call
The call that failed
result
bytes
The error data returned from the failed call

Access Control

The onlyPortal modifier restricts all execute functions to only be callable by the portal contract. This ensures:
  • Only validated and fulfilled intents can trigger executions
  • The executor cannot be used by arbitrary callers
  • All executions are properly authorized through the intent fulfillment process

Safety Validations

EOA Check

The executor validates each call to ensure it’s not targeting an EOA with calldata:
function _isCallToEoa(Call calldata call) internal view returns (bool) {
    return call.target.code.length == 0 && call.data.length > 0;
}
This prevents scenarios where:
  • Users might accidentally sign transactions that execute unintended calls
  • Calldata could be misinterpreted by wallets or interfaces
  • Phishing attacks that rely on EOA address confusion

Usage Example

// Executor is created by Inbox during construction
IExecutor executor = new Executor();

// Prepare calls
Call[] memory calls = new Call[](2);
calls[0] = Call({
    target: tokenAddress,
    data: abi.encodeWithSignature("transfer(address,uint256)", recipient, amount),
    value: 0
});
calls[1] = Call({
    target: dexAddress,
    data: abi.encodeWithSignature("swap(address,address,uint256)", tokenA, tokenB, amount),
    value: 0.1 ether
});

// Execute (only callable by portal)
bytes[] memory results = executor.execute{value: 0.1 ether}(calls);

Integration with Inbox

The Executor is tightly integrated with the Inbox contract:
  1. Deployment: Created during Inbox constructor
  2. Token Transfers: ERC20 tokens are transferred to the executor before call execution
  3. Execution: Inbox calls executor with native tokens and validated calls
  4. Results: Execution results are returned to the fulfiller
This separation of concerns ensures:
  • Clean separation between validation (Inbox) and execution (Executor)
  • Isolated execution environment for intent calls
  • Clear audit trail for executed operations

Build docs developers (and LLMs) love