Documentation
¶
Index ¶
- Constants
- Variables
- func GetContextedTxLogger(lgr logger.Logger, txID string, meta *commontypes.TxMeta) logger.Logger
- func NetworkPassphrase(chainID string) (string, error)
- type AccountStore
- type Config
- type FeeStrategy
- func (f *FeeStrategy) BumpInclusionFee(currentInclusionFee int64, networkPercentile uint64) (fee int64, clampedToMax bool)
- func (f *FeeStrategy) Calculate(minResourceFee int64, attempt uint64) int64
- func (f *FeeStrategy) CalculateRestoreFee(preambleMinResourceFee int64, restoreFeeBuffer int64) int64
- func (f *FeeStrategy) InclusionFee(attempt uint64) int64
- func (f *FeeStrategy) SeedInclusionFee(attempt uint64, networkPercentile uint64) (fee int64, clampedToMax bool)
- type InvokerAdapter
- func (a *InvokerAdapter) GetEvents(ctx context.Context, contractID string, startLedger uint32, topics []string) ([]protocolrpc.EventInfo, error)
- func (a *InvokerAdapter) InvokeContract(ctx context.Context, contractID string, functionName string, args []xdr.ScVal) (*xdr.ScVal, error)
- func (a *InvokerAdapter) SimulateContract(ctx context.Context, contractID string, functionName string, args []xdr.ScVal) (*xdr.ScVal, error)
- type InvokerAdapterOption
- type InvokerTxManager
- type RPCClient
- type RetryReason
- type StellarTx
- type StellarTxm
- func (s *StellarTxm) Close() error
- func (s *StellarTxm) Enqueue(ctx context.Context, req TxRequest) (string, error)
- func (s *StellarTxm) EnqueueAndWait(ctx context.Context, req TxRequest) (*TxResult, error)
- func (s *StellarTxm) GetStatus(transactionID string) (commontypes.TransactionStatus, error)
- func (s *StellarTxm) GetTransactionFee(transactionID string) (*big.Int, error)
- func (s *StellarTxm) GetTransactionResult(transactionID string) (*TxResult, error)
- func (s *StellarTxm) HealthReport() map[string]error
- func (s *StellarTxm) InflightCount() (int, int)
- func (s *StellarTxm) Name() string
- func (s *StellarTxm) Ready() error
- func (s *StellarTxm) Simulate(ctx context.Context, req TxRequest) (protocolrpc.SimulateTransactionResponse, error)
- func (s *StellarTxm) Start(_ context.Context) error
- type TxRequest
- type TxResult
- type TxStore
- func (s *TxStore) AddUnconfirmed(seq int64, hash string, maxLedger uint32, tx *StellarTx) error
- func (s *TxStore) Confirm(seq int64, hash string, failed bool) error
- func (s *TxStore) GetLastResyncedNonce() int64
- func (s *TxStore) GetNextSequence() int64
- func (s *TxStore) GetUnconfirmed() []*UnconfirmedTx
- func (s *TxStore) InflightCount() int
- func (s *TxStore) Release(seq int64)
- func (s *TxStore) ResyncNonce(nextExpectedSequence int64)
- type UnconfirmedTx
Constants ¶
const ( ErrorReasonSequenceNumber = "sequence_number" ErrorReasonStoreCreate = "store_create" ErrorReasonSimulation = "simulation" ErrorReasonAssembly = "assembly" ErrorReasonSigning = "signing" ErrorReasonNoHash = "no_hash" ErrorReasonStoreAdd = "store_add" ErrorReasonUnknownSubmit = "unknown_submit" ErrorReasonMaxRetries = "max_retries" ErrorReasonRevert = "revert" ErrorReasonTimedOut = "timed_out" ErrorReasonBadSeq = "bad_seq" ErrorReasonInsufficientBal = "insufficient_balance" ErrorReasonRestoreFailed = "restore_failed" ErrorReasonBadAuth = "bad_auth" ErrorReasonTryAgainLater = "try_again_later" ErrorReasonInsufficientFee = "insufficient_fee" ErrorReasonInternalError = "internal_error" ErrorReasonNilTx = "nil_tx" ErrorReasonNilTxStore = "nil_tx_store" // ErrorReasonSubmitErrorUndecoded means the node returned TXStatusError but // ErrorResultXDR was empty or not valid transaction-result XDR. ErrorReasonSubmitErrorUndecoded = "submit_error_undecoded" )
Error reason constants classify broadcast and confirmation failures.
const ( // DropReasonChannelFullOldestEvicted: the oldest queued tx was evicted to make // room for a newer one. The oldest has the stalest simulation data and the // nearest LedgerBounds expiry, so the new tx's intent takes priority. DropReasonChannelFullOldestEvicted = "channel_full_oldest_evicted" // DropReasonChannelFullNewRejected: the incoming tx was rejected because the // channel was still full after an attempted oldest-evict (concurrent enqueue race). DropReasonChannelFullNewRejected = "channel_full_new_rejected" )
Drop reasons classify why a pending transaction was dropped from the broadcast queue.
Variables ¶
var DefaultConfigSet = Config{ BroadcastChanSize: ptr(uint(100)), ConfirmPollInterval: config.MustNewDuration(3 * time.Second), BaseInclusionFee: ptr(int64(100)), MaxInclusionFee: ptr(int64(100_000)), FeeBumpMultiplier: ptr(1.5), ResourceFeeBuffer: ptr(int64(15_000)), RestoreFeeBuffer: ptr(int64(10_000)), FeeStatsPollInterval: config.MustNewDuration(5 * time.Second), MaxSimulateAttempts: ptr(uint(3)), MaxSubmitRetryAttempts: ptr(uint(10)), SubmitRetryDelay: config.MustNewDuration(3 * time.Second), TxTimeoutSecs: ptr(int64(300)), LedgerBoundsOffset: ptr(uint32(50)), MaxTxRetryAttempts: ptr(uint64(5)), MaxRestoreAttempts: ptr(uint(3)), PruneInterval: config.MustNewDuration(2 * time.Hour), PruneTxExpiration: config.MustNewDuration(2 * time.Hour), }
DefaultConfigSet is the default configuration for the Stellar Transaction Manager.
Functions ¶
func GetContextedTxLogger ¶
GetContextedTxLogger returns a logger with transaction context fields attached.
func NetworkPassphrase ¶
NetworkPassphrase returns the Stellar network passphrase for a given chain-selectors chain ID. Passphrases are static per network so we resolve them from chainID rather than carrying them through TOML config.
Localnet shares testnet's passphrase by convention — the network ID hash is what matters for signing, and Stellar localnet is configured against the testnet passphrase.
Types ¶
type AccountStore ¶
type AccountStore struct {
// contains filtered or unexported fields
}
AccountStore holds a TxStore per Stellar account address.
func NewAccountStore ¶
func NewAccountStore() *AccountStore
func (*AccountStore) CreateTxStore ¶
func (c *AccountStore) CreateTxStore(accountAddress string, initialSequence int64) (*TxStore, error)
CreateTxStore initializes a TxStore for a new account. Returns an error if a store already exists for this address.
func (*AccountStore) GetAllUnconfirmed ¶
func (c *AccountStore) GetAllUnconfirmed() map[string][]*UnconfirmedTx
func (*AccountStore) GetTotalInflightCount ¶
func (c *AccountStore) GetTotalInflightCount() int
func (*AccountStore) GetTxStore ¶
func (c *AccountStore) GetTxStore(accountAddress string) *TxStore
type Config ¶
type Config struct {
BroadcastChanSize *uint `toml:"BroadcastChanSize"`
ConfirmPollInterval *config.Duration `toml:"ConfirmPollInterval"`
// Fee strategy: Stellar fees = InclusionFee + ResourceFee.
// Only the inclusion fee is bumped on retries; the resource fee is deterministic from simulation.
BaseInclusionFee *int64 `toml:"BaseInclusionFee"`
MaxInclusionFee *int64 `toml:"MaxInclusionFee"`
FeeBumpMultiplier *float64 `toml:"FeeBumpMultiplier"`
ResourceFeeBuffer *int64 `toml:"ResourceFeeBuffer"`
RestoreFeeBuffer *int64 `toml:"RestoreFeeBuffer"`
// FeeStatsPollInterval controls how often GetFeeStats is called to refresh
// Soroban inclusion fee P50/P90 in the feeTracker; back-to-back broadcasts reuse values.
// Zero disables reuse (every inclusion-fee decision calls GetFeeStats).
FeeStatsPollInterval *config.Duration `toml:"FeeStatsPollInterval"`
// Retry & timeout
MaxSimulateAttempts *uint `toml:"MaxSimulateAttempts"`
MaxSubmitRetryAttempts *uint `toml:"MaxSubmitRetryAttempts"`
SubmitRetryDelay *config.Duration `toml:"SubmitRetryDelay"`
TxTimeoutSecs *int64 `toml:"TxTimeoutSecs"`
LedgerBoundsOffset *uint32 `toml:"LedgerBoundsOffset"`
MaxTxRetryAttempts *uint64 `toml:"MaxTxRetryAttempts"`
MaxRestoreAttempts *uint `toml:"MaxRestoreAttempts"`
// SimulationTerminalHints are matched case-insensitively as substrings
// against failed SimulateTransaction errors; any match means the error is
// treated as terminal (do not retry). Resolve merges these with built-in
// defaults (additive); list only extra hints to add on top of defaults.
SimulationTerminalHints []string `toml:"SimulationTerminalHints"`
// SimulationRetryableHints: any substring match means retry simulation when
// attempts remain. Resolve merges with built-in defaults (additive).
SimulationRetryableHints []string `toml:"SimulationRetryableHints"`
// Pruning
PruneInterval *config.Duration `toml:"PruneInterval"`
PruneTxExpiration *config.Duration `toml:"PruneTxExpiration"`
}
Config defines the Stellar transaction manager configuration. Pointer fields are used for TOML deserialization — nil means "not set by user". After calling Resolve(), scalar pointer fields are non-nil; simulation hint slices are non-empty (built-in defaults when unset).
type FeeStrategy ¶
type FeeStrategy struct {
BaseInclusionFee int64
MaxInclusionFee int64
BumpMultiplier float64
ResourceFeeBuffer int64
}
FeeStrategy calculates Stellar transaction fees.
Stellar fees have two independent components:
- Inclusion fee: market-based bid for validator priority (bumped on retries)
- Resource fee: deterministic cost from simulation (not negotiable)
Total fee = inclusionFee(attempt) + minResourceFee + resourceFeeBuffer
func NewFeeStrategyFromConfig ¶
func NewFeeStrategyFromConfig(cfg Config) FeeStrategy
NewFeeStrategyFromConfig constructs a FeeStrategy from the resolved Config.
func (*FeeStrategy) BumpInclusionFee ¶
func (f *FeeStrategy) BumpInclusionFee(currentInclusionFee int64, networkPercentile uint64) (fee int64, clampedToMax bool)
BumpInclusionFee returns the next inclusion fee after a submit rejection that requires a higher bid (e.g. tx_insufficient_fee): multiply the current fee, take max with networkPercentile (typically live P90 from GetFeeStats), and clamp to MaxInclusionFee.
func (*FeeStrategy) Calculate ¶
func (f *FeeStrategy) Calculate(minResourceFee int64, attempt uint64) int64
Calculate returns the total fee (in stroops) for a transaction at the given attempt. The inclusion fee is geometrically bumped per attempt; the resource fee is passed through from simulation with a flat safety buffer.
func (*FeeStrategy) CalculateRestoreFee ¶
func (f *FeeStrategy) CalculateRestoreFee(preambleMinResourceFee int64, restoreFeeBuffer int64) int64
CalculateRestoreFee returns the fee for a RestoreFootprint transaction. Restore fees are deterministic (no fee competition), so no geometric bumping.
func (*FeeStrategy) InclusionFee ¶
func (f *FeeStrategy) InclusionFee(attempt uint64) int64
InclusionFee returns the inclusion fee for the given attempt number.
func (*FeeStrategy) SeedInclusionFee ¶
func (f *FeeStrategy) SeedInclusionFee(attempt uint64, networkPercentile uint64) (fee int64, clampedToMax bool)
type InvokerAdapter ¶
type InvokerAdapter struct {
// contains filtered or unexported fields
}
InvokerAdapter implements bindings.Invoker by routing contract work through the Stellar TXM: InvokeContract → EnqueueAndWait (async pipeline, signing, fees, retries), SimulateContract → Simulate, GetEvents → RPC (no sequence).
Generated binding clients (e.g. routerbindings.NewRouterClient) take a bindings.Invoker — they do not reference InvokerAdapter directly. This type is one Invoker implementation for relayer/Chainlink-style TXM usage; other stacks may use a different Invoker (e.g. deployment.Deployer in ccv/devenv, which signs and submits via RPC without the TXM).
func NewInvokerAdapter ¶
func NewInvokerAdapter( txm InvokerTxManager, getClient func() (RPCClient, error), opts ...InvokerAdapterOption, ) (*InvokerAdapter, error)
NewInvokerAdapter creates a bindings.Invoker backed by the Stellar TXM.
func (*InvokerAdapter) GetEvents ¶
func (a *InvokerAdapter) GetEvents(ctx context.Context, contractID string, startLedger uint32, topics []string) ([]protocolrpc.EventInfo, error)
GetEvents reads contract events directly from RPC. TXM is intentionally not involved because event reads do not consume sequence numbers or fees.
func (*InvokerAdapter) InvokeContract ¶
func (a *InvokerAdapter) InvokeContract(ctx context.Context, contractID string, functionName string, args []xdr.ScVal) (*xdr.ScVal, error)
InvokeContract submits a Soroban contract invocation through the TXM and blocks until the transaction reaches a terminal state (or ctx is cancelled), then returns the Soroban return value from the confirmed transaction metadata.
func (*InvokerAdapter) SimulateContract ¶
func (a *InvokerAdapter) SimulateContract(ctx context.Context, contractID string, functionName string, args []xdr.ScVal) (*xdr.ScVal, error)
SimulateContract performs a read-only Soroban simulation through the TXM and returns the simulated Soroban return value.
type InvokerAdapterOption ¶
type InvokerAdapterOption func(*InvokerAdapter)
InvokerAdapterOption customizes InvokerAdapter behavior.
func WithInvokerFromAddress ¶
func WithInvokerFromAddress(fromAddress string) InvokerAdapterOption
WithInvokerFromAddress sets the account used as the source for contract invocations. If unset, StellarTxm falls back to its first keystore account.
func WithInvokerLedgerBoundsOffset ¶
func WithInvokerLedgerBoundsOffset(offset uint32) InvokerAdapterOption
WithInvokerLedgerBoundsOffset overrides the TXM default ledger bounds for invocations submitted through this adapter.
type InvokerTxManager ¶
type InvokerTxManager interface {
EnqueueAndWait(ctx context.Context, req TxRequest) (*TxResult, error)
Simulate(ctx context.Context, req TxRequest) (protocolrpc.SimulateTransactionResponse, error)
}
InvokerTxManager is the subset of StellarTxm used by InvokerAdapter.
type RPCClient ¶
type RPCClient interface {
SimulateTransaction(ctx context.Context, req protocolrpc.SimulateTransactionRequest) (protocolrpc.SimulateTransactionResponse, error)
SendTransaction(ctx context.Context, req protocolrpc.SendTransactionRequest) (protocolrpc.SendTransactionResponse, error)
GetTransaction(ctx context.Context, req protocolrpc.GetTransactionRequest) (protocolrpc.GetTransactionResponse, error)
GetEvents(ctx context.Context, req protocolrpc.GetEventsRequest) (protocolrpc.GetEventsResponse, error)
GetLedgerEntries(ctx context.Context, req protocolrpc.GetLedgerEntriesRequest) (protocolrpc.GetLedgerEntriesResponse, error)
GetLatestLedger(ctx context.Context) (protocolrpc.GetLatestLedgerResponse, error)
GetFeeStats(ctx context.Context) (protocolrpc.GetFeeStatsResponse, error)
}
RPCClient is the subset of the Stellar Soroban JSON-RPC client used by the TXM. Any value satisfying chain.RPCClient (a superset) automatically satisfies this.
type RetryReason ¶
type RetryReason int
RetryReason classifies why a transaction is being retried (Layer 3 lifecycle retries).
const ( RetryReasonResourceExhaustion RetryReason = iota RetryReasonTimedOut RetryReasonBadSeq RetryReasonTryAgainLater )
func (RetryReason) String ¶
func (r RetryReason) String() string
type StellarTx ¶
type StellarTx struct {
ID string
Metadata *commontypes.TxMeta
Timestamp uint64
FromAddress string // G... strkey: source account and signer for this TXM
Operations []txnbuild.Operation
LedgerBoundsOffset uint32 // per-tx override (0 = use config default)
Attempt uint64
Status commontypes.TransactionStatus
TxHash string
Fee *big.Int // total fee in stroops; updated to actual FeeCharged on confirmation
ResultXDR string // XDR-encoded transaction result from GetTransaction
ResultCode string // result code from GetTransaction (for diagnostics)
ResultMetaXDR string // XDR-encoded result meta from GetTransaction SUCCESS
MaxLedger uint32 // ledger bounds set during broadcast
MinResourceFee int64 // from simulation result
// Done is closed when the transaction reaches a terminal state.
Done chan struct{}
// contains filtered or unexported fields
}
StellarTx represents a single transaction tracked by the TXM from enqueue to confirmation.
type StellarTxm ¶
type StellarTxm struct {
// contains filtered or unexported fields
}
StellarTxm orchestrates the lifecycle of Stellar/Soroban transactions: enqueue → simulate → (restore) → assemble → sign → send → confirm.
func New ¶
func New( lgr logger.Logger, keystore core.Keystore, cfg Config, getClient func() (RPCClient, error), chainID string, ) (*StellarTxm, error)
New creates a StellarTxm. The getClient callback should be obtained from chain.Chain.GetClient to enable multi-node rotation; in normal wiring the chain package constructs the TXM and passes its own GetClient method. The network passphrase is resolved from chainID via NetworkPassphrase. cfg is normalized with Resolve so pointer fields (e.g. TxTimeoutSecs) are non-nil for the lifetime of the TXM; do not construct StellarTxm manually with an unresolved Config.
func (*StellarTxm) Close ¶
func (s *StellarTxm) Close() error
func (*StellarTxm) Enqueue ¶
Enqueue submits a Soroban transaction request for asynchronous processing. Returns the transaction ID (auto-generated if TxRequest.ID is empty). If TxRequest.ID is already in flight or tracked, returns that same id with a nil error and does not enqueue again (idempotent, aligned with EVM TxMgr idempotency key behavior).
func (*StellarTxm) EnqueueAndWait ¶
EnqueueAndWait submits a transaction and blocks until it reaches a terminal state (Finalized, Failed) or the context is cancelled.
func (*StellarTxm) GetStatus ¶
func (s *StellarTxm) GetStatus(transactionID string) (commontypes.TransactionStatus, error)
func (*StellarTxm) GetTransactionFee ¶
func (s *StellarTxm) GetTransactionFee(transactionID string) (*big.Int, error)
func (*StellarTxm) GetTransactionResult ¶
func (s *StellarTxm) GetTransactionResult(transactionID string) (*TxResult, error)
func (*StellarTxm) HealthReport ¶
func (s *StellarTxm) HealthReport() map[string]error
func (*StellarTxm) InflightCount ¶
func (s *StellarTxm) InflightCount() (int, int)
InflightCount returns (queued broadcast work items, total unconfirmed across all accounts).
func (*StellarTxm) Name ¶
func (s *StellarTxm) Name() string
func (*StellarTxm) Ready ¶
func (s *StellarTxm) Ready() error
func (*StellarTxm) Simulate ¶
func (s *StellarTxm) Simulate(ctx context.Context, req TxRequest) (protocolrpc.SimulateTransactionResponse, error)
Simulate performs a read-only simulation without consuming a sequence number or broadcasting. Callers receive the raw SimulateTransactionResponse so they can inspect resource usage, auth entries, and return values. This is the entry point for InvokerAdapter.SimulateContract and other read-only queries.
type TxRequest ¶
type TxRequest struct {
ID string // idempotency key (auto-generated if empty)
FromAddress string // optional; defaults to TXM's signer address
Operations []txnbuild.Operation // the Stellar operations to execute
LedgerBoundsOffset uint32 // per-tx override (0 = use config default)
Metadata *commontypes.TxMeta // optional; carries WorkflowExecutionID and other node-level context
}
TxRequest is the input accepted by Enqueue / EnqueueAndWait.
type TxResult ¶
type TxResult struct {
ID string
Hash string
Status commontypes.TransactionStatus
Fee *big.Int // total fee charged in stroops
ResultXDR string // XDR-encoded transaction result from GetTransaction
ResultMetaXDR string // XDR-encoded result meta from GetTransaction
Error error
}
TxResult is returned by EnqueueAndWait and Simulate with the outcome of a transaction.
type TxStore ¶
type TxStore struct {
// contains filtered or unexported fields
}
TxStore tracks sequence numbers and in-flight transactions for a single Stellar account. Sequence numbers are strictly sequential: a gap blocks all subsequent transactions. The failed-sequence recycling logic ensures gaps are plugged.
func NewTxStore ¶
func (*TxStore) AddUnconfirmed ¶
AddUnconfirmed records a transaction that has been submitted to the network. The sequence must match the value returned by the preceding GetNextSequence() call.
func (*TxStore) Confirm ¶
Confirm removes a transaction from the unconfirmed set. If failed is true and the sequence is still ahead of the last known on-chain sequence, it is added to failedSequences for recycling.
func (*TxStore) GetLastResyncedNonce ¶
func (*TxStore) GetNextSequence ¶
GetNextSequence returns the next sequence number to use. If there are failed (recycled) sequences, it returns the smallest of (nextSequence, min(failedSequences)) to plug gaps.
func (*TxStore) GetUnconfirmed ¶
func (s *TxStore) GetUnconfirmed() []*UnconfirmedTx
GetUnconfirmed returns a sorted (by sequence) snapshot of all unconfirmed transactions.
func (*TxStore) InflightCount ¶
func (*TxStore) Release ¶
Release returns an allocated-but-never-broadcast sequence to the failed pool for reuse. The broadcaster must call this at every early-return error path between GetNextSequence() and SendTransaction (e.g., simulation fails, assembly errors, signing errors). Without this, a pre-broadcast failure would permanently leak a sequence number.
func (*TxStore) ResyncNonce ¶
ResyncNonce updates the TxStore's view of on-chain state.
IMPORTANT: On Stellar, the on-chain account.SeqNum is the LAST USED sequence. The caller must pass onchainSeq+1 (the next expected sequence number).
This must not be called between GetNextSequence() and AddUnconfirmed(), as it mutates nextSequence.