Improve Add Token disclosure for SAC#2869
Conversation
|
PR Preview build is ready: https://github.com/stellar/freighter/releases/tag/untagged-1ea4e3149826d09d43f1 (SDF collaborators only — install instructions in the release description) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aea39a3184
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0b9b951deb
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e00510aa7d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1a1539b9dc
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
This PR updates the extension popup’s Add Token flow to treat SAC (classic-asset) tokens as a 2-step flow (Add Token → Change Trust review/submit), while keeping SEP-41 tokens as a direct approval path. It also refactors the prior “SAC implies changeTrust” logic out of useSetupAddTokenFlow and into the UI routing, and adds/updates tests and E2E stubs to cover the new behavior.
Changes:
- Route SAC adds through
ChangeTrustInternal(with fee disclosure/prop wiring) before resolving the dApp request; keep SEP-41 as a direct confirm. - Extend
ChangeTrustInternal/Submit UI to better support “standalone full-height” usage and avoid misleading close-tab prompts in this context. - Add new unit tests + expand E2E coverage/stubs for SAC vs SEP-41 add-token scenarios.
Reviewed changes
Copilot reviewed 16 out of 17 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| extension/src/popup/views/AddToken/styles.scss | Removes scroll overflow on the AddToken wrapper styling. |
| extension/src/popup/views/AddToken/index.tsx | Implements SAC 2-step routing into ChangeTrustInternal, fee display, and “Continue” CTA for SAC. |
| extension/src/popup/views/AddToken/tests/AddToken.test.tsx | Adds routing/unit coverage for SAC vs SEP-41 behavior in AddToken. |
| extension/src/popup/locales/pt/translation.json | Adds translations for “Auto-lock timer”, “Auto-Lock Timer”, and “Token address”. |
| extension/src/popup/locales/en/translation.json | Adds English strings for “Auto-lock timer”, “Auto-Lock Timer”, and “Token address”. |
| extension/src/popup/helpers/useSetupAddTokenFlow.ts | Removes SAC trustline submission from the “approve add token” helper; now always dispatches addToken+metrics. |
| extension/src/popup/helpers/useChangeTrustline.ts | Deletes the no-longer-used trustline hook. |
| extension/src/popup/components/WarningMessages/styles.scss | Adds styling for the asset-list banner variant (.ScanAssetList). |
| extension/src/popup/components/WarningMessages/index.tsx | Updates AssetListWarning styling class + icon. |
| extension/src/popup/components/manageAssets/ManageAssetRows/ChangeTrustInternal/SubmitTx/index.tsx | Adds onClose + hideCloseTabHint behavior; routes Done differently on success vs failure. |
| extension/src/popup/components/manageAssets/ManageAssetRows/ChangeTrustInternal/styles.scss | Adds “standalone/full-height” layout rules and scroll behavior for the external Add Token flow. |
| extension/src/popup/components/manageAssets/ManageAssetRows/ChangeTrustInternal/index.tsx | Adds onSuccess, initialFee, isFullHeight; passes fee into XDR build and adjusts padding for submit step. |
| extension/src/popup/components/manageAssets/ManageAssetRows/ChangeTrustInternal/hooks/useChangeTrustData.tsx | Makes change-trust data reload when key inputs (including fee) change. |
| extension/src/popup/components/tests/ChangeTrustInternal.test.tsx | Adds coverage for onSuccess vs onCancel behavior and fee initialization/unit correctness. |
| extension/e2e-tests/integration-tests/freighterApiIntegration.test.ts | Adds/updates E2E coverage for verified/unverified SEP-41 and SAC add-token flows, including SAC-specific stub injection. |
| extension/e2e-tests/helpers/stubs.ts | Adds SAC-specific stubs and a helper to mark a contractId as “verified” via asset-list stubbing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5e51fd73e6
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
….com/stellar/freighter into feat/addtoken-sac-changetrust-review
|
|
||
| return ( | ||
| <React.Fragment> | ||
| <SubviewHeader title={t("Auto-Lock Timer")} /> |
There was a problem hiding this comment.
side-fix to unify the translations and add the PT translation for it. the prettier was adding them back before push. was missing from the other PR that was merged related to the auto lock
Add-SAC-token: verified-list path can silently skip the trustlineSuggestion / latent edge — not a merge blocker. Reachable only when an asset-list entry for a real classic-asset SAC is imperfect (missing/normalized issuer or code), so it won't show up in the happy-path or the current tests — but when it does hit, the failure is silent (token added, no trustline) rather than a visible error. TL;DR: When an add-token request resolves a token via a verified asset list, the decision to create a trustline rests entirely on re-deriving the asset's contract from the list's Detailed explanation (for agents)Root cause: two different SAC tests, and on the verified-list path only the weaker one runs. There are two SAC determinations in this flow:
On the manual lookup path these agree, because freighter/extension/src/popup/helpers/useTokenLookup.ts Lines 89 to 110 in 6255393 On the verified-list path, freighter/extension/src/popup/helpers/useTokenLookup.ts Lines 132 to 140 in 6255393 Routing then rests solely on freighter/extension/src/popup/views/AddToken/index.tsx Lines 315 to 322 in 6255393 So for a genuine classic-asset SAC whose list entry is imperfect,
Secondary inconsistency: the two functions key on different network fields — Deterministic repro: drive Suggested fixes (increasing depth):
|
Add-SAC-token: a failed submit tells the dApp the user rejected, with no way to retrySuggestion / UX + correctness — not a merge blocker. Happens on any transient submit failure (network blip, TL;DR: If the trustline transaction fails to submit, the only thing the user can do is dismiss — and dismissing tells the dApp "the user rejected this request," even though the user wanted the token and the submit just failed. There's no retry. Ideally a transient failure should let the user retry in place, and the dApp should only ever hear "user rejected" when the user actually rejects. Steps to reproduce:
Detailed explanation (for agents)Root cause. In the failure state ( Done branches freighter/extension/src/popup/views/AddToken/index.tsx Lines 394 to 395 in 6255393
freighter/extension/src/popup/helpers/useSetupAddTokenFlow.ts Lines 40 to 43 in 6255393
freighter/@shared/api/helpers/extensionMessaging.ts Lines 94 to 103 in 6255393 So a failed submit and a deliberate user rejection are indistinguishable to the dApp, and the user has no way to retry. Note the same Also relevant: the window-close fallback Suggested fix (matches the desired behavior):
|
Add-SAC-token: success handler can leave the popup open and the dApp hangingSuggestion / correctness — not a merge blocker. Only triggers if the TL;DR: The SAC add-token success handler has no error handling, while the equivalent handler for the other token type does. If adding the token fails at the last step, the popup stays open, the dApp's request never resolves, and the failure isn't recorded — whereas the sibling path cleanly closes and records the failure. Detailed explanation (for agents)Root cause. freighter/extension/src/popup/views/AddToken/index.tsx Lines 123 to 128 in 6255393 The SEP-41 path uses freighter/extension/src/popup/helpers/useSetupAddTokenFlow.ts Lines 46 to 54 in 6255393
Suggested fix: have the SAC branch reuse |
Deleting
|
SAC trustline review re-does work AddToken already did (redundant network calls + a stale lint suppression)Suggestion / efficiency + maintainability — not a merge blocker. Several redundant network round-trips on the SAC review screen, plus one of them re-fires on every fee-editor change. TL;DR: When the SAC add-token review opens, it re-fetches data the previous screen already had — it scans the asset with Blockaid a second time and re-fetches network fees a second time, because neither result is passed down. On top of that, editing the fee in the review re-runs the whole data fetch (including the Blockaid scan and an account load) even though only the fee changed. None of this is user-visible beyond extra latency/requests, but it's easy to avoid by threading the already-fetched values through. Detailed explanation (for agents)These four share one root cause: (a) Fee edit re-runs the full data fetch. The data-fetch effect went from mount-once (
(b) Stale (c) Duplicate Blockaid scan. freighter/extension/src/popup/views/AddToken/index.tsx Lines 173 to 195 in 6255393 When the SAC review mounts, (d) Duplicate ChangeTrustInternal calls useNetworkFees() again on mount, issuing a second server.feeStats() round-trip purely to populate the Fee pane:Pass the parent's recommendedFee down alongside initialFee and skip the child's refetch on this path.
Net: threading |
…at/addtoken-sac-changetrust-review
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 75668d8cf5
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| networkDetails={state.data.settings.networkDetails} | ||
| onCancel={() => setShowTrustlineReview(false)} | ||
| onSuccess={addTokenAndClose} | ||
| onClose={rejectAndClose} |
There was a problem hiding this comment.
Send a failure response for failed SAC submits
When the external SAC trustline submit reaches the Failed screen, this onClose path calls rejectAndClose, which dispatches the generic reject handler; the background then receives undefined and maps the dApp addToken result to FreighterApiDeclinedError. Fresh evidence in this revision is that freighterApiMessageListener now has an explicit AddTokenResponse apiError branch for failed-then-dismissed submits, but this wiring never uses it, so dApps cannot distinguish a network/signing failure from the user declining the add-token request and metrics are also recorded as a rejection.
Useful? React with 👍 / 👎.
|
@piyalbasu adjusted some of the issues. like the SAC for XLM not loading, there was a hydration issue / race in some cases, so the pub key and network were empty. should work consistently now
not to create a separate ticket just for a small ui change, I increased the space a bit for the settings Screen.Recording.2026-06-30.at.11.25.47.movTrustline check for already added token
added an error message if trustline is already added and handling of the return message to the dapp on transaction success, not on done Screen.Recording.2026-06-30.at.14.00.51.mov |
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
Add-SAC-token: a successful trustline can still report "user rejected" to the dApp (residual race)Suggestion / correctness — not a merge blocker. Reachable only on a fast Done-click, but the outcome is the worst kind of wrong: the dApp is told the user declined a trustline that actually succeeded on-chain. TL;DR: This is the un-closed remainder of the concern Codex raised earlier (resolve add-token from submit completion, not from a window-close). The loading-time "Close" case was fixed by resolving off the transaction success — but that resolution is fire-and-forget, and the "Done" button still closes the window. If the user clicks Done before the token-add round-trip finishes, the window-close path wins and the dApp hears "user rejected" for a trustline that succeeded. Steps to reproduce:
Detailed explanation (for agents)Root cause. The dApp request is resolved off the transaction-success callback, but the call is not awaited and races the window close.
freighter/extension/src/popup/views/AddToken/index.tsx Lines 182 to 189 in 5b9161c
freighter/extension/src/popup/views/AddToken/index.tsx Lines 562 to 582 in 5b9161c In In the background, two resolvers compete and
freighter/extension/src/popup/helpers/useSetupAddTokenFlow.ts Lines 76 to 99 in 5b9161c If Deterministic repro. In a unit/integration test, render the SAC success state, invoke the Done handler synchronously in the same tick that Suggested fixes (increasing depth):
|
The
|
Add-token: a storage-write failure reports success to the dApp on the SEP-41 pathSuggestion / correctness regression — not a merge blocker. Narrow (only when the local storage write itself throws), but on that path the dApp is told the token was added when nothing was persisted. TL;DR: When persisting the added token fails, the code now swallows the error and returns success — justified by "the trustline already succeeded." That reasoning holds for SAC/classic assets (which submit a trustline first), but SEP-41 tokens have no trustline: the storage write is the whole operation. So a failed write on the SEP-41 path tells the dApp "added" while the token silently isn't there on reload. Before this PR that case returned an error. Detailed explanation (for agents)Root cause. The The comment justifies this for the case where "the user has already approved and submitted a successful trustline transaction" — but the same helper serves the SEP-41 direct-approval path, which submits no trustline. freighter/extension/src/popup/views/AddToken/index.tsx Lines 788 to 802 in 5b9161c freighter/extension/src/popup/helpers/useSetupAddTokenFlow.ts Lines 76 to 111 in 5b9161c The handler then maps a missing So on the SEP-41 path, if Deterministic repro. Unit-test Suggested fix. Gate the error-swallow on whether a trustline transaction actually occurred — e.g. pass a flag (or split the helper) so the SAC/classic post-trustline path swallows a persistence failure (to avoid declining an on-chain success), while the SEP-41 path surfaces the |
|
@piyalbasu taking a look at the new comments now. will report back soon |



Closes #2826
Small PR to adjust the flow for adding SAC tokens to make it a 2 step flow on the Add token + Confirm Transaction for clarity
and for SEP-41 it continues being a direct approval
e.g. "before" screenshot

Videos: 1 SAC verified / 1 SEP-41 unverified
Screen.Recording.2026-06-24.at.17.45.53.mov
Screen.Recording.2026-06-24.at.17.46.56.mov