// The length bound on a token symbol as displayed. // // A symbol is whatever an ERC-20's symbol() returns and the wallet fetches // it from the block explorer, which imposes no length: src/shared/balances.js // takes `item.token.symbol` as given. A kilobyte-long symbol is a real // return value, and rendering it pushes every amount off the row, scrolls // the balance list past the screen, and hides the figures the user is there // to read. // // This is a layout bound, not a security control. Escaping is what makes a // hostile symbol inert (see src/shared/html.js), and isSpoofedSymbol() is // what catches one impersonating a known ticker; neither job belongs here // and neither is done here. Truncating an unescaped symbol would still be // an injection, just a shorter one. // // 12 characters, which is the bound lookupTokenInfo() in // src/shared/balances.js already applies when it stores a symbol read // straight off a contract; the explorer path was the one with no bound at // all. The longest symbol across the 512 entries of the bundled list is 10 // (MSYRUPUSDP), so nothing the wallet ships as a real token is ever // truncated. The ellipsis is what tells the user the name they are looking // at is not the whole name — worth knowing before they send to it. const MAX_SYMBOL_LENGTH = 12; // The placeholder for a token whose symbol the explorer did not report. // balances.js already substitutes this; repeated here so a symbol that // arrives empty from anywhere else displays the same way rather than as a // blank gap in the row. const UNKNOWN_SYMBOL = "???"; function displaySymbol(symbol) { const s = symbol === null || symbol === undefined ? "" : String(symbol); if (s.length === 0) return UNKNOWN_SYMBOL; if (s.length <= MAX_SYMBOL_LENGTH) return s; return s.slice(0, MAX_SYMBOL_LENGTH - 1) + "…"; } module.exports = { displaySymbol, MAX_SYMBOL_LENGTH, UNKNOWN_SYMBOL, };