Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion .github/actions/setup-ccip-devenv/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ inputs:
# builds the verifier/devenv Docker images, while go.mod pins the `ccv up` tooling
# that generates their configs. A mismatch across config-format changes (e.g. #1193
# bootstrap monitoring.Config) makes the verifier container exit on startup.
default: 18539d60fc1d090363c3b6cf95af64b2f1f998ef
default: 3f3baa768540206ff32b0aa58daab18b73d0fc62
ccv-iam-role:
description: AWS IAM role ARN for CCV ECR authentication
required: true
Expand Down
2 changes: 1 addition & 1 deletion ccip/committee_verifier.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:1.7
FROM golang:1.26-alpine AS builder
FROM golang:1.26.5-alpine@sha256:0178a641fbb4858c5f1b48e34bdaabe0350a330a1b1149aabd498d0699ff5fb2 AS builder
WORKDIR /build

# Copy module files first to maximize cache reuse (deps layer invalidated only when go.mod/go.sum change)
Expand Down
159 changes: 138 additions & 21 deletions deployment/adapters/chain_family_adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,44 @@ type CantonChainFamilyAdapter struct{}

var CantonFamilySelector = [4]byte{0xdf, 0xaf, 0xaf, 0x4b}

func isEVMSelector(chainSelector uint64) bool {
family, err := chainsel.GetSelectorFamily(chainSelector)
if err != nil {
return false
}

return family == chainsel.FamilyEVM
}

func DefaultCantonFeeQuoterDestChainConfig() lanes.FeeQuoterDestChainConfig {
return lanes.FeeQuoterDestChainConfig{
OverrideExistingConfig: false,
IsEnabled: true,
MaxDataBytes: 30_000,
MaxPerMsgGasLimit: 3_000_000,
DestGasOverhead: 300_000,
DestGasPerPayloadByteBase: 16,
MaxDataBytes: 32_000,
MaxPerMsgGasLimit: 15_000_000,
DestGasOverhead: 0,
DestGasPerPayloadByteBase: 20,
ChainFamilySelector: binary.BigEndian.Uint32(CantonFamilySelector[:]),
DefaultTokenFeeUSDCents: 0,
DefaultTokenDestGasOverhead: 90_000,
DefaultTxGasLimit: 200_000,
NetworkFeeUSDCents: 50,
V1Params: nil,
V2Params: &lanes.FeeQuoterV2Params{
LinkFeeMultiplierPercent: 90,
USDPerUnitGas: big.NewInt(38),
},
}
}

