STAS 3.0 v0.1 §6.3 swap descriptor — the canonical, recursive form of the
var2 payload pushed by an atomic-swap STAS UTXO (action byte 0x01).
Wire layout (minimum 61 bytes):
offset 0 : action = 0x01 1 B
offset 1 : requestedScriptHash (SHA256) 32 B
offset 33 : receiveAddr (HASH160) 20 B
offset 53 : rateNumerator u32 LE 4 B
offset 57 : rateDenominator u32 LE 4 B
offset 61 : next variable, optionalThe next field, when present, is the var2 value that the maker requires
the taker to install on the maker's remainder UTXO after the swap is fully
or partially consumed (spec §6.3, §9.5). It can be:
- absent (zero bytes after the 61-byte head) →
next = nil - a passive var2 push (action byte
0x00+ arbitrary) →{:passive, bytes} - the frozen marker (single byte
0x02) →:frozen - another swap descriptor BUT with the leading
0x01action byte STRIPPED (per spec §6.3: "Encoding is the same as the top-level descriptor, minus including the leading action byte.") →{:swap, %SwapDescriptor{}}
This module implements both encoding (to_var2_bytes/1) and decoding
(parse/1) of the full recursive structure. The legacy 61-byte non-recursive
form continues to round-trip correctly: to_var2_bytes/1 of a descriptor
with next: nil produces exactly the same bytes that
BSV.Tokens.Script.Stas3Builder.encode_swap_action_data/1 would have
produced for the equivalent swap_fields() map.
Conversion helpers (from_swap_fields/2, to_swap_fields/1) bridge to the
legacy BSV.Tokens.ActionData.swap_fields() map shape used elsewhere in
the codebase.
Summary
Functions
Build a SwapDescriptor from the legacy
BSV.Tokens.ActionData.swap_fields() map (which uses :requested_pkh
rather than :receive_addr). The next field is taken from the second
argument, defaulting to nil for full backward compatibility with the
61-byte form.
Parse a full var2 payload (must include the leading 0x01 action byte)
into a SwapDescriptor.
Project a SwapDescriptor down to the legacy
BSV.Tokens.ActionData.swap_fields() map (drops the recursive next
field). Useful when an existing API only consumes the 61-byte form.
Encode a SwapDescriptor to its full var2 payload, INCLUDING the
leading 0x01 action byte.
Types
@type t() :: %BSV.Tokens.SwapDescriptor{ next: next_value(), rate_denominator: non_neg_integer(), rate_numerator: non_neg_integer(), receive_addr: <<_::160>>, requested_script_hash: <<_::256>> }
Functions
@spec from_swap_fields(BSV.Tokens.ActionData.swap_fields(), next_value()) :: t()
Build a SwapDescriptor from the legacy
BSV.Tokens.ActionData.swap_fields() map (which uses :requested_pkh
rather than :receive_addr). The next field is taken from the second
argument, defaulting to nil for full backward compatibility with the
61-byte form.
Parse a full var2 payload (must include the leading 0x01 action byte)
into a SwapDescriptor.
Recursively decodes the next chain until either:
- the remaining bytes are exhausted (
next = nil), - a
0x00passive marker is consumed (next = {:passive, rest}), - a single-byte
0x02frozen marker is consumed (next = :frozen), - another swap body (no leading
0x01) is consumed (next = {:swap, %SwapDescriptor{}}).
Returns {:ok, descriptor} on success or {:error, reason} on
malformed input (truncated header, frozen marker followed by extra
bytes, or a nested-swap header that does not contain 60 bytes).
@spec to_swap_fields(t()) :: BSV.Tokens.ActionData.swap_fields()
Project a SwapDescriptor down to the legacy
BSV.Tokens.ActionData.swap_fields() map (drops the recursive next
field). Useful when an existing API only consumes the 61-byte form.
Encode a SwapDescriptor to its full var2 payload, INCLUDING the
leading 0x01 action byte.
Returns the raw binary (61+ bytes) suitable for use as the var2 push body in a STAS 3.0 locking script.