feat: surface CAP-71 bound signer address in WalletKit sign flows#901
Conversation
Bump @stellar/stellar-sdk to 16.0.0-rc.1 (Protocol 27) and surface the signer address bound into Soroban authorization entries across the WalletKit sign_auth_entry and sign_xdr review screens. - Normalize both authorization preimage arms (legacy envelopeTypeSorobanAuthorization and the CAP-71 envelopeTypeSorobanAuthorizationWithAddress) via normalizeAuthPreimage; display the bound signer address in DappAuthEntryDisplay and as a "Signer address" row in the transaction Authorizations view - Add getAddressCredentials/getAuthEntryBoundAddress soroban helpers covering the address, addressV2, and addressWithDelegates credential arms - Add validateAuthEntryAddress: reject sign_auth_entry requests whose account-bound address differs from the active wallet, enforced both pre-UI (WalletKitProvider) and at approval time (walletKitUtil) - Fix bignumber.js v11 resolution in metro.config.js: SDK 16 pulls in bignumber v11, whose react-native field points at a browser-globals UMD build with no module.exports, crashing the app on launch; rewrite the resolved path to dist/bignumber.cjs - Add a withAddress preimage variant to the mock-dapp and cover the new helpers/validation in tests; add en/pt translations
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
There was a problem hiding this comment.
Pull request overview
Adds CAP-71 / Protocol 27 support to WalletConnect sign_auth_entry flows by surfacing and enforcing the signer-bound address (when present) across validation, signing, and UI display, alongside the required Stellar SDK v16 upgrade and Metro/Jest compatibility tweaks.
Changes:
- Upgrades
@stellar/stellar-sdkto16.0.0-rc.1, including Jest/Metro config adjustments needed for SDK 16 dependencies. - Normalizes CAP-71 “withAddress” auth-entry preimages, validates bound signer address against the active wallet, and enforces this both pre-UI and at approval/sign time.
- Updates signing review UI to display the bound signer address in both WalletKit auth-entry display and transaction authorization lists, with accompanying tests and i18n updates.
Reviewed changes
Copilot reviewed 21 out of 23 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| yarn.lock | Locks new SDK 16 dependency graph (incl. noble deps, bignumber.js v11, axios, etc.). |
| package.json | Bumps @stellar/stellar-sdk to 16.0.0-rc.1 and updates Node engine requirement. |
| src/helpers/walletKitValidation.ts | Adds CAP-71 preimage normalization + bound-address validation; threads wallet public key into validateSignAuthEntry. |
| src/providers/WalletKitProvider.tsx | Adds pre-UI bound-address validation for sign_auth_entry requests and passes publicKey into approval flow. |
| src/helpers/walletKitUtil.ts | Enforces bound-address validation at approval time via updated validateSignAuthEntry call. |
| src/helpers/stellar.ts | Updates signAuthEntry to accept CAP-71 preimage arm(s) and defensively reject mismatched bound addresses. |
| src/helpers/soroban.ts | Adds helpers to extract/display bound addresses from Soroban credentials (address/addressV2/addressWithDelegates); adjusts operation typing casts for SDK 16. |
| src/components/screens/WalletKit/DappAuthEntryDisplay.tsx | Parses/normalizes auth-entry preimages once and surfaces “Signer address” when present, with copy support. |
| src/components/screens/SignTransactionDetails/types/index.ts | Extends auth entry model to include a boundAddress for display; updates operation typing for SDK 16. |
| src/components/screens/SignTransactionDetails/hooks/useSignTransactionDetails.ts | Builds auth-entry display objects including bound address per entry. |
| src/components/screens/SignTransactionDetails/components/SignTransactionOperationDetails.tsx | Updates operation prop typing to align with SDK 16 changes. |
| src/components/screens/SignTransactionDetails/components/SignTransactionAuthorizations.tsx | Displays bound signer address per authorization entry with copy affordance. |
| src/components/screens/SignTransactionDetails/components/Operations.tsx | Updates operation typing and tightens enum indexing; adjusts issuer rendering for updated types. |
| src/components/screens/SignTransactionDetails/components/KeyVal.tsx | Refactors signer type handling for SDK 16 and renders signer hash fields consistently as hex. |
| src/i18n/locales/en/translations.json | Adds strings for bound-address mismatch error and “Signer address” label. |
| src/i18n/locales/pt/translations.json | Adds Portuguese strings for bound-address mismatch error and “Signer address” label. |
| mock-dapp/src/routes.ts | Adds a CAP-71 “withAddress” preimage variant for manual testing. |
| metro.config.js | Adds Metro resolver rewrite to force bignumber.js v11 to resolve to its CJS entry. |
| jest.config.js | Transforms newly-introduced ESM-only deps so Jest can load SDK 16’s CJS build. |
| tests/helpers/walletKitValidation.test.ts | Adds normalization + bound-address validation test coverage, including non-soroban preimage arms. |
| tests/helpers/stellarSdkV15.test.ts | Adds coverage for ADDRESS_V2 credential parsing and bound-address extraction in transaction review path. |
| tests/helpers/stellar.test.ts | Adds signAuthEntry tests for CAP-71 “withAddress” preimages, including mismatch rejection. |
| tests/helpers/soroban.test.ts | Adds tests for bound-address extraction across credential arms. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
iOS Simulator preview build is ready: https://github.com/stellar/freighter-mobile/releases/tag/untagged-b8fc9469791eda3a017d (SDF collaborators only — install instructions in the release description) |
… files: 7 workflows + metro.config.js):
chore: address PR review — bump CI to Node 22, harden bignumber metro fix
The @stellar/stellar-sdk 16 upgrade raised the Node engine requirement to
>=22, but CI workflows still pinned Node 20. Align them so CI runs on the
version the SDK requires.
- Bump NODE_VERSION 20 -> 22 in test, android, ios, android-e2e, ios-e2e,
and prPreviewIos workflows; update the inline node-version in new-release
- metro.config.js: normalize path separators before matching the bignumber.js
v11 rewrite so it also triggers on Windows (backslash) paths, and rewrite
the filename precisely (bignumber.js -> bignumber.cjs)
leofelix077
left a comment
There was a problem hiding this comment.
Tested the UI for different scenarios with mock data + docs playground with different preimage types + regression for cap-71 on main and checked the code
id: legacy-invoke
arm / type: legacy, contract call (transfer, your addr in args)
base64 HashIdPreimage
────────────────────────────────────────
id: cap71-account ✅ bound to you
arm / type: CAP-71, account signer = your wallet
base64 HashIdPreimage
────────────────────────────────────────
id: cap71-contract
arm / type: CAP-71, contract signer (passthrough)
base64 HashIdPreimage
────────────────────────────────────────
id: create-wasm
arm / type: WASM create-contract (your addr as deployer)
base64 HashIdPreimage
────────────────────────────────────────
id: create-sac
arm / type: SAC create-contract (USDC)
base64 HashIdPreimage
────────────────────────────────────────
id: nested
arm / type: legacy + 2 sub-invocations
base64 HashIdPreimage
lgtm
|
@aristidesstaffieri I've tested all 4 WC methods using Playground in both Android and iOS Testflight builds. All methods are working fine including the |
|
@aristidesstaffieri well, it looks like the issue is not related to the WC integration but to submitting transactions in general. I'm getting the same error when trying to do a regular in-app Send or Swap, see below. I'd suggest doing a overall pass on the app as the protocol upgrade could break in several different places. E.g. create new account, import account, send token, send collectible, swap, discover, etc. The Send/Swap E2E tests are probably passing because they are using old XDR formats maybe? |
The stellar-sdk 15 → 16 bump (this branch) swapped the SDK's bundled
XHR-based axios client for feaxios, a fetch-based shim. Two of its
fetch-isms aren't satisfied by React Native's Hermes runtime, breaking
every transaction submit (stellar_signAndSubmitXDR over WalletConnect and
the in-app Swap), while GET requests kept working:
1. AbortSignal.timeout / AbortSignal.any are missing in Hermes. feaxios
calls AbortSignal.timeout() on any request with a timeout option, and
submitTransaction is the only call that sets one — so submits threw
"undefined is not a function" (Sentry FREIGHTER-MOBILE-YN) while GETs,
which pass no timeout, were fine.
2. URLSearchParams request bodies aren't serialized by RN. feaxios turns
the SDK's form-urlencoded "tx=..." string body into a URLSearchParams
object; whatwg-fetch (RN's fetch) then calls xhr.send() with the object,
and RN's convertRequestBody only handles string/Blob/FormData/ArrayBuffer.
The body went out empty, so Horizon rejected submits as
transaction_malformed with an empty envelope_xdr.
Fixes:
- Add src/polyfills/abortSignal.ts polyfilling AbortSignal.timeout and
AbortSignal.any (guarded; no-ops where the runtime already has them),
wired into bootstrap.js.
- Patch XMLHttpRequest.send in src/polyfills/xhr.ts to stringify
URLSearchParams bodies — the boundary where RN drops them.
- Add regression tests for both polyfills.
Thanks Cassio. This is fixed in f970b5e I added unit tests to guard the polyfills but in order to have tests that more closely resemble that actual runtime we should consider if we should change our test runtime to match our real one. |
Bump the direct dependency from 16.0.0-rc.1 to the stable 16.0.0 release. The dependency set is identical between the two (axios 1.16.1, bignumber.js ^11.1.1, eventsource ^4.1.0, @noble/*), so this is a version-label change with no transitive churn. The branch already ran the rc with the metro/bignumber-v11 fix and the RN transaction-submission fix in place; tsc --noEmit and the full jest suite (146 suites, 2033 tests) pass against stable.
CassioMG
left a comment
There was a problem hiding this comment.
LGTM - manually tested on iOS Testflight and Android GPlay builds ✅
What
Adds Protocol 27 / CAP-71 support to the WalletConnect signing flows. When a dApp sends a Soroban authorization that is bound to a specific signer address, Freighter now surfaces that address in the review UI and refuses to sign an entry bound to any account other than the active wallet. The PR also bumps
@stellar/stellar-sdkto16.0.0-rc.1(the Protocol 27 line) and carries the Metro resolver fix that SDK requires to boot.What's in this PR:
package.json/yarn.lock— bump@stellar/stellar-sdkto16.0.0-rc.1(Protocol 27).src/helpers/walletKitValidation.ts—normalizeAuthPreimagecollapses the legacyenvelopeTypeSorobanAuthorizationand the new CAP-71envelopeTypeSorobanAuthorizationWithAddressarms into one shape.src/helpers/walletKitValidation.ts—validateAuthEntryAddressrejects asign_auth_entrywhose account-bound address differs from the active wallet.src/providers/WalletKitProvider.tsx/src/helpers/walletKitUtil.ts— enforce that validation both pre-UI and at approval time.src/helpers/soroban.ts—getAddressCredentials/getAuthEntryBoundAddresscovering theaddress,addressV2, andaddressWithDelegatescredential arms.SignTransactionDetails/*+WalletKit/DappAuthEntryDisplay.tsx— show the bound signer address in the auth-entry view and as a "Signer address" row in the transaction Authorizations list.metro.config.js— rewrite bignumber.js v11's brokenreact-nativeresolution to its real CJS entry (required for SDK 16 to run; see Why).mock-dapp/src/routes.ts+ tests +en/pttranslations —withAddresspreimage variant for manual testing, unit coverage, and i18n strings.Why
Protocol 27 (CAP-71) introduces a new authorization-preimage arm that embeds the signer address directly in the entry. Without handling it, the wallet would fail to parse these entries — or worse, blind-sign an authorization bound to an account the user doesn't control. This PR makes the binding visible in the review UI and enforced by validation, so a request bound to a different account is rejected before it can be signed.
The
metro.config.jschange ships here because it's a hard prerequisite, not an unrelated cleanup: SDK 16 pulls inbignumber.jsv11, whosereact-nativepackage field points Metro at a browser-globals UMD build with nomodule.exports. That makesrequire("bignumber.js")return{}and crashes the app on launch (OriginBigNumber__default.default.clone is not a function). The fix rewrites only that one broken resolution todist/bignumber.cjs; v9 (used bystellar-base) and app imports are untouched.Known limitations
16.0.0-rc.1) pending the final Protocol 27 release.addressV2/addressWithDelegatescredential arms are covered by unit tests but not yet exercised against a live Protocol 27 dApp (none available; the mock-dapp feeds prebuilt XDR).Checklist
PR structure
Testing
Release
Address v2 example with bound address
