Skip to content

Fix/add new wallet#570

Merged
Agilulfo1820 merged 11 commits into
mainfrom
fix/add-new-wallet
Dec 23, 2025
Merged

Fix/add new wallet#570
Agilulfo1820 merged 11 commits into
mainfrom
fix/add-new-wallet

Conversation

@Agilulfo1820

@Agilulfo1820 Agilulfo1820 commented Dec 23, 2025

Copy link
Copy Markdown
Member

Using latest version of dappkit so the connect modal is visible.
When clicking "add wallet" we display immediatly dappkit instead of our own ConnectModal, because I have difficulties in closing that modal after the login is completed. But there is no point to connect to a social login account, since those are not supported for multiwallets, so I think it actually even has more sense and saves some click to the user.
Applied some fixes to how we store new connections and set wallets active.

Registrazione.schermo.2025-12-23.alle.10.52.47.mov

I have only one issue I was not able to solve, but it's a minor I would like to address later:
It is not possible to remove the lastly connected wallet.
Scenario:
I have 1 wallet, add a new one, switch back to wallet 1, and try to remove wallet 2 (the lastly added): instead of removing it it's set as active.
I add another wallet 3, now I can remove wallet 2, but cannot remove wallet 3.

The weird stuff is that it always is set as active.


Another issue I noticed:

When doing login after disconnecting, the behaviour is not consistent in setting the wallet the user has in VeWorld as new.


Summary by CodeRabbit

  • New Features

    • Integrated DAppKit wallet modal with real-time wallet detection and auto-refresh.
    • Wallet button now shows balances and asset icons when applicable.
    • Logout control added to the address/profile UI; connect option is always visible.
  • Bug Fixes

    • Improved wallet switching and handling of recently removed wallets to avoid accidental re-adds.
  • Chores

    • Updated @vechain/dapp-kit-react dependency to v2.1.0-rc.5.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Dec 23, 2025

Copy link
Copy Markdown

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Integrates DAppKit wallet modal into the add-wallet flow, adds a 200ms wallet-polling hash to detect external wallet changes, emits a wallet_removed event with a 5s cooldown to avoid re-adding removed wallets, updates provider config to always show connect, bumps @vechain/dapp-kit-react to rc.5, and adds a few component props/utilities.

Changes

Cohort / File(s) Summary
Package Configuration
packages/vechain-kit/package.json
Updated @vechain/dapp-kit-react to 2.1.0-rc.5 in dependencies and peerDependencies.
Select wallet UI & flow
packages/vechain-kit/src/components/AccountModal/Contents/SelectWallet/SelectWalletContent.tsx
Replace prior modal dispatch with useDAppKitWalletModal / openDappKitModal. Added 200ms polling using simpleHash to detect external wallet additions and call refreshWallets().
Wallet state & activation logic
packages/vechain-kit/src/hooks/api/wallet/useWallet.ts
Added recentlyRemovedWalletsRef tracking (5s), getStoredWallets() exported in return type, updated activation logic to prefer stored active wallet and to guard against re-adding recently removed wallets; added wallet_removed listener. (Public API: getStoredWallets(): Wallet[] added.)
Wallet storage events
packages/vechain-kit/src/hooks/api/wallet/useWalletStorage.ts
On removeWallet, dispatches browser CustomEvent named wallet_removed with the removed address in detail.
Provider config
packages/vechain-kit/src/providers/VeChainKitProvider.tsx
Added alwaysShowConnect={true} to DAppKitProvider.
Profile / Account UI
packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx, packages/vechain-kit/src/components/AccountModal/Components/AccountSelector.tsx
Unified switch-wallet handling, adjusted rendering conditions for "Switch wallet" (only non-in-app browser + DAppKit), rewired modal callbacks and logout flows.
Address display / ProfileCard prop changes
packages/vechain-kit/src/components/common/AddressDisplay.tsx, packages/vechain-kit/src/components/AccountModal/Contents/Profile/Components/ProfileCard/ProfileCard.tsx
Added optional onLogout?: () => void prop to ProfileCard and AddressDisplay; AddressDisplay now renders a logout IconButton that launches a disconnect-confirm flow and calls onLogout on success. (Public prop added.)
Utilities
packages/vechain-kit/src/utils/stringUtils.ts
Added exported simpleHash(input: string): string used for wallet polling hash comparisons.

Sequence Diagram

