Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50a332f5a0 | ||
|
|
cd730241bb |
@@ -218,7 +218,8 @@ development workflow, and the Makefile targets are thin shims that call them. We
|
||||
provide:
|
||||
|
||||
- `script/bootstrap` — install all dependencies (pinned node via nvm if needed,
|
||||
yarn via corepack, `yarn install --frozen-lockfile`)
|
||||
yarn via corepack, `yarn install --frozen-lockfile`), then fail, naming the
|
||||
module, if node cannot find `jest`, `playwright-core` or `ethers`
|
||||
- `script/setup` — make a fresh clone ready for development: bootstrap plus the
|
||||
git pre-commit hook
|
||||
- `script/projectname` — print the project name (used for the Docker image tag)
|
||||
|
||||
@@ -45,6 +45,24 @@ but the review is broader than any of them.
|
||||
|
||||
# Completed Steps
|
||||
|
||||
- 2026-10-06: `script/bootstrap` no longer reports success while node cannot
|
||||
find a module the tests load
|
||||
([#263](https://git.eeqj.de/sneak/AutistMask/issues/263)). After the install
|
||||
it asks node for `jest`, `playwright-core` and `ethers`, and fails naming the
|
||||
missing one and the fix. yarn skips the install whenever
|
||||
`node_modules/.yarn-integrity` matches `yarn.lock`, so a package deleted from
|
||||
`node_modules` stayed deleted while bootstrap said it was complete. The
|
||||
fresh-clone failure the issue reports did not reproduce.
|
||||
|
||||
- 2026-10-06: A token symbol or name read off a contract is no longer stored cut
|
||||
between the two halves of an emoji
|
||||
([#458](https://git.eeqj.de/sneak/AutistMask/issues/458)). `lookupTokenInfo()`
|
||||
cut both by UTF-16 units; it now counts code points, as `displaySymbol()` has
|
||||
since [#329](https://git.eeqj.de/sneak/AutistMask/issues/329).
|
||||
`tests/tokenLookupTruncation.test.js` looks up a token whose symbol and name
|
||||
are made of emoji outside the Basic Multilingual Plane. A symbol already
|
||||
stored broken is not repaired.
|
||||
|
||||
- 2026-10-06: Reloading or closing the popup no longer logs a request it cancels
|
||||
as a failure in the transaction lists and ENS name lookups on the address and
|
||||
token screens, the address scan after a wallet is created, the endpoint checks
|
||||
|
||||
@@ -127,6 +127,30 @@ install_js_deps() {
|
||||
fi
|
||||
}
|
||||
|
||||
# resolvable <module>: true when node, run from the repo root, finds it
|
||||
resolvable() {
|
||||
if missing node && [ -s "$HOME/.nvm/nvm.sh" ]; then
|
||||
nvm_sh "nvm use $NODE_VERSION >/dev/null && cd \"$ROOT\" && \
|
||||
node -e \"require.resolve('$1')\"" 2>/dev/null
|
||||
else
|
||||
node -e "require.resolve('$1')" 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
# yarn install exits 0 without touching node_modules once
|
||||
# node_modules/.yarn-integrity matches yarn.lock, so a package deleted from
|
||||
# node_modules stays deleted. Fail unless node finds the modules the tests
|
||||
# load: jest for make test, playwright-core and ethers for tests/e2e/.
|
||||
check_js_deps() {
|
||||
for module in jest playwright-core ethers; do
|
||||
if ! resolvable "$module"; then
|
||||
echo "bootstrap: node cannot find $module after yarn install" >&2
|
||||
echo " fix: rm -rf node_modules && make bootstrap" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
main() {
|
||||
cd "$ROOT"
|
||||
|
||||
@@ -136,6 +160,7 @@ main() {
|
||||
ensure_node
|
||||
ensure_yarn
|
||||
install_js_deps
|
||||
check_js_deps
|
||||
|
||||
echo "bootstrap complete"
|
||||
}
|
||||
|
||||
@@ -334,9 +334,12 @@ async function lookupTokenInfo(contractAddress, rpcUrl, networkId, signal) {
|
||||
name = symbol;
|
||||
}
|
||||
|
||||
// Truncate to prevent storage of excessively long values from RPC
|
||||
name = String(name).slice(0, 64);
|
||||
symbol = String(symbol).slice(0, 12);
|
||||
// Truncate to prevent storage of excessively long values from RPC.
|
||||
// Counted in code points, as displaySymbol() counts them, so the cut never
|
||||
// falls between the two halves of an emoji and stores a half that renders
|
||||
// as U+FFFD.
|
||||
name = Array.from(String(name)).slice(0, 64).join("");
|
||||
symbol = Array.from(String(symbol)).slice(0, 12).join("");
|
||||
|
||||
log.infof("Token resolved:", symbol, "decimals", Number(decimals));
|
||||
return { name, symbol, decimals: Number(decimals) };
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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));
|
||||
});
|
||||
Reference in New Issue
Block a user