Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ConfigureCommitteeVerifierAsDest = operations.NewSequence( "canton/ccip/committee_verifier/configure_as_dest", semver.MustParse("2.0.0"), "Configures inbound CommitteeVerifier settings for remote chains", func(b operations.Bundle, deps chain.BlockChains, input ConfigureCommitteeVerifierAsDestInput) (output sequences.OnChainOutput, err error) { chain, ok := deps.CantonChains()[input.ChainSelector] if !ok { return sequences.OnChainOutput{}, fmt.Errorf("chain with selector %d not found", input.ChainSelector) } signatureConfigs := make([]ccvs.SignatureConfig, 0, len(input.RemoteChains)) for remoteSelector, remoteConfig := range input.RemoteChains { signerKeys := make([]types.TEXT, len(remoteConfig.SignatureConfig.Signers)) for i, signer := range remoteConfig.SignatureConfig.Signers { signerBytes, err := hex.DecodeString(strings.TrimPrefix(signer, "0x")) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to decode signer key %d for remote chain %d: %w", i, remoteSelector, err) } signerKeys[i] = types.TEXT(hex.EncodeToString(signerBytes)) } signatureConfigs = append(signatureConfigs, ccvs.SignatureConfig{ SourceChainSelector: types.NUMERIC(strconv.FormatUint(remoteSelector, 10)), Threshold: types.INT64(remoteConfig.SignatureConfig.Threshold), SignerKeys: signerKeys, }) } var proposalOutputs []contract.ExerciseOutput for _, addressRef := range input.CommitteeVerifier { address := contracts.HexToInstanceAddress(addressRef.Address) raw, err := dsutils.GetRawInstanceAddressFromAddressRef(addressRef) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("committee verifier raw instance address: %w", err) } sigReport, err := operations.ExecuteOperation(b, committee_verifier.ApplySignatureConfigs, chain, contract.ChoiceInput[ccvs.ApplySignatureConfigs]{ InstanceAddress: address, RawInstanceAddress: raw.String(), MCMSEnabled: input.MCMSEnabled, Args: ccvs.ApplySignatureConfigs{ SourceChainSelectorsToRemove: nil, SignatureConfigs: signatureConfigs, }, }) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to apply signature configs to CommitteeVerifier at address %s: %w", address.Hex(), err) } if input.MCMSEnabled && !sigReport.Output.Executed() { proposalOutputs = append(proposalOutputs, sigReport.Output) } } if !input.MCMSEnabled || len(proposalOutputs) == 0 { return sequences.OnChainOutput{}, nil } batchOp, err := contract.NewBatchOperationFromExercises(proposalOutputs) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("build MCMS batch for committee verifier dest configuration: %w", err) } if len(batchOp.Transactions) == 0 { return sequences.OnChainOutput{}, nil } return sequences.OnChainOutput{BatchOps: []mcms_types.BatchOperation{batchOp}}, nil }, )
var ConfigureCommitteeVerifierAsSource = operations.NewSequence( "canton/ccip/committee_verifier/configure_as_source", semver.MustParse("2.0.0"), "Configures the outbound CommitteeVerifier settings for remote chains", func(b operations.Bundle, deps chain.BlockChains, input ConfigureCommitteeVerifierAsSourceInput) (output sequences.OnChainOutput, err error) { chain, ok := deps.CantonChains()[input.ChainSelector] if !ok { return sequences.OnChainOutput{}, fmt.Errorf("chain with selector %d not found", input.ChainSelector) } remoteChainConfigArgs := make([]ccvs.RemoteChainConfigArgs, 0, len(input.RemoteChains)) allowListArgs := make([]ccvs.AllowListConfigArgs, 0, len(input.RemoteChains)) for remoteSelector, remoteConfig := range input.RemoteChains { remoteChainConfigArgs = append(remoteChainConfigArgs, ccvs.RemoteChainConfigArgs{ RemoteChainSelector: types.NUMERIC(strconv.FormatUint(remoteSelector, 10)), FeeUSDCents: types.NUMERIC(strconv.FormatUint(uint64(remoteConfig.FeeUSDCents), 10)), GasForVerification: types.INT64(remoteConfig.GasForVerification), PayloadSizeBytes: types.INT64(remoteConfig.PayloadSizeBytes), AllowListEnabled: types.BOOL(remoteConfig.AllowlistEnabled), }) allowListArgs = append(allowListArgs, ccvs.AllowListConfigArgs{ DestChainSelector: types.NUMERIC(strconv.FormatUint(remoteSelector, 10)), AllowListEnabled: types.BOOL(remoteConfig.AllowlistEnabled), AddedAllowListedSenders: convertPartySlice(remoteConfig.AddedAllowlistedSenders), RemovedAllowListedSenders: convertPartySlice(remoteConfig.RemovedAllowlistedSenders), }) } var proposalOutputs []contract.ExerciseOutput for _, addressRef := range input.CommitteeVerifier { address := contracts.HexToInstanceAddress(addressRef.Address) raw, err := dsutils.GetRawInstanceAddressFromAddressRef(addressRef) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("committee verifier raw instance address: %w", err) } remoteReport, err := operations.ExecuteOperation(b, committee_verifier.ApplyRemoteChainConfigUpdates, chain, contract.ChoiceInput[ccvs.ApplyRemoteChainConfigUpdates]{ InstanceAddress: address, RawInstanceAddress: raw.String(), MCMSEnabled: input.MCMSEnabled, Args: ccvs.ApplyRemoteChainConfigUpdates{ RemoteChainConfigArgs: remoteChainConfigArgs, }, }) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to apply remote chain configs to CommitteeVerifier at address %s: %w", address.Hex(), err) } if input.MCMSEnabled && !remoteReport.Output.Executed() { proposalOutputs = append(proposalOutputs, remoteReport.Output) } allowListReport, err := operations.ExecuteOperation(b, committee_verifier.ApplyAllowListUpdates, chain, contract.ChoiceInput[ccvs.ApplyAllowListUpdates]{ InstanceAddress: address, RawInstanceAddress: raw.String(), MCMSEnabled: input.MCMSEnabled, Args: ccvs.ApplyAllowListUpdates{ AllowListConfigArgsItems: allowListArgs, }, }) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to apply allow list updates to CommitteeVerifier at address %s: %w", address.Hex(), err) } if input.MCMSEnabled && !allowListReport.Output.Executed() { proposalOutputs = append(proposalOutputs, allowListReport.Output) } } if !input.MCMSEnabled || len(proposalOutputs) == 0 { return sequences.OnChainOutput{}, nil } batchOp, err := contract.NewBatchOperationFromExercises(proposalOutputs) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("build MCMS batch for committee verifier source configuration: %w", err) } if len(batchOp.Transactions) == 0 { return sequences.OnChainOutput{}, nil } return sequences.OnChainOutput{BatchOps: []mcms_types.BatchOperation{batchOp}}, nil }, )
var ConfigureLaneLegAsDest = operations.NewSequence( "CantonConfigureLaneLegAsDest", semver.MustParse("2.0.0"), "Configures a lane lad as dest on CCIP 2.0.0", func(b operations.Bundle, deps chain.BlockChains, input lanes.UpdateLanesInput) (output sequences.OnChainOutput, err error) { b.Logger.Infof("Canton Configuring lane leg as source. src: %+v, dest: %+v", input.Source, input.Dest) chain, ok := deps.CantonChains()[input.Dest.Selector] if !ok { return sequences.OnChainOutput{}, fmt.Errorf("chain with selector %d not found", input.Source.Selector) } sourceChain := input.Source destChain := input.Dest mcmsEnabled := len(chain.Participants[0].ReadAsPartyIDs) > 0 var proposalOutputs []contract.ExerciseOutput globalConfigRaw, err := dsutils.GetRawInstanceAddressFromAddressRef(destChain.CantonLaneConfig.GlobalConfig) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("global config raw instance address: %w", err) } laneMandatedInboundCCVs := make([]mcms.RawInstanceAddress, 0, len(destChain.LaneMandatedInboundCCVs)) for _, ccv := range destChain.LaneMandatedInboundCCVs { inboundCCV, err := dsutils.GetRawInstanceAddressFromAddressRef(ccv) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("getting lane mandated inbound CCV: %w", err) } laneMandatedInboundCCVs = append(laneMandatedInboundCCVs, inboundCCV.Binding()) } defaultInboundCCVs := make([]mcms.RawInstanceAddress, 0, len(destChain.DefaultInboundCCVs)) for _, ccv := range destChain.DefaultInboundCCVs { inboundCCV, err := dsutils.GetRawInstanceAddressFromAddressRef(ccv) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("getting lane mandated inbound CCV: %w", err) } defaultInboundCCVs = append(defaultInboundCCVs, inboundCCV.Binding()) } inboundOnRampAddresses := []types.TEXT{ types.TEXT(hex.EncodeToString(gethcommon.LeftPadBytes(sourceChain.OnRamp, 32))), } sourceChainConfigReport, err := operations.ExecuteOperation(b, global_config.ApplySourceChainConfigUpdates, chain, contract.ChoiceInput[common.ApplySourceChainConfigUpdates]{ InstanceAddress: globalConfigRaw.InstanceAddress(), RawInstanceAddress: globalConfigRaw.String(), MCMSEnabled: mcmsEnabled, Args: common.ApplySourceChainConfigUpdates{ SourceChainConfigUpdates: []common.SourceChainConfigArgs{ { SourceChainSelector: types.NUMERIC(strconv.FormatUint(sourceChain.Selector, 10)), IsEnabled: types.BOOL(!input.IsDisabled), OnRampAddresses: inboundOnRampAddresses, DefaultCCVs: defaultInboundCCVs, LaneMandatedCCVs: laneMandatedInboundCCVs, }, }, }, }) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("applying source chain config updates to global config: %w", err) } if mcmsEnabled && !sourceChainConfigReport.Output.Executed() { proposalOutputs = append(proposalOutputs, sourceChainConfigReport.Output) } var cvBatchOps []mcms_types.BatchOperation if !mcmsEnabled { for _, verifierConfig := range input.Dest.CommitteeVerifiers { cvReport, err := operations.ExecuteSequence(b, ConfigureCommitteeVerifierAsDest, deps, ConfigureCommitteeVerifierAsDestInput{ ChainSelector: chain.Selector, MCMSEnabled: mcmsEnabled, CommitteeVerifierConfig: verifierConfig, }) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("configuring committee verifier as dest: %w", err) } cvBatchOps = append(cvBatchOps, cvReport.Output.BatchOps...) } } if !mcmsEnabled { return sequences.OnChainOutput{}, nil } batchOp, err := contract.NewBatchOperationFromExercises(proposalOutputs) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("build MCMS batch for lane configuration: %w", err) } batchOps := cvBatchOps if len(batchOp.Transactions) > 0 { batchOps = append(batchOps, batchOp) } if len(batchOps) == 0 { return sequences.OnChainOutput{}, nil } return sequences.OnChainOutput{BatchOps: batchOps}, nil }, )
var ConfigureLaneLegAsSource = operations.NewSequence( "CantonConfigureLaneLegAsSource", semver.MustParse("2.0.0"), "Configures a lane leg as source on CCIP 2.0.0", func(b operations.Bundle, deps chain.BlockChains, input lanes.UpdateLanesInput) (output sequences.OnChainOutput, err error) { return configureLaneLegAsSource(b, deps, ConfigureLaneLegInput{Lane: input}) }, )
var ConfigureLaneLegAsSourceWithInput = operations.NewSequence( "CantonConfigureLaneLegAsSourceWithInput", semver.MustParse("2.0.0"), "Configures a lane leg as source on CCIP 2.0.0", configureLaneLegAsSource, )
var ConfigureTokenForTransfers = operations.NewSequence( "canton/token-adapter/configure-token-for-transfers", semver.MustParse("2.0.0"), "Configures a Canton token pool for cross-chain transfers", func(b operations.Bundle, chains chain.BlockChains, input tokenadapters.ConfigureTokenForTransfersInput) (ccipsequences.OnChainOutput, error) { if input.ExistingDataStore == nil { return ccipsequences.OnChainOutput{}, fmt.Errorf("existing datastore is required") } cantonChain, ok := chains.CantonChains()[input.ChainSelector] if !ok || len(cantonChain.Participants) == 0 { return ccipsequences.OnChainOutput{}, fmt.Errorf("canton chain with selector %d not found", input.ChainSelector) } participant := cantonChain.Participants[0] mcmsEnabled := len(participant.ReadAsPartyIDs) > 0 var batchOps []mcms_types.BatchOperation var proposalOutputs []contract.ExerciseOutput poolAddress := contracts.HexToInstanceAddress(input.TokenPoolAddress) logicalPoolType, err := resolveCantonTokenPoolType(input.PoolType) if err != nil { return ccipsequences.OnChainOutput{}, err } parsedPool, err := loadConfiguredCantonTokenPool(b.GetContext(), participant, logicalPoolType, poolAddress) if err != nil { return ccipsequences.OnChainOutput{}, err } registryRef, err := input.ExistingDataStore.Addresses().Get(datastore.NewAddressRefKey( input.ChainSelector, datastore.ContractType(token_admin_registry.ContractType), token_admin_registry.Version, "", )) if err != nil { return ccipsequences.OnChainOutput{}, fmt.Errorf("resolve token admin registry: %w", err) } tarRaw, err := dsutils.GetRawInstanceAddressFromAddressRef(registryRef) if err != nil { return ccipsequences.OnChainOutput{}, fmt.Errorf("resolve token admin registry raw address: %w", err) } regOut, err := operations.ExecuteSequence(b, RegisterTokenPool, cantonChain, RegisterTokenPoolInput{ TokenAdminRegistryInstanceAddress: contracts.HexToInstanceAddress(registryRef.Address), TokenAdminRegistryRawInstanceAddress: tarRaw, InstrumentId: parsedPool.InstrumentId, PoolInstanceID: string(parsedPool.InstanceId), CcipParty: string(parsedPool.CcipOwner), PoolOwnerParty: string(parsedPool.PoolOwner), }) if err != nil { return ccipsequences.OnChainOutput{}, fmt.Errorf("register token pool with token admin registry: %w", err) } batchOps = append(batchOps, regOut.Output.BatchOps...) factoryRefs := input.ExistingDataStore.Addresses().Filter( datastore.AddressRefByChainSelector(input.ChainSelector), datastore.AddressRefByType(datastore.ContractType(factoryops.ContractType)), ) factoryRef, err := dsutils.FactoryAddressRefFromRefs(input.ChainSelector, dsutils.QualifierCCIP, factoryRefs) if err != nil { return ccipsequences.OnChainOutput{}, err } factoryRaw, err := rawInstanceAddressFromAddressRef(factoryRef) if err != nil { return ccipsequences.OnChainOutput{}, fmt.Errorf("resolve CCIPFactory raw address: %w", err) } poolRaw := contracts.NewRawInstanceAddress(contracts.InstanceID(parsedPool.InstanceId), parsedPool.PoolOwner) out := ccipsequences.OnChainOutput{} committeeVerifierRefs := input.ExistingDataStore.Addresses().Filter( datastore.AddressRefByChainSelector(input.ChainSelector), datastore.AddressRefByType(datastore.ContractType(committee_verifier.ContractType)), datastore.AddressRefByVersion(committee_verifier.Version), ) updates := make([]tokenPoolChainUpdate, 0, len(input.RemoteChains)) for remoteSelector, remoteCfg := range input.RemoteChains { remoteSelectorKeyStr := strconv.FormatUint(remoteSelector, 10) remoteSelectorKey := types.NUMERIC(remoteSelectorKeyStr) if _, found := parsedPool.RemoteChainConfigs[remoteSelectorKey]; found { return out, fmt.Errorf("remote chain %d is already configured on token pool", remoteSelector) } inboundCCVs, err := resolveCommitteeVerifierRawAddresses(committeeVerifierRefs, remoteCfg.InboundCCVs) if err != nil { return out, fmt.Errorf("resolve inbound CCVs for remote chain %d: %w", remoteSelector, err) } outboundCCVs, err := resolveCommitteeVerifierRawAddresses(committeeVerifierRefs, remoteCfg.OutboundCCVs) if err != nil { return out, fmt.Errorf("resolve outbound CCVs for remote chain %d: %w", remoteSelector, err) } defaultOutboundBucket, _ := remoteCfg.GetOutboundRateLimitBuckets().DefaultBucket() defaultInboundBucket, _ := remoteCfg.GetInboundRateLimitBuckets().DefaultBucket() outboundDefaultCfg, inboundDefaultCfg := tokenadapters.GenerateTPRLConfigs( defaultOutboundBucket.RateLimit, defaultInboundBucket.RateLimit, uint8(parsedPool.Decimals), remoteCfg.RemoteDecimals, "canton", semver.MustParse("2.0.0"), lockReleasePoolType.String(), ) ffOutboundBucket, ffOutboundExists := remoteCfg.GetOutboundRateLimitBuckets().FastFinalityBucket() ffInboundBucket, ffInboundExists := remoteCfg.GetInboundRateLimitBuckets().FastFinalityBucket() customOutboundInput := defaultOutboundBucket.RateLimit customInboundInput := defaultInboundBucket.RateLimit if ffOutboundExists && ffInboundExists { customOutboundInput = ffOutboundBucket.RateLimit customInboundInput = ffInboundBucket.RateLimit } _, inboundCustomCfg := tokenadapters.GenerateTPRLConfigs( customOutboundInput, customInboundInput, uint8(parsedPool.Decimals), remoteCfg.RemoteDecimals, "canton", semver.MustParse("2.0.0"), lockReleasePoolType.String(), ) meta := rateLimiterPoolMeta{InstanceId: parsedPool.InstanceId, PoolOwner: parsedPool.PoolOwner} outboundRef, outboundRaw, err := deployTokenPoolRateLimiter( b, cantonChain, input.ChainSelector, factoryRaw, mcmsEnabled, input.ExistingDataStore, meta, input.TokenPoolAddress, remoteSelectorKeyStr, "outbound", outboundDefaultCfg, common.RateLimitModeRateLimitMode_DefaultFinality, &proposalOutputs, ) if err != nil { return out, fmt.Errorf("deploy outbound rate limiter for remote chain %d: %w", remoteSelector, err) } out.Addresses = append(out.Addresses, outboundRef) inboundRef, inboundRaw, err := deployTokenPoolRateLimiter( b, cantonChain, input.ChainSelector, factoryRaw, mcmsEnabled, input.ExistingDataStore, meta, input.TokenPoolAddress, remoteSelectorKeyStr, "inbound", inboundDefaultCfg, common.RateLimitModeRateLimitMode_DefaultFinality, &proposalOutputs, ) if err != nil { return out, fmt.Errorf("deploy inbound rate limiter for remote chain %d: %w", remoteSelector, err) } out.Addresses = append(out.Addresses, inboundRef) customRef, customInboundRaw, err := deployTokenPoolRateLimiter( b, cantonChain, input.ChainSelector, factoryRaw, mcmsEnabled, input.ExistingDataStore, meta, input.TokenPoolAddress, remoteSelectorKeyStr, "inbound-custom", inboundCustomCfg, common.RateLimitModeRateLimitMode_CustomFinality, &proposalOutputs, ) if err != nil { return out, fmt.Errorf("deploy custom inbound rate limiter for remote chain %d: %w", remoteSelector, err) } out.Addresses = append(out.Addresses, customRef) remoteFamily, err := chain_selectors.GetSelectorFamily(remoteSelector) if err != nil { return out, fmt.Errorf("get remote chain family for %d: %w", remoteSelector, err) } remotePoolAddress := strings.ToLower(hex.EncodeToString(remoteCfg.RemotePool)) remoteTokenAddress := strings.ToLower(hex.EncodeToString(remoteCfg.RemoteToken)) if remoteFamily == chain_selectors.FamilyEVM { remotePoolAddress = strings.TrimPrefix(strings.ToLower(gethcommon.BytesToHash(remoteCfg.RemotePool).Hex()), "0x") remoteTokenAddress = strings.TrimPrefix(strings.ToLower(gethcommon.BytesToAddress(remoteCfg.RemoteToken).Hex()), "0x") } updates = append(updates, tokenPoolChainUpdate{ RemoteChainSelector: remoteSelectorKey, RemotePools: []types.TEXT{types.TEXT(remotePoolAddress)}, RemoteTokenAddress: types.TEXT(remoteTokenAddress), InboundCCVs: inboundCCVs, OutboundCCVs: outboundCCVs, FinalityConfig: toCantonFinalityConfig(input.AllowedFinalityConfig), InboundRateLimiter: inboundRaw, InboundCustomBlockConfirmationsRateLimiter: customInboundRaw, OutboundRateLimiter: outboundRaw, }) } if len(updates) == 0 { if mcmsEnabled && len(proposalOutputs) > 0 { batchOp, err := contract.NewBatchOperationFromExercises(proposalOutputs) if err != nil { return out, fmt.Errorf("build MCMS batch for token pool configuration: %w", err) } if len(batchOp.Transactions) > 0 { batchOps = append(batchOps, batchOp) } } out.BatchOps = batchOps return out, nil } switch logicalPoolType { case lockReleasePoolType: lockReleaseUpdates := make([]lockreleasetokenpool.ChainUpdate, 0, len(updates)) for _, update := range updates { lockReleaseUpdates = append(lockReleaseUpdates, lockreleasetokenpool.ChainUpdate{ RemoteChainSelector: update.RemoteChainSelector, RemotePools: update.RemotePools, RemoteTokenAddress: update.RemoteTokenAddress, InboundCCVs: update.InboundCCVs, OutboundCCVs: update.OutboundCCVs, FinalityConfig: update.FinalityConfig, InboundRateLimiter: update.InboundRateLimiter, InboundCustomBlockConfirmationsRateLimiter: update.InboundCustomBlockConfirmationsRateLimiter, OutboundRateLimiter: update.OutboundRateLimiter, }) } applyReport, err := operations.ExecuteOperation(b, lock_release_token_pool.ApplyChainUpdates, cantonChain, contract.ChoiceInput[lockreleasetokenpool.ApplyChainUpdates]{ InstanceAddress: poolRaw.InstanceAddress(), RawInstanceAddress: poolRaw.String(), MCMSEnabled: mcmsEnabled, Args: lockreleasetokenpool.ApplyChainUpdates{ RemoteChainSelectorsToRemove: []types.NUMERIC{}, ChainsToAdd: lockReleaseUpdates, }, }) if err != nil { if strings.Contains(err.Error(), "ApplyChainUpdates: chain already exists:") { return out, nil } return out, fmt.Errorf("apply remote chain updates to lock/release pool: %w", err) } if mcmsEnabled && !applyReport.Output.Executed() { proposalOutputs = append(proposalOutputs, applyReport.Output) } case burnMintPoolType: burnMintUpdates := make([]burnminttokenpool.ChainUpdate, 0, len(updates)) for _, update := range updates { burnMintUpdates = append(burnMintUpdates, burnminttokenpool.ChainUpdate{ RemoteChainSelector: update.RemoteChainSelector, RemotePools: update.RemotePools, RemoteTokenAddress: update.RemoteTokenAddress, InboundCCVs: update.InboundCCVs, OutboundCCVs: update.OutboundCCVs, FinalityConfig: update.FinalityConfig, InboundRateLimiter: update.InboundRateLimiter, InboundCustomBlockConfirmationsRateLimiter: update.InboundCustomBlockConfirmationsRateLimiter, OutboundRateLimiter: update.OutboundRateLimiter, }) } applyReport, err := operations.ExecuteOperation(b, burn_mint_token_pool.ApplyChainUpdates, cantonChain, contract.ChoiceInput[burnminttokenpool.ApplyChainUpdates]{ InstanceAddress: poolRaw.InstanceAddress(), RawInstanceAddress: poolRaw.String(), MCMSEnabled: mcmsEnabled, Args: burnminttokenpool.ApplyChainUpdates{ RemoteChainSelectorsToRemove: []types.NUMERIC{}, ChainsToAdd: burnMintUpdates, }, }) if err != nil { if strings.Contains(err.Error(), "ApplyChainUpdates: chain already exists:") { return out, nil } return out, fmt.Errorf("apply remote chain updates to burn/mint pool: %w", err) } if mcmsEnabled && !applyReport.Output.Executed() { proposalOutputs = append(proposalOutputs, applyReport.Output) } default: return out, fmt.Errorf("unsupported Canton token pool type %q", logicalPoolType) } if mcmsEnabled && len(proposalOutputs) > 0 { batchOp, err := contract.NewBatchOperationFromExercises(proposalOutputs) if err != nil { return out, fmt.Errorf("build MCMS batch for token pool configuration: %w", err) } if len(batchOp.Transactions) > 0 { batchOps = append(batchOps, batchOp) } } out.BatchOps = batchOps return out, nil }, )
var DeployCCIPChainContractsFromFactory = operations.NewSequence( "canton/ccip/deploy_ccip_chain_contracts_from_factory", semver.MustParse("2.0.0"), "Deploys core CCIP contracts on Canton through CCIPFactory (excludes RMNRemote and CommitteeVerifier)", func(b operations.Bundle, deps canton.Chain, input DeployChainContractsParams) (sequences.OnChainOutput, error) { if input.RmnRemoteRawInstanceAddress == "" { return sequences.OnChainOutput{}, fmt.Errorf("RmnRemoteRawInstanceAddress is required for core CCIP factory deploy") } coreInput := input coreInput.CommitteeVerifiers = nil report, err := operations.ExecuteSequence(b, DeployChainContractsFromFactory, deps, coreInput) if err != nil { return sequences.OnChainOutput{}, err } return report.Output, nil }, )
DeployCCIPChainContractsFromFactory deploys CCIP contracts (no RMNRemote, no CommitteeVerifiers) via the ccip-qualified factory.
var DeployCCVFromFactory = operations.NewSequence( "canton/ccip/deploy_ccv_from_factory", semver.MustParse("0.1.0"), "Deploys CommitteeVerifier contracts on Canton through CCIPFactory", func(b operations.Bundle, deps canton.Chain, input DeployChainContractsParams) (sequences.OnChainOutput, error) { if len(input.CommitteeVerifiers) == 0 { return sequences.OnChainOutput{}, fmt.Errorf("at least one committee verifier is required") } if input.RmnRemoteRawInstanceAddress == "" { return sequences.OnChainOutput{}, fmt.Errorf("RmnRemoteRawInstanceAddress is required for CCV factory deploy") } _, ccipOwnerParty, err := requireOwnerParties(input) if err != nil { return sequences.OnChainOutput{}, err } ccvOwnerParty, err := requireCCVOwnerParty(input) if err != nil { return sequences.OnChainOutput{}, err } factoryRawInstanceAddress, err := rawInstanceAddressFromAddressRef(input.FactoryAddressRef) if err != nil { return sequences.OnChainOutput{}, err } var addresses []datastore.AddressRef var proposalOutputs []contract.ExerciseOutput for i, committeeVerifierParams := range input.CommitteeVerifiers { qualifier := committeeVerifierParams.Qualifier committeeVerifierOwner := ccvOwnerParty committeeVerifierInstanceID, err := ensureInstanceID(committeeVerifierParams.Template.InstanceId, "committeeverifier") if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to ensure CommitteeVerifier #%d instance ID: %w", i, err) } committeeVerifierTemplate := ccvsbindings.CommitteeVerifier{ InstanceId: types.TEXT(committeeVerifierInstanceID), Owner: committeeVerifierOwner, CcipOwner: ccipOwnerParty, VersionTag: committeeVerifierParams.Template.VersionTag, AllowListAdmin: committeeVerifierParams.Template.AllowListAdmin, MessageSentObservers: committeeVerifierParams.Template.MessageSentObservers, StorageLocations: committeeVerifierParams.Template.StorageLocations, StorageLocationsAdmin: committeeVerifierParams.Template.StorageLocationsAdmin, PendingStorageLocationsAdmin: committeeVerifierParams.Template.PendingStorageLocationsAdmin, RemoteChainConfigs: committeeVerifierParams.Template.RemoteChainConfigs, SignerConfigs: committeeVerifierParams.Template.SignerConfigs, Deps: ccvsbindings.CommitteeVerifierDeps{ RmnRemote: input.RmnRemoteRawInstanceAddress.Binding(), }, } deployReport, err := operations.ExecuteOperation(b, factoryops.DeployCommitteeVerifier, deps, newChoiceInput(factoryRawInstanceAddress, factorybindings.DeployCommitteeVerifier{Contract: committeeVerifierTemplate}, input.ProposalDriven)) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to deploy CommitteeVerifier #%d from factory: %w", i, err) } proposalOutputs = appendExerciseOutput(proposalOutputs, deployReport.Output, input.ProposalDriven) committeeVerifierRawInstanceAddress := committeeVerifierInstanceID.RawInstanceAddress(committeeVerifierOwner) addresses = append(addresses, newAddressRef(deps.ChainSelector(), committeeVerifierRawInstanceAddress, committee_verifier.ContractType, committee_verifier.Version, qualifier)) } out := sequences.OnChainOutput{Addresses: addresses} if input.ProposalDriven { batchOp, err := contract.NewBatchOperationFromExercises(proposalOutputs) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to build CCV proposal batch: %w", err) } if len(batchOp.Transactions) > 0 { out.BatchOps = []mcms_types.BatchOperation{batchOp} } } return out, nil }, )
DeployCCVFromFactory deploys CommitteeVerifier contracts via a CCV-qualified CCIPFactory.
var DeployChainContracts = operations.NewSequence( "canton/ccip/deploy_chain_contracts", semver.MustParse("2.0.0"), "Deploys all required contracts for CCIP 2.0.0 to a Canton chain", func(b operations.Bundle, deps canton.Chain, input DeployChainContractsParams) (sequences.OnChainOutput, error) { var addresses []datastore.AddressRef rmnOwnerParty, err := requireRMNOwnerParty(input) if err != nil { return sequences.OnChainOutput{}, err } deployRMNRemoteReport, err := operations.ExecuteOperation(b, rmn_remote.Deploy, deps, contract.DeployInput[rmn.RMNRemote]{ Template: rmn.RMNRemote{ RmnOwner: rmnOwnerParty, CcipOwner: types.PARTY(input.CCIPOwnerParty), CursedSubjects: input.RMNRemote.Template.CursedSubjects, CustomObservers: input.RMNRemote.Template.CustomObservers, }, OwnerParty: rmnOwnerParty, }) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to deploy RMNRemote: %w", err) } addresses = append(addresses, deployRMNRemoteReport.Output) rmnRemoteRawInstanceAddress, err := contracts.RawInstanceAddressFromString(deployRMNRemoteReport.Output.Labels.List()[0]) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to parse RMNRemote raw instance address: %w", err) } input.GlobalConfig.Template.CcipOwner = types.PARTY(input.CCIPOwnerParty) deployGlobalConfigReport, err := operations.ExecuteOperation(b, global_config.Deploy, deps, contract.DeployInput[common.GlobalConfig]{ Template: input.GlobalConfig.Template, OwnerParty: types.PARTY(input.CCIPOwnerParty), }) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to deploy GlobalConfig: %w", err) } addresses = append(addresses, deployGlobalConfigReport.Output) globalConfigRawInstanceAddress, err := contracts.RawInstanceAddressFromString(deployGlobalConfigReport.Output.Labels.List()[0]) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to parse GlobalConfig raw instance address: %w", err) } deployTokenAdminRegistryReport, err := operations.ExecuteOperation(b, token_admin_registry.Deploy, deps, contract.DeployInput[tokenadminregistry.TokenAdminRegistry]{ Template: tokenadminregistry.TokenAdminRegistry{ Owner: types.PARTY(input.CCIPOwnerParty), EntryCount: 0, }, OwnerParty: types.PARTY(input.CCIPOwnerParty), }) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to deploy TokenAdminRegistry: %w", err) } addresses = append(addresses, deployTokenAdminRegistryReport.Output) tokenAdminRegistryRawInstanceAddress, err := contracts.RawInstanceAddressFromString(deployTokenAdminRegistryReport.Output.Labels.List()[0]) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to parse TokenAdminRegistry raw instance address: %w", err) } linkTokenId := input.FeeQuoterConfig.Template.LinkTokenInstrumentId if linkTokenId.Admin == "" { linkTokenId = splice_api_token_holding_v1.InstrumentId{ Admin: types.PARTY(input.CCIPOwnerParty), Id: types.TEXT("link-token"), } } deployFeeQuoterReport, err := operations.ExecuteOperation(b, fee_quoter.Deploy, deps, contract.DeployInput[feequoter.FeeQuoter]{ Template: feequoter.FeeQuoter{ Owner: types.PARTY(input.CCIPOwnerParty), FeeTokens: types.SET{}, DestChainConfigs: nil, TokenTransferFeeConfigs: nil, UsdPerUnitGasByDestChainSelector: nil, UsdPerToken: nil, LinkTokenInstrumentId: linkTokenId, PriceUpdaters: input.FeeQuoterConfig.Template.PriceUpdaters, }, OwnerParty: types.PARTY(input.CCIPOwnerParty), }) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to deploy FeeQuoter: %w", err) } addresses = append(addresses, deployFeeQuoterReport.Output) feeQuoterRawInstanceAddress, err := contracts.RawInstanceAddressFromString(deployFeeQuoterReport.Output.Labels.List()[0]) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to parse FeeQuoter raw instance address: %w", err) } if input.FeeQuoterConfig.USDPerNative != nil { _, err = operations.ExecuteOperation(b, fee_quoter.UpdatePrices, deps, contract.ChoiceInput[feequoter.UpdatePrices]{ InstanceAddress: feeQuoterRawInstanceAddress.InstanceAddress(), Args: feequoter.UpdatePrices{ PriceUpdates: feequoter.PriceUpdates{ TokenPriceUpdates: []feequoter.TokenPriceUpdate{ { InstrumentId: input.NativeInstrumentId, UsdPerToken: types.NUMERIC(input.FeeQuoterConfig.USDPerNative.String()), }, }, }, }, }) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to update native token price on FeeQuoter: %w", err) } err = ensureNativeFeeTokenConfig( b, deps, tokenAdminRegistryRawInstanceAddress.InstanceAddress(), input.CCIPOwnerParty, input.NativeInstrumentId, ) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to ensure native fee token config: %w", err) } } deployOffRampReport, err := operations.ExecuteOperation(b, offramp.Deploy, deps, contract.DeployInput[offrampBinding.OffRamp]{ Template: offrampBinding.OffRamp{ CcipOwner: types.PARTY(input.CCIPOwnerParty), Deps: offrampBinding.OffRampDeps{ GlobalConfig: globalConfigRawInstanceAddress.Binding(), RmnRemote: rmnRemoteRawInstanceAddress.Binding(), TokenAdminRegistry: tokenAdminRegistryRawInstanceAddress.Binding(), }, }, OwnerParty: types.PARTY(input.CCIPOwnerParty), }) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to deploy OffRamp: %w", err) } addresses = append(addresses, deployOffRampReport.Output) offRampRawInstanceAddress, err := contracts.RawInstanceAddressFromString(deployOffRampReport.Output.Labels.List()[0]) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to parse OffRamp raw instance address: %w", err) } deployOnRampReport, err := operations.ExecuteOperation(b, onramp.Deploy, deps, contract.DeployInput[onrampBinding.OnRamp]{ Template: onrampBinding.OnRamp{ CcipOwner: types.PARTY(input.CCIPOwnerParty), MaxUSDCentsPerMsg: types.NUMERIC("100000000"), Deps: onrampBinding.OnRampDeps{ GlobalConfig: globalConfigRawInstanceAddress.Binding(), RmnRemote: rmnRemoteRawInstanceAddress.Binding(), TokenAdminRegistry: tokenAdminRegistryRawInstanceAddress.Binding(), FeeQuoter: feeQuoterRawInstanceAddress.Binding(), CcvRegistry: mcms.RawInstanceAddress{}, }, }, OwnerParty: types.PARTY(input.CCIPOwnerParty), }) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to deploy OnRamp: %w", err) } addresses = append(addresses, deployOnRampReport.Output) onRampRawInstanceAddress, err := contracts.RawInstanceAddressFromString(deployOnRampReport.Output.Labels.List()[0]) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to parse OnRamp raw instance address: %w", err) } deployPerPartyRouterFactoryReport, err := operations.ExecuteOperation(b, per_party_router_factory.Deploy, deps, contract.DeployInput[perpartyrouter.PerPartyRouterFactory]{ Template: perpartyrouter.PerPartyRouterFactory{ CcipOwner: types.PARTY(input.CCIPOwnerParty), Deps: perpartyrouter.PerPartyRouterDeps{ OnRamp: onRampRawInstanceAddress.Binding(), OffRamp: offRampRawInstanceAddress.Binding(), GlobalConfig: globalConfigRawInstanceAddress.Binding(), TokenAdminRegistry: tokenAdminRegistryRawInstanceAddress.Binding(), FeeQuoter: feeQuoterRawInstanceAddress.Binding(), RmnRemote: rmnRemoteRawInstanceAddress.Binding(), }, RegisteredRouters: nil, }, OwnerParty: types.PARTY(input.CCIPOwnerParty), }) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to deploy PerPartyRouterFactory: %w", err) } addresses = append(addresses, deployPerPartyRouterFactoryReport.Output) for i, committeeVerifier := range input.CommitteeVerifiers { committeeVerifier.Template.CcipOwner = types.PARTY(input.CCIPOwnerParty) deployCommitteeVerifierReport, err := operations.ExecuteOperation(b, committee_verifier.Deploy, deps, contract.DeployInput[ccvs.CommitteeVerifier]{ Template: ccvs.CommitteeVerifier{ Owner: committeeVerifier.Template.Owner, CcipOwner: types.PARTY(input.CCIPOwnerParty), VersionTag: committeeVerifier.Template.VersionTag, MessageSentObservers: committeeVerifier.Template.MessageSentObservers, StorageLocations: committeeVerifier.Template.StorageLocations, StorageLocationsAdmin: committeeVerifier.Template.StorageLocationsAdmin, PendingStorageLocationsAdmin: committeeVerifier.Template.PendingStorageLocationsAdmin, RemoteChainConfigs: committeeVerifier.Template.RemoteChainConfigs, SignerConfigs: committeeVerifier.Template.SignerConfigs, Deps: ccvs.CommitteeVerifierDeps{ RmnRemote: rmnRemoteRawInstanceAddress.Binding()}, }, OwnerParty: committeeVerifier.Template.Owner, Qualifier: &committeeVerifier.Qualifier, }) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to deploy CommitteeVerifier #%v: %w", i, err) } addresses = append(addresses, deployCommitteeVerifierReport.Output) } for i, params := range input.Executors { deployExecutorReport, err := operations.ExecuteOperation(b, executor.Deploy, deps, contract.DeployInput[executorBinding.Executor]{ Template: params.Template, OwnerParty: params.Template.Owner, Qualifier: ¶ms.Qualifier, }) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to deploy Executor #%v: %w", i, err) } addresses = append(addresses, deployExecutorReport.Output) } return sequences.OnChainOutput{ Addresses: addresses, }, nil }, )
var DeployChainContractsFromFactory = operations.NewSequence( "canton/ccip/deploy_chain_contracts_from_factory", semver.MustParse("2.0.0"), "Deploys CCIP contracts on Canton through CCIPFactory choices", func(b operations.Bundle, deps canton.Chain, input DeployChainContractsParams) (sequences.OnChainOutput, error) { var addresses []datastore.AddressRef var proposalOutputs []contract.ExerciseOutput ownerParty, ccipOwnerParty, err := requireOwnerParties(input) if err != nil { return sequences.OnChainOutput{}, err } factoryRawInstanceAddress, err := rawInstanceAddressFromAddressRef(input.FactoryAddressRef) if err != nil { return sequences.OnChainOutput{}, err } rmnRemoteRawInstanceAddress, rmnAddressRef, rmnProposalOutputs, err := resolveOrDeployRMNRemote( b, deps, input, ) if err != nil { return sequences.OnChainOutput{}, err } if rmnAddressRef != nil { addresses = append(addresses, *rmnAddressRef) } proposalOutputs = append(proposalOutputs, rmnProposalOutputs...) globalConfigInstanceID, err := ensureInstanceID(input.GlobalConfig.Template.InstanceId, "globalconfig") if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to ensure GlobalConfig instance ID: %w", err) } globalConfigTemplate := common.GlobalConfig{ InstanceId: types.TEXT(globalConfigInstanceID), CcipOwner: ccipOwnerParty, ChainSelector: input.GlobalConfig.Template.ChainSelector, DestChainConfigs: nil, SourceChainConfigs: nil, } deployGlobalConfigReport, err := operations.ExecuteOperation(b, factoryops.DeployGlobalConfig, deps, newChoiceInput(factoryRawInstanceAddress, factorybindings.DeployGlobalConfig{Contract: globalConfigTemplate}, input.ProposalDriven)) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to deploy GlobalConfig from factory: %w", err) } proposalOutputs = appendExerciseOutput(proposalOutputs, deployGlobalConfigReport.Output, input.ProposalDriven) globalConfigRawInstanceAddress := globalConfigInstanceID.RawInstanceAddress(ownerParty) addresses = append(addresses, newAddressRef(deps.ChainSelector(), globalConfigRawInstanceAddress, global_config.ContractType, global_config.Version, "")) tokenAdminRegistryInstanceID, err := ensureInstanceID("", "tokenadminregistry") if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to ensure TokenAdminRegistry instance ID: %w", err) } tokenAdminRegistryTemplate := tokenadminregistry.TokenAdminRegistry{ InstanceId: types.TEXT(tokenAdminRegistryInstanceID), Owner: ccipOwnerParty, EntryCount: 0, } deployTokenAdminRegistryReport, err := operations.ExecuteOperation(b, factoryops.DeployTokenAdminRegistry, deps, newChoiceInput(factoryRawInstanceAddress, factorybindings.DeployTokenAdminRegistry{Contract: tokenAdminRegistryTemplate}, input.ProposalDriven)) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to deploy TokenAdminRegistry from factory: %w", err) } proposalOutputs = appendExerciseOutput(proposalOutputs, deployTokenAdminRegistryReport.Output, input.ProposalDriven) tokenAdminRegistryRawInstanceAddress := tokenAdminRegistryInstanceID.RawInstanceAddress(ownerParty) addresses = append(addresses, newAddressRef(deps.ChainSelector(), tokenAdminRegistryRawInstanceAddress, token_admin_registry.ContractType, token_admin_registry.Version, "")) feeQuoterInstanceID, err := ensureInstanceID(input.FeeQuoterConfig.Template.InstanceId, "feequoter") if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to ensure FeeQuoter instance ID: %w", err) } linkTokenID := input.FeeQuoterConfig.Template.LinkTokenInstrumentId if linkTokenID.Admin == "" { linkTokenID = splice_api_token_holding_v1.InstrumentId{ Admin: ownerParty, Id: types.TEXT("link-token"), } } feeQuoterTemplate := feequoter.FeeQuoter{ InstanceId: types.TEXT(feeQuoterInstanceID), Owner: ownerParty, FeeTokens: types.SET{}, DestChainConfigs: nil, TokenTransferFeeConfigs: nil, UsdPerUnitGasByDestChainSelector: nil, UsdPerToken: nil, LinkTokenInstrumentId: linkTokenID, PriceUpdaters: input.FeeQuoterConfig.Template.PriceUpdaters, } deployFeeQuoterReport, err := operations.ExecuteOperation(b, factoryops.DeployFeeQuoter, deps, newChoiceInput(factoryRawInstanceAddress, factorybindings.DeployFeeQuoter{Contract: feeQuoterTemplate}, input.ProposalDriven)) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to deploy FeeQuoter from factory: %w", err) } proposalOutputs = appendExerciseOutput(proposalOutputs, deployFeeQuoterReport.Output, input.ProposalDriven) feeQuoterRawInstanceAddress := feeQuoterInstanceID.RawInstanceAddress(ownerParty) addresses = append(addresses, newAddressRef(deps.ChainSelector(), feeQuoterRawInstanceAddress, fee_quoter.ContractType, fee_quoter.Version, "")) var firstCommitteeVerifierBinding mcms.RawInstanceAddress if len(input.CommitteeVerifiers) == 0 { if input.CcvRegistryBinding.Unpack == "" { return sequences.OnChainOutput{}, fmt.Errorf("CcvRegistryBinding is required when CommitteeVerifiers is empty") } firstCommitteeVerifierBinding = input.CcvRegistryBinding } var ccvOwnerParty types.PARTY if len(input.CommitteeVerifiers) > 0 { ccvOwnerParty, err = requireCCVOwnerParty(input) if err != nil { return sequences.OnChainOutput{}, err } } for i, committeeVerifierParams := range input.CommitteeVerifiers { qualifier := committeeVerifierParams.Qualifier committeeVerifierOwner := ccvOwnerParty committeeVerifierInstanceID, err := ensureInstanceID(committeeVerifierParams.Template.InstanceId, "committeeverifier") if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to ensure CommitteeVerifier #%d instance ID: %w", i, err) } committeeVerifierTemplate := ccvsbindings.CommitteeVerifier{ InstanceId: types.TEXT(committeeVerifierInstanceID), Owner: committeeVerifierOwner, CcipOwner: ccipOwnerParty, VersionTag: committeeVerifierParams.Template.VersionTag, AllowListAdmin: committeeVerifierParams.Template.AllowListAdmin, MessageSentObservers: committeeVerifierParams.Template.MessageSentObservers, StorageLocations: committeeVerifierParams.Template.StorageLocations, StorageLocationsAdmin: committeeVerifierParams.Template.StorageLocationsAdmin, PendingStorageLocationsAdmin: committeeVerifierParams.Template.PendingStorageLocationsAdmin, RemoteChainConfigs: committeeVerifierParams.Template.RemoteChainConfigs, SignerConfigs: committeeVerifierParams.Template.SignerConfigs, Deps: ccvsbindings.CommitteeVerifierDeps{ RmnRemote: rmnRemoteRawInstanceAddress.Binding(), }, } deployCommitteeVerifierReport, err := operations.ExecuteOperation(b, factoryops.DeployCommitteeVerifier, deps, newChoiceInput(factoryRawInstanceAddress, factorybindings.DeployCommitteeVerifier{Contract: committeeVerifierTemplate}, input.ProposalDriven)) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to deploy CommitteeVerifier #%d from factory: %w", i, err) } proposalOutputs = appendExerciseOutput(proposalOutputs, deployCommitteeVerifierReport.Output, input.ProposalDriven) committeeVerifierRawInstanceAddress := committeeVerifierInstanceID.RawInstanceAddress(committeeVerifierOwner) if i == 0 { firstCommitteeVerifierBinding = committeeVerifierRawInstanceAddress.Binding() } addresses = append(addresses, newAddressRef(deps.ChainSelector(), committeeVerifierRawInstanceAddress, committee_verifier.ContractType, committee_verifier.Version, qualifier)) } offRampInstanceID, err := ensureInstanceID("", "offramp") if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to ensure OffRamp instance ID: %w", err) } offRampTemplate := offrampBinding.OffRamp{ InstanceId: types.TEXT(offRampInstanceID), CcipOwner: ccipOwnerParty, Deps: offrampBinding.OffRampDeps{ GlobalConfig: globalConfigRawInstanceAddress.Binding(), RmnRemote: rmnRemoteRawInstanceAddress.Binding(), TokenAdminRegistry: tokenAdminRegistryRawInstanceAddress.Binding(), }, } deployOffRampReport, err := operations.ExecuteOperation(b, factoryops.DeployOffRamp, deps, newChoiceInput(factoryRawInstanceAddress, factorybindings.DeployOffRamp{Contract: offRampTemplate}, input.ProposalDriven)) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to deploy OffRamp from factory: %w", err) } proposalOutputs = appendExerciseOutput(proposalOutputs, deployOffRampReport.Output, input.ProposalDriven) offRampRawInstanceAddress := offRampInstanceID.RawInstanceAddress(ownerParty) addresses = append(addresses, newAddressRef(deps.ChainSelector(), offRampRawInstanceAddress, offramp.ContractType, offramp.Version, "")) onRampInstanceID, err := ensureInstanceID("", "onramp") if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to ensure OnRamp instance ID: %w", err) } onRampTemplate := onrampBinding.OnRamp{ InstanceId: types.TEXT(onRampInstanceID), CcipOwner: ccipOwnerParty, MaxUSDCentsPerMsg: types.NUMERIC("100000000"), Deps: onrampBinding.OnRampDeps{ GlobalConfig: globalConfigRawInstanceAddress.Binding(), RmnRemote: rmnRemoteRawInstanceAddress.Binding(), TokenAdminRegistry: tokenAdminRegistryRawInstanceAddress.Binding(), FeeQuoter: feeQuoterRawInstanceAddress.Binding(), CcvRegistry: firstCommitteeVerifierBinding, }, } deployOnRampReport, err := operations.ExecuteOperation(b, factoryops.DeployOnRamp, deps, newChoiceInput(factoryRawInstanceAddress, factorybindings.DeployOnRamp{Contract: onRampTemplate}, input.ProposalDriven)) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to deploy OnRamp from factory: %w", err) } proposalOutputs = appendExerciseOutput(proposalOutputs, deployOnRampReport.Output, input.ProposalDriven) onRampRawInstanceAddress := onRampInstanceID.RawInstanceAddress(ownerParty) addresses = append(addresses, newAddressRef(deps.ChainSelector(), onRampRawInstanceAddress, onramp.ContractType, onramp.Version, "")) perPartyRouterFactoryInstanceID, err := ensureInstanceID("", "perpartyrouterfactory") if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to ensure PerPartyRouterFactory instance ID: %w", err) } perPartyRouterFactoryTemplate := perpartyrouter.PerPartyRouterFactory{ InstanceId: types.TEXT(perPartyRouterFactoryInstanceID), CcipOwner: ccipOwnerParty, Deps: perpartyrouter.PerPartyRouterDeps{ OnRamp: onRampRawInstanceAddress.Binding(), OffRamp: offRampRawInstanceAddress.Binding(), GlobalConfig: globalConfigRawInstanceAddress.Binding(), TokenAdminRegistry: tokenAdminRegistryRawInstanceAddress.Binding(), FeeQuoter: feeQuoterRawInstanceAddress.Binding(), RmnRemote: rmnRemoteRawInstanceAddress.Binding(), }, RegisteredRouters: nil, } deployPerPartyRouterFactoryReport, err := operations.ExecuteOperation(b, factoryops.DeployPerPartyRouterFactory, deps, newChoiceInput(factoryRawInstanceAddress, factorybindings.DeployPerPartyRouterFactory{Contract: perPartyRouterFactoryTemplate}, input.ProposalDriven)) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to deploy PerPartyRouterFactory from factory: %w", err) } proposalOutputs = appendExerciseOutput(proposalOutputs, deployPerPartyRouterFactoryReport.Output, input.ProposalDriven) perPartyRouterFactoryRawInstanceAddress := perPartyRouterFactoryInstanceID.RawInstanceAddress(ownerParty) addresses = append(addresses, newAddressRef(deps.ChainSelector(), perPartyRouterFactoryRawInstanceAddress, per_party_router_factory.ContractType, per_party_router_factory.Version, "")) for i, params := range input.Executors { executorInstanceID, err := ensureInstanceID(params.Template.InstanceId, "executor") if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to ensure Executor #%d instance ID: %w", i, err) } executorTemplate := params.Template executorTemplate.InstanceId = types.TEXT(executorInstanceID) deployExecutorReport, err := operations.ExecuteOperation(b, factoryops.DeployExecutor, deps, newChoiceInput(factoryRawInstanceAddress, factorybindings.DeployExecutor{ Contract: executorTemplate, }, input.ProposalDriven)) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to deploy Executor #%d from factory: %w", i, err) } proposalOutputs = appendExerciseOutput(proposalOutputs, deployExecutorReport.Output, input.ProposalDriven) executorRawInstanceAddress := executorInstanceID.RawInstanceAddress(params.Template.Owner) addresses = append(addresses, newAddressRef(deps.ChainSelector(), executorRawInstanceAddress, executor.ContractType, executor.Version, params.Qualifier)) } out := sequences.OnChainOutput{Addresses: addresses} if input.ProposalDriven { batchOp, err := contract.NewBatchOperationFromExercises(proposalOutputs) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to build proposal batch for factory deploys: %w", err) } if len(batchOp.Transactions) > 0 { out.BatchOps = []mcms_types.BatchOperation{batchOp} } } return out, nil }, )
DeployChainContractsFromFactory is the factory-backed Canton deploy path. It assumes CCIPFactory has already been bootstrapped and targets that existing factory for all core CCIP contract deployments.
var DeployRMNFromFactory = operations.NewSequence( "canton/ccip/deploy_rmn_from_factory", semver.MustParse("0.1.0"), "Deploys RMNRemote on Canton through the ccip CCIPFactory", func(b operations.Bundle, deps canton.Chain, input DeployChainContractsParams) (sequences.OnChainOutput, error) { ccipOwnerParty, err := requireCCIPOwnerParty(input) if err != nil { return sequences.OnChainOutput{}, err } factoryRawInstanceAddress, err := rawInstanceAddressFromAddressRef(input.FactoryAddressRef) if err != nil { return sequences.OnChainOutput{}, err } _, rmnAddressRef, proposalOutputs, err := deployRMNRemoteFromFactory( b, deps, input, factoryRawInstanceAddress, ccipOwnerParty, ) if err != nil { return sequences.OnChainOutput{}, err } out := sequences.OnChainOutput{Addresses: []datastore.AddressRef{*rmnAddressRef}} if input.ProposalDriven { batchOp, err := contract.NewBatchOperationFromExercises(proposalOutputs) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to build RMN proposal batch: %w", err) } if len(batchOp.Transactions) > 0 { out.BatchOps = []mcms_types.BatchOperation{batchOp} } } return out, nil }, )
DeployRMNFromFactory deploys RMNRemote via the rmn-qualified CCIPFactory.
var DeployTokenPoolForToken = operations.NewSequence( "canton/token-adapter/deploy-token-pool-for-token", semver.MustParse("2.0.0"), "Deploys a Canton token pool and returns both the canonical and logical datastore refs", func(b operations.Bundle, chains chain.BlockChains, input tokenadapters.DeployTokenPoolInput) (ccipsequences.OnChainOutput, error) { if input.TokenPoolVersion == nil { return ccipsequences.OnChainOutput{}, fmt.Errorf("TokenPoolVersion is required") } if input.ExistingDataStore == nil { return ccipsequences.OnChainOutput{}, fmt.Errorf("existing datastore is required") } logicalPoolType, err := resolveCantonTokenPoolType(input.PoolType) if err != nil { return ccipsequences.OnChainOutput{}, err } qualifier := strings.TrimSpace(input.TokenPoolQualifier) if qualifier == "" { return ccipsequences.OnChainOutput{}, fmt.Errorf("token pool qualifier is required") } if input.TokenRef == nil { return ccipsequences.OnChainOutput{}, fmt.Errorf("tokenRef is required and must include instrument labels") } instrumentID, _, err := parseInstrumentIDFromTokenRefLabels(*input.TokenRef) if err != nil { return ccipsequences.OnChainOutput{}, err } matches := input.ExistingDataStore.Addresses().Filter( datastore.AddressRefByType(logicalPoolType), datastore.AddressRefByChainSelector(input.ChainSelector), datastore.AddressRefByQualifier(qualifier), datastore.AddressRefByVersion(input.TokenPoolVersion), ) if len(matches) > 1 { return ccipsequences.OnChainOutput{}, fmt.Errorf("multiple Canton token pools found with qualifier %q", qualifier) } if len(matches) == 1 { b.Logger.Infof("Canton token pool already deployed at %s", matches[0].Address) return ccipsequences.OnChainOutput{}, nil } cantonChain, ok := chains.CantonChains()[input.ChainSelector] if !ok || len(cantonChain.Participants) == 0 { return ccipsequences.OnChainOutput{}, fmt.Errorf("canton chain with selector %d not found", input.ChainSelector) } participant := cantonChain.Participants[0] mcmsEnabled := len(participant.ReadAsPartyIDs) > 0 var batchOps []mcms_types.BatchOperation var proposalOutputs []contract.ExerciseOutput if logicalPoolType == burnMintPoolType { return ccipsequences.OnChainOutput{}, fmt.Errorf( "burn/mint token pools must be deployed through CCIPFactory; factory does not yet expose DeployBurnMintTokenPool", ) } factoryRefs := input.ExistingDataStore.Addresses().Filter( datastore.AddressRefByChainSelector(input.ChainSelector), datastore.AddressRefByType(datastore.ContractType(factoryops.ContractType)), ) factoryRef, err := dsutils.FactoryAddressRefFromRefs(input.ChainSelector, dsutils.QualifierCCIP, factoryRefs) if err != nil { return ccipsequences.OnChainOutput{}, err } factoryRaw, err := rawInstanceAddressFromAddressRef(factoryRef) if err != nil { return ccipsequences.OnChainOutput{}, fmt.Errorf("resolve CCIPFactory raw address: %w", err) } resolveRefAndRaw := func(name string, contractType datastore.ContractType, version *semver.Version) (datastore.AddressRef, contracts.RawInstanceAddress, error) { ref, err := input.ExistingDataStore.Addresses().Get(datastore.NewAddressRefKey( input.ChainSelector, contractType, version, "", )) if err != nil { return datastore.AddressRef{}, "", fmt.Errorf("resolve %s: %w", name, err) } raw, err := dsutils.GetRawInstanceAddressFromAddressRef(ref) if err != nil { return datastore.AddressRef{}, "", fmt.Errorf("resolve %s raw address: %w", name, err) } return ref, raw, nil } tokenAdminRegistryRef, tokenAdminRegistryRaw, err := resolveRefAndRaw("token admin registry", datastore.ContractType(token_admin_registry.ContractType), token_admin_registry.Version) if err != nil { return ccipsequences.OnChainOutput{}, err } _, rmnRemoteRaw, err := resolveRefAndRaw("rmn remote", datastore.ContractType(rmn_remote.ContractType), rmn_remote.Version) if err != nil { return ccipsequences.OnChainOutput{}, err } _, feeQuoterRaw, err := resolveRefAndRaw("fee quoter", datastore.ContractType(fee_quoter.ContractType), fee_quoter.Version) if err != nil { return ccipsequences.OnChainOutput{}, err } ccipOwnerParty, poolOwnerParty, err := resolveTokenPoolParties(input, participant) if err != nil { return ccipsequences.OnChainOutput{}, err } ccipOwner := types.PARTY(ccipOwnerParty) poolOwner := types.PARTY(poolOwnerParty) poolInstanceID := contracts.InstanceID(fmt.Sprintf("lockreleasetokenpool-%s", qualifier)) deployReport, err := operations.ExecuteOperation(b, factoryops.DeployLockReleaseTokenPool, cantonChain, newChoiceInput(factoryRaw, factorybindings.DeployLockReleaseTokenPool{ Contract: lockreleasetokenpool.LockReleaseTokenPool{ InstanceId: types.TEXT(poolInstanceID), CcipOwner: ccipOwner, PoolOwner: poolOwner, InstrumentId: instrumentID, Decimals: types.INT64(10), RemoteChainConfigs: map[types.NUMERIC]lockreleasetokenpool.RemoteChainConfig{}, TokenTransferFeeConfigs: map[types.NUMERIC]lockreleasetokenpool.TokenTransferFeeConfig2{}, PoolReceiveContext: splice_api_token_metadata_v1.ChoiceContext{ Values: map[string]splice_api_token_metadata_v1.AnyValue{}, }, TransferTimeout: lockreleasetokenpool.TransferTimeout{ RelativeHours: new(types.INT64(24)), }, Deps: lockreleasetokenpool.LockReleaseTokenPoolDeps{ TokenAdminRegistry: tokenAdminRegistryRaw.Binding(), RmnRemote: rmnRemoteRaw.Binding(), FeeQuoter: feeQuoterRaw.Binding(), }, }, }, mcmsEnabled)) if err != nil { return ccipsequences.OnChainOutput{}, fmt.Errorf("deploy Canton lock/release pool from factory: %w", err) } if mcmsEnabled && !deployReport.Output.Executed() { proposalOutputs = append(proposalOutputs, deployReport.Output) } rawPoolAddr := poolInstanceID.RawInstanceAddress(poolOwner) regOut, err := operations.ExecuteSequence(b, RegisterTokenPool, cantonChain, RegisterTokenPoolInput{ TokenAdminRegistryInstanceAddress: contracts.HexToInstanceAddress(tokenAdminRegistryRef.Address), TokenAdminRegistryRawInstanceAddress: tokenAdminRegistryRaw, InstrumentId: instrumentID, PoolInstanceID: rawPoolAddr.InstanceID(), CcipParty: participant.PartyID, PoolOwnerParty: participant.PartyID, }) if err != nil { return ccipsequences.OnChainOutput{}, fmt.Errorf("register Canton token pool: %w", err) } batchOps = append(batchOps, regOut.Output.BatchOps...) canonicalRef := newAddressRef(input.ChainSelector, rawPoolAddr, lock_release_token_pool.ContractType, lock_release_token_pool.Version, qualifier) logicalRef := datastore.AddressRef{ Address: canonicalRef.Address, Labels: canonicalRef.Labels, Type: logicalPoolType, Version: input.TokenPoolVersion, Qualifier: qualifier, ChainSelector: input.ChainSelector, } tokenAddress := strings.TrimSpace(input.TokenRef.Address) if tokenAddress == "" { return ccipsequences.OnChainOutput{}, fmt.Errorf("tokenRef.address is required") } tokenRef := datastore.AddressRef{ Address: tokenAddress, Type: datastore.ContractType("Token"), Version: input.TokenPoolVersion, Qualifier: qualifier, ChainSelector: input.ChainSelector, } if mcmsEnabled && len(proposalOutputs) > 0 { batchOp, err := contract.NewBatchOperationFromExercises(proposalOutputs) if err != nil { return ccipsequences.OnChainOutput{}, fmt.Errorf("build MCMS batch for token pool deployment: %w", err) } if len(batchOp.Transactions) > 0 { batchOps = append(batchOps, batchOp) } } return ccipsequences.OnChainOutput{ Addresses: []datastore.AddressRef{ logicalRef, canonicalRef, tokenRef, }, BatchOps: batchOps, }, nil }, )
var RegisterTokenPool = operations.NewSequence( "canton/ccip/register_token_pool", semver.MustParse("1.0.0"), "Registers a token pool with the TokenAdminRegistry (ProposeAdministrator, AcceptAdminRole, SetPool)", registerTokenPool, )
var SetTokenPoolRateLimits = operations.NewSequence( "canton/token-adapter/set-token-pool-rate-limits", semver.MustParse("2.0.0"), "Updates Canton lock/release pool rate limiter config for a remote chain", func(b operations.Bundle, chains chain.BlockChains, input tokenadapters.TPRLRemotes) (ccipsequences.OnChainOutput, error) { _ = b _ = chains _ = input return ccipsequences.OnChainOutput{}, fmt.Errorf( "SetTokenPoolRateLimits is disabled: initial Canton rate limiter setup happens during ConfigureTokenForTransfers", ) }, )
Canton wires rate limiters during initial remote-chain setup in ConfigureTokenForTransfers via ApplyChainUpdates, so this follow-up sequence is intentionally unused.
Functions ¶
This section is empty.
Types ¶
type CommitteeVerifierParams ¶
type CommitteeVerifierParams struct {
// Qualifier distinguishes between multiple deployments of the committee verifier on the same chain.
Qualifier string
Template ccvs.CommitteeVerifier
}
type ConfigureCommitteeVerifierAsDestInput ¶
type ConfigureCommitteeVerifierAsDestInput struct {
ChainSelector uint64
MCMSEnabled bool
lanes.CommitteeVerifierConfig[datastore.AddressRef]
}
type ConfigureCommitteeVerifierAsSourceInput ¶
type ConfigureCommitteeVerifierAsSourceInput struct {
ChainSelector uint64
MCMSEnabled bool
lanes.CommitteeVerifierConfig[datastore.AddressRef]
}
type ConfigureLaneLegInput ¶
type ConfigureLaneLegInput struct {
Lane lanes.UpdateLanesInput
DataStore datastore.DataStore
}
ConfigureLaneLegInput carries the lane update plus a datastore for fee quoter resolution.
type DeployChainContractsParams ¶
type DeployChainContractsParams struct {
// OwnerParty is the Canton instance owner (decentralized party); used for instanceId@owner addresses.
OwnerParty string
// CCIPOwnerParty is the operational ccipOwner on product templates (lanes, admin roles, etc.).
CCIPOwnerParty string
// CCVOwnerParty is the CommitteeVerifier signatory owner (ccvOwner); distinct from CCIPOwnerParty in dual-MCMS deploys.
CCVOwnerParty string
// RMNOwnerParty is the RMNRemote signatory owner (rmnOwner); distinct from CCIPOwnerParty in triple-MCMS deploys.
RMNOwnerParty string
CommitteeVerifiers []CommitteeVerifierParams
Executors []ExecutorParams
GlobalConfig GlobalConfigParams
RMNRemote RMNRemoteParams
FeeQuoterConfig FeeQuoterParams
// FactoryAddressRef is the ccip-qualified factory for core CCIP deploys.
FactoryAddressRef datastore.AddressRef
// RMNFactoryAddressRef is the rmn-qualified factory; required when DevenvBundledDeploy is true.
RMNFactoryAddressRef datastore.AddressRef
// ProposalDriven enables MCMS proposal generation for factory-backed deploys.
ProposalDriven bool
// CcvRegistryBinding is required for OnRamp deps when CommitteeVerifiers is empty (CCV deployed separately).
CcvRegistryBinding mcms.RawInstanceAddress
// RmnRemoteRawInstanceAddress is required for production split deploy paths.
RmnRemoteRawInstanceAddress contracts.RawInstanceAddress
// DevenvBundledDeploy runs RMN+CV+core in one sequence (devenv adapter only). Mutually exclusive with RmnRemoteRawInstanceAddress.
DevenvBundledDeploy bool
// The InstrumentId of the native token
NativeInstrumentId splice_api_token_holding_v1.InstrumentId
}
type ExecutorParams ¶
type ExecutorParams struct {
Qualifier string
Template executorBinding.Executor
}
type FeeQuoterParams ¶
type GlobalConfigParams ¶
type GlobalConfigParams struct {
Template common.GlobalConfig
}
type RMNRemoteParams ¶
type RegisterTokenPoolInput ¶
type RegisterTokenPoolInput struct {
// TokenAdminRegistryInstanceAddress is the instance address of the TokenAdminRegistry contract.
TokenAdminRegistryInstanceAddress contracts.InstanceAddress
// TokenAdminRegistryRawInstanceAddress is the raw instance address label for MCMS proposals.
TokenAdminRegistryRawInstanceAddress contracts.RawInstanceAddress
// InstrumentId identifies the token (admin party + token id).
InstrumentId splice_api_token_holding_v1.InstrumentId
// PoolInstanceID is the instance ID of the token pool.
PoolInstanceID string
// CcipParty is the CCIP owner party (acts on ProposeAdministrator).
CcipParty string
// PoolOwnerParty is the token pool owner party (acts on AcceptAdminRole and SetPool).
PoolOwnerParty string
}
RegisterTokenPoolInput is the input for registering a token pool with the TokenAdminRegistry.