sequenceDiagram
    actor User
    participant SelectWallet as SelectWalletContent
    participant DAppKit as DAppKit Modal
    participant Polling as Wallet Polling (200ms)
    participant Storage as useWalletStorage
    participant Hook as useWallet
    participant App as App State

    User->>SelectWallet: Click "Add Wallet"
    SelectWallet->>DAppKit: openDappKitModal()
    Note over DAppKit: User approves/connects wallet in modal
    DAppKit-->>SelectWallet: Modal closes (wallet added)

    rect `#E6F2FF`
        Polling->>Polling: every 200ms compute simpleHash(storedWallets)
        alt Hash changed (new wallet)
            Polling->>SelectWallet: trigger refreshWallets()
            SelectWallet->>Storage: getStoredWallets()
            Storage-->>SelectWallet: return updated list
            SelectWallet->>Hook: update active/other wallets
        end
    end

    rect `#FFF0E6`
        User->>App: Remove wallet
        App->>Storage: removeWallet(address)
        Storage-->>Storage: update storage
        Storage->>Hook: dispatch `wallet_removed` CustomEvent
        Hook->>Hook: add to recentlyRemovedWalletsRef (5s cooldown)
    end

    rect `#E8FFE6`
        Hook->>Hook: on new connection compare connected vs getStoredWallets()
        alt New wallet detected
            Hook->>Storage: setActiveWalletStorage(...)
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

safe-to-deploy

Suggested reviewers

  • mikeredmond
  • victorkl400
  • Vombato

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Fix/add new wallet' is vague and generic, using non-descriptive terms that don't clearly convey the main changes in the changeset. Consider a more specific title that reflects the primary change, such as 'Integrate DappKit wallet modal for new wallet additions' or 'Use DappKit modal instead of custom ConnectModal for wallet addition flow'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/add-new-wallet

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/vechain-kit/src/hooks/api/wallet/useWalletStorage.ts (1)

113-119: Redundant browser check can be removed.

The isBrowser() check on line 115 is redundant since line 94 already returns early when not in a browser environment. The event dispatch can be simplified.

🔎 Proposed simplification
 
-            // Dispatch event to notify that a wallet was removed
-            // This prevents useWallet from setting it as active again if it's still connected
-            if (isBrowser()) {
-                window.dispatchEvent(
-                    new CustomEvent('wallet_removed', { detail: { address } }),
-                );
-            }
+            // Dispatch event to notify that a wallet was removed
+            // This prevents useWallet from setting it as active again if it's still connected
+            window.dispatchEvent(
+                new CustomEvent('wallet_removed', { detail: { address } }),
+            );
packages/vechain-kit/src/components/AccountModal/Contents/SelectWallet/SelectWalletContent.tsx (1)

87-114: Consider event-driven updates instead of polling.

The 200ms polling mechanism works but could be optimized by using a custom event (e.g., wallet_added) dispatched when a new wallet is added, similar to the existing wallet_removed and wallet_switched events. This would eliminate continuous polling and improve efficiency.

💡 Event-driven alternative

Instead of polling, dispatch a wallet_added event in useWalletStorage.ts when saveWallet adds a new wallet:

In useWalletStorage.ts:

if (existingIndex < 0) {
    wallets.push(walletToSave);
    // Dispatch event for new wallet
    if (isBrowser()) {
        window.dispatchEvent(
            new CustomEvent('wallet_added', { detail: { address } })
        );
    }
}

Then replace the polling with an event listener in this file, similar to the existing wallet_switched listener on lines 68-85.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8d8ec7 and ab90774.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (5)
  • packages/vechain-kit/package.json
  • packages/vechain-kit/src/components/AccountModal/Contents/SelectWallet/SelectWalletContent.tsx
  • packages/vechain-kit/src/hooks/api/wallet/useWallet.ts
  • packages/vechain-kit/src/hooks/api/wallet/useWalletStorage.ts
  • packages/vechain-kit/src/providers/VeChainKitProvider.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/migration-guide-to-v2.mdc)

