Skip to content

Improve Add Token disclosure for SAC (classic-asset) tokens #2826

Description

@piyalbasu

Context

When a dapp calls addToken({ contractId }) and the resolved contract is a Stellar Asset Contract (SAC) — i.e. the deterministic Soroban wrapper around a classic Stellar asset — Freighter's /add-token confirm flow signs and submits a changeTrust operation for the underlying classic asset (useSetupAddTokenFlowuseChangeTrustlinesignFreighterTransactionsubmitFreighterTransaction).

For pure-Soroban (SEP-41) tokens — contract-id issuer, no classic counterpart — the same popup path only writes the token to the local "tracked" list; nothing is signed and nothing is broadcast.

The current popup uses identical copy and layout for both cases — the SAC branch never tells the user that a Stellar transaction will be signed, that a trustline reserve will be locked, or that a network fee will be charged. Separately, in both cases the popup omits the token's contract address, so users can't cross-check that Freighter is registering the same contract the dapp claimed it was registering.

This issue tracks tightening the disclosure in the /add-token popup so the user can see (a) the contract being registered, and (b) for the SAC branch, what is actually being signed on-chain.

Current design (production today)

Current Add Token popup — SAC token, no on-chain disclosure, no contract address

What the user sees today for a SAC token:

  • Generic description: "Allow token to be displayed and used with this wallet address"
  • One metadata row: Wallet
  • No contract address, no issuer, no reserve, no fee

What's actually about to happen when they hit Confirm:

  • changeTrust({ asset: new Asset(code, issuer) }) op gets built against the resolved classic issuer
  • The user's account is the source; their session-unlocked key signs it
  • The signed XDR is POSTed to the indexer's /submit-tx
  • A 0.5 XLM subentry reserve is locked, plus a network fee

For pure-Soroban tokens the popup is identical, even though only the local tracked-token list is touched. The token's self-reported name/symbol come from the contract's own name()/symbol() calls — strings the contract author controls — so there's no on-screen identifier the user can use to verify what's actually being registered.

Proposed design (AI-generated mock)

Proposed Add Token popup — Contract row + SAC disclosure block

The mock above includes:

  1. Contract row — shown in both branches (SEP-41 and SAC). The contract address (params.contractId, truncated) is the only un-spoofable on-screen identifier, so it should be displayed even for the SEP-41 case where nothing is signed. Lets the user cross-check Freighter's resolved contract against whatever the dapp told them.

  2. SAC-only disclosure block — rendered only when StrKey.isValidEd25519PublicKey(assetIssuer) === true:

    • Replacement description copy instead of the generic display-only line:

      "Approving will submit a Stellar transaction that opens a trustline to this asset's issuer. This will modify your Stellar account, lock a reserve, and charge a network fee."

    • Three additional metadata rows below Contract:
      • Issuer — truncated G… address of the underlying classic issuer
      • Account reserve0.5 XLM (from BASE_RESERVE)
      • Network fee — the same recommendedFee value that flows into getManageAssetXDR (so what's displayed matches what's actually charged)

The pure-Soroban branch keeps the existing description and adds only the Contract row. SAC tokens get Contract + the disclosure block.

(The mock is a working prototype, not a final design — handing off for adjustments. The "Not on your lists" banner in both screenshots is an existing component, not part of this change.)

Open design questions

  • Icon choices for the new rows (current mock: Icon.CodeCircle01 for Contract, Icon.User01 for Issuer, Icon.Lock01 for Account reserve, Icon.Coins03 for Network fee)
  • Row order — current is Wallet → Contract → Issuer → Reserve → Fee. Should the on-chain rows be visually grouped/separated from Wallet + Contract (which are present on every token)?
  • Wording on Account reserve"Trustline reserve" may communicate the locked-not-spent nature more clearly
  • Whether the description block needs additional visual weight (info or warning icon) on the SAC branch
  • Whether Contract should sit above or below Wallet (current: below)

Implementation

Draft PR: #2827 (branch chore/addtoken-sac-disclosure). Two commits, presentational only — no changes to useSetupAddTokenFlow, useChangeTrustline, or any signing path:

  • a9c5ed7f — SAC disclosure block (description copy + Issuer / Reserve / Fee rows)
  • 4c1ac5ec — unconditional Contract row

Held as draft until design direction is confirmed; ping me on the PR once we land on copy + layout adjustments and I'll iterate.

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions