sequences

package
v0.0.0-...-d2009c8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 10, 2026 License: MIT Imports: 54 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
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([]committeeverifier.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, committeeverifier.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[committeeverifier.ApplySignatureConfigs]{
				InstanceAddress:    address,
				RawInstanceAddress: raw.String(),
				MCMSEnabled:        input.MCMSEnabled,
				Args: committeeverifier.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
	},
)
View Source
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([]committeeverifier.RemoteChainConfigArgs, 0, len(input.RemoteChains))
		allowListArgs := make([]committeeverifier.AllowListConfigArgs, 0, len(input.RemoteChains))
		for remoteSelector, remoteConfig := range input.RemoteChains {
			remoteChainConfigArgs = append(remoteChainConfigArgs, committeeverifier.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, committeeverifier.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[committeeverifier.ApplyRemoteChainConfigUpdates]{
				InstanceAddress:    address,
				RawInstanceAddress: raw.String(),
				MCMSEnabled:        input.MCMSEnabled,
				Args: committeeverifier.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[committeeverifier.ApplyAllowListUpdates]{
				InstanceAddress:    address,
				RawInstanceAddress: raw.String(),
				MCMSEnabled:        input.MCMSEnabled,
				Args: committeeverifier.ApplyAllowListUpdates{
					AllowListConfigArgsItems: allowListArgs,
					Caller:                   types.PARTY(raw.Owner()),
				},
			})
			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
	},
)
View Source
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 dest. 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([]chainlinkapi.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([]chainlinkapi.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[core.ApplySourceChainConfigUpdates]{
			InstanceAddress:    globalConfigRaw.InstanceAddress(),
			RawInstanceAddress: globalConfigRaw.String(),
			MCMSEnabled:        mcmsEnabled,
			Args: core.ApplySourceChainConfigUpdates{
				SourceChainConfigUpdates: []core.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
	},
)
View Source
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})
	},
)
View Source
var ConfigureLaneLegAsSourceWithInput = operations.NewSequence(
	"CantonConfigureLaneLegAsSourceWithInput",
	semver.MustParse("2.0.0"),
	"Configures a lane leg as source on CCIP 2.0.0",
	configureLaneLegAsSource,
)
View Source
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,
			input.ExistingDataStore,
			input.ChainSelector,
			logicalPoolType,
			poolAddress,
			b.Logger,
		)
		if err != nil {
			return ccipsequences.OnChainOutput{}, err
		}

		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,
				ratelimiter.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,
				ratelimiter.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,
				ratelimiter.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.BytesToHash(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
	},
)
View Source
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.

