Skip to content
Open
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 deployment/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ Production lane configure avoids mixed batches by using **Run 1 + Run 2** (below

| Run | What | MCMS | Entry |
|---|---|---|---|
| **Run 1** | CCIP core lanes (GlobalConfig, FeeQuoter, OnRamp/OffRamp, Executor) | `ccipOwner` | Generic `configure-chains-for-lanes-from-topology` → `adapters/chain_family_adapter.go` |
| **Run 1** | CCIP core lanes (GlobalConfig, FeeQuoter, OnRamp/OffRamp, Executor) + native fee token TAR registration (Amulet) | `ccipOwner` | Generic `configure-chains-for-lanes-from-topology` → `adapters/chain_family_adapter.go` |
| **Run 2** | CommitteeVerifier only (`ApplyRemoteChainConfigUpdates`, signatures, allowlist) | `ccvOwner` | `changesets/configure_canton_committee_verifier_for_lanes.go` |

Run 1: omit CV from Canton chain input. Run 2: custom chain-family registry delegates only to `sequences/configure_committee_verifier_for_lanes.go`.
Expand Down
39 changes: 39 additions & 0 deletions deployment/adapters/chain_family_adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ import (
"github.com/smartcontractkit/chainlink-deployments-framework/datastore"
cldfops "github.com/smartcontractkit/chainlink-deployments-framework/operations"

"github.com/smartcontractkit/chainlink-canton/contracts"
committeeverifierop "github.com/smartcontractkit/chainlink-canton/deployment/operations/ccip/committee_verifier"
executorop "github.com/smartcontractkit/chainlink-canton/deployment/operations/ccip/executor"
"github.com/smartcontractkit/chainlink-canton/deployment/operations/ccip/fee_quoter"
"github.com/smartcontractkit/chainlink-canton/deployment/operations/ccip/global_config"
"github.com/smartcontractkit/chainlink-canton/deployment/operations/ccip/offramp"
"github.com/smartcontractkit/chainlink-canton/deployment/operations/ccip/onramp"
"github.com/smartcontractkit/chainlink-canton/deployment/operations/ccip/token_admin_registry"
"github.com/smartcontractkit/chainlink-canton/deployment/sequences"
dsutil "github.com/smartcontractkit/chainlink-canton/deployment/utils/datastore"
cantonmcms "github.com/smartcontractkit/chainlink-canton/deployment/utils/mcms"
Expand Down Expand Up @@ -135,6 +137,43 @@ func (a *CantonChainFamilyAdapter) configureChainForLanes(
return ccipseq.OnChainOutput{}, fmt.Errorf("resolve Canton native fee token instrument: %w", err)
}

// Register native fee token (Amulet) TokenConfig in TAR once per lane configure run.
// Skipped when already on-ledger. Not inlined in proposal-driven core deploy because
// timelock-execute pre-resolves TAR before the deploy batch creates it.
tarRef, err := findContractRef(
ds,
input.ChainSelector,
datastore.ContractType(token_admin_registry.ContractType),
token_admin_registry.Version,
"",
)
if err != nil {
return ccipseq.OnChainOutput{}, fmt.Errorf("resolve token admin registry: %w", err)
}
tarRaw, err := dsutil.GetRawInstanceAddressFromAddressRef(tarRef)
if err != nil {
return ccipseq.OnChainOutput{}, fmt.Errorf("resolve token admin registry raw address: %w", err)
}
ccipOwnerParty, err := resolveCcipOwnerParty(ds, input.ChainSelector)
if err != nil {
return ccipseq.OnChainOutput{}, fmt.Errorf("resolve ccipOwner party: %w", err)
}
mcmsEnabled := len(chain.Participants[0].ReadAsPartyIDs) > 0
tarReport, err := cldfops.ExecuteSequence(b, sequences.RegisterNativeFeeTokenInTAR, chain, sequences.RegisterNativeFeeTokenInTARInput{
TokenAdminRegistryInstanceAddress: contracts.HexToInstanceAddress(tarRef.Address),
TokenAdminRegistryRawInstanceAddress: tarRaw,
InstrumentId: nativeInstrument,
CcipOwnerParty: ccipOwnerParty,
TokenQualifier: string(nativeInstrument.Id),
ChainSelector: input.ChainSelector,
ProposalDriven: mcmsEnabled,
})
if err != nil {
return ccipseq.OnChainOutput{}, fmt.Errorf("register native fee token in TAR: %w", err)
}
out.BatchOps = append(out.BatchOps, tarReport.Output.BatchOps...)
out.Addresses = append(out.Addresses, tarReport.Output.Addresses...)

for remoteSelector, remoteCfg := range input.RemoteChains {
localExecutor, err := resolveContractRefByAddress(
ds,
Expand Down
3 changes: 3 additions & 0 deletions deployment/changesets/register_native_fee_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import (
// RegisterNativeFeeTokenInTARConfig registers the Canton native fee token (Amulet) in TAR.
// Creates TokenConfig only — no token pool. InstrumentId is resolved from the validator
// scan-proxy when admin/id are empty (requires ledger access at pipeline run time).
//
// New environments: this is also emitted automatically during lane configure Run 1.
// Keep this changeset for standalone registration or envs that ran lanes before that hook existed.
type RegisterNativeFeeTokenInTARConfig struct {
CCIPOwnerParty string `json:"ccipOwnerParty" yaml:"ccipOwnerParty"`
MinDelay time.Duration `json:"minDelay,omitempty" yaml:"minDelay,omitempty"`
Expand Down
37 changes: 21 additions & 16 deletions deployment/sequences/deploy_chain_contracts_from_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@ var DeployChainContractsFromFactory = operations.NewSequence(
if err != nil {
return sequences.OnChainOutput{}, err
}
nativeInstrumentID, err := resolveNativeInstrumentID(b, deps, input.NativeInstrumentId)
if err != nil {
return sequences.OnChainOutput{}, fmt.Errorf("failed to resolve native fee token instrument: %w", err)
}
factoryRawInstanceAddress, err := rawInstanceAddressFromAddressRef(input.FactoryAddressRef)
if err != nil {
return sequences.OnChainOutput{}, err
Expand Down Expand Up @@ -105,19 +101,28 @@ var DeployChainContractsFromFactory = operations.NewSequence(
tokenAdminRegistryRawInstanceAddress := tokenAdminRegistryInstanceID.RawInstanceAddress(ccipOwnerParty)
addresses = append(addresses, newAddressRef(deps.ChainSelector(), tokenAdminRegistryRawInstanceAddress, token_admin_registry.ContractType, token_admin_registry.Version, ""))

feeTokenConfigOutputs, err := ensureNativeFeeTokenConfig(
b,
deps,
tokenAdminRegistryRawInstanceAddress.InstanceAddress(),
tokenAdminRegistryRawInstanceAddress,
input.CCIPOwnerParty,
nativeInstrumentID,
input.ProposalDriven,
)
if err != nil {
return sequences.OnChainOutput{}, fmt.Errorf("failed to ensure native fee token config: %w", err)
// Native fee token (Amulet) TAR registration is skipped in proposal-driven factory
// deploy: timelock-execute resolves TAR from ledger before the batch creates it.
// Registered during lane configure Run 1 (configureChainForLanes) after TAR exists.
if !input.ProposalDriven {
nativeInstrumentID, err := resolveNativeInstrumentID(b, deps, input.NativeInstrumentId)
if err != nil {
return sequences.OnChainOutput{}, fmt.Errorf("failed to resolve native fee token instrument: %w", err)
}
feeTokenConfigOutputs, err := ensureNativeFeeTokenConfig(
b,
deps,
tokenAdminRegistryRawInstanceAddress.InstanceAddress(),
tokenAdminRegistryRawInstanceAddress,
input.CCIPOwnerParty,
nativeInstrumentID,
input.ProposalDriven,
)
if err != nil {
return sequences.OnChainOutput{}, fmt.Errorf("failed to ensure native fee token config: %w", err)
}
proposalOutputs = append(proposalOutputs, feeTokenConfigOutputs...)
}
proposalOutputs = append(proposalOutputs, feeTokenConfigOutputs...)

feeQuoterInstanceID, err := ensureInstanceID(input.FeeQuoterConfig.Template.InstanceId, "feequoter")
if err != nil {
Expand Down
Loading