Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
688c18b
Class XDR Implementation (#1422)
Ryang-21 Jul 23, 2026
99b2ecd
Migrate public API from Buffer to Uint8Array (#1564)
Ryang-21 Jul 28, 2026
c0fdb6e
fix(xdr): emit SEP-51 key `type` instead of Rust-escaped `type_` (#1571)
Ryang-21 Jul 29, 2026
5233dc5
build(xdr): regenerate xdr.json via docker from pinned stellar-xdr co…
Ryang-21 Jul 31, 2026
49a8cf0
build(xdr): regenerate schema from stellar-xdr with CAP-83 and CAP-85…
Ryang-21 Jul 31, 2026
cf0659b
feat(xdr): support CAP-83 empty tx set values and CAP-85 external exe…
Ryang-21 Jul 31, 2026
a25ca5e
Merge main into v17-feature-branch (#1579)
Ryang-21 Jul 31, 2026
9a52d96
chore: record main merge lost to squash of #1579
Ryang-21 Jul 31, 2026
5a6f76f
fix(xdr): bound decimal string length before BigInt parse in JSON dec…
Ryang-21 Aug 4, 2026
54e4662
fix(xdr): restrict fromJson to SEP-0051 keys and reject unknown field…
Ryang-21 Aug 4, 2026
a4bc598
fix(strkey): bound decodeCheck input length before base32 decode (#1583)
Ryang-21 Aug 4, 2026
b7bf681
fix(xdr): reject AssetCode12 JSON codes shorter than 5 bytes (#1585)
Ryang-21 Aug 4, 2026
5888209
fix(horizon)!: make TransactionFailedExtras result_codes.operations o…
Ryang-21 Aug 4, 2026
8c5aed6
Fix: signed payload strkey framing (#1588)
Ryang-21 Aug 4, 2026
1f8e773
test(xdr): source the real-traffic corpus from both Horizon and RPC (…
quietbits Aug 5, 2026
4b81e0c
test(xdr): decode a forged 101-op transaction instead of reading maxL…
Ryang-21 Aug 5, 2026
e7e91df
Fix: xdr json decode validation (#1592)
Ryang-21 Aug 5, 2026
0b2a397
Docs: xdr migration guide corrections (#1590)
Ryang-21 Aug 5, 2026
5f148de
docs(xdr): clarify that the utf-8 decode change affects no valid oper…
Ryang-21 Aug 5, 2026
4ea82c0
docs(xdr): fix MuxedAccount#setId example and add a staleness check r…
Ryang-21 Aug 5, 2026
9159189
fix(spec): restore instanceof Map check lost in the v17 merge
Ryang-21 Aug 6, 2026
0853c39
feat(xdr): add validateXdr static to every generated type (#1597)
Ryang-21 Aug 6, 2026
34bedce
test(browser): stop the unit suite losing its page mid-run (#1596)
quietbits Aug 6, 2026
6b70835
docs(migration): tighten prose and normalize method references
Ryang-21 Aug 7, 2026
f735704
docs(changelog): apply pr review feedback to v17 entries
Ryang-21 Aug 7, 2026
290a5d8
fix(contract): declare error classes, make types self-contained (#1627)
quietbits Aug 10, 2026
f4a91b2
perf(strkey): reject by length and prefix before decodeCheck throws (…
Ryang-21 Aug 10, 2026
6562185
fix: restore wide-int bounds statics, document Memo.text break (#1628)
quietbits Aug 10, 2026
2f7e8a9
docs(changelog): scope the non-utf8 scvaltonative note to scvstring
Ryang-21 Aug 10, 2026
24d8cb4
docs(changelog): tighten the memo.text breaking-change entry
Ryang-21 Aug 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/npm_publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ jobs:
run: pnpm run preversion

- name: Publish npm package
run: npm publish --access public --provenance
run: npm publish --access public --provenance --tag "rc"
59 changes: 46 additions & 13 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,6 @@ jobs:
- name: Install Dependencies
run: pnpm install --frozen-lockfile --network-concurrency 1

- name: Install Playwright Chromium
run: pnpm exec playwright install chromium

- name: Install Playwright Firefox
run: pnpm exec playwright install firefox

- name: Install Playwright system dependencies
run: pnpm exec playwright install-deps chromium firefox

- name: Build
run: pnpm run build:prod

Expand All @@ -65,11 +56,53 @@ jobs:
if: matrix.node-version == 22
run: pnpm run docs:snippets:check

- name: Browser Tests
run: pnpm run test:browser
# Browser tests get their own job per transport rather than two steps in
# `build_and_test`. Each pass loads the SDK source graph — ~470 unbundled
# `src/xdr` modules — once per test file, across 220 files and two browsers.
# Run back to back on one runner, the second pass reliably lost its browser
# page mid-run ("Browser connection was closed while running tests"), always
# after ~65 files with every test that ran passing. A fresh runner per
# transport keeps each pass within the runner's memory budget.
browser_test:
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
node-version: [22, 24]
transport: [fetch, axios]

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Setup pnpm
uses: pnpm/action-setup@v6

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v7
with:
node-version: ${{ matrix.node-version }}
cache: pnpm
cache-dependency-path: pnpm-lock.yaml

- name: Enable Corepack
run: corepack enable

- name: Install Dependencies
run: pnpm install --frozen-lockfile --network-concurrency 1

- name: Install Playwright Chromium
run: pnpm exec playwright install chromium

- name: Install Playwright Firefox
run: pnpm exec playwright install firefox

- name: Install Playwright system dependencies
run: pnpm exec playwright install-deps chromium firefox

- name: Browser Tests (Axios)
run: pnpm run test:browser:axios
- name: Browser Tests (${{ matrix.transport }})
run: pnpm run test:browser:${{ matrix.transport }}

# Placeholder to satisfy the `build_and_test (20)` required status check in
# the `protect-main` ruleset. Node 20 was dropped from the test matrix
Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,18 @@ config/tsconfig.tmp.json
.copilot/
.codex/

# xdr-roundtrip-monitor failure dumps (scripts/xdr-roundtrip-monitor.ts)
xdr-roundtrip-failures*.jsonl

# xdr-json-parity-monitor failure dumps (scripts/xdr-json-parity-monitor.ts)
xdr-json-parity-failures*.jsonl

# scratch: per-guide tests, never shipped
test-guides/
.vuln-hunt/
scratch/
.docs-build/
.xdr-build/

# Working data for scripts/test-event-bindings.mjs (contract-wasms clone +
# generated bindings under test)
Expand Down
43 changes: 43 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,48 @@

A breaking change will get clearly marked in this log.

## [v17.0.0-rc.1](https://github.com/stellar/js-stellar-sdk/compare/v16.2.0...v17.0.0-rc.1)

### Breaking Changes
* Public APIs use `Uint8Array` instead of Node's `Buffer` ([#1457](https://github.com/stellar/js-stellar-sdk/issues/1457)). Methods that returned `Buffer` (e.g. `hash()`, `Keypair`'s `sign`/`rawPublicKey`/`rawSecretKey`, `StrKey.decode*`, `Transaction.hash()`, `rpc.Server.getContractWasmByHash`, `getLiquidityPoolId()`, `AuthEntrySignature.signature`, and the signing payload passed *to* a `SigningCallback`) now return a plain `Uint8Array`, so Buffer-only conveniences like `.toString("hex")` and `.equals()` on results must be replaced — see [`docs/UINT8ARRAY_MIGRATION.md`](./docs/UINT8ARRAY_MIGRATION.md) for method-by-method recipes. Byte inputs still accept `Buffer` (it's a `Uint8Array` subclass), with three exceptions: a `SigningCallback` may no longer resolve to a raw `ArrayBuffer` (wrap it in a `Uint8Array`), `SorobanDataBuilder`'s constructor no longer accepts non-`Uint8Array` typed arrays, and `Memo.text` no longer accepts a plain `number[]` (see the next entry). The `buffer` dependency is gone (`base32.js`, which needed a Buffer global, is replaced by `@exodus/bytes`), and browsers/edge runtimes need no Buffer polyfill.
* `Memo.text` no longer accepts a plain `number[]`. Pass `new Uint8Array(arr)` instead ([#1457](https://github.com/stellar/js-stellar-sdk/issues/1457)). Through 16.2.0 it took a `string`, a plain array, or a `Buffer`, and rejected a bare `Uint8Array`. A `Uint8Array` is now the canonical byte input, and a plain array is the only input lost. `Memo.text([])` was a valid zero-byte memo and now throws. The error message is unchanged (`Expects string or Uint8Array, max 28 bytes`), so code that matches on it still works. See [`docs/UINT8ARRAY_MIGRATION.md`](./docs/UINT8ARRAY_MIGRATION.md) § 3.
* The `xdr` namespace is rebuilt on `@stellar/js-xdr` v5, and **every XDR value now has a different API** ([#1422](https://github.com/stellar/js-stellar-sdk/pull/1422)). The wire format is unchanged: bytes and base64 written by older SDKs still decode, and vice versa. Any code that reads or builds `xdr.*` values must be updated. The main shifts:
* **Start here: [`docs/XDR_MIGRATION.md`](./docs/XDR_MIGRATION.md) covers every change below with before/after examples and a quick-reference table.**
* Unions are discriminated classes. `.switch()` becomes a `.type` string literal, arm getters like `.contractData()` become properties, and `new xdr.LedgerEntryData(disc, val)` becomes a factory call such as `xdr.LedgerEntryData.contractData(val)`.
* Enums are singletons, not factory calls: `xdr.ContractDataDurability.persistent()` becomes `xdr.ContractDataDurability.persistent`.
* Primitives are plain JS values. Integers are `number` or `bigint` instead of class wrappers, `LargeInt` subclasses are gone, byte fields are `Uint8Array`, and fields are `readonly`.
* Absent optional fields decode to `null` instead of `undefined`, so `=== undefined` checks silently stop matching. Prefer `== null`.
* Acronyms in method names collapse to single-initial-cap form, with no back-compat aliases (e.g. `validateXDR()` is now `validateXdr()`). This reaches beyond the `xdr` namespace to the wrapper classes: `Transaction.toXDR()`, `TransactionBuilder.fromXDR()`, `Operation.fromXDRObject()`, `Asset.toXDRObject()`, `contract.AssembledTransaction.toXDR()` and others all gained the `Xdr` spelling.
* Struct field names are unchanged, but a few type names moved: `UInt128Parts` / `UInt256Parts` are now `Uint128Parts` / `Uint256Parts`, `ThresholdIndices` is now `ThresholdIndexes`, and the typedef aliases `Duration`, `TimePoint`, `SequenceNumber`, `ScVec`, `ScMap`, `LedgerEntryChanges`, `ContractCostParams`, `SorobanAuthorizationEntries`, `ScString`, `ScSymbol`, `String32`, `String64`, and `SponsorshipDescriptor` are gone in favor of what they stood for.
* New: `toJson()` / `fromJson()` for [SEP-0051](https://stellar.org/protocol/sep-51) JSON, `toXdrObject()` / `fromXdrObject()` on XDR values, and `equals()` for structural comparison. Failures throw `xdr.XdrError`, which is now exported.
* Removed: `Reader` and `Writer`; the v4 runtime type constructors (`Hyper`, `UnsignedHyper`, `Option`, `Opaque`, `VarOpaque`, `XDRArray`, `XDRString`, `Bool`, `SignedInt`, `UnsignedInt`), plus top-level `Hyper` / `UnsignedHyper` / `cereal`; and `xdr.scvSortedMap` (use the top-level `scvSortedMap`).
* `ScInt` and `XdrLargeInt` lost their `.int` property; read `.value` (a `bigint`) instead, and note `valueOf()` now returns a `bigint`.

* Rebuilding the XDR layer changed a few SDK-level behaviors that don't involve typing `xdr.` yourself. Most of these fail silently, so they won't surface as compile errors ([#1422](https://github.com/stellar/js-stellar-sdk/pull/1422)):
* [`scValToNative`](https://stellar.github.io/js-stellar-sdk/reference/core-soroban-primitives/#scvaltonative) returns a `Uint8Array` for an `scvString` whose contents aren't valid UTF-8. It previously always returned a string, substituting U+FFFD — its byte-returning branch was unreachable. Guards like `typeof result === "string"` and calls like `result.startsWith(...)` are now data-dependent. (`scvSymbol` follows the same rule, but the host restricts symbols to `[_0-9A-Za-z]`, so a symbol that came off the network always decodes to a string.) The same applies to [`contract.Spec.scValToNative`](https://stellar.github.io/js-stellar-sdk/reference/contracts-client/#specscvaltonativescv-typedef) and [`contract.Spec.funcResToNative`](https://stellar.github.io/js-stellar-sdk/reference/contracts-client/#specfuncrestonativename-val_or_base64) for `Bytes` / `BytesN`, which return `Uint8Array`; those are generically typed, so TypeScript won't flag it.
* [`Operation.fromXdrObject`](https://stellar.github.io/js-stellar-sdk/reference/core-transactions/#operationfromxdrobjectoperation) decodes `manageData`'s `name`, `setOptions`'s `homeDomain`, and `revokeSponsorship`'s data-entry name as UTF-8 rather than ASCII. Only bytes ≥ `0x80` decode differently, and stellar-core rejects those in all three fields, so no valid operation is affected — but snapshots taken over synthetic or forged XDR will change (`[0xC3, 0xA9]` now decodes to `"é"`, was `"C)"`). See the migration guide for the round-trip details.
* `SorobanDataBuilder` still chains, and its setters still mutate the builder. What changed is one level down: because XDR fields are `readonly` now, [`setReadOnly`](https://stellar.github.io/js-stellar-sdk/reference/core-soroban-primitives/#sorobandatabuildersetreadonlyreadonly) / [`setReadWrite`](https://stellar.github.io/js-stellar-sdk/reference/core-soroban-primitives/#sorobandatabuildersetreadwritereadwrite) / [`setResources`](https://stellar.github.io/js-stellar-sdk/reference/core-soroban-primitives/#sorobandatabuildersetresourcescpuinstrs-diskreadbytes-writebytes) replace the internal data rather than edit it in place. Two consequences: a footprint you captured from [`getFootprint()`](https://stellar.github.io/js-stellar-sdk/reference/core-soroban-primitives/#sorobandatabuildergetfootprint) before one of those calls is a stale snapshot, so re-read it afterward; and you can no longer configure the builder *through* that object (`builder.getFootprint().readOnly(keys)`) — call the setters instead.
* [`MuxedAccount.setId`](https://stellar.github.io/js-stellar-sdk/reference/core-transactions/#muxedaccountsetidid) no longer mutates an `xdr.MuxedAccount` you already obtained from `toXdrObject()`; call it again after `setId`.

* `HorizonApi.TransactionFailedExtras`'s `result_codes.operations` is now optional (`operations?: string[]`). Horizon omits the field when a transaction fails a transaction-level check (e.g. `tx_bad_seq`) and no operations were evaluated, so the type now matches the wire format. Under `strictNullChecks`, unguarded reads of the raw response (`extras.result_codes.operations.map(...)`) no longer compile; guard them, or use `TransactionFailedError.getResultCodes()`, which normalizes the omitted field to `[]` ([#1527](https://github.com/stellar/js-stellar-sdk/issues/1527)).

### Added
- `rpc.Server.getExternalRefWasmHash(ref)`: resolves a CAP-85 external executable reference to the 32-byte Wasm hash it names by reading the persistent tag entry on the owner contract ([#1577](https://github.com/stellar/js-stellar-sdk/pull/1577)).
- The XDR schema covers [CAP-83](https://stellar.org/protocol/cap-83) (empty transaction set values), adding a `stellarValueEmptyTxSet` arm to `xdr.StellarValueType` ([#1577](https://github.com/stellar/js-stellar-sdk/pull/1577)).
- The XDR schema covers [CAP-85](https://stellar.org/protocol/cap-85) (external contract executables), adding a `contractExecutableExternalRef` arm to `xdr.ContractExecutableType` — an `executableOwner` address plus a `tag` — and an `scvExecutableTag` arm to `xdr.ScValType` ([#1577](https://github.com/stellar/js-stellar-sdk/pull/1577)).

### Changed
- `scValToNative` converts an `scvExecutableTag` to its tag: a string when the bytes are valid UTF-8, otherwise the raw bytes (same rule as `scvString`) ([#1577](https://github.com/stellar/js-stellar-sdk/pull/1577)).
- `buildInvocationTree` renders CAP-85 external-executable creations instead of throwing. `CreateInvocation.type` gains an `"external"` case, whose details live in a new `external` field (`owner`, `tag`, `address`, `salt`, and `constructorArgs` for `CREATE_CONTRACT_V2`). `tag` is `string | Uint8Array` — an executable tag is an unbounded `SCString`, so a binary one is returned as raw bytes rather than lossily decoded ([#1577](https://github.com/stellar/js-stellar-sdk/pull/1577)).
- `StrKey.decode*` and the underlying `decodeCheck` now validate the encoded string's length against the requested strkey type before decoding it. Two consequences: a long attacker-supplied string is rejected up front instead of driving a full base32 decode plus canonical re-encode, and a strkey whose payload is the wrong size for its type now throws instead of returning a mis-sized buffer (previously, a 37-byte payload encoded as an `ed25519PublicKey` strkey decoded to 37 bytes and only failed later, if at all). Inputs that were already invalid may now report a length error rather than a checksum or version-byte error ([#1583](https://github.com/stellar/js-stellar-sdk/pull/1583)).
- `contract.Client.from` and `rpc.Server.getContractWasmByContractId` support contracts created from a CAP-85 external executable reference. The reference names an owner contract and a tag; the owner holds a persistent contract data entry keyed by that tag whose value is the Wasm hash, so both methods resolve that entry and then load the Wasm as usual ([#1577](https://github.com/stellar/js-stellar-sdk/pull/1577)).
- `contract.Client.txFromJSON` is now `txFromJson`, and generated bindings' `fromJSON` is now `fromJson`, matching the `toJson`/`fromJson` naming used across the XDR layer. Both keep a deprecated alias, so existing calls still work ([#1422](https://github.com/stellar/js-stellar-sdk/pull/1422)).

### Fixed
- `StrKey.decodeSignedPayload` and `StrKey.isValidSignedPayload` now validate the framing inside a `P...` strkey: the declared payload length must be 1-64, must match the number of payload bytes present, and the padding must be zero. The three [SEP-23](https://stellar.org/protocol/sep-23) invalid signed-payload test cases — length prefix shorter than the payload, longer than the payload, and missing zero padding — were previously accepted ([#1588](https://github.com/stellar/js-stellar-sdk/pull/1588)).
- `StrKey.decodeClaimableBalance` and `StrKey.isValidClaimableBalance` now validate the discriminant byte that leads a `B...` strkey. `CLAIMABLE_BALANCE_ID_TYPE_V0` (0) is the only case `ClaimableBalanceID` declares, so the XDR decoder has always refused anything else — but the strkey checksum covers whatever byte is present, so a `B...` key with an unknown discriminant was decoded and reported valid.
- The published type declarations no longer reference types the package doesn't provide, so the SDK compiles under `skipLibCheck: false` with no `@types` packages installed. `@types/json-schema` moved from `devDependencies` to `dependencies`, since `contract.Spec.jsonSchema` returns a `JSONSchema7` (previously `Cannot find module 'json-schema'`); and `contract.SentTransaction.Errors`' three error classes are declared instead of inlined, which stops TypeScript emitting their inferred static side and with it a `NodeJS.CallSite` reference from `@types/node` (previously `Cannot find namespace 'NodeJS'`). No runtime or API change ([#1626](https://github.com/stellar/js-stellar-sdk/issues/1626)).

## [v16.2.0](https://github.com/stellar/js-stellar-sdk/compare/v16.1.0...v16.2.0)

### Added
Expand Down Expand Up @@ -44,6 +86,7 @@ A breaking change will get clearly marked in this log.
- The UMD (`dist/`) build now sets `inlineDynamicImports` so the single-file bundle stays whole despite the SAC spec's lazy `import()` ([#1501](https://github.com/stellar/js-stellar-sdk/pull/1501)).

### Fixed
- `Horizon.ManageDataOperationResponse.value` is now typed as `string`, matching what Horizon actually returns (a base64 string in JSON, which the SDK never decodes). It was previously mistyped as `Buffer` ([#1564](https://github.com/stellar/js-stellar-sdk/pull/1564)).
- `Horizon.Server.submitTransaction` and `submitAsyncTransaction` now reject with
SDK error types on HTTP failures, as documented: a `TransactionFailedError` for
Horizon result codes, a `BadResponseError` otherwise. The wrapping branch used
Expand Down
Loading
Loading