txs

package
v1.3.15 Latest Latest
Warning

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

Go to latest
Published: Jun 22, 2026 License: BSD-3-Clause Imports: 6 Imported by: 0

Documentation

Overview

Package txs defines transaction types for the DEX VM — a STATELESS ATOMIC ZAP PROXY. There is NO local matching, NO AMM, NO embedded order book. The transaction surface is exactly the two concerns a proxy owns:

  1. ATOMIC VALUE MOVEMENT (C-Chain <-> proxy), modeled on the platformvm Import/Export txs that move value between primary-network chains in a single atomic shared-memory commit: - TxImport : claim value exported from C-Chain into the proxy. - TxExport : settle proceeds (and unfilled IOC remainder) back to C-Chain, derived ONLY from confirmed d-chain fills.

  2. ORDER RELAY (proxy -> d-chain over ZAP), transport only: - TxRelayOrder : an opaque, byte-identical clob_* ZAP payload plus a reference to the collateral already locked by an Import. The matcher is the d-chain; the proxy never matches. - TxPlaceOrder / TxCancelOrder : thin relay envelopes (a CLOB place / cancel forwarded verbatim). They carry NO price/size matching semantics in the proxy — the d-chain is the single source of truth.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidSignature is returned by RelayOrderTx.Verify when a relay carries a
	// signature that does not recover to its From (a spoofed-From relay). It is the
	// admission-time provenance gate; settle authority itself derives from the
	// consumed C->D object's recorded owner (the escrow owner), not from From.
	ErrInvalidSignature  = errors.New("invalid signature")
	ErrInvalidTxType     = errors.New("invalid transaction type")
	ErrInvalidAmount     = errors.New("invalid amount")
	ErrInvalidPrice      = errors.New("invalid price")
	ErrInsufficientFunds = errors.New("insufficient funds")
)

Functions

func Marshal added in v1.3.8

func Marshal[T any](tx *T, txType TxType) ([]byte, error)

Marshal serializes a concrete transaction struct into wire bytes: type byte + JSON body.

Types

type AtomicInput added in v1.3.8

type AtomicInput struct {
	UTXOID ids.ID `json:"utxoId"`
	Asset  ids.ID `json:"asset"`
	Amount uint64 `json:"amount"`
}

AtomicInput references a UTXO exported from the source chain that an Import consumes via shared memory. UTXOID is the source-chain UTXO id (the InputID()); Asset and Amount describe the value being claimed.

type AtomicOutput added in v1.3.8

type AtomicOutput struct {
	Rail   Rail        `json:"rail,omitempty"`
	Owner  ids.ShortID `json:"owner"`
	Asset  ids.ID      `json:"asset"`
	Amount uint64      `json:"amount"`
	// Spent is the matched INPUT amount (in the locked/input asset's units) that produced
	// this output, set ONLY on a swap PROCEEDS leg (RailSwap, output asset != locked
	// asset). It rides the shared-memory object so the C-side ImportSettlement can enforce
	// the taker's OWN recorded slippage limit (proceeds >= spent * worstRate) INDEPENDENTLY
	// of the keeper that built the relay — the taker-authenticated MEV/sandwich floor. It is
	// 0 on a REFUND leg (output asset == locked asset; the per-taker principal cap governs
	// that leg) and on every LP leg. It is informational to value conservation (the object's
	// Amount alone moves value); it is the price-binding witness the proceeds floor checks.
	Spent uint64 `json:"spent,omitempty"`
}

AtomicOutput is a value output: an Import credits these locally; an Export puts these into shared memory for the destination chain to claim. Rail is the lane the value travels (RailSwap default / RailLP), bound byte-for-byte into the shared-memory object so the precompile's matching consume path is the only one that can claim it.

type BaseTx

type BaseTx struct {
	TxID      ids.ID      `json:"-"`
	TxType    TxType      `json:"type"`
	From      ids.ShortID `json:"from"`
	Nonce     uint64      `json:"nonce"`
	GasPrice  uint64      `json:"gasPrice"`
	GasLimit  uint64      `json:"gasLimit"`
	CreatedAt int64       `json:"createdAt"`
	Signature []byte      `json:"signature"`
	// contains filtered or unexported fields
}

