Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 30 additions & 7 deletions build/devenv/cli/send/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ import (
"github.com/smartcontractkit/chainlink-ccv/build/devenv/evm"
)

// v2DefaultExecutionGasLimit is used for V2 sends when --gas-limit is not set.
const v2DefaultExecutionGasLimit uint32 = 200_000

func Command() *cobra.Command {
var args sendArgs
var token string
Expand All @@ -52,6 +55,7 @@ func Command() *cobra.Command {
if err != nil {
return err
}
args.gasLimitSet = cmd.Flags().Changed("gas-limit")

return run(args)
},
Expand All @@ -70,6 +74,7 @@ func Command() *cobra.Command {
cmd.Flags().Uint64Var(&args.finalitySel, "finality", 0, "Finality chain selector (optional, only for V3 messages)")
cmd.Flags().BoolVar(&args.omitCommittee, "omit-committee", false, "Omit committee verifier from CCVs (e.g. for CCTP-only sends)")
cmd.Flags().BoolVar(&args.useTestRouter, "use-test-router", false, "Look up TestRouter contract from datastore instead of regular Router")
cmd.Flags().Uint32Var(&args.gasLimit, "gas-limit", 0, "Execution gas limit for destination chain delivery (V2 default 200000 when unset; V3 default 0 when unset)")

_ = cmd.MarkFlagRequired("src")
_ = cmd.MarkFlagRequired("dest")
Expand All @@ -91,6 +96,8 @@ type sendArgs struct {
srcSel uint64
destSel uint64
finalitySel uint64
gasLimit uint32
gasLimitSet bool
}

func run(args sendArgs) error {
Expand Down Expand Up @@ -180,16 +187,21 @@ func run(args sendArgs) error {
return fmt.Errorf("failed to send message: %w", err)
}

ccv.Plog.Info().Msgf("Message ID: %s", hexutil.Encode(result.MessageID[:]))
msgID := hexutil.Encode(result.MessageID[:])
ccv.Plog.Info().Msgf("Message ID: %s", msgID)
ccv.Plog.Info().Msgf("CCIP Explorer: https://ccip.chain.link/msg/%s", msgID)
ccv.Plog.Info().Msgf("Receipt issuers: %s", result.ReceiptIssuers)
return nil
}

func getMessageOptions(args sendArgs, addrs datastore.AddressRefStore) (cciptestinterfaces.MessageOptions, uint8, error) {
if args.finalitySel == 0 {
isV2 := args.finalitySel == 0
executionGasLimit := resolveExecutionGasLimit(args.gasLimit, args.gasLimitSet, isV2)

if isV2 {
// V2 format - use the dedicated V2 function
return cciptestinterfaces.MessageOptions{
ExecutionGasLimit: 200_000,
ExecutionGasLimit: executionGasLimit,
OutOfOrderExecution: true,
}, 2, nil
}
Expand All @@ -205,10 +217,11 @@ func getMessageOptions(args sendArgs, addrs datastore.AddressRefStore) (cciptest
return cciptestinterfaces.MessageOptions{}, 0, fmt.Errorf("failed to get executor address: %w", err)
}
opts := cciptestinterfaces.MessageOptions{
FinalityConfig: protocol.Finality(args.finalitySel),
Executor: common.HexToAddress(executorRef.Address).Bytes(),
ExecutorArgs: nil,
TokenArgs: nil,
FinalityConfig: protocol.Finality(args.finalitySel),
ExecutionGasLimit: executionGasLimit,
Executor: common.HexToAddress(executorRef.Address).Bytes(),
ExecutorArgs: nil,
TokenArgs: nil,
}
if args.omitCommittee {
opts.CCVs = nil
Expand All @@ -233,6 +246,16 @@ func getMessageOptions(args sendArgs, addrs datastore.AddressRefStore) (cciptest
return opts, 3, nil
}

func resolveExecutionGasLimit(gasLimit uint32, gasLimitSet bool, isV2 bool) uint32 {
if gasLimitSet {
return gasLimit
}
if isV2 {
return v2DefaultExecutionGasLimit
}
return 0
}

// resolveFeeToken converts the --fee-token CLI value into an on-chain address.
// Supported inputs:
// - "" or "native": pay in the chain's native gas token (zero address).
Expand Down
33 changes: 21 additions & 12 deletions build/devenv/evm/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,16 @@ func (m *CCIP17EVM) haveEnoughTransferTokens(ctx context.Context, chain evm.Chai
}

func (m *CCIP17EVM) haveEnoughFeeTokens(ctx context.Context, chain evm.Chain, auth *bind.TransactOpts, routerAddress, feeToken common.Address, amount *big.Int) (hasEnough bool, msgValue *big.Int, err error) {
if feeToken == (common.Address{}) {
// if no fee token is specified, pure native token is used, so this is just a BalanceAt check.
balance, err := chain.Client.BalanceAt(ctx, chain.DeployerKey.From, nil)
if err != nil {
return false, nil, fmt.Errorf("failed to get balance: %w", err)
}
// msg.Value is equal to amount in the native token case
return balance.Cmp(amount) >= 0, amount, nil
}

wrappedNativeRef, err := m.ds.Addresses().Get(datastore.NewAddressRefKey(chain.Selector, datastore.ContractType(weth.ContractType), semver.MustParse(weth.Deploy.Version()), ""))
if err != nil {
return false, nil, fmt.Errorf("failed to get wrapped native address: %w", err)
Expand All @@ -552,18 +562,10 @@ func (m *CCIP17EVM) haveEnoughFeeTokens(ctx context.Context, chain evm.Chain, au
return false, nil, fmt.Errorf("failed to get link address: %w", err)
}
wrappedNative := common.HexToAddress(wrappedNativeRef.Address)
link := common.HexToAddress(linkRef.Address)
// TODO: should check if fee token is enabled somehow? Check feeQuoter contract?
linkToken := common.HexToAddress(linkRef.Address)

switch feeToken {
case common.Address{}:
// if no fee token is specified, pure native token is used, so this is just a BalanceAt check.
balance, err := chain.Client.BalanceAt(ctx, chain.DeployerKey.From, nil)
if err != nil {
return false, nil, fmt.Errorf("failed to get balance: %w", err)
}
// msg.Value is equal to amount in the native token case
return balance.Cmp(amount) >= 0, amount, nil
case wrappedNative, link:
case wrappedNative, linkToken:
ok, err := m.ensureERC20HasBalanceAndAllowance(ctx, chain, auth, feeToken, chain.DeployerKey.From, routerAddress, amount)
if err != nil {
return false, nil, err
Expand All @@ -573,7 +575,14 @@ func (m *CCIP17EVM) haveEnoughFeeTokens(ctx context.Context, chain evm.Chain, au
}
return true, big.NewInt(0), nil
default:
return false, nil, fmt.Errorf("unsupported fee token: %s", feeToken.String())
ok, err := m.ensureERC20HasBalanceAndAllowance(ctx, chain, auth, feeToken, chain.DeployerKey.From, routerAddress, amount)
if err != nil {
return false, nil, fmt.Errorf("failed to check fee token balance or allowance for %s: %w", feeToken.Hex(), err)
}
if !ok {
return false, nil, fmt.Errorf("insufficient fee token balance or allowance for %s", feeToken.Hex())
}
return true, big.NewInt(0), nil
}
}

Expand Down
Loading