Skip to content

feat: add evm chain support#2098

Open
Nick-1979 wants to merge 47 commits intomainfrom
ethSupport00
Open

feat: add evm chain support#2098
Nick-1979 wants to merge 47 commits intomainfrom
ethSupport00

Conversation

@Nick-1979
Copy link
Member

@Nick-1979 Nick-1979 commented Jan 6, 2026

Summary by CodeRabbit

  • New Features

    • Add full Ethereum/EVM support: create/import Ethereum accounts, sign & send ETH/ERC‑20 transactions, view Etherscan-backed tx history, and fetch EVM assets/NFTs.
  • Improvements

    • Unified address handling across UI (receive, send, account flows) with chain-aware validation and visuals.
    • New account-type selector and refined dark theme highlight.
  • Dependencies

    • Added Ethereum tooling and icon packages to enable EVM features.

@Nick-1979 Nick-1979 self-assigned this Jan 6, 2026
@Nick-1979 Nick-1979 added the WIP Work In Progress label Jan 6, 2026
@Nick-1979 Nick-1979 changed the title featL add evm chain support feat: add evm chain support Jan 6, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 6, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds broad EVM/Ethereum support across the extension: Ethereum account types, address handling, EVM asset fetching (worker), providers/fee estimation, signer adapter, UI account-type controls, and send-fund fee/transaction flow changes.

Changes

