Skip to content

feat: surface CAP-71 bound signer address in WalletKit sign flows#901

Merged
aristidesstaffieri merged 5 commits into
mainfrom
chore/p27
Jun 17, 2026
Merged

feat: surface CAP-71 bound signer address in WalletKit sign flows#901
aristidesstaffieri merged 5 commits into
mainfrom
chore/p27

Conversation

@aristidesstaffieri

@aristidesstaffieri aristidesstaffieri commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

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-sdk to 16.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-sdk to 16.0.0-rc.1 (Protocol 27).
  • src/helpers/walletKitValidation.tsnormalizeAuthPreimage collapses the legacy envelopeTypeSorobanAuthorization and the new CAP-71 envelopeTypeSorobanAuthorizationWithAddress arms into one shape.
  • src/helpers/walletKitValidation.tsvalidateAuthEntryAddress rejects a sign_auth_entry whose 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.tsgetAddressCredentials / getAuthEntryBoundAddress covering the address, addressV2, and addressWithDelegates credential 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 broken react-native resolution to its real CJS entry (required for SDK 16 to run; see Why).
  • mock-dapp/src/routes.ts + tests + en/pt translations — withAddress preimage 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.js change ships here because it's a hard prerequisite, not an unrelated cleanup: SDK 16 pulls in bignumber.js v11, whose react-native package field points Metro at a browser-globals UMD build with no module.exports. That makes require("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 to dist/bignumber.cjs; v9 (used by stellar-base) and app imports are untouched.

Known limitations

  • SDK is on a release candidate (16.0.0-rc.1) pending the final Protocol 27 release.
  • Manually verified on iOS only so far (iPhone 17 Pro simulator); Android and small screens still need a pass.
  • The addressV2 / addressWithDelegates credential arms are covered by unit tests but not yet exercised against a live Protocol 27 dApp (none available; the mock-dapp feeds prebuilt XDR).
  • Bundles the required Metro/bignumber build fix alongside the feature because the app can't launch on this branch without it.

Checklist

PR structure

  • This PR does not mix refactoring changes with feature changes (break it down into smaller PRs if not).
  • This PR has reasonably narrow scope (break it down into smaller PRs if not).
  • This PR includes relevant before and after screenshots/videos highlighting these changes.
  • I took the time to review my own PR.

Testing

  • These changes have been tested and confirmed to work as intended on Android.
  • These changes have been tested and confirmed to work as intended on iOS.
  • These changes have been tested and confirmed to work as intended on small iOS screens.
  • These changes have been tested and confirmed to work as intended on small Android screens.
  • I have tried to break these changes while extensively testing them.
  • This PR adds tests for the new functionality or fixes.

Release

  • This is not a breaking change.
  • This PR updates existing JSDocs when applicable.
  • This PR adds JSDocs to new functionalities.
  • I've checked with the product team if we should add metrics to these changes.
  • I've shared relevant before and after screenshots/videos highlighting these changes with the design team and they've approved the changes.

Address v2 example with bound address
Simulator Screenshot - iPhone 17 Pro - 2026-06-12 at 09 58 46

Simulator Screenshot - iPhone 17 Pro - 2026-06-12 at 09 59 51

 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
@aristidesstaffieri aristidesstaffieri self-assigned this Jun 12, 2026
@socket-security

socket-security Bot commented Jun 12, 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
Addednpm/​@​stellar/​stellar-sdk@​16.0.09910010099100

View full report

@aristidesstaffieri aristidesstaffieri marked this pull request as ready for review June 12, 2026 17:30
Copilot AI review requested due to automatic review settings June 12, 2026 17:30
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

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.

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-sdk to 16.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.

Comment thread package.json
Comment thread metro.config.js
@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

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 leofelix077 self-requested a review June 16, 2026 14:06

@leofelix077 leofelix077 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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

@CassioMG

CassioMG commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

@aristidesstaffieri I've tested all 4 WC methods using Playground in both Android and iOS Testflight builds. All methods are working fine including the stellar_signAuthEntry with bounded signer, but the stellar_signAndSubmitXDR method stopped working on this branch apparently (I tested on main and it's working fine there). This is the error I'm getting when trying to stellar_signAndSubmitXDR using a Send or Swap XDR from my Freighter account:

IMG_9299

@CassioMG

CassioMG commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

@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?

IMG_9302 2 IMG_9301 2

  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.
@aristidesstaffieri

Copy link
Copy Markdown
Contributor Author

@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?

IMG_9302 2 IMG_9301 2

Thanks Cassio. This is fixed in f970b5e
There was a change to the underlying axios client which changed the polyfill needed in certain cases. This impacted the API because it used the fetch timeout which was backed by the axios client.
This is unrelated to XDR or p27 changes, but the existing tests miss it because they run in Node 22(V8) while the app runs in Hermes so the underlying APIs differ and in this case they still work.

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 CassioMG 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.

LGTM - manually tested on iOS Testflight and Android GPlay builds ✅

@aristidesstaffieri aristidesstaffieri merged commit 27da00e into main Jun 17, 2026
32 of 37 checks passed
@aristidesstaffieri aristidesstaffieri deleted the chore/p27 branch June 17, 2026 17:17
@github-actions github-actions Bot mentioned this pull request Jun 17, 2026
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.

4 participants