svm

package
v0.0.41 Latest Latest
Warning

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

Go to latest
Published: Jul 14, 2026 License: MIT Imports: 29 Imported by: 0

Documentation

Overview

Package svm implements the Solana transaction builder for Push Chain cross-chain outbounds.

Two-signature model

Every gateway tx carries two signatures:

  • TSS (secp256k1/ECDSA): authorizes the cross-chain op. Signs the keccak256 of the canonical message; gateway recovers via secp256k1_recover and checks against the TSS PDA's stored ETH address.
  • Relayer (Ed25519): standard Solana tx signature. Relayer pays the SOL fee.

Gateway entry points

  • finalize_universal_tx — id=1 withdraw, id=2 execute (CPI).
  • finalize_universal_tx_with_ix_data_ref — same flow but ix_data is loaded from a stored PDA (large-payload path). See the Ref-Finalize Route section.
  • revert_universal_tx — id=3, refund-on-failure for SOL and SPL.
  • rescue_funds — id=4, emergency drain of locked vault funds.

Gateway-internals shorthand used throughout this file:

  • PDA: deterministic address from seeds + program ID (Solana CREATE2 analog).
  • Anchor discriminator: sha256("global:<method>")[:8] — 8-byte function selector.
  • Borsh: Solana's binary encoding — LE integers, Vec<T> = 4-byte LE len + bytes.
  • CEA: per-sender identity PDA used as the CPI signer for execute mode.

Index

Constants

View Source
const (
	EventTypeSendFunds = "send_funds"
	// Outbound observation events (emitted by gateway on SVM since there's no vault)
	EventTypeFinalizeUniversalTx = "finalize_universal_tx"
	EventTypeRevertUniversalTx   = "revert_universal_tx"
	EventTypeFundsRescued        = "funds_rescued"
)

Event type constants

Variables

This section is empty.

Functions

func ParseEvent added in v0.0.13

func ParseEvent(log string, signature string, slot uint64, logIndex uint, eventType string, chainID string, logger zerolog.Logger) *store.Event

ParseEvent parses a log into a store.Event based on the event type. eventType should be one of: "send_funds", "executeUniversalTx", "revertUniversalTx"

Types

type ChainMetaOracle added in v0.0.19

type ChainMetaOracle struct {
	// contains filtered or unexported fields
}

ChainMetaOracle handles fetching and reporting gas prices (prioritization fees)

func NewChainMetaOracle added in v0.0.19

func NewChainMetaOracle(
	rpcClient *RPCClient,
	pushSigner *pushsigner.Signer,
	chainID string,
	gasPriceIntervalSeconds int,
	gasPriceMarkupPercent int,
	logger zerolog.Logger,
) *ChainMetaOracle

NewChainMetaOracle creates a new gas oracle

func (*ChainMetaOracle) Start added in v0.0.19

func (g *ChainMetaOracle) Start(ctx context.Context) error

Start begins fetching and voting on gas prices

func (*ChainMetaOracle) Stop added in v0.0.19

func (g *ChainMetaOracle) Stop()

Stop stops the gas oracle

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client implements the ChainClient interface for Solana chains

func NewClient

func NewClient(
	config *uregistrytypes.ChainConfig,
	database *db.DB,
	chainConfig *config.ChainSpecificConfig,
	pushSigner *pushsigner.Signer,
	nodeHome string,
	logger zerolog.Logger,
) (*Client, error)

NewClient creates a new Solana chain client

func (*Client) ChainID added in v0.0.13

func (c *Client) ChainID() string

ChainID returns the chain ID string

func (*Client) GetConfig added in v0.0.13

func (c *Client) GetConfig() *uregistrytypes.ChainConfig

GetConfig returns the registry chain config

func (*Client) GetTxBuilder added in v0.0.13

func (c *Client) GetTxBuilder() (common.TxBuilder, error)

GetTxBuilder returns the TxBuilder for this chain

func (*Client) IsHealthy

func (c *Client) IsHealthy() bool

IsHealthy checks if the Solana chain RPC client is healthy

func (*Client) Start

func (c *Client) Start(ctx context.Context) error