Cohort / File(s) Summary
Config & Build
\.env.example, package.json
New env placeholders (Alchemy, Etherscan, Infura, Sentry); added manifest copy scripts; added ethers + web3 icons and devDependency.
Extension Manifests & Background
packages/extension/manifest.dev.json, packages/extension/manifest.prod.json, packages/extension/src/background.ts
Added separate MV3 dev/prod manifests; removed sr25519-only keyring filter.
Account creation & messaging
packages/extension-base/.../types.ts, packages/extension-base/.../Extension.ts, packages/extension-polkagate/src/messaging.ts
Propagates optional type?: KeypairType through request types, background handler, and createAccountExternal messaging.
Address/chain normalization & utilities
packages/extension-polkagate/src/util/*.ts*
Ethereum-aware address validation/formatting, chain name sanitization, chain lookup helper, logo support and color derivation, EVM constants (chain IDs/genesis/token maps).
EVM provider & fee tooling
packages/extension-polkagate/src/util/evmUtils/*
Added getEthProvider, getEthFee, ETH chain ID/constants, ERC20 metadata and token icons.
EVM signer & transaction history
packages/extension-polkagate/src/components/signerAdaptor.ts, packages/extension-polkagate/src/util/api/getEtherscan.ts
KeyringEthSigner to sign/send Ethereum transactions; Etherscan history fetch + transform.
Worker & EVM asset fetching
packages/extension-polkagate/src/util/workers/shared-helpers/getAssetOnEvm.js, .../sharedWorker.js, worker helpers
New EVM asset fetcher integrated into shared worker and listener; worker posts per-address native + ERC-20 assets.
Hooks: address, signer & assets
packages/extension-polkagate/src/hooks/*
New useBaseAddress, useEthSigner; ethereum-aware useAccount/useFormatted/useIdentity/useProxies; asset fetching split to support isEvmChain.
Send-fund flow & fees
packages/extension-polkagate/src/fullscreen/sendFund/*
Refactored useLimitedFeeCall signature and returns, added useParaSpellFeeCall paraspell gating, optional call and fee fields, ED logic, and UI wiring to prefer call or paraSpellTransaction.
UI: address input, account creation & display
packages/extension-polkagate/src/components/*, packages/extension-polkagate/src/popup/newAccount/*, packages/extension-polkagate/src/popup/import/*, packages/extension-polkagate/src/fullscreen/*
AddressInput gains setType and Ethereum detection; ModeSwitch and accountType plumbing added to create account flows; Identity/Account components handle account.type and genesisHash for EVM; receive flow switched to DropdownOption.
NFTs, proxies, history & other guards
packages/extension-polkagate/src/util/workers/shared-helpers/getNFTs.js, packages/extension-polkagate/src/util/api/getTXsHistory.ts, packages/extension-polkagate/src/popup/history/explorer.ts, hooks
Filtered Ethereum addresses from NFT/proxy/Subscan flows; special-case Etherscan explorer links for Ethereum; proxies hook may return null for EVM addresses.
Types & constants
packages/extension-polkagate/src/util/types.ts, packages/extension-polkagate/src/util/constants.ts, replacements/interfaces.js
Extended types to accept BN for fees, made some TransferRequest fields optional, added POLKADOT_TEST_NETS and EVM test genesis hashes, added FETCHING_ASSETS_FUNCTION_NAMES.EVM and selectableNetworks export.
Styling & theme
packages/extension-polkagate/src/themes/dark.ts, packages/extension-polkagate/src/style/PolkaGateIdenticon.tsx
Added text.highlight and warning.light; PolkaGateIdenticon uses useBaseAddress for EVM handling.
Minor / formatting
assorted files
Small formatting or whitespace edits across signing popup and other components.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant UI as AddressInput UI
    participant Addr as AddressInput
    participant UIParent as Add/Watch UI
    participant Backend as Extension Backend
    participant Keyring as Keyring

    User->>UI: Paste/enter address
    UI->>Addr: validate input
    Addr->>Addr: isEthereumAddress(addr)?
    alt Ethereum
        Addr->>UIParent: setType('ethereum')
    else Substrate
        Addr->>UIParent: setType(undefined)
    end
    User->>UIParent: Confirm create watch-only
    UIParent->>Backend: createAccountExternal(name, addr, undefined, type)
    Backend->>Keyring: keyring.addExternal({ address, type })
    Keyring-->>Backend: added
    Backend-->>UIParent: success
Loading
sequenceDiagram
    actor User
    participant SendUI as Send Fund UI
    participant Step3 as Step3Amount
    participant FeeHook as useLimitedFeeCall
    participant ChainAPI as Chain API
    participant ParaSpell as ParaSpell Builder
    participant Backend as Node / Extrinsic

    User->>SendUI: Configure transfer (amount, recipient, chain)
    SendUI->>Step3: render inputs
    Step3->>FeeHook: request fee (amount, decimal, recipientAddress, recipientChain, transferType)
    FeeHook->>ChainAPI: resolve chain info / decimals
    FeeHook->>FeeHook: build onChainParam
    FeeHook->>Backend: prepare call / estimate fee
    alt paraspell supported
        Step3->>ParaSpell: build paraSpell transaction & getTransferInfo
        ParaSpell-->>Step3: paraSpellTransaction + paraSpellFee
    end
    FeeHook-->>Step3: (call, limitedTotalFee, params)
    Step3->>SendUI: update UI, enable signing
Loading
sequenceDiagram
    participant Worker as SharedWorker
    participant Provider as Eth Provider
    participant Contracts as ERC20 Contracts
    participant Port as MessagePort
    participant Main as Main Thread

    Main->>Worker: request getEvmAssets(addresses, chain)
    Worker->>Provider: init provider for chain
    loop per address
        Worker->>Contracts: call balanceOf for tokens
        Contracts-->>Worker: token balances
        Worker->>Provider: getBalance(native)
        Provider-->>Worker: native balance
        Worker->>Worker: assemble assets array
    end
    Worker->>Port: postMessage({ function: 'getEvmAssets', result })
    Port-->>Main: deliver assets
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

new feature

Suggested reviewers

  • AMIRKHANEF

Poem

🐰 I sniffed the chain across the wire,

Ethereum hops into our mire.
Signers, fees, and tokens in sight,
The bunny dances through the night.
Hooray — cross-chain carrots take flight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding EVM chain support. It directly relates to the primary objective of the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ethSupport00

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/extension-polkagate/src/themes/dark.ts (1)

14-14: Clarify or correct the comment.

The comment indicates this property is "for staking," but the PR is focused on adding EVM chain support. Please verify whether this comment is accurate or should be updated to reflect its actual usage in the EVM-related UI changes.

packages/extension-polkagate/src/hooks/useFetchAssetsOnChains.ts (1)

107-143: Filter relay chain addresses to substrateAddresses for consistency.

Relay chains (line 117) currently receive the unfiltered addresses array, while asset hubs (line 134) and multi-asset chains (line 139) only receive substrateAddresses. Since relay chains are substrate-based and cannot process Ethereum addresses, they should use substrateAddresses as well. This would also require moving the relay chain logic check after the substrate address guard (currently at lines 120-122).

🤖 Fix all issues with AI Agents
In @.env.example:
- Line 1: The EXTENSION_PREFIX in .env.example was changed to the generic
MY_EXTENSION; revert it to the project-specific value (e.g., POLKAGATE) or
another appropriate project identifier by updating the EXTENSION_PREFIX entry in
.env.example, and then verify all usages of process.env.EXTENSION_PREFIX (search
for EXTENSION_PREFIX in the codebase) still expect that value and adjust any
documentation or tests that assume POLKAGATE to match the new prefix if you
intentionally want a different name.

In @package.json:
- Line 27: The package.json "build" script contains an invalid double ampersand
("&& &&") causing a shell syntax error; open the "build" script entry (the value
for the "build" key) and remove the extra ampersand so the commands are chained
correctly (i.e., ensure there is a single "&&" between "yarn copy-prod-manifest"
and "yarn apply-version"), leaving the rest of the pipeline
("polkadot-dev-build-ts && yarn build:zip && yarn build:rollup") intact.
- Around line 60-62: The project imports v5 sub-packages (@ethersproject/bytes,
@ethersproject/keccak256, @ethersproject/transactions) while using ethers v6;
update signerAdaptor.ts to import arrayify, keccak256, serialize and
UnsignedTransaction (or the v6 equivalent types) directly from "ethers" instead
of the @ethersproject/* packages, adjust any call signatures to match ethers v6
APIs per the ethers v6 migration guide, and remove the three @ethersproject/*
dependencies from package.json to avoid version mixing and bundle duplication.
- Line 87: Update the dependency entry for "@web3icons/react" in package.json
from "^4.0.25" to "^4.1.12"; after editing the "@web3icons/react" version
string, run your package manager (npm install or yarn install) to update
node_modules and regenerate the lockfile (package-lock.json or yarn.lock), then
run the test/build to ensure nothing breaks.

In @packages/extension-base/src/background/handlers/Extension.ts:
- Around line 113-114: In accountsCreateExternal, keyring.addExternal is being
called with the account type inside the meta object; move the type out to the
third argument: update the call in accountsCreateExternal so it passes address
and a meta object with genesisHash and name, and supply type as the separate
third parameter to keyring.addExternal.

In @packages/extension-polkagate/src/components/FormatBalance2.tsx:
- Around line 40-49: The getFormat function may access decimals[0] or tokens[1]
when the arrays are shorter than expected; validate that decimals and tokens are
non-empty at the start of getFormat (or throw a clear error) and change the
token fallback to use tokens[0] instead of tokens[1]; ensure the returned tuple
uses formatIndex if in-range otherwise decimals[0] and tokens[0] as safe
fallbacks to avoid undefined accesses.
- Around line 58-63: splitFormat assumes value contains both '.' and a space
which can cause a runtime error when postfixFull is undefined; add validation in
splitFormat to safely handle malformed input by checking that value includes a
'.' before splitting and that postfixFull is defined before calling split(' '),
providing sensible fallbacks (e.g., postfix = '' and unit = '') or returning a
safe default React node; ensure createElement(prefix, postfix, unit, label,
isShort, decimalPoint) always receives defined strings and preserve existing
behavior for valid inputs.
- Around line 82-89: FormatBalance currently calls getFormat(decimals, tokens,
formatIndex) without ensuring decimals and tokens are non-empty arrays, risking
runtime errors in getFormat; update the useMemo for formatInfo (in function
FormatBalance) to validate that decimals and tokens are arrays with length > 0
(e.g., Array.isArray(decimals) && decimals.length && Array.isArray(tokens) &&
tokens.length) before calling getFormat, and if validation fails fall back to a
safe default (either the provided format prop or a safe format object/undefined)
so getFormat is never invoked with empty or invalid arrays.
- Around line 65-80: The applyFormat function uses rest.slice(4) without
validating rest length and compares prefix length to K_LENGTH (which is
unclear); update this by renaming K_LENGTH to a clearer SI_UNIT_THRESHOLD (or
similar) and update all references, and defend the slice by ensuring rest.length
>= 4 before slicing (e.g., compute unit = rest.length >= 4 ? rest.slice(4) : ''
or use a safe substring equivalent) so you never index past the string; keep the
original behavior of switching to SI formatting using the renamed threshold.
- Around line 51-56: In createElement ensure postfix is defensively handled
before any use: coerce postfix to a safe string (e.g., const safePostfix =
postfix ?? '') and use safePostfix everywhere (replace postfix, postfix?.slice,
and the parseFloat check that sets maybeTilde) and guard the parseFloat
comparison (skip maybeTilde when safePostfix is empty or shorter than
decimalPoint). Update references in createElement (maybeTilde, trimmedPostfix,
slice for span content) to use the safe variable so no code path calls
parseFloat or .slice on undefined.

In @packages/extension-polkagate/src/components/PasswordInput.tsx:
- Line 76: The focusing state is initialized incorrectly from the focused prop,
causing an inverted visual when autoFocus triggers; change the initialization of
focusing in PasswordInput (state variable focusing and setter setFocused) to a
hard-coded default (e.g., false) instead of using the focused prop, and if you
need initial sync, handle it in a useEffect that sets focusing based on focused
only once on mount; also ensure the onFocus handler that calls toggle() remains
consistent so autoFocus won’t flip the visual state unexpectedly.

In @packages/extension-polkagate/src/components/signerAdaptor.ts:
- Around line 49-54: The v value is computed incorrectly as sig[64] + 27 in
signerAdaptor.ts; update the signature recovery calculation to be EIP-155
compliant by using the transaction chainId: compute recoveryId = sig[64] & 1 (0
or 1) and set v = chainId * 2 + 35 + recoveryId (for chainId present) instead of
adding 27; ensure the signing method that contains sigBytes -> sig/r/s/v (the
method where const sigBytes = this.keypair.sign(msgHash) is used) accepts or
reads the appropriate chainId (e.g., from the transaction or this object) and
falls back to v = recoveryId + 27 only if chainId is undefined/null for legacy
behavior.
- Around line 26-60: Ensure the signer only uses ECDSA/secp256k1 keypairs by
validating the key type before signing: in the constructor of the SignerAdaptor
class or at the very start of signTransaction(tx: TransactionRequest) check
this.keypair.type (or equivalent property/method on the keypair) and if it is
not ECDSA/secp256k1 (e.g., not "ecdsa" or "secp256k1") throw a clear error
describing the unsupported key type; do not attempt to sign with non-ECDSA
keypairs (fail fast) and include this.keypair reference in the validation to
locate the check quickly.
- Line 4: Remove the top-level //@ts-nocheck from signerAdaptor.ts and re-enable
TypeScript checking; then fix any resulting type errors in the signer adaptor
implementation (e.g., adjust types for functions/values such as the signer
adaptor export, sign/verify methods, key objects, and any helper functions) by
declaring proper interfaces/types or using narrow, explicit casts; if an
individual line truly cannot be typed immediately, replace broad suppression
with targeted @ts-expect-error comments and short explanations next to them
rather than disabling checks for the whole file.
- Around line 26-66: signTransaction and sendTransaction lack validation and
error handling; add input checks (e.g., ensure tx.to is present and valid,
gasPrice/gasLimit/nonce/value types are sane, tx.data convertible), validate
signature parts after this.keypair.sign (ensure sigBytes length >=65 and r/s/v
in expected ranges) and wrap signing and serialization in try/catch to throw
descriptive errors (e.g., "signTransaction: invalid input", "signTransaction:
signing failed", "signTransaction: invalid signature"). In sendTransaction wrap
provider.broadcastTransaction in try/catch to translate network/provider errors
into clear errors (e.g., "sendTransaction: broadcast failed") and rethrow or
return a rejected Promise; reference signTransaction, sendTransaction,
this.keypair.sign, serialize, keccak256, and provider.broadcastTransaction when
locating code to change.

In @packages/extension-polkagate/src/components/SignUsingProxy.tsx:
- Line 121: The component widened the type of the prop proxies to include null
but the code still only checks for undefined; update all loading checks (e.g.,
the loadingProxy boolean and the JSX loading indicator) to treat null like
undefined by changing conditions from proxies === undefined to proxies == null
or (proxies === undefined || proxies === null), and ensure any subsequent
branches that assume proxies is an array first guard against proxies being null
before accessing it.

In @packages/extension-polkagate/src/fullscreen/sendFund/index.tsx:
- Around line 52-78: The effect currently mis-checks the recipient component and
blocks recalculation: replace the incorrect check of RecipientAddress with a
runtime check of the recipient value (variable to) in the condition; ensure
ethFeeRef is reset so fees can be recalculated when inputs change (e.g., set
ethFeeRef.current = false in a finally or cleanup when amount/to/token/address
change or avoid a permanent guard and instead guard per-request), and remove the
//@ts-ignore by constructing the correct FeeInfo-typed object before calling
setInputs (import or define FeeInfo and set fee: { originFee: res as FeeInfo }
or map res to the expected structure) so setInputs receives the properly typed
fee shape when getEthFee resolves; keep getEthFee, ethFeeRef, inputs, setInputs,
address, chainName, amount, to, token as the referenced symbols to locate
changes.

In @packages/extension-polkagate/src/fullscreen/sendFund/useLimitedFeeCall.ts:
- Around line 146-156: The onChainParam useMemo builds transfer args with the
sender `address` instead of the destination `recipientAddress`; update the three
branches in `onChainParam` (the currencies/tokens branch, the assets branch, and
the balances branch) to use `recipientAddress` as the destination
(`['recipientAddress', assetToTransfer.currencyId, amountAsBN]`,
`[parsedAssetId, recipientAddress, amountAsBN]`, and `[recipientAddress,
amountAsBN]` respectively) and include `recipientAddress` in the useMemo
dependency array; also remove the unsafe `as unknown` cast on
`assetToTransfer.currencyId` and replace it with a correct typed assertion or
ensure `currencyId` is correctly typed on `FetchedBalance` so the parameter
matches the expected on-chain API type.

In @packages/extension-polkagate/src/fullscreen/sendFund/useParaSpellFeeCall.ts:
- Around line 102-108: The @ts-ignore on the setParaSpellState update hides a
type mismatch: the fallback object only sets paraSpellFee.originFee and omits
paraSpellFee.destinationFee which violates the expected ParaspellFees shape;
update the setParaSpellState callback (the function passed to setParaSpellState)
to provide a full-paraspell fee object matching ParaspellFees (include both
originFee and destinationFee with BN_ZERO or appropriate defaults), or
alternatively fix the ParaspellFees type if the destinationFee can be optional,
or if suppression is truly required replace @ts-ignore with @ts-expect-error and
add a comment explaining why the type mismatch is safe; specifically reference
setParaSpellState, paraSpellFee, originFee, destinationFee, ParaspellFees and
BN_ZERO when making the change.

In @packages/extension-polkagate/src/hooks/useEthSigner.ts:
- Line 10: The import for getEthProvider is pointing to the wrong path; update
the import statement that references getEthProvider in useEthSigner.ts to import
from the evmUtils folder (i.e., replace the '../util/getEthProvider' import with
the correct '../util/evmUtils/getEthProvider' path) so the getEthProvider symbol
resolves to the actual module.
- Around line 13-17: The useEthSigner hook (useEthSigner) currently never sets
its signer state because the signer resolution logic (commented-out block around
lines 18-47 and similar at 49-50) is removed, so the hook always returns
undefined; either re-enable/implement that logic to resolve and set the
JsonRpcSigner (using chainName and address to instantiate provider and call
getSigner) and update signer via setSigner, or add a clear TODO/FIXME at the top
of useEthSigner stating it is intentionally a placeholder and ensure callers
handle undefined signer; also add a unit or runtime guard in dependent code to
gracefully handle an undefined signer before marking the PR ready.
- Around line 18-47: The large commented-out useMemo/useEffect block in
useEthSigner.ts (the provider useMemo and side-effect that calls
provider.getSigner and setSigner) and its debugging console.log calls should not
remain in production; either fully implement and uncomment this logic using
getEthProvider, provider, signer, setSigner (ensuring proper dependency arrays
and type guards for provider.getSigner) or delete the commented block entirely
(or replace it with a single TODO comment describing the intended behavior).
Also remove all console.log statements referenced in the comment (the logs
around '_provider', 'Getting signer ...', and 'signer:') so no debug prints
remain. Ensure references to getEthProvider, provider.getSigner, setSigner,
signer remain correct if you implement the logic.
- Line 4: Remove the top-line `//@ts-nocheck`, delete the unused `Wallet`
import, and correct the faulty import path from '../util/getEthProvider' to
'../util/evmUtils/getEthProvider' so TypeScript can resolve the module in the
useEthSigner hook; keep all other imports and the useEthSigner implementation
unchanged.

In @packages/extension-polkagate/src/hooks/useFetchAssetsOnChains.ts:
- Around line 88-105: There is a leftover debug console.log in
useFetchAssetsOnChains.ts: remove the console.log(' to fetch evm chains
balances:', evmAddresses?.length) inside the block that checks evmAddresses and
isEvmChain so production logs aren’t polluted; keep the surrounding logic intact
(the destructuring that produces evmAddresses/substrateAddresses, the chainName
resolution via getChainName(genesisHash, genesisOptions), the error branch that
returns FAILED, and the final return fetchEvmAssets(chainName, evmAddresses)).

In @packages/extension-polkagate/src/popup/history/explorer.ts:
- Around line 8-10: The Ethereum explorer branch currently always returns a
mainnet tx URL and concatenates raw data; update the explorer logic in
explorer.ts to (1) use the incoming type parameter (when type === 'account'
build an /address/ URL, when 'extrinsic' or 'tx' use /tx/), (2) resolve the
correct Etherscan-style domain per chain using the ETH_CHAIN_IDS.ts mapping
(support "ethereum", "polygon", "arbitrum", "optimism", "base", etc.) and pick
the correct subdomain for testnets like sepolia, (3) validate the data parameter
with a simple regex for tx-hash or checksummed/hex address before building the
URL and return a safe fallback (null or a standardized error object) for invalid
data, and (4) centralize this in the same function/block that references
chainName, type, and data so other EVM chains are handled consistently.

In @packages/extension-polkagate/src/themes/dark.ts:
- Line 40: The dark theme defines a redundant warning.light color in
packages/extension-polkagate/src/themes/dark.ts (warning.light: '#FFCE4F') that
is unused and inconsistent with light.ts; either remove the warning.light
property from the exported dark theme object or add a matching warning.light to
light.ts and update components/styles to reference warning.light where a lighter
warning variant is required (search for usages of the warning palette and ensure
components reference warning.light instead of duplicating colors). Ensure the
change keeps the exported theme shape consistent across dark.ts and light.ts and
run a project-wide search for "warning.light" to verify no broken references
remain.

In @packages/extension-polkagate/src/util/api/getEtherscan.ts:
- Around line 81-103: In getEthTxHistory, validate the chain input before
building the URL: check the result of EVM_CHAIN_ID_MAP[chainName.toLowerCase()]
(and optionally getChainInfoByName(chainName)) and if chainId (or tokenDecimal)
is undefined, throw or return a rejected Promise with a clear "unsupported
chain" error (including the provided chainName and/or a list of supported
chains) instead of proceeding to construct the URL; locate symbols
getEthTxHistory, EVM_CHAIN_ID_MAP and getChainInfoByName to add this guard so
you never call the Etherscan API with chainid=undefined.
- Line 6: Remove the node-fetch import and switch to the native browser fetch:
delete the line importing 'node-fetch' in getEtherscan.ts, update any calls in
the getEthTxHistory function to use the global fetch (no import), and if
TypeScript compilation complains about fetch types ensure the project includes
DOM lib typings (or add appropriate types) so the native fetch is recognized at
compile time.
- Around line 47-72: The function convertTxToPolkaGateStyle hardcodes
asset_symbol: 'ETH' which breaks support for other EVM chains; change
convertTxToPolkaGateStyle to accept a token symbol parameter (e.g., symbol) or
derive it using EVM_CHAIN_ID_MAP given the chain id, update where
convertTxToPolkaGateStyle is called (the caller around line 118) to pass the
correct native token symbol (BNB, MATIC, AVAX, etc.) based on chain info, and
ensure the returned object uses that symbol instead of the hardcoded 'ETH'.

In @packages/extension-polkagate/src/util/evmUtils/constantsEth.ts:
- Around line 62-68: The chain key is misspelled as "goreli" in this file which
conflicts with the correct "goerli" used at line 62; update all occurrences of
the "goreli" string keys in the exported constants (in this file’s constantsEth
object) to "goerli" so token lookups (e.g., the USDC entry and any ERC20
lookups) use a consistent chain name.
- Around line 9-16: The Goerli testnet constant and map entry are misspelled and
use the wrong genesis hash: rename GORELI_GENESISHASH to GOERLI_GENESISHASH (or
update its usage) and update its value to the correct Goerli genesis hash
0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a; then update
the EVM_GENESISHASH_MAP key from "goreli" to "goerli" (and reference the
corrected GOERLI_GENESISHASH) so the map and other code (e.g., getLogo2.ts) use
the consistent "goerli" spelling and correct hash.

In @packages/extension-polkagate/src/util/evmUtils/ETH_CHAIN_IDS.ts:
- Line 21: Rename the mistaken key berachainBepolia to berachainSepolia in the
ETH_CHAIN_IDS map and update any usages that reference berachainBepolia to the
new key; keep the numeric value (80069) unchanged so lookups still resolve
correctly and adjust any tests or type definitions that reference the old
identifier (e.g., in code that accesses ETH_CHAIN_IDS.berachainBepolia).

In @packages/extension-polkagate/src/util/evmUtils/getEthProvider.ts:
- Line 8: There is a misspelling of the Goerli testnet — update the key/value
"goreli" to the correct "goerli" wherever it appears (specifically in the
NETWORK_MAP constant and in the isTestNet function) so references match the
proper network name; ensure both occurrences are renamed consistently and run
tests/type checks to confirm no other references remain.

In @packages/extension-polkagate/src/util/getChain.ts:
- Line 22: The return uses an unsafe assertion "found as unknown as NetworkInfo"
allowing undefined to be treated as NetworkInfo; instead return the variable
found directly (remove the double-cast) so the function respects its declared
return type NetworkInfo | undefined—locate the found variable and the return
statement in getChain (the line returning found as unknown as NetworkInfo) and
replace it with a plain return found.

In @packages/extension-polkagate/src/util/getLogo2.ts:
- Line 66: The array evmChainsWithEthLogo contains the correctly spelled chain
name "goerli" but the genesis-hash mapping in the constants (the genesis hash
map key currently spelled "goreli") is misspelled, causing lookup mismatches;
update the genesis hash map key from "goreli" to "goerli" (and search/replace
any other occurrences of the misspelling) so keys in the genesis hash map match
evmChainsWithEthLogo and other lookups that use "goerli".

In
@packages/extension-polkagate/src/util/workers/shared-helpers/getAssetOnEvm.js:
- Around line 68-77: The code incorrectly treats a zero bigint balance as falsy
and returns early; update the check around the result of
tokenContract['balanceOf'] (variable balance in getAssetOnEvm.js) to only bail
out for failed fetches (null or undefined) rather than any falsy value—i.e.,
replace the `if (!balance)` style check with an explicit null/undefined check
(e.g., `balance == null` or `balance === undefined`) so zero balances (0n) are
preserved for downstream processing.

In
@packages/extension-polkagate/src/util/workers/shared-helpers/getAssetOnRelayChain.js:
- Line 21: The line mutates the input addresses array; instead create a new
array (e.g., normalizedAddresses) by mapping addresses with the existing
isEthereumAddress and evmToAddress logic instead of reassigning to addresses,
and then use normalizedAddresses wherever the code currently references
addresses (ensure references such as the mapping line and any subsequent calls
that expect normalized values are updated to use normalizedAddresses).

In @packages/extension-polkagate/src/util/workers/shared-helpers/getNFTs.js:
- Around line 239-245: The check for filteredAddresses is incorrect because
filter() always returns an array; replace the truthiness check with an emptiness
check so that when filteredAddresses is undefined/null or has length === 0 you
short-circuit and post the NFT_FUNCTION_NAME result as undefined; update the
logic around filteredAddresses (from the isEthereumAddress filter step) to test
(!filteredAddresses || filteredAddresses.length === 0) before calling
getNFTs([]) and calling port.postMessage(JSON.stringify({ functionName:
NFT_FUNCTION_NAME, results: undefined })) to avoid unnecessary work.

In @packages/extension/manifest.dev.json:
- Around line 2-3: The manifest's "description" currently references only
Polkadot/Substrate ecosystems but this PR adds Ethereum/EVM support; update the
"description" value in packages/extension/manifest.dev.json (the JSON key
"description") to mention both Polkadot/Substrate and Ethereum/EVM-based
blockchains (e.g., "A user-friendly wallet for seamless browser-based
interaction with Polkadot/Substrate and Ethereum/EVM-based blockchains."),
keeping the message concise and within any store length limits.

In @packages/extension/manifest.prod.json:
- Around line 2-3: Update the "description" value in manifest.prod.json to
mention both Polkadot/Substrate and Ethereum/EVM support: locate the
"description" field (currently "A user-friendly wallet for seamless
browser-based interaction with Polkadot/Substrate-based blockchains.") and
change it to a concise string that references both ecosystems (e.g., include
"Polkadot/Substrate and Ethereum/EVM" or similar phrasing) to reflect the new
EVM support.

In @replacements/interfaces.js:
- Around line 54-73: The Goerli entry in the evmTestChains array uses an
incorrect SLIP-44 coin type (prefix: 5); update the Goerli testnet object (the
object with displayName "Goerli Testnet" inside evmTestChains) to use prefix: 60
to match Ethereum's coin type so address derivation and network identification
remain consistent with mainnet and Sepolia.
🟠 Major comments (16)
packages/extension-polkagate/src/components/PasswordInput.tsx-76-76 (1)

76-76: Logic error: inverted focus state when auto-focusing.

Initializing focusing with the focused prop creates incorrect visual behavior when focused={true} is passed:

  1. focusing state initializes to true
  2. autoFocus={focused} (line 131) causes the input to focus on mount
  3. The onFocus event fires and calls toggle(), flipping focusing from true to false
  4. The Check icon now displays as unfocused (bulk variant, common color) even though the input IS focused

This inverts the expected visual state whenever the component auto-focuses.

🔎 Recommended fix

Revert to the original hard-coded initialization:

-  const [focusing, setFocused] = useState<boolean>(focused);
+  const [focusing, setFocused] = useState<boolean>(false);

If you need to synchronize initial focus state with the prop, consider using useEffect to handle the initial focus without conflicting with the toggle behavior, or reconsider whether the toggle callback should be conditional.

packages/extension-polkagate/src/components/FormatBalance2.tsx-82-89 (1)

82-89: Add validation for required array props.

The component passes decimals and tokens to getFormat (Line 87) without validating they are non-empty arrays. Given the array access issues in getFormat, this could cause runtime errors.

🔎 Proposed validation
 function FormatBalance({ children, decimalPoint = FLOATING_POINT_DIGIT, decimals, format, formatIndex, isShort, label, labelPost, style, tokenColor, tokens, value, valueFormatted, withCurrency, withSi }: FormatBalanceProps): React.ReactElement<FormatBalanceProps> {
   const { t } = useTranslation();
   const { isHideNumbers } = useIsHideNumbers();
+
+  if (!decimals?.length || !tokens?.length) {
+    console.error('FormatBalance: decimals and tokens must be non-empty arrays');
+    return <div style={style}>-</div>;
+  }

   const formatInfo = useMemo(() =>
     format || getFormat(decimals, tokens, formatIndex)
     , [decimals, tokens, format, formatIndex]);
packages/extension-polkagate/src/components/FormatBalance2.tsx-65-80 (1)

65-80: Verify string slice safety and improve constant naming.

Two concerns:

  1. Line 74: rest.slice(4) assumes the string returned by formatBalance has at least 4 characters in the rest portion. While this may be guaranteed by the @polkadot/util library, it's not explicitly validated here.

  2. Line 38: The constant K_LENGTH = 3 + 1 is unclear. Consider renaming to something more descriptive like SI_UNIT_THRESHOLD to clarify its purpose (threshold for switching to SI unit formatting).

🔎 Suggested improvements

Add validation for slice operation:

   if (prefix.length > K_LENGTH) {
     const [major, rest] = formatBalance(value, { decimals, withSi: withSi ?? true, withUnit: false }).split('.');
     const minor = rest.slice(0, decimalPoint);
-    const unit = rest.slice(4);
+    const unit = rest?.slice(4) ?? '';

     return <>{major}.<span>{minor}</span><span style={{ color: tokenColor ?? 'inherit' }}>{unit}{unit ? unitPost : ` ${unitPost}`}</span>{labelPost || ''}</>;
   }

Improve constant naming:

-const K_LENGTH = 3 + 1;
+const SI_UNIT_THRESHOLD = 4;
packages/extension-polkagate/src/popup/history/explorer.ts-8-10 (1)

8-10: Multiple issues with Ethereum link generation.

  1. Type parameter ignored — The function accepts type: 'account' | 'extrinsic' but always returns a transaction URL (/tx/) for Ethereum, even when type === 'account'. Etherscan uses /address/ for accounts and /tx/ for transactions.

  2. Mainnet-only hardcoding — The implementation only supports Ethereum mainnet. Testnets like Sepolia require different subdomains (e.g., sepolia.etherscan.io).

  3. Limited EVM chain coverage — The PR objectives mention "comprehensive Ethereum/EVM support," but this only handles a chain named "ethereum". The codebase includes configurations for Polygon, Arbitrum, Optimism, Base, and other EVM chains in ETH_CHAIN_IDS.ts, yet none receive explorer link support.

  4. No data validation — The data parameter is concatenated directly without validation. Invalid or malformed data produces broken URLs.

packages/extension-polkagate/src/components/signerAdaptor.ts-26-66 (1)

26-66: Add error handling and input validation.

The methods lack error handling for:

  • Invalid transaction parameters (e.g., missing to address)
  • Network failures during provider calls
  • Signing failures
  • Invalid signature construction
  • Broadcast failures

This can lead to unclear error messages and difficult debugging for users.

🔎 Recommended error handling enhancements
   async signTransaction(tx: TransactionRequest): Promise<string> {
+    if (!tx.to && !tx.data) {
+      throw new Error('Transaction must have either a recipient address (to) or data');
+    }
+
+    try {
       const chainId = tx.chainId ?? 1;
       // ... rest of signing logic ...
       
       const sigBytes = this.keypair.sign(msgHash);
+      
+      if (!sigBytes || sigBytes.length !== 65) {
+        throw new Error(`Invalid signature length: expected 65 bytes, got ${sigBytes?.length || 0}`);
+      }
 
       const sig = new Uint8Array(sigBytes);
       // ... construct r, s, v ...
       
       return serialize(txUnsigned, sigTyped);
+    } catch (error) {
+      throw new Error(`Failed to sign transaction: ${error instanceof Error ? error.message : String(error)}`);
+    }
   }
 
   async sendTransaction(tx: TransactionRequest): Promise<TransactionResponse> {
+    try {
       const signedTx = await this.signTransaction(tx);
-
       return this.provider.broadcastTransaction(signedTx);
+    } catch (error) {
+      throw new Error(`Failed to send transaction: ${error instanceof Error ? error.message : String(error)}`);
+    }
   }
packages/extension-polkagate/src/components/signerAdaptor.ts-27-33 (1)

27-33: Replace hardcoded defaults with network-fetched values.

The current implementation uses hardcoded fallback values that will cause transaction failures or suboptimal behavior:

  1. nonce ?? 0 (line 28): Using nonce 0 will fail if the account has sent any prior transactions. The nonce must be fetched from the network.
  2. gasPrice ?? ethers.parseUnits('10', 'gwei') (line 29): 10 gwei is arbitrary and may be too high (wasting funds) or too low (causing transaction delays). Should fetch current gas price.
  3. gasLimit ?? 21000 (line 30): 21000 is only correct for simple ETH transfers. Contract interactions and token transfers require gas estimation.
  4. chainId ?? 1 (line 27): Defaulting to mainnet (chainId 1) is dangerous—users could accidentally broadcast test transactions to mainnet.
🔎 Proposed fix using provider for dynamic values
   async signTransaction(tx: TransactionRequest): Promise<string> {
-    const chainId = tx.chainId ?? 1;
-    const nonce = tx.nonce ?? 0;
-    const gasPrice = BigInt(tx.gasPrice ?? ethers.parseUnits('10', 'gwei'));
-    const gasLimit = BigInt(tx.gasLimit ?? 21000);
+    const network = await this.provider.getNetwork();
+    const chainId = tx.chainId ?? Number(network.chainId);
+    
+    const address = this.getAddress();
+    const nonce = tx.nonce ?? await this.provider.getTransactionCount(address, 'pending');
+    
+    const feeData = await this.provider.getFeeData();
+    const gasPrice = BigInt(tx.gasPrice ?? feeData.gasPrice ?? ethers.parseUnits('10', 'gwei'));
+    
+    // Estimate gas if not provided and we have a valid 'to' address
+    let gasLimit = BigInt(tx.gasLimit ?? 21000);
+    if (!tx.gasLimit && tx.to) {
+      try {
+        gasLimit = await this.provider.estimateGas(tx);
+      } catch {
+        gasLimit = 21000n; // fallback for simple transfers
+      }
+    }
+    
     const to = tx.to?.toString() ?? undefined;

Note: Consider throwing an error if critical parameters like to are missing instead of allowing undefined values.

package.json-60-62 (1)

60-62: Replace @ethersproject v5 imports with ethers v6 equivalents to eliminate version mixing.

The code in signerAdaptor.ts directly imports utilities from @ethersproject/bytes, @ethersproject/keccak256, and @ethersproject/transactions (all v5) while also using ethers v6. According to ethers v6 documentation, all functionality from these v5 sub-packages has been consolidated into the main ethers v6 package with updated APIs:

  • arrayify() from @ethersproject/bytes → available from ethers
  • keccak256() from @ethersproject/keccak256 → available from ethers
  • serialize() and UnsignedTransaction from @ethersproject/transactions → available from ethers

Migrate these imports to use ethers v6 directly and remove the v5 sub-packages from dependencies to avoid code duplication, potential bundle bloat, and API inconsistencies. See the ethers v6 migration guide for renamed utilities and updated API signatures.

packages/extension-polkagate/src/hooks/useEthSigner.ts-13-17 (1)

13-17: Hook is non-functional and always returns undefined.

The hook initializes a signer state but never updates it, meaning it will always return undefined. All signer resolution logic is commented out (lines 18-47).

This is acceptable for WIP but must be addressed before merging:

  1. Either implement the commented-out logic
  2. Or add a clear TODO/FIXME comment explaining this is a placeholder
  3. Ensure dependent code gracefully handles undefined signer

Given the PR is marked WIP, please ensure this hook is fully functional before marking the PR ready for review.

Also applies to: 49-50

packages/extension-polkagate/src/hooks/useEthSigner.ts-18-47 (1)

18-47: Remove or implement commented-out code before merging.

Large blocks of commented code reduce readability and should be avoided in production code.

Please either:

  1. Implement and uncomment this logic when ready
  2. Remove it entirely (git history preserves the code if needed later)
  3. Replace with clear TODO comments explaining what needs to be implemented

Additionally, remove debugging console.log statements (lines 23, 27, 41, 46) before merging, as they should not appear in production code.

packages/extension-polkagate/src/hooks/useEthSigner.ts-4-4 (1)

4-4: Fix the import path and remove //@ts-nocheck.

The import statement at line 10 uses an incorrect path: '../util/getEthProvider' should be '../util/evmUtils/getEthProvider'. This module resolution error is likely why //@ts-nocheck was added. Fix the import path instead of suppressing type checking. Also remove the unused Wallet import on line 7.

packages/extension-polkagate/src/util/api/getEtherscan.ts-15-15 (1)

15-15: Add validation for missing API key.

The ETHERSCAN_API_KEY is retrieved from process.env without validation. If the environment variable is not set, the API calls will fail with unclear error messages. Consider adding validation and providing a helpful error message.

🔎 Proposed fix
 const ETHERSCAN_API_KEY = process.env['ETHERSCAN_API_KEY'];
+
+if (!ETHERSCAN_API_KEY) {
+  console.warn('ETHERSCAN_API_KEY not configured. Etherscan API calls will fail.');
+}
replacements/interfaces.js-54-73 (1)

54-73: Incorrect prefix for Goerli testnet.

Line 56 assigns prefix: 5 to the Goerli testnet, but Ethereum testnets should use the same SLIP-44 coin type as Ethereum mainnet, which is 60. Having different prefixes for mainnet and testnets could cause issues with address derivation and network identification.

🔎 Proposed fix
 const evmTestChains = [
     {
-        prefix: 5, // Goerli testnet
+        prefix: 60, // Ethereum SLIP-44 coin type
         network: "goerli",
         displayName: "Goerli Testnet",
         symbols: ["ETH"],
         decimals: [18],
         standardAccount: "0x",
         website: "https://goerli.net/#about"
     },
     {
         prefix: 60, // Sepolia testnet
         network: "sepolia",
         displayName: "Sepolia Testnet",
         symbols: ["ETH"],
         decimals: [18],
         standardAccount: "0x",
         website: "https://sepolia.dev"
     }
 ];
packages/extension-polkagate/src/util/api/getEtherscan.ts-47-72 (1)

47-72: Hardcoded ETH symbol may not work for all EVM chains.

Line 56 hardcodes asset_symbol: 'ETH', but this function is intended to support multiple EVM chains (as evidenced by the EVM_CHAIN_ID_MAP). Chains like BNB, Polygon, and Avalanche have different native tokens (BNB, MATIC, AVAX). Consider passing the token symbol as a parameter or deriving it from chain info.

🔎 Proposed fix
-const convertTxToPolkaGateStyle = (transfers: EtherscanTx[], decimal: number | undefined) => {
+const convertTxToPolkaGateStyle = (transfers: EtherscanTx[], decimal: number | undefined, token: string | undefined) => {
   return transfers.map(({ blockNumber, from, functionName, gasPrice, gasUsed, hash, isError, nonce, timeStamp, to, transactionIndex, value }) => {
     const _gasUsed = new BN(gasUsed);
     const _gasPrice = new BN(gasPrice);

     const feeWei = _gasUsed.mul(_gasPrice);

     return {
       amount: amountToHuman(value, decimal),
-      asset_symbol: 'ETH',
+      asset_symbol: token || 'ETH',
       block_num: Number(blockNumber),

Then update the caller at line 118:

-        transfers: convertTxToPolkaGateStyle(data.result, decimal)
+        transfers: convertTxToPolkaGateStyle(data.result, decimal, chainInfo?.coin)

Committable suggestion skipped: line range outside the PR's diff.

packages/extension-polkagate/src/util/workers/shared-helpers/getAssetOnRelayChain.js-21-21 (1)

21-21: Avoid mutating the input addresses array.

The current implementation mutates the input parameter directly, which is a side effect that could surprise callers. Consider creating a new array instead.

🔎 Proposed fix
-    addresses = addresses.map((addr) => isEthereumAddress(addr) ? evmToAddress(addr) : addr);
+    const normalizedAddresses = addresses.map((addr) => isEthereumAddress(addr) ? evmToAddress(addr) : addr);

Then update line 23 to use normalizedAddresses instead of addresses:

-    const { api, balanceInfo, connectionsToBeClosed } = await getBalances(chainName, addresses, userAddedEndpoints, port) ?? {};
+    const { api, balanceInfo, connectionsToBeClosed } = await getBalances(chainName, normalizedAddresses, userAddedEndpoints, port) ?? {};

Committable suggestion skipped: line range outside the PR's diff.

packages/extension-polkagate/src/hooks/useFetchAssetsOnChains.ts-88-105 (1)

88-105: Remove debug console.log statement.

The address splitting logic is correct, but Line 95 contains a debug console.log statement that should be removed before merging to production.

🔎 Proposed fix
    if (evmAddresses?.length && isEvmChain) {
-     console.log(' to fetch evm chains balances:', evmAddresses?.length)
      const chainName = getChainName(genesisHash, genesisOptions);
packages/extension-polkagate/src/fullscreen/sendFund/index.tsx-52-78 (1)

52-78: Several issues in ETH fee calculation effect.

  1. Bug on Line 55: The condition !RecipientAddress checks if the imported component is falsy, which will always be truthy. This appears to be a mistake - perhaps you intended to check !to (recipient address value) instead?

  2. ethFeeRef never resets: Once ethFeeRef.current is set to true on Line 63, it's never reset. This means the ETH fee will only be calculated once per component mount. If the user changes amount, recipient, or token, the fee won't be recalculated.

  3. @ts-ignore suppresses type errors: The type mismatch on Line 72 should be addressed properly rather than suppressed.

🔎 Proposed fix
 useEffect(() => {
   const { amount, fee, recipientAddress: to, token } = inputs ?? {};

-  if (ethFeeRef.current || fee || !address || !chainName || !isEthereumAddress(address) || !amount || !to || !token || !RecipientAddress) {
+  if (fee?.originFee || !address || !chainName || !isEthereumAddress(address) || !amount || !to || !token) {
     return;
   }

   if (Number(amount) <= 0) {
     return;
   }

-  ethFeeRef.current = true;
-
   getEthFee({
     chainName,
     from: address,
     to,
     token,
     value: amount
   }).then((res) => {
-    //@ts-ignore
-    res && setInputs((prev) => ({
+    res && setInputs((prev: Inputs | undefined) => ({
       ...(prev || {}),
-      fee: { originFee: res }
+      fee: { originFee: res as FeeInfo }
     }));
   }).catch(console.error);
 }, [address, chainName, inputs]);

Note: You may need to import or define the proper type for FeeInfo to match the expected structure.

Committable suggestion skipped: line range outside the PR's diff.

🟡 Minor comments (13)
packages/extension-polkagate/src/themes/dark.ts-40-40 (1)

40-40: Remove the unused warning.light color or implement its usage.

The warning.light property at line 40 is not referenced anywhere in the codebase. Additionally, the light theme variant in light.ts does not include a light property for the warning palette, creating an inconsistency. Either remove this property or ensure it is actively used in components/styles before merging.

packages/extension-polkagate/src/components/FormatBalance2.tsx-51-56 (1)

51-56: Add defensive handling for postfix parameter.

The postfix parameter may be undefined in edge cases (e.g., if the input value doesn't contain a decimal point), but Line 53 uses it without optional chaining while Line 55 does use optional chaining.

🔎 Proposed fix for consistent null safety
 function createElement(prefix: string, postfix: string, unit: string, label: LabelPost = '', isShort = false, decimalPoint: number, tokenColor?: string | undefined): React.ReactNode {
-  const trimmedPostfix = postfix?.replace(/0+$/, '') || '';
-  const maybeTilde = postfix && parseFloat(trimmedPostfix) > parseFloat(trimmedPostfix.slice(0, decimalPoint)) ? '~' : '';
+  const trimmedPostfix = postfix?.replace(/0+$/, '') ?? '';
+  const maybeTilde = trimmedPostfix && parseFloat(trimmedPostfix) > parseFloat(trimmedPostfix.slice(0, decimalPoint)) ? '~' : '';

   return <>{`${maybeTilde}${prefix}${isShort ? '' : '.'}`}{!isShort && <span>{`00${postfix?.slice(0, decimalPoint) || ''}`.slice(-decimalPoint)}</span>}<span style={{ color: tokenColor ?? 'inherit' }}> {unit}</span>{label}</>;
 }
packages/extension/manifest.prod.json-2-3 (1)

2-3: Update the extension description to reflect EVM support.

The description currently mentions only "Polkadot/Substrate-based blockchains," but this PR adds comprehensive Ethereum/EVM support. The description should be updated to reflect both ecosystems.

🔎 Proposed fix
-  "description": "A user-friendly wallet for seamless browser-based interaction with Polkadot/Substrate-based blockchains.",
+  "description": "A user-friendly wallet for seamless browser-based interaction with Polkadot/Substrate and Ethereum/EVM-based blockchains.",
packages/extension/manifest.dev.json-2-3 (1)

2-3: Update the extension description to reflect EVM support.

The description currently mentions only "Polkadot/Substrate-based blockchains," but this PR adds comprehensive Ethereum/EVM support. The description should be updated to reflect both ecosystems.

🔎 Proposed fix
-  "description": "A user-friendly wallet for seamless browser-based interaction with Polkadot/Substrate-based blockchains.",
+  "description": "A user-friendly wallet for seamless browser-based interaction with Polkadot/Substrate and Ethereum/EVM-based blockchains.",
package.json-87-87 (1)

87-87: Update @web3icons/react to the latest stable version 4.1.12.

@web3icons/react@4.0.25 is outdated; the latest stable version is 4.1.12. Update this dependency to include the latest improvements and bug fixes.

react-devtools-core@7.0.1 is already at the latest stable version.

.env.example-1-1 (1)

1-1: Verify the EXTENSION_PREFIX value change.

The prefix changed from POLKAGATE to MY_EXTENSION, which appears to be a generic placeholder. For the PolkaGate extension, this should likely remain POLKAGATE or use a more project-appropriate value.

packages/extension-polkagate/src/util/evmUtils/ETH_CHAIN_IDS.ts-21-21 (1)

21-21: Fix typo in chain name.

Line 21 contains berachainBepolia which appears to be a typo. Based on the pattern of other testnet naming (e.g., sepolia, abstractSepolia), this should likely be berachainSepolia.

🔎 Proposed fix
-  berachainBepolia: 80069,
+  berachainSepolia: 80069,
packages/extension-polkagate/src/util/workers/shared-helpers/getNFTs.js-239-245 (1)

239-245: Fix the empty array check.

The condition !filteredAddresses only catches null or undefined, but filter() always returns an array. If all addresses are Ethereum addresses, filteredAddresses will be an empty array [], which is truthy, and the function will proceed to call getNFTs([]) unnecessarily.

🔎 Proposed fix
  const filteredAddresses = addresses?.filter((addr) => !isEthereumAddress(addr));

-  if (!filteredAddresses) {
+  if (!filteredAddresses || filteredAddresses.length === 0) {
    console.warn('Shared worker, No addresses to NFTs');

    return port.postMessage(JSON.stringify({ functionName: NFT_FUNCTION_NAME, results: undefined }));
  }
packages/extension-polkagate/src/util/evmUtils/constantsEth.ts-62-68 (1)

62-68: Inconsistent spelling: "goerli" here vs "goreli" elsewhere.

Line 62 uses "goerli" (correct) while lines 9 and 14 use "goreli" (incorrect). This will cause lookup failures when trying to match chain names to ERC20 tokens.

packages/extension-polkagate/src/util/getLogo2.ts-66-66 (1)

66-66: Inconsistent spelling: "goerli" vs "goreli".

This array uses "goerli" (correct spelling), but in constantsEth.ts the genesis hash map uses "goreli" (misspelled). This inconsistency will cause lookups to fail when matching chain names.

packages/extension-polkagate/src/util/evmUtils/constantsEth.ts-9-16 (1)

9-16: Fix misspelled Goerli network name and incorrect genesis hash.

The Goerli testnet is misspelled as "goreli" on lines 9 and 14, conflicting with the correct spelling "goerli" used on line 62 and in getLogo2.ts line 66. Additionally, the current genesis hash is incorrect; the correct Goerli genesis hash is 0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a.

Suggested fix
-const GORELI_GENESISHASH = '0x5fbe6b8f0e9b91f8c5c88ee6f7a3b7f7d05c1e71f6e4c4c1c6e8c123456789ab';
+const GOERLI_GENESISHASH = '0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a';

 export const EVM_GENESISHASH_MAP: Record<string, string> = {
   ethereum: ETHEREUM_GENESISHASH,
-  goreli: GORELI_GENESISHASH,
+  goerli: GOERLI_GENESISHASH,
   sepolia: SEPOLIA_GENESISHASH
 };

 export const EVM_TEST_CHAINS_GENESISHASH = [
-  GORELI_GENESISHASH,
+  GOERLI_GENESISHASH,
   SEPOLIA_GENESISHASH
 ];

Committable suggestion skipped: line range outside the PR's diff.

packages/extension-polkagate/src/fullscreen/sendFund/useParaSpellFeeCall.ts-102-108 (1)

102-108: Remove or justify the @ts-ignore directive.

The @ts-ignore comment on line 102 suggests a type mismatch that's being suppressed rather than resolved. This can mask real type safety issues.

Please either:

  1. Fix the underlying type issue by ensuring ParaspellFees correctly types the originFee property
  2. Use @ts-expect-error with a comment explaining why the type mismatch is expected and safe
  3. Adjust the fallback structure to match the expected type
🔎 Investigation needed
// Current code suppresses the error:
// @ts-ignore
setParaSpellState((pre) => ({
  ...(pre || {}),
  paraSpellFee: {
    originFee: { fee: BN_ZERO }
  }
}));

Based on lines 92-96, the expected structure is:

paraSpellFee: {
  destinationFee: info.destination.xcmFee,
  originFee: info.origin.xcmFee
}

The fallback only provides originFee without destinationFee, which may cause issues downstream.

packages/extension-polkagate/src/util/workers/shared-helpers/getAssetOnEvm.js-68-77 (1)

68-77: Zero balances are incorrectly skipped.

Lines 75-77 skip processing when balance is falsy, which includes zero balances (0n in bigint):

if (!balance) {
  return;
}

This means accounts with zero token balances won't have those tokens listed in their assets, which could confuse users or break UI assumptions.

🔎 Proposed fix
 try {
   balance = await tokenContract['balanceOf'](addr);
 } catch (err) {
   console.warn(`Failed to fetch balanceOf for ERC-20 ${token} for ${addr} on ${chainName}:`, err?.message || err);

   return;
 }

-if (!balance) {
+if (balance === null || balance === undefined) {
   return;
 }

This allows zero balances through while still filtering out failed fetches.

Comment on lines +40 to +49
function getFormat(decimals: number[], tokens: string[], formatIndex = 0): [number, string] {
return [
formatIndex < decimals.length
? decimals[formatIndex]
: decimals[0],
formatIndex < tokens.length
? tokens[formatIndex]
: tokens[1]
];
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix array bounds issue in fallback logic.

Lines 44 and 47 have potential array access issues:

  • Line 44: Assumes decimals array is non-empty when accessing decimals[0]
  • Line 47: Assumes tokens array has at least 2 elements when accessing tokens[1]

If tokens has only one element, tokens[1] will be undefined, causing incorrect rendering or runtime errors.

🔎 Proposed fix
 function getFormat(decimals: number[], tokens: string[], formatIndex = 0): [number, string] {
   return [
     formatIndex < decimals.length
       ? decimals[formatIndex]
       : decimals[0],
     formatIndex < tokens.length
       ? tokens[formatIndex]
-      : tokens[1]
+      : tokens[0]
   ];
 }

Alternatively, add validation to ensure arrays have the expected minimum length:

function getFormat(decimals: number[], tokens: string[], formatIndex = 0): [number, string] {
  if (decimals.length === 0 || tokens.length === 0) {
    throw new Error('decimals and tokens arrays must not be empty');
  }
  
  return [
    formatIndex < decimals.length ? decimals[formatIndex] : decimals[0],
    formatIndex < tokens.length ? tokens[formatIndex] : tokens[0]
  ];
}
🤖 Prompt for AI Agents
In @packages/extension-polkagate/src/components/FormatBalance2.tsx around lines
40 - 49, The getFormat function may access decimals[0] or tokens[1] when the
arrays are shorter than expected; validate that decimals and tokens are
non-empty at the start of getFormat (or throw a clear error) and change the
token fallback to use tokens[0] instead of tokens[1]; ensure the returned tuple
uses formatIndex if in-range otherwise decimals[0] and tokens[0] as safe
fallbacks to avoid undefined accesses.

Comment on lines +58 to +63
function splitFormat(value: string, decimalPoint: number, label?: LabelPost, isShort?: boolean): React.ReactNode {
const [prefix, postfixFull] = value.split('.');
const [postfix, unit] = postfixFull.split(' ');

return createElement(prefix, postfix, unit, label, isShort, decimalPoint);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add validation for expected string format.

Lines 59-60 assume value contains both a '.' and a ' ' character, but no validation is performed:

  • If value has no '.', postfixFull will be undefined
  • Calling .split(' ') on undefined will cause a runtime error
🔎 Proposed fix with validation
 function splitFormat(value: string, decimalPoint: number, label?: LabelPost, isShort?: boolean): React.ReactNode {
   const [prefix, postfixFull] = value.split('.');
+  
+  if (!postfixFull) {
+    // Handle case where value doesn't contain decimal point
+    return <>{prefix}{label}</>;
+  }
+  
   const [postfix, unit] = postfixFull.split(' ');

   return createElement(prefix, postfix, unit, label, isShort, decimalPoint);
 }
🤖 Prompt for AI Agents
In @packages/extension-polkagate/src/components/FormatBalance2.tsx around lines
58 - 63, splitFormat assumes value contains both '.' and a space which can cause
a runtime error when postfixFull is undefined; add validation in splitFormat to
safely handle malformed input by checking that value includes a '.' before
splitting and that postfixFull is defined before calling split(' '), providing
sensible fallbacks (e.g., postfix = '' and unit = '') or returning a safe
default React node; ensure createElement(prefix, postfix, unit, label, isShort,
decimalPoint) always receives defined strings and preserve existing behavior for
valid inputs.

Comment on lines +142 to +147
console.log('Signature:', signature);
console.log('payload', payload);
console.log('signer', from);
const check = signatureVerify(String(payload), signature, from);

console.log('signatureVerify result:', check);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Signature verification result is ignored, and type conversion is incorrect.

The code computes a signature verification but never uses the result. The transaction proceeds to extrinsic.addSignature() on line 149 regardless of whether verification passes or fails, making this check ineffective.

Additionally, the type conversion String(payload) is incorrect when payload is of type ExtrinsicPayloadValue (an object). Converting an object with String() produces "[object Object]", which will always fail verification. The payload parameter has the union type Uint8Array | ExtrinsicPayloadValue | HexString, so this code path will produce incorrect results for object payloads.

Security & debugging concerns:

  • Logging the signature and payload (lines 142-143) exposes sensitive cryptographic material in console logs.
  • These changes appear unrelated to the PR objective (EVM chain support) and may be leftover debug code.
🔎 Proposed resolution

If signature verification is intended: Check the result and handle failures appropriately:

-    console.log('✈️ Sending the transaction ...');
-    console.log('Signature:', signature);
-    console.log('payload', payload);
-    console.log('signer', from);
-    const check = signatureVerify(String(payload), signature, from);
-
-    console.log('signatureVerify result:', check);
+    console.log('✈️ Sending the transaction ...');
+    
+    // Verify signature before submission
+    const payloadToVerify = payload instanceof Uint8Array ? payload : 
+                           typeof payload === 'string' ? payload : 
+                           extrinsic.toU8a();
+    const check = signatureVerify(payloadToVerify, signature, from);
+    
+    if (!check.isValid) {
+      console.error('❌ Invalid signature detected');
+      resolve({ block: 0, failureText: 'Invalid signature', fee: '', success: false, txHash: '' });
+      return;
+    }

If this is debug code: Remove the verification and sensitive logging entirely:

     console.log('✈️ Sending the transaction ...');
-    console.log('Signature:', signature);
-    console.log('payload', payload);
-    console.log('signer', from);
-    const check = signatureVerify(String(payload), signature, from);
-
-    console.log('signatureVerify result:', check);
 
     extrinsic.addSignature(from, signature, payload);

Committable suggestion skipped: line range outside the PR's diff.

}

return found;
return found as unknown as NetworkInfo;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Unsafe type assertion returns undefined as NetworkInfo.

The type assertion as unknown as NetworkInfo forces TypeScript to accept undefined as NetworkInfo when no matching chain is found. This violates type safety and can cause runtime errors in calling code that expects a valid NetworkInfo object.

🔎 Proposed fix
-  return found as unknown as NetworkInfo;
+  return found;

The function signature already declares NetworkInfo | undefined as the return type, so the type assertion is unnecessary. Simply return found directly.

📝 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.

Suggested change
return found as unknown as NetworkInfo;
return found;
🤖 Prompt for AI Agents
In @packages/extension-polkagate/src/util/getChain.ts at line 22, The return
uses an unsafe assertion "found as unknown as NetworkInfo" allowing undefined to
be treated as NetworkInfo; instead return the variable found directly (remove
the double-cast) so the function respects its declared return type NetworkInfo |
undefined—locate the found variable and the return statement in getChain (the
line returning found as unknown as NetworkInfo) and replace it with a plain
return found.

Comment on lines +46 to +52
const polkagateAddedGenesisHashes = {
ethereum: '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3',
goerli: '0x5fbe6b8f0e9b91f8c5c88ee6f7a3b7f7d05c1e71f6e4c4c1c6e8c123456789ab',
sepolia: '0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9'
};

const assetHubs = [{
"prefix": 42,
"network": "westmint",
"displayName": "Westend Asset Hub",
"symbols": ["WND"],
"decimals": [12],
"standardAccount": "*25519",
"website": "https://polkadot.network"
},
{
"prefix": 2,
"network": "statemine",
"displayName": "Kusama Asset Hub",
"symbols": ["KSM"],
"decimals": [12],
"standardAccount": "*25519",
"website": "https://kusama.network"
},
{
"prefix": 0,
"network": "statemint",
"displayName": "Polkadot Asset Hub",
"symbols": ["DOT"],
"decimals": [10],
"standardAccount": "*25519",
"website": "https://polkadot.network"
},
{
"prefix": 0,
"network": "paseoAssetHub",
"displayName": "Paseo Asset Hub",
"symbols": ["PAS"],
"decimals": [10],
"standardAccount": "*25519",
"website": "https://polkadot.network"
}];
Object.entries(polkagateAddedGenesisHashes).forEach(([key, hash]) => (knownGenesis[key] = [hash]));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Ethereum mainnet genesis hash block 0

💡 Result:

Ethereum mainnet genesis (block 0) hash: 0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3. [1][2]

Genesis block mined: July 30, 2015. To fetch locally: eth.getBlock(0). [2][3]

Sources:

  • Ethereum genesis/hash discussion. [1]
  • Ethereum Foundation blog / genesis details. [2]
  • Example showing eth.getBlock(0) output. [3]

🌐 Web query:

Goerli testnet genesis hash block 0

💡 Result:

Goerli (Görli) genesis block (block 0) hash: 0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a. [1]

Source: eth-clients Goerli testnet metadata. [1]


🌐 Web query:

Sepolia testnet genesis hash block 0

💡 Result:

Do you mean the on-chain block 0 hash (genesis block hash) for Sepolia, or the genesis.json file contents (e.g., the chain genesis configuration)?


🌐 Web query:

Sepolia testnet block 0 genesis hash 0x25a5cc

💡 Result:

Short answer: No — Sepolia's genesis (block 0) is not 0x25a5cc.

Authoritative values from the eth-clients sepolia repo:

  • Eth1 deposit block hash (used in genesis eth1_data.block_hash): 0x491ebac1b7f9c0eb426047a495dc577140cb3e09036cd3f7266eda86b635d9fa. [1]
  • Beacon-chain initial state root / genesis state (Initial State Root): 0xfb9afe32150fa39f4b346be2519a67e2a4f5efcd50a1dc192c3f6b3d013d2798. [1]
  • Genesis block root (two variants shown in repo): 0xeade62f0457b2fdf48e7d3fc4b60736688286be7c7a3ac4c9a16a5e0600bd9e4 (without state-root update) and 0xfb9b64fe445f76696407e1e3cc390371edff147bf712db86db6197d4b31ede43 (with state-root update). [1]

Source: eth-clients/sepolia GitHub repository (Sepolia testnet configuration). [1]

If you want, I can look up the exact block-0 record on a block explorer (Etherscan/Sepolia scan) and paste its displayed hash.


🌐 Web query:

Sepolia chain ID genesis block hash explorers

💡 Result:

Sources: [1] sepolia.dev (Sepolia resources), [2] Chain-List / Alchemy Sepolia pages, [3] Alchemy overview for Sepolia.


Fix incorrect Goerli genesis hash.

The Goerli genesis hash is incorrect. The code contains 0x5fbe6b8f0e9b91f8c5c88ee6f7a3b7f7d05c1e71f6e4c4c1c6e8c123456789ab, but the correct Goerli genesis hash (block 0) is 0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a. This incorrect value will cause Goerli chain identification to fail. The Ethereum mainnet and Sepolia hashes are correct.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI Agents
In @package.json:
- Around line 60-62: package.json lists @ethersproject/bytes,
@ethersproject/keccak256, and @ethersproject/transactions at v5.8.0 while the
repo uses ethers v6.15.0, causing v5/v6 conflicts; choose one strategy:
(recommended) remove the three "@ethersproject/*" dependencies and update code
to import utilities (e.g., keccak256, byte helpers) from "ethers" v6, or
alternatively downgrade "ethers" to a v5.x release to keep the @ethersproject v5
packages; update package.json accordingly and adjust all imports that reference
'@ethersproject/keccak256', '@ethersproject/bytes',
'@ethersproject/transactions' to the appropriate "ethers" v6 APIs (or to
v5-compatible imports if you downgrade).
🧹 Nitpick comments (1)
package.json (1)

50-50: Consider using dev manifest for watch mode.

The watch script copies the production manifest before starting webpack watch. Since watch mode is typically used during development, consider using copy-dev-manifest instead to ensure the development manifest is active.

🔎 Proposed fix
-    "watch": "yarn copy-prod-manifest && cd packages/extension && yarn polkadot-exec-webpack --config webpack.watch.cjs --mode development --watch"
+    "watch": "yarn copy-dev-manifest && cd packages/extension && yarn polkadot-exec-webpack --config webpack.watch.cjs --mode development --watch"
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a8d9e58 and ab4180f.

📒 Files selected for processing (1)
  • package.json
⏰ 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). (2)
  • GitHub Check: Analyze (javascript)
  • GitHub Check: build
🔇 Additional comments (1)
package.json (1)

60-62: Verify dependency versions and security advisories.

Ensure the added dependencies are using secure, compatible versions and check for any known vulnerabilities:

  • @ethersproject/bytes@^5.8.0
  • @ethersproject/keccak256@^5.8.0
  • @ethersproject/transactions@^5.8.0
  • @web3icons/react@^4.0.25
  • ethers@^6.15.0
  • react-devtools-core@^7.0.1
#!/bin/bash
# Verify latest versions and security advisories for new dependencies

echo "=== Checking latest versions ==="
for pkg in "@ethersproject/bytes" "@ethersproject/keccak256" "@ethersproject/transactions" "ethers" "@web3icons/react" "react-devtools-core"; do
  echo "Package: $pkg"
  npm view "$pkg" version 2>/dev/null || echo "  Could not fetch version"
done

echo -e "\n=== Checking for security advisories ==="
for pkg in "ethersproject/bytes" "ethersproject/keccak256" "ethersproject/transactions" "ethers" "web3icons/react" "react-devtools-core"; do
  echo "Checking: $pkg"
  gh api graphql -f query="
  {
    securityVulnerabilities(first: 5, ecosystem: NPM, package: \"${pkg}\") {
      nodes {
        advisory {
          summary
          severity
          publishedAt
        }
        vulnerableVersionRange
        firstPatchedVersion {
          identifier
        }
      }
    }
  }" 2>/dev/null | jq -r '.data.securityVulnerabilities.nodes[] | "  [\(.advisory.severity)] \(.advisory.summary) | Vulnerable: \(.vulnerableVersionRange) | Patched: \(.firstPatchedVersion.identifier // "N/A")"' || echo "  No advisories found or error occurred"
done

Also applies to: 87-87, 96-96, 138-138

Comment on lines +60 to +62
"@ethersproject/bytes": "^5.8.0",
"@ethersproject/keccak256": "^5.8.0",
"@ethersproject/transactions": "^5.8.0",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Resolve ethers version conflict.

The project is mixing ethers v6.15.0 with @ethersproject/* packages at v5.8.0. Ethers v6 is a complete rewrite that internalized all @ethersproject packages with breaking changes. Using both versions simultaneously creates:

  • Duplicate functionality and increased bundle size
  • API inconsistencies (v5 vs v6 have different interfaces)
  • Potential runtime conflicts
  • Developer confusion about which version to import

Choose one strategy:

  • Option 1 (recommended): Use ethers v6 exclusively and remove the @ethersproject/* v5 packages. Ethers v6 includes all necessary utilities internally.
  • Option 2: Use ethers v5 alongside @ethersproject/* v5 packages (requires downgrading ethers to v5.x).
🔎 Recommended fix (Option 1)
-    "@ethersproject/bytes": "^5.8.0",
-    "@ethersproject/keccak256": "^5.8.0",
-    "@ethersproject/transactions": "^5.8.0",
     "@lottiefiles/dotlottie-react": "^0.17.5",

Then import from ethers v6 instead:

// Before (v5):
import { keccak256 } from '@ethersproject/keccak256';
import { arrayify } from '@ethersproject/bytes';

// After (v6):
import { keccak256, getBytes } from 'ethers';

Also applies to: 96-96

🤖 Prompt for AI Agents
In @package.json around lines 60 - 62, package.json lists @ethersproject/bytes,
@ethersproject/keccak256, and @ethersproject/transactions at v5.8.0 while the
repo uses ethers v6.15.0, causing v5/v6 conflicts; choose one strategy:
(recommended) remove the three "@ethersproject/*" dependencies and update code
to import utilities (e.g., keccak256, byte helpers) from "ethers" v6, or
alternatively downgrade "ethers" to a v5.x release to keep the @ethersproject v5
packages; update package.json accordingly and adjust all imports that reference
'@ethersproject/keccak256', '@ethersproject/bytes',
'@ethersproject/transactions' to the appropriate "ethers" v6 APIs (or to
v5-compatible imports if you downgrade).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @packages/extension-polkagate/src/util/evmUtils/constantsEth.ts:
- Around line 49-69: The ERC20_TOKENS object has a naming inconsistency for the
Goerli testnet key (mistyped as "goreli"); update the key to "goerli" so it
matches references elsewhere (e.g., EVM_GENESISHASH_MAP,
getLogo2/getEthProvider) and ensure the nested entry remains USDC with its
existing contract, icon, and empty priceId; verify no other occurrences of
"goreli" remain in ERC20_TOKENS or related constants.
- Line 9: The constant name GORELI_GENESISHASH is misspelled and its value is an
invalid placeholder; rename the identifier to GOERLI_GENESISHASH and replace its
value with the official Goerli genesis hash
0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a so chain
identification and lookups (e.g., where ERC20_TOKENS uses the goerli key)
resolve correctly.
🧹 Nitpick comments (2)
packages/extension-polkagate/src/util/evmUtils/constantsEth.ts (2)

34-39: Consider whether additional ERC20 methods are needed.

The ABI includes the essential methods for balance queries and basic transfers. However, standard ERC20 tokens also include approve, transferFrom, and allowance methods which enable delegated transfers and are commonly used in DeFi interactions.

If the extension will support more complex ERC20 interactions beyond simple transfers, consider expanding the ABI.

📝 Extended ERC20 ABI (if needed)
 export const ERC20_ABI = [
   'function balanceOf(address) view returns (uint256)',
   'function decimals() view returns (uint8)',
   'function symbol() view returns (string)',
+  'function name() view returns (string)',
+  'function totalSupply() view returns (uint256)',
+  'function allowance(address owner, address spender) view returns (uint256)',
+  'function approve(address spender, uint256 amount) returns (bool)',
   'function transfer(address to, uint256 amount) returns (bool)',
+  'function transferFrom(address from, address to, uint256 amount) returns (bool)'
 ];

41-45: Consider tightening the icon type for better type safety.

The icon property uses React.ComponentType<any> which is less type-safe than the React.ComponentType<{ size: number }> used in TOKEN_MAP.

🔧 Proposed type improvement
 interface TokenInfo {
   contract: string;
-  icon: React.ComponentType<any>;
+  icon: React.ComponentType<{ size: number }>;
   priceId: string;
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab4180f and 5674527.

📒 Files selected for processing (3)
  • packages/extension-polkagate/src/util/constants.ts
  • packages/extension-polkagate/src/util/evmUtils/constantsEth.ts
  • packages/extension-polkagate/src/util/workers/shared-helpers/getAssetOnRelayChain.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/extension-polkagate/src/util/workers/shared-helpers/getAssetOnRelayChain.js
🧰 Additional context used
🧬 Code graph analysis (1)
packages/extension-polkagate/src/util/constants.ts (1)
packages/extension-polkagate/src/util/evmUtils/constantsEth.ts (1)
  • ETHEREUM_TEST_CHAINS_GENESISHASH (18-21)
⏰ 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). (2)
  • GitHub Check: Analyze (javascript)
  • GitHub Check: build
🔇 Additional comments (7)
packages/extension-polkagate/src/util/constants.ts (4)

6-7: LGTM!

The import is correctly structured and the imported constant is properly used in TEST_NETS.


79-86: LGTM!

Good refactoring to extract Polkadot test nets into a named constant. This improves clarity and maintainability.


88-92: LGTM!

The composition of TEST_NETS correctly combines both Polkadot and Ethereum test networks to support the new EVM functionality.


348-353: LGTM!

The EVM entry follows the established pattern and integrates cleanly with the existing asset fetching function names.

packages/extension-polkagate/src/util/evmUtils/constantsEth.ts (3)

1-6: LGTM!

Imports are correctly structured with proper type-only import for React and icon components from @web3icons/react.


18-26: Array composition is correct, pending genesis hash fixes.

The structure and composition of these arrays is sound. However, the validity depends on resolving the GORELI_GENESISHASH issues flagged above.


28-32: LGTM!

The TOKEN_MAP is well-typed and correctly maps token symbols to their React icon components with the appropriate prop interface.

import { TokenETH, TokenUSDC, TokenUSDT } from '@web3icons/react';

export const ETHEREUM_GENESISHASH = '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3';
const GORELI_GENESISHASH = '0x5fbe6b8f0e9b91f8c5c88ee6f7a3b7f7d05c1e71f6e4c4c1c6e8c123456789ab';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's find and examine the file mentioned in the review
fd -t f "constantsEth.ts" -x cat -n {}

Repository: PolkaGate/extension

Length of output: 2443


🏁 Script executed:

# Search for all references to GORELI/GOERLI in the codebase
rg -in "goreli" --type ts | head -30

Repository: PolkaGate/extension

Length of output: 668


🏁 Script executed:

# Check the EVM_GENESISHASH_MAP and related code around lines 9-19
fd -t f "constantsEth.ts" -x sed -n '1,30p' {}

Repository: PolkaGate/extension

Length of output: 1063


🌐 Web query:

Goerli Ethereum testnet genesis hash official

💡 Result:

Official Goerli genesis block hash: 0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a. [1]

Sources:
[1] eth-clients/goerli (Goerli testnet configuration).


🌐 Web query:

Ethereum Goerli genesis block hash 0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a

💡 Result:

Yes — the Goerli testnet genesis block hash is 0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a. [1]

Sources:
[1] eth-clients/goerli (Goerli metadata, genesis hash).


🏁 Script executed:

# Check if there are any tests or configurations that validate these hash values
fd -t f -e ts -e json -e config | xargs rg -l "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3|0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9" | head -10

Repository: PolkaGate/extension

Length of output: 125


🏁 Script executed:

# Check how this constant is used in the codebase
rg -B3 -A3 "ETHEREUM_TEST_CHAINS_GENESISHASH|EVM_GENESISHASH_MAP" --type ts | head -50

Repository: PolkaGate/extension

Length of output: 3025


Fix the Goerli constant name and correct the invalid genesis hash.

Line 9 contains two critical issues:

  1. Misspelled constant name: GORELI_GENESISHASH is missing the 'e' — the correct spelling is GOERLI_GENESISHASH. This creates an inconsistency with line 62 where ERC20_TOKENS uses goerli as the key.

  2. Incorrect genesis hash: The value 0x5fbe6b8f0e9b91f8c5c88ee6f7a3b7f7d05c1e71f6e4c4c1c6e8c123456789ab is invalid. The pattern ending in 123456789ab appears to be a placeholder. The official Goerli genesis hash is 0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a.

This impacts chain identification logic and will cause failures when validating Goerli transactions or blocks.

Required fixes
-const GORELI_GENESISHASH = '0x5fbe6b8f0e9b91f8c5c88ee6f7a3b7f7d05c1e71f6e4c4c1c6e8c123456789ab';
+const GOERLI_GENESISHASH = '0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a';

Update line 14:

 export const EVM_GENESISHASH_MAP: Record<string, string> = {
   ethereum: ETHEREUM_GENESISHASH,
-  goreli: GORELI_GENESISHASH,
+  goerli: GOERLI_GENESISHASH,
   sepolia: SEPOLIA_GENESISHASH
 };

Update line 19:

 export const ETHEREUM_TEST_CHAINS_GENESISHASH = [
-  GORELI_GENESISHASH,
+  GOERLI_GENESISHASH,
   SEPOLIA_GENESISHASH
 ];

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @packages/extension-polkagate/src/util/evmUtils/constantsEth.ts at line 9,
The constant name GORELI_GENESISHASH is misspelled and its value is an invalid
placeholder; rename the identifier to GOERLI_GENESISHASH and replace its value
with the official Goerli genesis hash
0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a so chain
identification and lookups (e.g., where ERC20_TOKENS uses the goerli key)
resolve correctly.

Comment on lines +49 to +69
export const ERC20_TOKENS: ERC20_TOKENS_TYPE = {
ethereum: {
USDC: {
contract: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
icon: TokenUSDC,
priceId: 'usd-coin'
},
USDT: {
contract: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
icon: TokenUSDT,
priceId: 'tether'
}
},
goerli: {
USDC: {
contract: '0x07865c6e87b9f70255377e024ace6630c1eaa37f',
icon: TokenUSDC,
priceId: ''
}
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n packages/extension-polkagate/src/util/evmUtils/constantsEth.ts | head -80

Repository: PolkaGate/extension

Length of output: 2443


🌐 Web query:

What are the official Ethereum mainnet contract addresses for USDC and USDT tokens?

💡 Result:

  • USDC (Ethereum mainnet): 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48. [1][2]

  • USDT (Ethereum mainnet): 0xdAC17F958D2ee523a2206206994597C13D831ec7. [3]

Sources:
[1] Circle — USDC contract addresses.
[2] Etherscan — Circle: USDC token contract.
[3] Etherscan — Tether: USDT token contract.


🏁 Script executed:

# Check for references to goerli and goreli in the codebase
rg -ni "go[e]?rli" --type ts packages/extension-polkagate/src/

Repository: PolkaGate/extension

Length of output: 487


🌐 Web query:

What is the official Goerli testnet contract address for USDC token?

💡 Result:

The Goerli USDC contract address is: 0x07865c6E87B9F70255377e024ace6630C1Eaa37F. [1][2][3]

Sources:

  • Swing docs (lists USDC on Goerli). [1]
  • Zenland docs (lists Goerli USDC address). [2]
  • Payb.is supported-assets list (USDC-GOERLI). [3]

Fix goerli/goreli naming inconsistency.

The naming inconsistency is confirmed: Line 14 uses goreli: while line 62 uses goerli:. This must be corrected for consistency across the codebase, as other files (getLogo2.ts, getEthProvider.ts) reference goerli. Ensure the key in ERC20_TOKENS aligns with EVM_GENESISHASH_MAP.

All contract addresses are verified as correct:

  • USDC (mainnet): 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
  • USDT (mainnet): 0xdAC17F958D2ee523a2206206994597C13D831ec7
  • USDC (Goerli): 0x07865c6e87b9f70255377e024ace6630c1eaa37f

The empty priceId on line 66 for testnet USDC is intentional—testnet tokens lack market prices.

🤖 Prompt for AI Agents
In @packages/extension-polkagate/src/util/evmUtils/constantsEth.ts around lines
49 - 69, The ERC20_TOKENS object has a naming inconsistency for the Goerli
testnet key (mistyped as "goreli"); update the key to "goerli" so it matches
references elsewhere (e.g., EVM_GENESISHASH_MAP, getLogo2/getEthProvider) and
ensure the nested entry remains USDC with its existing contract, icon, and empty
priceId; verify no other occurrences of "goreli" remain in ERC20_TOKENS or
related constants.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/extension-polkagate/src/fullscreen/components/AccountListModal.tsx (1)

75-149: Fix chain filtering to avoid empty list and broken section headers
When chain is undefined, isEvmChain is undefined, so the current inline filter (isEvmChain !== (account.type === 'ethereum')) hides all accounts. Also, filtering inside the map breaks isFirstAccount/isLast and can suppress section headers, while NothingFound and the Apply button still rely on unfiltered counts. Consider filtering by chain in filteredCategorizedAccounts and only when chain type is known, then remove the inline if inside the map.

✅ Suggested fix (move chain filter into memo)
-  const filteredCategorizedAccounts = useMemo(() => {
-    if (!searchKeyword) {
-      return categorizedAccounts;
-    }
-
-    const keywordLower = searchKeyword.toLowerCase();
-
-    return Object.entries(categorizedAccounts).reduce((acc, [label, accounts]) => {
-      const filteredAccounts = accounts.filter((account) =>
-        account.name?.toLowerCase().includes(keywordLower) ||
-        account.address.toLowerCase().includes(keywordLower)
-      );
-
-      if (filteredAccounts.length > 0) {
-        acc[label] = filteredAccounts;
-      }
-
-      return acc;
-    }, {} as Record<string, AccountJson[]>);
-  }, [categorizedAccounts, searchKeyword]);
+  const filteredCategorizedAccounts = useMemo(() => {
+    const keywordLower = searchKeyword?.toLowerCase();
+    const hasChainType = !!chain?.definition?.chainType;
+
+    return Object.entries(categorizedAccounts).reduce((acc, [label, accounts]) => {
+      const chainFiltered = hasChainType
+        ? accounts.filter((account) => (account.type === 'ethereum') === isEvmChain)
+        : accounts;
+
+      const filteredAccounts = keywordLower
+        ? chainFiltered.filter((account) =>
+          account.name?.toLowerCase().includes(keywordLower) ||
+          account.address.toLowerCase().includes(keywordLower)
+        )
+        : chainFiltered;
+
+      if (filteredAccounts.length > 0) {
+        acc[label] = filteredAccounts;
+      }
+
+      return acc;
+    }, {} as Record<string, AccountJson[]>);
+  }, [categorizedAccounts, searchKeyword, isEvmChain, chain?.definition?.chainType]);
-                        {accounts.map((account, accIndex) => {
+                        {accounts.map((account, accIndex) => {
                           const isFirstProfile = profileIndex === 0;
                           const isFirstAccount = accIndex === 0;
                           const isLast = accIndex === accounts.length - 1;
-
-                          if (isEvmChain !== (account.type === 'ethereum')) {
-                            return null;
-                          }
packages/extension-polkagate/src/fullscreen/sendFund/index.tsx (1)

257-280: Add call: undefined to RESET_INPUTS to prevent stale transaction signing.

The call field is not cleared in the RESET_INPUTS object (index.tsx:109-118), but it persists in the Inputs state and is reused as a fallback at line 280. When sender or network changes, onReset is triggered (index.tsx:132-138), but it fails to clear the previous call, creating a window where the SignArea could use an outdated transaction before Step3Amount recomputes it.

Required fix
const RESET_INPUTS: Partial<Inputs> = {
  amount: undefined,
  amountAsBN: undefined,
  fee: undefined,
  feeInfo: undefined,
  paraSpellTransaction: undefined,
+ call: undefined,
  recipientAddress: undefined,
  recipientChain: undefined,
  recipientGenesisHashOrParaId: undefined
};
🤖 Fix all issues with AI agents
In `@packages/extension-polkagate/src/components/AddressInput.tsx`:
- Around line 57-75: When resetting or switching away from EVM addresses, clear
the previously set account type: update onReset (used to clear the input) to
also call setType?.(undefined) and include setType in its dependency array, and
update onSet to call setType?.('ethereum') when isEvm is true and call
setType?.(undefined) when isEvm is false so non‑EVM inputs don't leave the
parent stuck on 'ethereum'; reference the onReset and onSet callbacks and the
setType setter in your changes.
🧹 Nitpick comments (3)
packages/extension-polkagate/src/fullscreen/sendFund/partials/RecipientAddress.tsx (1)

33-42: Verify edge case when recipientChain.value is undefined.

The reset logic correctly clears the address when the chain changes. However, if inputs?.recipientChain?.value is undefined while genesisHash is defined, the condition evaluates to true and resets the address—even on initial render or before the recipient chain is set. Please verify this is the intended behavior.

Also, minor inconsistency: local state is set to null while recipientAddress is set to undefined. Consider aligning these for consistency:

♻️ Suggested consistency fix
   useEffect(() => {
     if (genesisHash && genesisHash !== inputs?.recipientChain?.value) {
-      setAddress(null);
+      setAddress(undefined);

       setInputs((prevInputs) => ({
         ...(prevInputs || {}),
         recipientAddress: undefined
       }));
     }
   }, [genesisHash, inputs?.recipientChain?.value, setInputs]);

,

packages/extension-polkagate/src/util/api/getTransfers.ts (1)

9-17: Avoid exporting a mutable singleton for nullObject.

Now that this is public, any consumer can mutate it; since getTxTransfers returns the same reference, a single mutation can taint all future results. Consider freezing and returning a clone (or expose a factory) to prevent shared-state bugs.

♻️ Proposed fix (immutable + clone)
-export const nullObject = {
-  code: 0,
-  data: {
-    count: 0,
-    transfers: null
-  },
-  generated_at: Date.now(),
-  message: 'Failed'
-} as unknown as TransferRequest;
+export const nullObject = Object.freeze({
+  code: 0,
+  data: Object.freeze({
+    count: 0,
+    transfers: null
+  }),
+  generated_at: Date.now(),
+  message: 'Failed'
+}) as Readonly<TransferRequest>;
+
+const cloneNullTransfer = (): TransferRequest => ({
+  ...nullObject,
+  data: { ...nullObject.data }
+});
-    return (await Promise.resolve(nullObject));
+    return (await Promise.resolve(cloneNullTransfer()));
-    return (await Promise.resolve(nullObject));
+    return (await Promise.resolve(cloneNullTransfer()));
packages/extension-polkagate/src/fullscreen/sendFund/index.tsx (1)

31-75: Consider removing the commented‑out ETH fee block or gating it.

Leaving large commented sections in production code makes the flow harder to reason about. If this is future work, consider a feature flag or a tracked TODO; otherwise, delete it.

🧹 Optional cleanup
-  // const ethFeeRef = useRef<boolean>(false);
...
-  // useEffect(() => {
-  //   const { amount, fee, recipientAddress: to, token } = inputs ?? {};
-  //   ...
-  // }, [address, chainName, inputs]);

Comment on lines +57 to +75
const onReset = useCallback((): void => {
setAddress?.(null);
setEnteredAddress(undefined);
setInvalidAddress(false);
}, [setAddress]);

const onSet = useCallback((value: string): void => {
const isEvm = isEthereumAddress(value);

isEvm && setType?.('ethereum');
const isValid = isValidAddress(value);

isValid
? setAddress?.(value)
: setAddress?.(undefined);

setEnteredAddress(value);
setInvalidAddress(!isValid);
}, [setAddress, setType]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Clear stale setType when input resets or switches away from EVM.

After an EVM address is entered, setType is never cleared. If the user clears the field or enters a non‑EVM address, the parent can remain stuck on ethereum, which can mis-route account‑type handling.

🛠️ Proposed fix
-  const onReset = useCallback((): void => {
-    setAddress?.(null);
-    setEnteredAddress(undefined);
-    setInvalidAddress(false);
-  }, [setAddress]);
+  const onReset = useCallback((): void => {
+    setAddress?.(null);
+    setEnteredAddress(undefined);
+    setInvalidAddress(false);
+    setType?.((prev) => (prev === 'ethereum' ? undefined : prev));
+  }, [setAddress, setType]);

   const onSet = useCallback((value: string): void => {
     const isEvm = isEthereumAddress(value);

-    isEvm && setType?.('ethereum');
+    setType?.((prev) => (isEvm ? 'ethereum' : prev === 'ethereum' ? undefined : prev));
     const isValid = isValidAddress(value);
🤖 Prompt for AI Agents
In `@packages/extension-polkagate/src/components/AddressInput.tsx` around lines 57
- 75, When resetting or switching away from EVM addresses, clear the previously
set account type: update onReset (used to clear the input) to also call
setType?.(undefined) and include setType in its dependency array, and update
onSet to call setType?.('ethereum') when isEvm is true and call
setType?.(undefined) when isEvm is false so non‑EVM inputs don't leave the
parent stuck on 'ethereum'; reference the onReset and onSet callbacks and the
setType setter in your changes.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@package.json`:
- Line 50: The "watch" npm script currently runs the production manifest copy
("copy-prod-manifest") which makes development runs inconsistent with "start"
and "build:dev"; update the "watch" script to use the development manifest step
(replace "copy-prod-manifest" with the dev manifest command or make it
conditional) so that the "watch" script aligns with NODE_ENV=development and
"build:dev" behavior; ensure you update the script named "watch" in package.json
and verify there is a corresponding dev manifest copy task (or add one) rather
than invoking "copy-prod-manifest".

In `@packages/extension-polkagate/src/popup/receive/Receive.tsx`:
- Around line 95-100: Add a useEffect in Receive.tsx that watches the networks
value and updates chainsToShow via setChainsToShow when networks change so the
dropdown reflects updated options from useGenesisHashOptions; e.g., inside the
Receive component add a useEffect(() => setChainsToShow(networks), [networks])
to keep chainsToShow in sync with networks (ensure this complements the existing
onSearch logic).

In `@packages/extension-polkagate/src/util/api/signAndSend.ts`:
- Line 16: The import signatureVerify from '@polkadot/util-crypto' in
signAndSend.ts is unused and causing a TS6133 build error; remove the unused
import statement (the "signatureVerify" specifier) from the import line in
packages/extension-polkagate/src/util/api/signAndSend.ts so the file only
imports what is actually used.
🧹 Nitpick comments (2)
packages/extension-polkagate/src/popup/receive/Receive.tsx (2)

93-94: Avoid mutating hook output with in‑place sort.

Array.sort() mutates allChains, which can produce unexpected ordering side effects. Prefer sorting a copy.

♻️ Suggested change
-  const networks = useMemo(() => allChains.sort(customSort), [allChains, customSort]);
+  const networks = useMemo(() => [...allChains].sort(customSort), [allChains, customSort]);

239-245: Remove or update the stale commented Ethereum auto‑select block.

It still references the old NetworkInfo shape, which can confuse future edits. Consider deleting or updating it to the new DropdownOption format.

"test": "polkadot-dev-run-test --silent --detectOpenHandles --testPathPattern=packages/extension-polkagate/src --maxWorkers=1",
"test:one": "polkadot-dev-run-test --detectOpenHandles",
"watch": "cd packages/extension && yarn polkadot-exec-webpack --config webpack.watch.cjs --mode development --watch"
"watch": "yarn copy-prod-manifest && cd packages/extension && yarn polkadot-exec-webpack --config webpack.watch.cjs --mode development --watch"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use the dev manifest for the watch flow.
start runs with NODE_ENV=development, but watch copies the prod manifest, which can mask dev-only permissions or URLs and makes dev behavior inconsistent with build:dev. Consider switching to the dev manifest (or make it conditional).

Proposed fix
-    "watch": "yarn copy-prod-manifest && cd packages/extension && yarn polkadot-exec-webpack --config webpack.watch.cjs --mode development --watch"
+    "watch": "yarn copy-dev-manifest && cd packages/extension && yarn polkadot-exec-webpack --config webpack.watch.cjs --mode development --watch"
🤖 Prompt for AI Agents
In `@package.json` at line 50, The "watch" npm script currently runs the
production manifest copy ("copy-prod-manifest") which makes development runs
inconsistent with "start" and "build:dev"; update the "watch" script to use the
development manifest step (replace "copy-prod-manifest" with the dev manifest
command or make it conditional) so that the "watch" script aligns with
NODE_ENV=development and "build:dev" behavior; ensure you update the script
named "watch" in package.json and verify there is a corresponding dev manifest
copy task (or add one) rather than invoking "copy-prod-manifest".

Comment on lines +95 to 100
const [chainsToShow, setChainsToShow] = useState<DropdownOption[]>(networks);

const onSearch = useCallback((keyword: string) => {
if (!keyword) {
return setChainsToShow(networks);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat packages/extension-polkagate/src/popup/receive/Receive.tsx | head -150

Repository: PolkaGate/extension

Length of output: 6066


🏁 Script executed:

fd -t f 'useGenesisHashOptions' --exec grep -l "useGenesisHashOptions" {} \; | head -5

Repository: PolkaGate/extension

Length of output: 128


🏁 Script executed:

cat packages/extension-polkagate/src/hooks/useGenesisHashOptions.ts

Repository: PolkaGate/extension

Length of output: 2553


🏁 Script executed:

# Check if there are any other useEffect hooks in the Receive.tsx component that might handle this
cat packages/extension-polkagate/src/popup/receive/Receive.tsx | grep -n "useEffect\|useState\|networks"

Repository: PolkaGate/extension

Length of output: 689


Add useEffect to sync chainsToShow when networks changes.

When useGenesisHashOptions updates (e.g., metadata/testnet toggle), the displayed chain list becomes stale until the user performs a new search. Without a sync effect, chainsToShow will not reflect the latest network options.

🛠️ Suggested fix
-import React, { useCallback, useMemo, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
@@
   const [chainsToShow, setChainsToShow] = useState<DropdownOption[]>(networks);
+
+  useEffect(() => {
+    setChainsToShow(networks);
+  }, [networks]);
📝 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.

Suggested change
const [chainsToShow, setChainsToShow] = useState<DropdownOption[]>(networks);
const onSearch = useCallback((keyword: string) => {
if (!keyword) {
return setChainsToShow(networks);
}
import React, { useCallback, useEffect, useMemo, useState } from 'react';
const [chainsToShow, setChainsToShow] = useState<DropdownOption[]>(networks);
useEffect(() => {
setChainsToShow(networks);
}, [networks]);
const onSearch = useCallback((keyword: string) => {
if (!keyword) {
return setChainsToShow(networks);
}
🤖 Prompt for AI Agents
In `@packages/extension-polkagate/src/popup/receive/Receive.tsx` around lines 95 -
100, Add a useEffect in Receive.tsx that watches the networks value and updates
chainsToShow via setChainsToShow when networks change so the dropdown reflects
updated options from useGenesisHashOptions; e.g., inside the Receive component
add a useEffect(() => setChainsToShow(networks), [networks]) to keep
chainsToShow in sync with networks (ensure this complements the existing
onSearch logic).

import type { HexString } from '@polkadot/util/types';
import type { TxResult } from '../types';

import { signatureVerify } from '@polkadot/util-crypto';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix build-breaking unused import.

signatureVerify is unused and is failing CI (TS6133). Remove it or use it intentionally.

🛠️ Proposed fix (remove unused import)
-import { signatureVerify } from '@polkadot/util-crypto';
📝 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.

Suggested change
import { signatureVerify } from '@polkadot/util-crypto';
🧰 Tools
🪛 GitHub Actions: CI Workflow

[error] 16-16: TS6133: 'signatureVerify' is declared but its value is never read.

🪛 GitHub Check: build

[failure] 16-16:
'signatureVerify' is declared but its value is never read.

🤖 Prompt for AI Agents
In `@packages/extension-polkagate/src/util/api/signAndSend.ts` at line 16, The
import signatureVerify from '@polkadot/util-crypto' in signAndSend.ts is unused
and causing a TS6133 build error; remove the unused import statement (the
"signatureVerify" specifier) from the import line in
packages/extension-polkagate/src/util/api/signAndSend.ts so the file only
imports what is actually used.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@packages/extension-polkagate/src/fullscreen/sendFund/useParaSpellFeeCall.ts`:
- Around line 64-82: The stale paraSpellState issue is caused by leaving
previous paraSpellTransaction/paraSpellFee in state while builder.build() runs;
at the start of the effect (before calling builder.build()) clear/reset the
paraSpell-related state via setParaSpellState to remove paraSpellTransaction and
paraSpellFee (e.g., set to null/undefined or an empty object), keep the existing
cancelled flag logic, and ensure any subsequent setParaSpellState((pre) => ({
...(pre||{}), paraSpellTransaction: tx })) only sets the new tx when not
cancelled so old values cannot persist if the new build fails.
- Around line 102-108: The error fallback for ParaspellFees used in
setParaSpellState omits required fields on originFee (it currently sets only
originFee: { fee: BN_ZERO }), causing a type mismatch with ParaspellFees /
TXcmFeeBase; update the fallback to include the required properties (e.g.,
originFee: { fee: BN_ZERO, sufficient: false, balanceAfter: 0n } or another
appropriate default) or explicitly cast the object as ParaspellFees to satisfy
the type checker; locate the update in the setParaSpellState call and adjust the
originFee shape (and destinationFee if used) so it matches the ParaspellFees
interface.
🧹 Nitpick comments (1)
packages/extension-polkagate/src/fullscreen/sendFund/useParaSpellFeeCall.ts (1)

66-111: Parallel build() and getTransferInfo() can produce partial state.

Since both calls fire independently, one can succeed while the other fails — leaving paraSpellTransaction set but paraSpellFee zeroed (or vice versa). Ensure the consumer checks that both paraSpellTransaction and a valid paraSpellFee are present before allowing the user to submit.

Comment on lines +64 to +82
let cancelled = false;

Promise.all([builder.build(), builder.getTransferInfo()])
.then(([tx, info]) => {
builder.build()
.then((tx) => {
if (cancelled) {
return;
}

setParaSpellState({
setParaSpellState((pre) => ({
...(pre || {}),
paraSpellTransaction: tx
}));
}).catch((err) => {
if (!cancelled) {
setError('Something went wrong while building transaction!');
}

console.error('building transaction error', err);
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Stale paraSpellState persists across effect re-runs.

When dependencies change and the effect re-runs, the old paraSpellTransaction and paraSpellFee remain in state until the new promises resolve (or reject). If the new build() fails, the previous transaction stays in state—potentially mismatched with the current inputs. This could let a user submit a transaction built with outdated parameters.

Reset state at the top of the effect body:

Proposed fix
     const amount = transferType === 'All' ? 'ALL' : amountAsBN.toString();
 
     try {
+      setParaSpellState({});
+
       const builder = Builder({ abstractDecimals: false }/* node api/ws_url_string/ws_url_array - optional*/)