BaseTx contains common fields for all transactions.

TxID is intentionally NOT serialized (json:"-"): the transaction ID is the SHA-256 checksum of the wire bytes, so embedding it in those same bytes would be circular and would make the ID depend on whatever value happened to be in the field at marshal time. It is always (re)derived from the wire on Parse, and stamped by finalize on construction.

func (*BaseTx) Bytes

func (tx *BaseTx) Bytes() []byte

func (*BaseTx) ID

func (tx *BaseTx) ID() ids.ID

func (*BaseTx) Sender

func (tx *BaseTx) Sender() ids.ShortID

func (*BaseTx) Timestamp

func (tx *BaseTx) Timestamp() int64

func (*BaseTx) Type

func (tx *BaseTx) Type() TxType

type CancelOrderTx

type CancelOrderTx struct {
	BaseTx
	PoolID  [32]byte `json:"poolId"`
	OrderID uint64   `json:"orderId"`
}

CancelOrderTx is a thin relay envelope for a CLOB cancel. The d-chain authenticates the cancel against the resting order's maker — the proxy only forwards the (market, orderID) reference.

func NewCancelOrderTx

func NewCancelOrderTx(from ids.ShortID, nonce uint64, poolID [32]byte, orderID uint64) *CancelOrderTx

NewCancelOrderTx creates a new cancel-order relay envelope.

func (*CancelOrderTx) Verify

func (tx *CancelOrderTx) Verify() error

type ExportTx added in v1.3.8

type ExportTx struct {
	BaseTx
	DestinationChain ids.ID         `json:"destinationChain"`
	ExportedOutputs  []AtomicOutput `json:"exportedOutputs"`
	// FillRef binds this settlement to the d-chain relay receipt whose confirmed
	// fills justify the exported amounts (replay-idempotency / audit).
	FillRef ids.ID `json:"fillRef"`
}

ExportTx settles proceeds back to C-Chain (the DestinationChain). The atomic shared-memory export leg: ExportedOutputs are written into shared memory for the destination chain to claim. The amounts MUST be derived ONLY from confirmed d-chain fills (and any unfilled IOC remainder being refunded) — the proxy never mints. This is the FINAL leg of the conservation ordering.

func NewExportTx added in v1.3.8

func NewExportTx(from ids.ShortID, nonce uint64, destChain ids.ID, out []AtomicOutput, fillRef ids.ID) *ExportTx

NewExportTx creates a new export transaction.

func NewSettlementExportTx added in v1.3.8

func NewSettlementExportTx(from ids.ShortID, txIndex uint32, destChain ids.ID, out []AtomicOutput, fillRef ids.ID, createdAt int64) *ExportTx