Start initializes and starts the Solana chain client

func (*Client) Stop

func (c *Client) Stop() error

Stop gracefully shuts down the Solana chain client

type EventConfirmer added in v0.0.13

type EventConfirmer struct {
	// contains filtered or unexported fields
}

EventConfirmer periodically checks pending events and marks them as CONFIRMED once their transactions are confirmed on-chain.

func NewEventConfirmer added in v0.0.13

func NewEventConfirmer(
	rpcClient *RPCClient,
	database *db.DB,
	chainID string,
	pollIntervalSeconds int,
	fastConfirmations uint64,
	standardConfirmations uint64,
	logger zerolog.Logger,
) *EventConfirmer

NewEventConfirmer creates a new event confirmer

func (*EventConfirmer) Start added in v0.0.13

func (ec *EventConfirmer) Start(ctx context.Context) error

Start begins checking and confirming events

func (*EventConfirmer) Stop added in v0.0.13

func (ec *EventConfirmer) Stop()

Stop stops the event confirmer

type EventListener added in v0.0.13

type EventListener struct {
	// contains filtered or unexported fields
}

EventListener listens for gateway events on SVM chains and stores them in the database

func NewEventListener added in v0.0.13

func NewEventListener(
	rpcClient rpcClientInterface,
	gatewayAddress string,
	chainID string,
	gatewayMethods []*uregistrytypes.GatewayMethods,
	database *db.DB,
	eventPollingSeconds int,
	eventStartFrom *int64,
	logger zerolog.Logger,
) (*EventListener, error)

NewEventListener creates a new SVM event listener

func (*EventListener) IsRunning added in v0.0.13

func (el *EventListener) IsRunning() bool

IsRunning returns whether the listener is currently running

func (*EventListener) Start added in v0.0.13

func (el *EventListener) Start(ctx context.Context) error

Start begins listening for gateway events

func (*EventListener) Stop added in v0.0.13

func (el *EventListener) Stop() error

Stop gracefully stops the event listener

type GatewayAccountMeta added in v0.0.16

type GatewayAccountMeta struct {
	Pubkey     [32]byte // raw 32-byte pubkey, not base58
	IsWritable bool
}

GatewayAccountMeta describes one CPI account the target program needs for execute-mode (instruction_id=2) outbounds. Mirrors the Rust struct in state.rs.

type RPCClient added in v0.0.13

type RPCClient struct {
	// contains filtered or unexported fields
}

RPCClient provides SVM-specific RPC operations

func NewRPCClient added in v0.0.13

func NewRPCClient(rpcURLs []string, expectedGenesisHash string, logger zerolog.Logger) (*RPCClient, error)

NewRPCClient creates a new SVM RPC client from RPC URLs and validates genesis hash

func (*RPCClient) BroadcastTransaction added in v0.0.13

func (rc *RPCClient) BroadcastTransaction(ctx context.Context, tx *solana.Transaction) (string, error)

BroadcastTransaction broadcasts a signed transaction and returns the transaction signature (hash)

func (*RPCClient) Close added in v0.0.13

func (rc *RPCClient) Close()

Close closes all RPC connections

func (*RPCClient) GetAccountData added in v0.0.13

func (rc *RPCClient) GetAccountData(ctx context.Context, pubkey solana.PublicKey) ([]byte, error)

GetAccountData fetches account data for a given public key. Uses Solana RPC's default commitment (`finalized`, per the JSON-RPC spec) — reorg-safe for both terminal decisions and race-recovery probes.

func (*RPCClient) GetGasPrice added in v0.0.13

func (rc *RPCClient) GetGasPrice(ctx context.Context) (*big.Int, error)

GetGasPrice fetches the current gas price (prioritization fee) from Solana

func (*RPCClient) GetLatestSlot added in v0.0.13

func (rc *RPCClient) GetLatestSlot(ctx context.Context) (uint64, error)

GetLatestSlot returns the latest slot number

func (*RPCClient) GetRecentBlockhash added in v0.0.13

func (rc *RPCClient) GetRecentBlockhash(ctx context.Context) (solana.Hash, error)

GetRecentBlockhash gets a recent blockhash for transaction building