🤖 Prompt for AI Agents
In `@packages/extension-polkagate/src/fullscreen/sendFund/useParaSpellFeeCall.ts`
around lines 64 - 82, The stale paraSpellState issue is caused by leaving
previous paraSpellTransaction/paraSpellFee in state while builder.build() runs;
at the start of the effect (before calling builder.build()) clear/reset the
paraSpell-related state via setParaSpellState to remove paraSpellTransaction and
paraSpellFee (e.g., set to null/undefined or an empty object), keep the existing
cancelled flag logic, and ensure any subsequent setParaSpellState((pre) => ({
...(pre||{}), paraSpellTransaction: tx })) only sets the new tx when not
cancelled so old values cannot persist if the new build fails.

Comment on lines +102 to +108
// @ts-ignore
setParaSpellState((pre) => ({
...(pre || {}),
paraSpellFee: {
originFee: { fee: BN_ZERO }
}
}));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for ParaspellFees type definition
echo "=== ParaspellFees type definition ==="
rg -n "type ParaspellFees|interface ParaspellFees" --type=ts -A 10

echo ""
echo "=== xcmFee type definition ==="
rg -n "type xcmFee|interface xcmFee" --type=ts -A 10

echo ""
echo "=== How paraSpellFee is accessed in the codebase ==="
rg -n "paraSpellFee" --type=ts -B 2 -A 2 | head -100

