-
Notifications
You must be signed in to change notification settings - Fork 232
feat: export private keys #751
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
feat: export private key
WalkthroughRepository-wide updates include Node.js/runtime version bumps, dependency upgrades, and multiple network registry changes (additions/removals/renames). A new private-key export flow is added end-to-end (UI → background → keyring). Name-resolution disables SID paths. Tests/settings updated accordingly. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant UI as Accounts View / Export Modal
participant BG as Background Internal Handler
participant KR as KeyRingBase / KeyRing
participant Store as Key Storage / Signer
rect rgb(240,248,255)
note over User,UI: Export private key flow (new)
User->>UI: Click "Export" on account
UI->>UI: Prompt for extension password
UI->>BG: InternalMethods.getPrivateKey([account, password])
BG->>KR: getPrivateKey(options, password)
KR->>Store: Retrieve/derive private key
Store-->>KR: privateKey (string)
KR-->>BG: privateKey
BG-->>UI: { result: JSON.stringify(privateKey) }
UI->>UI: Show private key in modal
end
rect rgb(255,240,240)
alt Error
Store-->>KR: throw Error
KR-->>BG: Error
BG-->>UI: { error }
UI->>User: Display error message
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60–90 minutes Possibly related PRs
Pre-merge checks (3 passed)✅ Passed checks (3 passed)
Pre-merge checks and finishing touches✅ Passed checks (3 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/signers/polkadot/src/index.ts (2)
115-146: Async verify is not awaited — assertions always pass.
assert(this.verify(...))passes a Promise (truthy), so failures won’t be caught. This affects ECDSA, Ed25519, and Sr25519 branches.Apply:
- assert( - this.verify( - bufferToHex(msgHashBuffer), - bufferToHex(sig), - bufferToHex(pair.publicKey), - ), - Errors.SigningErrors.UnableToVerify, - ); + { + const ok = await this.verify( + bufferToHex(msgHashBuffer), + bufferToHex(sig), + bufferToHex(pair.publicKey), + ); + assert(ok, Errors.SigningErrors.UnableToVerify); + } ... - assert( - this.verify( - bufferToHex(msgHashBuffer), - bufferToHex(sig), - bufferToHex(pair.publicKey), - ), - Errors.SigningErrors.UnableToVerify, - ); + { + const ok = await this.verify( + bufferToHex(msgHashBuffer), + bufferToHex(sig), + bufferToHex(pair.publicKey), + ); + assert(ok, Errors.SigningErrors.UnableToVerify); + } ... - assert( - this.verify( - bufferToHex(msgHashBuffer), - bufferToHex(sig), - bufferToHex(pair.publicKey), - ), - Errors.SigningErrors.UnableToVerify, - ); + { + const ok = await this.verify( + bufferToHex(msgHashBuffer), + bufferToHex(sig), + bufferToHex(pair.publicKey), + ); + assert(ok, Errors.SigningErrors.UnableToVerify); + }Add regression tests that flip a bit in the signature and expect an error.
49-55: Remove unsupportedroundsargument from mnemonicToMiniSecret call
mnemonicToMiniSecret only accepts (mnemonic, password?, wordlist?, onlyJs?), so the fifth parameter (2048) in packages/signers/polkadot/src/index.ts lines 49–55 is invalid and should be removed.
🧹 Nitpick comments (28)
packages/name-resolution/tests/resolver.test.ts (1)
23-24: SID path disabled → assert null; clean up the commented expectationGood change. Drop the commented line and use a null-specific assertion for clarity.
- // expect(address).to.be.eq("0xb5932a6B7d50A966AEC6C74C97385412Fb497540"); - expect(address).to.be.eq(null); + expect(address).to.be.null;packages/name-resolution/tests/sid.test.ts (1)
4-4: Make the skip explicit in the suite title (tracks intent in CI output)This keeps intent visible without hunting PR context.
-describe.skip("SID Name resolving", () => { +describe.skip("[temporarily disabled] SID Name resolving", () => {packages/name-resolution/src/index.ts (3)
27-28: Feature-flag SID initialization instead of commenting it outKeeps the path dormant by default but easy to re-enable without code churn.
- // this.sid.init(), + // Feature-flag SID init; disabled by default. + (process.env.ENABLE_SID === "true" ? this.sid.init() : Promise.resolve()),
35-35: Gate SID reverse resolution behind a flag (no-op by default)Avoids long-term commented code while preserving easy toggling.
- // this.sid.resolveReverseName(address), + // Feature-flag SID reverse resolution. + ...(process.env.ENABLE_SID === "true" ? [this.sid.resolveReverseName(address)] : []),
58-60: Keep the disabled SID path self-consistent and flag-gatedUpdates the stale variable name in comments and shows how to re-enable safely.
- // if (this.sid.isSupportedName(name)) - // return this.sid.resolveAddress(name, paymentIdChain); + // Feature-flag SID address resolution. + // if (this.sid.isSupportedName(name) && process.env.ENABLE_SID === "true") + // return this.sid.resolveAddress(name, _paymentIdChain);packages/extension/src/providers/ethereum/libs/assets-handlers/assetinfo-mew.ts (1)
145-149: Drop empty tbName for consistencyOther
bsEndpointentries omittbName. Suggest removing it instead of setting to an empty string.Apply:
[NetworkNames.Sanko]: { - tbName: '', cgPlatform: CoingeckoPlatform.Sanko, bsEndpoint: true, },package.json (1)
37-38: Unify dev tool versions across workspace
- @types/node is pinned to ^22.18.1 in most packages but ^24.3.1 in hw-wallets and all signers—align all to ^24.3.1
- typescript is specified as ~5.9.2 in packages/extension and ^5.9.2 elsewhere—choose one semver spec (recommend ^5.9.2)
Run a workspace-wide dedupe (e.g.pnpm dedupeoryarn dedupe) to collapse duplicates.Dockerfile (1)
1-1: Pin base image digest and enable Corepack for Yarn 4 determinism.
Current tag-only pin risks silent base image drift; also ensure Yarn 4 is activated explicitly.Apply:
-FROM node:22.18-bookworm -RUN apt-get update -RUN apt-get install build-essential zip -y +FROM node:22.18-bookworm@sha256:<digest-here> +RUN apt-get update && \ + apt-get install -y --no-install-recommends build-essential zip && \ + rm -rf /var/lib/apt/lists/* ENV HOME /home ENV NODE_OPTIONS --max-old-space-size=8192 -RUN node -v && npm -v && yarn -v +RUN corepack enable && corepack prepare yarn@4.5.1 --activate && node -v && npm -v && yarn -v WORKDIR /home(Replace with the current digest.)
.github/workflows/test-all.yml (1)
12-12: Avoid Node version drift by sourcing from .nvmrc.
Keeps CI and local dev in lockstep as versions change.- node-version: "22.18.0" + node-version-file: ".nvmrc" cache: "yarn".github/workflows/test-swap.yml (1)
12-12: Use .nvmrc in CI for consistency.
Same rationale as the main workflow.- node-version: "22.18.0" + node-version-file: ".nvmrc" cache: "yarn"packages/utils/package.json (1)
27-27: Confirm @PolkaDot dependency alignment
All @polkadot/util and @polkadot/util-crypto versions are ^13.5.6 and @polkadot/wasm-crypto is ^7.5.1 across every package.json—no version skew detected. Optional: bumpengines.nodeto ≥18 (or ≥22) in the workspace root to match CI/runtime.packages/extension-bridge/package.json (1)
47-53: Align @types/node versions across all packages
Detected both^22.18.1and^24.3.1in workspace packages—standardize on a single version to avoid type-surface drift.packages/swap/package.json (1)
27-29: Solana deps consistent
Bothpackages/extension/package.jsonandpackages/swap/package.jsonuse@solana/web3.js@^1.98.4and@solana/spl-token@^0.4.14, with no version mismatches detected. Optional: hoist these entries to the rootpackage.json(or a shared config) for easier maintenance.packages/signers/ethereum/package.json (1)
35-38: @types/node version mismatch: use ^22.18.1 or verify no Node-24-specific types
packages/signers/ethereum/package.json uses ^24.3.1, while .nvmrc pins v22.18.0 and most packages use ^22.18.1. Either downgrade or confirm compatibility.packages/name-resolution/package.json (1)
25-28: Deps upgrade OK; fix repository URL placeholder (nit)
Library bumps look good; no code-level imports of “viem” detected, so the bump is safe. Update therepositoryfield to match other packages:- "repository": { - "type": "git", - "url": "git+https://github.com/<FILL_IT>" - }, + "repository": { + "type": "git", + "url": "git+https://github.com/enkryptcom/enKrypt/tree/main/packages/name-resolution" + },packages/keyring/src/index.ts (4)
318-340: Add session guard and timer reset for parity with other sensitive ops.Other secrets-touching methods assert unlocked and/or reset the auto-lock timer. For consistency and UX (prevent mid-flow re-lock), at least reset the timer when already unlocked.
Apply:
async getPrivateKey( options: SignOptions, keyringPassword: string, ): Promise<string> { + // Keep UX consistent with sign/encrypt flows + if (!this.#isLocked && this.autoLockTime !== 0) { + this.#resetTimeout(); + }
318-340: Ensure the requested account actually exists before exporting its key.Currently, mnemonic branch derives any path; and privkey branch trusts presence in ENCRYPTED_PRIVKEYS. Add a check against stored accounts to avoid exporting keys for non-existent records or mismatched signer/basePath combinations.
): Promise<string> { + const exists = (await this.getKeysArray()).some( + (a) => + a.basePath === options.basePath && + a.pathIndex === options.pathIndex && + a.signerType === options.signerType && + a.walletType === options.walletType && + !a.isHardware + ); + assert(exists, Errors.KeyringErrors.AddressDoesntExists);
328-332: Minor: reuse in-memory privkeys when unlocked to avoid extra decrypt.If already unlocked, prefer this.#privkeys to reduce storage I/O and crypto ops. Fallback to storage when locked.
- if (options.walletType === WalletType.privkey) { - const privkeys = await this.#getPrivateKeys(keyringPassword); - assert(privkeys[options.pathIndex.toString()], Errors.KeyringErrors.AddressDoesntExists); - return privkeys[options.pathIndex.toString()]; + if (options.walletType === WalletType.privkey) { + const map = !this.#isLocked && this.#privkeys + ? this.#privkeys + : await this.#getPrivateKeys(keyringPassword); + const key = map[options.pathIndex.toString()]; + assert(key, Errors.KeyringErrors.AddressDoesntExists); + return key;
333-339: Style: terminate statement; keep codebase conventions consistent.Add a semicolon after the await call.
- const mnemonic = await this.#getMnemonic(keyringPassword) + const mnemonic = await this.#getMnemonic(keyringPassword);packages/types/src/networks.ts (1)
89-90: Guard rename of CotiDevnet → CotiTestnet
Renaming this enum breaks persisted settings—add a migration to remap stored"CotiDevnet"→"CotiTestnet"on load.packages/extension/src/providers/ethereum/networks/zksepolia.ts (1)
6-23: Rename vars from zkgoerli → zksepolia for clarityNames still say “goerli”; rename to avoid confusion.
-const zkgoerliOptions: EvmNetworkOptions = { +const zksepoliaOptions: EvmNetworkOptions = { name: NetworkNames.zkSyncSepolia, name_long: 'zkSync Sepolia', @@ -const zkgoerli = new EvmNetwork(zkgoerliOptions); -export default zkgoerli; +const zksepolia = new EvmNetwork(zksepoliaOptions); +export default zksepolia;packages/extension/src/libs/keyring/keyring.ts (1)
100-102: Wrapper method: LGTM; consider documenting return formatIf callers expect 0x-prefixed hex, document/normalize here to avoid UI edge cases.
packages/extension/src/ui/action/views/accounts/components/accounts-list-item.vue (1)
82-83: Accessibility nit: add aria-label to the More buttonSmall a11y win for keyboard/screen readers.
- <div + <div v-if="showEdit" ref="toggle" class="accounts-item__more" + aria-label="Account actions menu" @click.stop="toggleEdit" >packages/extension/src/ui/action/views/accounts/components/accounts-list-item-menu.vue (1)
13-19: Make Export action accessible (keyboard/semantics).Use button semantics so keyboard users can trigger Export. Minimal change: add role and key handlers.
- <a + <a v-if="exportable" class="accounts-item__edit-item" - @click.stop="$emit('action:export')" + role="button" + tabindex="0" + @click.stop="$emit('action:export')" + @keyup.enter.stop="$emit('action:export')" + @keyup.space.stop="$emit('action:export')" >packages/extension/src/providers/ethereum/networks/index.ts (1)
58-59: Align naming: usecotiTestnet(noNodesuffix) for consistency.Most keys are network names without the
Nodesuffix. Consider renaming for consistency.-import cotiTestnetNode from './coti-testnet'; +import cotiTestnet from './coti-testnet'; @@ - cotiTestnetNode: cotiTestnetNode, + cotiTestnet: cotiTestnet,Also applies to: 144-145
packages/extension/src/ui/action/views/accounts/index.vue (1)
14-31: Prefer stable keys.Using
indexas:keycan cause rendering issues on mutations. If feasible, key byaccount.address.- :key="index" + :key="account.address"packages/extension/src/ui/action/views/accounts/components/export-account-form.vue (2)
18-27: Show actionable error text and reset errors before retry.Display a generic but accurate message and clear previous errors when the user retries.
- <p v-show="keyringError" class="export-account-form__error"> - Invalid Keyring password - </p> + <p v-show="keyringError" class="export-account-form__error"> + {{ errorText || 'Invalid password or export not allowed for this account.' }} + </p>
30-36: Optional: add copy-to-clipboard with auto-hide.For usability and safety, consider a “Copy” button and auto-hide the key after a short interval (e.g., 30s). I can provide a small patch if desired.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (53)
.github/workflows/test-all.yml(1 hunks).github/workflows/test-swap.yml(1 hunks).nvmrc(1 hunks)Dockerfile(1 hunks)package.json(1 hunks)packages/extension-bridge/package.json(2 hunks)packages/extension/configs/vitest.config.mts(1 hunks)packages/extension/package.json(6 hunks)packages/extension/src/libs/background/index.ts(2 hunks)packages/extension/src/libs/background/internal/get-private-key.ts(1 hunks)packages/extension/src/libs/background/internal/index.ts(2 hunks)packages/extension/src/libs/keyring/keyring.ts(1 hunks)packages/extension/src/providers/common/libs/new-features.ts(1 hunks)packages/extension/src/providers/ethereum/libs/activity-handlers/providers/etherscan/configs.ts(1 hunks)packages/extension/src/providers/ethereum/libs/assets-handlers/assetinfo-mew.ts(1 hunks)packages/extension/src/providers/ethereum/networks/cagaAnkara.ts(0 hunks)packages/extension/src/providers/ethereum/networks/coti-devnet.ts(0 hunks)packages/extension/src/providers/ethereum/networks/coti-testnet.ts(1 hunks)packages/extension/src/providers/ethereum/networks/form-testnet.ts(0 hunks)packages/extension/src/providers/ethereum/networks/form.ts(0 hunks)packages/extension/src/providers/ethereum/networks/index.ts(6 hunks)packages/extension/src/providers/ethereum/networks/skale/chaos.ts(0 hunks)packages/extension/src/providers/ethereum/networks/skale/index.ts(0 hunks)packages/extension/src/providers/ethereum/networks/tac.ts(1 hunks)packages/extension/src/providers/ethereum/networks/zkgoerli.ts(0 hunks)packages/extension/src/providers/ethereum/networks/zksepolia.ts(1 hunks)packages/extension/src/providers/polkadot/networks/index.ts(0 hunks)packages/extension/src/providers/polkadot/networks/unique/opal.ts(0 hunks)packages/extension/src/types/messenger.ts(1 hunks)packages/extension/src/ui/action/views/accounts/components/accounts-list-item-menu.vue(2 hunks)packages/extension/src/ui/action/views/accounts/components/accounts-list-item.vue(2 hunks)packages/extension/src/ui/action/views/accounts/components/export-account-form.vue(1 hunks)packages/extension/src/ui/action/views/accounts/index.vue(6 hunks)packages/hw-wallets/package.json(3 hunks)packages/keyring/package.json(2 hunks)packages/keyring/src/index.ts(1 hunks)packages/name-resolution/package.json(3 hunks)packages/name-resolution/src/index.ts(2 hunks)packages/name-resolution/tests/resolver.test.ts(1 hunks)packages/name-resolution/tests/sid.test.ts(1 hunks)packages/request/package.json(2 hunks)packages/signers/bitcoin/package.json(2 hunks)packages/signers/ethereum/package.json(2 hunks)packages/signers/kadena/package.json(2 hunks)packages/signers/polkadot/package.json(2 hunks)packages/signers/polkadot/src/index.ts(1 hunks)packages/storage/package.json(2 hunks)packages/swap/package.json(2 hunks)packages/swap/src/common/estimateGasList.ts(0 hunks)packages/swap/src/index.ts(1 hunks)packages/types/package.json(2 hunks)packages/types/src/networks.ts(3 hunks)packages/utils/package.json(2 hunks)
💤 Files with no reviewable changes (10)
- packages/extension/src/providers/ethereum/networks/cagaAnkara.ts
- packages/extension/src/providers/polkadot/networks/index.ts
- packages/extension/src/providers/ethereum/networks/form.ts
- packages/extension/src/providers/ethereum/networks/skale/chaos.ts
- packages/swap/src/common/estimateGasList.ts
- packages/extension/src/providers/ethereum/networks/form-testnet.ts
- packages/extension/src/providers/ethereum/networks/coti-devnet.ts
- packages/extension/src/providers/ethereum/networks/zkgoerli.ts
- packages/extension/src/providers/ethereum/networks/skale/index.ts
- packages/extension/src/providers/polkadot/networks/unique/opal.ts
🧰 Additional context used
🧬 Code graph analysis (6)
packages/extension/src/libs/background/internal/get-private-key.ts (5)
packages/extension/src/libs/background/internal/index.ts (1)
getPrivateKey(19-19)packages/extension/src/libs/keyring/keyring.ts (2)
getPrivateKey(100-102)KeyRingBase(14-103)packages/keyring/src/index.ts (2)
getPrivateKey(318-340)password(111-131)packages/extension/src/types/messenger.ts (1)
InternalOnMessageResponse(59-62)packages/extension/src/libs/error/index.ts (1)
getCustomError(27-33)
packages/extension/src/providers/ethereum/networks/tac.ts (2)
packages/extension/src/providers/ethereum/types/evm-network.ts (2)
EvmNetworkOptions(22-53)EvmNetwork(55-286)packages/extension/src/providers/ethereum/libs/activity-handlers/index.ts (1)
EtherscanActivity(10-10)
packages/extension/src/providers/ethereum/networks/zksepolia.ts (1)
packages/extension/src/providers/ethereum/types/evm-network.ts (2)
EvmNetworkOptions(22-53)EvmNetwork(55-286)
packages/extension/src/providers/ethereum/networks/coti-testnet.ts (1)
packages/extension/src/providers/ethereum/types/evm-network.ts (2)
EvmNetworkOptions(22-53)EvmNetwork(55-286)
packages/keyring/src/index.ts (1)
packages/keyring/src/utils.ts (1)
pathParser(3-18)
packages/extension/src/libs/background/index.ts (3)
packages/extension/src/libs/background/internal/index.ts (1)
getPrivateKey(19-19)packages/extension/src/libs/keyring/keyring.ts (1)
getPrivateKey(100-102)packages/keyring/src/index.ts (1)
getPrivateKey(318-340)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: buildAll
- GitHub Check: test
- GitHub Check: test
🔇 Additional comments (30)
packages/name-resolution/src/index.ts (2)
51-51: Param rename to underscore-prefixed is finePrevents unused param warnings without breaking the public signature.
23-28: Verify no callers rely on SID or the thirdresolveAddressparameter
Automated grep failed – please manually confirm no remaining code depends on SID resolution or a 3-arg call toresolveAddressby checking:
- calls to
resolver.resolveAddresspassing three parameters- any direct references to
SIDResolver- resolution of
.arbor.bnbdomainspackages/extension/src/providers/ethereum/libs/assets-handlers/assetinfo-mew.ts (2)
145-149: Sanko routed to Blockscout — LGTMSwitching Sanko to
bsEndpoint: truelooks correct and consistent with other Blockscout-backed networks.
145-149: Blockscout support and native-token injection verified for Sanko
TheNetworkEndpointsconfig includes[NetworkNames.Sanko]: 'https://explorer.sanko.xyz/', andgetBlockscoutBalancesalways prepends a{ token: NATIVE_TOKEN_ADDRESS, … }entry before merging ERC-20 balances, so the native coin is correctly handled onbsEndpoint..nvmrc (1)
1-1: Node version pin aligned — looks good.
Matches Docker and CI updates to 22.18.0.packages/request/package.json (1)
21-23: ```shell
#!/bin/bashPrint root package.json engines section if present
echo "==== root package.json ===="
if grep -q '"engines"' package.json; then
sed -n '1,200p' package.json | rg -nP '"engines"\s*:\s*{[^}]*' -C2
else
echo "No engines field in root package.json"
fiLocate and print packages/request/package.json snippet around engines
REQUEST_PKG=$(find . -type f -path '*packages/request/package.json' | head -n1)
echo
echo "==== packages/request/package.json ===="
if [ -n "$REQUEST_PKG" ]; then
sed -n '1,200p' "$REQUEST_PKG"
else
echo "packages/request/package.json not found"
fiList all engines.node entries across all package.json files
echo
echo "==== All engines.node entries ===="
find . -type f -name package.json -exec grep -Hn '"engines"' -C1 {} ; | grep -B1 '"node"\s*:' || echo "No other engines.node entries found"</blockquote></details> <details> <summary>packages/swap/src/index.ts (1)</summary><blockquote> `270-271`: **No verbatimModuleSyntax usage; explicit type-only re-export is optional** Search across tsconfig*.json shows no instances of `verbatimModuleSyntax: true`, so no change is needed now. Convert to `export type { ToTokenType }` only if that flag is enabled in the future. </blockquote></details> <details> <summary>packages/storage/package.json (1)</summary><blockquote> `30-33`: **Tooling bumps look good** No runtime impact; matches workspace upgrades. Also applies to: 43-44 </blockquote></details> <details> <summary>packages/signers/bitcoin/package.json (1)</summary><blockquote> `35-38`: **Resolved:** No Node built-in imports detected in packages/signers/bitcoin; keeping @types/node at ^24.3.1 is safe. </blockquote></details> <details> <summary>packages/signers/polkadot/package.json (2)</summary><blockquote> `34-37`: **Verify Node.js toolchain alignment across monorepo** No Node.js engine/version detected in CI configs, .nvmrc, or Dockerfiles; ensure a Node.js version compatible with @types/node@24.x is used to prevent type lib/runtime mismatches. --- `27-29`: **Polkadot dependencies aligned across all packages.** Verified @polkadot/util and @polkadot/util-crypto are pinned to ^13.5.6, and @polkadot/wasm-crypto to ^7.5.1 in every package.json; no mismatches found. </blockquote></details> <details> <summary>packages/signers/kadena/package.json (1)</summary><blockquote> `31-31`: **DevDependency bumps look good.** No functional/runtime impact expected for the signer; safe to merge. Also applies to: 33-37, 46-47 </blockquote></details> <details> <summary>packages/keyring/package.json (2)</summary><blockquote> `32-32`: **@polkadot/util patch bump LGTM.** Aligned with other packages; no breaking API changes expected. --- `37-41`: **Confirm Node engine and @types/node parity across packages** - All packages specify `"engines.node": ">=14.15.0"`, but devDependencies list mixed `@types/node` majors (^22.18.1 vs ^24.3.1). - Standardize on a single `@types/node` major or tighten `engines.node` (e.g. `>=24.x`) to match the highest types version; tsconfig lib targets (ESNext, dom) are already consistent. </blockquote></details> <details> <summary>packages/types/package.json (1)</summary><blockquote> `27-31`: **Toolchain refresh LGTM.** Pure tooling changes; minimal risk. Also applies to: 40-41 </blockquote></details> <details> <summary>packages/keyring/src/index.ts (1)</summary><blockquote> `318-340`: **No duplicate getPrivateKey implementations.** The only logic lives in packages/keyring/src/index.ts; the method in packages/extension/src/libs/keyring/keyring.ts is merely a proxy to this core implementation. </blockquote></details> <details> <summary>packages/hw-wallets/package.json (1)</summary><blockquote> `55-63`: ```shell #!/bin/bash # Verify @ledgerhq dependencies across Yarn workspaces to detect duplicate transports set -euo pipefail # Install dependencies quietly yarn install --silent # Extract workspace names workspace_names=$(yarn workspaces list --json | jq -r .name) # Iterate each workspace and list @ledgerhq packages at depth 0 for ws in $workspace_names; do echo "=== Workspace: $ws ===" yarn workspace "$ws" list --pattern "@ledgerhq" --depth=0 || true donepackages/types/src/networks.ts (2)
110-110: Extension: Missing TAC provider/config and registration
No ‘TAC’ references found underpackages/extension/src—please add TAC’s RPC endpoint, chainId, currency, and logo in the extension’s provider/config files and register it in all NetworkNames usages (UI filters, network switcher, RPC registry).
31-31: No ZkSyncGoerli references detected; no downstream updates required.packages/extension/src/providers/common/libs/new-features.ts (1)
3-3: TAC in newNetworks: LGTMLooks consistent with the TAC additions elsewhere.
packages/extension/src/providers/ethereum/libs/activity-handlers/providers/etherscan/configs.ts (1)
71-72: Verify TAC explorer API compatibilityConfirm that https://explorer.tac.build/ supports the Etherscan-compatible API endpoints your handler uses (it constructs URLs by appending
api?module=account&action=txlist&address=…to the base URL in packages/extension/src/providers/ethereum/libs/activity-handlers/providers/etherscan/index.ts (lines 23–26)). Verify the exact path, any required authentication and rate limits.packages/extension/src/providers/ethereum/networks/zksepolia.ts (1)
16-17: No action: WS RPC already supported
Multiple existing network configs (e.g. glmr.ts, sepolia.ts, zksync.ts) usewss://endpoints, confirming wrapper support.packages/extension/package.json (2)
3-3: Extension manifest(s) not detected: confirm their location and update version to 2.11.0 for Chrome/Firefox submission.
92-142: Smoke-test Vite 7 & ECharts integration
- vue-echarts@7.x still targets ECharts 5 (ECharts 6 support arrives in vue-echarts v8) – either stay on ECharts 5 or upgrade to vue-echarts v8.
- @crxjs/vite-plugin@2.2.0 supports Vite 3+, including Vite 7, but Vite 7 raises Node requirements (>= 20.19 or 22.12+). Verify your Node version and test dev/build flows (HMR, svg icons, build.rollupOptions.input).
- Run full end-to-end builds/tests in both Chrome and Firefox before merging.
packages/extension/src/libs/background/index.ts (1)
171-173: Access control and telemetry for getPrivateKey verified
getPrivateKey is only handled in internalHandler (not reachable via externalHandler) and no persistence or telemetry code touches its returned value.packages/extension/src/libs/background/internal/index.ts (1)
9-20: Export wiring: LGTMInternal export is consistent with the new handler.
packages/extension/src/ui/action/views/accounts/components/accounts-list-item.vue (1)
34-35: Export event forwarding is correctly wired
accounts/index.vue binds@action:export="exportAccount(index)",accounts-list-item.vueforwards$attrs, andaccounts-list-item-menu.vueemitsaction:export.packages/extension/src/providers/ethereum/networks/tac.ts (1)
7-21: Network config validated—ensure RPC CORS/keyless support.
explorer.tac.buildis already mapped in the EtherscanActivity provider and is Etherscan-compatible.coingeckoID: 'tac'matches CoinGecko’s TAC asset slug.- Verify that
https://rpc.ankr.com/tacrequires no API key and returns the necessary CORS headers for the extension.packages/extension/src/providers/ethereum/networks/coti-testnet.ts (1)
16-19: No changes required — EvmNetwork already supports WSS RPC URLs (numerous networks in packages/extension/src/providers/ethereum/networks usewss://).packages/extension/src/ui/action/views/accounts/index.vue (1)
26-30: Make Export button conditional on walletType
Replace the always-true binding with a check againstWalletType, for example:- :exportable="true" + :exportable=" + account.walletType === WalletType.mnemonic || + account.walletType === WalletType.privkey + "Ensure
WalletTypeis imported into this component’s script. Add a unit test verifying that hardware-type accounts do not render the Export action.
| include: ['crypto'], | ||
| }), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Confirm crypto-only polyfill is sufficient.
Restricting to ['crypto'] may break tests using Buffer/stream/process. If failures appear, include missing shims explicitly.
Quick scan:
Add needed shims, e.g.:
- include: ['crypto'],
+ include: ['crypto', 'buffer'],🏁 Script executed:
#!/bin/bash
rg -nP '\b(Buffer|process|stream|Readable|Writable)\b' packages/extension/src packages/extension/tests || trueLength of output: 16275
Include ‘buffer’ in Vitest polyfills
Tests throughout the extension reference Buffer (e.g. in swap, send-transactions, import-account, providers, etc.), so replace:
– include: ['crypto'],
+ include: ['crypto', 'buffer'],Add ‘stream’ or ‘process’ to the include array only if you hit further undefined‐shim errors.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| include: ['crypto'], | |
| }), | |
| include: ['crypto', 'buffer'], | |
| }), |
🤖 Prompt for AI Agents
In packages/extension/configs/vitest.config.mts around lines 9 to 10, the Vitest
polyfills currently include only 'crypto' but tests reference Buffer; update the
include array to add 'buffer' (so Buffer is polyfilled) and run the tests; only
add 'stream' or 'process' to the include array if you encounter additional
undefined-shim errors for those globals.
| find: /^(vite-plugin-node-polyfills\/shims\/.+)/, | ||
| replacement: '$1', | ||
| customResolver(source) { | ||
| return import.meta.resolve(source).replace(/^file:\/\//, ''); | ||
| }, | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Make customResolver path-safe across OSes and URLs.
import.meta.resolve returns a file:// URL; string replace is brittle (Windows, percent-encoding). Use fileURLToPath.
+import { fileURLToPath } from 'node:url';
...
customResolver(source) {
- return import.meta.resolve(source).replace(/^file:\/\//, '');
+ return fileURLToPath(import.meta.resolve(source));
},Confirm Node ≥20 runs Vitest in CI/local, since import.meta.resolve isn’t supported on older Node:
🏁 Script executed:
#!/bin/bash
node -p "process.version"Length of output: 7
Use fileURLToPath instead of string replace in customResolver
Replace brittle URL replacement with fileURLToPath so paths are OS-safe:
+import { fileURLToPath } from 'node:url';
…
customResolver(source) {
- return import.meta.resolve(source).replace(/^file:\/\//, '');
+ return fileURLToPath(import.meta.resolve(source));
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| find: /^(vite-plugin-node-polyfills\/shims\/.+)/, | |
| replacement: '$1', | |
| customResolver(source) { | |
| return import.meta.resolve(source).replace(/^file:\/\//, ''); | |
| }, | |
| }, | |
| // at the top of packages/extension/configs/vitest.config.mts | |
| import { fileURLToPath } from 'node:url'; | |
| … | |
| export default defineConfig({ | |
| plugins: [ | |
| // … | |
| { | |
| find: /^(vite-plugin-node-polyfills\/shims\/.+)/, | |
| replacement: '$1', | |
| customResolver(source) { | |
| return fileURLToPath(import.meta.resolve(source)); | |
| }, | |
| }, | |
| // … | |
| ], | |
| }); |
🤖 Prompt for AI Agents
In packages/extension/configs/vitest.config.mts around lines 23 to 28, the
customResolver currently strips the file URL via string replace which is OS- and
edge-case brittle; import fileURLToPath from 'url' at the top of the file and
replace the replace call with a fileURLToPath conversion (e.g. const resolved =
import.meta.resolve(source); return fileURLToPath(new URL(resolved));), ensuring
the resolver returns an OS-safe file path; add the import line: import {
fileURLToPath } from 'url'.
| const account = message.params[0] as EnkryptAccount | ||
| const password = message.params[1] as string | ||
| try { | ||
| const privKey = await keyring.getPrivateKey(account, password) | ||
| return { | ||
| result: JSON.stringify(privKey), | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Use SignOptions, return plain string, and preserve ProviderError.
KeyRingBase.getPrivateKeyexpectsSignOptions, notEnkryptAccount. Cast/validate accordingly to avoid type drift.- Return the private key as a plain string; JSON-wrapping forces callers to JSON.parse.
- Preserve
code/messageif the thrown error is already aProviderError.
-import { EnkryptAccount, RPCRequestType } from '@enkryptcom/types'
+import { RPCRequestType, SignOptions } from '@enkryptcom/types'
@@
- const account = message.params[0] as EnkryptAccount
+ const options = message.params[0] as SignOptions
const password = message.params[1] as string
try {
- const privKey = await keyring.getPrivateKey(account, password)
+ const privKey = await keyring.getPrivateKey(options, password)
return {
- result: JSON.stringify(privKey),
+ result: privKey,
}
} catch (e: any) {
- return {
- error: getCustomError(e.message),
- }
+ // If it's already a ProviderError, forward it; else wrap it.
+ if (e && typeof e === 'object' && 'code' in e && 'message' in e) {
+ return { error: e }
+ }
+ return { error: getCustomError(e?.message ?? 'unknown error') }
}I can also add runtime shape checks to coerce EnkryptAccount into SignOptions if you prefer keeping the UI payload unchanged.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const account = message.params[0] as EnkryptAccount | |
| const password = message.params[1] as string | |
| try { | |
| const privKey = await keyring.getPrivateKey(account, password) | |
| return { | |
| result: JSON.stringify(privKey), | |
| } | |
| import { RPCRequestType, SignOptions } from '@enkryptcom/types' | |
| // …other imports… | |
| const options = message.params[0] as SignOptions | |
| const password = message.params[1] as string | |
| try { | |
| const privKey = await keyring.getPrivateKey(options, password) | |
| return { | |
| result: privKey, | |
| } | |
| } catch (e: any) { | |
| // If it's already a ProviderError, forward it; else wrap it. | |
| if (e && typeof e === 'object' && 'code' in e && 'message' in e) { | |
| return { error: e } | |
| } | |
| return { error: getCustomError(e?.message ?? 'unknown error') } | |
| } |
🤖 Prompt for AI Agents
In packages/extension/src/libs/background/internal/get-private-key.ts around
lines 14 to 20, the handler currently passes an EnkryptAccount and JSON-wraps
the private key while losing ProviderError details; change it to
convert/validate the incoming payload into SignOptions (or coerce EnkryptAccount
-> SignOptions with runtime checks), call keyring.getPrivateKey(signOptions,
password) with the correct type, return the private key as a raw string in
result (not JSON.stringify), and when catching errors rethrow/preserve existing
ProviderError instances (preserve their code/message) instead of wrapping or
discarding them.
| getPrivateKey = 'enkrypt_getPrivateKey', | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Introducing getPrivateKey: confirm strict gating and scope.
Ensure this internal method is only callable from trusted endpoints, requires an unlocked keyring + fresh user confirmation, and never responds to content scripts.
Suggested checks (background handler):
- Validate sender.origin against allowlist.
- Require re-auth with password/PIN per invocation.
- Rate-limit and audit-log export attempts.
- Return ephemeral data (avoid storing in Redux/state), disable clipboard auto-copy, and scrub from memory/UI ASAP.
You can locate uses with:
Optional typing tweak:
- Consider typing result as a branded Hex type (e.g., HexString) instead of plain string in InternalOnMessageResponse.
🏁 Script executed:
#!/bin/bash
rg -nP -C3 'InternalMethods\.(getPrivateKey)|["'\'']enkrypt_getPrivateKey["'\'']'Length of output: 2027
Enforce strict access controls for getPrivateKey
- In
packages/extension/src/libs/background/index.ts, don’t callgetPrivateKeyunconditionally—validatesender.originagainst your allowlist and require a fresh password/PIN unlock of the keyring per request. - Rate-limit and audit-log each export; return the key as ephemeral data (no Redux/UI storage), disable any auto-copy, and scrub it from memory/UI immediately.
- (Optional) Brand the return type as
HexStringinstead of a rawstringinInternalOnMessageResponse.
🤖 Prompt for AI Agents
In packages/extension/src/types/messenger.ts around lines 38-39, the
getPrivateKey message is defined but background handlers currently call it
unconditionally; update the background handler in
packages/extension/src/libs/background/index.ts to enforce strict controls:
validate sender.origin against a configured allowlist before proceeding, require
an explicit per-request keyring unlock (password/PIN prompt) and fail if not
provided, apply rate-limiting and write an audit log entry for each export
attempt, return the key as ephemeral data (preferably branded as HexString in
InternalOnMessageResponse) without persisting to Redux/UI or auto-copying, and
immediately scrub the key from memory/UI after use.
| const model = defineModel<boolean>(); | ||
| const closeWindow = () => { | ||
| model.value = false; | ||
| }; | ||
| const keyringError = ref(false); | ||
| const isDone = ref(false); | ||
| const isLoading = ref(false); | ||
| const keyringPassword = ref(__PREFILL_PASSWORD__!); | ||
| const privKey = ref(''); | ||
| const keyringBase = new KeyRingBase(); | ||
| const props = defineProps({ | ||
| account: { | ||
| type: Object as PropType<EnkryptAccount>, | ||
| default: () => ({}), | ||
| }, | ||
| }); | ||
| const showAction = async () => { | ||
| try { | ||
| isLoading.value = true; | ||
| const res = await sendToBackgroundFromAction({ | ||
| message: JSON.stringify({ | ||
| method: InternalMethods.getPrivateKey, | ||
| params: [props.account, keyringPassword.value], | ||
| }), | ||
| }); | ||
| if (res.error) { | ||
| throw res.error; | ||
| } else { | ||
| privKey.value = JSON.parse(res.result!); | ||
| } | ||
| isDone.value = true; | ||
| } catch (err) { | ||
| keyringError.value = true; | ||
| } | ||
| isLoading.value = false; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid JSON round-trip and improve error handling UX.
- Use the plain string result (after backend change).
- Reset error state before attempting.
- Provide a generic error message instead of always “Invalid Keyring password”.
const showAction = async () => {
- try {
- isLoading.value = true;
+ try {
+ isLoading.value = true;
+ keyringError.value = false;
+ errorText.value = '';
const res = await sendToBackgroundFromAction({
message: JSON.stringify({
method: InternalMethods.getPrivateKey,
params: [props.account, keyringPassword.value],
}),
});
if (res.error) {
throw res.error;
} else {
- privKey.value = JSON.parse(res.result!);
+ privKey.value = res.result!;
}
isDone.value = true;
} catch (err) {
- keyringError.value = true;
+ keyringError.value = true;
+ errorText.value = 'Invalid password or export not allowed for this account.';
}
isLoading.value = false;
};Add this state in <script setup>:
-const keyringError = ref(false);
+const keyringError = ref(false);
+const errorText = ref('');📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const model = defineModel<boolean>(); | |
| const closeWindow = () => { | |
| model.value = false; | |
| }; | |
| const keyringError = ref(false); | |
| const isDone = ref(false); | |
| const isLoading = ref(false); | |
| const keyringPassword = ref(__PREFILL_PASSWORD__!); | |
| const privKey = ref(''); | |
| const keyringBase = new KeyRingBase(); | |
| const props = defineProps({ | |
| account: { | |
| type: Object as PropType<EnkryptAccount>, | |
| default: () => ({}), | |
| }, | |
| }); | |
| const showAction = async () => { | |
| try { | |
| isLoading.value = true; | |
| const res = await sendToBackgroundFromAction({ | |
| message: JSON.stringify({ | |
| method: InternalMethods.getPrivateKey, | |
| params: [props.account, keyringPassword.value], | |
| }), | |
| }); | |
| if (res.error) { | |
| throw res.error; | |
| } else { | |
| privKey.value = JSON.parse(res.result!); | |
| } | |
| isDone.value = true; | |
| } catch (err) { | |
| keyringError.value = true; | |
| } | |
| isLoading.value = false; | |
| }; | |
| // In <script setup>, add new errorText state next to keyringError | |
| const keyringError = ref(false); | |
| const errorText = ref(''); | |
| const isDone = ref(false); | |
| const isLoading = ref(false); | |
| const keyringPassword = ref(__PREFILL_PASSWORD__!); | |
| const privKey = ref(''); | |
| const keyringBase = new KeyRingBase(); | |
| const showAction = async () => { | |
| try { | |
| isLoading.value = true; | |
| keyringError.value = false; | |
| errorText.value = ''; | |
| const res = await sendToBackgroundFromAction({ | |
| message: JSON.stringify({ | |
| method: InternalMethods.getPrivateKey, | |
| params: [props.account, keyringPassword.value], | |
| }), | |
| }); | |
| if (res.error) { | |
| throw res.error; | |
| } else { | |
| // backend now returns a plain string | |
| privKey.value = res.result!; | |
| } | |
| isDone.value = true; | |
| } catch (err) { | |
| keyringError.value = true; | |
| errorText.value = 'Invalid password or export not allowed for this account.'; | |
| } finally { | |
| isLoading.value = false; | |
| } | |
| }; |
packages/hw-wallets/package.json
Outdated
| "@types/node": "^24.3.1", | ||
| "@typescript-eslint/eslint-plugin": "^8.43.0", | ||
| "@typescript-eslint/parser": "^8.43.0", | ||
| "eslint": "^9.35.0", | ||
| "eslint-config-airbnb-base": "^15.0.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Bump Node engine to match upgraded toolchain (ESLint 9, Vitest 3, TS 5.9).
These deps require Node ≥18 (ESLint 9 specifically ≥18.18). Current "engines.node": ">=14.15.0" is misleading and can break installs/CI.
Apply:
"engines": {
- "node": ">=14.15.0"
+ "node": ">=18.18.0"
},Run to confirm consistency repo-wide:
🏁 Script executed:
#!/bin/bash
fd package.json -t f | xargs -I{} jq -r '.name + " => " + (.engines.node // "N/A")' {}Length of output: 508
Bump Node engine to ≥18.18.0 in all packages
Update every engines.node entry currently set to >=14.15.0 to >=18.18.0 in each package.json (e.g. @enkryptcom/hw-wallets, keyring, name-resolution, request, signer-*, storage, swap, types, utils). Root packages (enkrypt, extension, extension-bridge) currently lack an engines field—consider adding one for consistency.
🤖 Prompt for AI Agents
In packages/hw-wallets/package.json around lines 25 to 29, the project needs its
Node engine requirement updated for compatibility; change or add an "engines"
field to require "node": ">=18.18.0" (and update the same "engines.node" value
across all package.json files mentioned: @enkryptcom/hw-wallets, keyring,
name-resolution, request, signer-*, storage, swap, types, utils), and for root
packages (enkrypt, extension, extension-bridge) add a consistent "engines": {
"node": ">=18.18.0" } entry if missing; ensure package.json formatting and
version constraints remain valid and run a quick npm/yarn install or lint to
verify no schema errors.
Summary by CodeRabbit