Skip to content

feat(tii): type-directed argument encoding to the TaggedArg wire form#43

Merged
scarmuega merged 1 commit into
mainfrom
feat/tagged-arg-encoder
Jun 22, 2026
Merged

feat(tii): type-directed argument encoding to the TaggedArg wire form#43
scarmuega merged 1 commit into
mainfrom
feat/tagged-arg-encoder

Conversation

@scarmuega

Copy link
Copy Markdown
Contributor

What

Teaches the SDK to serialize an argument of an aggregate type (record, List, Map, Tuple, variant) into the TRP TaggedArg wire form, so trix invoke / any SDK consumer can pass complex args. New tii::encode, wired into Invocation::into_resolve_request.

One walk, not two paths

encode(param, value) is a single recursive walk over (type, value) — scalars are just the leaf cases:

  • A scalar leaf renders bare at the top level (the resolver coerces it via the param's flat TIR type) and tagged when nested inside an aggregate (the resolver has no element type there).
  • An aggregate always renders to its tagged structural form, recursing into elements.

So every mapped arg goes through encode; there's no is_aggregate gate. Bare top-level scalars keep the exact pre-existing wire shape, so this is back-compatible for scalar-only protocols.

Encoding rules

  • Record{ "struct": { "constructor": 0, "fields": [<declared order>] } }. Field order comes from the schema's required array — tx3c emits it in source-declaration order, whereas properties is alphabetized, so required (not properties) drives positional order.
  • Variantstruct with constructor = the case index in the .tii oneOf ordering.
  • Map → array of [key, value] pairs; keys are string leaves (the .tii erases the Tx3 key type), pairs sorted by key for determinism.
  • Reject before sending: missing/extra record field, wrong tuple arity, unknown variant case.
  • unknown / utxo / anyAsset params pass through unchanged (no element types to drive encoding).

Breaking change

ParamType::Record now carries an ordered Vec<(String, ParamType)> instead of a BTreeMap (the encoder needs declared order). Added ParamType::field(name) for by-name lookup. This is the coordinated breaking SDK change; the version bump + publish are a separate release step.

Tests

  • Vector-driven over the shared cross-SDK oracle (tests/fixtures/wire-vectors.json, mirrored from sdk-spec): every accept vector encodes byte-equal, every reject vector errors.
  • Field-order regression (Meta { tags, level }[list, int], not alphabetical [int, list]).
  • Top-level-bare vs nested-tagged scalar rendering.
  • End-to-end into_resolve_request on a real TII: meta tagged, scalars bare.

cargo test -p tx3-sdk --lib green (23); cargo clippy --lib clean.

🤖 Generated with Claude Code

Marshal every arg through one recursive walk over `(ParamType, value)`
in `tii::encode`, wired into `Invocation::into_resolve_request`. A
scalar leaf renders bare at the top level (the resolver coerces it via
the flat TIR type) and tagged when nested in an aggregate; aggregates
(list/tuple/map/record/variant) render to the self-describing tagged
form. There is no separate scalar/aggregate path — scalars are the leaf
cases of the same walk.

Records map the user's by-name object to positional `struct` fields in
declared order, taken from the schema's `required` array (which `tx3c`
emits in source order — `properties` is alphabetized and must not drive
field order). Variants resolve the case index from the `oneOf` ordering.
Map keys are carried as `string` leaves (the `.tii` erases the key
type), pairs sorted by key. Ill-shaped values (missing/extra field,
wrong arity, unknown case) are rejected before sending.

BREAKING: `ParamType::Record` now carries ordered `Vec<(String,
ParamType)>` instead of a `BTreeMap`, with a `ParamType::field(name)`
accessor for by-name lookup.

Tests drive the shared cross-SDK oracle (accept + reject vectors) plus
an end-to-end `into_resolve_request` check on a real TII.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@scarmuega scarmuega merged commit 99f2778 into main Jun 22, 2026
5 checks passed
@scarmuega scarmuega deleted the feat/tagged-arg-encoder branch June 22, 2026 16:11
@scarmuega scarmuega mentioned this pull request Jun 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant