Skip to content

fix(soroban): render signed SCMap entry on the signing screens - #3017

Draft
aristidesstaffieri wants to merge 1 commit into
masterfrom
fix/soroban-signing-display-fidelity
Draft

aristidesstaffieri wants to merge 1 commit into
masterfrom
fix/soroban-signing-display-fidelity

Conversation

@aristidesstaffieri

@aristidesstaffieri aristidesstaffieri commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Renders Soroban contract-call arguments on the signing screens from the signed SCVal rather than from a decoded native value, so the screen matches what is being signed. Three defects are fixed:

  • Map entries no longer disappear. Decoding a map to a JS object coerced every key to a string and let a later entry overwrite an earlier one, so an SCMap with N signed entries could render as one line — with no glyph and no warning. Maps now render from the signed entry list, so entry count is preserved by construction.
  • Key types stay distinguishable. u64(1) and string("1"), or symbol("a") and string("a"), previously rendered as identical keys. Quoting now carries the type: strings quoted, symbols and numbers bare, structured keys inline.
  • Binary text renders as labelled hex. SCString, SCSymbol, function names and CAP-85 executable tags are decoded strictly instead of leniently, so two distinct non-UTF-8 payloads can no longer collapse onto the same U+FFFD string.

All three hold at every nesting depth. Only a top-level argument was handled before: one level down a map collapsed, a non-UTF-8 string rendered as {"0":97,"1":108,…}, and bytes as {"type":"Buffer","data":[…]}.

Auth-entry invocation trees now carry xdr.ScVal args through to the renderer instead of decoding to natives and re-encoding, a round trip that silently re-typed map keys.

Affected surfaces: SignTransaction (Parameters and Auth Entries), SignAuthEntry, and the ReviewTransaction auth block.

Display format change — map and vector parameters render as a value literal rather than JSON:

{                          {                        {
  1: "from-u64",             a: "sym",                { id: 0 }: "v0",
  "1": "from-string"         "a": "str"               { id: 1 }: "v1"
}                          }                        }
 u64 vs string key          symbol vs string key     struct keys

Test plan

  • yarn test:ci — 1929 passing, including 24 new cases: entry-count preservation for structured and mixed-type map keys, address- and u32-keyed controls, nested containers, and strict decoding of non-UTF-8 strings, symbols, executable tags and function names
  • yarn build:extension
  • Manual: non-UTF-8 string arguments, nested containers and a non-UTF-8 function name through the signing screen
  • Manual: struct-keyed, mixed-key and symbol-vs-string map arguments
  • CI green
  • Refresh Playwright screenshot baselines covering the sign flow

…oNative

  scValToNative decodes SCMaps through Object.fromEntries, which coerces
  every key via ToPropertyKey and applies last-write-wins, so a map with N
  signed entries could render as one on the approval screen — with no glyph
  and no warning. Render maps and vecs from the signed entry list instead,
  as a value literal where quoting carries the key type.

  Decode SCString/SCSymbol-backed fields (arguments, function names, CAP-85
  executable tags) strictly rather than leniently, so invalid UTF-8 renders
  as labelled hex instead of collapsing distinct payloads onto one U+FFFD
  string. Carry auth-tree args through as ScVal rather than round tripping
  them through nativeToScVal, which silently re-typed map keys.
@github-actions

Copy link
Copy Markdown
Contributor

PR Preview build is ready: https://github.com/stellar/freighter/releases/tag/untagged-28a85236081970aaaca9
Backend: sandbox (aristidesstaffieri). SDF collaborators only — install instructions in the release description.

@aristidesstaffieri aristidesstaffieri changed the title fix(soroban): better rendering of SC map/vec values fix(soroban): render signed SCMap entry on the signing screens Sep 21, 2026

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.

Copilot review overview

🟡 Changes recommended

Some distinct signed values remain ambiguous or incomplete in the approval UI.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 5 High severity · 1 Medium severity

Open (6)
What changed in this PR

Improves Soroban signing-screen rendering while preserving signed XDR value types.

Changes:

  • Adds byte-safe rendering for Soroban strings, symbols, and tags.
  • Renders maps and vectors without lossy native conversion.
  • Adds helper and UI regression tests.
File Description
extension/​src/​popup/​helpers/​soroban.ts Adds type-aware Soroban value rendering.
extension/​src/​popup/​helpers/​__tests__/​soroban.test.js Tests invocation fidelity and binary names.
extension/​src/​popup/​helpers/​__tests__/​scValByType.test.js Tests container and binary-value rendering.
extension/​src/​popup/​components/​signTransaction/​Operations/​KeyVal/​index.tsx Passes original SCVals to the renderer.
extension/​src/​popup/​components/​signTransaction/​Operations/​index.tsx Uses safer function-name rendering.
extension/​src/​popup/​components/​signTransaction/​Operations/​__tests__/​index.test.tsx Tests rendered contract-call parameters.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +643 to +646
const decoded = value.asStringOrBytes();
return typeof decoded === "string"
? decoded
: `${kind}(0x${xdr.encodeBytes(decoded, "hex")})`;
Comment on lines +696 to +699
const entries = scVal.map || [];
if (!entries.length) {
return "{}";
}
Comment on lines +700 to +704
const lines = entries.map(
(entry) =>
`${scValToDisplayValue(entry.key, {
compact: true,
})}: ${scValToDisplayValue(entry.val, {
Comment on lines +715 to +718
const values = scVal.vec || [];
if (!values.length) {
return "[]";
}
Comment on lines +755 to +759
case "scvContractInstance": {
const executable = scVal.instance.executable;
return executable.type === "contractExecutableWasm"
? `contractInstance(0x${xdr.encodeBytes(executable.wasmHash.toBytes(), "hex")})`
: `contractInstance(${executable.type})`;
Comment on lines +709 to +711
return compact
? `{ ${lines.join(", ")} }`
: `{\n${innerPad}${lines.join(`,\n${innerPad}`)}\n${pad}}`;
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