Exact scValToNative outputs
These fixtures use the following values. They illustrate encoding; they are not payment instructions for a live name.scValToNative(returnedScVal), resolve_destination returns exactly one of these shapes:
Both numeric memos and muxed IDs are
bigint, including small values. Their range is 0n through 18446744073709551615n. Do not convert them to JavaScript number. Muxed { account, id } identifies the full M address and carries no separate memo. C destinations only support ["None"].
Text is a JavaScript string containing 1–28 UTF-8 bytes, not necessarily 1–28 characters. Preserve its bytes, including whitespace and a leading byte-order mark. Hash values are a 32-byte Uint8Array under SDK 17, not a hex string until you explicitly encode them.
None is not null
The optional values are not encoded as
["Some", value] or ["None"]. A payment memo is a separate custom enum with different wire encoding.
Nested resolution results
resolve_v2 returns a struct whose result field wraps the destination:
result: ["NativePayment", ["Muxed", { account: G, id: 420n }]]. The legacy shape is result: ["LegacyAddress", G]; this leaves memo capability unknown, so it is not a complete payment instruction. The current deployment does not enable legacy routing. Legacy vectors exist to test decoder compatibility.
scValToNative decodes an ScVal; it does not establish that the result conforms to this ABI. A String and a Symbol can both become JavaScript strings, for example. Check simulation success and restoration first, then validate variant arity, field names, wire types, address types and memo bounds. The reference decoder below validates the XDR boundary before returning the native shape. See Stellar’s custom type encoding.
Status and batch shapes
The additive status and batch capabilities each report version1; the payment ABI remains version 2. With raw scValToNative, name_status returns:
record has the five fields illustrated above. For an expired state, its nonzero expires_at must be less than the result’s timestamp. Validate the returned name and node against your request. The downloadable vectors contain exact valid node bytes.
For batch inputs, encode a G/C address as [Symbol("Direct"), Address(G_or_C)] and a muxed route as [Symbol("Muxed"), {account: Address(G), id: U64(id)}]. These describe XDR construction; they are not JavaScript constructors. The full input is a vector of at most two identity variants. Both primary_names and reverse_names return this native shape:
Here
None is an enum array, unlike null from the older single-read Option<String>. Error codes are positive u32 numbers. Preserve unknown codes as failures. Results have exactly the input count and order, including duplicates. Empty input yields results: []. Whole-simulation failures have no usable result vector. See the resource limits and retry rules.
Generated bindings represent these variants with tagged objects instead: {tag: "Active", values: [record]}, {tag: "None", values: undefined} or {tag: "Failed", values: [10]}. They also wrap each method’s Result; unwrap it only after checking the read outcome.
Downloadable test vectors
Download lookup-returns-v1.json, or use the source copy. It contains:- 19 valid return values: all destination variants, u64 boundaries, UTF-8 text boundaries, nested resolution, optional names and active/expired metadata.
- 23 rejection cases: malformed variants, missing or extra fields, incorrect XDR types, invalid UTF-8, invalid memo sizes, C-with-memo, incorrect muxed bases and truncated XDR.
- The Lookup executable hash and relevant embedded spec entries used to encode the fixtures. The payment fixture file retains its original hash; those return types are unchanged by the additive read upgrade.
scValToNative result. JSON cannot represent bigint or typed byte arrays, so the file uses {"$bigint":"77"} for 77n and {"$bytes":"0001..."} for a byte array. These markers describe the test file; the SDK does not return those marker objects. Ordinary null remains null.
These are synthetic fixtures matching the deployed ABI. They do not prove a live record, successful transaction or trusted RPC result. Test against your own decoder as well as the fixture verifier.
The standalone example includes the strict decoder, normalization helper, dependency lockfile and tests. It depends only on Stellar SDK. From a checkout of the docs repository:
xdrBase64 with xdr.ScVal.fromXDR(value, "base64"), decode it, and compare its tagged JSON representation with expectedNative. Every item in invalid must fail the relevant typed decoder even if generic ScVal decoding succeeds. decode-reads.mjs validates status against expectedName and batches against expectedCount. The combined suite runs 77 tests.
Generated bindings
The deployed Lookup embeds its spec. For a Stellar SDK 17 app, generate compatible TypeScript code with the SDK’s own generator:Result wrapper is also distinct from the raw RPC return ScVal. Handle RPC/restore failures and contract errors before using its value. Regenerate and review bindings when adopting a changed ABI; generating once does not pin future contract code. See the Stellar SDK generator reference.
The Rust Stellar CLI also supports stellar contract bindings typescript --network testnet --contract-id <id> --output-dir <directory>. Check the generated package’s SDK dependency. CLI 25.2.0 generates SDK 14.5 code; changing only that dependency to SDK 17 fails typechecking. Use the tested SDK 17 generator above for this guide. See the Stellar CLI reference.
Normalize UI input
Use the example’snormalizeName before encoding a name argument. It trims surrounding whitespace, rejects remaining non-ASCII characters before case conversion, lowercases and validates the two labels. For example: