Conversation
feat: Massa network
fix massa signature verify
devop: export types
devop: massa: migrate crypto to local
feat: add TAC mainnet
rework sent input amount
feat: massalabs network
Prep Release: v2.11.0
WalkthroughAdds Massa blockchain support across provider, networks, API, UI, signer, and keyring; updates types and background to register Massa. Adjusts Ethereum network registry (add TAC, zkSync Sepolia; remove several), updates Fantom explorer and configs. Removes SID resolution. Bumps Node versions, Docker base, dependencies, tooling, and minor UI/UX tweaks. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant UI as Massa Send UI
participant Prov as MassaProvider
participant Net as MassaNetwork/API
participant KR as KeyRing
participant Req as Request Layer
participant Act as ActivityState
User->>UI: Enter to, amount, token
UI->>Net: getAllTokenInfo(address)
Net-->>UI: assets, MAS price/balances
UI->>Net: getMinimalFee()
Net-->>UI: fee
User->>UI: Click Send
UI->>Prov: request(massa_setNetwork?)
Prov->>Req: apply middlewares
UI->>KR: MassaTransactionSigner(sign)
KR-->>UI: signature
UI->>Net: submit operation (sendOperation)
Net-->>UI: operationId
UI->>Act: store activities (sender/receiver)
Act-->>UI: saved
UI-->>User: Navigate to verify/success
rect rgb(245,248,255)
note right of Net: New Massa integration (balances, fees, submit)
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
💼 Build Files |
There was a problem hiding this comment.
Actionable comments posted: 22
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
packages/extension/src/providers/ethereum/libs/assets-handlers/assetinfo-mew.ts (1)
241-243: Blockscout path may omit native token → crash risk downstreamThe TB path backfills the native token when missing (Lines 257–266). The Blockscout path returns raw results and does not add a native entry. If Blockscout returns no entries or omits the native token,
balances[NATIVE_TOKEN_ADDRESS]is undefined andbalances[address].balance(Line 337) will throw.Fix by backfilling the native token in the Blockscout branch:
} else if (supportedNetworks[chain].bsEndpoint) { - return getBlockscoutBalances(chain, address); + return getBlockscoutBalances(chain, address).then(tokens => { + const hasNative = tokens.some( + (i: TokenBalance) => i.contract === NATIVE_TOKEN_ADDRESS, + ); + if (!hasNative) { + tokens.push({ + contract: NATIVE_TOKEN_ADDRESS, + balance: '0x0', + } as TokenBalance); + } + return tokens; + }); }Also applies to: 334-345
package.json (1)
30-31: Remove hardcoded OTP from publish script (package.json:30).
Hardcoded OTP "312003" is committed — remove it from VCS. Pass OTP via env (e.g. --otp $NPM_OTP), use an npm CI token instead, or omit and enter interactively.- "publish": "yarn workspaces foreach --worktree -pv --exclude @enkryptcom/extension exec 'yarn npm publish --access public --otp 312003'", + "publish": "yarn workspaces foreach --worktree -pv --exclude @enkryptcom/extension exec 'yarn npm publish --access public'",packages/extension/src/libs/tokens-state/index.ts (2)
35-43: Duplicate detection ignores Massa tokens.Currently checks only TokenType.ERC20, allowing duplicate Massa tokens by address on the same chain.
Apply:
- if ( - t.type === TokenType.ERC20 && - (t as CustomErc20Token).address.toLowerCase() === - token.address.toLowerCase() - ) { + // Prevent duplicates for any address-bearing token (ERC20/MRC20) + if ( + "address" in t && + (t as CustomErc20Token | CustomMassaToken).address.toLowerCase() === + token.address.toLowerCase() + ) { return false; }
54-55: Persist write is not awaited.addErc20Token returns before storage is flushed; removeErc20Token awaits it. Make behavior consistent to avoid race conditions.
- this.storage.set(StorageKeys.customTokens, state); + await this.storage.set(StorageKeys.customTokens, state);
🧹 Nitpick comments (99)
packages/extension/src/ui/action/components/app-menu/index.vue (1)
356-357: Guard against missing name_long and sort case-insensitively.If any network lacks name_long, localeCompare will throw. Also, case-insensitive compare improves UX consistency.
Apply this diff:
- ? a.name_long.localeCompare(b.name_long) - : b.name_long.localeCompare(a.name_long); + ? (a.name_long ?? a.name).localeCompare(b.name_long ?? b.name, undefined, { sensitivity: 'base' }) + : (b.name_long ?? b.name).localeCompare(a.name_long ?? a.name, undefined, { sensitivity: 'base' });packages/extension/src/providers/common/ui/send-transaction/send-input-amount.vue (8)
147-151: Make truncation robust in flex; avoid duplicate ellipsis on the container.Ellipsis should be applied on the text element; keeping it on the container can cause unexpected clipping. Also add min-width: 0 so the child flex item can shrink in Safari/Firefox.
&__fiat { position: absolute; left: 16px; bottom: 16px; display: flex; justify-content: flex-start; align-items: center; flex-direction: row; - max-width: 200px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + max-width: 200px; + min-width: 0; // allow children to shrink inside flex container }
165-168: Ensure the text actually truncates across browsers.Add min-width: 0 (and optionally flex: 1 1 auto) to the span so it can shrink and show the ellipsis reliably.
span { font-style: normal; font-weight: 400; font-size: 14px; line-height: 20px; - text-align: center; + text-align: center; letter-spacing: 0.25px; color: @tertiaryLabel; + min-width: 0; // important for flex overflow handling + flex: 1 1 auto; // optional: let text take remaining space within max container width overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
16-16: Optional: show full fiat value on hover.Add a title for discoverable access to the truncated value.
- <span>{{ $filters.parseCurrency(fiatEquivalent) }}</span> + <span :title="$filters.parseCurrency(fiatEquivalent)">{{ $filters.parseCurrency(fiatEquivalent) }}</span>
62-64: Guard against NaN when amount is empty/invalid.Multiplying by an empty string yields NaN in BigNumber, which may render as “NaN”. Return "0" instead.
-const fiatEquivalent = computed(() => { - return new BigNumber(props.fiatValue).times(props.amount).toString(); -}); +const fiatEquivalent = computed(() => { + const amt = props.amount?.trim(); + if (!amt) return '0'; + const v = new BigNumber(props.fiatValue || 0).times(new BigNumber(amt || 0)); + return v.isFinite() ? v.toString() : '0'; +});
74-86: Replace keypress filtering with input-based sanitization (handles paste, IME, and mobile).keypress/keyCode are deprecated and won’t catch paste/drag. Sanitize on input instead.
- @keypress="onlyNumber" + @input="sanitizeAmount"-const onlyNumber = ($event: KeyboardEvent) => { - const keyCode = $event.keyCode ? $event.keyCode : $event.which; - // Numeric - if (keyCode >= /* 0 */ 48 && keyCode <= /* 9 */ 57) { - return; - } - // Only allow a single period - if (keyCode === /* '.' */ 46 && amount.value.indexOf('.') === -1) { - return; - } - // Alphabetical (/non-numeric) or mulitple periods. Don't propagate change - $event.preventDefault(); -}; +const sanitizeAmount = (e: Event) => { + const raw = (e.target as HTMLInputElement).value; + // keep digits and at most one dot + const cleaned = raw + .replace(/[^\d.]/g, '') + .replace(/^(\.)+/, '0.') + .replace(/(\..*)\./g, '$1'); + emit('update:inputAmount', cleaned); +};
10-11: Avoid toggle-on-both focus/blur; set explicit state.Toggling can desync on edge cases. Set true on focus, false on blur.
- @focus="changeFocus" - @blur="changeFocus" + @focus="setFocus(true)" + @blur="setFocus(false)"-const changeFocus = () => { - isFocus.value = !isFocus.value; -}; +const setFocus = (v: boolean) => { + isFocus.value = v; +};Also applies to: 87-89
8-8: Theme the error color instead of hardcoding red/black.Bind a class and style via theme tokens for consistency with dark modes/themes.
- :style="{ color: !hasEnoughBalance ? 'red' : 'black' }" + :class="{ 'is-error': !hasEnoughBalance }".send-input-amount { + & .is-error { + color: @error; // or appropriate theme var + } }
130-137: Dead CSS for number inputs.Input is type="text"; the number spin-button overrides are unused here.
- input[type='number']::-webkit-outer-spin-button, - input[type='number']::-webkit-inner-spin-button { - -webkit-appearance: none; - margin: 0; - } - input[type='number'] { - -moz-appearance: textfield; - }packages/extension/src/providers/common/libs/new-features.ts (1)
3-3: Optional: mark as readonly to prevent accidental mutationTiny safety/intent signal; no behavior change.
Apply:
-const newNetworks: NetworkNames[] = [NetworkNames.Massa]; +const newNetworks: readonly NetworkNames[] = [NetworkNames.Massa];packages/signers/massa/tsconfig.json (6)
18-24: Include vs exclude conflict for testsYou include tests//*.ts but exclude tests/ at the same time, so tests won’t compile. Decide one way. If this tsconfig is for build output only, exclude tests and don’t include them; otherwise, create a separate tsconfig for tests.
Apply if “build-only”:
- "include": ["src/**/*.ts", "tests/**/*.ts"], + "include": ["src/**/*.ts"], @@ - ".vscode/**/*", - "tests/**/*" + ".vscode/**/*"
3-17: Add rootDir to stabilize emit layoutWithout rootDir, TS infers it (can widen if tests are included later). Set rootDir to src to keep dist predictable.
"compilerOptions": { @@ - "outDir": "dist", + "outDir": "dist", + "rootDir": "src",
4-5: Consider modern module settings aligned with bundlingIf this package is bundled for the browser extension, prefer module ESNext and moduleResolution bundler (or nodenext if consumed by Node). This improves tree-shaking and aligns with TS 5 recommendations.
Option A (bundled):
- "moduleResolution": "node", + "moduleResolution": "bundler", @@ - "module": "commonjs", + "module": "ESNext",Option B (Node consumption):
- "moduleResolution": "node", + "moduleResolution": "nodenext", - "module": "commonjs", + "module": "NodeNext",Confirm your toolchain (webpack/rollup/tsup) and consumers before changing.
Also applies to: 10-11, 16-16
4-4: Libs: DOM may be insufficient for MV3 service workerIf any code runs in a background/service worker, add WebWorker lib; if strictly UI, DOM is fine. Many extensions use both; you can include both.
- "lib": ["ESNext", "dom"], + "lib": ["ESNext", "dom", "WebWorker"],
6-9: Tighten type-safety (enable strict mode)You’ve enabled unused checks; add strict for nullability and inference.
"noUnusedLocals": true, "noUnusedParameters": true, + "strict": true, + "noImplicitReturns": true, "removeComments": true,
3-17: Build performance: consider skipLibCheckLarge monorepos benefit from skipping lib checks to speed CI; safe if libs are trusted.
"esModuleInterop": true, "declaration": true, - "downlevelIteration": true + "downlevelIteration": true, + "skipLibCheck": true.github/workflows/test-all.yml (1)
12-16: Add Corepack enable and proper Yarn 4 cache to speed up CI.
actions/setup-node’s cache: yarn isn’t very effective for Yarn 4 workspaces. Cache .yarn/cache and enable Corepack.- uses: actions/setup-node@v4 with: node-version: "22.18.0" cache: "yarn" + - run: corepack enable + - name: Cache Yarn artifacts + uses: actions/cache@v4 + with: + path: | + .yarn/cache + .yarn/unplugged + .yarn/build-state.yml + .pnp.cjs + key: ${{ runner.os }}-yarn4-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn4- - run: yarn install.github/workflows/test-swap.yml (1)
12-16: Mirror CI improvements: enable Corepack and cache Yarn 4 artifacts.
Same optimization as the main test workflow.- uses: actions/setup-node@v4 with: node-version: "22.18.0" cache: "yarn" + - run: corepack enable + - name: Cache Yarn artifacts + uses: actions/cache@v4 + with: + path: | + .yarn/cache + .yarn/unplugged + .yarn/build-state.yml + .pnp.cjs + key: ${{ runner.os }}-yarn4-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn4- - run: yarn installDockerfile (1)
1-3: Slim the image and enable Corepack in one pass.
Fewer layers, cleaned apt lists, and Corepack enabled for Yarn 4.-FROM node:22.18-bookworm -RUN apt-get update -RUN apt-get install build-essential zip -y +FROM node:22.18-bookworm +RUN apt-get update \ + && apt-get install -y --no-install-recommends build-essential zip \ + && rm -rf /var/lib/apt/lists/* +RUN corepack enablepackages/extension/vite.config.ts (1)
56-60: Avoid locale-dependent build timestamp; use a deterministic source.
toLocaleString()is locale/timezone dependent and can produce different values across CI/dev machines. PreferDate.now()(digits-only) ortoISOString()for reproducible builds.Apply:
- : JSON.stringify(new Date().toLocaleString().replace(/\D/g, '')), + : JSON.stringify(String(Date.now())),packages/name-resolution/tests/sid.test.ts (1)
4-4: Top-level import still executes even when suite is skipped.If
../src/sidis removed, this file will fail to load despitedescribe.skip. Move the import inside the skipped callback or remove the suite.Example:
-import SIDResolver from "../src/sid"; - -describe.skip("SID Name resolving", () => { +describe.skip("SID Name resolving", () => { + // NOTE: kept inside skipped block to avoid resolving when SID is removed + const getResolver = async () => (await import("../src/sid")).default; // ...packages/name-resolution/tests/resolver.test.ts (1)
23-24: Expectation updated to null — also remove stale SID config above to avoid confusion.Now that SID is no longer used, keep the expectation as null, but also drop the
sidblock from the resolver config to reflect actual behavior.Patch:
const resolver = new NameResolver({ ens: { node: "https://nodes.mewapi.io/rpc/eth", }, - sid: { - node: { - bnb: "https://nodes.mewapi.io/rpc/bsc", - arb: "https://nodes.mewapi.io/rpc/arb", - }, - }, });packages/signers/massa/src/crypto/interfaces/hasher.ts (1)
1-4: Clarify input encoding and broaden accepted binary types.Specify expected encoding for
string(UTF‑8 vs hex) and consider acceptingArrayBufferViewto coverDataView/typed arrays uniformly.Example:
-export default interface Hasher { - hash(data: Buffer | Uint8Array | string): Uint8Array -} +/** + * hash() expects: + * - string: UTF-8 by default (document if hex is supported) + * - binary: Uint8Array/ArrayBufferView + */ +export default interface Hasher { + hash(data: Uint8Array | ArrayBufferView | string): Uint8Array; +}packages/signers/kadena/package.json (1)
31-37: Dev toolchain bumped — align engines with repo Node baseline.Repo CI/node baseline is 22.18.0. Consider updating this package’s
engines.nodefrom>=14.15.0to>=18(or>=22) to avoid misleading consumers.Patch:
"engines": { - "node": ">=14.15.0" + "node": ">=18.18.0" },packages/swap/package.json (1)
42-46: Align engines with workspace Node 22.xThe repo CI and .nvmrc use Node 22.18.x. Consider updating
"engines.node"to reduce install-time warnings and mismatches.- "engines": { - "node": ">=14.15.0" - }, + "engines": { + "node": ">=18.18.0" + },Also applies to: 55-56
packages/signers/ethereum/package.json (1)
35-39: Dev tooling bumps look good; consider engines updateChanges are fine. Suggest aligning
"engines.node"with repo standard to avoid drift.- "engines": { - "node": ">=14.15.0" - }, + "engines": { + "node": ">=18.18.0" + },Also applies to: 48-49
packages/signers/bitcoin/package.json (1)
35-39: Dev bumps OK; engines consistencyLooks good. Consider updating
"engines.node"to match repo Node 22 baseline.- "engines": { - "node": ">=14.15.0" - }, + "engines": { + "node": ">=18.18.0" + },Also applies to: 48-49
packages/signers/polkadot/package.json (2)
25-31: Move commitlint to devDependencies
@commitlint/cliis a build-time tool; keeping it in dependencies bloats consumers."dependencies": { - "@commitlint/cli": "^19.8.1", "@enkryptcom/utils": "workspace:^", "@polkadot/util": "^13.5.6", "@polkadot/util-crypto": "^13.5.6", "@polkadot/wasm-crypto": "^7.5.1", "assert": "^2.1.0" }, "devDependencies": { "@enkryptcom/types": "workspace:^", + "@commitlint/cli": "^19.8.1",
33-37: Engines alignmentMatch workspace Node baseline to prevent install-time warnings.
- "engines": { - "node": ">=14.15.0" - }, + "engines": { + "node": ">=18.18.0" + },Also applies to: 47-48
packages/signers/massa/src/crypto/blake3.ts (1)
7-11: Match interface signature explicitlyMinor: keep the method signature identical to
Hasherto avoid future variance issues.-import { blake3 as hashBlake3 } from '@noble/hashes/blake3' -import Hasher from './interfaces/hasher' +import { blake3 as hashBlake3 } from '@noble/hashes/blake3' +import type { Buffer } from 'node:buffer' +import Hasher from './interfaces/hasher' export default class Blake3 implements Hasher { // eslint-disable-next-line class-methods-use-this -- Expected by the interface. - hash(data: Uint8Array | string): Uint8Array { + hash(data: Buffer | Uint8Array | string): Uint8Array { return hashBlake3(data) } }packages/extension/src/types/activity.ts (1)
9-9: Use type-only import to avoid bundling massa-web3Prevents accidental inclusion of runtime code from
@massalabs/massa-web3in the extension bundle.-import { OperationStatus } from '@massalabs/massa-web3'; +import type { OperationStatus } from '@massalabs/massa-web3';packages/extension/src/providers/ethereum/networks/zksepolia.ts (1)
6-6: Rename zkgoerli identifiers to zksepolia for clarity.**Leftover “goerli” naming is misleading in a Sepolia module.
-const zkgoerliOptions: EvmNetworkOptions = { +const zksepoliaOptions: EvmNetworkOptions = { ... -const zkgoerli = new EvmNetwork(zkgoerliOptions); -export default zkgoerli; +const zksepolia = new EvmNetwork(zksepoliaOptions); +export default zksepolia;Also applies to: 21-23
packages/hw-wallets/package.json (1)
21-23: Align Node engine with repository baseline.Repo CI and .nvmrc target Node 22.x; this package still says ">=14.15.0". Suggest raising the floor to avoid accidental use of older Node in isolated builds.
"engines": { - "node": ">=14.15.0" + "node": ">=18.18.0" },Confirm no consumers require Node <18 before raising the floor.
packages/signers/massa/src/crypto/interfaces/sealer.ts (1)
1-7: Make Sealer generic to eliminateanyand improve type safety.You can keep a true interface while avoiding
any:-// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -- True interface. -export default interface Sealer { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - seal(data: Uint8Array): Promise<any> - // eslint-disable-next-line @typescript-eslint/no-explicit-any - unseal(data: any): Promise<Uint8Array> -} +// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -- True interface. +export default interface Sealer<TSealed = unknown> { + seal(data: Uint8Array): Promise<TSealed>; + unseal(data: TSealed): Promise<Uint8Array>; +}This will require adjusting implementers (e.g., PasswordSeal) to specify
TSealed.Please confirm the intended sealed payload shape so I can patch implementations in this PR.
packages/extension/src/providers/massa/libs/activity-handlers/massa.ts (2)
21-23: Use the repo logger instead ofconsole.errorfor consistency.Switch to the shared debug logger (e.g., utils/debug-logger) to honor log levels and formats.
What’s the preferred logger import here (path/name) so I can submit a follow-up diff?
13-20: Optional: de-dup and/or sort activities if upstream may return duplicates.If
ActivityState.getAllActivitiescan produce duplicates or unsorted results, consider normalizing here before returning.packages/extension/configs/vitest.config.mts (1)
20-29:import.meta.resolveusage is Node ≥20; ensure CI runs on Node 22 (it does).Looks good given the repo baseline. If you ever need Node <20 locally, provide a small fallback using
createRequire().resolve.If desired, I can add a backward-compatible resolver shim guarded by Node version.
packages/name-resolution/src/index.ts (1)
23-28: Fully remove SID to reduce bundle size and dead code.SID is no longer initialized or used; keep importing/instantiating it adds weight and potential side effects. Remove the field, import, and commented branches. Also update
NameResolverOptionsto dropsid.-import SIDResolver from "./sid"; ... - sid: SIDResolver; ... - this.sid = new SIDResolver(options.sid); this.initDone = Promise.all([ this.ens.init(), this.rns.init(), this.ud.init(), - // this.sid.init(), ]); ... return this.initDone.then(() => Promise.all([ this.ens.resolveReverseName(address), - // this.sid.resolveReverseName(address), this.rns.resolveReverseName(address), this.ud.resolveReverseName(address), ]).then((results) => { ... return this.initDone.then(() => { if (this.rns.isSupportedName(name)) return this.rns.resolveAddress(name, coin); if (this.ud.isSupportedName(name)) return this.ud.resolveAddress(name, coin); - // if (this.sid.isSupportedName(name)) - // return this.sid.resolveAddress(name, paymentIdChain); if (this.ens.isSupportedName(name)) return this.ens.resolveAddress(name, coin);After removing SID here, I can follow through with a types update and pruning any remaining SID tests/imports.
Also applies to: 31-38, 48-60
packages/extension/src/libs/tokens-state/index.ts (1)
20-29: API/docs mismatch: method now accepts Massa tokens too.JSDoc still says “ERC20” and param type mentions only CustomErc20Token. Update docs to reflect CustomMassaToken support (or rename method to a chain-agnostic name in a follow-up).
- * Add a new custom ERC20 token for a given network. + * Add a new custom token with an address (ERC20/MRC20) for a given network. ... - * @param {CustomErc20Token} token - The token information being added. + * @param {CustomErc20Token | CustomMassaToken} token - The token information being added.packages/extension/src/types/base-network.ts (1)
11-11: Use path alias for consistency.All other provider API imports use '@/…'. Prefer the alias for maintainability.
-import MassaAPI from '../providers/massa/libs/api'; +import MassaAPI from '@/providers/massa/libs/api';packages/signers/massa/src/libs/ed25519.ts (2)
14-23: Seed handling: accept 0x-prefixed hex and validate length.Guard against invalid hex and odd-length strings.
-const getMasterKeyFromSeed = (seed: Hex): Keys => { - const hmac = createHmac("sha512", Buffer.from(ED25519_CURVE, "utf8")); - const I = hmac.update(Buffer.from(seed, "hex")).digest(); +const getMasterKeyFromSeed = (seed: Hex): Keys => { + const seedHex = seed.startsWith("0x") ? seed.slice(2) : seed; + if (!/^[0-9a-fA-F]+$/.test(seedHex) || seedHex.length % 2 !== 0) { + throw new Error("Invalid seed hex"); + } + const I = hmac(sha512, utf8ToBytes(ED25519_CURVE), hexToBytes(seedHex)); const IL = I.subarray(0, 32); const IR = I.subarray(32); return { key: IL, chainCode: IR, }; };
53-69: Optional: enforce apostrophes or document hardened-by-default behavior.You always add HARDENED_OFFSET regardless of apostrophe. Either enforce apostrophes in isValidPath or add a brief comment clarifying that all segments are treated as hardened.
- const segments = path + // All segments are treated as hardened (SLIP-0010) + const segments = path .split("/") .slice(1) .map(replaceDerive) .map((el) => parseInt(el, 10));Add tests for:
- invalid paths (letters, negatives, ≥ 2^31),
- paths without apostrophes treated as hardened,
- seed with 0x prefix.
packages/name-resolution/package.json (1)
21-23: Align engines.node with repo standard.Repo CI/.nvmrc moved to Node 22.18.0. Consider updating
"engines": { "node": ">=22.18.0" }here to prevent accidental installs on older Node.packages/extension/src/providers/massa/types/index.ts (1)
24-31: Consider optional fields for UX tolerance.If
fromAddressNameortoTokenAddresscan be absent in some flows, mark them optional to reduce casting downstream.packages/signers/massa/package.json (2)
21-23: Match Node engines to workspace.Consider
"node": ">=22.18.0"to align with CI and avoid crypto/WebCrypto mismatches on older Node.
1-62: Optional: define package exports.Add an
"exports"map to avoid deep-imports and improve ESM/CJS resolution for consumers.packages/signers/massa/src/crypto/cross-browser.ts (6)
24-45: Avoid require() in ESM; use dynamic import.In TS ESM sources,
requireis undefined at runtime. Useawait import('node:crypto')in the Node path.Apply this diff:
- // eslint-disable-next-line @typescript-eslint/no-var-requires - const crypto = require('crypto') + const crypto = await import('node:crypto')
47-82: Hash name normalization between Node and WebCrypto.Node expects
sha256/sha512; WebCrypto expectsSHA-256/SHA-512. Current code passesopts.hashverbatim and can break cross‑env. Also, prefer Uint8Array over Buffer for browser salt type.Apply this diff:
-async function pbkdf2Browser( - password: string, - salt: Buffer, - opts: PBKDF2Options -): Promise<Uint8Array> { +async function pbkdf2Browser( + password: string, + salt: Uint8Array, + opts: PBKDF2Options +): Promise<Uint8Array> { const { iterations, keyLength, hash } = opts - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const crypto = window.crypto || (window as any).msCrypto - - if (!crypto) throw new Error('Your browser does not expose window.crypto.') + const subtle = globalThis.crypto?.subtle + if (!subtle) throw new Error('WebCrypto subtle API is unavailable.') @@ - const derivedKey = await crypto.subtle.deriveKey( + const derivedKey = await subtle.deriveKey( { name: 'PBKDF2', - salt: salt, + salt, iterations: iterations, - hash: { name: hash }, + hash: { name: normalizeHash(hash).web }, }, keyMaterial, { name: 'AES-GCM', length: keyLength * U8_SIZE_BITS }, false, ['encrypt', 'decrypt'] ) @@ - const exportedKey = await crypto.subtle.exportKey('raw', derivedKey) - const buffer = Buffer.from(exportedKey) - - return buffer + const exportedKey = await subtle.exportKey('raw', derivedKey) + return new Uint8Array(exportedKey)And update typings:
-export type PBKDF2Options = { +export type PBKDF2Hash = 'sha256' | 'sha512' | 'SHA-256' | 'SHA-512' +export type PBKDF2Options = { iterations: number keyLength: number - hash: string + hash: PBKDF2Hash }Add helper:
function normalizeHash(hash: PBKDF2Hash): { node: 'sha256' | 'sha512'; web: 'SHA-256' | 'SHA-512' } { switch (hash) { case 'sha256': case 'SHA-256': return { node: 'sha256', web: 'SHA-256' } case 'sha512': case 'SHA-512': return { node: 'sha512', web: 'SHA-512' } default: return { node: 'sha256', web: 'SHA-256' } // safe default } }Finally, use it in pbkdf2Node:
- crypto.pbkdf2( + crypto.pbkdf2( password, salt, iterations, keyLength, - hash, + normalizeHash(hash).node, (err, derivedKey) => {
99-109: pbkdf2: unify salt type.Change
salt: BuffertoUint8Arrayin the publicpbkdf2signature for consistency with AES helpers and browser environments.Apply this diff:
export async function pbkdf2( password: string, - salt: Buffer, + salt: Uint8Array, opts: PBKDF2Options ): Promise<Uint8Array> {
123-159: Prefer globalThis.crypto; avoid Buffer in browser path.Use
globalThis.crypto.subtleand returnUint8Arrayto avoid relying on Buffer polyfills in browsers.Apply this diff:
- const keyData = await window.crypto.subtle.importKey( + const keyData = await globalThis.crypto!.subtle.importKey( @@ - const encrypted = await window.crypto.subtle.encrypt( + const encrypted = await globalThis.crypto!.subtle.encrypt( @@ - return Buffer.from(encrypted) + return new Uint8Array(encrypted)
176-217: Minor robustness + lint fix in decrypt (Node path).
- Don’t reassign function params (Airbnb’s no‑param‑reassign).
- Validate ciphertext length before slicing auth tag.
Apply this diff:
if (isNode()) { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const crypto = require('crypto') - encryptedData = Buffer.from(encryptedData) + const crypto = await import('node:crypto') + const data = Buffer.from(encryptedData) + if (data.length < AUTH_TAG_SIZE_BYTES) { + throw new Error('encryptedData too short to contain GCM auth tag') + } const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv) decipher.setAuthTag( - encryptedData.slice(encryptedData.length - AUTH_TAG_SIZE_BYTES) + data.slice(data.length - AUTH_TAG_SIZE_BYTES) ) const decrypted = Buffer.concat([ - decipher.update( - encryptedData.slice(0, encryptedData.length - AUTH_TAG_SIZE_BYTES) - ), + decipher.update(data.slice(0, data.length - AUTH_TAG_SIZE_BYTES)), decipher.final(), ]) return decrypted }
14-22: Drop legacy msCrypto mention.IE11 isn’t a target; carrying
msCryptosuggests support we don’t provide. The diffs above switch toglobalThis.crypto.packages/signers/massa/src/crypto/passwordSeal.ts (1)
14-21: Iteration count trade‑off: 600k PBKDF2 iterations may jank the UI600,000 PBKDF2‑SHA256 iterations can cause noticeable latency on lower‑end devices. Consider making this configurable or calibrating at runtime, or switching to a memory‑hard KDF where feasible. Keep default high, but allow tuning.
Would you like a small utility to benchmark KDF latency at startup and pick an iteration count that targets ~250–500 ms on the device?
packages/extension/src/providers/massa/ui/send-transaction/components/send-address-input.vue (6)
127-130: Emit toggle:showContacts on blur as wellCurrently emits only on focus. Emit both true and false to keep parent state in sync.
-const changeFocus = (val: FocusEvent) => { - isFocus.value = val.type === 'focus'; - if (isFocus.value) emit('toggle:showContacts', isFocus.value); -}; +const changeFocus = (val: FocusEvent) => { + const focused = val.type === 'focus'; + isFocus.value = focused; + emit('toggle:showContacts', focused); +};
79-82: Guard against missing network methods
props.network.isValidAddressmay be undefined when network is{}(default). Guard before calling to avoid runtime errors.-const isValidMassaAddress = computed(() => { - if (!massaAddress.value || massaAddress.value.trim() === '') return false; - return props.network.isValidAddress(massaAddress.value); -}); +const isValidMassaAddress = computed(() => { + const v = massaAddress.value?.trim(); + if (!v) return false; + return typeof props.network?.isValidAddress === 'function' + ? !!props.network.isValidAddress(v) + : false; +});
10-16: Avoid renderingwhen placeholder is unavailable
Rendering an
with empty src can trigger unintended requests. Only render the placeholder
when a source exists; otherwise show a styled div.
- <img - v-else - :src="placeholderIdenticonSrc" - alt="" - class="send-address-input__avatar-placeholder" - /> + <img + v-else-if="placeholderIdenticonSrc" + :src="placeholderIdenticonSrc" + alt="" + class="send-address-input__avatar-placeholder" + /> + <div v-else class="send-address-input__avatar-placeholder"></div>Also applies to: 108-115
26-28: Prefer CSS classes over inline colors (theme/dark‑mode friendly)Replace inline color toggling with a class and theme var.
- :style="{ - color: massaAddress && !isValidMassaAddress ? 'red' : 'black', - }" + :class="{ 'is-invalid': massaAddress && !isValidMassaAddress }"Add to styles (inside
.send-address-input__address):.send-address-input { &__address { @@ input { width: 290px; @@ padding: 0; } + input.is-invalid { + color: @error; + } } }Also applies to: 189-201
97-103: Computed with side‑effects (sets state) — move to event handler/watch
identiconSrccomputed mutatesidenticonErrorvia nextTick; avoid side‑effects in computed. Set error only inhandleIdenticonErroror a dedicated watcher.Do you want me to refactor this to a
watch([massaAddress, props.network], ...)that updates acurrentIdenticonSrcref and handles errors without mutating inside a computed?
145-147: Tiny CSS nit: duplicate box-sizing
box-sizing: border-box;is declared twice.- box-sizing: border-box; border: 1px solid @gray02; - box-sizing: border-box;packages/keyring/package.json (1)
38-53: Engines vs. tooling mismatch: ESLint 9 requires Node ≥18.18.
You bumped dev tooling (eslint 9, TS 5.9) but engines still allow Node 14. This can break local scripts/CI for this package.Recommended change if the repo standard is Node 18+:
- "engines": { - "node": ">=14.15.0" - }, + "engines": { + "node": ">=18.18.0" + },packages/extension/src/libs/background/types.ts (1)
20-27: Avoidtypeofon type-only imports (consistency).
If you see TS errors like “typeofreferences a type-only import,” convert theethereum/polkadot/kadenaimports to value imports for consistency with Solana/Massa.Apply if needed:
-import type EthereumProvider from '@/providers/ethereum'; -import type PolkadotProvider from '@/providers/polkadot'; -import type KadenaProvider from '@/providers/kadena'; +import EthereumProvider from '@/providers/ethereum'; +import PolkadotProvider from '@/providers/polkadot'; +import KadenaProvider from '@/providers/kadena';packages/extension/src/ui/action/views/network-activity/index.vue (1)
222-245: Refine Massa status mapping to avoid false failures and keep polling non‑terminal states.
Right now, any status other than explicit success defaults to failed. Safer to mark as failed/dropped only for terminal negatives; keep polling otherwise.Apply:
- } else if (props.network.provider === ProviderName.massa) { - if (!info) return; - const massaInfo = info as MassaRawInfo; - if (isActivityUpdating || massaInfo === OperationStatus.PendingInclusion) - return; - - let status = ActivityStatus.failed; - // if the transaction is not found, and it's been less than 1 minute, wait for it to be processed - if ( - massaInfo === OperationStatus.NotFound && - Date.now() < activity.timestamp + 60_000 - ) { - return; - } else if ( - massaInfo === OperationStatus.Success || - massaInfo === OperationStatus.SpeculativeSuccess - ) { - status = ActivityStatus.success; - } - - activity.status = status; - activity.rawInfo = massaInfo; - updateActivitySync(activity).then(() => updateVisibleActivity(activity)); + } else if (props.network.provider === ProviderName.massa) { + if (!info) return; + const massaInfo = info as MassaRawInfo; + if (isActivityUpdating || massaInfo === OperationStatus.PendingInclusion) return; + + // Give NotFound up to 1 minute to appear + if ( + massaInfo === OperationStatus.NotFound && + Date.now() < activity.timestamp + 60_000 + ) { + return; + } + + let status: ActivityStatus | null = null; + if ( + massaInfo === OperationStatus.Success || + massaInfo === OperationStatus.SpeculativeSuccess + ) { + status = ActivityStatus.success; + } else if (massaInfo === OperationStatus.NotFound) { + status = ActivityStatus.dropped; + } else { + // non-terminal/unknown -> keep polling + return; + } + + activity.status = status; + activity.rawInfo = massaInfo; + updateActivitySync(activity).then(() => updateVisibleActivity(activity)); }packages/extension/src/providers/massa/networks/index.ts (1)
5-8: Type the registry for clarity and IDE help.Annotate the export as a typed Partial<Record<NetworkNames, BaseNetwork>> to make intent explicit and avoid widening.
-import { NetworkNames } from '@enkryptcom/types'; +import { NetworkNames } from '@enkryptcom/types'; +import { BaseNetwork } from '@/types/base-network'; -export default { +const networks: Partial<Record<NetworkNames, BaseNetwork>> = { [NetworkNames.Massa]: mainnet, [NetworkNames.MassaBuildnet]: buildnet, -}; +} as const; + +export default networks;packages/extension/src/providers/massa/methods/index.ts (1)
7-19: Drop the ESLint disable and consume the param; also name the passthrough middleware.Keeps the signature stable for call sites while staying lint-clean and easier to grep.
-export default ( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _provider: BackgroundProviderInterface, -): MiddlewareFunction[] => { - return [ +export default (_provider: BackgroundProviderInterface): MiddlewareFunction[] => { + void _provider; + const massa_passthrough: MiddlewareFunction = async (_req, _res, next) => next(); + return [ massa_getBalance, massa_getNetwork, massa_setNetwork, - async (request, response, next) => { - // Add any additional Massa-specific middleware logic here - return next(); - }, + massa_passthrough, ]; };packages/extension/src/ui/action/App.vue (2)
214-222: Optional: avoid hard-coded buy URL and guard when no selected account.
- Consider moving the Massa buy link into the network config (like others) for consistency.
- The default branch assumes a selected account; add a safe fallback to prevent a runtime NPE when no accounts exist.
case NetworkNames.Massa: - return 'https://www.massa.net/get-mas'; + return 'https://www.massa.net/get-mas'; // consider sourcing from network.options.buyLink default: - return `https://ccswap.myetherwallet.com/?to=${currentNetwork.value.displayAddress( - accountHeaderData.value.selectedAccount!.address, - )}&network=${currentNetwork.value.name}&crypto=${ - currentNetwork.value.currencyName - }&platform=enkrypt`; + if (accountHeaderData.value.selectedAccount) { + return `https://ccswap.myetherwallet.com/?to=${currentNetwork.value.displayAddress( + accountHeaderData.value.selectedAccount.address, + )}&network=${currentNetwork.value.name}&crypto=${currentNetwork.value.currencyName}&platform=enkrypt`; + } + return 'https://ccswap.myetherwallet.com/?platform=enkrypt';
325-337: Fix: ensure selectedAccount is null when no active accounts.Avoids assigning undefined to a field typed as EnkryptAccount | null and prevents downstream non-null assertions from breaking.
-const selectedAddress = await domainState.getSelectedAddress(); - -let selectedAccount = activeAccounts[0]; +const selectedAddress = await domainState.getSelectedAddress(); + +let selectedAccount: EnkryptAccount | null = activeAccounts[0] ?? null; if (selectedAddress) { const found = activeAccounts.find(acc => acc.address === selectedAddress); if (found) selectedAccount = found; } accountHeaderData.value = { activeAccounts, inactiveAccounts, selectedAccount,packages/extension/src/providers/massa/methods/massa_getNetwork.ts (1)
12-19: Tighten control flow; try/catch seems unnecessary.
this.network.nameshould be defined; remove try/catch and return after responding to avoid accidental fallthrough.- if (payload.method !== 'massa_getNetwork') return next(); - else { - try { - res(null, this.network.name); - } catch { - res(null, NetworkNames.Massa); - } - } + if (payload.method !== 'massa_getNetwork') return next(); + res(null, this.network.name ?? NetworkNames.Massa); + return;packages/signers/massa/src/crypto/base58.ts (1)
4-6: Nit: Clarify it's Base58Check, not plain Base58.The implementation uses bs58check. Update the docstring to avoid confusion.
-/** - * Base58 implementation of the Serializer interface. - */ +/** + * Base58Check implementation of the Serializer interface. + */packages/keyring/tests/generate.test.ts (2)
256-279: Optional: Lock keyring after assertions for consistency with other tests.Keeps patterns uniform and avoids residual state in future edits.
expect(pair.address).equals( "AU12XkXdPKYBwuy3NANX44dR3cL7WMobsYcj1fWMv1mxkkHU9qBWB", ); + keyring.lock();
280-305: Optional: Lock keyring after assertions.expect(pair.address).equals( "AU12M9c9LwomXh7sEuK779RnV8QbzFfQRZ1qpKhwWzXPpShhrBfz2", ); + keyring.lock();packages/extension/src/providers/massa/methods/massa_setNetwork.ts (2)
16-33: Make params parsing robust and match case-insensitively.Handle object/array params and case differences; return a clearer error if not found.
- const networkName = payload.params?.[0]; + const raw = payload.params as unknown; + const requested = + Array.isArray(raw) ? raw[0] : (raw as any)?.name ?? raw; + const networkName = + typeof requested === 'string' ? requested : undefined; if (!networkName) { res(getCustomError('Network name is required')); return; } - // Check if the network exists - const availableNetworks = Object.keys(massaNetworks); - - if (!availableNetworks.includes(networkName)) { + const keys = Object.keys(massaNetworks); + const matchedKey = keys.find( + k => k.toLowerCase() === networkName.toLowerCase(), + ); + if (!matchedKey) { res( getCustomError( - `Invalid network name. Available networks: ${availableNetworks.join(', ')}`, + `Invalid network name. Available networks: ${keys.join(', ')}`, ), ); return; } - // Get the network object - const network = massaNetworks[networkName as keyof typeof massaNetworks]; + // Get the network object + const network = massaNetworks[matchedKey as keyof typeof massaNetworks];
39-44: Minor:setRequestProviderexists; no need for guard.You can call it directly; the class always defines it.
- if (this.setRequestProvider) { - this.setRequestProvider(network); - res(null, { success: true, network: network.name }); - } else { - res(getCustomError('Network switching not supported')); - } + this.setRequestProvider(network); + res(null, { success: true, network: network.name });packages/extension/src/ui/action/views/network-assets/index.vue (1)
62-68: Use computedselectedAddressinstead of non‑null assertion.Avoids potential undefined at runtime and keeps template consistent.
- <custom-evm-token v-if="showAddCustomTokens && isEvmNetwork" :address="props.accountInfo.selectedAccount?.address!" + <custom-evm-token v-if="showAddCustomTokens && isEvmNetwork" :address="selectedAddress" :network="(props.network as EvmNetwork)" @update:token-added="addCustomAsset" @update:close="toggleShowAddCustomTokens"></custom-evm-token> @@ - <custom-massa-token v-if="showAddCustomTokens && isMassaNetwork" :address="props.accountInfo.selectedAccount?.address!" + <custom-massa-token v-if="showAddCustomTokens && isMassaNetwork" :address="selectedAddress" :network="props.network" @update:token-added="addCustomAsset" @update:close="toggleShowAddCustomTokens"></custom-massa-token>packages/extension/src/providers/massa/ui/send-transaction/components/send-token-select.vue (4)
2-2: Add keyboard accessibility to the clickable rowAnchor without href is not keyboard-activatable. Add role and key handlers (or switch to a button) to meet a11y basics.
- <a class="send-token-select" @click="emit('update:toggleTokenSelect')"> + <a + class="send-token-select" + role="button" + tabindex="0" + @click="emit('update:toggleTokenSelect')" + @keydown.enter.prevent="emit('update:toggleTokenSelect')" + @keydown.space.prevent="emit('update:toggleTokenSelect')" + >
4-11: Harden null/undefined fields; improve alt texttoken.icon/name/symbol can be undefined (Partial). Avoid broken UI and improve accessibility.
- <img :src="token.icon" alt="" /> + <img :src="token.icon" :alt="token.symbol || token.name || 'token'" />- <h5>{{ token.name }}</h5> + <h5>{{ token.name || 'Unknown' }}</h5>- <span>{{ token.symbol }}</span> + <span>{{ token.symbol || '' }}</span>
53-56: Duplicate CSS declarationbox-sizing is set twice. Remove the duplicate.
box-sizing: border-box; - border: 1px solid @gray02; - box-sizing: border-box; + border: 1px solid @gray02;
84-93: Avoid fixed widths for text blocksFixed 290px widths will truncate in narrow layouts and hurt i18n. Let flex handle it.
- width: 290px; + width: auto; + max-width: 100%;- width: 290px; + width: auto; + max-width: 100%;Also applies to: 94-107
packages/extension/src/providers/massa/methods/massa_getBalance.ts (1)
16-21: Validate type/whitespace and use JSON‑RPC error code -32602 for invalid paramsCurrent check allows non-string inputs and returns a non-standard error code. Trim input and return -32602 for invalid params.
- const address = payload.params?.[0]; + const raw = payload.params?.[0]; const network = this.network as MassaNetwork; - if (!address || !network.isValidAddress(address)) { - res(getCustomError('Please enter a valid Massa address')); + if (typeof raw !== 'string') { + res(getCustomError('Please enter a valid Massa address', -32602)); + return; + } + const address = raw.trim(); + if (!address || !network.isValidAddress(address)) { + res(getCustomError('Please enter a valid Massa address', -32602)); return; }packages/signers/massa/src/crypto/varintVersioner.ts (1)
17-20: Avoid spread to construct Uint8ArraySpreading allocates intermediate arrays. Use pre-sized buffer for a tiny perf win.
- const versionArray = varint.encode(version); - return new Uint8Array([...versionArray, ...data]); + const versionArray = varint.encode(version); + const out = new Uint8Array(versionArray.length + data.length); + out.set(versionArray, 0); + out.set(data, versionArray.length); + return out;packages/signers/massa/tests/sign.test.ts (2)
14-15: Make hex prefix explicit for robustnessPass true to bufferToHex to guarantee 0x prefix regardless of defaults.
- const msgHash = bufferToHex(blake2AsU8a(msg)); + const msgHash = bufferToHex(blake2AsU8a(msg), true);
18-26: Consider full BIP44 pathOptional: include the address index (e.g., .../0/0) for clarity and future wallet interoperability.
If you decide to change it, update both occurrences:
- const keypair = await signer.generate(MNEMONIC, "m/44'/632'/0'/0'"); + const keypair = await signer.generate(MNEMONIC, "m/44'/632'/0'/0/0");Verify no consumers assume the shorter path.
packages/extension/src/providers/massa/libs/api.ts (1)
27-31: Optional: normalize empty responses in getBalanceIf provider returns undefined or malformed entry, ensure a string is always returned.
- if (!account) return '0'; - return account.balance.toString(); + if (!account || account.balance == null) return '0'; + return String(account.balance);packages/extension/src/providers/massa/ui/send-transaction/index.vue (3)
549-549: Use numeric price, not formatted pricef, in payload.
toToken.priceshould be the raw numeric-string (value), not the formatted display string (valuef).- price: selectedAsset.value.valuef || '0', + price: selectedAsset.value.value || '0',
567-571: Avoid Node Buffer in browser UI for base64.
Buffermay be undefined in the web extension build. Use Web APIs or import a polyfill.Minimal swap:
- txData: Buffer.from(JSON.stringify(txVerifyInfo), 'utf8').toString( - 'base64', - ), + txData: btoa(unescape(encodeURIComponent(JSON.stringify(txVerifyInfo)))),Alternatively import a polyfill once:
import { Buffer } from 'buffer'and keep current code. Please confirm which approach your build supports.
310-351: Enforce minimal fee at runtime (not just via input min).Input
minisn’t sufficient. Guard in validation to prevent sending below network minimal fee.const amountBN = new BigNumber(sendAmount.value); if (amountBN.lte(0)) { return false; } - return hasEnoughBalance.value; + // Fee guard + const feeBN = new BigNumber(fee.value || '0'); + const minFeeBN = new BigNumber(minimalFee.value || '0'); + if (feeBN.lt(minFeeBN)) return false; + return hasEnoughBalance.value;packages/extension/src/providers/massa/index.ts (1)
78-82: Don’t JSON.stringify message string in error.
JSON.stringify(e.message)produces quoted JSON strings. Return a plain string.- return { - error: JSON.stringify(e.message), - }; + return { error: String(e?.message ?? e) };packages/signers/massa/src/index.ts (1)
39-51: Await verify to properly catch async errors.Keeps try/catch effective if the inner verify throws.
- return massaPublicKey.verify(hexToBuffer(msgHash), signature); + return await massaPublicKey.verify(hexToBuffer(msgHash), signature);packages/extension/src/ui/action/views/network-assets/components/custom-massa-token.vue (1)
171-178: Token type consistency check.You’re setting
type: TokenType.ERC20for an MRC‑20 token. If a Massa‑specific enum exists, use it; otherwise confirmaddErc20Tokenexpects this value for Massa.packages/extension/src/providers/massa/ui/libs/signer.ts (4)
80-86: Avoid non‑null assertion on chainId; fail fast with clear error
chainId!will crash obscurely if misconfigured. Add an explicit guard.Apply this diff:
- const chainId = (network as MassaNetworkOptions).chainId; - const publicKey = PublicKey.fromString(account.publicKey); - const serialized = OperationManager.canonicalize( - chainId!, - operationDetails, - publicKey, - ); + const chainId = (network as MassaNetworkOptions).chainId; + if (chainId === undefined || chainId === null) { + throw new Error('Missing Massa chainId in network config'); + } + const publicKey = PublicKey.fromString(account.publicKey); + const serialized = OperationManager.canonicalize(chainId, operationDetails, publicKey);
87-92: Nit: rename local variable for clarityMinor readability tweak.
- const bufferTosign = bufferToHex(serialized); + const bufferToSign = bufferToHex(serialized); return sendUsingInternalMessengers({ method: InternalMethods.sign, - params: [bufferTosign, account], + params: [bufferToSign, account],
115-121: Broaden message payload type; don’t assume Node Buffer in browser buildsBrowser builds may lack global Buffer. Accept
Uint8Array | stringand handle accordingly.Apply this diff:
-export const MassaMessageSigner = (options: { +export const MassaMessageSigner = (options: { account: EnkryptAccount; network: BaseNetwork; - payload: Buffer; + payload: Uint8Array | Buffer | string; }): Promise<InternalOnMessageResponse> => { const { account, payload } = options; - const msgHash = bufferToHex(payload); + const msgHash = + typeof payload === 'string' ? payload : bufferToHex(payload as Uint8Array);Also applies to: 122-126
89-109: Consistent error surface from internal signer
.then(res => res.error ? Promise.reject(res) : …)mixes shapes. Consider throwingnew Error(message)to unify catch handling at call sites.packages/extension/src/providers/massa/ui/send-transaction/verify-transaction/index.vue (4)
151-153: isWindowPopup is set from hardware flag; likely wrong source
isWindowPopupshould reflect window context, not hardware. Use computedisPopup.Apply this diff:
- isWindowPopup.value = account.value.isHardware; + isWindowPopup.value = isPopup;
15-21: Show error paragraph only when there’s an errorAvoid empty red line in UI.
Apply this diff:
- <p - class="verify-transaction__description" - style="color: red" - :class="{ popup: isPopup }" - > + <p + v-if="errorMsg" + class="verify-transaction__description" + style="color: red" + :class="{ popup: isPopup }" + > {{ errorMsg }} </p>
137-139: Ensure Buffer polyfill/import in the SFC
Buffer.from(...)requires a polyfill/import in many bundlers. Import explicitly.Apply this diff:
<script setup lang="ts"> +import { Buffer } from 'buffer';Also applies to: 88-111
278-283: Don’t auto-hide Operation ID; let the user dismissAuto‑hiding after 4s can frustrate copying/sharing.
Apply this diff to remove the timeout:
- setTimeout(() => { - showOperationId.value = false; - isProcessing.value = false; - callToggleRate(); - router.push({ name: 'activity', params: { id: network.value.name } }); - }, 4000); + isProcessing.value = false;Optionally navigate only after Close.
packages/extension/src/providers/massa/networks/massa-base.ts (2)
40-105: Price/sparkline fetch is best‑effort; add minimal telemetry when it failsThe
catch {}fully hides failures, complicating support. Log once at debug level.Apply this minimal diff:
- } catch { - // Price data not available, continue with default values - } + } catch (e) { + // Price data not available, continue with default values + console.debug('Massa price data unavailable:', e); + }
22-35: Constructor assumes chainId; validate inputsAdd a sanity check to fail fast if misconfigured networks slip in.
Apply this diff:
constructor(options: MassaNetworkOptions) { super(options); - this.chainId = options.chainId!; + if (options.chainId === undefined || options.chainId === null) { + throw new Error('MassaNetwork: chainId is required'); + } + this.chainId = options.chainId as unknown as bigint;packages/signers/massa/src/libs/address.ts (1)
162-165: Consistently varint‑encode the type byteFunctionally equivalent for 0/1, but encoding via varint mirrors
fromStringand avoids footguns if types expand.Apply this diff:
- address.bytes = Uint8Array.from([ - AddressType.EOA, - ...address.versioner.attach(Version.V0, rawBytes), - ]); + address.bytes = Uint8Array.from([ + ...varint.encode(AddressType.EOA), + ...address.versioner.attach(Version.V0, rawBytes), + ]);
Summary by CodeRabbit