Severity: High · Area: Frontend (frontend/src/services/stellar-impl.ts)
Description
scValToString decodes an scvAddress of contract type into a raw 64-character hex string rather than the canonical C… StrKey (stellar-impl.ts:384-396):
if (addr.switch() === xdr.ScAddressType.scAddressTypeAccount()) {
return StrKey.encodeEd25519PublicKey(addr.accountId().ed25519())
}
return Array.from(addr.contractId() as Uint8Array)
.map((b) => b.toString(16).padStart(2, '0'))
.join('')
The account branch is correct; the contract branch is not. StrKey.encodeContract is imported-adjacent but never called anywhere in the file. Every event field holding a token contract address — created.tokenAddress (:483), meta.tokenAddress (:489), mint.tokenAddress (:493), burn.tokenAddress (:498) — is therefore stored in an encoding no other part of the system speaks.
Downstream breakage, re-verified against the current tree:
- Per-token history is always empty.
getTokenEvents filters with event.data.tokenAddress === tokenAddress (:1336) where the argument is a C… StrKey supplied by the UI. Hex never equals StrKey, so the filter matches nothing and the history view renders empty for every token that has events.
- The explorer's index search is broken.
TokenExplorer.tsx:250-255 pulls event.data.tokenAddress from the created-events list and passes it to loadTokenByAddress(...), which reaches new Address(hexString) — that throws, and the user sees "Token not found at this index" for a token that exists.
- Explorer links and address displays are meaningless.
TokenExplorer.tsx:91-99 builds its indexToAddress and addressToMeta maps from the same hex values, and useTokenDashboard.ts:72-74 attaches them to every dashboard row as address. Anything rendering or linking that value emits a dead link and an address no block explorer will resolve.
- There is no test coverage on the decoder at all — no test file in
frontend/src references scValToString or encodeContract, which is why an encoding bug this consequential survived.
One claim from the original filing is now stale and has been removed: getTokenInfoByAddress no longer depends on this comparison. It routes through resolveTokenInfoByAddress → getTokenInfoByAddressView (:1200, :1260-1285), a real contract view call, and returns an explicit unresolved marker instead of fabricating placeholder name/decimals. Token identity is authoritative today; it is the event-derived address plumbing that is broken.
Tasks
Acceptance Criteria
Re-verified and sharpened on 2026-08-19 during the 30-issue codebase audit tracked in ISSUES.md.
Severity: High · Area: Frontend (
frontend/src/services/stellar-impl.ts)Description
scValToStringdecodes anscvAddressof contract type into a raw 64-character hex string rather than the canonicalC…StrKey (stellar-impl.ts:384-396):The account branch is correct; the contract branch is not.
StrKey.encodeContractis imported-adjacent but never called anywhere in the file. Every event field holding a token contract address —created.tokenAddress(:483),meta.tokenAddress(:489),mint.tokenAddress(:493),burn.tokenAddress(:498) — is therefore stored in an encoding no other part of the system speaks.Downstream breakage, re-verified against the current tree:
getTokenEventsfilters withevent.data.tokenAddress === tokenAddress(:1336) where the argument is aC…StrKey supplied by the UI. Hex never equals StrKey, so the filter matches nothing and the history view renders empty for every token that has events.TokenExplorer.tsx:250-255pullsevent.data.tokenAddressfrom the created-events list and passes it toloadTokenByAddress(...), which reachesnew Address(hexString)— that throws, and the user sees "Token not found at this index" for a token that exists.TokenExplorer.tsx:91-99builds itsindexToAddressandaddressToMetamaps from the same hex values, anduseTokenDashboard.ts:72-74attaches them to every dashboard row asaddress. Anything rendering or linking that value emits a dead link and an address no block explorer will resolve.frontend/srcreferencesscValToStringorencodeContract, which is why an encoding bug this consequential survived.One claim from the original filing is now stale and has been removed:
getTokenInfoByAddressno longer depends on this comparison. It routes throughresolveTokenInfoByAddress→getTokenInfoByAddressView(:1200,:1260-1285), a real contract view call, and returns an explicitunresolvedmarker instead of fabricating placeholder name/decimals. Token identity is authoritative today; it is the event-derived address plumbing that is broken.Tasks
StrKey.encodeContract(addr.contractId())inscValToString, and review the account branch for muxed-account (scAddressTypeMuxedAccount) handling while there.scValToNativeplus a thin formatting layer over the hand-rolled decoder wherever the SDK's canonical conversion suffices, so this class of bug cannot recur field by field.C…for contract addresses andG…for account addresses — the missing coverage is itself part of this defect.getTokenEventsreturns a created token's events when queried by its StrKey address, and that the explorer's index search resolves to a real token.AddressDisplaytruncation, and CSV export render canonical StrKey, since all three consume the same values.Acceptance Criteria
scValToStringhas direct unit-test coverage for the address branches it decodes.Re-verified and sharpened on 2026-08-19 during the 30-issue codebase audit tracked in
ISSUES.md.