Skip to content

feat(evm): revive evm wallet connector#863

Open
hassnian wants to merge 1 commit intomainfrom
feat/init-evm-wallet
Open

feat(evm): revive evm wallet connector#863
hassnian wants to merge 1 commit intomainfrom
feat/init-evm-wallet

Conversation

@hassnian
Copy link
Contributor

@hassnian hassnian commented Mar 20, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Enabled wallet selection tabs for filtering installed wallets by type (All, Polkadot, EVM).
  • Improvements

    • Enhanced EVM wallet connection handling with improved account synchronization.
    • Better wallet extension state management during connection changes.
  • Chores

    • Removed external wallet service dependencies.
    • Simplified wallet connection configuration.

@vercel
Copy link

vercel bot commented Mar 20, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
koda Ready Ready Preview, Comment Mar 20, 2026 10:26am

@railway-app
Copy link

railway-app bot commented Mar 20, 2026

🚅 Deployed to the app-pr-863 environment in chaotic-art-preview

Service Status Web Updated (UTC)
app 😴 Sleeping (View Logs) Web Mar 20, 2026 at 10:35 am

@coderabbitai
Copy link

coderabbitai bot commented Mar 20, 2026

📝 Walkthrough

Walkthrough

This PR migrates the EVM wallet connection mechanism from Reown AppKit to direct wagmi/viem integration. The changes replace the Reown-based authorization flow with a wagmi connector-driven approach, introduce viem utilities for chain and public client management, remove Reown dependencies and configuration, and update wallet selection UI to support multiple tab filtering.

Changes

Cohort / File(s) Summary
Core Wallet Connection Logic
app/components/wallet/connect/WalletConnectModal.client.vue, app/components/wallet/connect/stage/authorization/WalletAuthorizationStage.client.vue
Replaced Reown modal integration with wagmi useConnection/useConnectors bindings. Updated EVM wallet watching to track live connector accounts and state. Authorization now calls connectAsync with wagmi chains and normalizes returned accounts. Handles wallet disconnection by resetting extension state and clearing selected accounts.
Wallet Selection UI
app/components/wallet/connect/stage/wallet/WalletSelectionStage.client.vue, app/components/wallet/connect/stage/wallet/WalletSelectionTabs.vue
Enabled WalletSelectionTabs with v-model binding to activeTab. Updated filteredInstalledWallets to dynamically filter by active tab using ACTIVE_TAB_VM_MAP, replacing hardcoded Polkadot-only filter. Removed icon from "All" tab in tabItems array.
Wallet Management Composables
app/composables/useWalletManager.ts, app/composables/useReown.ts (deleted)
Removed useReown() composable entirely (125 lines). Updated useWalletManager EVM disconnect to dynamically import @wagmi/core and call disconnect($wagmi.config, { connector }) instead of Reown disconnect.
Blockchain Utilities
app/utils/viem.ts, app/utils/wallet/evm/config.ts, app/utils/wallet/index.ts
Added new viem.ts module with chainsForWagmi, supportedChains constants, and getPublicClient(chainId) function with per-chain caching. Removed REOWN_WALLET_CONFIG export. Updated formatEvmAccounts signature to accept { accounts: EvmWalletAccount[]; extension } instead of Reown params; removed wallet RDNS from account ID generation.
Plugin & Environment Configuration
app/plugins/wagmi.client.ts, env.public.ts
Removed Reown AppKit adapter integration; replaced with direct wagmi createConfig using injected connector, cookie storage, and per-chain HTTP transports. Updated wagmi plugin interface to export only { config }. Removed REOWN_CONNECT_PROJECT_ID env var and reownProjectId from public config.
Dependencies
package.json
Removed @reown/appkit and @reown/appkit-adapter-wagmi from dependencies.

Sequence Diagram

sequenceDiagram
    participant User
    participant WalletSelectionStage
    participant WalletAuthorizationStage
    participant wagmi as Wagmi Connector
    participant WalletExtension
    participant AccountStore

    User->>WalletSelectionStage: Select EVM wallet
    WalletSelectionStage->>WalletAuthorizationStage: Pass selected extension
    
    WalletAuthorizationStage->>wagmi: Lookup matching connector by id
    wagmi-->>WalletAuthorizationStage: Return connector instance
    
    WalletAuthorizationStage->>wagmi: connectAsync(chainsForWagmi[0].id)
    wagmi->>WalletExtension: Request account connection
    WalletExtension-->>wagmi: Return accounts
    wagmi-->>WalletAuthorizationStage: Return connected accounts
    
    WalletAuthorizationStage->>WalletAuthorizationStage: formatEvmAccounts(accounts, extension)
    WalletAuthorizationStage->>AccountStore: setWalletConnected(accounts, extension)
    AccountStore-->>User: Display connected account

Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 Wagmi wagmi, no more Reown chains,
Viem connectors dancing through wagmi's lanes,
Accounts sync'd in perfect accord,
EVM wallets linked—a protocol restored! ✨💎

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% 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 'revive evm wallet connector' accurately captures the main change: replacing the deprecated Reown integration with a wagmi-based EVM wallet connector implementation across multiple components and utilities.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/init-evm-wallet
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

CodeRabbit can generate a title for your PR based on the changes with custom instructions.

Set the reviews.auto_title_instructions setting to generate a title for your PR based on the changes in the PR with custom instructions.

@cloudflare-workers-and-pages
Copy link

Deploying app with  Cloudflare Pages  Cloudflare Pages

Latest commit: 9a64f1c
Status: ✅  Deploy successful!
Preview URL: https://8dd7cd5e.app-bzd.pages.dev
Branch Preview URL: https://feat-init-evm-wallet.app-bzd.pages.dev

View logs

Copy link

@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

🧹 Nitpick comments (1)
app/utils/viem.ts (1)

4-15: Avoid introducing a second chain registry.

supportedChains now duplicates chain identifiers, RPC URLs, explorer URLs, and currency metadata outside the app’s existing chain model. That makes the wagmi/viem setup drift-prone as soon as route handling or chain metadata changes elsewhere. Please derive this from SupportedChain/chainSpec instead of hard-coding a parallel source of truth here.

Based on learnings, "Applies to **/.{ts,tsx} : Use SupportedChain type from ~/plugins/sdk.client for chain identifiers" and "Applies to **/.{ts,tsx,vue} : Do NOT import from kodadot1/static; use internal implementations instead (useChain, SupportedChain, chainSpec)."