**/*.{ts,tsx}: In VeChain Kit Version 2, use useThor instead of useConnex for contract interactions
For single contract read operations, use the useCallClause hook with the pattern: import dependencies, define ABI and method as const, create query key function using getCallClauseQueryKeyWithArgs, and wrap with useCallClause including data transformation in the select option
For multiple parallel contract calls, use the executeMultipleClausesCall utility wrapped in a useQuery hook with the pattern: define query key function, use executeMultipleClausesCall in queryFn mapping items to clause objects, and transform results
For transaction building and sending, use the useBuildTransaction hook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Always provide an arguments array for contract calls, even when no parameters are required - use an empty array for parameter-less functions to enable TypeScript type checking
Always conditionally enable queries using the enabled property to prevent unnecessary contract calls, checking for all required parameters: enabled: !!requiredParam && !!otherRequiredParam
Use the select option in useCallClause or transform data in queryFn to handle data transformation, particularly for converting BigInt values to strings and normalizing contract return data
Maintain consistent query key patterns: use getCallClauseQueryKeyWithArgs for contract calls with arguments and getCallClauseQueryKey for calls without arguments to ensure proper caching and invalidation
Use TypeScript as const assertions for method names and as 0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions

Files:

  • packages/vechain-kit/src/hooks/api/wallet/useWalletStorage.ts
  • packages/vechain-kit/src/providers/VeChainKitProvider.tsx
  • packages/vechain-kit/src/components/AccountModal/Contents/SelectWallet/SelectWalletContent.tsx
  • packages/vechain-kit/src/hooks/api/wallet/useWallet.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : In VeChain Kit Version 2, use `useThor` instead of `useConnex` for contract interactions
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : In VeChain Kit Version 2, use `useThor` instead of `useConnex` for contract interactions

Applied to files:

  • packages/vechain-kit/src/providers/VeChainKitProvider.tsx
🧬 Code graph analysis (3)
packages/vechain-kit/src/hooks/api/wallet/useWalletStorage.ts (1)
packages/vechain-kit/src/utils/ssrUtils.ts (1)
  • isBrowser (75-77)
packages/vechain-kit/src/components/AccountModal/Contents/SelectWallet/SelectWalletContent.tsx (1)
packages/vechain-kit/src/hooks/api/wallet/useWallet.ts (1)
  • useWallet (62-493)
packages/vechain-kit/src/hooks/api/wallet/useWallet.ts (1)
packages/vechain-kit/src/utils/ssrUtils.ts (1)
  • isBrowser (75-77)
⏰ 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). (4)
  • GitHub Check: build-size-check
  • GitHub Check: deploy
  • GitHub Check: Lint & Build
  • GitHub Check: e2e-test
🔇 Additional comments (6)
packages/vechain-kit/src/providers/VeChainKitProvider.tsx (1)

694-694: LGTM! Aligns with PR objectives.

Setting alwaysShowConnect={true} ensures the DAppKit connect UI is always visible, supporting the new wallet modal flow where the DAppKit modal is opened directly for adding wallets.

packages/vechain-kit/src/components/AccountModal/Contents/SelectWallet/SelectWalletContent.tsx (1)

19-19: LGTM! Modal flow simplified.

The change to directly open the DAppKit modal via useDAppKitWalletModal and openDappKitModal() simplifies the flow and aligns with the PR objective to bypass the custom ConnectModal. This addresses the issue with closing the modal after login.

Also applies to: 46-46, 271-273

packages/vechain-kit/src/hooks/api/wallet/useWallet.ts (3)

277-308: LGTM! Removal tracking prevents unwanted re-activation.

The recentlyRemovedWalletsRef tracking with a 5-second grace period effectively prevents the useWallet hook from re-activating wallets that were just removed by the user. The event listener correctly responds to the wallet_removed event dispatched from useWalletStorage.ts.


320-336: LGTM! Correct new wallet detection.

Checking isNewWallet against the stored wallets list before saving ensures accurate detection of newly added wallets. The guard against recently removed wallets prevents the user experience issue mentioned in the PR.


236-249: LGTM! Improved wallet activation logic.

The changes correctly handle wallet activation across multiple scenarios:

  • New wallets are automatically activated for better UX
  • New connections (no stored active wallet) are activated
  • Reconnections with the stored active wallet maintain state
  • Wallet switches in progress are not overridden

Reading currentStoredActiveWallet directly from storage on each render avoids race conditions with state updates.

Also applies to: 352-362

packages/vechain-kit/package.json (1)

67-67: This is a release candidate, not a stable release—confirm the team intends to use pre-release code.

The version 2.1.0-rc.5 exists on npm and is not deprecated, but it's currently the latest published version with no stable 2.1.0 release available yet. The 2.1.0 series consists only of release candidates (rc.1–rc.5), with rc.5 published on 2025-12-23. Before merging, confirm the team is aware this introduces a pre-release dependency and review the release notes for any known issues or breaking changes.

@github-actions

github-actions Bot commented Dec 23, 2025

Copy link
Copy Markdown
Contributor

Size Change: +14.4 kB (+0.25%)

Total Size: 5.76 MB

Filename Size Change
packages/vechain-kit/dist/index-BoPfjKEQ.d.mts 0 B -151 kB (removed) 🏆
packages/vechain-kit/dist/index-BoPfjKEQ.d.mts.map 0 B -43.8 kB (removed) 🏆
packages/vechain-kit/dist/index-CkflGtlh.d.cts 0 B -150 kB (removed) 🏆
packages/vechain-kit/dist/index-CkflGtlh.d.cts.map 0 B -43.8 kB (removed) 🏆
packages/vechain-kit/dist/index.cjs.map 1.86 MB +5.31 kB (+0.29%)
packages/vechain-kit/dist/index.mjs.map 1.81 MB +5.17 kB (+0.29%)
packages/vechain-kit/dist/utils-Bl-JeVTg.cjs 0 B -26.2 kB (removed) 🏆
packages/vechain-kit/dist/utils-Bl-JeVTg.cjs.map 0 B -63 kB (removed) 🏆
packages/vechain-kit/dist/utils-DAs6kMGs.mjs 0 B -21.1 kB (removed) 🏆
packages/vechain-kit/dist/utils-DAs6kMGs.mjs.map 0 B -62.7 kB (removed) 🏆
packages/vechain-kit/dist/index-DbSQXvxu.d.mts 151 kB +151 kB (new file) 🆕
packages/vechain-kit/dist/index-DbSQXvxu.d.mts.map 43.8 kB +43.8 kB (new file) 🆕
packages/vechain-kit/dist/index-Zv2k4buI.d.cts 151 kB +151 kB (new file) 🆕
packages/vechain-kit/dist/index-Zv2k4buI.d.cts.map 43.8 kB +43.8 kB (new file) 🆕
packages/vechain-kit/dist/utils-CNYVq6tT.mjs 21.2 kB +21.2 kB (new file) 🆕
packages/vechain-kit/dist/utils-CNYVq6tT.mjs.map 63.4 kB +63.4 kB (new file) 🆕
packages/vechain-kit/dist/utils-DcAJej3n.cjs 26.4 kB +26.4 kB (new file) 🆕
packages/vechain-kit/dist/utils-DcAJej3n.cjs.map 63.7 kB +63.7 kB (new file) 🆕
ℹ️ View Unchanged
Filename Size Change
packages/vechain-kit/dist/assets 4.1 kB 0 B
packages/vechain-kit/dist/assets-aAdDxPJu.mjs 50.1 kB 0 B
packages/vechain-kit/dist/assets-aAdDxPJu.mjs.map 70.2 kB 0 B
packages/vechain-kit/dist/assets-DXVXPy3w.cjs 54.8 kB 0 B
packages/vechain-kit/dist/assets-DXVXPy3w.cjs.map 71.6 kB 0 B
packages/vechain-kit/dist/assets/index.cjs 716 B 0 B
packages/vechain-kit/dist/assets/index.d.cts 973 B 0 B
packages/vechain-kit/dist/assets/index.d.mts 973 B 0 B
packages/vechain-kit/dist/assets/index.mjs 718 B 0 B
packages/vechain-kit/dist/index--hSO7Xv4.d.mts 5.63 kB 0 B
packages/vechain-kit/dist/index--hSO7Xv4.d.mts.map 2.99 kB 0 B
packages/vechain-kit/dist/index-I8fe7GR2.d.cts 5.63 kB 0 B
packages/vechain-kit/dist/index-I8fe7GR2.d.cts.map 2.99 kB 0 B
packages/vechain-kit/dist/index.cjs 612 kB +833 B (+0.14%)
packages/vechain-kit/dist/index.d.cts 20.5 kB 0 B
packages/vechain-kit/dist/index.d.mts 20.5 kB 0 B
packages/vechain-kit/dist/index.mjs 578 kB +791 B (+0.14%)
packages/vechain-kit/dist/utils 4.1 kB 0 B
packages/vechain-kit/dist/utils/index.cjs 1.94 kB +24 B (+1.25%)
packages/vechain-kit/dist/utils/index.d.cts 2.97 kB +30 B (+1.02%)
packages/vechain-kit/dist/utils/index.d.mts 2.97 kB +30 B (+1.02%)
packages/vechain-kit/dist/utils/index.mjs 1.96 kB +26 B (+1.35%)

compressed-size-action

@victorkl400 victorkl400 requested a review from Copilot December 23, 2025 10:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR enhances multi-wallet functionality by upgrading to a newer version of dapp-kit-react that improves modal visibility, streamlining the "add wallet" flow, and implementing safeguards to prevent recently removed wallets from being automatically re-activated.

Key Changes

  • Updated @vechain/dapp-kit-react from rc.4 to rc.5 and enabled alwaysShowConnect prop for better modal visibility
  • Simplified "add wallet" flow to directly open DAppKit modal instead of custom ConnectModal
  • Added event-based tracking system with 5-second buffer to prevent removed wallets from being re-activated

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/vechain-kit/package.json Updated @vechain/dapp-kit-react dependency to rc.5 in both dependencies and peerDependencies
packages/vechain-kit/src/providers/VeChainKitProvider.tsx Added alwaysShowConnect prop to DAppKitProvider for improved modal visibility
packages/vechain-kit/src/hooks/api/wallet/useWalletStorage.ts Added wallet_removed custom event dispatch when removing wallets
packages/vechain-kit/src/hooks/api/wallet/useWallet.ts Implemented recently-removed-wallets tracking, updated wallet activation logic to prioritize new wallets, and enhanced storage consistency checks
packages/vechain-kit/src/components/AccountModal/Contents/SelectWallet/SelectWalletContent.tsx Simplified add wallet handler to directly open DAppKit modal and added 200ms polling to detect wallet changes

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

) {
refreshWallets();
}
}, 200); // Check every 200ms

Copilot AI Dec 23, 2025

Copy link

Choose a reason for hiding this comment

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

The 200ms polling interval is aggressive and could impact performance with unnecessary re-renders. Consider increasing the interval to 500-1000ms, or implementing a more event-driven approach using the existing custom events like 'wallet_removed' to trigger updates only when needed.

Suggested change
}, 200); // Check every 200ms
}, 800); // Check every 800ms

Copilot uses AI. Check for mistakes.
recentlyRemovedWalletsRef.current.delete(
event.detail.address.toLowerCase(),
);
}, 5000);

Copilot AI Dec 23, 2025

Copy link

Choose a reason for hiding this comment

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

The 5-second buffer timeout is a magic number without clear justification. Consider extracting this as a named constant (e.g., WALLET_REMOVAL_BUFFER_MS) with a comment explaining why this specific duration was chosen.

Copilot uses AI. Check for mistakes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (1)
packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx (1)

41-56: Dead code: the in-app browser branch is unreachable.

The handleSwitchWallet function includes logic to handle in-app browser scenarios (lines 42-43), but this function is only called from the second Switch button condition (line 161) which explicitly checks !connection.isInAppBrowser. This means the in-app browser branch will never execute.

Consider refactoring to either:

  1. Remove the in-app browser logic from handleSwitchWallet if it's meant only for desktop
  2. Use handleSwitchWallet consistently for both Switch button conditions (lines 141 and 161) to eliminate code duplication
🔎 Option 1: Remove dead code and clarify function purpose
 const handleSwitchWallet = () => {
-    if (isInAppBrowser) {
-        switchWallet();
-    } else {
-        // Desktop: navigate to select wallet screen
-        setCurrentContent({
-            type: 'select-wallet',
-            props: {
-                setCurrentContent,
-                onClose: () => {},
-                returnTo: 'profile',
-                onLogoutSuccess,
-            },
-        });
-    }
+    // Desktop: navigate to select wallet screen
+    setCurrentContent({
+        type: 'select-wallet',
+        props: {
+            setCurrentContent,
+            onClose: () => {},
+            returnTo: 'profile',
+            onLogoutSuccess,
+        },
+    });
 };

Option 2: Use handleSwitchWallet for both conditions and consolidate logic

Update handleSwitchWallet to use the connection.isInAppBrowser and add the isSwitchWalletEnabled check:

 const handleSwitchWallet = () => {
-    if (isInAppBrowser) {
-        switchWallet();
+    if (connection.isInAppBrowser && isSwitchWalletEnabled) {
+        await switchWallet();
     } else {
         // Desktop: navigate to select wallet screen
         setCurrentContent({

Then use it for the first Switch button as well:

-                        onClick={async () => {
-                            await switchWallet();
-                            // For VeWorld in-app browser, the wallet_switched event will be dispatched
-                            // by the dapp-kit when the wallet actually changes
-                        }}
+                        onClick={handleSwitchWallet}

Note: This would require making handleSwitchWallet async if you choose option 2.

🧹 Nitpick comments (1)
packages/vechain-kit/src/components/AccountModal/Components/AccountSelector.tsx (1)

127-129: Consider extracting complex condition to improve readability.

The boolean condition spanning lines 127-129 determines when to show the switch wallet button versus the copy address button. While correct, the logic could be more maintainable if extracted to a descriptive variable.

🔎 Suggested refactor
+    const shouldShowSwitchWallet =
+        (connection.isInAppBrowser && isSwitchWalletEnabled) ||
+        (!connection.isInAppBrowser && connection.isConnectedWithDappKit);
+
     return (
         <HStack
             mt={mt}
             w={'full'}
             {...style}
             justifyContent={'flex-start'}
             alignItems={'center'}
         >
             {/* ... Button component ... */}

-            {(connection.isInAppBrowser && isSwitchWalletEnabled) ||
-            (!connection.isInAppBrowser &&
-                connection.isConnectedWithDappKit) ? (
+            {shouldShowSwitchWallet ? (
                 <IconButton
                     aria-label="Switch wallet"
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab90774 and 5f1be01.

📒 Files selected for processing (2)
  • packages/vechain-kit/src/components/AccountModal/Components/AccountSelector.tsx
  • packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/migration-guide-to-v2.mdc)

**/*.{ts,tsx}: In VeChain Kit Version 2, use useThor instead of useConnex for contract interactions
For single contract read operations, use the useCallClause hook with the pattern: import dependencies, define ABI and method as const, create query key function using getCallClauseQueryKeyWithArgs, and wrap with useCallClause including data transformation in the select option
For multiple parallel contract calls, use the executeMultipleClausesCall utility wrapped in a useQuery hook with the pattern: define query key function, use executeMultipleClausesCall in queryFn mapping items to clause objects, and transform results
For transaction building and sending, use the useBuildTransaction hook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Always provide an arguments array for contract calls, even when no parameters are required - use an empty array for parameter-less functions to enable TypeScript type checking
Always conditionally enable queries using the enabled property to prevent unnecessary contract calls, checking for all required parameters: enabled: !!requiredParam && !!otherRequiredParam
Use the select option in useCallClause or transform data in queryFn to handle data transformation, particularly for converting BigInt values to strings and normalizing contract return data
Maintain consistent query key patterns: use getCallClauseQueryKeyWithArgs for contract calls with arguments and getCallClauseQueryKey for calls without arguments to ensure proper caching and invalidation
Use TypeScript as const assertions for method names and as 0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions

Files:

  • packages/vechain-kit/src/components/AccountModal/Components/AccountSelector.tsx
  • packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx
⏰ 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). (4)
  • GitHub Check: build-size-check
  • GitHub Check: Lint & Build
  • GitHub Check: deploy
  • GitHub Check: e2e-test
🔇 Additional comments (3)
packages/vechain-kit/src/components/AccountModal/Components/AccountSelector.tsx (1)

48-50: No issue found: isInAppBrowser values are already synchronized.

The isInAppBrowser exported by useSwitchWallet() is directly derived from connection.isInAppBrowser returned by useWallet() (see useSwitchWallet.ts line 21: const isInAppBrowser = connection.isInAppBrowser). Both the rendering condition on line 127 and the handler on line 66 reference the same source value—one uses the direct property and the other uses the exported alias. No inconsistency exists and no changes are needed.

Likely an incorrect or invalid review comment.

packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx (2)

152-153: Condition change correctly separates desktop from in-app browser scenarios.

The updated condition properly ensures the desktop Switch button only appears when NOT in the in-app browser and connected with DappKit. This prevents overlap with the in-app browser Switch button condition and correctly implements the three-way branching logic described in the comment.


133-165: No action needed. The code is correct as written.

The in-app and desktop Switch buttons intentionally use different conditions:

  • In-app (line 133): connection.isInAppBrowser && isSwitchWalletEnabled gates on feature availability
  • Desktop (lines 152-153): !connection.isInAppBrowser && connection.isConnectedWithDappKit gates on connection type

This difference is intentional, reflecting different capability requirements for each environment.

Additionally, connection.isInAppBrowser and isInAppBrowser from useSwitchWallet are consistent—the latter derives directly from the former (line 30 of useSwitchWallet.ts), so they are guaranteed to match.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5f1be01 and b143e58.

📒 Files selected for processing (4)
  • packages/vechain-kit/src/components/AccountModal/Components/AccountSelector.tsx
  • packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx
  • packages/vechain-kit/src/components/AccountModal/Contents/SelectWallet/SelectWalletContent.tsx
  • packages/vechain-kit/src/utils/stringUtils.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx
  • packages/vechain-kit/src/components/AccountModal/Contents/SelectWallet/SelectWalletContent.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/migration-guide-to-v2.mdc)

**/*.{ts,tsx}: In VeChain Kit Version 2, use useThor instead of useConnex for contract interactions
For single contract read operations, use the useCallClause hook with the pattern: import dependencies, define ABI and method as const, create query key function using getCallClauseQueryKeyWithArgs, and wrap with useCallClause including data transformation in the select option
For multiple parallel contract calls, use the executeMultipleClausesCall utility wrapped in a useQuery hook with the pattern: define query key function, use executeMultipleClausesCall in queryFn mapping items to clause objects, and transform results
For transaction building and sending, use the useBuildTransaction hook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Always provide an arguments array for contract calls, even when no parameters are required - use an empty array for parameter-less functions to enable TypeScript type checking
Always conditionally enable queries using the enabled property to prevent unnecessary contract calls, checking for all required parameters: enabled: !!requiredParam && !!otherRequiredParam
Use the select option in useCallClause or transform data in queryFn to handle data transformation, particularly for converting BigInt values to strings and normalizing contract return data
Maintain consistent query key patterns: use getCallClauseQueryKeyWithArgs for contract calls with arguments and getCallClauseQueryKey for calls without arguments to ensure proper caching and invalidation
Use TypeScript as const assertions for method names and as 0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions

Files:

  • packages/vechain-kit/src/utils/stringUtils.ts
  • packages/vechain-kit/src/components/AccountModal/Components/AccountSelector.tsx
⏰ 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). (4)
  • GitHub Check: Lint & Build
  • GitHub Check: deploy
  • GitHub Check: build-size-check
  • GitHub Check: e2e-test
🔇 Additional comments (2)
packages/vechain-kit/src/components/AccountModal/Components/AccountSelector.tsx (2)

77-77: LGTM!

Adding the onLogoutSuccess callback ensures the modal closes after a successful logout from the select-wallet screen.


128-130: Verify the conditional logic change aligns with intended behavior.

The switch wallet button rendering condition has been made more restrictive. For in-app browsers with a DappKit connection, the isSwitchWalletEnabled flag from the DappKit library must now also be true, whereas on desktop, only isConnectedWithDappKit is required. This same pattern is applied consistently across both AccountSelector.tsx and ProfileContent.tsx, which suggests it is intentional. However, since isSwitchWalletEnabled originates from the external @vechain/dapp-kit-react library, the exact semantics and implications of this flag require verification against the DappKit library behavior and the PR description.

Comment on lines +19 to +27
export const simpleHash = (input: string): string => {
let hash = 0;
for (let i = 0; i < input.length; i++) {
const char = input.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash; // Convert to 32bit integer
}
return hash.toString(36);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix incorrect 32-bit integer coercion.

Line 24 uses hash & hash, which is an identity operation and does not convert the value to a 32-bit integer. This defeats the purpose of the comment and may lead to incorrect hash values or overflow issues.

🔎 Proposed fix
 export const simpleHash = (input: string): string => {
     let hash = 0;
     for (let i = 0; i < input.length; i++) {
         const char = input.charCodeAt(i);
         hash = (hash << 5) - hash + char;
-        hash = hash & hash; // Convert to 32bit integer
+        hash = hash | 0; // Convert to 32bit integer
     }
     return hash.toString(36);
 };
📝 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
export const simpleHash = (input: string): string => {
let hash = 0;
for (let i = 0; i < input.length; i++) {
const char = input.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash; // Convert to 32bit integer
}
return hash.toString(36);
};
export const simpleHash = (input: string): string => {
let hash = 0;
for (let i = 0; i < input.length; i++) {
const char = input.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash | 0; // Convert to 32bit integer
}
return hash.toString(36);
};
🤖 Prompt for AI Agents
In packages/vechain-kit/src/utils/stringUtils.ts around lines 19 to 27, the
current line "hash = hash & hash" is an identity operation and does not coerce
the value to a 32-bit integer; replace it with a proper 32-bit coercion (for
example "hash = hash | 0" or "hash |= 0") so the value is truncated to a signed
32-bit integer as intended, keep the rest of the loop and return unchanged.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx (1)

109-112: Consider consolidating duplicate disconnect-confirm flows.

The disconnect-confirm navigation pattern appears in multiple locations:

  • ProfileContent footer Logout button (lines 175-186)
  • AddressDisplay Logout button (packages/vechain-kit/src/components/common/AddressDisplay.tsx, lines 182-189)
  • ProfileCard's onLogout callback (lines 109-112) differs slightly by directly calling disconnect

Both the AddressDisplay and ProfileContent footer buttons trigger nearly identical disconnect-confirm flows. Consider extracting this into a shared helper function to reduce duplication and improve maintainability.

💡 Suggested refactoring approach

Create a shared helper function:

const handleLogoutConfirm = (
    setCurrentContent: React.Dispatch<React.SetStateAction<AccountModalContentTypes>>,
    onDisconnect: () => void,
    returnTo: AccountModalContentTypes = 'profile'
) => {
    setCurrentContent({
        type: 'disconnect-confirm',
        props: {
            onDisconnect,
            onBack: () => setCurrentContent(returnTo),
        },
    });
};

Then use it in both locations:

// In ProfileContent
onClick={() => handleLogoutConfirm(setCurrentContent, () => {
    disconnect();
    onLogoutSuccess?.();
})}

// In AddressDisplay (via prop or context)
onClick={() => handleLogoutConfirm(setCurrentContent, () => onLogout?.())}

Also applies to: 175-186

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b143e58 and 54021b7.

📒 Files selected for processing (3)
  • packages/vechain-kit/src/components/AccountModal/Contents/Profile/Components/ProfileCard/ProfileCard.tsx
  • packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx
  • packages/vechain-kit/src/components/common/AddressDisplay.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/migration-guide-to-v2.mdc)

**/*.{ts,tsx}: In VeChain Kit Version 2, use useThor instead of useConnex for contract interactions
For single contract read operations, use the useCallClause hook with the pattern: import dependencies, define ABI and method as const, create query key function using getCallClauseQueryKeyWithArgs, and wrap with useCallClause including data transformation in the select option
For multiple parallel contract calls, use the executeMultipleClausesCall utility wrapped in a useQuery hook with the pattern: define query key function, use executeMultipleClausesCall in queryFn mapping items to clause objects, and transform results
For transaction building and sending, use the useBuildTransaction hook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Always provide an arguments array for contract calls, even when no parameters are required - use an empty array for parameter-less functions to enable TypeScript type checking
Always conditionally enable queries using the enabled property to prevent unnecessary contract calls, checking for all required parameters: enabled: !!requiredParam && !!otherRequiredParam
Use the select option in useCallClause or transform data in queryFn to handle data transformation, particularly for converting BigInt values to strings and normalizing contract return data
Maintain consistent query key patterns: use getCallClauseQueryKeyWithArgs for contract calls with arguments and getCallClauseQueryKey for calls without arguments to ensure proper caching and invalidation
Use TypeScript as const assertions for method names and as 0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions

Files:

  • packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx
  • packages/vechain-kit/src/components/common/AddressDisplay.tsx
  • packages/vechain-kit/src/components/AccountModal/Contents/Profile/Components/ProfileCard/ProfileCard.tsx
🧬 Code graph analysis (1)
packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx (2)
packages/vechain-kit/src/hooks/api/wallet/useTotalBalance.ts (1)
  • useTotalBalance (13-44)
packages/vechain-kit/src/components/WalletButton/AssetIcons.tsx (1)
  • AssetIcons (27-129)
⏰ 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). (4)
  • GitHub Check: Lint & Build
  • GitHub Check: e2e-test
  • GitHub Check: deploy
  • GitHub Check: build-size-check
🔇 Additional comments (6)
packages/vechain-kit/src/components/common/AddressDisplay.tsx (1)

20-20: LGTM: onLogout prop addition.

The addition of the optional onLogout callback prop is implemented correctly and follows TypeScript best practices.

Also applies to: 36-36, 45-45

packages/vechain-kit/src/components/AccountModal/Contents/Profile/Components/ProfileCard/ProfileCard.tsx (1)

21-21: LGTM: Clean prop threading.

The onLogout prop is correctly threaded from ProfileCard to AddressDisplay. The implementation is straightforward and maintains type safety.

Also applies to: 39-39, 197-197

packages/vechain-kit/src/components/AccountModal/Contents/Profile/ProfileContent.tsx (4)

10-10: LGTM: Import additions.

All new imports are appropriately used within the component for the balance display and wallet switching functionality.

Also applies to: 12-17, 28-28


47-49: LGTM: Balance hook integration.

The useTotalBalance hook is properly integrated to support the balance-aware wallet button display.


51-66: LGTM: Simplified switch-wallet logic.

The unified handleSwitchWallet function cleanly handles both in-app browser and desktop flows, improving code clarity.


123-142: LGTM: Balance-aware wallet button.

The conditional rendering of asset icons and formatted balance enhances the wallet button's informativeness when the user has assets.

Comment on lines +179 to +196
<IconButton
icon={<LuLogOut />}
onClick={() =>
setCurrentContent?.({
type: 'disconnect-confirm',
props: {
onDisconnect: () => onLogout?.(),
onBack: () =>
setCurrentContent?.('profile'),
},
})
}
variant="vechainKitSecondary"
height="30px"
w="30px"
borderRadius="5px"
aria-label="Copy address"
/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix the incorrect aria-label on the Logout button.

The aria-label is set to "Copy address" but this button triggers logout functionality. This is an accessibility issue that will confuse screen reader users.

🔎 Proposed fix
                            <IconButton
                                icon={<LuLogOut />}
                                onClick={() =>
                                    setCurrentContent?.({
                                        type: 'disconnect-confirm',
                                        props: {
                                            onDisconnect: () => onLogout?.(),
                                            onBack: () =>
                                                setCurrentContent?.('profile'),
                                        },
                                    })
                                }
                                variant="vechainKitSecondary"
                                height="30px"
                                w="30px"
                                borderRadius="5px"
-                               aria-label="Copy address"
+                               aria-label="Logout"
                            />
📝 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
<IconButton
icon={<LuLogOut />}
onClick={() =>
setCurrentContent?.({
type: 'disconnect-confirm',
props: {
onDisconnect: () => onLogout?.(),
onBack: () =>
setCurrentContent?.('profile'),
},
})
}
variant="vechainKitSecondary"
height="30px"
w="30px"
borderRadius="5px"
aria-label="Copy address"
/>
<IconButton
icon={<LuLogOut />}
onClick={() =>
setCurrentContent?.({
type: 'disconnect-confirm',
props: {
onDisconnect: () => onLogout?.(),
onBack: () =>
setCurrentContent?.('profile'),
},
})
}
variant="vechainKitSecondary"
height="30px"
w="30px"
borderRadius="5px"
aria-label="Logout"
/>
🤖 Prompt for AI Agents
In packages/vechain-kit/src/components/common/AddressDisplay.tsx around lines
179 to 196, the IconButton used to open the logout confirmation has an incorrect
aria-label "Copy address"; change it to a descriptive logout label such as
"Logout" or "Sign out" to accurately reflect the action and improve
accessibility for screen readers, leaving all other props and behavior
unchanged.

@Agilulfo1820 Agilulfo1820 merged commit 03ebc15 into main Dec 23, 2025
8 checks passed
@Agilulfo1820 Agilulfo1820 deleted the fix/add-new-wallet branch December 23, 2025 11:09
@coderabbitai coderabbitai Bot mentioned this pull request Jan 5, 2026
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.

4 participants