Skip to content

Add Soran names to Freighter payments and history - #3010

Open
flarcos wants to merge 10 commits into
stellar:masterfrom
flarcos:feature/soran-payments-history
Open

flarcos wants to merge 10 commits into
stellar:masterfrom
flarcos:feature/soran-payments-history

Conversation

@flarcos

@flarcos flarcos commented Sep 12, 2026

Copy link
Copy Markdown

Freighter can resolve Soran names when sending payments and display names in account and asset history on Stellar Testnet.

What is Soran?

Soran is a naming system built on Stellar's Soroban smart contracts. Names use the format username.namespace, such as alice.nova or robert.orange. The same Universal Lookup contract serves supported namespaces; .nova is an example, not a restriction.

This integration reads Universal Lookup through unsigned Soroban RPC simulation. It does not use Soran's HTTP API or submit transactions for lookups. See the documentation and on-chain resolution reference.

Changes

  • Resolve complete payment instructions for classic accounts (G...), accounts with ID/text/hash memos, muxed accounts (M...) and contract destinations (C...) in supported transfer flows. Preserve required memos and muxed IDs, retain the name through review and recents, and revalidate before sending.
  • Keep recipient selection and transaction review tied to the current payment inputs, account and network. Discard superseded lookups and simulations, including changes between ordinary addresses and Soran names. Require successful simulation for Soran review, close stale reviews, and keep errors and retry available on the amount screen.
  • Encode Soran token amounts locally as exact i128 transaction XDR before simulation, avoiding numeric JSON rounding. Reuse Freighter’s existing transaction-simulation endpoint without changing its backend schema. Validate destinations, memos, operation types, contract IDs, exact token amounts and displayed simulation fees before accepting prepared transactions. Reject unsupported routing, excess precision, nonpositive amounts and overflow. Before software/hardware signing and submission, require the full transaction body to match the reviewed transaction; only signatures may change. Apply these checks to Soran path payments as well as direct payments. Use the shared splitCanonical helper for token symbols containing :.
  • Read current display names through batched primary_names calls with bounded caching and a 20-second HTTP timeout. Retain the correct counterparty and direction for token, collectible (including ID 0) and account-creation history, including transfers without asset balance changes. Omit names when direction is unavailable.
  • Save the name used for a successful payment locally, keyed by payer, network, transaction hash and complete route. History reads wait for earlier queued annotation saves. Keep current names distinct from historical annotations, with addresses visible and copyable. Restrict saved-name messages to trusted extension pages and the configured development-server relay.
  • Include regression tests and English/Portuguese translations, including readable unsupported-routing errors. Fix the undefined history timestamp suffix while retaining each locale's preferred hour cycle and requested regional language tag (en-GB, en-US, pt-BR).

Testnet examples

Switch Freighter to Testnet and use a funded test wallet whose address differs from the recipient. In Send → XLM, enter one of these names and continue to review. Confirm the exact destination and routing information below; memo-bearing names must populate and lock the memo. Looking up a name does not send funds.

Name to enter Expected destination Expected routing information
orange.orange GAOHJ6KE5XUQCKXBANQ4JLHZLQ25727DBJDU7PEEZ77OAJVTA2DC3XMM None
mux.nova MDHHA2WBSH4ZKIAWALPY4KVOC57ZUT6W6HWS3JBUQ4KFJRT6US4MWAAAAAAAAAAAFKV2W Muxed ID 42; no transaction memo
robert.orange GDRECIBHDKSB2X72Z7TPSU4BKABFV5ALFAD57X57YBOBCWYOZ2B2AVEC Id 77
alice.nova GBES5UHJYI445RV4XBGWHZOMBW4RYXBHOX47ZNZAJZAH2WP42ZEP2DYQ Text hello

For mux.nova, the complete M... address must be retained; its base account is GDHHA2WBSH4ZKIAWALPY4KVOC57ZUT6W6HWS3JBUQ4KFJRT6US4MXTZ5 and its routing ID is 42. The ID is not a transaction memo. An unsupported flow must reject the transfer rather than replace the M address with its base G account.

Expected history display: the current Primary names are mux.nova, robert.orange and alice.nova for their corresponding destinations. orange.orange currently returns no Primary name, so history without a saved payment-name annotation falls back to the address. After a successful send through this integration, Freighter saves and displays Sent using <name> separately from the current Primary name. For G-plus-memo routes, a Primary name identifies the account, not a particular memo-routed customer.

These results were verified through read-only Universal Lookup calls on 12 September 2026 at 16:11 UTC (destination ledger 4,641,182; Primary ledger 4,641,183). Testnet records can change; these are observed results, not permanent fixtures.

