Documentation
¶
Overview ¶
Package sequences defines CLDF deployment sequences for Stellar CCIP.
Deploy chain contracts uses cldf_chain.BlockChains as the dependency type to match chainlink-ccip deployment/v2_0_0 adapter contracts. The sequence builds deployment/operations/stellardeps.StellarDeps and a CCIP devenv host from the CLDF Stellar chain entry, loads stashed offchain topology via TakeStellarDeployOffchainTopologyForSelector when CCV registered one (in PreDeployContractsForSelector), then runs RunStellarCCIPFullDeploy. A missing stash is tolerated: committee verifier signer quorums are applied at lane-configuration time, so the deploy itself does not need NOP topology data. RunStellarCCIPFullDeployForCCV converts CCV environment topology and is used by ccv/chain; it keeps its nil-topology check, and devenv always supplies a CCV topology. The sequence return type matches chainlink-ccip/deployment/v2_0_0/adapters.DeployChainContractsAdapter for the module version in go.mod. DeployStellarCCIPInnerInput carries ExistingAddresses from the CCIP changeset input so deploy can seed the in-memory datastore like EVM ExistingAddresses. AllSelectors lists every chain selector in the environment (derived from BlockChains in the adapter sequence).
Index ¶
- Constants
- Variables
- func ApplyStellarFeeAggregator(b cldfops.Bundle, chains cldfchain.BlockChains, env cldf.Environment, ...) (seqcore.OnChainOutput, error)
- func ApplyStellarWithdrawFeeTokens(b cldfops.Bundle, chains cldfchain.BlockChains, env cldf.Environment, ...) (output seqcore.OnChainOutput, err error)
- func CREForwarderDeploySalt(chainSelector uint64, qual string) [32]byte
- func ClearStellarDeployOffchainTopologyForSelector(selector uint64)
- func CvGasForVerificationForRemote(remoteSelector uint64) (uint32, error)
- func DeployStellarCCIPContracts(ctx context.Context, opBundle cldfops.Bundle, host stellarccip.CCIPDevenvHost, ...) (datastore.DataStore, error)
- func FindExistingStellarCREForwarder(refs []datastore.AddressRef, chainSelector uint64, qual string) (string, bool)
- func RegisterStellarDeployOffchainTopologyForSelector(selector uint64, topo *offchain.EnvironmentTopology)
- func ResolveCREForwarderWasmPath() (string, error)
- func RunStellarCCIPFullDeploy(ctx context.Context, b cldf_ops.Bundle, deps stellardeps.StellarDeps, ...) (seq_core.OnChainOutput, error)
- func RunStellarCCIPFullDeployForCCV(ctx context.Context, opBundle cldfops.Bundle, deps stellardeps.StellarDeps, ...) (seq_core.OnChainOutput, error)
- func StellarCREForwarderDatastoreRef(chainSelector uint64, qual, contractID string) datastore.AddressRef
- func TakeStellarDeployOffchainTopologyForSelector(selector uint64) (*offchain.EnvironmentTopology, bool)
- func VerifyStellarMCMSGovernance(ctx context.Context, deps stellardeps.StellarDeps, refs []datastore.AddressRef, ...) error
- func VerifyStellarMCMSGovernanceWithExpectations(ctx context.Context, deps stellardeps.StellarDeps, refs []datastore.AddressRef, ...) error
- type CREForwarderDONConfig
- type ComponentDeployOutput
- type ComponentDeps
- type DeployAdvancedPoolHooksExtractorInput
- type DeployAdvancedPoolHooksInput
- type DeployBnmTokenContractInput
- type DeployBnmTokenInput
- type DeployBnmTokenOutput
- type DeployBurnMintPoolInput
- type DeployCCIPReceiverInput
- type DeployChainContractsOutput
- type DeployCommitteeVerifierInput
- type DeployExecutorInput
- type DeployFeeQuoterInput
- type DeployLinkTokenInput
- type DeployLockReleasePoolInput
- type DeployOffRampInput
- type DeployOnRampInput
- type DeployRMNProxyInput
- type DeployRMNRemoteInput
- type DeployRampRegistryInput
- type DeployRouterInput
- type DeploySiloedLockReleasePoolInput
- type DeployStellarCCIPInnerInput
- type DeployStellarCREForwarderInput
- type DeployTokenAdminRegistryInput
- type DeployTokenLockBoxInput
- type DeployVVRInput
- type InstanceStateReader
- type StellarActivateRMNInput
- type StellarApplyDestChainConfigInput
- type StellarCCIPDeploySession
- type StellarCurseInput
- type StellarSetCommitteeVerifierFinalityInput
- type StellarSetTokenTransferFeeInput
- type StellarTransferOwnershipInput
- type StellarUpgradeContractsInput
Constants ¶
const DefaultCREForwarderWasmRelative = "target/wasm32v1-none/release/forwarder.wasm"
DefaultCREForwarderWasmRelative is the release artifact path relative to the chainlink-stellar repo root.
const StellarSetFeeAggregatorSequenceID = "stellar-set-fee-aggregator"
StellarSetFeeAggregatorSequenceID is the pipeline sequence ID for SetFeeAggregator on Stellar (OnRamp, VVR, Committee Verifier). Use [StellarFeeAggregatorAdapter.SetFeeAggregator], which wraps ApplyStellarFeeAggregator with the cldf.Environment needed for datastore lookups.
const StellarWithdrawFeeTokensSequenceID = "stellar-withdraw-fee-tokens"
StellarWithdrawFeeTokensSequenceID is the pipeline sequence ID for WithdrawFeeTokens on Stellar (OnRamp, VVR, Committee Verifier). Use [StellarFeeAggregatorAdapter.WithdrawFeeTokens], which wraps ApplyStellarWithdrawFeeTokens with the cldf.Environment needed for datastore lookups.
Variables ¶
var DeployAdvancedPoolHooks = cldf_ops.NewSequence( "stellar-deploy-advanced-pool-hooks", SequenceVersion, "Deploys and initializes the Advanced Pool Hooks Soroban contract with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeployAdvancedPoolHooksInput) (ComponentDeployOutput, error) { owner := defaultOwner(in.Owner, deps.DeployerAddress) if err := statReleaseWasm(in.WasmPath, "Advanced Pool Hooks"); err != nil { return ComponentDeployOutput{}, err } return deployAndInitialize(b.GetContext(), b, deps, aphops.Deploy, stellarccip.AdvancedPoolHooksDatastoreRef(in.Qualifier), in.ChainSelector, componentSaltLabel("advanced-pool-hooks", in.Qualifier), in.WasmPath, owner, in.ExistingAddresses, func(contractID string) error { _, err := execStellarCCIPOp(b, deps.StellarDeps, aphops.Initialize, aphops.InitializeInput{ ContractID: contractID, Owner: owner, Allowlist: in.Allowlist, ThresholdAmount: in.ThresholdAmount, AuthorizedCallers: in.AuthorizedCallers, PolicyEngine: in.PolicyEngine, }, withComponentInstanceIdempotencyKey[aphops.InitializeInput](in.ChainSelector, in.Qualifier)) return err }) }, )
var DeployAdvancedPoolHooksExtractor = cldf_ops.NewSequence( "stellar-deploy-advanced-pool-hooks-extractor", SequenceVersion, "Deploys the stateless Advanced Pool Hooks Extractor Soroban contract with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeployAdvancedPoolHooksExtractorInput) (ComponentDeployOutput, error) { if err := statReleaseWasm(in.WasmPath, "Advanced Pool Hooks Extractor"); err != nil { return ComponentDeployOutput{}, err } return deployStatelessComponent(b.GetContext(), b, deps, aphxops.Deploy, stellarccip.AdvancedPoolHooksExtractorDatastoreRef(), in.ChainSelector, "advanced-pool-hooks-extractor", in.WasmPath, in.ExistingAddresses) }, )
var DeployBnmToken = cldf_ops.NewSequence( "stellar-deploy-bnm-token", SequenceVersion, "Deploys and initializes the BnM Soroban token with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeployBnmTokenContractInput) (ComponentDeployOutput, error) { owner := defaultOwner(in.Owner, deps.DeployerAddress) name, symbol, decimals := bnmTokenDefaults(in.Name, in.Symbol, in.Decimals) if err := statReleaseWasm(in.WasmPath, "BnM token"); err != nil { return ComponentDeployOutput{}, err } return deployAndInitialize(b.GetContext(), b, deps, bnmops.Deploy, stellarccip.BnmTokenDatastoreRef(), in.ChainSelector, "bnm-token", in.WasmPath, owner, in.ExistingAddresses, func(contractID string) error { _, err := execStellarCCIPOp(b, deps.StellarDeps, bnmops.Initialize, bnmops.InitializeInput{ ContractID: contractID, Admin: owner, Name: name, Symbol: symbol, Decimals: decimals, }, withComponentIdempotencyKey[bnmops.InitializeInput](in.ChainSelector)) return err }) }, )
var DeployBurnMintPool = cldf_ops.NewSequence( "stellar-deploy-burn-mint-pool", SequenceVersion, "Deploys and initializes the burn-mint token pool with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeployBurnMintPoolInput) (ComponentDeployOutput, error) { owner := defaultOwner(in.Owner, deps.DeployerAddress) if err := statReleaseWasm(in.WasmPath, "Burn-mint pool"); err != nil { return ComponentDeployOutput{}, err } return deployAndInitialize(b.GetContext(), b, deps, bmpops.Deploy, stellarccip.BurnMintPoolDatastoreRef(in.Qualifier), in.ChainSelector, componentSaltLabel("burn-mint-pool", in.Qualifier), in.WasmPath, owner, in.ExistingAddresses, func(contractID string) error { _, err := execStellarCCIPOp(b, deps.StellarDeps, bmpops.Initialize, bmpops.InitializeInput{ ContractID: contractID, Owner: owner, Token: in.Token, TokenDecimals: in.TokenDecimals, Router: in.Router, RampRegistry: in.RampRegistry, RmnProxy: in.RmnProxy, }, withComponentInstanceIdempotencyKey[bmpops.InitializeInput](in.ChainSelector, in.Qualifier)) return err }) }, )
var DeployCCIPReceiver = cldf_ops.NewSequence( "stellar-deploy-ccip-receiver", SequenceVersion, "Deploys and initializes the ccip_receiver_example Soroban contract with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeployCCIPReceiverInput) (ComponentDeployOutput, error) { owner := defaultOwner(in.Owner, deps.DeployerAddress) if err := statReleaseWasm(in.WasmPath, "ccip_receiver_example"); err != nil { return ComponentDeployOutput{}, err } if in.Router == "" { return ComponentDeployOutput{}, fmt.Errorf("router is required: deploy Router first") } return deployAndInitialize(b.GetContext(), b, deps, recvops.Deploy, stellarccip.CCIPReceiverDatastoreRefWithQualifier(in.Qualifier), in.ChainSelector, componentSaltLabel("ccip-receiver-example", in.Qualifier), in.WasmPath, owner, in.ExistingAddresses, func(contractID string) error { _, err := execStellarCCIPOp(b, deps.StellarDeps, recvops.Initialize, recvops.InitializeInput{ ContractID: contractID, Owner: owner, Router: in.Router, }, withComponentInstanceIdempotencyKey[recvops.InitializeInput](in.ChainSelector, in.Qualifier)) return err }) }, )
var DeployCommitteeVerifier = cldf_ops.NewSequence( "stellar-deploy-committee-verifier", SequenceVersion, "Deploys and initializes the CommitteeVerifier Soroban contract with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeployCommitteeVerifierInput) (ComponentDeployOutput, error) { owner := defaultOwner(in.Owner, deps.DeployerAddress) allowlistAdmin := in.AllowlistAdmin if allowlistAdmin == "" { allowlistAdmin = owner } feeAggregator := in.FeeAggregator if feeAggregator == "" { feeAggregator = owner } versionTag := in.VersionTag if versionTag == [4]byte{} { versionTag = stellarutil.DefaultCommitteeVerifierVersionTag() } if err := statReleaseWasm(in.WasmPath, "Committee Verifier"); err != nil { return ComponentDeployOutput{}, err } if len(in.StorageLocations) == 0 { return ComponentDeployOutput{}, fmt.Errorf("storageLocations is required: pass the committee storage locations") } if in.RmnProxy == "" { return ComponentDeployOutput{}, fmt.Errorf("rmnProxy is required: deploy RMN Proxy first") } return deployAndInitialize(b.GetContext(), b, deps, cvops.Deploy, stellarccip.CommitteeVerifierDatastoreRefWithQualifier(in.Qualifier), in.ChainSelector, componentSaltLabel("committee-verifier", in.Qualifier), in.WasmPath, owner, in.ExistingAddresses, func(contractID string) error { _, err := execStellarCCIPOp(b, deps.StellarDeps, cvops.Initialize, cvops.InitializeInput{ ContractID: contractID, Owner: owner, DynamicConfig: cvbindings.DynamicConfig{ AllowlistAdmin: &allowlistAdmin, FeeAggregator: &feeAggregator, }, StorageLocations: in.StorageLocations, RmnProxy: in.RmnProxy, VersionTag: versionTag, }, withComponentInstanceIdempotencyKey[cvops.InitializeInput](in.ChainSelector, in.Qualifier)) return err }) }, )
var DeployExecutor = cldf_ops.NewSequence( "stellar-deploy-executor", SequenceVersion, "Deploys and initializes the Executor Soroban contract with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeployExecutorInput) (ComponentDeployOutput, error) { qualifier := in.Qualifier if qualifier == "" { qualifier = stellarccip.DefaultExecutorQualifier } owner := defaultOwner(in.Owner, deps.DeployerAddress) maxCCVs := in.MaxCCVsPerMsg if maxCCVs == 0 { maxCCVs = 10 } feeAggregator := in.FeeAggregator if feeAggregator == "" { feeAggregator = owner } if err := statReleaseWasm(in.WasmPath, "Executor"); err != nil { return ComponentDeployOutput{}, err } out, err := deployAndInitialize(b.GetContext(), b, deps, execops.Deploy, stellarccip.ExecutorDatastoreRefWithQualifier(in.Qualifier), in.ChainSelector, componentSaltLabel("executor", in.Qualifier), in.WasmPath, owner, in.ExistingAddresses, func(contractID string) error { _, err := execStellarCCIPOp(b, deps.StellarDeps, execops.Initialize, execops.InitializeInput{ ContractID: contractID, Owner: owner, MaxCCVsPerMsg: maxCCVs, DynamicConfig: executorbindings.DynamicConfig{ AllowedFinalityConfig: in.AllowedFinalityConfig, CcvAllowlistEnabled: in.CcvAllowlistEnabled, FeeAggregator: &feeAggregator, }, }, withComponentInstanceIdempotencyKey[execops.InitializeInput](in.ChainSelector, in.Qualifier)) return err }) if err != nil { return ComponentDeployOutput{}, err } proxyRef := stellarccip.ExecutorProxyDatastoreRef(qualifier) refs, err := componentRefs(proxyRef, in.ChainSelector, out.ContractID) if err != nil { return ComponentDeployOutput{}, err } proxyRow := refs[0] if existing := findExistingComponentRef(in.ExistingAddresses, proxyRef, in.ChainSelector); existing == nil || existing.Address != proxyRow.Address { out.Refs = append(out.Refs, proxyRow) } return out, nil }, )
var DeployFeeQuoter = cldf_ops.NewSequence( "stellar-deploy-fee-quoter", SequenceVersion, "Deploys and initializes the FeeQuoter Soroban contract with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeployFeeQuoterInput) (ComponentDeployOutput, error) { owner := defaultOwner(in.Owner, deps.DeployerAddress) if err := statReleaseWasm(in.WasmPath, "FeeQuoter"); err != nil { return ComponentDeployOutput{}, err } if in.FeeToken == "" { return ComponentDeployOutput{}, fmt.Errorf("feeToken is required: pass the fee-token SAC strkey") } maxFee := in.MaxFeeJuelsPerMsg if maxFee == nil { maxFee = big.NewInt(1_000_000_000_000_000_000) } callers := in.AuthorizedCallers if len(callers) == 0 { callers = []string{owner} } return deployAndInitialize(b.GetContext(), b, deps, fqops.Deploy, stellarccip.FeeQuoterDatastoreRef(), in.ChainSelector, "fee-quoter", in.WasmPath, owner, in.ExistingAddresses, func(contractID string) error { _, err := execStellarCCIPOp(b, deps.StellarDeps, fqops.Initialize, fqops.InitializeInput{ ContractID: contractID, Owner: owner, StaticConfig: fqbindings.StaticConfig{ LinkToken: in.FeeToken, MaxFeeJuelsPerMsg: maxFee, }, AuthorizedCallers: callers, }, withComponentIdempotencyKey[fqops.InitializeInput](in.ChainSelector)) return err }) }, )
var DeployLinkToken = cldf_ops.NewSequence( "stellar-deploy-link-token", SequenceVersion, "Deploys and initializes the LINK Soroban token with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeployLinkTokenInput) (ComponentDeployOutput, error) { owner := defaultOwner(in.Owner, deps.DeployerAddress) name, symbol, decimals := linkTokenDefaults(in.Name, in.Symbol, in.Decimals) if err := statReleaseWasm(in.WasmPath, "LINK token"); err != nil { return ComponentDeployOutput{}, err } return deployAndInitialize(b.GetContext(), b, deps, lnkops.Deploy, stellarccip.LinkTokenDatastoreRef(), in.ChainSelector, "link-token", in.WasmPath, owner, in.ExistingAddresses, func(contractID string) error { _, err := execStellarCCIPOp(b, deps.StellarDeps, lnkops.Initialize, lnkops.InitializeInput{ ContractID: contractID, Admin: owner, Name: name, Symbol: symbol, Decimals: decimals, }, withComponentIdempotencyKey[lnkops.InitializeInput](in.ChainSelector)) return err }) }, )
var DeployLockReleasePool = cldf_ops.NewSequence( "stellar-deploy-lock-release-pool", SequenceVersion, "Deploys and initializes the canonical lock-release token pool with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeployLockReleasePoolInput) (ComponentDeployOutput, error) { owner := defaultOwner(in.Owner, deps.DeployerAddress) if err := statReleaseWasm(in.WasmPath, "Lock-release pool"); err != nil { return ComponentDeployOutput{}, err } return deployAndInitialize(b.GetContext(), b, deps, lrops.Deploy, stellarccip.LockReleasePoolDatastoreRef(in.Qualifier), in.ChainSelector, componentSaltLabel("lock-release-pool", in.Qualifier), in.WasmPath, owner, in.ExistingAddresses, func(contractID string) error { _, err := execStellarCCIPOp(b, deps.StellarDeps, lrops.Initialize, lrops.InitializeInput{ ContractID: contractID, Owner: owner, Token: in.Token, TokenDecimals: in.TokenDecimals, Router: in.Router, RampRegistry: in.RampRegistry, RmnProxy: in.RmnProxy, LockBox: in.LockBox, }, withComponentInstanceIdempotencyKey[lrops.InitializeInput](in.ChainSelector, in.Qualifier)) return err }) }, )
var DeployOffRamp = cldf_ops.NewSequence( "stellar-deploy-offramp", SequenceVersion, "Deploys and initializes the OffRamp Soroban contract with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeployOffRampInput) (ComponentDeployOutput, error) { owner := defaultOwner(in.Owner, deps.DeployerAddress) if err := statReleaseWasm(in.WasmPath, "OffRamp"); err != nil { return ComponentDeployOutput{}, err } if in.RmnProxy == "" { return ComponentDeployOutput{}, fmt.Errorf("rmnProxy is required: deploy RMN Proxy first") } if in.TokenAdminRegistry == "" { return ComponentDeployOutput{}, fmt.Errorf("tokenAdminRegistry is required: deploy TokenAdminRegistry first") } return deployAndInitialize(b.GetContext(), b, deps, offrampops.Deploy, stellarccip.OffRampDatastoreRef(), in.ChainSelector, "offramp", in.WasmPath, owner, in.ExistingAddresses, func(contractID string) error { _, err := execStellarCCIPOp(b, deps.StellarDeps, offrampops.Initialize, offrampops.InitializeInput{ ContractID: contractID, Owner: owner, Config: offrampbindings.StaticConfig{ ChainSelector: in.ChainSelector, RmnProxy: in.RmnProxy, TokenAdminRegistry: in.TokenAdminRegistry, }, }, withComponentIdempotencyKey[offrampops.InitializeInput](in.ChainSelector)) return err }) }, )
var DeployOnRamp = cldf_ops.NewSequence( "stellar-deploy-onramp", SequenceVersion, "Deploys and initializes the OnRamp Soroban contract with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeployOnRampInput) (ComponentDeployOutput, error) { owner := defaultOwner(in.Owner, deps.DeployerAddress) feeAggregator := in.FeeAggregator if feeAggregator == "" { feeAggregator = owner } maxUsdCents := in.MaxUsdCentsPerMessage if maxUsdCents == 0 { maxUsdCents = 500_000 } if err := statReleaseWasm(in.WasmPath, "OnRamp"); err != nil { return ComponentDeployOutput{}, err } if in.TokenAdminRegistry == "" { return ComponentDeployOutput{}, fmt.Errorf("tokenAdminRegistry is required: deploy TokenAdminRegistry first") } if in.RmnProxy == "" { return ComponentDeployOutput{}, fmt.Errorf("rmnProxy is required: deploy RMN Proxy first") } if in.FeeQuoter == "" { return ComponentDeployOutput{}, fmt.Errorf("feeQuoter is required: deploy FeeQuoter first") } return deployAndInitialize(b.GetContext(), b, deps, onrampops.Deploy, stellarccip.OnRampDatastoreRef(), in.ChainSelector, "onramp", in.WasmPath, owner, in.ExistingAddresses, func(contractID string) error { _, err := execStellarCCIPOp(b, deps.StellarDeps, onrampops.Initialize, onrampops.InitializeInput{ ContractID: contractID, Owner: owner, StaticConfig: onrampbindings.StaticConfig{ ChainSelector: in.ChainSelector, TokenAdminRegistry: in.TokenAdminRegistry, RmnProxy: in.RmnProxy, MaxUsdCentsPerMessage: maxUsdCents, }, DynamicConfig: onrampbindings.DynamicConfig{ FeeQuoter: in.FeeQuoter, FeeAggregator: feeAggregator, }, }, withComponentIdempotencyKey[onrampops.InitializeInput](in.ChainSelector)) return err }) }, )
var DeployRMNProxy = cldf_ops.NewSequence( "stellar-deploy-rmn-proxy", SequenceVersion, "Deploys and initializes the RMN Proxy Soroban contract with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeployRMNProxyInput) (ComponentDeployOutput, error) { owner := defaultOwner(in.Owner, deps.DeployerAddress) if err := statReleaseWasm(in.WasmPath, "RMN Proxy"); err != nil { return ComponentDeployOutput{}, err } if in.RmnRemote == "" { return ComponentDeployOutput{}, fmt.Errorf("rmnRemote is required: deploy RMN Remote first") } return deployAndInitialize(b.GetContext(), b, deps, rmnproxyops.Deploy, stellarccip.RMNProxyDatastoreRef(), in.ChainSelector, "rmn-proxy", in.WasmPath, owner, in.ExistingAddresses, func(contractID string) error { _, err := execStellarCCIPOp(b, deps.StellarDeps, rmnproxyops.Initialize, rmnproxyops.InitializeInput{ ContractID: contractID, Owner: owner, RmnRemote: in.RmnRemote, }, withComponentIdempotencyKey[rmnproxyops.InitializeInput](in.ChainSelector)) return err }) }, )
var DeployRMNRemote = cldf_ops.NewSequence( "stellar-deploy-rmn-remote", SequenceVersion, "Deploys and initializes the RMN Remote Soroban contract with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeployRMNRemoteInput) (ComponentDeployOutput, error) { owner := defaultOwner(in.Owner, deps.DeployerAddress) if err := statReleaseWasm(in.WasmPath, "RMN Remote"); err != nil { return ComponentDeployOutput{}, err } curseAdmins := in.CurseAdmins if in.EnableFastCurse { fastQual := in.FastCurseQualifier if fastQual == "" { fastQual = cciputils.UltraFastCurseMCMSQualifier } fastTL, ok := mcmsutil.FindExistingStellarTimelock(in.ExistingAddresses, in.ChainSelector, fastQual) if !ok { return ComponentDeployOutput{}, fmt.Errorf( "enable fast curse: no RBACTimelock deployed for qualifier %q on chain %d; deploy the fast-curse MCMS stack first", fastQual, in.ChainSelector) } curseAdmins = append([]string{fastTL}, curseAdmins...) } return deployAndInitialize(b.GetContext(), b, deps, rmnremoteops.Deploy, stellarccip.RMNRemoteDatastoreRef(), in.ChainSelector, "rmn-remote", in.WasmPath, owner, in.ExistingAddresses, func(contractID string) error { _, err := execStellarCCIPOp(b, deps.StellarDeps, rmnremoteops.Initialize, rmnremoteops.InitializeInput{ ContractID: contractID, Owner: owner, CurseAdmins: curseAdmins, }, withComponentIdempotencyKey[rmnremoteops.InitializeInput](in.ChainSelector)) return err }) }, )
var DeployRampRegistry = cldf_ops.NewSequence( "stellar-deploy-ramp-registry", SequenceVersion, "Deploys and initializes the RampRegistry Soroban contract with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeployRampRegistryInput) (ComponentDeployOutput, error) { owner := defaultOwner(in.Owner, deps.DeployerAddress) if err := statReleaseWasm(in.WasmPath, "RampRegistry"); err != nil { return ComponentDeployOutput{}, err } return deployAndInitialize(b.GetContext(), b, deps, rrops.Deploy, stellarccip.RampRegistryDatastoreRef(), in.ChainSelector, "ramp-registry", in.WasmPath, owner, in.ExistingAddresses, func(contractID string) error { _, err := execStellarCCIPOp(b, deps.StellarDeps, rrops.Initialize, rrops.InitializeInput{ ContractID: contractID, Owner: owner, }, withComponentIdempotencyKey[rrops.InitializeInput](in.ChainSelector)) return err }) }, )
var DeployRouter = cldf_ops.NewSequence( "stellar-deploy-router", SequenceVersion, "Deploys and initializes the Router Soroban contract with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeployRouterInput) (ComponentDeployOutput, error) { owner := defaultOwner(in.Owner, deps.DeployerAddress) if err := statReleaseWasm(in.WasmPath, "Router"); err != nil { return ComponentDeployOutput{}, err } if in.RmnProxy == "" { return ComponentDeployOutput{}, fmt.Errorf("rmnProxy is required: deploy RMN Proxy first") } return deployAndInitialize(b.GetContext(), b, deps, routerops.Deploy, stellarccip.RouterDatastoreRef(), in.ChainSelector, "router", in.WasmPath, owner, in.ExistingAddresses, func(contractID string) error { _, err := execStellarCCIPOp(b, deps.StellarDeps, routerops.Initialize, routerops.InitializeInput{ ContractID: contractID, Owner: owner, RmnProxy: in.RmnProxy, }, withComponentIdempotencyKey[routerops.InitializeInput](in.ChainSelector)) return err }) }, )
var DeploySiloedLockReleasePool = cldf_ops.NewSequence( "stellar-deploy-siloed-lock-release-pool", SequenceVersion, "Deploys and initializes the siloed lock-release token pool with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeploySiloedLockReleasePoolInput) (ComponentDeployOutput, error) { owner := defaultOwner(in.Owner, deps.DeployerAddress) if err := statReleaseWasm(in.WasmPath, "Siloed lock-release pool"); err != nil { return ComponentDeployOutput{}, err } return deployAndInitialize(b.GetContext(), b, deps, slrrops.Deploy, stellarccip.SiloedLockReleasePoolDatastoreRef(in.Qualifier), in.ChainSelector, componentSaltLabel("siloed-lock-release-pool", in.Qualifier), in.WasmPath, owner, in.ExistingAddresses, func(contractID string) error { _, err := execStellarCCIPOp(b, deps.StellarDeps, slrrops.Initialize, slrrops.InitializeInput{ ContractID: contractID, Owner: owner, Token: in.Token, TokenDecimals: in.TokenDecimals, Router: in.Router, RampRegistry: in.RampRegistry, RmnProxy: in.RmnProxy, }, withComponentInstanceIdempotencyKey[slrrops.InitializeInput](in.ChainSelector, in.Qualifier)) return err }) }, )
var DeployStellarCREForwarder = cldfops.NewSequence( "stellar-deploy-cre-forwarder", stellarops.ContractDeploymentVersion, "Deploy and bootstrap the CRE forwarder Soroban contract (deployer-owned until governance handoff)", func(b cldfops.Bundle, chains cldfchain.BlockChains, in DeployStellarCREForwarderInput) (seqcore.OnChainOutput, error) { ch, ok := chains.StellarChains()[in.ChainSelector] if !ok { return seqcore.OnChainOutput{}, fmt.Errorf("stellar chain %d not found in environment", in.ChainSelector) } qual := mcmsutil.QualifierStr(in.Qualifier) dep, err := stellarDeployerFromChain(ch) if err != nil { return seqcore.OnChainOutput{}, err } deps := stellardeps.FromDeployer(dep) if existing, found := FindExistingStellarCREForwarder(in.ExistingAddresses, in.ChainSelector, qual); found { return seqcore.OnChainOutput{Addresses: []datastore.AddressRef{StellarCREForwarderDatastoreRef(in.ChainSelector, qual, existing)}}, nil } wasm, err := ResolveCREForwarderWasmPath() if err != nil { return seqcore.OnChainOutput{}, err } depOut, err := cldfops.ExecuteOperation(b, creforwarderops.Deploy, deps, stellarops.DeployInput{ WasmPath: wasm, Salt: CREForwarderDeploySalt(in.ChainSelector, qual), }) if err != nil { return seqcore.OnChainOutput{}, fmt.Errorf("cre forwarder deploy: %w", err) } cid := depOut.Output.ContractID _, err = cldfops.ExecuteOperation(b, creforwarderops.Initialize, deps, creforwarderops.InitializeInput{ ContractID: cid, Owner: dep.SignerAddress(), }) if err != nil { return seqcore.OnChainOutput{}, fmt.Errorf("cre forwarder initialize: %w", err) } if cfg := in.InitialDONConfig; cfg != nil { _, err = cldfops.ExecuteOperation(b, creforwarderops.SetConfig, deps, creforwarderops.SetConfigInput{ ContractID: cid, DonID: cfg.DonID, ConfigVersion: cfg.ConfigVersion, F: cfg.F, Signers: cfg.Signers, }) if err != nil { return seqcore.OnChainOutput{}, fmt.Errorf("cre forwarder set_config: %w", err) } } for _, transmitter := range in.Transmitters { _, err = cldfops.ExecuteOperation(b, creforwarderops.AddForwarder, deps, creforwarderops.AddForwarderInput{ ContractID: cid, Forwarder: transmitter, }) if err != nil { return seqcore.OnChainOutput{}, fmt.Errorf("cre forwarder add_forwarder %s: %w", transmitter, err) } } return seqcore.OnChainOutput{Addresses: []datastore.AddressRef{StellarCREForwarderDatastoreRef(in.ChainSelector, qual, cid)}}, nil }, )
DeployStellarCREForwarder deploys and initializes the CRE forwarder owned by the deployer, then applies the optional bootstrap DON config and transmitter registry. Ownership handoff to the RBACTimelock is deliberately separate: reuse StellarTransferOwnershipViaMCMS and StellarAcceptOwnership with the emitted ref, exactly like the CCIP contracts. Reruns are idempotent: an already-deployed forwarder is left untouched (changes go through governance).
var DeployStellarMCMS = cldfops.NewSequence( "stellar-deploy-mcms", deploy.MCMSVersion, "Deploy three role-specific Soroban MCMS instances and a self-administered RBACTimelock", func(b cldfops.Bundle, chains cldfchain.BlockChains, in deploy.MCMSDeploymentConfigPerChainWithAddress) (seqcore.OnChainOutput, error) { ch, ok := chains.StellarChains()[in.ChainSelector] if !ok { return seqcore.OnChainOutput{}, fmt.Errorf("stellar chain %d not found in environment", in.ChainSelector) } qual := mcmsutil.QualifierStr(in.Qualifier) label := mcmsutil.QualifierStr(in.Label) dep, err := stellarDeployerFromChain(ch, stellardeployment.WithLogger(b.Logger)) if err != nil { return seqcore.OnChainOutput{}, err } deps := stellardeps.FromDeployer(dep) roleConfig := map[mcmsutil.MCMSRole]mcmstypes.Config{ mcmsutil.RoleProposer: in.Proposer, mcmsutil.RoleCanceller: in.Canceller, mcmsutil.RoleBypasser: in.Bypasser, } addrs := map[mcmsutil.MCMSRole]string{} fresh := map[mcmsutil.MCMSRole]bool{} var mcmsWasm string for _, role := range mcmsutil.AllMCMSRoles { existing, found, err := mcmsutil.FindExistingStellarMCMSByRole(in.ExistingAddresses, in.ChainSelector, qual, role) if err != nil { return seqcore.OnChainOutput{}, err } if found { addrs[role] = existing continue } if mcmsWasm == "" { if mcmsWasm, err = mcmsutil.ResolveMCMSWasmPath(); err != nil { return seqcore.OnChainOutput{}, err } } salt := mcmsutil.MCMSRoleDeploySalt(in.ChainSelector, qual, label, role) depOut, err := cldfops.ExecuteOperation(b, mcmsops.Deploy, deps, stellarops.DeployInput{WasmPath: mcmsWasm, Salt: salt}) if err != nil { return seqcore.OnChainOutput{}, fmt.Errorf("mcms deploy (%s): %w", role, err) } addrs[role] = depOut.Output.ContractID fresh[role] = true } tlID, haveTL := mcmsutil.FindExistingStellarTimelock(in.ExistingAddresses, in.ChainSelector, qual) if !haveTL { tlWasm, err := mcmsutil.ResolveTimelockWasmPath() if err != nil { return seqcore.OnChainOutput{}, err } tlSalt := mcmsutil.TimelockDeploySalt(in.ChainSelector, qual, label) tlOut, err := cldfops.ExecuteOperation(b, timelockops.Deploy, deps, stellarops.DeployInput{WasmPath: tlWasm, Salt: tlSalt}) if err != nil { return seqcore.OnChainOutput{}, fmt.Errorf("timelock deploy: %w", err) } tlID = tlOut.Output.ContractID minDelay, err := timelockMinDelay(in) if err != nil { return seqcore.OnChainOutput{}, err } _, err = cldfops.ExecuteOperation(b, timelockops.Initialize, deps, timelockops.InitializeInput{ ContractID: tlID, MinDelay: minDelay, Proposers: []string{addrs[mcmsutil.RoleProposer]}, Cancellers: []string{addrs[mcmsutil.RoleProposer], addrs[mcmsutil.RoleCanceller]}, Bypassers: []string{addrs[mcmsutil.RoleBypasser]}, }) if err != nil { return seqcore.OnChainOutput{}, fmt.Errorf("timelock initialize: %w", err) } } chainNetID := mcmsutil.ChainNetworkID(ch.NetworkPassphrase) for _, role := range mcmsutil.AllMCMSRoles { if !fresh[role] { continue } cfg := roleConfig[role] signerAddrs, signerGroups, gq, gp, _, err := mcmsutil.ConfigToStellarSetConfig(&cfg, true) if err != nil { return seqcore.OnChainOutput{}, fmt.Errorf("mcms config (%s): %w", role, err) } _, err = cldfops.ExecuteOperation(b, mcmsops.Initialize, deps, mcmsops.InitializeInput{ ContractID: addrs[role], Owner: tlID, ChainNetworkID: chainNetID, SignerAddresses: signerAddrs, SignerGroups: signerGroups, GroupQuorums: gq, GroupParents: gp, InstanceLabel: role.InstanceLabel(), }) if err != nil { return seqcore.OnChainOutput{}, fmt.Errorf("mcms initialize (%s): %w", role, err) } } refs := make([]datastore.AddressRef, 0, len(mcmsutil.AllMCMSRoles)+1) for _, role := range mcmsutil.AllMCMSRoles { ref, err := mcmsutil.StellarMCMSRoleDatastoreRef(in.ChainSelector, qual, role, addrs[role]) if err != nil { return seqcore.OnChainOutput{}, err } refs = append(refs, ref) } refs = append(refs, mcmsutil.StellarTimelockDatastoreRef(in.ChainSelector, qual, tlID)) return seqcore.OnChainOutput{Addresses: refs}, nil }, )
DeployStellarMCMS deploys three role-specific Soroban MCMS instances (proposer, canceller, bypasser) plus one self-administered RBACTimelock. Each MCMS gets an independent signer config, its own deterministic salt/address, an immutable instance label, and is owned by the timelock from initialization (no deployer ownership ever exists). Timelock roles follow the matrix: proposer MCMS holds PROPOSER and CANCELLER, canceller MCMS holds CANCELLER bypasser MCMS holds BYPASSER; execution is permissionless. Reruns are idempotent: an already-deployed role instance is left untouched (config changes go through governance).
var DeployTokenAdminRegistry = cldf_ops.NewSequence( "stellar-deploy-token-admin-registry", SequenceVersion, "Deploys and initializes the TokenAdminRegistry Soroban contract with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeployTokenAdminRegistryInput) (ComponentDeployOutput, error) { owner := defaultOwner(in.Owner, deps.DeployerAddress) if err := statReleaseWasm(in.WasmPath, "TokenAdminRegistry"); err != nil { return ComponentDeployOutput{}, err } return deployAndInitialize(b.GetContext(), b, deps, tarops.Deploy, stellarccip.TokenAdminRegistryDatastoreRef(), in.ChainSelector, "token-admin-registry", in.WasmPath, owner, in.ExistingAddresses, func(contractID string) error { _, err := execStellarCCIPOp(b, deps.StellarDeps, tarops.Initialize, tarops.InitializeInput{ ContractID: contractID, Owner: owner, }, withComponentIdempotencyKey[tarops.InitializeInput](in.ChainSelector)) return err }) }, )
var DeployTokenLockBox = cldf_ops.NewSequence( "stellar-deploy-token-lock-box", SequenceVersion, "Deploys and initializes the TokenLockBox Soroban contract with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeployTokenLockBoxInput) (ComponentDeployOutput, error) { owner := defaultOwner(in.Owner, deps.DeployerAddress) if err := statReleaseWasm(in.WasmPath, "Token lock box"); err != nil { return ComponentDeployOutput{}, err } return deployAndInitialize(b.GetContext(), b, deps, tlbops.Deploy, stellarccip.TokenLockBoxDatastoreRef(in.Qualifier), in.ChainSelector, componentSaltLabel("token-lock-box", in.Qualifier), in.WasmPath, owner, in.ExistingAddresses, func(contractID string) error { _, err := execStellarCCIPOp(b, deps.StellarDeps, tlbops.Initialize, tlbops.InitializeInput{ ContractID: contractID, Owner: owner, Token: in.Token, }, withComponentInstanceIdempotencyKey[tlbops.InitializeInput](in.ChainSelector, in.Qualifier)) return err }) }, )
var DeployVVR = cldf_ops.NewSequence( "stellar-deploy-versioned-verifier-resolver", SequenceVersion, "Deploys and initializes the VersionedVerifierResolver Soroban contract with skip-if-exists", func(b cldf_ops.Bundle, deps ComponentDeps, in DeployVVRInput) (ComponentDeployOutput, error) { owner := defaultOwner(in.Owner, deps.DeployerAddress) feeAggregator := in.FeeAggregator if feeAggregator == "" { feeAggregator = owner } if err := statReleaseWasm(in.WasmPath, "VVR"); err != nil { return ComponentDeployOutput{}, err } return deployAndInitialize(b.GetContext(), b, deps, vvrops.Deploy, stellarccip.VVRDatastoreRefWithQualifier(in.Qualifier), in.ChainSelector, componentSaltLabel("versioned-verifier-resolver", in.Qualifier), in.WasmPath, owner, in.ExistingAddresses, func(contractID string) error { _, err := execStellarCCIPOp(b, deps.StellarDeps, vvrops.Initialize, vvrops.InitializeInput{ ContractID: contractID, Owner: owner, FeeAggregator: feeAggregator, }, withComponentInstanceIdempotencyKey[vvrops.InitializeInput](in.ChainSelector, in.Qualifier)) return err }) }, )
var ErrBaseExecutionGasCostLowered = errors.New("baseExecutionGasCost would be lowered")
ErrBaseExecutionGasCostLowered guards the OnRamp BaseExecutionGasCost: the family default is one flat value, so a lane re-run would otherwise reset a destination whose gas cost was raised by hand and break execution there.
var ErrZeroAddressNotAllowed = errors.New("zero address not allowed")
ErrZeroAddressNotAllowed rejects zero-valued source OnRamp entries: the OffRamp matches an incoming message by hashing the onramp bytes it carries, so a zero entry can never match a real source and would leave the lane enabled but unusable. The Soroban OffRamp would accept it silently, so the guard lives here (EVM ZeroAddressNotAllowed ABI parity).
var FamilyDefaultAllowedFinality = func() uint32 { raw := finality.Config{WaitForFinality: true, WaitForSafe: true, BlockDepth: 1}.Raw() return binary.BigEndian.Uint32(raw[:]) }()
FamilyDefaultAllowedFinality is the raw allowed-finality value the stellar chain family declares (adapters.StellarChainFamilyAdapter.GetDefaultFinalityConfig: wait-for-finality | safe-flag | depth 1; keep in sync with it). EVM deploy defaults apply the same kind of family default to executors (ccv deploy_defaults.go defaultFinalityConfig), so the Executor deploys permissive of every requested-finality mode the lane flows send (0, block depth, safe flag). The default AllowedFinalityConfig of 0 (wait-for-finality only) makes the OnRamp's get_fee path — which cross-calls Executor::get_fee — reject depth/safe requests with InvalidRequestedFinality (#315).
var FinalizeStellarDeployMCMS = cldfops.NewSequence( "stellar-finalize-deploy-mcms", deploy.MCMSVersion, "Verify no residual deployer authority and timelock ownership of each MCMS after deploy", func(b cldfops.Bundle, chains cldfchain.BlockChains, in deploy.MCMSDeploymentConfigPerChainWithAddress) (seqcore.OnChainOutput, error) { ch, ok := chains.StellarChains()[in.ChainSelector] if !ok { return seqcore.OnChainOutput{}, fmt.Errorf("stellar chain %d not found in environment", in.ChainSelector) } dep, err := stellarDeployerFromChain(ch, stellardeployment.WithLogger(b.Logger)) if err != nil { return seqcore.OnChainOutput{}, err } if err := VerifyStellarMCMSGovernance(b.GetContext(), stellardeps.FromDeployer(dep), in.ExistingAddresses, in.ChainSelector, mcmsutil.QualifierStr(in.Qualifier), dep.SignerAddress()); err != nil { return seqcore.OnChainOutput{}, fmt.Errorf("mcms deployment finalize: %w", err) } return seqcore.OnChainOutput{}, nil }, )
FinalizeStellarDeployMCMS verifies the post-deployment authority invariants and fails the deployment if any residual deployer authority remains or the timelock does not own each MCMS.
var GrantAdminRoleToTimelockStellar = cldfops.NewSequence( "stellar-grant-admin-role-to-timelock", deploy.MCMSVersion, "No-op: Stellar has no CallProxy executor grant step (EVM parity stub)", func(b cldfops.Bundle, chains cldfchain.BlockChains, in deploy.GrantAdminRoleToTimelockConfigPerChainWithSelector) (seqcore.OnChainOutput, error) { return seqcore.OnChainOutput{}, nil }, )
GrantAdminRoleToTimelockStellar is a no-op (no Stellar CallProxy; timelock executors can be added in a follow-up).
var SequenceVersion = semver.MustParse("2.0.0")
SequenceVersion is the semver for Stellar CCIP deployment sequences aligned with deployment/v2_0_0 adapters.
var StellarAcceptOwnership = cldfops.NewSequence( "stellar-seq-accept-ownership", deploy.MCMSVersion, "Accepts Soroban contract ownership via MCMS or deployer", func(b cldfops.Bundle, chains cldfchain.BlockChains, in StellarTransferOwnershipInput) (output seqcore.OnChainOutput, err error) { ch, ok := chains.StellarChains()[in.ChainSelector] if !ok { return output, fmt.Errorf("stellar chain %d not found in environment", in.ChainSelector) } dep, err := stellardeployment.NewDeployerFromChain(ch, stellardeployment.WithLogger(b.Logger)) if err != nil { return output, err } deps := stellardeps.FromDeployer(dep) deployerAddr := dep.SignerAddress() ctx := b.GetContext() for _, ref := range in.ContractRef { cid, err := ownership.NormalizeContractAddress(ref.Address) if err != nil { return output, fmt.Errorf("accept ownership on chain %d: %w", in.ChainSelector, err) } ref.Address = cid owner, err := ownership.ContractOwner(ctx, deps, ref) if err != nil { return output, fmt.Errorf("read owner %s: %w", ref.Address, err) } if owner == in.ProposedOwner { continue } var wo evmcontract.WriteOutput switch { case in.ProposedOwner == deployerAddr: if err := ownership.ExecuteAcceptOwnership(b, deps, ref); err != nil { return output, fmt.Errorf("accept ownership %s: %w", ref.Address, err) } wo = evmcontract.WriteOutput{ ChainSelector: in.ChainSelector, ExecInfo: &evmcontract.ExecInfo{Hash: "stellar-direct-accept"}, } case in.ProposedOwner == in.GovernanceAddr: data, err := mcmsutil.EncodeSorobanMCMSInvokePayload("accept_ownership", nil) if err != nil { return output, err } wo = evmcontract.WriteOutput{ ChainSelector: in.ChainSelector, Tx: mcmstypes.Transaction{ OperationMetadata: mcmstypes.OperationMetadata{ ContractType: string(ref.Type), }, To: ref.Address, Data: data, AdditionalFields: mcmsutil.StellarMCMSTxAdditionalFields(), }, } default: return output, fmt.Errorf( "accept routing: proposed owner %q must be deployer %q or governance %q for %s", in.ProposedOwner, deployerAddr, in.GovernanceAddr, ref.Address, ) } batchOp, err := evmcontract.NewBatchOperationFromWrites([]evmcontract.WriteOutput{wo}) if err != nil { return output, err } output.BatchOps = append(output.BatchOps, batchOp) } return output, nil }, )
StellarAcceptOwnership mirrors EVM OpAcceptOwnership: deployer accepts directly; when the pending owner is the timelock (governance), emit an MCMS batch op — no timelock==MCMS check.
var StellarActivateRMN = cldfops.NewSequence( "stellar-seq-activate-rmn", deploy.MCMSVersion, "Grants curse admins and transfers Stellar RMN Remote ownership to the governance timelock", func(b cldfops.Bundle, chains cldfchain.BlockChains, in StellarActivateRMNInput) (output seqcore.OnChainOutput, err error) { govQual := in.GovernanceQualifier if govQual == "" { govQual = cciputils.RMNTimelockQualifier } fastQual := in.FastCurseQualifier if fastQual == "" { fastQual = cciputils.UltraFastCurseMCMSQualifier } govTL, ok := mcmsutil.FindExistingStellarTimelock(in.ExistingAddresses, in.ChainSelector, govQual) if !ok { return output, fmt.Errorf("no RBACTimelock deployed for qualifier %q on chain %d; deploy the %s MCMS stack first", govQual, in.ChainSelector, govQual) } fastTL, ok := mcmsutil.FindExistingStellarTimelock(in.ExistingAddresses, in.ChainSelector, fastQual) if !ok { return output, fmt.Errorf("no RBACTimelock deployed for qualifier %q on chain %d; deploy the fast-curse MCMS stack first", fastQual, in.ChainSelector) } rmnRecordedRef, opsRef, err := resolveRMNRemoteRefs(in) if err != nil { return output, err } ch, ok := chains.StellarChains()[in.ChainSelector] if !ok { return output, fmt.Errorf("stellar chain %d not found in environment", in.ChainSelector) } dep, err := stellardeployment.NewDeployerFromChain(ch, stellardeployment.WithLogger(b.Logger)) if err != nil { return output, err } deps := stellardeps.FromDeployer(dep) deployerAddr := dep.SignerAddress() ctx := b.GetContext() owner, err := ownership.ContractOwner(ctx, deps, opsRef) if err != nil { return output, fmt.Errorf("read RMN Remote owner: %w", err) } currentAdmins, err := ownership.CurseAdmins(ctx, deps, opsRef) if err != nil { return output, fmt.Errorf("read RMN Remote curse admins: %w", err) } added := dedupeStrkeys(append([]string{fastTL}, in.ExtraCurseAdmins...)) if slices.Contains(added, owner) { b.Logger.Warnw("RMN Remote owner is being added to the curse-admin list; on chain nothing filters the owner out of the stored list", "owner", owner) } var toAdd []string for _, addr := range added { if !slices.Contains(currentAdmins, addr) { toAdd = append(toAdd, addr) } } var batchOps []mcmstypes.BatchOperation if len(toAdd) > 0 { route, err := routeCurseAdminGrant(owner, deployerAddr, govTL) if err != nil { return output, err } switch route { case curseAdminGrantDirect: if err := ownership.ExecuteApplyCurseAdminUpdates(b, deps, opsRef, toAdd, nil); err != nil { return output, fmt.Errorf("apply curse admin updates: %w", err) } case curseAdminGrantPropose: data, err := mcmsutil.EncodeSorobanMCMSInvokePayload("apply_curse_admin_updates", []xdr.ScVal{ scval.AddressSliceToScVal(toAdd), scval.AddressSliceToScVal(nil), }) if err != nil { return output, err } batchOps = append(batchOps, mcmsutil.MCMSInvokeTx(opsRef, data)) } } if len(in.SubjectsToMigrate) > 0 { if owner != deployerAddr { return output, fmt.Errorf( "cannot migrate curse subjects: RMN Remote owner %q is not the deployer %q; migration must happen before ownership transfer", owner, deployerAddr, ) } for _, subject := range in.SubjectsToMigrate { if _, err := cldfops.ExecuteOperation(b, rmnremoteops.Curse, deps, rmnremoteops.CurseInput{ ContractID: opsRef.Address, Caller: deployerAddr, Subjects: [][16]byte{subject}, }); err != nil { return output, fmt.Errorf("migrate curse subject %x: %w", subject, err) } } } transferReport, err := cldfops.ExecuteSequence(b, StellarTransferOwnershipViaMCMS, chains, StellarTransferOwnershipInput{ TransferOwnershipPerChainInput: deploy.TransferOwnershipPerChainInput{ ChainSelector: in.ChainSelector, ContractRef: []datastore.AddressRef{opsRef}, ProposedOwner: govTL, }, GovernanceAddr: govTL, }) if err != nil { return output, fmt.Errorf("transfer RMN Remote ownership: %w", err) } batchOps = append(batchOps, transferReport.Output.BatchOps...) output.BatchOps = batchOps output.Addresses = append(output.Addresses, rmnRecordedRef) return output, nil }, )
StellarActivateRMN activates an existing RMN Remote for MCMS governance.
var StellarApplyDestChainConfig = cldfops.NewSequence( "stellar-apply-dest-chain-config", stellarops.ContractDeploymentVersion, "Apply dest chain config updates on Stellar FeeQuoter", func(b cldfops.Bundle, chains cldfchain.BlockChains, in StellarApplyDestChainConfigInput) (seqcore.OnChainOutput, error) { ch, ok := chains.StellarChains()[in.Selector] if !ok { return seqcore.OnChainOutput{}, fmt.Errorf("stellar chain %d not found", in.Selector) } dep, err := stellardeployment.NewDeployerFromChain(ch, stellardeployment.WithLogger(b.Logger)) if err != nil { return seqcore.OnChainOutput{}, err } deps := stellardeps.FromDeployer(dep) var cfgArgs []fqbindings.DestChainConfigArgs for destSel, cfg := range in.Settings { cfgArgs = append(cfgArgs, fqbindings.DestChainConfigArgs{ DestChainSelector: destSel, Config: fqbindings.DestChainConfig{ IsEnabled: cfg.IsEnabled, MaxDataBytes: cfg.MaxDataBytes, MaxPerMsgGasLimit: cfg.MaxPerMsgGasLimit, DestGasOverhead: cfg.DestGasOverhead, DestGasPerPayloadByte: uint32(cfg.DestGasPerPayloadByteBase), DefaultTokenFeeUsd: uint32(cfg.DefaultTokenFeeUSDCents), DefaultTokenDestGas: cfg.DefaultTokenDestGasOverhead, DefaultTxGasLimit: cfg.DefaultTxGasLimit, NetworkFeeUsdCents: uint32(cfg.NetworkFeeUSDCents), LinkPremiumPercent: uint32(cfg.V2Params.LinkFeeMultiplierPercent), }, }) } if len(cfgArgs) == 0 { return seqcore.OnChainOutput{}, nil } _, err = cldfops.ExecuteOperation(b, fqops.ApplyDestChainConfigs, deps, fqops.ApplyDestChainConfigsInput{ ContractID: in.FQContractID, Configs: cfgArgs, }) if err != nil { return seqcore.OnChainOutput{}, fmt.Errorf("apply dest chain configs on chain %d: %w", in.Selector, err) } return seqcore.OnChainOutput{}, nil }, )
StellarApplyDestChainConfig applies destination chain configs on the Stellar FeeQuoter.
var StellarConfigureChainForLanes = cldf_ops.NewSequence( "stellar-configure-chain-for-lanes", SequenceVersion, "Applies full lane wiring (OffRamp, OnRamp, Executor, FeeQuoter, Router) and committee verifier signature quorums on Stellar for each remote lane", func(b cldf_ops.Bundle, chains cldf_chain.BlockChains, input ccvadapters.ConfigureChainForLanesInput) (seq_core.OnChainOutput, error) { ch, ok := chains.StellarChains()[input.ChainSelector] if !ok { return seq_core.OnChainOutput{}, fmt.Errorf("stellar chain not found for selector %d", input.ChainSelector) } dep, err := stellardeployment.NewDeployerFromChain(ch, stellardeployment.WithLogger(b.Logger)) if err != nil { return seq_core.OnChainOutput{}, fmt.Errorf("stellar deployer from chain: %w", err) } return runStellarConfigureChainForLanes(b, stellardeps.FromDeployer(dep), input) }, )
StellarConfigureChainForLanes is the Stellar leg of the shared ConfigureChainsForLanesFromTopology changeset. Per remote chain it wires the full lane, EVM-sequence style: OffRamp source chain config, OnRamp dest chain config, Executor dest chain config, FeeQuoter dest chain config, and Router ramp entries. Every write is guarded by a read of the current on-chain state, so re-runs are idempotent: zero/empty/nil input values keep the current on-chain value, and a remote whose desired state already matches emits no write at all. Writes are ordered infra-first (OffRamp → OnRamp → Executor → FeeQuoter) with the Router write last, so the Router never routes to a contract that is not yet configured.
Guards, EVM-parity wording where one exists:
- AllowOnrampOverride=false refuses to replace a Router onRamp entry that already points at a different OnRamp (use a migration to swap OnRamps).
- AllowLoweringBaseExecutionGasCost=false refuses to lower the OnRamp BaseExecutionGasCost below its current on-chain value (ErrBaseExecutionGasCostLowered).
- A remote whose SkipExecutorConfig is set gets the no-execution sentinel (stellarutil.NoExecutionExecutorStrkey, = Rust NO_EXECUTION_STRKEY) as the OnRamp DefaultExecutor and no Executor write at all — EVM NoExecutionAddress parity.
- FeeQuoter configs already enabled on chain are left alone unless the input's OverrideExistingConfig is set, protecting deploy-time pricing.
- Source OnRamp entries that are empty or all-zero are rejected (ErrZeroAddressNotAllowed) instead of being written verbatim.
Deliberately NOT done here:
- FeeQuoter gas/token price updates (UpdatePrices): deploy seeds them and the offchain price updater owns them afterwards (Solana precedent).
- CommitteeVerifier remote chain configs/allowlists/resolver routing: coalesced defaults here would clobber deploy-time values. The committee verifier writes this sequence does make are the signature quorums (applyCommitteeVerifierSignatureQuorums) and the verifier-global allowed-finality config (applyCommitteeVerifierAllowedFinality, EVM ConfigureChainForLanes parity).
- RampRegistry sync: not reachable from ConfigureChainForLanesInput; would need a FamilyExtras change in chainlink-ccip (noted as a follow-up).
Committee verifier signature quorums run at lane-configuration time, not deploy time, on purpose: since the chainlink-ccv 2026-06-26 signing-key-sync cutover, topology enrichment skips families whose bootstrappers push signing keys to JD on connect, so signer addresses are not available to RunStellarCCIPFullDeploy. The changeset resolves them from JD (falling back to topology signer addresses) and passes them in via CommitteeVerifierRemoteChainConfig.SignatureConfig.
Everything executes as plain EOA transactions; Soroban has no MCMS, so the output carries no BatchOps.
var StellarCurse = cldfops.NewSequence( "stellar-curse-rmn-remote", stellarops.ContractDeploymentVersion, "Curse subjects on Stellar RMN Remote", func(b cldfops.Bundle, chains cldfchain.BlockChains, in StellarCurseInput) (seqcore.OnChainOutput, error) { ch, ok := chains.StellarChains()[in.ChainSelector] if !ok { return seqcore.OnChainOutput{}, fmt.Errorf("stellar chain %d not found", in.ChainSelector) } dep, err := stellardeployment.NewDeployerFromChain(ch, stellardeployment.WithLogger(b.Logger)) if err != nil { return seqcore.OnChainOutput{}, err } deps := stellardeps.FromDeployer(dep) deployerAddr := dep.SignerAddress() caller, effectiveQualifier, err := authorizeCurseCaller(in, deployerAddr) if err != nil { return seqcore.OnChainOutput{}, fmt.Errorf( "curse on chain %d: %w; grant curse-admin access with apply_curse_admin_updates, or set the MCMS qualifier to a stack that is authorized", in.ChainSelector, err, ) } if caller == deployerAddr { if _, err := cldfops.ExecuteOperation(b, rmnremoteops.Curse, deps, rmnremoteops.CurseInput{ ContractID: in.RMNContractID, Caller: caller, Subjects: in.Subjects, }); err != nil { return seqcore.OnChainOutput{}, fmt.Errorf("curse on chain %d: %w", in.ChainSelector, err) } return directExecOutput(b, in.ChainSelector, in.RMNContractID, "stellar-direct-curse") } if in.MCMSQualifier == "" { return seqcore.OnChainOutput{}, fmt.Errorf( "curse on chain %d cannot run directly (deployer %s is neither the owner %s nor a curse admin of RMN Remote %s) and no MCMS qualifier was supplied to route a proposal; set the qualifier %q in the MCMS config to propose via its timelock %s", in.ChainSelector, deployerAddr, in.Owner, in.RMNContractID, effectiveQualifier, caller, ) } data, err := mcmsutil.EncodeSorobanMCMSInvokePayload("curse", []xdr.ScVal{ scval.AddressToScVal(caller), scval.Bytes16SliceToScVal(in.Subjects), }) if err != nil { return seqcore.OnChainOutput{}, err } b.Logger.Infow("proposing stellar curse via MCMS", "qualifier", in.MCMSQualifier, "timelock", caller, "chainSelector", in.ChainSelector) return seqcore.OnChainOutput{ BatchOps: []mcmstypes.BatchOperation{{ ChainSelector: mcmstypes.ChainSelector(in.ChainSelector), Transactions: []mcmstypes.Transaction{{ OperationMetadata: mcmstypes.OperationMetadata{ContractType: rmnremoteops.ContractType}, To: in.RMNContractID, Data: data, AdditionalFields: mcmsutil.StellarMCMSTxAdditionalFields(), }}, }}, }, nil }, )
StellarCurse curses subjects on a Stellar RMN Remote contract: directly when the deployer is authorized, otherwise as an MCMS proposal whose caller is the timelock of the qualifier chosen for this run.
var StellarDeployChainContracts = cldf_ops.NewSequence( "stellar-deploy-chain-contracts", SequenceVersion, "Deploys Stellar Soroban CCIP contracts via CLDF Stellar chain (adapter path); uses pre-stashed offchain topology from CCV pre-deploy when present.", func(b cldf_ops.Bundle, chains cldf_chain.BlockChains, input ccvadapters.DeployChainContractsInput) (DeployChainContractsOutput, error) { ch, ok := chains.StellarChains()[input.ChainSelector] if !ok { return DeployChainContractsOutput{}, fmt.Errorf("stellar chain not found for selector %d", input.ChainSelector) } dep, err := stellardeployment.NewDeployerFromChain(ch, stellardeployment.WithLogger(b.Logger)) if err != nil { return DeployChainContractsOutput{}, fmt.Errorf("stellar deployer from chain: %w", err) } deps := stellardeps.FromDeployer(dep) lg := stellarccip.DefaultStellarDeployZerolog() host, err := stellarccip.NewCLDFStellarCCIPDevenvHost(ch, lg, dep) if err != nil { return DeployChainContractsOutput{}, fmt.Errorf("stellar devenv host: %w", err) } offTopo, _ := TakeStellarDeployOffchainTopologyForSelector(input.ChainSelector) output, err := RunStellarCCIPFullDeploy(b.GetContext(), b, deps, host, offTopo, DeployStellarCCIPInnerInput{ ChainSelector: input.ChainSelector, AllSelectors: allSelectorsFromBlockChains(chains), ExistingAddresses: input.ExistingAddresses, }) if err != nil { return DeployChainContractsOutput{}, err } return DeployChainContractsOutput{OnChainOutput: output}, nil }, )
StellarDeployChainContracts is the adapter sequence: DEP is BlockChains. It builds StellarDeps and a stellarccip.CCIPDevenvHost from the CLDF Stellar chain entry. CCV stashes offchain topology in RegisterStellarDeployOffchainTopologyForSelector during PreDeployContractsForSelector; this sequence TakeStellarDeployOffchainTopologyForSelector before RunStellarCCIPFullDeploy. A missing stash entry is tolerated: the stash is optional here, and signer quorums are applied at lane-configuration time rather than during the deploy.
var StellarSetCommitteeVerifierFinality = cldfops.NewSequence( "stellar-seq-set-committee-verifier-finality", stellarops.ContractDeploymentVersion, "Sets the Stellar Committee Verifier allowed finality, directly or via MCMS", func(b cldfops.Bundle, chains cldfchain.BlockChains, in StellarSetCommitteeVerifierFinalityInput) (output seqcore.OnChainOutput, err error) { if err := in.AllowedFinality.Validate(); err != nil { return output, err } raw := in.AllowedFinality.Raw() allowed := binary.BigEndian.Uint32(raw[:]) ch, ok := chains.StellarChains()[in.ChainSelector] if !ok { return output, fmt.Errorf("stellar chain %d not found in environment", in.ChainSelector) } dep, err := stellardeployment.NewDeployerFromChain(ch, stellardeployment.WithLogger(b.Logger)) if err != nil { return output, fmt.Errorf("stellar deployer from chain: %w", err) } deps := stellardeps.FromDeployer(dep) owner, err := readContractOwner(b.GetContext(), deps, in.CommitteeVerifier) if err != nil { return output, fmt.Errorf("read owner of committee verifier %s: %w", in.CommitteeVerifier.Address, err) } switch owner { case dep.SignerAddress(): _, err := cldfops.ExecuteOperation(b, cvops.SetAllowedFinalityConfig, deps, cvops.SetAllowedFinalityConfigInput{ ContractID: in.CommitteeVerifier.Address, AllowedFinalityConfig: allowed, }) return output, err case in.Timelock: data, err := mcmsutil.EncodeSorobanMCMSInvokePayload("set_allowed_finality_config", []xdr.ScVal{scval.Uint32ToScVal(allowed)}) if err != nil { return output, fmt.Errorf("encode set_allowed_finality_config payload: %w", err) } output.BatchOps = []mcmstypes.BatchOperation{mcmsutil.MCMSInvokeTx(in.CommitteeVerifier, data)} return output, nil default: return output, fmt.Errorf("committee verifier %s owner %q is neither the deployer %q nor the timelock %q", in.CommitteeVerifier.Address, owner, dep.SignerAddress(), in.Timelock) } }, )
StellarSetCommitteeVerifierFinality calls set_allowed_finality_config directly when the deployer owns the verifier, and emits it as an MCMS batch op when the timelock does.
var StellarSetTokenTransferFee = cldfops.NewSequence( "stellar-set-token-transfer-fee", stellarops.ContractDeploymentVersion, "Set per-token transfer fees on Stellar FeeQuoter", func(b cldfops.Bundle, chains cldfchain.BlockChains, in StellarSetTokenTransferFeeInput) (seqcore.OnChainOutput, error) { ch, ok := chains.StellarChains()[in.Selector] if !ok { return seqcore.OnChainOutput{}, fmt.Errorf("stellar chain %d not found", in.Selector) } dep, err := stellardeployment.NewDeployerFromChain(ch, stellardeployment.WithLogger(b.Logger)) if err != nil { return seqcore.OnChainOutput{}, err } deps := stellardeps.FromDeployer(dep) var cfgArgs []fqbindings.TokenFeeConfigArgs for destSel, tokenFees := range in.Settings { for token, feeArgs := range tokenFees { if feeArgs == nil { continue } cfgArgs = append(cfgArgs, fqbindings.TokenFeeConfigArgs{ DestChainSelector: destSel, Token: token, Config: fqbindings.TokenTransferFeeConfig{ DestBytesOverhead: feeArgs.DestBytesOverhead, DestGasOverhead: feeArgs.DestGasOverhead, FeeUsdCents: feeArgs.MinFeeUSDCents, IsEnabled: feeArgs.IsEnabled, }, }) } } if len(cfgArgs) == 0 { return seqcore.OnChainOutput{}, nil } _, err = cldfops.ExecuteOperation(b, fqops.ApplyTokenFeeConfigs, deps, fqops.ApplyTokenFeeConfigsInput{ ContractID: in.FQContractID, AddConfigs: cfgArgs, }) if err != nil { return seqcore.OnChainOutput{}, fmt.Errorf("apply token fee configs on chain %d: %w", in.Selector, err) } return seqcore.OnChainOutput{}, nil }, )
StellarSetTokenTransferFee applies per-token transfer fee configs on the Stellar FeeQuoter.
var StellarTransferOwnershipViaMCMS = cldfops.NewSequence( "stellar-seq-transfer-ownership-via-mcms", deploy.MCMSVersion, "Transfers Soroban contract ownership via MCMS or deployer", func(b cldfops.Bundle, chains cldfchain.BlockChains, in StellarTransferOwnershipInput) (output seqcore.OnChainOutput, err error) { ch, ok := chains.StellarChains()[in.ChainSelector] if !ok { return output, fmt.Errorf("stellar chain %d not found in environment", in.ChainSelector) } dep, err := stellardeployment.NewDeployerFromChain(ch, stellardeployment.WithLogger(b.Logger)) if err != nil { return output, err } deps := stellardeps.FromDeployer(dep) deployerAddr := dep.SignerAddress() ctx := b.GetContext() for _, ref := range in.ContractRef { cid, err := ownership.NormalizeContractAddress(ref.Address) if err != nil { return output, fmt.Errorf("transfer ownership on chain %d: %w", in.ChainSelector, err) } ref.Address = cid owner, err := ownership.ContractOwner(ctx, deps, ref) if err != nil { return output, fmt.Errorf("read owner %s: %w", ref.Address, err) } if owner == in.ProposedOwner { continue } var wo evmcontract.WriteOutput switch { case owner == deployerAddr: if err := ownership.ExecuteTransferOwnership(b, deps, ref, in.ProposedOwner); err != nil { return output, fmt.Errorf("transfer ownership %s: %w", ref.Address, err) } wo = evmcontract.WriteOutput{ ChainSelector: in.ChainSelector, ExecInfo: &evmcontract.ExecInfo{Hash: "stellar-direct-transfer"}, } case owner == in.GovernanceAddr: data, err := mcmsutil.EncodeSorobanMCMSInvokePayload("transfer_ownership", []xdr.ScVal{scval.AddressToScVal(in.ProposedOwner)}) if err != nil { return output, err } wo = evmcontract.WriteOutput{ ChainSelector: in.ChainSelector, Tx: mcmstypes.Transaction{ OperationMetadata: mcmstypes.OperationMetadata{ ContractType: string(ref.Type), }, To: ref.Address, Data: data, AdditionalFields: mcmsutil.StellarMCMSTxAdditionalFields(), }, } default: return output, fmt.Errorf( "contract %s owner %q is neither deployer %q nor governance %q", ref.Address, owner, deployerAddr, in.GovernanceAddr, ) } batchOp, err := evmcontract.NewBatchOperationFromWrites([]evmcontract.WriteOutput{wo}) if err != nil { return output, err } output.BatchOps = append(output.BatchOps, batchOp) } return output, nil }, )
StellarTransferOwnershipViaMCMS mirrors EVM chains/evm/.../operations/mcms OpTransferOwnership: deployer sends the tx directly; when the current owner is the timelock (governance), emit an MCMS batch op (MCMS is the transport, not compared to timelock).
var StellarUncurse = cldfops.NewSequence( "stellar-uncurse-rmn-remote", stellarops.ContractDeploymentVersion, "Uncurses subjects on Stellar RMN Remote", func(b cldfops.Bundle, chains cldfchain.BlockChains, in StellarCurseInput) (seqcore.OnChainOutput, error) { ch, ok := chains.StellarChains()[in.ChainSelector] if !ok { return seqcore.OnChainOutput{}, fmt.Errorf("stellar chain %d not found", in.ChainSelector) } dep, err := stellardeployment.NewDeployerFromChain(ch, stellardeployment.WithLogger(b.Logger)) if err != nil { return seqcore.OnChainOutput{}, err } deps := stellardeps.FromDeployer(dep) deployerAddr := dep.SignerAddress() if in.Owner == "" { return seqcore.OnChainOutput{}, fmt.Errorf( "uncurse on chain %d: RMN Remote owner is unknown and uncurse is owner-only, so it cannot be routed; "+ "the curse adapter reads the owner (failing loudly on RPC errors), so an empty Owner means the input bypassed it — populate Owner in the input", in.ChainSelector) } if deployerAddr == in.Owner { if _, err := cldfops.ExecuteOperation(b, rmnremoteops.Uncurse, deps, rmnremoteops.UncurseInput{ ContractID: in.RMNContractID, Subjects: in.Subjects, }); err != nil { return seqcore.OnChainOutput{}, fmt.Errorf("uncurse on chain %d: %w", in.ChainSelector, err) } return directExecOutput(b, in.ChainSelector, in.RMNContractID, "stellar-direct-uncurse") } caller, effectiveQualifier, err := authorizeCurseCaller(in, deployerAddr) if err != nil { return seqcore.OnChainOutput{}, fmt.Errorf( "uncurse on chain %d: %w; uncurse is owner-only — curse-admin grants cannot authorize it; transfer RMN Remote ownership, or route via the owner timelock's qualifier", in.ChainSelector, err, ) } if caller != in.Owner { return seqcore.OnChainOutput{}, fmt.Errorf( "uncurse on chain %d via qualifier %q is not possible: uncurse is owner-only and fast-uncurse is not supported; route it through the owner timelock %s", in.ChainSelector, in.MCMSQualifier, in.Owner) } if in.MCMSQualifier == "" { return seqcore.OnChainOutput{}, fmt.Errorf( "uncurse on chain %d is owner-only and the deployer %s is not the owner %s, so it must run as a proposal via the owner timelock %s; set the MCMS qualifier %q in the MCMS config", in.ChainSelector, deployerAddr, in.Owner, caller, effectiveQualifier, ) } data, err := mcmsutil.EncodeSorobanMCMSInvokePayload("uncurse", []xdr.ScVal{ scval.Bytes16SliceToScVal(in.Subjects), }) if err != nil { return seqcore.OnChainOutput{}, err } b.Logger.Infow("proposing stellar uncurse via MCMS", "qualifier", in.MCMSQualifier, "timelock", caller, "chainSelector", in.ChainSelector) return seqcore.OnChainOutput{ BatchOps: []mcmstypes.BatchOperation{{ ChainSelector: mcmstypes.ChainSelector(in.ChainSelector), Transactions: []mcmstypes.Transaction{{ OperationMetadata: mcmstypes.OperationMetadata{ContractType: rmnremoteops.ContractType}, To: in.RMNContractID, Data: data, AdditionalFields: mcmsutil.StellarMCMSTxAdditionalFields(), }}, }}, }, nil }, )
StellarUncurse uncurses subjects on a Stellar RMN Remote contract. Uncurse is owner-only on chain and takes no caller argument, so it always routes through the RMNMCMS stack; a non-owner qualifier (including UltraFastCurse) is a build-time error: fast-uncurse is not supported.
var StellarUpgradeContractsViaMCMS = cldfops.NewSequence( "stellar-seq-upgrade-contracts-via-mcms", deploy.MCMSVersion, "Upgrades Soroban CCIP contracts in place via MCMS or deployer", func(b cldfops.Bundle, chains cldfchain.BlockChains, in StellarUpgradeContractsInput) (output seqcore.OnChainOutput, err error) { ch, ok := chains.StellarChains()[in.ChainSelector] if !ok { return output, fmt.Errorf("stellar chain %d not found in environment", in.ChainSelector) } dep, err := stellardeployment.NewDeployerFromChain(ch, stellardeployment.WithLogger(b.Logger)) if err != nil { return output, err } deps := stellardeps.FromDeployer(dep) deployerAddr := dep.SignerAddress() ctx := b.GetContext() for _, ref := range in.ContractRef { wo, err := upgradeContractRef(ctx, b, deps, dep, deployerAddr, in.GovernanceAddr, in.ChainSelector, ref) if err != nil { return output, err } batchOp, err := evmcontract.NewBatchOperationFromWrites([]evmcontract.WriteOutput{wo}) if err != nil { return output, err } output.BatchOps = append(output.BatchOps, batchOp) } return output, nil }, )
StellarUpgradeContractsViaMCMS upgrades Soroban CCIP contracts in place. For each ref it uploads the freshly-compiled release WASM for that contract type, reads the current owner, and either executes the upgrade directly (EOA owner) or emits an MCMS proposal transaction (governance/timelock owner). Mirrors StellarTransferOwnershipViaMCMS; there is no EVM OpUpgrade to mirror (EVM upgrades by deploy-new-and-repoint, while Soroban swaps the Wasm hash in place via update_current_contract_wasm).
var UpdateStellarMCMSConfig = cldfops.NewSequence( "stellar-update-mcms-config", deploy.MCMSVersion, "Updates signer config on Stellar MCMS directly or through its timelock", func(b cldfops.Bundle, chains cldfchain.BlockChains, in deploy.UpdateMCMSConfigInputPerChainWithSelector) (output seqcore.OnChainOutput, err error) { ch, ok := chains.StellarChains()[in.ChainSelector] if !ok { return output, fmt.Errorf("stellar chain %d not found in environment", in.ChainSelector) } dep, err := stellarDeployerFromChain(ch, stellardeployment.WithLogger(b.Logger)) if err != nil { return output, err } deps := stellardeps.FromDeployer(dep) signerAddrs, signerGroups, gq, gp, _, err := mcmsutil.ConfigToStellarSetConfig(&in.MCMConfig, true) if err != nil { return output, err } cfg := mcmsops.SetConfigInput{ SignerAddresses: signerAddrs, SignerGroups: signerGroups, GroupQuorums: gq, GroupParents: gp, ClearRoot: true, } for _, ref := range in.MCMContracts { if ref.Address == "" { continue } governanceAddr, _ := mcmsutil.FindExistingStellarTimelock(in.ExistingAddresses, in.ChainSelector, ref.Qualifier) wo, err := setStellarMCMSConfigForRef(b.GetContext(), b, deps, dep.SignerAddress(), governanceAddr, in.ChainSelector, ref, cfg) if err != nil { return output, err } if wo.Executed() { continue } batchOp, err := evmcontract.NewBatchOperationFromWrites([]evmcontract.WriteOutput{wo}) if err != nil { return output, err } output.BatchOps = append(output.BatchOps, batchOp) } return output, nil }, )
UpdateStellarMCMSConfig sets the signer config on each listed MCMS. Each MCMS is updated directly when the deployer owns it, or through an MCMS batch op when the timelock of its own stack (same qualifier) owns it.
Functions ¶
func ApplyStellarFeeAggregator ¶
func ApplyStellarFeeAggregator(b cldfops.Bundle, chains cldfchain.BlockChains, env cldf.Environment, in fees.FeeAggregatorForChain) (seqcore.OnChainOutput, error)
ApplyStellarFeeAggregator updates the fee aggregator on Stellar contracts that hold CCIP fee funds. When in.Contracts is empty, OnRamp, Versioned Verifier Resolver, and Committee Verifier are updated.
func ApplyStellarWithdrawFeeTokens ¶
func ApplyStellarWithdrawFeeTokens(b cldfops.Bundle, chains cldfchain.BlockChains, env cldf.Environment, in fees.WithdrawFeeTokensForChain) (output seqcore.OnChainOutput, err error)
ApplyStellarWithdrawFeeTokens sweeps accumulated fee token balances to the configured fee aggregator on the Stellar contracts that hold CCIP fee funds. When in.Contracts is empty, OnRamp, Versioned Verifier Resolver, and Committee Verifier are swept; otherwise only the referenced contracts are, resolved by full ref (Type + Version + Qualifier).
withdraw_fee_tokens is permissionless on chain (it only sends to the trusted fee aggregator), so MCMS adds no authorization here; each contract is instead routed the same way the EVM fee adapters route their allowed-caller check: when the deployer owns the contract the withdrawal executes directly and returns no batch ops, otherwise it is emitted as an unexecuted MCMS write so the shared changeset proposes it via Build(cfg.MCMS). Governed environments (the owner is a timelock) therefore get a proposal, dev environments (the deployer owns) execute immediately.
func CREForwarderDeploySalt ¶
CREForwarderDeploySalt derives a deterministic deploy salt for a CRE forwarder instance.
func ClearStellarDeployOffchainTopologyForSelector ¶
func ClearStellarDeployOffchainTopologyForSelector(selector uint64)
ClearStellarDeployOffchainTopologyForSelector deletes a stashed topology without returning it. Call from post-deploy after Take may have already run (idempotent cleanup).
func CvGasForVerificationForRemote ¶
cvGasForVerificationForRemote returns the per-CCV dest verification gas for a remote chain, mirroring the chain-family adapter default the lane changeset coalesces to for that remote family: EVM 75_000 (chains/evm deployment/v2_0_0/adapters/chain_family.go GetDefaultCommitteeVerifierRemoteChainConfig), stellar 60_000 (deployment/ adapters/stellar_chain_family_adapter.go). The full deploy is the only writer of stellar CV remote configs, so the remote's scale must be honored here — a stellar-scale value for an EVM remote under-budgets the on-wire execution_gas_limit (see the remoteChainConfigs loop above).
func DeployStellarCCIPContracts ¶
func DeployStellarCCIPContracts( ctx context.Context, opBundle cldfops.Bundle, host stellarccip.CCIPDevenvHost, allSelectors []uint64, selector uint64, topology *ccvdeployment.EnvironmentTopology, existingAddresses []datastore.AddressRef, ) (datastore.DataStore, error)
DeployStellarCCIPContracts deploys the full Stellar CCIP stack and returns a sealed in-memory datastore containing address refs for merge into the environment datastore. opBundle must be the CLDF bundle for this run (parent changeset bundle from sequences, or cldfops.NewBundle for tests). topology must be non-nil so committee verifier signature config can be applied (RunStellarCCIPFullDeployForCCV).
func FindExistingStellarCREForwarder ¶
func FindExistingStellarCREForwarder(refs []datastore.AddressRef, chainSelector uint64, qual string) (string, bool)
FindExistingStellarCREForwarder returns the CRE forwarder contract id from refs when present.
func RegisterStellarDeployOffchainTopologyForSelector ¶
func RegisterStellarDeployOffchainTopologyForSelector(selector uint64, topo *offchain.EnvironmentTopology)
RegisterStellarDeployOffchainTopologyForSelector records offchain topology for the Stellar chain selector. It is overwritten if already present. nil topo is a no-op.
func ResolveCREForwarderWasmPath ¶
ResolveCREForwarderWasmPath returns the path to forwarder.wasm. Order: STELLAR_CRE_FORWARDER_WASM (full path), then CHAINLINK_STELLAR_ROOT + DefaultCREForwarderWasmRelative, then cwd-relative.
func RunStellarCCIPFullDeploy ¶
func RunStellarCCIPFullDeploy( ctx context.Context, b cldf_ops.Bundle, deps stellardeps.StellarDeps, h stellarccip.CCIPDevenvHost, topology *offchain.EnvironmentTopology, in DeployStellarCCIPInnerInput, ) (seq_core.OnChainOutput, error)
RunStellarCCIPFullDeploy deploys and configures the full Stellar CCIP Soroban stack for devenv using CLDF operations on the given bundle. It mirrors the phased devenv pipeline (foundation → verification/fees → ramps → receiver). Each component deploys and initializes through its own rerun-safe sequence; this orchestrator passes dependencies, applies the config ops and updates the host.
topology is optional: committee verifier signature quorums are applied at lane-configuration time, not during the deploy, so the deploy itself needs no NOP/committee data. It is kept as a parameter for callers that hold one (CCV's stash → StellarDeployChainContracts, or RunStellarCCIPFullDeployForCCV which converts CCV topology) and for future deploy-time needs.
Output addresses carry only the refs this run recorded (new deploys, adoptions, strkey→hex rewrites); refs seeded from ExistingAddresses are never re-reported, so a re-run where every contract already exists records no refs. Post-deploy configuration (executor dest chains, FeeQuoter configs and prices,
func RunStellarCCIPFullDeployForCCV ¶
func RunStellarCCIPFullDeployForCCV( ctx context.Context, opBundle cldfops.Bundle, deps stellardeps.StellarDeps, host stellarccip.CCIPDevenvHost, allSelectors []uint64, selector uint64, topology *ccvdeployment.EnvironmentTopology, existingAddresses []datastore.AddressRef, ) (seq_core.OnChainOutput, error)
RunStellarCCIPFullDeployForCCV runs the full Stellar CCIP Soroban deploy with CCV environment topology (converted to offchain). Used by ccv/chain and by DeployStellarCCIPContracts. topology must be non-nil so committee verifier signature config can be derived after conversion.
func StellarCREForwarderDatastoreRef ¶
func StellarCREForwarderDatastoreRef(chainSelector uint64, qual, contractID string) datastore.AddressRef
StellarCREForwarderDatastoreRef is the datastore ref emitted for a deployed CRE forwarder.
func TakeStellarDeployOffchainTopologyForSelector ¶
func TakeStellarDeployOffchainTopologyForSelector(selector uint64) (*offchain.EnvironmentTopology, bool)
TakeStellarDeployOffchainTopologyForSelector loads and removes the topology for selector. The second return is false if nothing was registered.
func VerifyStellarMCMSGovernance ¶
func VerifyStellarMCMSGovernance(ctx context.Context, deps stellardeps.StellarDeps, refs []datastore.AddressRef, chainSelector uint64, qual, deployerAddr string) error
VerifyStellarMCMSGovernance resolves the three role instances + timelock from refs and asserts the post-deployment authority invariants (distinct addresses, correct labels, timelock ownership, exact role matrix, no residual deployer authority). It fails closed on any missing ref.
func VerifyStellarMCMSGovernanceWithExpectations ¶
func VerifyStellarMCMSGovernanceWithExpectations(ctx context.Context, deps stellardeps.StellarDeps, refs []datastore.AddressRef, chainSelector uint64, qual, deployerAddr string, exp governanceExpectations) error
VerifyStellarMCMSGovernanceWithExpectations additionally asserts the timelock min delay and that the timelock owns each supplied governed contract.
Types ¶
type CREForwarderDONConfig ¶
type CREForwarderDONConfig struct {
DonID uint32 `json:"don_id"`
ConfigVersion uint32 `json:"config_version"`
F uint32 `json:"f"`
Signers [][32]byte `json:"signers"`
}
CREForwarderDONConfig is the initial DON signer configuration applied deployer-signed on a fresh deploy.
type ComponentDeployOutput ¶
type ComponentDeployOutput struct {
ContractID string `json:"contract_id"`
Deployed bool `json:"deployed"`
Initialized bool `json:"initialized"`
Refs []datastore.AddressRef `json:"refs,omitempty"`
}
ComponentDeployOutput is what every component sequence returns.
type ComponentDeps ¶
type ComponentDeps struct {
stellardeps.StellarDeps
NetworkPassphrase string
DeployerAddress string
Ledger InstanceStateReader
}
ComponentDeps is the host-free chain I/O every component sequence needs: the plain StellarDeps plus the inputs of the skip-if-exists check that must NOT enter the sequence input hash (they are environment, not parameters).
type DeployAdvancedPoolHooksExtractorInput ¶
type DeployAdvancedPoolHooksExtractorInput struct {
ChainSelector uint64 `json:"chainSelector"`
WasmPath string `json:"wasmPath"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeployAdvancedPoolHooksExtractorInput deploys the Advanced Pool Hooks Extractor — the stateless policy extractor that projects the pool-hooks preflight/postflight payloads into the named Parameter array the policy engine evaluates. There is no initialize step (the contract exposes only the pure extract / type_and_version views), so the sequence runs skip layers 1-2 only. Wiring the extractor to a policy engine (set_extractor) is an operator action on the engine, not deployment tooling (EVM parity).
type DeployAdvancedPoolHooksInput ¶
type DeployAdvancedPoolHooksInput struct {
ChainSelector uint64 `json:"chainSelector"`
Qualifier string `json:"qualifier"`
Owner string `json:"owner,omitempty"`
WasmPath string `json:"wasmPath"`
Allowlist []string `json:"allowlist,omitempty"`
ThresholdAmount *big.Int `json:"thresholdAmount,omitempty"` // nil = 0: the hook applies to every amount
AuthorizedCallers []string `json:"authorizedCallers,omitempty"`
PolicyEngine *string `json:"policyEngine,omitempty"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeployAdvancedPoolHooksInput deploys and initializes the Advanced Pool Hooks contract — the per-token, issuer-owned hooks contract through which a token issuer / pool owner requires the CCV set for their token's transfers. The Qualifier identifies the instance on chains with more than one hooks contract (typically the token symbol); ThresholdAmount defaults to 0 (the hook applies to every amount) and PolicyEngine nil (policy checks dormant, EVM address(0)).
type DeployBnmTokenContractInput ¶
type DeployBnmTokenContractInput struct {
ChainSelector uint64 `json:"chainSelector"`
Owner string `json:"owner,omitempty"`
WasmPath string `json:"wasmPath"`
Name string `json:"name,omitempty"`
Symbol string `json:"symbol,omitempty"`
Decimals uint32 `json:"decimals,omitempty"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeployBnmTokenContractInput deploys and initializes the BnM test token — the custom (non-SAC) Soroban token implementing the full token::StellarAssetInterface that burn-mint token pools mint/burn. Name/Symbol/Decimals default to "CCIP BnM" / "BnM" / 7. The deployer is the initial admin (and owner); the set_admin mint-authority handoff to the pool and the TokenAdminRegistry registration are operator steps (see RunDeployBnmToken for the one-shot onboarding). No initial supply is minted.
type DeployBnmTokenInput ¶
type DeployBnmTokenInput struct {
TarContractID string `json:"tar_contract_id"`
Name string `json:"name"`
Symbol string `json:"symbol"`
Decimals uint32 `json:"decimals"`
}
DeployBnmTokenInput configures a BnM token + burn-mint pool onboarding. Name/Symbol/Decimals default to "CCIP BnM" / "BnM" / 7 (Stellar SAC convention) when left zero. TarContractID is the already-deployed TokenAdminRegistry that will link the BnM token to its pool.
type DeployBnmTokenOutput ¶
type DeployBnmTokenOutput struct {
BnmTokenID string `json:"bnm_token_id"`
PoolID string `json:"pool_id"`
}
DeployBnmTokenOutput is the result of a BnM onboarding: the deployed BnM token contract ID and the burn-mint pool wired to it.
func RunDeployBnmToken ¶
func RunDeployBnmToken( ctx context.Context, b cldfops.Bundle, deps stellardeps.StellarDeps, host stellarccip.CCIPDevenvHost, in DeployBnmTokenInput, ) (DeployBnmTokenOutput, error)
RunDeployBnmToken onboards a CCIP BnM test token as a remotely-issued burn-mint token: it deploys the custom BnM Soroban token (implementing the full `token::StellarAssetInterface`), hands mint authority to a burn-mint token pool via `set_admin`, initializes the pool against the core CCIP stack (Router / RampRegistry / RMN proxy already on the host), and registers the token↔pool link in the TokenAdminRegistry. No initial supply is minted — supply tracks bridge flow (mint on inbound, burn on outbound).
This mirrors the canonical lock-release onboarding (`deploySiloedLockReleaseTestTokenPool`) but for the burn-mint model: there is no lock-box / approve / deposit (a burn-mint pool holds only accrued fees), and the burn-mint-specific step is the `set_admin` mint-authority handoff (the deployer, initial BnM admin, transfers admin to the pool so the pool can mint on inbound messages).
Prerequisites on the host: Router, RampRegistry, RMN proxy, and the TokenAdminRegistry (TarContractID) must already be deployed (run RunStellarCCIPFullDeploy first). Per-remote-chain pool config (apply_chain_updates) is NOT applied here — it is added at lane-configuration time, matching the lock-release onboarding.
deps is the Stellar deploy/invoke surface (as in RunStellarCCIPFullDeploy) so the sequence is unit-testable with the operationstest recording harness without constructing a real framework Deployer; in production it is `stellardeps.FromDeployer(host.Deployer())`.
type DeployBurnMintPoolInput ¶
type DeployBurnMintPoolInput struct {
ChainSelector uint64 `json:"chainSelector"`
Qualifier string `json:"qualifier"`
Owner string `json:"owner,omitempty"`
WasmPath string `json:"wasmPath"`
Token string `json:"token"`
TokenDecimals uint32 `json:"tokenDecimals"`
Router string `json:"router"`
RampRegistry string `json:"rampRegistry"`
RmnProxy string `json:"rmnProxy"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeployBurnMintPoolInput deploys and initializes a burn-mint token pool against the already-deployed core stack. The Qualifier identifies the pool instance on chains with more than one pool (typically the token symbol). Token/TokenDecimals come from config (the pool takes any custom token — BnM, LINK, or an issuer token); the Router/RampRegistry/RMN Proxy strkeys are resolved from the environment datastore by the changeset. The set_admin mint-authority handoff and the TokenAdminRegistry registration are operator steps (see RunDeployBnmToken for the one-shot onboarding); per-remote-chain pool config (apply_chain_updates) is applied at lane-configuration time.
type DeployCCIPReceiverInput ¶
type DeployCCIPReceiverInput struct {
ChainSelector uint64 `json:"chainSelector"`
Owner string `json:"owner,omitempty"`
WasmPath string `json:"wasmPath"`
Router string `json:"router"`
Qualifier string `json:"qualifier,omitempty"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeployCCIPReceiverInput deploys and initializes the ccip_receiver_example contract (the 12th component; devenv needs it). Router is a required input. Enabling remote chains is a config op and stays with the orchestrator. Qualifier deploys an additional receiver instance recorded under that qualifier (empty keeps the original instance); the deploy salt and idempotency key are suffixed with it so each instance lands at its own deterministic contract ID.
type DeployChainContractsOutput ¶
type DeployChainContractsOutput = ccvadapters.DeployChainContractsOutput
DeployChainContractsOutput wraps seq_core.OnChainOutput with additional adapter-specific fields. This type alias ensures compatibility with ccvadapters.DeployChainContractsOutput.
type DeployCommitteeVerifierInput ¶
type DeployCommitteeVerifierInput struct {
ChainSelector uint64 `json:"chainSelector"`
Qualifier string `json:"qualifier,omitempty"`
Owner string `json:"owner,omitempty"`
WasmPath string `json:"wasmPath"`
StorageLocations [][]byte `json:"storageLocations"`
RmnProxy string `json:"rmnProxy"`
AllowlistAdmin string `json:"allowlistAdmin,omitempty"`
FeeAggregator string `json:"feeAggregator,omitempty"`
VersionTag [4]byte `json:"versionTag"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeployCommitteeVerifierInput deploys and initializes the CommitteeVerifier contract. StorageLocations is a required input (G4): the deploy never invents one. AllowlistAdmin and FeeAggregator default to the owner, VersionTag to the default committee-verifier tag, and RmnProxy is a required dependency. An empty Qualifier deploys the default instance; a non-empty one deploys a distinct instance (separate datastore ref, salt and op reports) for extra, non-default verifiers.
type DeployExecutorInput ¶
type DeployExecutorInput struct {
ChainSelector uint64 `json:"chainSelector"`
Qualifier string `json:"qualifier,omitempty"`
Owner string `json:"owner,omitempty"`
WasmPath string `json:"wasmPath"`
MaxCCVsPerMsg uint32 `json:"maxCCVsPerMsg,omitempty"`
AllowedFinalityConfig uint32 `json:"allowedFinalityConfig,omitempty"`
CcvAllowlistEnabled bool `json:"ccvAllowlistEnabled,omitempty"`
FeeAggregator string `json:"feeAggregator,omitempty"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeployExecutorInput deploys and initializes the source-side Executor contract. MaxCCVsPerMsg defaults to 10 (EVM parity: the ccv devenv deploys its executor with MaxCCVsPerMsg 10), AllowedFinalityConfig to 0, CcvAllowlistEnabled to false and FeeAggregator to the owner (the monolith's constants). The sequence records both the executor and executor-proxy rows: Soroban has no delegate proxy, so both point at the same contract. An empty Qualifier deploys the default instance; a non-empty one deploys a distinct instance (separate datastore refs, salt and op reports) for extra executor pools (e.g. the tcapi custom executor).
type DeployFeeQuoterInput ¶
type DeployFeeQuoterInput struct {
ChainSelector uint64 `json:"chainSelector"`
Owner string `json:"owner,omitempty"`
WasmPath string `json:"wasmPath"`
FeeToken string `json:"feeToken"`
MaxFeeJuelsPerMsg *big.Int `json:"maxFeeJuelsPerMsg,omitempty"`
AuthorizedCallers []string `json:"authorizedCallers,omitempty"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeployFeeQuoterInput deploys and initializes the FeeQuoter contract. FeeToken is the strkey of the SAC used for fee payments, a required input (G9): the deploy never derives it. MaxFeeJuelsPerMsg defaults to 1e18 and AuthorizedCallers to [owner] (the monolith's constants).
type DeployLinkTokenInput ¶
type DeployLinkTokenInput struct {
ChainSelector uint64 `json:"chainSelector"`
Owner string `json:"owner,omitempty"`
WasmPath string `json:"wasmPath"`
Name string `json:"name,omitempty"`
Symbol string `json:"symbol,omitempty"`
Decimals uint32 `json:"decimals,omitempty"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeployLinkTokenInput deploys and initializes the LINK token — the custom (non-SAC) Soroban LINK token used as the CCIP fee token. Name/Symbol/Decimals default to "ChainLink Token" / "LINK" / 7 (the deliberate Stellar divergence from EVM's 18 decimals). The deployer is the initial admin (and owner); the set_admin mint-authority handoff to the pool and the TokenAdminRegistry registration are operator steps (see RunDeployBnmToken for the onboarding order). No initial supply is minted — mint authority moves to the pool via set_admin, and the pre-handoff window is the only operator minting window.
type DeployLockReleasePoolInput ¶
type DeployLockReleasePoolInput struct {
ChainSelector uint64 `json:"chainSelector"`
Qualifier string `json:"qualifier"`
Owner string `json:"owner,omitempty"`
WasmPath string `json:"wasmPath"`
Token string `json:"token"`
TokenDecimals uint32 `json:"tokenDecimals"`
LockBox string `json:"lockBox"`
Router string `json:"router"`
RampRegistry string `json:"rampRegistry"`
RmnProxy string `json:"rmnProxy"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeployLockReleasePoolInput deploys and initializes the canonical (non-siloed) lock-release token pool. The Qualifier identifies the pool instance on chains with more than one pool (typically the token symbol). Token / TokenDecimals / LockBox come from config; the Router/RampRegistry/RMN Proxy strkeys are resolved from the environment datastore by the changeset. The LockBox must be an initialized TokenLockBox for the same token (the pool validates token support on-chain).
type DeployOffRampInput ¶
type DeployOffRampInput struct {
ChainSelector uint64 `json:"chainSelector"`
Owner string `json:"owner,omitempty"`
WasmPath string `json:"wasmPath"`
RmnProxy string `json:"rmnProxy"`
TokenAdminRegistry string `json:"tokenAdminRegistry"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeployOffRampInput deploys and initializes the OffRamp contract. RmnProxy and TokenAdminRegistry are the dependency strkeys, required inputs.
type DeployOnRampInput ¶
type DeployOnRampInput struct {
ChainSelector uint64 `json:"chainSelector"`
Owner string `json:"owner,omitempty"`
WasmPath string `json:"wasmPath"`
TokenAdminRegistry string `json:"tokenAdminRegistry"`
RmnProxy string `json:"rmnProxy"`
FeeQuoter string `json:"feeQuoter"`
MaxUsdCentsPerMessage uint32 `json:"maxUsdCentsPerMessage,omitempty"`
FeeAggregator string `json:"feeAggregator,omitempty"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeployOnRampInput deploys and initializes the OnRamp contract in one step. TokenAdminRegistry, RmnProxy and FeeQuoter are the dependency strkeys, required inputs. MaxUsdCentsPerMessage defaults to 500000 ($5000) and FeeAggregator to the owner (the monolith's devenv values).
type DeployRMNProxyInput ¶
type DeployRMNProxyInput struct {
ChainSelector uint64 `json:"chainSelector"`
Owner string `json:"owner,omitempty"`
WasmPath string `json:"wasmPath"`
RmnRemote string `json:"rmnRemote"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeployRMNProxyInput deploys and initializes the RMN Proxy contract. RmnRemote is the RMN Remote contract strkey this proxy points at, a required input.
type DeployRMNRemoteInput ¶
type DeployRMNRemoteInput struct {
ChainSelector uint64 `json:"chainSelector"`
Owner string `json:"owner,omitempty"`
WasmPath string `json:"wasmPath"`
CurseAdmins []string `json:"curseAdmins,omitempty"`
EnableFastCurse bool `json:"enableFastCurse,omitempty"`
FastCurseQualifier string `json:"fastCurseQualifier,omitempty"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeployRMNRemoteInput deploys and initializes the RMN Remote contract. Owner defaults to the deployer address. CurseAdmins default to none; EnableFastCurse prepends the fast-curse timelock resolved from ExistingAddresses (qualifier FastCurseQualifier, default the Ultra Fast Curse qualifier), mirroring the EVM deploy.
type DeployRampRegistryInput ¶
type DeployRampRegistryInput struct {
ChainSelector uint64 `json:"chainSelector"`
Owner string `json:"owner,omitempty"`
WasmPath string `json:"wasmPath"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeployRampRegistryInput deploys and initializes the RampRegistry contract. The ramp map updates are config ops and stay with the orchestrator.
type DeployRouterInput ¶
type DeployRouterInput struct {
ChainSelector uint64 `json:"chainSelector"`
Owner string `json:"owner,omitempty"`
WasmPath string `json:"wasmPath"`
RmnProxy string `json:"rmnProxy"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeployRouterInput deploys and initializes the Router contract. RmnProxy is a required input.
type DeploySiloedLockReleasePoolInput ¶
type DeploySiloedLockReleasePoolInput struct {
ChainSelector uint64 `json:"chainSelector"`
Qualifier string `json:"qualifier"`
Owner string `json:"owner,omitempty"`
WasmPath string `json:"wasmPath"`
Token string `json:"token"`
TokenDecimals uint32 `json:"tokenDecimals"`
Router string `json:"router"`
RampRegistry string `json:"rampRegistry"`
RmnProxy string `json:"rmnProxy"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeploySiloedLockReleasePoolInput deploys and initializes the siloed lock-release token pool — the lock-release variant whose per-remote-chain lock boxes are configured post-deploy (there is no immutable lock box at initialize). The Qualifier identifies the pool instance on chains with more than one pool (typically the token symbol). Token/TokenDecimals come from config; the Router/RampRegistry/RMN Proxy strkeys are resolved from the environment datastore by the changeset. Lock-box wiring (configure_lock_boxes) and per-remote-chain pool config are lane-configuration-time operator steps.
type DeployStellarCCIPInnerInput ¶
type DeployStellarCCIPInnerInput struct {
ChainSelector uint64
AllSelectors []uint64
ExistingAddresses []datastore.AddressRef
// CurseAdmins seeds the RMN Remote curse-admin list at deploy time (strkeys).
// Zero value keeps the previous behavior: no curse admins.
CurseAdmins []string
// EnableFastCurse prepends the fast-curse RBACTimelock (resolved by
// FastCurseQualifier, default the UltraFastCurse qualifier) to CurseAdmins,
// mirroring EVM where the Ultra Fast Curse timelock is always the curse admin
// on a newly deployed RMN. Errors if that timelock is not deployed.
EnableFastCurse bool
// FastCurseQualifier overrides the qualifier used to resolve the fast-curse
// timelock when EnableFastCurse is set. Defaults to the UltraFastCurse qualifier.
FastCurseQualifier string
// GovernanceQualifier is carried for downstream activation wiring; the deploy
// itself does not use it. Defaults to the RMNMCMS qualifier.
GovernanceQualifier string
}
DeployStellarCCIPInnerInput carries per-chain inputs for the Stellar full-stack deploy sequence. AllSelectors must list every chain selector in the environment (from BlockChains).
type DeployStellarCREForwarderInput ¶
type DeployStellarCREForwarderInput struct {
ChainSelector uint64 `json:"chain_selector"`
Qualifier *string `json:"qualifier,omitempty"`
ExistingAddresses []datastore.AddressRef `json:"existing_addresses,omitempty"`
// InitialDONConfig and Transmitters are optional deployer-signed bootstrap applied only on a
// fresh deploy, before ownership handoff. Post-handoff changes go through MCMS/timelock.
InitialDONConfig *CREForwarderDONConfig `json:"initial_don_config,omitempty"`
Transmitters []string `json:"transmitters,omitempty"`
}
DeployStellarCREForwarderInput drives an idempotent CRE forwarder deploy on one chain.
type DeployTokenAdminRegistryInput ¶
type DeployTokenAdminRegistryInput struct {
ChainSelector uint64 `json:"chainSelector"`
Owner string `json:"owner,omitempty"`
WasmPath string `json:"wasmPath"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeployTokenAdminRegistryInput deploys and initializes the TokenAdminRegistry contract. No params beyond owner and dependencies.
type DeployTokenLockBoxInput ¶
type DeployTokenLockBoxInput struct {
ChainSelector uint64 `json:"chainSelector"`
Qualifier string `json:"qualifier"`
Owner string `json:"owner,omitempty"`
WasmPath string `json:"wasmPath"`
Token string `json:"token"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeployTokenLockBoxInput deploys and initializes a TokenLockBox — the box the canonical lock-release pool deposits user tokens into. The Qualifier identifies the box instance on chains with more than one (typically the token symbol, matching the pool it serves). Token comes from config; a lock-release pool takes the box strkey in its own config.
type DeployVVRInput ¶
type DeployVVRInput struct {
ChainSelector uint64 `json:"chainSelector"`
Qualifier string `json:"qualifier,omitempty"`
Owner string `json:"owner,omitempty"`
WasmPath string `json:"wasmPath"`
FeeAggregator string `json:"feeAggregator,omitempty"`
ExistingAddresses []datastore.AddressRef `json:"existingAddresses,omitempty"`
}
DeployVVRInput deploys and initializes the VersionedVerifierResolver contract. FeeAggregator defaults to the owner (the monolith's devenv value). An empty Qualifier deploys the default instance; a non-empty one deploys a distinct instance (separate datastore ref, salt and op reports) for extra, non-default committees.
type InstanceStateReader ¶
type InstanceStateReader interface {
ContractInstanceState(ctx context.Context, contractID string) (bool, xdr.Hash, error)
}
InstanceStateReader reports whether a Soroban contract instance exists on chain and which WASM hash it runs. Satisfied by *deployment.Deployer.
type StellarActivateRMNInput ¶
type StellarActivateRMNInput struct {
ChainSelector uint64
// ExistingAddresses carries the chain's recorded refs. The RMN Remote ref is
// resolved from it (stored hex, converted to the strkey form the ownership
// helpers require); the governance and fast-curse RBACTimelocks are resolved
// by qualifier.
ExistingAddresses []datastore.AddressRef
// RMNRemoteRef optionally overrides the RMN Remote ref resolved from
// ExistingAddresses. Address must be the contract strkey.
RMNRemoteRef *datastore.AddressRef
// GovernanceQualifier names the MCMS stack that will own the RMN after
// activation. Defaults to the RMNMCMS qualifier.
GovernanceQualifier string
// FastCurseQualifier names the MCMS stack granted curse-admin. Defaults to
// the UltraFastCurse qualifier.
FastCurseQualifier string
// ExtraCurseAdmins are granted curse-admin alongside the fast-curse timelock.
// Addresses must be strkeys.
ExtraCurseAdmins []string
// SubjectsToMigrate are cursed directly while the deployer still owns the RMN
// (idempotent on chain). Only valid pre-transfer: a non-empty list errors once
// ownership has moved off the deployer.
SubjectsToMigrate [][16]byte
// Description labels emitted MCMS proposals.
Description string
}
StellarActivateRMNInput configures activation of an existing RMN Remote: grant curse-admin to the fast-curse timelock (plus extras), migrate curse subjects while the deployer still owns, and transfer ownership to the governance (RMNMCMS) timelock. Mirrors EVM DeployAndActivateRMN: the UltraFastCurse timelock is granted curse-admin; the RMNMCMS timelock's curse authority comes from ownership and it is never listed in get_curse_admins.
type StellarApplyDestChainConfigInput ¶
type StellarApplyDestChainConfigInput struct {
fees.ApplyDestChainConfigSequenceInput
FQContractID string
}
StellarApplyDestChainConfigInput is the sequence input for applying dest chain configs.
type StellarCCIPDeploySession ¶
type StellarCCIPDeploySession struct {
// contains filtered or unexported fields
}
StellarCCIPDeploySession runs the full Stellar CCIP Soroban deploy via DeployStellarCCIPContracts.
func NewStellarCCIPDeploySession ¶
func NewStellarCCIPDeploySession( ctx context.Context, opBundle cldfops.Bundle, host stellarccip.CCIPDevenvHost, allSelectors []uint64, selector uint64, topology *ccvdeployment.EnvironmentTopology, existingAddresses []datastore.AddressRef, ) (*StellarCCIPDeploySession, error)
NewStellarCCIPDeploySession validates inputs for a Stellar CCIP deploy run. opBundle must be a valid CLDF bundle (e.g. the parent changeset bundle from chainlink-ccv DeployContractsForSelector, or cldfops.NewBundle for standalone tests); all Soroban operations run on this bundle.
func (*StellarCCIPDeploySession) RunAllPhasesAndSeal ¶
func (s *StellarCCIPDeploySession) RunAllPhasesAndSeal() (datastore.DataStore, error)
RunAllPhasesAndSeal deploys and configures the full stack and returns a sealed datastore view.
type StellarCurseInput ¶
type StellarCurseInput struct {
api.CurseInput
// RMNContractID is the RMN Remote contract the curse targets (strkey).
RMNContractID string
// Owner is the RMN Remote's current owner (the owner is implicitly
// curse-authorized even though it is never listed in get_curse_admins).
Owner string
// CurseAdmins is the raw stored curse-admin list.
CurseAdmins []string
// Timelocks maps MCMS qualifier → RBACTimelock contract ID deployed for that
// qualifier on the chain. An absent qualifier means that stack is not deployed.
Timelocks map[string]string
}
StellarCurseInput extends the shared CurseInput with the resolved RMN Remote contract ID and the on-chain facts needed to route the curse to the timelock that will execute it. All addresses are strkeys.
type StellarSetCommitteeVerifierFinalityInput ¶
type StellarSetCommitteeVerifierFinalityInput struct {
ChainSelector uint64
// CommitteeVerifier is the verifier ref; Address must be the contract strkey.
CommitteeVerifier datastore.AddressRef
// Timelock is the RBACTimelock strkey allowed to own the verifier.
Timelock string
AllowedFinality finality.Config
}
StellarSetCommitteeVerifierFinalityInput sets the allowed finality of one Committee Verifier.
type StellarSetTokenTransferFeeInput ¶
type StellarSetTokenTransferFeeInput struct {
fees.SetTokenTransferFeeSequenceInput
FQContractID string
}
StellarSetTokenTransferFeeInput is the sequence input for applying per-token transfer fees.
type StellarTransferOwnershipInput ¶
type StellarTransferOwnershipInput struct {
deploy.TransferOwnershipPerChainInput
GovernanceAddr string
}
StellarTransferOwnershipInput extends the shared input with the RBAC timelock (governance) address from the datastore — same role as EVM OpTransferOwnershipInput.TimelockAddress.
type StellarUpgradeContractsInput ¶
type StellarUpgradeContractsInput struct {
ChainSelector uint64 `json:"chain_selector"`
ContractRef []datastore.AddressRef `json:"contract_ref"`
GovernanceAddr string `json:"governance_addr"`
}
StellarUpgradeContractsInput drives an in-place upgrade of one or more deployed Soroban CCIP contracts on a single chain. Each ref is upgraded independently: the freshly-compiled release WASM for its contract type is uploaded, the contract's current owner is read, and the upgrade is routed either as a direct on-chain call (owner == deployer EOA) or as an MCMS proposal transaction (owner == governance/timelock) — the same two-path model as StellarTransferOwnershipViaMCMS.
Source Files
¶
- activate_rmn.go
- advanced_pool_hooks_deploy.go
- advanced_pool_hooks_extractor_deploy.go
- blockchains_util.go
- bnm_token_deploy.go
- burn_mint_pool_deploy.go
- ccip_receiver_deploy.go
- ccv_stellar_deploy.go
- committee_verifier_deploy.go
- committee_verifier_finality.go
- component_deploy.go
- configure_stellar_lanes.go
- cre_forwarder_deploy.go
- curse.go
- deploy_bnm_token.go
- deploy_stellar_ccip.go
- deploy_topology_stash.go
- doc.go
- executor_deploy.go
- fee.go
- fee_quoter_deploy.go
- link_token_deploy.go
- lock_release_pool_deploy.go
- mcms.go
- mcms_verify.go
- offramp_deploy.go
- onramp_deploy.go
- ramp_registry_deploy.go
- rmn_proxy_deploy.go
- rmn_remote_deploy.go
- router_deploy.go
- siloed_lock_release_pool_deploy.go
- stellar_ccip_full_deploy.go
- token_admin_registry_deploy.go
- token_lock_box_deploy.go
- transfer_ownership.go
- upgrade.go
- version.go
- versioned_verifier_resolver_deploy.go
- withdraw_fee_tokens.go