Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Example 05: ExtraArgsV3 No-Execution-Tag (Manual Execution Path)

This example sends a Hello World data message from an EOA, but disables automatic execution by setting executor to NO_EXECUTION_ADDRESS inside ExtraArgsV3.

Script path: script/examples/Example05.s.sol

What You Will Do

  1. Ensure a compatible destination receiver exists (default finality or Fast Transfers (FTF) based on your blockConfirmations).
  2. Send a CCIP data message with ExtraArgsV3 no-execution-tag.
  3. Observe pending execution in CCIP Explorer and execute manually.

Before You Start

Important: Keystore first

Use a local keystore account for script execution:

cast wallet import myAccount --interactive
Enter private key:
Enter password:
`myAccount` keystore was saved successfully. Address: <YOUR_EOA_ADDRESS_SHOULD_APPEAR_HERE>

This chapter assumes --account myAccount.

If you do not have a receiver deployed yet

Choose deployment based on your blockConfirmations:

forge script script/examples/Example04.s.sol:DeployBasicMessageReceiver \
  --rpc-url polygonAmoy \
  --account myAccount \
  --broadcast \
  --sig "run(address)" \
  <DESTINATION_ROUTER>

Use this for default finality (blockConfirmations = 0). Save this as <BASIC_MESSAGE_RECEIVER_ADDRESS>.

forge script script/examples/Example02.s.sol:DeployBasicMessageReceiverWithCCVs \
  --rpc-url polygonAmoy \
  --account myAccount \
  --broadcast \
  --sig "run(address)" \
  <DESTINATION_ROUTER>

If you use Fast Transfers (blockConfirmations > 0), configure min block depth for your source chain:

forge script script/examples/Example02.s.sol:SetBasicMessageReceiverWithCCVsMinBlockDepth \
  --rpc-url polygonAmoy \
  --account myAccount \
  --broadcast \
  --sig "run(address,uint64,uint16)" \
  <BASIC_MESSAGE_RECEIVER_WITH_CCVS_ADDRESS> \
  <SOURCE_CHAIN_SELECTOR> \
  <MIN_BLOCK_DEPTH>

Use <MIN_BLOCK_DEPTH> > 0 for Fast Transfers.

Use this for Fast Transfers (blockConfirmations > 0). Save this as <BASIC_MESSAGE_RECEIVER_WITH_CCVS_ADDRESS>.

Understanding ExtraArgsV3

The ExtraArgsV3 is a structured set of delivery/execution options encoded and attached to a CCIP message.

struct GenericExtraArgsV3 {
    uint32 gasLimit;
    bytes4 requestedFinalityConfig;
    address[] ccvs;
    bytes[] ccvArgs;
    address executor;
    bytes executorArgs;
    bytes tokenReceiver;
    bytes tokenArgs;
}

(On-chain this struct lives in ExtraArgsCodec from @chainlink/contracts-ccip.)

  • gasLimit: gas allocated for callback execution on destination. If 0 and message data is empty, no callback executes.
  • requestedFinalityConfig: bytes4 finality mode + parameters per FinalityCodec (not a bare uint16). All-zero means wait for default/lane finality. For block-depth style Fast Transfers, encode with FinalityCodec._encodeBlockDepth(uint16) (see CCIP / FinalityCodec NatSpec). Example05 and EncodeExtraArgsOffchain.encodeV3 / encodeV3Basic take a blockConfirmations argument only as a convenience and set this field to FinalityCodec._encodeBlockDepth(blockConfirmations).
  • ccvs: list of cross-chain verifier addresses. Empty means default verifiers.
  • ccvArgs: optional arguments for each CCV. Must match ccvs length.
  • executor: executor address on source chain. address(0) uses default executor.
  • executorArgs: chain/executor-specific args (format depends on chain family/executor implementation).
  • tokenReceiver: encoded destination token receiver. If empty, receiver address is used.
  • tokenArgs: extra token transfer args (pool-specific format).

Related helpers in script/EncodeExtraArgsOffchain.s.sol:

  • encodeV3Basic(gasLimit, blockConfirmations) for a minimal V3 payload.
  • getNoExecutionAddress() returns Client.NO_EXECUTION_ADDRESS for manual execution path.

Step 1: Send Message With No-Execution-Tag

forge script script/examples/Example05.s.sol:Example05 \
  --rpc-url ethereumSepolia \
  --account myAccount \
  --broadcast \
  --sig "run(address,uint64,address,string,uint32,uint16,address)" \
  <SOURCE_ROUTER> \
  <DESTINATION_CHAIN_SELECTOR> \
  <BASIC_MESSAGE_RECEIVER_ADDRESS_OR_BASIC_MESSAGE_RECEIVER_WITH_CCVS_ADDRESS> \
  "Hello, World" \
  <GAS_LIMIT> \
  <BLOCK_CONFIRMATIONS> \
  <FEE_TOKEN_ADDRESS>

Parameter notes:

  • <GAS_LIMIT> must be > 0 because the receiver callback needs gas.
  • <BLOCK_CONFIRMATIONS> can be 0 (default finality) or > 0.
  • If <BLOCK_CONFIRMATIONS> > 0, destination receiver must accept Fast Transfers (minBlockDepth > 0) for this source chain.
  • If <BLOCK_CONFIRMATIONS> = 0, a default-finality receiver is sufficient.
  • <FEE_TOKEN_ADDRESS>: Pass the LINK token address on the source chain here. If you want to pay for CCIP fees in native coin instead, pass 0x0000000000000000000000000000000000000000
  • This example sets executor to NO_EXECUTION_ADDRESS, so execution is not automatic.

Verify Result

Example05 logs a CCIP message ID.

Use that ID in CCIP Explorer:

  • https://ccip.chain.link

Expected behavior:

  • Message is delivered to a pending/manual execution state because automatic execution is disabled.