Testnet contracts

Addresses: Soran's shared Testnet deployment. Other networks are currently rejected. Contract reads trust the selected RPC and configured deployment; Wasm hashes are not pinned.

Validation

  • Full unit suite passed: 2,274 tests across 244 suites (51 tests / 6 suites skipped).
  • TypeScript checking, translation generation, the development extension build, formatting and whitespace checks passed. Earlier production-build checks also passed.
  • Earlier Chromium browser regression run: 24 passed, 1 existing skip, covering account history, muxed counterparties, memo requirements, recipient changes and review navigation.
  • Earlier Firefox 155.0.1 smoke test passed for all four example names using a fresh disposable Testnet wallet. Full destinations, the muxed address and locked required memos were verified through transaction review; canceling alice.nova, changing 1→2 XLM and reviewing again retained the locked hello memo. No payments were submitted in this retest.
  • Earlier Firefox 155.0.1 production-build testing used an isolated disposable wallet on Testnet: successful 1 XLM payments to all four examples above, required memos retained, muxed destination preserved, and saved-name annotations verified after reload in account and asset history. This Firefox run used Selenium in headless mode; hardware wallets and contract-token transfers were not exercised in Firefox.
  • New regression cases exercise the actual hook/API boundary and decode posted XDR, including 9007199254740993, the i128 maximum, G/M/C destinations, rejected altered responses, numeric/custom-network compatibility, and ordinary-address fallback after capability lookup failures. Read-only Testnet checks through the existing simulation endpoint passed for G/M destinations with exact amounts and consistent fees; no transactions were signed or submitted.
  • Regression tests exercise real transaction envelopes with mocked signing/RPC: destination, memo, amount, contract, fee and full transaction-body mismatches; stale requests and review invalidation; unauthorized saved-name callers; history counterparty edge cases; and real English/Portuguese translation fallback.
  • Read-only Testnet checks exercised forward resolution and batched Primary lookup. Manual testing used Google Chrome for Testing, including a shared-account case: sending with adam.nova retained memo 12345 and the original payment name, while the return payment displayed the account’s elected Primary name, general.nova.

Demonstration

Freightersorandemo.mov

Copilot AI balanced review requested due to automatic review settings September 12, 2026 17:30

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 Testnet Soran name resolution to payment flows and transaction history, including route validation, local annotations, and localization.

Changes:

  • Resolves and validates Soran destinations, memos, muxed accounts, and contracts.
  • Displays current and historically used names in account and asset history.
  • Adds secure storage messaging and comprehensive regression tests.

Reviewed changes