func (*RPCClient) GetSignaturesForAddress added in v0.0.13

func (rc *RPCClient) GetSignaturesForAddress(ctx context.Context, address solana.PublicKey, before solana.Signature) ([]*rpc.TransactionSignature, error)

GetSignaturesForAddress gets transaction signatures for an address. If `before` is the zero signature, fetching starts from the most recent block; otherwise it returns signatures strictly older than `before`, enabling backward pagination.

func (*RPCClient) GetTransaction added in v0.0.13

func (rc *RPCClient) GetTransaction(ctx context.Context, signature solana.Signature) (*rpc.GetTransactionResult, error)

GetTransaction gets a transaction by signature

func (*RPCClient) IsHealthy added in v0.0.13

func (rc *RPCClient) IsHealthy(ctx context.Context) bool

IsHealthy checks if any RPC in the pool is healthy by pinging it

func (*RPCClient) LatestFinalizedBlockTime added in v0.0.36

func (rc *RPCClient) LatestFinalizedBlockTime(ctx context.Context) (int64, error)

LatestFinalizedBlockTime returns the unix timestamp of the latest finalized block — the cluster's view of "now" against which on-chain deadline checks fire. Used by broadcaster and resolver to gate deadline-based decisions: comparing local wall-clock to this value catches host-clock skew, full cluster halts (block time stops advancing), and finalization stalls (the latest *finalized* block ages even while production continues).

Returns 0 + nil error if block time is unavailable for the latest slot (e.g., the slot is too new for the RPC to have indexed). Returns 0 + err only when the slot lookup itself fails.

func (*RPCClient) SimulateTransaction added in v0.0.16

func (rc *RPCClient) SimulateTransaction(ctx context.Context, tx *solana.Transaction) (*rpc.SimulateTransactionResult, error)

SimulateTransaction runs a transaction against the current ledger state without broadcasting. Returns the simulation result (logs, error, compute units consumed). Skips signature verification so the TSS/relayer signatures don't need to be valid.

type RentReclaimer added in v0.0.40

type RentReclaimer struct {
	// contains filtered or unexported fields
}

