/Developers. · 04.

Example: a cross-chain contract call

An order with empty outputs and a hook. Encode the selector, put the calldata in the Order, and a solver runs it on the destination chain.

iLayer.io~5 min4 chapters
callDataselectorEthereum → Polygon
/Example./Ethereum → Polygon./01.selectorkeccak256("setValue(uint256)")/02.callDataselector ++ newValue/03.Orderoutputs: [] · callRecipient set/04.ExecutorCalls setValue on Polygon.abi.encodePackedcallValue: 0inputs pay the solverreentrancy blocked
/Example./01.selectoreccak256("setValue(uint256)")/02.callDataselector ++ newValue/03.Orderoutputs: [] ·callRecipient set/04.ExecutorCalls setValueon Polygon.abi.encodePackedcallValue: 0inputs pay the solverreentrancy blocked

Sometimes the outcome you want is not a token but a state change on another chain. iLayer can pay for that with an order whose outputs are empty and whose hook carries the call. Here is the smallest possible example: a dummy contract on Polygon exposing setValue(uint256), triggered from Ethereum.

/01 · Chapter.

Encode the calldata

bytes4 selector = bytes4(keccak256("setValue(uint256)"));
bytes memory callData = abi.encodePacked(selector, newValue);

Standard EVM encoding: four bytes of selector, then the arguments. Any tooling that produces calldata for a normal transaction produces the right bytes here.

/02 · Chapter.

Build the order

{
  sourceChainId: "1",          // Ethereum mainnet
  destinationChainId: "137",   // Polygon PoS
  inputs: [
    // WETH, the solver's payment
    { tokenType: "FUNGIBLE_TOKEN", tokenAddress: "0x…4200000000000000000000000000000000000006", tokenId: "0", amount: "10000000" }
  ],
  outputs: [],
  callRecipient: "0x0…",       // the contract address, as bytes32
  callData: callData,
  callValue: 0,
}

Inputs are what the solver earns for executing; outputs are empty because nothing needs delivering. callValue would carry native value if the target function were payable.

/03 · Chapter.

What happens on Polygon

A solver calls fillOrder() on the Polygon Spoke. There are no outputs to check, so the Spoke goes straight to the hook: the Executor calls setValue(newValue) on your contract. If the call reverts, the fill reverts with ExternalCallFailed() and the solver is not paid. If it succeeds, the Spoke messages the Hub and the WETH is released.

Reentrancy is blocked inside the Executor, so the target cannot call back into iLayer during execution.

/04 · Chapter.

Why this is more than a toy

Replace setValue with claim(), vote(), mint() or deposit() and you have cross-chain governance, cross-chain minting with gas sponsorship, or a vault entry, each as one signed order and with no bridge code in your dapp.

/Read next.

Multi-token outputs.

Next: one input, three outputs, and the weight rule that makes it work.

All posts