Copilot reviewed 42 out of 42 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
extension/src/popup/views/AccountHistory/hooks/useGetHistoryData.tsx Adds naming metadata and muxed senders.
extension/src/popup/views/AccountHistory/hooks/__tests__/useGetHistoryData.test.tsx Tests history naming metadata.
extension/src/popup/views/__tests__/Account.test.tsx Updates localized date expectation.
extension/src/popup/locales/pt/translation.json Adds Portuguese Soran strings.
extension/src/popup/locales/en/translation.json Adds English Soran strings.
extension/src/popup/hooks/useSoranHistoryName.ts Loads current and saved names.
extension/src/popup/hooks/__tests__/useSoranHistoryName.test.tsx Tests history-name loading.
extension/src/popup/helpers/soranTransaction.ts Validates Soran transaction routes.
extension/src/popup/helpers/soranPrimary.ts Implements batched Primary-name reads.
extension/src/popup/helpers/soranHistory.ts Selects counterparties and formats timestamps.
extension/src/popup/helpers/soran.ts Implements Soran lookup and decoding.
extension/src/popup/helpers/__tests__/soranTransaction.test.ts Tests transaction validation.
extension/src/popup/helpers/__tests__/soranPrimary.test.ts Tests Primary-name reads and caching.
extension/src/popup/helpers/__tests__/soranHistory.test.ts Tests history helpers.
extension/src/popup/helpers/__tests__/soranAdversary.test.tsx Tests adversarial signing and access cases.
extension/src/popup/helpers/__tests__/soran.test.ts Tests name resolution and ABI decoding.
extension/src/popup/helpers/__tests__/fixtures/soran-destinations.json Adds Soran ABI vectors.
extension/src/popup/components/sendCollectible/SelectedCollectible/hooks/useSimulateTxData.ts Validates collectible routes.
extension/src/popup/components/send/SendTo/index.tsx Integrates names into recipient selection.
extension/src/popup/components/send/SendTo/hooks/useSendToData.tsx Resolves names and prevents stale results.
extension/src/popup/components/send/SendTo/hooks/__tests__/useSendToData.soran.test.tsx Tests recipient resolution.
extension/src/popup/components/send/SendAmount/index.tsx Locks Soran-provided memos.
extension/src/popup/components/send/SendAmount/hooks/useSimulateTxData.tsx Revalidates simulated transfers.
extension/src/popup/components/InternalTransaction/SubmitTransaction/hooks/useSubmitTxData.tsx Revalidates submission and saves names.
extension/src/popup/components/InternalTransaction/SubmitTransaction/hooks/__tests__/useSubmitTxData.telemetry.test.tsx Tests submission revalidation and annotations.
extension/src/popup/components/hardwareConnect/HardwareSign/index.tsx Validates before hardware signing.
extension/src/popup/components/hardwareConnect/HardwareSign/__tests__/HardwareSign.test.tsx Tests hardware validation.
extension/src/popup/components/accountHistory/TransactionDetail/styles.scss Styles name details.
extension/src/popup/components/accountHistory/TransactionDetail/index.tsx Displays names and revised timestamps.
extension/src/popup/components/accountHistory/HistoryItem/styles.scss Styles history-row names.
extension/src/popup/components/accountHistory/HistoryItem/index.tsx Displays names in history rows.
extension/src/popup/components/__tests__/SoranHistoryDisplay.test.tsx Tests name presentation.
extension/src/constants/localStorageTypes.ts Adds the annotation storage key.
extension/src/background/messageListener/popupMessageListener.ts Routes protected annotation messages.
extension/src/background/messageListener/handlers/soranPaymentNames.ts Stores and retrieves annotations.
extension/src/background/messageListener/handlers/__tests__/soranPaymentNames.test.ts Tests annotation storage.
extension/e2e-tests/accountHistory.test.ts Updates address selectors.
@shared/constants/services.ts Adds Soran service types.
@shared/api/types/types.ts Types muxed senders.
@shared/api/types/soran.ts Defines annotation references and keys.
@shared/api/types/message-request.ts Defines annotation messages.
@shared/api/internal.ts Adds annotation API wrappers.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread extension/src/popup/helpers/soran.ts Outdated
Comment thread extension/src/popup/helpers/soranHistory.ts
export const formatHistoryTimestamp = (createdAt: string, locale?: string) => {
const date = new Date(createdAt);
if (Number.isNaN(date.getTime())) return "—";
return `${date.toLocaleDateString(locale, { year: "numeric", month: "short", day: "numeric" })} • ${date.toLocaleTimeString(locale, { hour: "2-digit", minute: "2-digit" })}`;
Copilot AI review requested due to automatic review settings September 12, 2026 17:51

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

Copilot reviewed 42 out of 42 changed files in this pull request and generated 2 comments.

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

extension/src/popup/helpers/soranHistory.ts:42

  • The new formatter still follows the locale's default hour cycle, so locales such as en-US render 9:58 AM rather than the promised 24-hour time. Set an explicit 24-hour cycle; the current test only checks that the output is valid and therefore misses this regression.

Comment thread extension/src/popup/components/send/SendTo/index.tsx
Copilot AI review requested due to automatic review settings September 12, 2026 18:07

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

Copilot reviewed 43 out of 43 changed files in this pull request and generated no new comments.

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

extension/src/popup/helpers/soranHistory.ts:22

  • Token-transfer rows fall through when Horizon omits asset_balance_changes. getRowDataByOpType still recognizes those rows via attrs.amount, and already computes isReceiving plus attrs.from/actualDestination, but its metadata only sets isTokenTransfer and to; because this condition does not include isTokenTransfer, no Soran name is shown for that supported fallback path. Preserve from and isReceiving in the token-transfer metadata and handle isTokenTransfer here as well.

Copilot AI review requested due to automatic review settings September 12, 2026 18:30

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

Copilot reviewed 43 out of 43 changed files in this pull request and generated no new comments.

Suppressed comments (3)

Previously missed (1) — in code that hasn't changed since the last review.

extension/src/popup/helpers/soranTransaction.ts:47

  • Contract token symbols may contain :, but splitting at the first colon treats part of the symbol as the issuer. A valid Soran transfer for a token such as foo:bar:<contract> therefore computes an invalid expected contract and is rejected. Parse the canonical ID at its last colon (as splitCanonical does elsewhere).

This issue also appears on line 117 of the same file.

extension/src/popup/helpers/soranTransaction.ts:119

  • This contract-asset guard also parses the first colon, so a contract token whose symbol contains : is misclassified as a classic asset. An altered classic payment to the same recipient could then pass route validation instead of being rejected. Inspect the issuer after the final colon.
    extension/src/popup/helpers/soranHistory.ts:43
  • The PR specifies a 24-hour clock, but toLocaleTimeString defaults to the locale's hour cycle; for example, en-US renders 09:58 AM. Explicitly disable the 12-hour cycle so the timestamp meets that requirement in every supported locale.

@flarcos

flarcos commented Sep 12, 2026

Copy link
Copy Markdown
Author

The amount-precision finding is fixed in e1d83a0. Soran token payments now carry exact base-unit decimal strings into locally constructed i128 transfer XDR. Built-in networks send this XDR through the existing /simulate-tx endpoint. Existing numeric callers and custom-network RPC behavior remain compatible; no indexer schema or Soran contract update is needed. The returned amount, destination and fee are still validated before review.

For the mux-capability finding, the described failure does not occur in the current implementation: checkIsMuxedSupported catches capability lookup failures and returns false, and determineMuxedDestination preserves ordinary G/C recipients in that case. Only an actual unsupported M destination throws. Two new hook regressions use the real helper with a rejected contract-spec lookup; both pass without changing the production fallback.

Validation: 2,274 unit tests across 244 suites passed, including 15 additional cases. Tests now decode the actual outgoing simulation XDR rather than only mocking a prepared response. They cover amounts above JavaScript’s safe-integer limit, the i128 maximum, G/M/C destinations, custom-network compatibility and rejection of altered responses. TypeScript checking, formatting, translation generation and the extension build passed. Read-only Testnet simulations through /simulate-tx also passed for G/M destinations, preserving exact amounts and matching inclusion-plus-resource fees. No transactions were signed or submitted.

The earlier audit fixes in 0062587 remain in place: stale simulation/review invalidation, displayed-fee consistency, Soran path-payment validation, collectible ID 0, correct account-creation counterparties, recent-name selection and translated routing errors. Earlier Chromium testing passed 24 cases with one existing skip; Firefox verified all four documented names through review and a 1→2 XLM retry with the required memo retained.

The timestamp change continues to preserve each locale’s preferred hour cycle and requested regional language tag. Tests cover en-GB, en-US and pt-BR, including translation-bundle fallback; it does not impose a universal 24-hour clock.

Copilot AI review requested due to automatic review settings September 12, 2026 18:43

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

Copilot reviewed 43 out of 43 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

extension/src/popup/helpers/soranHistory.ts:43

  • This still follows each locale’s default hour cycle, so en-US renders values such as 09:58 AM; it does not implement the PR’s stated 24-hour clock behavior. Explicitly request the 24-hour cycle.

Comment thread extension/src/popup/helpers/soranTransaction.ts
Copilot AI review requested due to automatic review settings September 12, 2026 18:57

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

Copilot reviewed 43 out of 43 changed files in this pull request and generated no new comments.

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

extension/src/popup/components/accountHistory/TransactionDetail/index.tsx:342

  • Use the detected locale rather than resolvedLanguage here. This repository only provides en and pt resource bundles, so i18next resolves an en-GB browser to the en bundle; passing that value to Intl loses the region and can select the 12-hour English default, contrary to the intended en-GB 24-hour display. i18n.language retains the detected regional tag while translations can still fall back normally.

Copilot AI review requested due to automatic review settings September 12, 2026 19:12

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

Copilot reviewed 43 out of 43 changed files in this pull request and generated 1 comment.

Comment thread extension/src/popup/helpers/soranTransaction.ts Outdated
Copilot AI review requested due to automatic review settings September 12, 2026 19:26

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

Copilot reviewed 44 out of 44 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

extension/src/popup/views/AccountHistory/hooks/useGetHistoryData.tsx:943

  • Valid collectible token ID 0 never reaches this new naming metadata: the preceding if (!attrs.tokenId) returns the generic row for zero, even though the parser represents a scvU32(0) as tokenId: 0. As a result, token #0 transfers still cannot show sender/current-name information. Treat only null/undefined as missing (and add a zero-ID regression case).

Comment thread extension/src/popup/components/send/SendAmount/hooks/useSimulateTxData.tsx Outdated
Copilot AI review requested due to automatic review settings September 12, 2026 19:57

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

Copilot reviewed 46 out of 46 changed files in this pull request and generated 2 comments.

sorobanMemo = "";
}
} catch (error) {
if (isSoranPayment) throw unsupportedSoranMuxed();
Copilot AI review requested due to automatic review settings September 12, 2026 20:12

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

Copilot reviewed 47 out of 47 changed files in this pull request and generated no new comments.

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.

2 participants