Skip to main content
Version: 1.5.1

safe-propose

Propose a CCIP send as a Safe multisig transaction.

Synopsis

Bash
ccip-cli safe-propose -s <source> -d <dest> -r <router> --safe <address> [options]

Description

The safe-propose command submits a CCIP message to the Safe Transaction Service queue without executing it on-chain. Other Safe owners can then review, sign, and execute the transaction in the Safe UI.

If the CCIP send requires ERC-20 approvals (token transfers or LINK fee payment), those are proposed as separate Safe transactions ahead of the ccipSend, with sequential nonces so they can be executed in order.

The proposer must be a wallet that exposes a raw private key (private key, Foundry keystore, or Hardhat account). Hardware wallets are not supported.

Options

Required

OptionAliasTypeDescription
--source-sstringSource network (chain ID, selector, or name)
--dest-dstringDestination network (chain ID, selector, or name)
--router-rstringCCIP Router contract address on source chain
--safe-stringSafe multisig address (the CCIP message sender)

Message

OptionAliasTypeDefaultDescription
--receiver--tostringSafe addressReceiver address on destination. Defaults to the Safe address if omitted.
--data-string-Message data payload. Hex byte arrays (0x...) and base64 strings are decoded as raw bytes. Prefix with 0x: to ABI-encode as a Solidity string. All other values are raw UTF-8 encoded.
--transfer-tokens-tstring[]-Token transfers as token=amount. See Token Amount Format.
--fee-token-stringNativeFee token address or symbol (e.g., LINK). Omit to pay in native token. Note: if native fee is used, the Safe must hold enough native token before owners execute.
--token-receiver-string-Token receiver address on destination if different from --receiver.

Gas & Execution

OptionAliasTypeDefaultDescription
--gas-limit-L, --compute-unitsnumber-Gas limit for receiver callback.
--allow-out-of-order-exec--ooobooleantrueAllow out-of-order execution. Supported on v1.5+ lanes.

Safe

OptionTypeDefaultDescription
--safe-api-keystring$SAFE_API_KEYAPI key for the Safe Transaction Service. Required when using api.safe.global. Can also be set via the SAFE_API_KEY environment variable.
--safe-service-urlstringOfficial Safe service for the chainOverride the Safe Transaction Service URL. Use this for self-hosted or custom deployments.

Wallet

OptionAliasTypeDescription
--wallet-wstringProposer wallet. Accepts 0x<privateKey>, foundry:<name>, or hardhat:<name>. Ledger is not supported.
--approve-max-booleanApprove maximum token allowance instead of exact amount for any ERC-20 approvals.

Extra Args

OptionAliasTypeDescription
--extra-xstring[]Extra args as key=value. Values parsed as JSON with BigInt support; fallback to string. Repeated keys become arrays.

See Configuration for global options (--rpcs, --rpcs-file, --format, etc.).

Token Amount Format

Same as send token amount format: token=amount pairs with human-readable decimals.

Bash
-t 0xTokenAddress=1.5
-t USDC=100

Output

Text output (default)

Fee: 223802089017692n = 0.000223802089017692 ETH
Safe balance may be insufficient for fee: has 0.0 ETH, needs 0.000223802089017692 ETH. Ensure the Safe is funded before owners execute the queued transaction.
1 ERC-20 approval(s) needed — will be queued before the ccipSend.
✔ Enter password for Foundry keystore 'mykey'
Proposing 2 transaction(s) to Safe 0xSafe... starting at nonce 3...
✓ Proposed approve(tCCIP) (nonce 3): 0xabcd...
✓ Proposed ccipSend (nonce 4): 0x1234...

View & sign in Safe UI:
https://app.safe.global/transactions/queue?safe=sep:0xSafe...

Proposed safeTxHashes:
approve(tCCIP): 0xabcd...
ccipSend: 0x1234...

Note: execute the approval(s) in the Safe UI before executing the ccipSend.

Once ccipSend is executed, track CCIP delivery with:
ccip-cli show <onChainTxHash> -s ethereum-testnet-sepolia

JSON output (--format json)

JSON
{
"safeAddress": "0xSafe...",
"proposer": "0xProposer...",
"transactionsProposed": 2,
"approvalsProposed": 1,
"fee": "223802089017692",
"feeFormatted": "0.000223802089017692 ETH",
"proposedHashes": ["0xabcd...", "0x1234..."],
"safeUiUrl": "https://app.safe.global/transactions/queue?safe=sep:0xSafe..."
}

Examples

Propose a data-only CCIP message

Bash
ccip-cli safe-propose \
-s ethereum-testnet-sepolia \
-d arbitrum-sepolia \
-r 0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59 \
--safe 0xYourSafeAddress \
--to 0xReceiverAddress \
--data "hello" \
--wallet foundry:mykey

Propose a token transfer (queues approve + ccipSend)

Bash
ccip-cli safe-propose \
-s ethereum-testnet-sepolia \
-d arbitrum-sepolia \
-r 0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59 \
--safe 0xYourSafeAddress \
-t 0xTokenAddress=1.5 \
--wallet foundry:mykey \
--safe-api-key $SAFE_API_KEY
Bash
ccip-cli safe-propose \
-s ethereum-testnet-sepolia \
-d arbitrum-sepolia \
-r 0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59 \
--safe 0xYourSafeAddress \
--to 0xReceiverAddress \
--data "hello" \
--fee-token LINK \
--wallet 0xYourPrivateKey

Notes

Transaction ordering

When approvals are needed, they are proposed with nonces strictly before the ccipSend. In the Safe UI, execute them in the displayed order — approvals first, then ccipSend.

Native fee and Safe balance

If the fee is paid in the native token (default), the Safe must hold enough native token at execution time. The command warns if the Safe's current balance is below the estimated fee, but execution can still be queued and the Safe funded later.

Proposer requirements

  • Must be listed as an owner (or delegate) of the Safe on the Safe Transaction Service.
  • Must provide a private-key-based wallet. Hardware wallets (Ledger) are not supported because Safe transaction signing requires access to the raw private key to generate an EIP-712 signature.

API key

The Safe Transaction Service at api.safe.global requires an API key. Obtain one at developer.safe.global. Pass it via --safe-api-key or the SAFE_API_KEY environment variable. Self-hosted deployments typically do not require an API key — use --safe-service-url to point to them.