echo ""
echo "=== How originFee is accessed ==="
rg -n "\.originFee" --type=ts -B 2 -A 2

echo ""
echo "=== How destinationFee is accessed ==="
rg -n "\.destinationFee" --type=ts -B 2 -A 2

Repository: PolkaGate/extension

Length of output: 10109


🏁 Script executed:

#!/bin/bash
# Find TXcmFeeBase definition
echo "=== TXcmFeeBase type definition ==="
rg -n "type TXcmFeeBase|interface TXcmFeeBase" --type=ts -A 15

echo ""
echo "=== Check for direct access to originFee properties other than .fee ==="
rg -n "originFee\." --type=ts -B 1 -A 1 | grep -v "originFee.fee"

echo ""
echo "=== Check for direct access to originFee.sufficient ==="
rg -n "sufficient" --type=ts -B 2 -A 2

echo ""
echo "=== Check for direct access to originFee.balanceAfter ==="
rg -n "balanceAfter" --type=ts -B 2 -A 2

Repository: PolkaGate/extension

Length of output: 14976


Address type mismatch in the error fallback for ParaspellFees.

The error fallback at lines 105-107 sets originFee: { fee: BN_ZERO }, which violates the ParaspellFees interface. originFee requires TXcmFeeBase & { sufficient: boolean; balanceAfter: bigint; }, but the fallback omits sufficient and balanceAfter. While the current codebase only accesses originFee.fee (which exists) and safely handles optional destinationFee with defensive chaining, the type mismatch should be resolved to prevent future bugs if the code evolves to access these properties. Consider aligning the fallback object with the expected ParaspellFees shape or explicitly typing the error state.

🤖 Prompt for AI Agents
In `@packages/extension-polkagate/src/fullscreen/sendFund/useParaSpellFeeCall.ts`
around lines 102 - 108, The error fallback for ParaspellFees used in
setParaSpellState omits required fields on originFee (it currently sets only
originFee: { fee: BN_ZERO }), causing a type mismatch with ParaspellFees /
TXcmFeeBase; update the fallback to include the required properties (e.g.,
originFee: { fee: BN_ZERO, sufficient: false, balanceAfter: 0n } or another
appropriate default) or explicitly cast the object as ParaspellFees to satisfy
the type checker; locate the update in the setParaSpellState call and adjust the
originFee shape (and destinationFee if used) so it matches the ParaspellFees
interface.

@Nick-1979 Nick-1979 added test on testing phase and removed WIP Work In Progress labels Feb 25, 2026
@AMIRKHANEF
Copy link
Member

Issues To Fix:
1- 0x accounts can't be found on the Receive popup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test on testing phase

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants