// lookupTokenInfo() cuts the symbol and name it reads off a contract on // code-point boundaries, as displaySymbol() counts them, so an emoji outside // the Basic Multilingual Plane is never stored cut in half // (https://git.eeqj.de/sneak/AutistMask/issues/458). // An emoji outside the Basic Multilingual Plane: two UTF-16 units. const FOX = "\u{1F98A}"; // The leading "A" puts every emoji at an odd UTF-16 offset, so a cut that // counts UTF-16 units lands between the two halves of one. const SYMBOL = "A" + FOX.repeat(12); const NAME = "A" + FOX.repeat(64); // The contract answers symbol(), decimals() and name() with the values above. jest.doMock("ethers", () => ({ ...jest.requireActual("ethers"), Contract: function () { return { symbol: async () => SYMBOL, decimals: async () => 18n, name: async () => NAME, }; }, })); const { lookupTokenInfo } = require("../src/shared/balances"); const ADDRESS = "0x1111111111111111111111111111111111111111"; const RPC_URL = "https://rpc.example.invalid"; beforeEach(() => { // A token found is logged at info level, which is not under test. jest.spyOn(console, "log").mockImplementation(() => {}); }); afterEach(() => { jest.restoreAllMocks(); }); test("the symbol is cut to 12 code points, never inside an emoji", async () => { const { symbol } = await lookupTokenInfo(ADDRESS, RPC_URL, "mainnet"); expect(symbol).toBe("A" + FOX.repeat(11)); }); test("the name is cut to 64 code points, never inside an emoji", async () => { const { name } = await lookupTokenInfo(ADDRESS, RPC_URL, "mainnet"); expect(name).toBe("A" + FOX.repeat(63)); });