NewSettlementExportTx builds the settlement export the VM constructs INSIDE block processing (settleFromFills) — never a client-submitted tx. Because it is reconstructed independently on every validator, its identity must be a pure function of consensus-agreed inputs only. The wall-clock CreatedAt used by the client-facing NewExportTx would make tx.ID() (and therefore any shared-memory UTXO key derived from it) diverge per node, splitting the atomic commit. Here CreatedAt is pinned to createdAt — the deterministic settlement coordinate (the relay's UnixNano block time) supplied by the caller — so the wire bytes, the TxID, and every derived export UTXO key are byte-identical across validators. Nonce carries the settlement's txIndex so two settlements in the same block produce distinct identities.

func (*ExportTx) Verify added in v1.3.8

func (tx *ExportTx) Verify() error

type ImportTx added in v1.3.8

type ImportTx struct {
	BaseTx
	SourceChain    ids.ID         `json:"sourceChain"`
	ImportedInputs []AtomicInput  `json:"importedInputs"`
	Outputs        []AtomicOutput `json:"outputs"`
}

ImportTx claims value exported from C-Chain (the SourceChain) into the proxy. The atomic shared-memory import leg: the imported UTXOs are removed from shared memory and their value credited to local outputs. This is the FIRST leg of the conservation ordering (import -> ZAP submit -> export).

func NewImportTx added in v1.3.8

func NewImportTx(from ids.ShortID, nonce uint64, sourceChain ids.ID, in []AtomicInput, out []AtomicOutput) *ImportTx

NewImportTx creates a new import transaction.

func (*ImportTx) Verify added in v1.3.8

func (tx *ImportTx) Verify() error

type PlaceOrderTx

type PlaceOrderTx struct {
	BaseTx
	// PoolID is the 32-byte market identity (V4 poolId) the d-chain keys by.
	PoolID [32]byte `json:"poolId"`
	Side   uint8    `json:"side"` // 0 = Buy/bid, 1 = Sell/ask
	Price  uint64   `json:"price"`
	Size   uint64   `json:"size"`
	// CollateralRef references the Import whose locked value backs this order.
	CollateralRef ids.ID `json:"collateralRef"`
}

PlaceOrderTx is a thin relay envelope for a CLOB limit-order placement. It carries the wire fields a clob_place frame needs but NO matching logic — the VM forwards it to the d-chain and settles from the returned ack/fills.

func NewPlaceOrderTx

func NewPlaceOrderTx(from ids.ShortID, nonce uint64, poolID [32]byte, side uint8, price, size uint64) *PlaceOrderTx

NewPlaceOrderTx creates a new place-order relay envelope.

func (*PlaceOrderTx) Verify

func (tx *PlaceOrderTx) Verify() error

type Rail added in v1.3.14

type Rail uint8

Rail is the cross-chain object's lane discriminator — the FIRST wire byte of the shared-memory object (atomic.go encodeExportedOutput). It is the H1-closing tag the precompile (precompile/dex/native_wire.go Rail) binds on the C side: a swap-fill object is RailSwap, an LP-collect object is RailLP, and each C-side consume path accepts ONLY its own rail (so a cross-rail consume can never reach the wrong pot). The proxy stamps it on every exported output and binds it on every import, so the rail round-trips through the atomic core unchanged.

const (
	// RailSwap is the swap-fill / refund lane — the ZERO value, so an output whose rail
	// is unstated (the proxy's settleFromFills fill/refund exports, every legacy
	// custody/conservation path) defaults to the swap rail. The precompile's
	// ImportSettlement consumes ONLY this rail.
	RailSwap Rail = 0
	// RailLP is the LP position-commit / collect lane — a non-zero tag the proxy's
	// executeWithdraw stamps for an LP collect/withdraw. The precompile's
	// ImportPositionCollect consumes ONLY this rail.
	RailLP Rail = 1
)

type RelayOrderTx added in v1.3.8

type RelayOrderTx struct {
	BaseTx
	// Method is the ZAP CLOB method ("clob_submit", "clob_place", "clob_cancel").
	Method string `json:"method"`
	// Payload is the opaque, byte-identical clob_* frame forwarded verbatim.
	Payload []byte `json:"payload"`
	// CollateralRef references the Import whose locked value backs this order.
	CollateralRef ids.ID `json:"collateralRef"`
	// AssetOut is the REAL injective output-asset id the taker receives for a settling
	// clob_submit — the OPPOSITE side of the market this order trades on, the same
	// assetID(currency_out) the C-side ImportSettlement requires (recAsset==claim.Asset,
	// the id that keys seamReserve[assetOut]). The HIGH-1 fix: settleFromFills exports
	// the PROCEEDS leg under THIS id, NOT a SHA256(ref||leg) routing handle (which never
	// matched, so a swap's output was permanently unclaimable). It is keeper-asserted and
	// signature-bound (JSON-covered by SigningBytes); a wrong value can only break the
	// taker's OWN liveness — ImportSettlement equality-rejects a mismatched asset (no
	// theft, no mint), the same bounded surface as the carried-fills proposer-trust model.
	// PriceLimit is the worst-acceptable CLOB price (quote-per-base, float64-bits) for a
	// settling clob_submit, derived from the V4 SqrtPriceLimitX96. settleFromFills refuses
	// any carried fill WORSE than this (BUY: price above the limit; SELL: price below it),
	// enforcing the taker's slippage floor against a sandwich/MEV move. Zero = no limit.
	// LimitIsUpper records the direction the limit bounds (true: reject price ABOVE the
	// limit, the BUY/exact-input ceiling; false: reject price BELOW it, the SELL floor).
	// Empty/zero for place/cancel (non-settling) relays.
	AssetOut     ids.ID `json:"assetOut,omitempty"`
	PriceLimit   uint64 `json:"priceLimit,omitempty"`
	LimitIsUpper bool   `json:"limitIsUpper,omitempty"`
}

RelayOrderTx forwards an opaque clob_* ZAP payload to the d-chain matcher, bound to a locked-collateral reference. The payload is the byte-identical clob_submit/clob_place frame the d-chain's zap_server.go expects; the proxy is pure transport and does not interpret it beyond routing.

func NewRelayOrderTx added in v1.3.8

func NewRelayOrderTx(from ids.ShortID, nonce uint64, method string, payload []byte, collateralRef ids.ID) *RelayOrderTx

NewRelayOrderTx creates a new relay-order transaction (UNSIGNED). From carries no settle authority (the escrow owner does), so an unsigned relay is admissible; a client that wants authenticated From provenance calls Sign afterwards. assetOut is the real output-asset id a settling clob_submit credits (the HIGH-1 fix); it is ids.Empty for non-settling place/cancel relays. priceLimit/limitIsUpper carry the taker's slippage floor (0 = none).

func NewSettlingRelayOrderTx added in v1.3.15

func NewSettlingRelayOrderTx(from ids.ShortID, nonce uint64, payload []byte, collateralRef, assetOut ids.ID, priceLimit uint64, limitIsUpper bool) *RelayOrderTx

NewSettlingRelayOrderTx builds a settling clob_submit relay carrying the output-asset id + slippage limit the proxy settles against. The keeper (which knows the market) populates assetOut (the opposite side of the market) and the price limit derived from the taker's V4 SqrtPriceLimitX96. This is the canonical constructor for the settling rail; non-settling relays use NewRelayOrderTx (assetOut empty).

func (*RelayOrderTx) Sign added in v1.3.15

func (tx *RelayOrderTx) Sign(key *secp256k1.PrivateKey) error

Sign stamps the relay with a secp256k1 signature over SigningBytes by `key`, and sets From to key's EVM address (the owner format the C->D object carries). After this, Verify authenticates From cryptographically. Used by clients/keepers that build relays; the proxy itself never signs (it derives authority from escrow).

func (*RelayOrderTx) SigningBytes added in v1.3.15

func (tx *RelayOrderTx) SigningBytes() ([]byte, error)

SigningBytes is the canonical message a RelayOrderTx signature commits to: the wire bytes with the Signature field cleared (so the signature binds From, Method, Payload, CollateralRef, Nonce — but never itself). Deterministic (the struct has no maps; encoding/json emits fields in declaration order).

func (*RelayOrderTx) Verify added in v1.3.8

func (tx *RelayOrderTx) Verify() error

type Tx

type Tx interface {
	// ID returns the unique identifier for this transaction.
	ID() ids.ID
	// Type returns the transaction type.
	Type() TxType
	// Sender returns the sender's address.
	Sender() ids.ShortID
	// Timestamp returns when the transaction was created.
	Timestamp() int64
	// Bytes returns the serialized transaction.
	Bytes() []byte
	// Verify validates the transaction.
	Verify() error
}

Tx is the interface for all DEX proxy transactions.

type TxParser

type TxParser struct{}

TxParser parses raw transaction bytes.

func (*TxParser) Parse

func (p *TxParser) Parse(data []byte) (Tx, error)

Parse parses a transaction from bytes.

type TxType

type TxType uint8

TxType represents the type of transaction.

const (
	// TxImport claims value exported from C-Chain into the proxy (atomic
	// shared-memory import leg). Consumes imported UTXOs, credits local outputs.
	TxImport TxType = iota
	// TxExport settles proceeds back to C-Chain (atomic shared-memory export
	// leg). Produces exported outputs derived ONLY from confirmed d-chain fills.
	TxExport
	// TxRelayOrder forwards an opaque clob_* ZAP payload to the d-chain matcher,
	// bound to a locked-collateral reference. Transport only — no local match.
	TxRelayOrder
	// TxPlaceOrder is a thin relay envelope for a CLOB limit-order placement.
	TxPlaceOrder
	// TxCancelOrder is a thin relay envelope for a CLOB cancel.
	TxCancelOrder
)

func (TxType) String

func (t TxType) String() string

Jump to

Keyboard shortcuts

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