RentReclaimer closes orphaned StoredIxData PDAs to recover rent (~0.002 SOL each).

  • Orphan = PDA created by store_execute_ix_data whose finalize never succeeded (so the program's auto-close path never ran).
  • Skips PDAs younger than minAge to avoid racing in-flight finalize broadcasts.

func NewRentReclaimer added in v0.0.40

func NewRentReclaimer(builder *TxBuilder, interval, minAge time.Duration, logger zerolog.Logger) *RentReclaimer

func (*RentReclaimer) Start added in v0.0.40

func (r *RentReclaimer) Start(ctx context.Context)

type TxBuilder added in v0.0.13

type TxBuilder struct {
	// contains filtered or unexported fields
}

func NewTxBuilder added in v0.0.13

func NewTxBuilder(
	rpcClient *RPCClient,
	chainID string,
	gatewayAddress string,
	nodeHome string,
	logger zerolog.Logger,
	chainConfig *config.ChainSpecificConfig,
) (*TxBuilder, error)

NewTxBuilder creates a new Solana transaction builder. gatewayAddress must be a valid base58-encoded Solana public key pointing to the deployed gateway program.

func (*TxBuilder) BroadcastFundMigrationTx added in v0.0.28

func (tb *TxBuilder) BroadcastFundMigrationTx(ctx context.Context, req *common.UnsignedSigningReq, data *common.FundMigrationData, signature []byte) (string, error)

func (*TxBuilder) BroadcastOutboundSigningRequest added in v0.0.13

func (tb *TxBuilder) BroadcastOutboundSigningRequest(
	ctx context.Context,
	req *common.UnsignedSigningReq,
	data *uetypes.OutboundCreatedEvent,
	signature []byte,
) (string, error)

BroadcastOutboundSigningRequest assembles a complete Solana transaction with the TSS signature and broadcasts it to the Solana network. For execute-mode outbounds (instruction_id=2) whose direct tx exceeds maxDirectTxSize, falls back to the 2-tx ref-finalize route automatically.

Returned tx hash is always the FINALIZE tx (direct or ref-finalize). The resolver / event listener only need to track this — the store tx is an implementation detail invisible to downstream consumers.

func (*TxBuilder) BuildOutboundTransaction added in v0.0.16

func (tb *TxBuilder) BuildOutboundTransaction(
	ctx context.Context,
	req *common.UnsignedSigningReq,
	data *uetypes.OutboundCreatedEvent,
	signature []byte,
) (*solana.Transaction, uint8, error)

BuildOutboundTransaction assembles a complete signed Solana transaction from the TSS signature and event data, without broadcasting. Returns the transaction and the instruction ID for logging. Use this for simulation or inspection.

func (*TxBuilder) BuildRefRouteTransactions added in v0.0.36

func (tb *TxBuilder) BuildRefRouteTransactions(
	ctx context.Context,
	req *common.UnsignedSigningReq,
	data *uetypes.OutboundCreatedEvent,
	signature []byte,
) (*solana.Transaction, *solana.Transaction, solana.PublicKey, error)

BuildRefRouteTransactions builds the (storeTx, refFinalizeTx) pair for a large-payload execute outbound. Only valid for instructionID=2 with non-empty ix_data; callers should size-check the direct tx first and only invoke this when the direct route doesn't fit.

Returns the storedIxData PDA alongside the txs so the broadcaster can probe for pre-existing PDAs (retry idempotency) before re-broadcasting the store tx.

func (*TxBuilder) GetFundMigrationSigningRequest added in v0.0.28

func (tb *TxBuilder) GetFundMigrationSigningRequest(ctx context.Context, data *common.FundMigrationData, nonce uint64) (*common.UnsignedSigningReq, error)

func (*TxBuilder) GetGasFeeUsed added in v0.0.19

func (tb *TxBuilder) GetGasFeeUsed(ctx context.Context, txHash string) (string, error)

GetGasFeeUsed returns "0" for SVM. SVM gas accounting is handled via vault gasFee reimbursement — the actual gas cost is the base fee + PDA rent paid by the relayer, which is reimbursed from the gasFee baked into the signed message.

func (*TxBuilder) GetNextNonce added in v0.0.16

func (tb *TxBuilder) GetNextNonce(ctx context.Context, signerAddress string, useFinalized bool) (uint64, error)

GetNextNonce returns 0 for SVM. The contract no longer uses a global nonce; replay protection is handled by per-tx ExecutedTx PDAs.

func (*TxBuilder) GetOutboundSigningRequest added in v0.0.13

func (tb *TxBuilder) GetOutboundSigningRequest(
	ctx context.Context,
	data *uetypes.OutboundCreatedEvent,
	nonce uint64,
) (*common.UnsignedSigningReq, error)

GetOutboundSigningRequest creates a signing request from an outbound event. Returns a 32-byte keccak256 hash that the TSS nodes need to sign.

func (*TxBuilder) IsAlreadyExecuted added in v0.0.17

func (tb *TxBuilder) IsAlreadyExecuted(ctx context.Context, txID string) (bool, int64, error)

IsAlreadyExecuted checks if the ExecutedTx PDA for the given txID exists on-chain, indicating another relayer has already processed this transaction. Also returns the latest finalized block's unix timestamp — the cluster's view of "now" — so callers can gate deadline-based decisions against cluster time rather than the host's local clock. queryBlockTime is best-effort: 0 means the RPC couldn't supply it and the caller should treat cluster freshness as unknown (defer irreversible decisions).

func (*TxBuilder) VerifyBroadcastedTx added in v0.0.16

func (tb *TxBuilder) VerifyBroadcastedTx(ctx context.Context, txHash string) (found bool, blockHeight uint64, confirmations uint64, status uint8, err error)

VerifyBroadcastedTx checks the status of a broadcasted transaction on Solana. Returns (found, blockHeight, confirmations, status, error):

  • found=false: tx not found or not yet confirmed
  • found=true: tx exists on-chain
  • confirmations: number of slots since the tx was included (0 = just confirmed)
  • status: 0 = failed, 1 = success

Jump to

Keyboard shortcuts

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