func defaultCantonFeeQuoterDestChainConfig() lanes.FeeQuoterDestChainConfig {
return lanes.FeeQuoterDestChainConfig{
OverrideExistingConfig: false,
IsEnabled: true,
MaxDataBytes: 32_000,
MaxPerMsgGasLimit: 15_000_000,
DestGasOverhead: 0,
DestGasPerPayloadByteBase: 20,
ChainFamilySelector: binary.BigEndian.Uint32(CantonFamilySelector[:]),
DefaultTokenFeeUSDCents: 25,
DefaultTokenDestGasOverhead: 90_000,
Expand Down Expand Up @@ -188,13 +218,7 @@ func (a *CantonChainFamilyAdapter) configureChainForLanes(
if err != nil {
return out, fmt.Errorf("resolve executor for remote chain %d: %w", remoteSelector, err)
}
defaultInboundCCVs, err := resolveContractRefsByAddresses(
ds,
input.ChainSelector,
datastore.ContractType(committeeverifierop.ContractType),
committeeverifierop.Version,
remoteCfg.DefaultInboundCCVs,
)
defaultInboundCCVs, err := resolveDefaultInboundCCVs(ds, input.ChainSelector, remoteCfg)
if err != nil {
return out, fmt.Errorf("resolve default inbound ccvs for remote chain %d: %w", remoteSelector, err)
}
Expand Down Expand Up @@ -413,8 +437,31 @@ func (a *CantonChainFamilyAdapter) GetDefaultCommitteeVerifierRemoteChainConfig(
}

// GetDefaultFeeQuoterDestChainConfig implements [adapters.ChainFamily].
func (a *CantonChainFamilyAdapter) GetDefaultFeeQuoterDestChainConfig(_, _ uint64, chainFamilySelector [4]byte) ccipadapters.FeeQuoterDestChainConfigOverrides {
defaults := DefaultCantonFeeQuoterDestChainConfig()
//
// The lane changeset asks the *remote* chain's adapter how the local chain should configure
// its FeeQuoter for that remote as a destination, so this is reached with Canton as the
// destination and sourceChainSelector naming the sending chain.
func (a *CantonChainFamilyAdapter) GetDefaultFeeQuoterDestChainConfig(sourceChainSelector, _ uint64, chainFamilySelector [4]byte) ccipadapters.FeeQuoterDestChainConfigOverrides {
if isEVMSelector(sourceChainSelector) {
// Canton has no gas market and execution is manual, so every gas-derived field is
// zeroed and the lane is priced by a flat network fee instead.
return ccipadapters.FeeQuoterDestChainConfigOverrides{
IsEnabled: new(true),
MaxDataBytes: new(uint32(32_000)),
MaxPerMsgGasLimit: new(uint32(15_000_000)),
DestGasOverhead: new(uint32(0)),
DestGasPerPayloadByteBase: new(uint8(20)),
ChainFamilySelector: chainFamilySelector,
DefaultTokenFeeUSDCents: new(uint16(0)),
DefaultTokenDestGasOverhead: new(uint32(0)),
DefaultTxGasLimit: new(uint32(1)),
NetworkFeeUSDCents: new(uint16(50)),
LinkFeeMultiplierPercent: new(uint8(90)),
// USDPerUnitGas is left unset to avoid a gas price update by default.
}
}

defaults := defaultCantonFeeQuoterDestChainConfig()
linkFeeMultiplier := uint8(0)
if defaults.V2Params != nil {
linkFeeMultiplier = defaults.V2Params.LinkFeeMultiplierPercent
Expand Down Expand Up @@ -448,17 +495,37 @@ func (a *CantonChainFamilyAdapter) GetDefaultFinalityConfig() finality.Config {
}

// GetDefaultRemoteChainConfig implements [adapters.ChainFamily].
func (a *CantonChainFamilyAdapter) GetDefaultRemoteChainConfig(_, _ uint64) ccipadapters.RemoteChainDefaults {
//
// The lane changeset asks the *remote* chain's adapter for the defaults the local chain should
// apply to that remote, so this is reached with Canton as the remote and sourceChainSelector
// naming the sending chain.
func (a *CantonChainFamilyAdapter) GetDefaultRemoteChainConfig(sourceChainSelector, _ uint64) ccipadapters.RemoteChainDefaults {
if isEVMSelector(sourceChainSelector) {
// Canton messages are executed manually (ManuallyExecuteMessage), so no EVM Executor
// supports Canton as a destination. SkipExecutorConfig keeps the EVM sequence from
// reading or configuring an Executor contract for this lane; the EVM sequence writes
// its own no-execution sentinel into the OnRamp's default-executor field, so no
// family-specific address crosses this boundary.
return ccipadapters.RemoteChainDefaults{
AllowTrafficFrom: true,
ExecutorDestChainConfig: ccipadapters.ExecutorDestChainConfig{
USDCentsFee: 0,
Enabled: false,
},
BaseExecutionGasCost: 200_000,
TokenReceiverAllowed: false,
MessageNetworkFeeUSDCents: 50,
TokenNetworkFeeUSDCents: 50,
SkipExecutorConfig: true,
}
}

return ccipadapters.RemoteChainDefaults{
AllowTrafficFrom: true, // TODO: check what this does?
ExecutorDestChainConfig: ccipadapters.ExecutorDestChainConfig{
USDCentsFee: 0,
Enabled: true,
},
AllowTrafficFrom: true,
BaseExecutionGasCost: 50_000,
TokenReceiverAllowed: true,
MessageNetworkFeeUSDCents: 0,
TokenNetworkFeeUSDCents: 0,
MessageNetworkFeeUSDCents: 10,
TokenNetworkFeeUSDCents: 25,
}
}

Expand Down Expand Up @@ -502,6 +569,56 @@ func resolveContractRefsByAddresses(ds datastore.DataStore, chainSelector uint64
return refs, nil
}

func resolveDefaultInboundCCVs(
ds datastore.DataStore,
chainSelector uint64,
remoteCfg ccipadapters.RemoteChainConfig[[]byte, string],
) ([]datastore.AddressRef, error) {
if len(remoteCfg.DefaultInboundCCVs) > 0 {
return resolveContractRefsByAddresses(
ds,
chainSelector,
datastore.ContractType(committeeverifierop.ContractType),
committeeverifierop.Version,
remoteCfg.DefaultInboundCCVs,
)
}

// Hardening: default inbound CCV is invalid-ccv (fail-closed) when not explicitly configured.
// The sentinel is hardcoded rather than resolved from the datastore so lanes stay fail-closed
// even when no invalid-ccv qualifier was ever written to the address book.
ref, err := invalidCCVAddressRef(chainSelector)
if err != nil {
return nil, fmt.Errorf("build default invalid-ccv inbound ref: %w", err)
}

return []datastore.AddressRef{ref}, nil
}

// InvalidCCVRawInstanceAddress is the fail-closed sentinel CCV used as the default inbound CCV
// when a remote chain config does not specify one. It intentionally points at a CommitteeVerifier
// instance that can never verify, so unconfigured inbound lanes reject messages.
const InvalidCCVRawInstanceAddress = "invalid-ccv@ccvOwner::1220e382f4e57b0815e6be737006e381e6b7de448e06bd033ece6df498017879f551"

// invalidCCVAddressRef synthesizes the datastore ref for InvalidCCVRawInstanceAddress. The label
// carries the raw instance address, which is what dsutil.GetRawInstanceAddressFromAddressRef reads
// when the ref is converted to a binding.
func invalidCCVAddressRef(chainSelector uint64) (datastore.AddressRef, error) {
raw, err := contracts.RawInstanceAddressFromString(InvalidCCVRawInstanceAddress)
if err != nil {
return datastore.AddressRef{}, fmt.Errorf("parse %q: %w", InvalidCCVRawInstanceAddress, err)
}

return datastore.AddressRef{
ChainSelector: chainSelector,
Type: datastore.ContractType(committeeverifierop.ContractType),
Version: committeeverifierop.Version,
Qualifier: "invalid-ccv",
Address: raw.InstanceAddress().Hex(),
Labels: datastore.NewLabelSet(raw.String()),
}, nil
}

func convertCommitteeVerifierConfigs(configs []ccipadapters.CommitteeVerifierConfig[datastore.AddressRef]) []lanes.CommitteeVerifierConfig[datastore.AddressRef] {
out := make([]lanes.CommitteeVerifierConfig[datastore.AddressRef], 0, len(configs))
for _, cfg := range configs {
Expand Down
2 changes: 1 addition & 1 deletion deployment/adapters/configure_lanes_token_prices.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const defaultLinkTokenInstrumentID = "link-token"
const defaultLinkUsdPerTokenDollars int64 = 10

// defaultNativeUsdPerToken is the nominal Amulet/USD spot used when FamilyExtras omit a price.
const defaultNativeUsdPerToken = "0.1"
const defaultNativeUsdPerToken = "0.15"

// cantonUsdPerTokenScale is the internal fixed-point scale (USD * 1e8) before formatting to Decimal.
const cantonUsdPerTokenScale int64 = 100_000_000
Expand Down
2 changes: 1 addition & 1 deletion eds/eds.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# build stage
FROM golang:1.26-alpine@sha256:91eda9776261207ea25fd06b5b7fed8d397dd2c0a283e77f2ab6e91bfa71079d AS builder
FROM golang:1.26.5-alpine@sha256:0178a641fbb4858c5f1b48e34bdaabe0350a330a1b1149aabd498d0699ff5fb2 AS builder

# Layer 1: Download dependencies first to leverage Docker layer caching
WORKDIR /build
Expand Down
18 changes: 9 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/smartcontractkit/chainlink-canton

go 1.26.2
go 1.26.5

// Coming from chainlink-deployments-framework
replace github.com/fbsobreira/gotron-sdk => github.com/smartcontractkit/chainlink-tron/relayer/gotron-sdk v0.0.5-0.20251014124537-af6b1684fe15
Expand Down Expand Up @@ -34,11 +34,11 @@ require (
github.com/rs/zerolog v1.35.1
github.com/schollz/progressbar/v3 v3.19.1
github.com/smartcontractkit/chain-selectors v1.0.107
github.com/smartcontractkit/chainlink-ccip/chains/evm v0.0.0-20260728180354-4f966ecacc23
github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260728180354-4f966ecacc23
github.com/smartcontractkit/chainlink-ccv v0.2.1-0.20260803114202-18539d60fc1d
github.com/smartcontractkit/chainlink-ccv/build/devenv v0.3.1-0.20260803114202-18539d60fc1d
github.com/smartcontractkit/chainlink-ccv/deployment v0.1.1-0.20260803114202-18539d60fc1d
github.com/smartcontractkit/chainlink-ccip/chains/evm v0.0.0-20260811072549-2a0dec67e6be
github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260811072549-2a0dec67e6be
github.com/smartcontractkit/chainlink-ccv v0.2.1-0.20260811153216-3f3baa768540
github.com/smartcontractkit/chainlink-ccv/build/devenv v0.3.1-0.20260811153216-3f3baa768540
github.com/smartcontractkit/chainlink-ccv/deployment v0.1.1-0.20260811153216-3f3baa768540
github.com/smartcontractkit/chainlink-common v0.11.2-0.20260715145851-8219609496a4
github.com/smartcontractkit/chainlink-deployments-framework v0.117.1
github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.6
Expand Down Expand Up @@ -163,7 +163,7 @@ require (
github.com/segmentio/ksuid v1.0.4 // indirect
github.com/sercand/kuberesolver/v6 v6.0.0 // indirect
github.com/smartcontractkit/ccip-owner-contracts v0.1.0 // indirect
github.com/smartcontractkit/chainlink-ccv/integration/evm v0.0.2-0.20260728185140-e41659e2b739 // indirect
github.com/smartcontractkit/chainlink-ccv/integration/evm v0.0.2-0.20260811153216-3f3baa768540 // indirect
github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260709082627-78ab5315e367 // indirect
github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20260709082627-78ab5315e367 // indirect
github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20260521164805-26d78d5e1243 // indirect
Expand Down Expand Up @@ -292,7 +292,7 @@ require (
github.com/ebitengine/purego v0.10.0 // indirect
github.com/emicklei/dot v1.6.2 // indirect
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
github.com/ethereum/c-kzg-4844/v2 v2.1.7 // indirect
github.com/ethereum/c-kzg-4844/v2 v2.1.8 // indirect
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab // indirect
github.com/failsafe-go/failsafe-go v0.9.0 // indirect
github.com/fbsobreira/gotron-sdk v0.0.0-20250403083053-2943ce8c759b // indirect
Expand Down Expand Up @@ -326,7 +326,7 @@ require (
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/golang/snappy v1.0.1-0.20260716114414-9ae09f520e93 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.7.0
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
Expand Down
Loading
Loading