Also applies to: 44-49

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/components/wallet/connect/stage/authorization/WalletAuthorizationStage.client.vue`:
- Around line 76-79: The code currently passes chainsForWagmi[0]?.id into
connectAsync, which hard-codes the first array entry; change to resolve the
target chain from the route-based composable useChain() (falling back to
usePrefix() only if useChain() is not available) and cast/convert it to the
SupportedChain identifier expected by connectAsync. Specifically, replace use of
chainsForWagmi[0] in the connectAsync call with the chain id/value returned by
useChain() (ensuring the value is typed as SupportedChain from
~/plugins/sdk.client) and still pass the same connector variable; ensure the
chain id property you pass matches the shape connectAsync expects (e.g., .id or
equivalent).

In `@app/components/wallet/connect/stage/wallet/WalletSelectionStage.client.vue`:
- Around line 20-25: The computed filteredInstalledWallets currently returns
ACTIVE_TAB_VM_MAP[activeTab.value] (a string) when activeTab is not 'All';
change it to return a filtered array of installedWallets instead: when
activeTab.value === 'All' return installedWallets.value, otherwise map activeTab
to the VM type via ACTIVE_TAB_VM_MAP and filter installedWallets.value for
wallets whose vm property matches that VM; this will fix callers such as
onConnectAll() iteration and the InstalledWallets :extensions prop which expect
an array.

In `@app/components/wallet/connect/WalletConnectModal.client.vue`:
- Around line 109-121: The code currently filters out the generic 'injected'
connector from connectors.value which can remove the only available EVM
connector; modify the logic in the mapping block (the expression using
connectors.value.filter(...).map(...)) to retain the 'injected' connector as a
fallback: either remove the filter that excludes connector.id === 'injected' or
detect when getEvmWalletExtensions() / connectors.value yields no discovered
providers and then append a generic injected fallback object (id: 'injected',
name: 'Injected', icon: '/partners/logo-evm.svg', source: 'injected', installed:
true, vm: 'EVM', accounts: [], state: WalletStates.Idle) so users can always
attempt to connect an injected EVM wallet.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f49c7c73-2016-4d83-b263-81b7ac5eeef9

📥 Commits

Reviewing files that changed from the base of the PR and between 2e66ef2 and 9a64f1c.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • public/partners/logo-evm.svg is excluded by !**/*.svg
📒 Files selected for processing (12)
  • app/components/wallet/connect/WalletConnectModal.client.vue
  • app/components/wallet/connect/stage/authorization/WalletAuthorizationStage.client.vue
  • app/components/wallet/connect/stage/wallet/WalletSelectionStage.client.vue
  • app/components/wallet/connect/stage/wallet/WalletSelectionTabs.vue
  • app/composables/useReown.ts
  • app/composables/useWalletManager.ts
  • app/plugins/wagmi.client.ts
  • app/utils/viem.ts
  • app/utils/wallet/evm/config.ts
  • app/utils/wallet/index.ts
  • env.public.ts
  • package.json
💤 Files with no reviewable changes (4)
  • app/utils/wallet/evm/config.ts
  • package.json
  • env.public.ts
  • app/composables/useReown.ts

Comment on lines +76 to +79
const connectedData = await connectAsync({
chainId: chainsForWagmi[0]?.id,
connector,
})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Resolve the target chain from route context, not array order.

Line 77 always requests chainsForWagmi[0]. As soon as more than one EVM chain is enabled again, authorization will still force whichever chain happens to be first in this array instead of the route’s chain. Please drive this from useChain()/SupportedChain rather than a hard-coded index.

As per coding guidelines, "Use SupportedChain type from ~/plugins/sdk.client for chain identifiers" and "Use useChain composable (route-based) as the primary method for chain handling; use usePrefix only as fallback."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/components/wallet/connect/stage/authorization/WalletAuthorizationStage.client.vue`
around lines 76 - 79, The code currently passes chainsForWagmi[0]?.id into
connectAsync, which hard-codes the first array entry; change to resolve the
target chain from the route-based composable useChain() (falling back to
usePrefix() only if useChain() is not available) and cast/convert it to the
SupportedChain identifier expected by connectAsync. Specifically, replace use of
chainsForWagmi[0] in the connectAsync call with the chain id/value returned by
useChain() (ensuring the value is typed as SupportedChain from
~/plugins/sdk.client) and still pass the same connector variable; ensure the
chain id property you pass matches the shape connectAsync expects (e.g., .id or
equivalent).

Comment on lines 20 to 25
const filteredInstalledWallets = computed(() => {
// if (activeTab.value === 'All') {
// return installedWallets.value
// }
return installedWallets.value.filter(wallet => wallet.vm === ACTIVE_TAB_VM_MAP.Polkadot)
if (activeTab.value === 'All') {
return installedWallets.value
}
return ACTIVE_TAB_VM_MAP[activeTab.value]
})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical bug: Returns VM string instead of filtered wallet array.

When activeTab.value is not 'All', the computed returns ACTIVE_TAB_VM_MAP[activeTab.value] which is a string ('SUB' or 'EVM'), not a filtered array of wallets. This breaks:

  • Line 68: onConnectAll() iterates over filteredInstalledWallets.value
  • Line 90: InstalledWallets expects an array via :extensions
🐛 Proposed fix: Filter wallets by VM type
 const filteredInstalledWallets = computed(() => {
   if (activeTab.value === 'All') {
     return installedWallets.value
   }
-  return ACTIVE_TAB_VM_MAP[activeTab.value]
+  const vm = ACTIVE_TAB_VM_MAP[activeTab.value]
+  return installedWallets.value.filter(wallet => wallet.vm === vm)
 })
📝 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 filteredInstalledWallets = computed(() => {
// if (activeTab.value === 'All') {
// return installedWallets.value
// }
return installedWallets.value.filter(wallet => wallet.vm === ACTIVE_TAB_VM_MAP.Polkadot)
if (activeTab.value === 'All') {
return installedWallets.value
}
return ACTIVE_TAB_VM_MAP[activeTab.value]
})
const filteredInstalledWallets = computed(() => {
if (activeTab.value === 'All') {
return installedWallets.value
}
const vm = ACTIVE_TAB_VM_MAP[activeTab.value]
return installedWallets.value.filter(wallet => wallet.vm === vm)
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/wallet/connect/stage/wallet/WalletSelectionStage.client.vue`
around lines 20 - 25, The computed filteredInstalledWallets currently returns
ACTIVE_TAB_VM_MAP[activeTab.value] (a string) when activeTab is not 'All';
change it to return a filtered array of installedWallets instead: when
activeTab.value === 'All' return installedWallets.value, otherwise map activeTab
to the VM type via ACTIVE_TAB_VM_MAP and filter installedWallets.value for
wallets whose vm property matches that VM; this will fix callers such as
onConnectAll() iteration and the InstalledWallets :extensions prop which expect
an array.

Comment on lines +109 to +121
return connectors.value
.filter(connector => connector.id !== 'injected')
.map(connector => ({
id: connector.id,
name: connector.name,
icon: connector.icon ?? '/partners/logo-evm.svg',
url: '',
source: connector.id,
installed: true,
vm: 'EVM',
accounts: [],
state: WalletStates.Idle,
},
]
}))
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, find and examine the WalletConnectModal.client.vue file
fd WalletConnectModal.client.vue

Repository: chaotic-art/app

Length of output: 118


🏁 Script executed:

# Then locate wagmi.client.ts
fd wagmi.client.ts

Repository: chaotic-art/app

Length of output: 86


🏁 Script executed:

# Get context around lines 109-121 in WalletConnectModal.client.vue
fd WalletConnectModal.client.vue -x cat -n {} | sed -n '100,130p'

Repository: chaotic-art/app

Length of output: 1047


🏁 Script executed:

# Check the wagmi configuration file (lines 11-19)
fd wagmi.client.ts -x cat -n {} | sed -n '1,30p'

Repository: chaotic-art/app

Length of output: 1061


🏁 Script executed:

# Search for how connectors.value is populated
rg "connectors.value" -A 5 -B 5 app/components/wallet/connect/

Repository: chaotic-art/app

Length of output: 2363


🏁 Script executed:

# Check imports to understand the connector source
rg "useConnectors\|connectors" app/components/wallet/connect/WalletConnectModal.client.vue -B 2 -A 2

Repository: chaotic-art/app

Length of output: 41


🏁 Script executed:

# Find where connectors is defined/imported in WalletConnectModal.client.vue
cat app/components/wallet/connect/WalletConnectModal.client.vue | head -50

Repository: chaotic-art/app

Length of output: 1519


🏁 Script executed:

# Check more of the WalletConnectModal file to find connectors definition
rg "connectors\s*=" app/components/wallet/connect/WalletConnectModal.client.vue -B 3 -A 3

Repository: chaotic-art/app

Length of output: 305


🏁 Script executed:

# Search for useConnectors import
rg "useConnectors" app/components/wallet/connect/ -B 2 -A 2

Repository: chaotic-art/app

Length of output: 2414


🌐 Web query:

Wagmi Vue useConnectors multiInjectedProviderDiscovery EIP-6963 connector discovery

💡 Result:

In Wagmi for Vue, multiInjectedProviderDiscovery enables automatic discovery of multiple injected wallet providers via EIP-6963. This feature is configured at the config level using createConfig, not directly in useConnectors. Key points: - Set multiInjectedProviderDiscovery: true (default) in createConfig to enable EIP-6963 discovery using the mipd library. This automatically generates injected connectors for discovered providers. - Example config: import { createConfig, http } from '@wagmi/vue' import { mainnet } from '@wagmi/vue/chains' import { injected } from '@wagmi/vue/connectors' export const config = createConfig({ chains: [mainnet], connectors: [injected], // Optional: explicit connectors multiInjectedProviderDiscovery: true, // Enables EIP-6963 (default: true) transports: { [mainnet.id]: http } }) - useConnectors returns the full list of available connectors from the config, including those dynamically discovered via EIP-6963 if enabled. - In a Vue component: import { useConnectors, useConnect } from '@wagmi/vue' const connectors = useConnectors const { connect } = useConnect // Render buttons for each connector <button v-for="connector in connectors" @click="connect.mutate({ connector })"> {{ connector.name }} - When multiInjectedProviderDiscovery is true, Wagmi discovers EIP-6963 providers (e.g., MetaMask, Coinbase) and creates separate injected connectors for each, allowing users to choose specific wallets. It filters duplicates (e.g., vs. explicit metaMask connector). - To disable: multiInjectedProviderDiscovery: false. - Providers announce via window.ethereum events; Wagmi converts to connectors with proper id, name, icon from EIP-6963 info. - Docs updated as of 2026; feature stable in Wagmi Vue (mirrors React core).

Citations:


🏁 Script executed:

# Check if multiInjectedProviderDiscovery is explicitly configured
rg "multiInjectedProviderDiscovery" app/plugins/wagmi.client.ts

Repository: chaotic-art/app

Length of output: 41


🏁 Script executed:

# Check full wagmi.client.ts to see complete config
cat app/plugins/wagmi.client.ts

Repository: chaotic-art/app

Length of output: 851


🏁 Script executed:

# Search for any comments explaining why 'injected' is filtered
rg "injected|getEvmWalletExtensions" app/components/wallet/connect/WalletConnectModal.client.vue -B 3 -A 3

Repository: chaotic-art/app

Length of output: 481


🏁 Script executed:

# Check if there are other EVM wallet sources besides the filtered connectors
rg "getEvmWalletExtensions\|getWalletExtensions" app/components/wallet/connect/WalletConnectModal.client.vue -A 15

Repository: chaotic-art/app

Length of output: 41


Keep the generic injected connector as a fallback.

With only injected() registered in app/plugins/wagmi.client.ts, filtering it out (line 110) removes the only guaranteed connector. Wagmi's multiInjectedProviderDiscovery defaults to true and discovers EIP-6963 providers (MetaMask, Coinbase, etc.), but discovery is not guaranteed—it depends on browser support and installed wallets. On environments without discovered providers, getEvmWalletExtensions() returns an empty array, leaving users unable to connect any EVM wallet.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/wallet/connect/WalletConnectModal.client.vue` around lines 109
- 121, The code currently filters out the generic 'injected' connector from
connectors.value which can remove the only available EVM connector; modify the
logic in the mapping block (the expression using
connectors.value.filter(...).map(...)) to retain the 'injected' connector as a
fallback: either remove the filter that excludes connector.id === 'injected' or
detect when getEvmWalletExtensions() / connectors.value yields no discovered
providers and then append a generic injected fallback object (id: 'injected',
name: 'Injected', icon: '/partners/logo-evm.svg', source: 'injected', installed:
true, vm: 'EVM', accounts: [], state: WalletStates.Idle) so users can always
attempt to connect an injected EVM wallet.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant