Skip to content

chore(deps): upgrade @stellar/stellar-sdk to 17.4.0 for Protocol 30 - #1034

Merged
CassioMG merged 17 commits into
mainfrom
cg-p30-sdk-bump
Nov 6, 2026
Merged

CassioMG merged 17 commits into
mainfrom
cg-p30-sdk-bump

Conversation

@CassioMG

@CassioMG CassioMG commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

TL;DR

Bumps @stellar/stellar-sdk from 17.0.1 to 17.4.0 for Protocol 30, and adds the shared groundwork for muxed contract (W…) addresses: parsing helpers, log and Sentry scrubbing, and Protocol 30 SDK tests. The send, signing-screen and history changes are separate PRs based on this one.

The new SDK decodes federation and stellar.toml responses with a strict UTF-8 TextDecoder. The decoder the app gets on Hermes can't decode strictly, so federation address lookup and stellar.toml asset icons broke on device, while Jest, which runs on Node, stayed green. This PR adds a strict-UTF-8 TextDecoder polyfill that fixes both.

Changed lines

Files Added Removed Share of changed lines
Source code 5 446 13 25%
Tests 6 1,216 5 67%
Other 5 117 22 8%
Total 16 1,779 40

Source code is the app code under src/. Tests are the unit tests and their fixtures. Other is the lockfile, package.json, the polyfill README, AGENTS.md and the dependencies guide.

Screenshots and Videos

Federation lookup (freighter.c7*lobstr.co) and a stellar.toml asset icon (testnet CARBON), before and after the TextDecoder polyfill.

Before the polyfill After the polyfill
Federation lookup fails before the polyfill Federation lookup resolves after the polyfill
CARBON shows the CA placeholder CARBON shows its icon
Implementation details (for agents)

Every link below is a permalink at 9e5e0d2a4229be8bb97d5c50d05bb4c2a72903fb.

What changed

  • SDK bump (9c4cae6c). @stellar/stellar-sdk 17.0.1 to 17.4.0 in package.json#L89. yarn.lock#L4746-L4768 gains the 17.4.0 entry and its new transitive dependencies; 17.0.1 stays in the lockfile only because @stellar/typescript-wallet-sdk-km@5.0.0 pins it.
  • Muxed contract (W…) helpers (dbabee9e, 50946fc4). src/helpers/muxedContract.ts exports isMuxedContract, isContractDestination, getMuxedContractBaseId, getMuxedContractMuxId and buildMuxedContract. It is a leaf module that imports only the SDK, so helpers/stellar and helpers/soroban can both use it without an import cycle. The getters and the builder throw on invalid input (the M… helpers return null), and mux ids are decimal strings, never Number, because a uint64 does not survive a Number round trip. Nothing in src imports it yet; the follow-up PRs wire it in. The shared SEP-23 fixtures are in __tests__/support/muxedContractFixtures.ts, and __tests__/helpers/muxedContract.test.ts covers the helpers, including a real checksum failure.
  • Log and Sentry scrubbing (84d377c8). The strkey pattern in src/helpers/stellarStrKey.ts#L12 is now [MW][A-Z2-7]{68} for the 69-character muxed forms, so a W… is masked as W*** the way an M… is masked as M***. __tests__/helpers/stellarStrKey.test.ts pins each word-boundary anchor and the length.
  • Protocol 30 SDK tests (654e7848). __tests__/helpers/stellarSdkCompat.test.ts#L208-L262 uses the real SDK, with no mocks, to round-trip the SEP-23 W vector through StrKey and new Address(W) through ScAddress arm 5, its XDR bytes and ScVal, and to check the CAP-87 ML-DSA cost types (86 to 94) and the CAP-88 millisecond StellarValue arms.
  • SDK compatibility file renamed (9e5e0d2a). stellarSdkV15.test.ts is now stellarSdkCompat.test.ts, with a version-free top-level describe. It had already grown past SDK 15, with CAP-71 (Protocol 27) and now Protocol 30 blocks.
  • Horizon submission-error tests (9e5e0d2a). stellarSdkCompat.test.ts#L362-L511 drives the real Horizon.Server.submitTransaction against a stubbed fetch.
    • Why: SDK 16 replaced axios with fetch, and 16.1.0 / 17.0.0 made submissions reject with TransactionFailedError or BadResponseError. The app reads those errors through isHorizonError: response.status for the 504 retry and the log-severity split, and response.data.extras.result_codes in the transaction builder and useManageTokens. Until now every app test built those errors by hand.
    • What's pinned: a 400 with result codes, a 429 without them, and a 504. A mutation that drops response.data from the SDK's errors fails the first two.
  • Memo-folding tests (fe7c70ce). __tests__/helpers/memoFolding.test.ts pins, against the real SDK, which memos fold into an M… address (see "Behaviour changes").
  • Strict UTF-8 TextDecoder polyfill (a0b66717, 46930ea3). src/polyfills/textDecoder.ts, required from src/bootstrap.js#L22-L28, documented in src/polyfills/README.md#L20-L53, with 103 tests in __tests__/polyfills/textDecoder.test.ts. The README's "Usage" section now says polyfills are required from src/bootstrap.js, which index.js loads before the app, not from src/index.ts. See the next section.
  • Final-review polish (4ffbfdd1, 8b84ab04). A strict decode now reads only the bytes that a non-Uint8Array view covers (toUint8Array). A stale comment in src/helpers/soroban.ts#L214-L226 no longer claims React Native's decoder throws on fatal (comment only). Several test titles were clarified.
  • Review follow-ups (900fd03a, b80ca267). Two tests the code review found missing: the log scrubber leaves a 68-character W alone, and buildMuxedContract drops leading zeros from the id. AGENTS.md and the dependencies guide now say Node 22, which package.json and every workflow already require.

The TextDecoder polyfill

Why. SDK 17.4.0's bounded fetch, which StellarToml.Resolver and Federation.Server use, decodes every response body with new TextDecoder("utf-8", { fatal: true }). Hermes has no TextDecoder, so the app's global one is fast-text-encoding 1.0.6, whose constructor throws "the 'fatal' option is unsupported". Node's decoder supports fatal, so Jest could not see it. An on-device smoke test found it.

How. installFatalTextDecoder probes new TextDecoder("utf-8", { fatal: true }) and, only when that throws, replaces the global with a wrapper. A UTF-8 decoder built with fatal: true validates its input with isValidUtf8, a WHATWG-conformant check (#L120-L184), and throws a TypeError on malformed bytes. It also follows the WHATWG byte order mark rule (one leading U+FEFF is removed from the output unless ignoreBOM is set), which is what the SDK's bounded fetch gets from Node and browsers. Every other decoder delegates to fast-text-encoding untouched. Where fatal already works (Node and Jest, or a future Hermes) the module does nothing.

/**
* Makes `scope.TextDecoder` accept `{ fatal: true }` for UTF-8 by wrapping it.
* It does nothing when the constructor already supports `fatal`, or when there
* is no `TextDecoder` to wrap, so calling it again is safe.
*
* @param scope - The global object to patch.
* @returns `true` when it wrapped the constructor, `false` when it changed
* nothing.
*/
export const installFatalTextDecoder = (
scope: typeof globalThis = globalThis,
): boolean => {
const BaseTextDecoder = scope.TextDecoder as typeof TextDecoder | undefined;
if (typeof BaseTextDecoder !== "function" || supportsFatal(BaseTextDecoder)) {
return false;
}
// eslint-disable-next-line no-param-reassign
scope.TextDecoder = createFatalTextDecoder(BaseTextDecoder);
return true;
};
installFatalTextDecoder();

Load order. index.js runs bootstrap.js before shim.js, which is where the app first installs a global Buffer, and fast-text-encoding picks its decode path at load time depending on whether Buffer exists. So the polyfill first installs the same buffer module's Buffer (the assignment shim.js makes, which then skips its own) and requires fast-text-encoding itself. Without that, the app's decoder would silently use a different path for every caller. On device this ran under Metro's default inlineRequires.

Side effect. XdrString.toStringStrict() also asks for a strict decoder, and it sits behind scValToNative for Soroban symbols, strings and executable tags. With the app's old decoder it threw and fell back to raw bytes (already the case on main with 17.0.1); it now returns strings, as under Node. That is covered by a Jest harness that simulates the Hermes setup (fast-text-encoding as the global decoder); it was not asserted on a device.

Third site. The SDK's WASM custom-section name parsing (contract/utils) also asks for a strict decoder and gets the same one. No test covers that path.

Behaviour changes

  1. Non-canonical numeric memos no longer fold into an M… address. Since SDK 17.3, MuxedAccount accepts decimal-only ids. A memo such as "0x10" or " 16 " used to fold into an M… address and no longer does; decimal uint64 memos ("0" through "18446744073709551615") fold as before. Pinned by memoFolding.test.ts#L34-L39 and #L58-L69.
  2. Soroban symbols and strings decode to text on device (see "Side effect" above).
  3. A non-UTF-8 stellar.toml or federation response now fails on every platform. That is the SDK's intent since 17.3, and it matches the extension.

Audit of SDK behaviour changes, 17.0.1 to 17.4.0

Each SDK change was checked against the app's call sites. Paths are under src/, and line numbers are at 9e5e0d2a….

Change Mobile call sites Verdict
17.1 Horizon call builders throw synchronously on a bad id getTransactionDetails returns early on an empty hash and runs in a try (services/stellar.ts:321-345). loadAccount is async, so a bad id rejects rather than throws: services/stellar.ts:274, :311; services/transactionService.ts:381, :531, :622, :720; helpers/getIconUrlFromIssuer.ts:27 (G-checked at :19, in a try). checkMemoRequired (hooks/useValidateTransactionMemo.ts:63) only loads the destination of payment-type operations, and skips M… destinations. The app opens no streams. Safe
17.1 stellar.toml domain validation; 17.3 federation URL checks, no redirects on Node, allowedRedirects validation helpers/getIconUrlFromIssuer.ts:51 (in a try, returns ""). ducks/sendRecipient.ts:211 (in a try, shows federationNotFound). React Native still follows redirects through XHR, and the app never passes allowedRedirects. Safe; device-verified
17.1 Asset.fromOperation keeps the alphanum12 arm, Asset.equals compares the type helpers/soroban.ts:1036 only calls .toString(). Nothing calls Asset.equals (the .equals at helpers/walletKitValidation.ts:166 is on an xdr.Hash). Safe
17.2.1 map key ordering in nativeToScVal and scvSortedMap; 17.3 nativeToScVal rejects function values One call, an i128 built from a string amount (services/transactionService.ts:307). Safe
17.3 CAP-84 W addresses; contract_credited muxed fields; regenerated XDR arms new Address(W) and Address.fromScAddress now succeed, so addressToString returns the W (helpers/soroban.ts:139-144). Auth-entry address validation compares against the wallet's G, so a W never matches (helpers/walletKitValidation.ts:206-212, unchanged). isValidStellarAddress still rejects W here (helpers/stellar.ts:179). History envelopes that contain a W now decode (helpers/soroban.ts:786). The app's switches have default branches, and tsc passes. Safe, intended; history handling is a follow-up PR
17.3 setTimeout throws when the timeout falls before minTime services/stellar.ts:284 sets no timebounds. Safe
17.3 bounded fetch decodes response bodies with strict UTF-8 (fatal: true) stellar.toml and federation: helpers/getIconUrlFromIssuer.ts:51, ducks/sendRecipient.ts:211. Regression on Hermes, fixed here by the polyfill and device-verified on iOS and Android
17.3 scValToNative throws on colliding map keys helpers/soroban.ts:626 (scvError only), :646 and :1168 (numeric scalars only), :694-705 (inside getArgsForTokenInvocation, whose only caller, :748, runs in a try). Safe
17.3 MuxedAccount accepts decimal-only ids createMuxedAccount (helpers/stellar.ts:124, constructor call at :140), reached through determineMuxedDestination (helpers/muxedAddress.ts:110). MuxedAccount.fromAddress(M, "0") at helpers/stellar.ts:105 and :164 only parses an existing M…. Behaviour change, see "Behaviour changes"; pinned by the memo-folding tests
17.3 new Asset("xlm", issuer) keeps its case Asset constructions at services/stellar.ts:263, services/transactionService.ts:248 and :647, helpers/soroban.ts:812, helpers/balances.ts:528, useSwapForXlmReserve.ts:94. Safe, and a fix: 17.0.1 re-pointed an issued asset coded xlm at XLM:issuer
17.3 Transaction copies its envelope, so an unencodable envelope fails in the constructor The dApp parse sites run in a try: useSignTransactionDetails.ts:88, helpers/walletKitUtil.ts:529, hooks/useValidateTransactionMemo.ts:141. Every other site parses XDR that the app built, Horizon returned, or the app's backend prepared (services/backend.ts:1405). Safe
17.3 build() throws for a Soroban transaction with a memo or an M… source A memo is added to non-Soroban transactions only (services/transactionService.ts:401). The collectible builder (:749) adds none. Sources always come from loadAccount(G). Safe
Duplicate SDK copy @stellar/typescript-wallet-sdk-km@5.0.0 pins 17.0.1. It handles key storage only, and unlock and signing passed on both platforms. Harmless; removed by stellar/wallet-eng-monorepo#112
Everything else in 17.1 to 17.4 No mobile call site (list below). n/a

No mobile call site:

  • RPC: rpc.getContractData errors and rpc.Server timeout validation (getSorobanRpcServer, services/stellar.ts:112, has no callers), getEvents, and the new rpc.queryEvents. The app reads no RPC events.
  • Auth entries: signAuthEntries and authorizeEntry, and contract clients and getSACBalance rejecting muxed addresses. The app signs auth entries from the preimage (helpers/stellar.ts:423-450).
  • Assets and offers: Asset.compare (LiquidityPoolAsset is only a type, KeyVal.tsx:8 and :477), offer-id validation, and 17.4's toXdrPrice zero-numerator check. Offer operations are display-only (Operations.tsx:326, :362, :403, :894, :1110), and the app builds none.
  • Transaction preparation: resourceFee bounds and the cloneFrom and assembleTransaction inclusion fee. The backend prepares the transaction, and services/backend.ts:1405 only parses it.
  • Other: the _links templating guard, WebAuth, Node-only redirect and header rules, per-request RPC timeouts, fetch params with a fragment, the AssembledTransaction fee, Spec.parseEvent, delegate expirations, the Wasm spec-section rule, the toJson muxed range, stream() parse errors, record link names, templated relative hrefs and the XDR .value doc change.
  • Not bundled or type-only: the SDK's own axios 1.18 to 1.20 (the SDK reaches it only through its ./axios subpaths, Metro has package exports off in metro.config.js:47, and the app has its own axios 1.13.2), and the HorizonApi.Predicate type (tsc passes).

Hermes-only risks checked:

  • TextDecoder: the SDK passes only fatal and ignoreBOM. fatal is covered by the polyfill, and ignoreBOM: true on a non-fatal decoder is what the base already does.
  • TextEncoder is native in Hermes. The SDK uses ReadableStream only through response.body.getReader(), which the existing responseBody polyfill covers.
  • Native in Hermes: WeakSet, 2n ** 50n (terser keeps it), Object.hasOwn and Error cause. The URL getters come from react-native-url-polyfill.

Verification

At 9e5e0d2a:

  • yarn test passes at b80ca267: 334 suites passed (1 skipped), 4,831 tests passed (6 skipped). This PR adds 140 tests: 103 for the polyfill, 7 for the W helpers, 12 for scrubbing, 11 for memo folding, 4 in the Protocol 30 block and 3 for Horizon submission errors.
  • The polyfill tests simulate Hermes in Jest (they remove the global decoder, load fast-text-encoding, then the polyfill) and run the real SDK's StellarToml.Resolver, Federation.Server and scValToNative against it, with fetch mocked. A scratch harness on the hermes VM that ships with React Native 0.81.4 (not part of this PR) also passes at 8b84ab04: 46 of 46 checks, 9 of them for 4ffbfdd1's view-window change.
  • yarn lint:ts passes.
  • yarn check exits 1, because of lint debt that is already on main: yarn lint:check reports 111 eslint errors in 18 files, and yarn format:check, which yarn check never reaches after that failure, flags 9 tracked files. This PR adds none. None of those files is touched here, the per-file error counts and the flagged files are identical to main's, and eslint and prettier are clean on every file this PR changes.
  • yarn lint:translations reports zero translation violations. It exits 1 only because of 9 existing import/order and no-continue errors in 4 files this PR does not touch.

Device smoke tests, against testnet on an iPhone 17 Pro simulator (iOS 26.5) and a Pixel 10 Pro emulator (Android API 37):

Check Before the polyfill (iOS / Android) After the polyfill (iOS / Android)
Unlock with the password pass / pass not re-run
Classic XLM send, then History pass / pass not re-run
Federation address resolves to a G address fail / fail pass / pass
stellar.toml asset icon (testnet CARBON) fail / fail pass / pass
dApp stellar_signXDR from the mock dApp, signature verified on the host pass / pass pass / not re-run
Cold boot reaches Welcome or Home with no red box, and Metro and logcat show no TextDecoder, Buffer or polyfill errors not checked separately pass / pass
  • The first run is how the regression was found: federation and icons failed on both platforms with "Failed to construct 'TextDecoder': the 'fatal' option is unsupported". After the polyfill the same checks pass.
  • The device re-run was at 46930ea3, and the device runs were not repeated for the later commits:
    • 4ffbfdd1 and 8b84ab04 are covered by the unit suite and the Hermes VM harness;
    • 9e5e0d2a only touches tests.
  • All device runs used a dev build with IS_E2E_TEST=true, which skips WalletConnect's request-origin check. That does not matter for an SDK-bump smoke test. They used debug (Metro) bundles only.
  • Screenshots and screen recordings of each check, before and after, exist; they can be attached or shared on request.

Follow-ups and out of scope

Closes stellar/wallet-eng-monorepo#100

🤖 Generated with Claude Code

CassioMG and others added 11 commits October 29, 2026 22:57
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Cover the Protocol 30 SDK surface the wallet depends on: the SEP-23
muxed contract (W) strkey vector, ScAddress arm 5 round-trips, the
CAP-87 ML-DSA cost types (86-94) and the CAP-88 millisecond
StellarValue arms. All four fail against the 17.0.1 SDK.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Pin which transaction memos fold into a muxed (M...) address on stellar-sdk 17.4.0. Decimal uint64 ids fold; hex, padded, empty and out-of-range values do not. The tests run against the real SDK, with only services/backend mocked.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
fast-text-encoding, the app's global TextDecoder, throws when it is asked
for { fatal: true }. SDK 17.4.0 asks for it in its bounded-fetch adapter, so
federation addresses did not resolve and stellar.toml asset icons did not
load on device. The SDK also asks for it in XdrString.toStringStrict(), so
scValToNative returned raw bytes instead of strings for symbols and strings.

Wrap the global TextDecoder so a UTF-8 decoder built with { fatal: true }
validates its input with a WHATWG-conformant check and throws a TypeError on
malformed bytes. Every other decoder is built and used as before, and the
wrapper does nothing where TextDecoder already supports fatal, as on Node
and in Jest.

The polyfill requires fast-text-encoding itself and installs Buffer first,
as shim.js does later, so the decoder keeps decoding through Buffer.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A TextDecoder built with { fatal: true } and the default ignoreBOM now drops
one leading U+FEFF from its output, as the native decoder does.
fast-text-encoding never strips it, so a stellar.toml or federation response
that starts with a BOM failed to parse on device: smol-toml and JSON.parse
both reject the BOM.

The SDK's bounded fetch builds its decoder with the default ignoreBOM, so it
gets the stripping. XdrString.toStringStrict() and the WASM name parser pass
ignoreBOM: true and keep the BOM. A strict decoder now reports the ignoreBOM
option it honors. Non-fatal decoders are unchanged and still keep a BOM.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A TextDecoder built with { fatal: true } now reads only the window of a
DataView or non-Uint8Array typed array that byteOffset and byteLength
describe, as the native decoder does. It used to validate and decode the
whole backing buffer, as fast-text-encoding does. Every caller in the app
passes a Uint8Array, which is unchanged, so this brings the strict path in
line with the native decoder rather than changing what the app decodes.
Non-fatal decoders still hand their input straight to the base.

The UTF8_LABEL comment now says which labels the strict path treats as
UTF-8, and that the Buffer-backed base accepts only utf-8 and utf8. The
tests gain a view-window check against the native decoder, a parity check for
decode() with no input on a non-fatal decoder, and a JSDoc on the hex helper.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The decodeUtf8At comment said React Native's TextDecoder throws on fatal,
which stopped being true with the strict TextDecoder polyfill. It now says
why the function exists: the escape renderer needs one code point and its
width at an index, which a decoder of a whole buffer cannot give.

The strkey scrubbing tests add a letter before, a letter after and one more
base32 character after a W key, so the leading anchor, the trailing anchor
and the exact length are each pinned on their own. The memo folding tests
split the non-folding memos by reason: not a canonical decimal id, and not a
uint64. The SDK compatibility suite's header and top-level title now cover
SDK 17 and Protocols 27 and 30. No production code changes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
stellarSdkV15.test.ts has covered more than SDK 15 since it gained the
Protocol 27 (CAP-71) block and the Protocol 30 block, so it becomes
stellarSdkCompat.test.ts, with a version-free top-level describe.

It also gains a block for the Horizon submission errors of the SDK 16+
HTTP client. SDK 16 replaced axios with fetch, and 16.1.0 / 17.0.0 made
submitTransaction reject with TransactionFailedError or BadResponseError.
The app reads those errors through isHorizonError: response.status for
the 504 retry and the log-severity split, and
response.data.extras.result_codes in the transaction builder and
useManageTokens. Every app test built that error by hand, so nothing
pinned it to the real SDK. The new tests drive the real Horizon.Server
against a stubbed fetch for a 400 with result codes, a 429 without them,
and a 504. If the SDK stops exposing response.data, the first two fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@socket-security

socket-security Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatednpm/​@​stellar/​typescript-wallet-sdk-km@​5.0.0 ⏵ 6.0.082 +110093 +199 +1100
Updatednpm/​@​stellar/​stellar-sdk@​17.0.1 ⏵ 17.4.099 +1100100 +1100 +1100

View full report

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Android Emulator preview APK is ready: https://github.com/stellar/freighter-mobile/releases/tag/untagged-625b0f13bf0caf551c53
Backend: V1 prod + V2 staging (no sandbox configured for @CassioMG). SDF collaborators only — install instructions in the release description.

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

iOS Simulator preview build is ready: https://github.com/stellar/freighter-mobile/releases/tag/untagged-5be3c2d0bf7d2522e10f
Backend: V1 prod + V2 staging (no sandbox configured for @CassioMG). SDF collaborators only — install instructions in the release description.

Comment thread .yarnrc.yml Resolved
17.4.0 is now older than the 7-day npmMinimalAgeGate, so the
temporary npmPreapprovedPackages entry is no longer needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@CassioMG
CassioMG marked this pull request as ready for review November 2, 2026 22:21
@CassioMG
CassioMG requested a lite review from Copilot November 2, 2026 22:22
@CassioMG CassioMG self-assigned this Nov 2, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A moderate active issue remains because the strict UTF-8 path passes the valid alias "unicode-1-1-utf-8" to a constructor that rejects it, causing decoder construction to throw on Hermes.

1 open issue
What changed in this PR

This pull request upgrades @stellar/stellar-sdk to 17.4.0 for Protocol 30, adds shared muxed contract (W…) support, and introduces a Hermes-compatible strict UTF-8 TextDecoder polyfill.

Changes:

  • Updates the Stellar SDK and lockfile dependencies.
  • Adds muxed contract helpers, fixtures, StrKey scrubbing, and Protocol 30 compatibility coverage.
  • Adds and bootstraps the strict UTF-8 decoder polyfill with integration tests.
  • Adds coverage for memo folding, Horizon submission errors, and SDK behavior changes.
File Reviewed changes
yarn.lock Records the Stellar SDK upgrade and transitive dependency resolutions.
src/​polyfills/​textDecoder.ts Implements strict UTF-8 decoding, BOM handling, and Hermes compatibility. Moderate issue (2 votes): normalize the valid unicode-1-1-utf-8 alias to utf-8 on the strict path while preserving invalid and empty labels for the base constructor.
src/​polyfills/​README.md Documents the decoder polyfill and bootstrap ordering.
src/​helpers/​stellarStrKey.ts Extends sensitive StrKey scrubbing to muxed contract addresses.
src/​helpers/​soroban.ts Updates UTF-8 decoder documentation.
src/​helpers/​muxedContract.ts Adds validation, extraction, and construction helpers for W… addresses.
src/​bootstrap.js Loads the TextDecoder polyfill during application startup.
package.json Bumps @stellar/stellar-sdk to 17.4.0.
__tests__/​support/​muxedContractFixtures.ts Provides shared muxed contract and account test vectors.
__tests__/​polyfills/​textDecoder.test.ts Tests strict decoding, BOM behavior, Hermes simulation, and SDK integrations.
__tests__/​helpers/​stellarStrKey.test.ts Tests W… scrubbing and word-boundary behavior.
__tests__/​helpers/​stellarSdkCompat.test.ts Adds Protocol 30 and Horizon submission compatibility coverage.
__tests__/​helpers/​muxedContract.test.ts Tests muxed contract validation, extraction, construction, and invalid inputs.
__tests__/​helpers/​memoFolding.test.ts Pins SDK behavior for decimal mux IDs and memo folding.

This pull request requires changes.

Review effort: Lite

Comment thread src/polyfills/textDecoder.ts Resolved
CassioMG and others added 2 commits November 2, 2026 17:16
…eros

The scrubber tests checked a W with one extra character but not one with
a character missing: a 68-character run must stay untouched, since only a
69-character M or W is a key. buildMuxedContract's comment says it drops
leading zeros from the id; nothing asserted it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AGENTS.md and the dependencies guide still said Node 20, but
package.json's engines field asks for >= 22.12.0 and every workflow runs
Node 22. Prettier, run by the pre-commit hook, also re-pads the Best
Practices table in AGENTS.md, which wasn't formatted before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@CassioMG
CassioMG requested a review from a team November 3, 2026 21:01
Comment thread package.json Resolved
6.0.0 pins @stellar/stellar-sdk 17.4.0, the version the app already
depends on, so the lockfile no longer resolves a second stellar-sdk
(17.0.1) for the key manager. Its one breaking change is in
KeyManager.fetchAuthToken, which the app doesn't call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@CassioMG
CassioMG merged commit 3999960 into main Nov 6, 2026
37 checks passed
@CassioMG
CassioMG deleted the cg-p30-sdk-bump branch November 6, 2026 23:18
CassioMG added a commit that referenced this pull request Nov 6, 2026
Brings in #1034 (squash-merged as 3999960) now that this PR targets
main. package.json and yarn.lock take main's versions: this PR doesn't
change them, and main's carry #1034's later typescript-wallet-sdk-km
6.0.0 bump.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CassioMG added a commit that referenced this pull request Nov 6, 2026
Brings in #1034 (squash-merged as 3999960) now that this PR targets
main. package.json and yarn.lock take main's versions: this PR doesn't
change them, and main's carry #1034's later typescript-wallet-sdk-km
6.0.0 bump.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CassioMG added a commit that referenced this pull request Nov 7, 2026
Brings in #1034 (squash-merged as 3999960) now that this PR targets
main. package.json and yarn.lock take main's versions: this PR doesn't
change them, and main's carry #1034's later typescript-wallet-sdk-km
6.0.0 bump.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

3 participants