View Source
var DeployCCVFromFactory = operations.NewSequence(
	"canton/ccip/deploy_ccv_from_factory",
	semver.MustParse("2.0.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 := requireCCIPOwnerParty(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 := committeeverifier.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: committeeverifier.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.

View Source
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
		}
		nativeInstrumentID, err := resolveNativeInstrumentID(b, deps, input.NativeInstrumentId)
		if err != nil {
			return sequences.OnChainOutput{}, fmt.Errorf("failed to resolve native fee token instrument: %w", err)
		}

		deployRMNRemoteReport, err := operations.ExecuteOperation(b, rmn_remote.Deploy, deps, contract.DeployInput[core.RMNRemote]{
			Template:   rmnRemoteDeployTemplate(rmnOwnerParty, types.PARTY(input.CCIPOwnerParty), input.RMNRemote),
			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[core.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[core.TokenAdminRegistry]{
			Template: core.TokenAdminRegistry{
				CcipOwner:  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[core.FeeQuoter]{
			Template: core.FeeQuoter{
				CcipOwner:                        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[core.UpdatePrices]{
				InstanceAddress: feeQuoterRawInstanceAddress.InstanceAddress(),
				Args: core.UpdatePrices{
					PriceUpdates: core.PriceUpdates{
						TokenPriceUpdates: []core.TokenPriceUpdate{
							{
								InstrumentId: 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(),
			tokenAdminRegistryRawInstanceAddress,
			input.CCIPOwnerParty,
			nativeInstrumentID,
			false,
		)
		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[ccipruntime.OffRamp]{
			Template: ccipruntime.OffRamp{
				CcipOwner: types.PARTY(input.CCIPOwnerParty),
				Deps: ccipruntime.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[ccipruntime.OnRamp]{
			Template: ccipruntime.OnRamp{
				CcipOwner:         types.PARTY(input.CCIPOwnerParty),
				MaxUSDCentsPerMsg: types.NUMERIC("100000000"),
				Deps: ccipruntime.OnRampDeps{
					GlobalConfig:       globalConfigRawInstanceAddress.Binding(),
					RmnRemote:          rmnRemoteRawInstanceAddress.Binding(),
					TokenAdminRegistry: tokenAdminRegistryRawInstanceAddress.Binding(),
					FeeQuoter:          feeQuoterRawInstanceAddress.Binding(),
				},
			},
			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[ccipruntime.PerPartyRouterFactory]{
			Template: ccipruntime.PerPartyRouterFactory{
				CcipOwner: types.PARTY(input.CCIPOwnerParty),
				Deps: ccipruntime.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[committeeverifier.CommitteeVerifier]{
				Template: committeeverifier.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: committeeverifier.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:  &params.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
	},
)
View Source
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

		ccipOwnerParty, err := requireCCIPOwnerParty(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 := core.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(ccipOwnerParty)
		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 := core.TokenAdminRegistry{
			InstanceId: types.TEXT(tokenAdminRegistryInstanceID),
			CcipOwner:  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(ccipOwnerParty)
		addresses = append(addresses, newAddressRef(deps.ChainSelector(), tokenAdminRegistryRawInstanceAddress, token_admin_registry.ContractType, token_admin_registry.Version, ""))

		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...)
		}

		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: ccipOwnerParty,
				Id:    types.TEXT("link-token"),
			}
		}
		feeQuoterTemplate := core.FeeQuoter{
			InstanceId:                       types.TEXT(feeQuoterInstanceID),
			CcipOwner:                        ccipOwnerParty,
			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(ccipOwnerParty)
		addresses = append(addresses, newAddressRef(deps.ChainSelector(), feeQuoterRawInstanceAddress, fee_quoter.ContractType, fee_quoter.Version, ""))

		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 := committeeverifier.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: committeeverifier.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)
			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 := ccipruntime.OffRamp{
			InstanceId: types.TEXT(offRampInstanceID),
			CcipOwner:  ccipOwnerParty,
			Deps: ccipruntime.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(ccipOwnerParty)
		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 := ccipruntime.OnRamp{
			InstanceId:        types.TEXT(onRampInstanceID),
			CcipOwner:         ccipOwnerParty,
			MaxUSDCentsPerMsg: types.NUMERIC("100000000"),
			Deps: ccipruntime.OnRampDeps{
				GlobalConfig:       globalConfigRawInstanceAddress.Binding(),
				RmnRemote:          rmnRemoteRawInstanceAddress.Binding(),
				TokenAdminRegistry: tokenAdminRegistryRawInstanceAddress.Binding(),
				FeeQuoter:          feeQuoterRawInstanceAddress.Binding(),
			},
		}
		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(ccipOwnerParty)
		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 := ccipruntime.PerPartyRouterFactory{
			InstanceId: types.TEXT(perPartyRouterFactoryInstanceID),
			CcipOwner:  ccipOwnerParty,
			Deps: ccipruntime.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(ccipOwnerParty)
		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.

View Source
var DeployLinkTokenFromFactory = operations.NewSequence(
	"canton/ccip/deploy_link_token_from_factory",
	semver.MustParse("2.0.0"),
	"Deploys LinkRegistry on Canton through CCIPFactory",
	func(b operations.Bundle, deps canton.Chain, input DeployLinkTokenFromFactoryParams) (sequences.OnChainOutput, error) {
		if input.CCIPOwnerParty == "" {
			return sequences.OnChainOutput{}, fmt.Errorf("CCIPOwnerParty is required")
		}
		ccipOwnerParty := types.PARTY(input.CCIPOwnerParty)

		factoryRaw, err := rawInstanceAddressFromAddressRef(input.FactoryAddressRef)
		if err != nil {
			return sequences.OnChainOutput{}, err
		}

		instrumentID := input.InstrumentID
		if instrumentID.Admin == "" {
			instrumentID = splice_api_token_holding_v1.InstrumentId{
				Admin: ccipOwnerParty,
				Id:    types.TEXT(defaultCantonLinkTokenInstrumentID),
			}
		}
		if instrumentID.Admin != ccipOwnerParty {
			return sequences.OnChainOutput{}, fmt.Errorf("instrument admin must match ccipOwnerParty")
		}

		linkInstanceID, err := ensureInstanceID(input.LinkTokenInstanceID, "link-registry")
		if err != nil {
			return sequences.OnChainOutput{}, fmt.Errorf("link token instance ID: %w", err)
		}

		deployReport, err := operations.ExecuteOperation(b, factoryops.DeployLinkToken, deps, newChoiceInput(factoryRaw, factorybindings.DeployLinkToken{
			Contract: link.LinkRegistry{
				RegistryAdmin:        ccipOwnerParty,
				RegistryInstrumentId: instrumentID,
				InstanceId:           types.TEXT(linkInstanceID),
				RegistryMeta:         splice_api_token_metadata_v1.Metadata{},
			},
		}, input.ProposalDriven))
		if err != nil {
			return sequences.OnChainOutput{}, fmt.Errorf("deploy LinkRegistry from factory: %w", err)
		}

		linkRawAddr := linkInstanceID.RawInstanceAddress(ccipOwnerParty)
		tokenEncodedAddress := contracts.EncodeInstrumentID(instrumentID).Hex()
		qualifier := stringsTrimOrDefault(input.TokenQualifier, "LINK")

		out := sequences.OnChainOutput{
			Addresses: []datastore.AddressRef{
				newAddressRef(deps.ChainSelector(), linkRawAddr, linkregistry.ContractType, linkregistry.Version, ""),
				{
					Address: tokenEncodedAddress,
					Labels: datastore.NewLabelSet(
						fmt.Sprintf("instrument-admin:%s", instrumentID.Admin),
						fmt.Sprintf("instrument-id:%s", instrumentID.Id),
						fmt.Sprintf("ccip-owner:%s", ccipOwnerParty),
					),
					Type:          datastore.ContractType("Token"),
					Version:       linkregistry.Version,
					Qualifier:     qualifier,
					ChainSelector: deps.ChainSelector(),
				},
			},
		}

		if input.ProposalDriven && !deployReport.Output.Executed() {
			batchOp, err := contract.NewBatchOperationFromExercises([]contract.ExerciseOutput{deployReport.Output})
			if err != nil {
				return sequences.OnChainOutput{}, fmt.Errorf("build MCMS batch for link token deploy: %w", err)
			}
			if len(batchOp.Transactions) > 0 {
				out.BatchOps = []mcms_types.BatchOperation{batchOp}
			}
		}

		return out, nil
	},
)

DeployLinkTokenFromFactory emits an MCMS batch for DeployLinkToken when ProposalDriven is true.

View Source
var DeployRMNFromFactory = operations.NewSequence(
	"canton/ccip/deploy_rmn_from_factory",
	semver.MustParse("2.0.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.

View Source
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

		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)
		poolReceiveContext := splice_api_token_metadata_v1.ChoiceContext{
			Values: map[string]splice_api_token_metadata_v1.AnyValue{},
		}

		var (
			rawPoolAddr  contracts.RawInstanceAddress
			canonicalRef datastore.AddressRef
		)
		switch logicalPoolType {
		case lockReleasePoolType:
			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.TokenTransferFeeConfig{},
					PoolReceiveContext:      poolReceiveContext,
					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)
			canonicalRef = newAddressRef(input.ChainSelector, rawPoolAddr, lock_release_token_pool.ContractType, lock_release_token_pool.Version, qualifier)
		case burnMintPoolType:
			poolInstanceID := contracts.InstanceID(fmt.Sprintf("burnminttokenpool-%s", qualifier))
			deployReport, err := operations.ExecuteOperation(b, factoryops.DeployBurnMintTokenPool, cantonChain, newChoiceInput(factoryRaw, factorybindings.DeployBurnMintTokenPool{
				Contract: burnminttokenpool.BurnMintTokenPool{
					InstanceId:              types.TEXT(poolInstanceID),
					CcipOwner:               ccipOwner,
					PoolOwner:               poolOwner,
					InstrumentId:            instrumentID,
					Decimals:                types.INT64(10),
					RemoteChainConfigs:      map[types.NUMERIC]burnminttokenpool.RemoteChainConfig{},
					TokenTransferFeeConfigs: map[types.NUMERIC]burnminttokenpool.TokenTransferFeeConfig{},
					PoolReceiveContext:      poolReceiveContext,
					TransferTimeout: burnminttokenpool.TransferTimeout{
						RelativeHours: new(types.INT64(24)),
					},
					Deps: burnminttokenpool.BurnMintTokenPoolDeps{
						TokenAdminRegistry: tokenAdminRegistryRaw.Binding(),
						RmnRemote:          rmnRemoteRaw.Binding(),
						FeeQuoter:          feeQuoterRaw.Binding(),
					},
				},
			}, mcmsEnabled))
			if err != nil {
				return ccipsequences.OnChainOutput{}, fmt.Errorf("deploy Canton burn/mint pool from factory: %w", err)
			}
			if mcmsEnabled && !deployReport.Output.Executed() {
				proposalOutputs = append(proposalOutputs, deployReport.Output)
			}
			rawPoolAddr = poolInstanceID.RawInstanceAddress(poolOwner)
			canonicalRef = newAddressRef(input.ChainSelector, rawPoolAddr, burn_mint_token_pool.ContractType, burn_mint_token_pool.Version, qualifier)
		default:
			return ccipsequences.OnChainOutput{}, fmt.Errorf("unsupported Canton token pool type %q", logicalPoolType)
		}

		regOut, err := operations.ExecuteSequence(b, RegisterTokenPool, cantonChain, RegisterTokenPoolInput{
			TokenAdminRegistryInstanceAddress:    contracts.HexToInstanceAddress(tokenAdminRegistryRef.Address),
			TokenAdminRegistryRawInstanceAddress: tokenAdminRegistryRaw,
			InstrumentId:                         instrumentID,
			PoolInstanceID:                       rawPoolAddr.InstanceID(),
			CcipParty:                            ccipOwnerParty,
			PoolOwnerParty:                       poolOwnerParty,
			PoolAdminParty:                       ccipOwnerParty,
		})
		if err != nil {
			return ccipsequences.OnChainOutput{}, fmt.Errorf("register Canton token pool: %w", err)
		}
		batchOps = append(batchOps, regOut.Output.BatchOps...)

		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
	},
)
View Source
var HardenCantonInboundLane = operations.NewSequence(
	"HardenCantonInboundLane",
	semver.MustParse("2.0.0"),
	"Applies Canton inbound lane hardening (fee quoter prices + invalid default inbound CCV + optional outbound dest fees)",
	hardenCantonInboundLane,
)
View Source
var RegisterNativeFeeTokenInTAR = operations.NewSequence(
	"canton/ccip/register_native_fee_token_in_tar",
	semver.MustParse("2.0.0"),
	"Registers the native Canton fee token in TokenAdminRegistry (ProposeAdministrator, AcceptAdminRole)",
	registerNativeFeeTokenInTAR,
)
View Source
var RegisterTokenPool = operations.NewSequence(
	"canton/ccip/register_token_pool",
	semver.MustParse("2.0.0"),
	"Registers a token pool with the TokenAdminRegistry (ProposeAdministrator, AcceptAdminRole, SetPool)",
	registerTokenPool,
)
View Source
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

func CantonTokenPoolDecimalsFromDataStore

func CantonTokenPoolDecimalsFromDataStore(
	ds datastore.DataStore,
	chainSelector uint64,
	poolRef datastore.AddressRef,
) (uint8, error)

CantonTokenPoolDecimalsFromDataStore resolves pool decimals from address_refs when the pool contract is not yet active on the Canton ledger (e.g. deploy MCMS still pending).

func IsCantonPoolNotOnLedgerErr

func IsCantonPoolNotOnLedgerErr(err error) bool

IsCantonPoolNotOnLedgerErr reports whether err is the standard "pool not yet deployed" ledger lookup failure.

Types

type CantonOutboundDestFeeInput

type CantonOutboundDestFeeInput struct {
	Source *lanes.ChainDefinition
	Dest   *lanes.ChainDefinition
}

CantonOutboundDestFeeInput carries resolved lane legs for Canton→remote outbound dest fee hardening.

type CommitteeVerifierParams

type CommitteeVerifierParams struct {
	// Qualifier distinguishes between multiple deployments of the committee verifier on the same chain.
	Qualifier string
	Template  committeeverifier.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
	// 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 DeployLinkTokenFromFactoryParams

type DeployLinkTokenFromFactoryParams struct {
	CCIPOwnerParty      string `json:"ccipOwnerParty,omitempty" yaml:"ccipOwnerParty,omitempty"`
	FactoryAddressRef   datastore.AddressRef
	ProposalDriven      bool                                     `json:"proposalDriven,omitempty" yaml:"proposalDriven,omitempty"`
	LinkTokenInstanceID types.TEXT                               `json:"linkTokenInstanceId,omitempty" yaml:"linkTokenInstanceId,omitempty"`
	InstrumentID        splice_api_token_holding_v1.InstrumentId `json:"instrumentId,omitzero" yaml:"instrumentId,omitempty"`
	TokenQualifier      string                                   `json:"tokenQualifier,omitempty" yaml:"tokenQualifier,omitempty"`
}

DeployLinkTokenFromFactoryParams deploys Canton LinkRegistry via the ccip-qualified CCIPFactory.

type ExecutorParams

type ExecutorParams struct {
	Qualifier string
	Template  executorBinding.Executor
}

type FeeQuoterParams

type FeeQuoterParams struct {
	Template core.FeeQuoter
	// The price of the native token to be set on the FeeQuoter.
	// If not-nil, native will be added as a fee token and the price will be set.
	USDPerNative *big.Int
}

type GlobalConfigParams

type GlobalConfigParams struct {
	Template core.GlobalConfig
}

type HardenCantonInboundLaneInput

type HardenCantonInboundLaneInput struct {
	CantonChainSelector       uint64
	RemoteSourceChainSelector uint64
	GlobalConfigRef           datastore.AddressRef
	FeeQuoterRef              datastore.AddressRef
	DefaultInboundCCVs        []datastore.AddressRef
	RemoteOnRampAddress       gethcommon.Address
	TokenPrices               map[string]*big.Int
	USDPerUnitGas             *big.Int
	OutboundDestFees          *CantonOutboundDestFeeInput
}

HardenCantonInboundLaneInput carries pre-resolved contract refs and lane hardening params. Emits FeeQuoter::UpdatePrices + GlobalConfig::ApplySourceChainConfigUpdates, and optionally Canton→remote outbound dest fee ops (GlobalConfig + FeeQuoter dest config).

type RMNRemoteParams

type RMNRemoteParams struct {
	Template core.RMNRemote
}

RMNRemoteParams configures RMNRemote deploy. Set Template.CustomObservers (typically ccvOwner) so NOP verifiers can poll curse state via sourcereader.

type RegisterNativeFeeTokenInTARInput

type RegisterNativeFeeTokenInTARInput struct {
	TokenAdminRegistryInstanceAddress    contracts.InstanceAddress
	TokenAdminRegistryRawInstanceAddress contracts.RawInstanceAddress
	InstrumentId                         splice_api_token_holding_v1.InstrumentId
	CcipOwnerParty                       string
	TokenQualifier                       string
	ChainSelector                        uint64
	CcipParticipantIndex                 int `json:"ccipParticipantIndex,omitempty"`
	ProposalDriven                       bool
}

RegisterNativeFeeTokenInTARInput registers the Canton native fee token (typically Amulet) in the TokenAdminRegistry by creating a TokenConfig via ProposeAdministrator + AcceptAdminRole. No SetPool is performed — fee tokens are not CCIP transfer tokens.

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 (registry owner; acts on ProposeAdministrator).
	CcipParty string
	// PoolOwnerParty is the token pool contract owner (recorded in PoolRegistration only).
	PoolOwnerParty string
	// PoolAdminParty is the TokenConfig admin (proposed/accepts admin role; acts on SetPool).
	PoolAdminParty string
	// CcipParticipantIndex selects which participant submits TAR registration choices.
	// Zero value defaults to the first participant.
	CcipParticipantIndex int `json:"ccipParticipantIndex,omitempty"`
}

RegisterTokenPoolInput is the input for registering a token pool with the TokenAdminRegistry.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL