Skip to content

Commit 1a6b117

Browse files
Ryang-21quietbits
andauthored
V17 feature branch (#1580)
* Class XDR Implementation (#1422) * feat(xdr): codegen tool + schema source * feat(xdr): add class-based XDR runtime and sep51 JSON walker * refactor(numbers): drop LargeInt classes, delegate to new XDR layer * refactor(base): migrate src/base to new XDR layer; drop legacy xdr.ts + generated * refactor: migrate downstream consumers (bindings/contract/horizon/rpc/webauth) * feat(base/scval): add bool to ScValType * allow opaque xdr types to be initalized via string * refactor xdr strings to be represented soley via bytes with a dx friendly XdrString wrapper class * generate a value getter function for void union cases * add a is() function to the generated XDR union classes for instanceOf checks * feat(xdr): regenerate schemas against @stellar/js-xdr and add CAP-71 credentials * feat(xdr): wire the toJSON hook so JSON.stringify emits SEP-0051 * fix(bindings): emit Uint8Array for bytes/bytesN to match scValToNative * refactor(contract): rename fromJSON to fromJson with deprecated aliases * feat(xdr): accept ASCII asset codes with zero padding in constructors * Migrate public API from Buffer to Uint8Array (#1564) * feat(base): migrate crypto and strkey APIs to Uint8Array * feat(base)!: migrate value types to Uint8Array * feat(base)!: migrate transactions, operations, and auth to Uint8Array * feat!: migrate contract, rpc, and webauth layers to Uint8Array * build!: drop buffer polyfill and dependency * fix(horizon): type manage_data value as string to match runtime API * fix(xdr): emit SEP-51 key `type` instead of Rust-escaped `type_` (#1571) * build(xdr): regenerate xdr.json via docker from pinned stellar-xdr commit (#1575) * build(xdr): regenerate schema from stellar-xdr with CAP-83 and CAP-85 ungated (#1576) * build(xdr): regenerate schema from stellar-xdr with CAP-83 and CAP-85 ungated * fix(xdr): keep consumers compiling against the regenerated union arms * fix(vitest): isolate browser dep cache per transport * feat(xdr): support CAP-83 empty tx set values and CAP-85 external executables (#1577) * build(xdr): fail the schema download instead of masking it in a pipe * feat(xdr): support CAP-83 and CAP-85 protocol values * fix(xdr): bound decimal string length before BigInt parse in JSON decode (#1581) * fix(xdr): bound decimal string length before BigInt parse in json decode * refactor(xdr): name the digit-budget constants in bigint-parts * fix(xdr): restrict fromJson to SEP-0051 keys and reject unknown fields (#1582) * fix(xdr): restrict fromJson to SEP-51 keys and reject unknown fields * fix(test): correct horizon corpus fixture path so corpus tests run * fix(strkey): bound decodeCheck input length before base32 decode (#1583) * fix(xdr): reject AssetCode12 JSON codes shorter than 5 bytes (#1585) * fix(horizon)!: make TransactionFailedExtras result_codes.operations optional (#1586) * Fix: signed payload strkey framing (#1588) * fix(strkey): validate signed payload framing in decodeCheck * Fix: xdr json decode validation (#1592) * fix(xdr): throw on unknown union discriminant in fromXdrObject * fix(strkey): validate the claimable balance discriminant byte * fix(xdr): reject non-decimal integer strings in JSON decoding * V17.0.0 rc.1 (#1593) * chore(release): cut v17.0.0-rc.1 * fix(spec): restore instanceof Map check lost in the v17 merge * feat(xdr): add validateXdr static to every generated type (#1597) * feat(xdr): add validateXdr static to every generated type * fix(contract): declare error classes, make types self-contained (#1627) * perf(strkey): reject by length and prefix before decodeCheck throws (#1629) * fix: restore wide-int bounds statics, document Memo.text break (#1628) * fix(xdr): restore wide-int MIN_VALUE/MAX_VALUE statics --------- Co-authored-by: Iveta <quietbits@users.noreply.github.com>
1 parent 0336c41 commit 1a6b117

776 files changed

Lines changed: 103335 additions & 33011 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/npm_publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,4 @@ jobs:
4242
run: pnpm run preversion
4343

4444
- name: Publish npm package
45-
run: npm publish --access public --provenance
45+
run: npm publish --access public --provenance --tag "rc"

.github/workflows/tests.yml

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,6 @@ jobs:
3838
- name: Install Dependencies
3939
run: pnpm install --frozen-lockfile --network-concurrency 1
4040

41-
- name: Install Playwright Chromium
42-
run: pnpm exec playwright install chromium
43-
44-
- name: Install Playwright Firefox
45-
run: pnpm exec playwright install firefox
46-
47-
- name: Install Playwright system dependencies
48-
run: pnpm exec playwright install-deps chromium firefox
49-
5041
- name: Build
5142
run: pnpm run build:prod
5243

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

68-
- name: Browser Tests
69-
run: pnpm run test:browser
59+
# Browser tests get their own job per transport rather than two steps in
60+
# `build_and_test`. Each pass loads the SDK source graph — ~470 unbundled
61+
# `src/xdr` modules — once per test file, across 220 files and two browsers.
62+
# Run back to back on one runner, the second pass reliably lost its browser
63+
# page mid-run ("Browser connection was closed while running tests"), always
64+
# after ~65 files with every test that ran passing. A fresh runner per
65+
# transport keeps each pass within the runner's memory budget.
66+
browser_test:
67+
runs-on: ubuntu-latest
68+
69+
strategy:
70+
fail-fast: false
71+
matrix:
72+
node-version: [22, 24]
73+
transport: [fetch, axios]
74+
75+
steps:
76+
- name: Checkout
77+
uses: actions/checkout@v7
78+
79+
- name: Setup pnpm
80+
uses: pnpm/action-setup@v6
81+
82+
- name: Use Node.js ${{ matrix.node-version }}
83+
uses: actions/setup-node@v7
84+
with:
85+
node-version: ${{ matrix.node-version }}
86+
cache: pnpm
87+
cache-dependency-path: pnpm-lock.yaml
88+
89+
- name: Enable Corepack
90+
run: corepack enable
91+
92+
- name: Install Dependencies
93+
run: pnpm install --frozen-lockfile --network-concurrency 1
94+
95+
- name: Install Playwright Chromium
96+
run: pnpm exec playwright install chromium
97+
98+
- name: Install Playwright Firefox
99+
run: pnpm exec playwright install firefox
100+
101+
- name: Install Playwright system dependencies
102+
run: pnpm exec playwright install-deps chromium firefox
70103

71-
- name: Browser Tests (Axios)
72-
run: pnpm run test:browser:axios
104+
- name: Browser Tests (${{ matrix.transport }})
105+
run: pnpm run test:browser:${{ matrix.transport }}
73106

74107
# Placeholder to satisfy the `build_and_test (20)` required status check in
75108
# the `protect-main` ruleset. Node 20 was dropped from the test matrix

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,18 @@ config/tsconfig.tmp.json
3939
.copilot/
4040
.codex/
4141

42+
# xdr-roundtrip-monitor failure dumps (scripts/xdr-roundtrip-monitor.ts)
43+
xdr-roundtrip-failures*.jsonl
44+
45+
# xdr-json-parity-monitor failure dumps (scripts/xdr-json-parity-monitor.ts)
46+
xdr-json-parity-failures*.jsonl
47+
4248
# scratch: per-guide tests, never shipped
4349
test-guides/
4450
.vuln-hunt/
4551
scratch/
4652
.docs-build/
53+
.xdr-build/
4754

4855
# Working data for scripts/test-event-bindings.mjs (contract-wasms clone +
4956
# generated bindings under test)

CHANGELOG.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,48 @@
22

33
A breaking change will get clearly marked in this log.
44

5+
## [v17.0.0-rc.1](https://github.com/stellar/js-stellar-sdk/compare/v16.2.0...v17.0.0-rc.1)
6+
7+
### Breaking Changes
8+
* 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.
9+
* `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.
10+
* 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:
11+
* **Start here: [`docs/XDR_MIGRATION.md`](./docs/XDR_MIGRATION.md) covers every change below with before/after examples and a quick-reference table.**
12+
* 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)`.
13+
* Enums are singletons, not factory calls: `xdr.ContractDataDurability.persistent()` becomes `xdr.ContractDataDurability.persistent`.
14+
* 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`.
15+
* Absent optional fields decode to `null` instead of `undefined`, so `=== undefined` checks silently stop matching. Prefer `== null`.
16+
* 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.
17+
* 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.
18+
* 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.
19+
* 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`).
20+
* `ScInt` and `XdrLargeInt` lost their `.int` property; read `.value` (a `bigint`) instead, and note `valueOf()` now returns a `bigint`.
21+
22+
* 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)):
23+
* [`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.
24+
* [`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.
25+
* `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.
26+
* [`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`.
27+
28+
* `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)).
29+
30+
### Added
31+
- `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)).
32+
- 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)).
33+
- 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)).
34+
35+
### Changed
36+
- `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)).
37+
- `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)).
38+
- `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)).
39+
- `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)).
40+
- `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)).
41+
42+
### Fixed
43+
- `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)).
44+
- `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.
45+
- 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)).
46+
547
## [v16.2.0](https://github.com/stellar/js-stellar-sdk/compare/v16.1.0...v16.2.0)
648

749
### Added
@@ -44,6 +86,7 @@ A breaking change will get clearly marked in this log.
4486
- 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)).
4587

4688
### Fixed
89+
- `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)).
4790
- `Horizon.Server.submitTransaction` and `submitAsyncTransaction` now reject with
4891
SDK error types on HTTP failures, as documented: a `TransactionFailedError` for
4992
Horizon result codes, a `BadResponseError` otherwise. The wrapping branch used

0 commit comments

Comments
 (0)