Repository: vultisig/vultisig-windows
Pull request #1646: Task get chains on request account method
Author: login: devzl
Created: 2025-05-20T16:11:38Z | Merged: 2025-05-22T01:57:47Z

## Description

Please include a summary of the change and which issue is fixed. 
use addressDerive to generate addresses for chains that are not selected in the portfolio on request-accounts
Fixes #1636 

## Which feature is affected?
- [ ] Create vault ( Secure / Fast) - Please ensure you created a Secure vault & fast vault
- [ ] Sending  - Please attach a tx link here
- [ ] Swap - Please attach a tx link for the swap here
- [ ] New Chain / Chain related feature  -  Please attach tx link here

## Checklist

- [ ] I have performed a self-review of my code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works

## Screenshots (if applicable):

## Additional context

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **Bug Fixes**
	- Improved account address retrieval to ensure addresses are dynamically derived when not already stored, providing more reliable results across different scenarios.
	- Enhanced vault selection logic for account generation to align with the current active vault, improving accuracy and consistency.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Linked issue #1636: [Task]: Use deriveAddress to get chains on request-accounts method
Currently all supported chain addresses are pre generated and stored. To improve efficiency and reduce unnecessary storage implement deriveAddress to generate missing chain addresses on request-accounts method.
task linked to [https://github.com/vultisig/vultisig-windows/issues/1558](url)

Diff:
diff --git a/clients/extension/src/background/handlers/accountsHandler.ts b/clients/extension/src/background/handlers/accountsHandler.ts
index 97a1ce9f9c..67c862ded1 100644
--- a/clients/extension/src/background/handlers/accountsHandler.ts
+++ b/clients/extension/src/background/handlers/accountsHandler.ts
@@ -1,7 +1,10 @@
 import { Chain } from '@core/chain/Chain'
 import { isFeeCoin } from '@core/chain/coin/utils/isFeeCoin'
+import { getPublicKey } from '@core/chain/publicKey/getPublicKey'
+import { deriveAddress } from '@core/chain/utils/deriveAddress'
 import { getVaultPublicKeyExport } from '@core/ui/vault/share/utils/getVaultPublicKeyExport'
 import { getVaultId } from '@core/ui/vault/Vault'
+import { shouldBePresent } from '@lib/utils/assert/shouldBePresent'
 
 import { Messenger } from '../../messengers/createMessenger'
 import { getVaultAppSessions } from '../../sessions/state/appSessions'
@@ -12,6 +15,7 @@ import { setStoredRequest } from '../../utils/storage'
 import { getCurrentVaultId } from '../../vault/state/currentVaultId'
 import { getVaults } from '../../vault/state/vaults'
 import { getVaultsCoins } from '../../vault/state/vaultsCoins'
+import { getWalletCore } from '../walletCore'
 import { handleOpenPanel } from '../window/windowManager'
 
 const instance: Record<Instance, boolean> = {
@@ -28,19 +32,43 @@ export const handleFindAccounts = async (
 
   if (!currentVaultId) return []
   const vaultSessions = await getVaultAppSessions(currentVaultId)
-
   const currentSession = vaultSessions[getDappHostname(sender)] ?? null
 
-  if (currentSession) {
-    const vaultsCoins = await getVaultsCoins()
-    return vaultsCoins[currentVaultId]
-      .filter(
-        accountCoin => isFeeCoin(accountCoin) && accountCoin.chain === chain
-      )
-      .map(({ address }) => address ?? '')
-  } else {
+  // Check if the chain already exists in the vault's portfolio
+  const vaultsCoins = await getVaultsCoins()
+  const existingAccount = vaultsCoins[currentVaultId].find(
+    account => isFeeCoin(account) && account.chain === chain
+  )
+
+  if (existingAccount) {
+    return [existingAccount.address]
+  }
+
+  // If not derive the address from the vault's public key via deriveAddress()
+  const vaults = await getVaults()
+  const vault = shouldBePresent(
+    vaults.find(vault => getVaultId(vault) === currentVaultId)
+  )
+
+  const walletCore = await getWalletCore()
+
+  const publicKey = getPublicKey({
+    chain,
+    walletCore,
+    hexChainCode: vault.hexChainCode,
+    publicKeys: vault.publicKeys,
+  })
+
+  const address = deriveAddress({
+    chain,
+    publicKey,
+    walletCore,
+  })
+
+  if (!currentSession) {
     return []
   }
+  return [address]
 }
 
 const handleFindVault = async (
diff --git a/clients/extension/src/background/utils/cosmosAccount.ts b/clients/extension/src/background/utils/cosmosAccount.ts
index 83682967c1..2078978ce8 100644
--- a/clients/extension/src/background/utils/cosmosAccount.ts
+++ b/clients/extension/src/background/utils/cosmosAccount.ts
@@ -1,12 +1,13 @@
 import { getWalletCore } from '@clients/extension/src/background/walletCore'
+import { getCurrentVaultId } from '@clients/extension/src/vault/state/currentVaultId'
 import { getVaults } from '@clients/extension/src/vault/state/vaults'
-import { getVaultsCoins } from '@clients/extension/src/vault/state/vaultsCoins'
 import { Chain } from '@core/chain/Chain'
-import { isFeeCoin } from '@core/chain/coin/utils/isFeeCoin'
 import { getPublicKey } from '@core/chain/publicKey/getPublicKey'
 import { toHexPublicKey } from '@core/chain/utils/toHexPublicKey'
 import { getVaultId } from '@core/ui/vault/Vault'
+import { shouldBePresent } from '@lib/utils/assert/shouldBePresent'
 import { hexToBytes } from '@lib/utils/hexToBytes'
+
 interface CosmosAccount {
   pubkey: number[]
   address: string
@@ -21,20 +22,12 @@ export const generateCosmosAccount = async (
   chain: Chain
 ): Promise<CosmosAccount[] | undefined> => {
   try {
-    const vaults = await getVaults()
-    const allCoins = await getVaultsCoins()
+    const currentVaultId = await getCurrentVaultId()
 
-    const targetVault = vaults.find(vault => {
-      const coins = allCoins[getVaultId(vault)] ?? []
-      return coins.some(
-        coin =>
-          isFeeCoin(coin) &&
-          coin.chain === chain &&
-          coin.address === responseAddress
-      )
-    })
-
-    if (!targetVault) throw new Error('Vault not found!')
+    const vaults = await getVaults()
+    const currentVault = shouldBePresent(
+      vaults.find(vault => getVaultId(vault) === currentVaultId)
+    )
 
     const walletCore = await getWalletCore()
     if (!walletCore) throw new Error('WalletCore is not initialized!')
@@ -42,8 +35,8 @@ export const generateCosmosAccount = async (
     const publicKey = getPublicKey({
       chain,
       walletCore,
-      hexChainCode: targetVault.hexChainCode,
-      publicKeys: targetVault.publicKeys,
+      hexChainCode: currentVault.hexChainCode,
+      publicKeys: currentVault.publicKeys,
     })
 
     const keyBytes = hexToBytes(toHexPublicKey({ publicKey, walletCore }))


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>clients/extension/src/background/handlers/accountsHandler.ts (1)</summary><blockquote>

`70-76`: **Consider moving the session check earlier.**

The current implementation performs address derivation even when there's no current session, only to return an empty array afterward. Consider checking for the session earlier to avoid unnecessary derivation work.

```diff
  try {
+   if (!currentSession) {
+     return []
+   }
    const publicKey = getPublicKey({
      chain,
      walletCore,
      hexChainCode: vault.hexChainCode,
      publicKeys: vault.publicKeys,
    })

    const address = deriveAddress({
      chain,
      publicKey,
      walletCore,
    })

-   if (!currentSession) {
-     return []
-   }
    return [address]
  } catch (error) {
    console.error(`Error deriving address for chain ${chain}:`, error)
    return []
  }
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**
**Cache: Disabled due to data retention organization setting**
**Knowledge Base: Disabled due to data retention organization setting**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between fbfb8cd5f8a1e44a32d9a126014abe5d0b233148 and 01b42d09e866f5448962efacbdc0e21ffda902e3.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `clients/extension/src/background/handlers/accountsHandler.ts` (3 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>clients/extension/src/background/handlers/accountsHandler.ts (6)</summary><blockquote>

<details>
<summary>clients/extension/src/vault/state/vaultsCoins.ts (1)</summary>

* `getVaultsCoins` (13-13)

</details>
<details>
<summary>clients/extension/src/vault/state/vaults.ts (1)</summary>

* `getVaults` (11-11)

</details>
<details>
<summary>core/ui/vault/Vault.ts (1)</summary>

* `getVaultId` (21-21)

</details>
<details>
<summary>clients/extension/src/background/walletCore.ts (1)</summary>

* `getWalletCore` (5-10)

</details>
<details>
<summary>core/chain/publicKey/getPublicKey.ts (1)</summary>

* `getPublicKey` (18-56)

</details>
<details>
<summary>core/chain/utils/deriveAddress.ts (1)</summary>

* `deriveAddress` (16-44)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (1)</summary>

* GitHub Check: lint

</details>

<details>
<summary>🔇 Additional comments (7)</summary><blockquote>

<details>
<summary>clients/extension/src/background/handlers/accountsHandler.ts (7)</summary>

`3-4`: **Good addition of imports for the new address derivation functionality.**

The new imports for `getPublicKey` and `deriveAddress` are appropriate for implementing the dynamic address derivation feature.

---

`17-17`: **Appropriate import for walletCore.**

This import is necessary for the new address derivation functionality.

---

`36-46`: **Good implementation for checking existing fee coin addresses first.**

This approach efficiently checks for already derived addresses before attempting to derive new ones, which avoids redundant operations.

---

`48-55`: **Proper setup for address derivation.**

The code correctly retrieves the vault and initializes the wallet core before attempting to derive addresses.

---

`56-69`: **Good implementation of address derivation using core utilities.**

The code properly uses the `getPublicKey` and `deriveAddress` utilities with the correct parameters to derive addresses for the requested chain.

---

`36-76`: **Excellent implementation of the feature to get chains on request-accounts method.**

This implementation successfully addresses the issue by deriving addresses for chains that are not currently selected in the portfolio during the request-accounts process. The code first checks for existing fee coin addresses and falls back to dynamic derivation if needed, with proper error handling.

---

`56-76`: <details>
<summary>✅ Verification successful</summary>

**Verify that all chain types are properly supported by the derivation functions.**

The code now derives addresses dynamically, which might encounter issues with certain chain types. Ensure that all supported chains work correctly with the `getPublicKey` and `deriveAddress` functions.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check for all chain types used in the codebase to ensure compatibility
echo "Checking for all Chain enum values used in the codebase..."
rg -A 1 "Chain\.[A-Za-z]+" --type ts
```

Length of output: 65238

---

🏁 Script executed:

```shell
#!/bin/bash
# Locate and display the deriveAddress utility to inspect chain support
fd deriveAddress.ts -t f
echo "---- File Content of deriveAddress.ts ----"
sed -n '1,200p' core/chain/utils/deriveAddress.ts
```

Length of output: 1114

---

🏁 Script executed:

```shell
#!/bin/bash
# Show import section of accountsHandler.ts to locate getPublicKey definition
sed -n '1,30p' clients/extension/src/background/handlers/accountsHandler.ts

# Locate the getPublicKey utility in the codebase
fd getPublicKey.ts -t f
echo "---- File Content of getPublicKey.ts ----"
sed -n '1,200p' core/chain/utils/getPublicKey.ts
```

Length of output: 1674

---

🏁 Script executed:

```shell
#!/bin/bash
# Display the getPublicKey implementation to verify chain support
sed -n '1,200p' core/chain/publicKey/getPublicKey.ts
```

Length of output: 1468

---



</details>

**All chain types are supported by `getPublicKey` and `deriveAddress`.**

- `getPublicKey` dispatches by `ChainKind` (EVM, UTXO, Cosmos, Other), selecting the correct signature algorithm and key‐derivation path or using the provided eddsa key.
- `deriveAddress` special‐cases `MayaChain` (Bech32) and strips the prefix for `BitcoinCash`, then falls back to `walletCore.CoinTypeExt.deriveAddressFromPublicKey` for all other chains.

No unsupported chains were found in these utilities.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T16:15:11Z
user: login: rcoderdev
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-05-21T03:51:31Z
user: login: devzl
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T09:06:47Z
user: login: rcoderdev
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-21T13:52:23Z
user: login: rcoderdev
body: Good, just a small comment
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-21T13:52:58Z
user: login: Ehsan-saradar
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-21T20:01:10Z
user: login: rcoderdev
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-22T01:57:27Z

Review comments:
diff_hunk: @@ -28,17 +31,48 @@ export const handleFindAccounts = async (
 
   if (!currentVaultId) return []
   const vaultSessions = await getVaultAppSessions(currentVaultId)
-
   const currentSession = vaultSessions[getDappHostname(sender)] ?? null
 
-  if (currentSession) {
-    const vaultsCoins = await getVaultsCoins()
-    return vaultsCoins[currentVaultId]
-      .filter(
-        accountCoin => isFeeCoin(accountCoin) && accountCoin.chain === chain
-      )
-      .map(({ address }) => address ?? '')
-  } else {
+  // First check if we have the address in our coins
+  const vaultsCoins = await getVaultsCoins()
+  const existingCoins = vaultsCoins[currentVaultId]
+    .filter(
+      accountCoin => isFeeCoin(accountCoin) && accountCoin.chain === chain
+    )
+    .map(({ address }) => address ?? '')
path: clients/extension/src/background/handlers/accountsHandler.ts
user: login: rcoderdev
body: Let's avoid using this kind of notation `?? ''`, as it indicates that we are intentionally setting the value to an invalid one. If something is wrong we should throw an error, I don't see why there will be a missing address.
created_at: 2025-05-21T03:44:31Z
author_association: COLLABORATOR
diff_hunk: @@ -28,17 +31,48 @@ export const handleFindAccounts = async (
 
   if (!currentVaultId) return []
   const vaultSessions = await getVaultAppSessions(currentVaultId)
-
   const currentSession = vaultSessions[getDappHostname(sender)] ?? null
 
-  if (currentSession) {
-    const vaultsCoins = await getVaultsCoins()
-    return vaultsCoins[currentVaultId]
-      .filter(
-        accountCoin => isFeeCoin(accountCoin) && accountCoin.chain === chain
-      )
-      .map(({ address }) => address ?? '')
-  } else {
+  // First check if we have the address in our coins
+  const vaultsCoins = await getVaultsCoins()
+  const existingCoins = vaultsCoins[currentVaultId]
+    .filter(
+      accountCoin => isFeeCoin(accountCoin) && accountCoin.chain === chain
+    )
+    .map(({ address }) => address ?? '')
path: clients/extension/src/background/handlers/accountsHandler.ts
user: login: rcoderdev
body: If you need to find a single account, why use filter and map instead of using find and naming it existingCoin? Why should there be multiple coins for a chain? Also why call it coins, if those are addresses?
created_at: 2025-05-21T03:47:22Z
author_association: COLLABORATOR
diff_hunk: @@ -28,17 +31,48 @@ export const handleFindAccounts = async (
 
   if (!currentVaultId) return []
   const vaultSessions = await getVaultAppSessions(currentVaultId)
-
   const currentSession = vaultSessions[getDappHostname(sender)] ?? null
 
-  if (currentSession) {
-    const vaultsCoins = await getVaultsCoins()
-    return vaultsCoins[currentVaultId]
-      .filter(
-        accountCoin => isFeeCoin(accountCoin) && accountCoin.chain === chain
-      )
-      .map(({ address }) => address ?? '')
-  } else {
+  // First check if we have the address in our coins
+  const vaultsCoins = await getVaultsCoins()
+  const existingCoins = vaultsCoins[currentVaultId]
+    .filter(
+      accountCoin => isFeeCoin(accountCoin) && accountCoin.chain === chain
+    )
+    .map(({ address }) => address ?? '')
path: clients/extension/src/background/handlers/accountsHandler.ts
user: login: rcoderdev
body: Maybe this function should be renamed if there could be only a single account?
created_at: 2025-05-21T03:48:52Z
author_association: COLLABORATOR
diff_hunk: @@ -28,17 +31,48 @@ export const handleFindAccounts = async (
 
   if (!currentVaultId) return []
   const vaultSessions = await getVaultAppSessions(currentVaultId)
-
   const currentSession = vaultSessions[getDappHostname(sender)] ?? null
 
-  if (currentSession) {
-    const vaultsCoins = await getVaultsCoins()
-    return vaultsCoins[currentVaultId]
-      .filter(
-        accountCoin => isFeeCoin(accountCoin) && accountCoin.chain === chain
-      )
-      .map(({ address }) => address ?? '')
-  } else {
+  // First check if we have the address in our coins
+  const vaultsCoins = await getVaultsCoins()
+  const existingCoins = vaultsCoins[currentVaultId]
+    .filter(
+      accountCoin => isFeeCoin(accountCoin) && accountCoin.chain === chain
+    )
+    .map(({ address }) => address ?? '')
+
+  if (existingCoins.length > 0) {
+    return existingCoins
+  }
+
+  // If not derive the address from the vault's public key via deriveAddress()
+  const vaults = await getVaults()
+  const vault = vaults.find(vault => getVaultId(vault) === currentVaultId)
+  if (!vault) return []
path: clients/extension/src/background/handlers/accountsHandler.ts
user: login: rcoderdev
body: Is an empty array a valid output? If not, throw an error using `shouldBePresent`.
created_at: 2025-05-21T03:49:32Z
author_association: COLLABORATOR
diff_hunk: @@ -28,17 +31,48 @@ export const handleFindAccounts = async (
 
   if (!currentVaultId) return []
   const vaultSessions = await getVaultAppSessions(currentVaultId)
-
   const currentSession = vaultSessions[getDappHostname(sender)] ?? null
 
-  if (currentSession) {
-    const vaultsCoins = await getVaultsCoins()
-    return vaultsCoins[currentVaultId]
-      .filter(
-        accountCoin => isFeeCoin(accountCoin) && accountCoin.chain === chain
-      )
-      .map(({ address }) => address ?? '')
-  } else {
+  // First check if we have the address in our coins
+  const vaultsCoins = await getVaultsCoins()
+  const existingCoins = vaultsCoins[currentVaultId]
+    .filter(
+      accountCoin => isFeeCoin(accountCoin) && accountCoin.chain === chain
+    )
+    .map(({ address }) => address ?? '')
+
+  if (existingCoins.length > 0) {
+    return existingCoins
+  }
+
+  // If not derive the address from the vault's public key via deriveAddress()
+  const vaults = await getVaults()
+  const vault = vaults.find(vault => getVaultId(vault) === currentVaultId)
+  if (!vault) return []
+
+  const walletCore = await getWalletCore()
+  if (!walletCore) return []
path: clients/extension/src/background/handlers/accountsHandler.ts
user: login: rcoderdev
body: There should be no need for a walletCore; the function should always return walletCore.
created_at: 2025-05-21T03:50:25Z
author_association: COLLABORATOR
diff_hunk: @@ -28,17 +31,48 @@ export const handleFindAccounts = async (
 
   if (!currentVaultId) return []
   const vaultSessions = await getVaultAppSessions(currentVaultId)
-
   const currentSession = vaultSessions[getDappHostname(sender)] ?? null
 
-  if (currentSession) {
-    const vaultsCoins = await getVaultsCoins()
-    return vaultsCoins[currentVaultId]
-      .filter(
-        accountCoin => isFeeCoin(accountCoin) && accountCoin.chain === chain
-      )
-      .map(({ address }) => address ?? '')
-  } else {
+  // First check if we have the address in our coins
+  const vaultsCoins = await getVaultsCoins()
+  const existingCoins = vaultsCoins[currentVaultId]
+    .filter(
+      accountCoin => isFeeCoin(accountCoin) && accountCoin.chain === chain
+    )
+    .map(({ address }) => address ?? '')
+
+  if (existingCoins.length > 0) {
+    return existingCoins
+  }
+
+  // If not derive the address from the vault's public key via deriveAddress()
+  const vaults = await getVaults()
+  const vault = vaults.find(vault => getVaultId(vault) === currentVaultId)
+  if (!vault) return []
+
+  const walletCore = await getWalletCore()
+  if (!walletCore) return []
+
+  try {
+    const publicKey = getPublicKey({
+      chain,
+      walletCore,
+      hexChainCode: vault.hexChainCode,
+      publicKeys: vault.publicKeys,
+    })
+
+    const address = deriveAddress({
+      chain,
+      publicKey,
+      walletCore,
+    })
+
+    if (!currentSession) {
+      return []
+    }
+    return [address]
+  } catch (error) {
path: clients/extension/src/background/handlers/accountsHandler.ts
user: login: rcoderdev
body: I feel the same way; there's no need for a try-catch block. If it fails, let it fail—just don't return invalid output.
created_at: 2025-05-21T03:51:07Z
author_association: COLLABORATOR
diff_hunk: @@ -28,17 +31,48 @@ export const handleFindAccounts = async (
 
   if (!currentVaultId) return []
   const vaultSessions = await getVaultAppSessions(currentVaultId)
-
   const currentSession = vaultSessions[getDappHostname(sender)] ?? null
 
-  if (currentSession) {
-    const vaultsCoins = await getVaultsCoins()
-    return vaultsCoins[currentVaultId]
-      .filter(
-        accountCoin => isFeeCoin(accountCoin) && accountCoin.chain === chain
-      )
-      .map(({ address }) => address ?? '')
-  } else {
+  // First check if we have the address in our coins
+  const vaultsCoins = await getVaultsCoins()
+  const existingCoins = vaultsCoins[currentVaultId]
+    .filter(
+      accountCoin => isFeeCoin(accountCoin) && accountCoin.chain === chain
+    )
+    .map(({ address }) => address ?? '')
path: clients/extension/src/background/handlers/accountsHandler.ts
user: login: devzl
body: I renamed existingCoins to use more accurate terminology (account instead of coin) and simplified the existing logic by directly returning the address if it's found, to match the new changes.
created_at: 2025-05-21T09:06:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -28,19 +32,46 @@ export const handleFindAccounts = async (
 
   if (!currentVaultId) return []
   const vaultSessions = await getVaultAppSessions(currentVaultId)
-
   const currentSession = vaultSessions[getDappHostname(sender)] ?? null
 
-  if (currentSession) {
-    const vaultsCoins = await getVaultsCoins()
-    return vaultsCoins[currentVaultId]
-      .filter(
-        accountCoin => isFeeCoin(accountCoin) && accountCoin.chain === chain
-      )
-      .map(({ address }) => address ?? '')
-  } else {
+  // Check if the chain already exists in the vault's portfolio
+  const vaultsCoins = await getVaultsCoins()
+  const existingAccount = vaultsCoins[currentVaultId].find(
+    account => isFeeCoin(account) && account.chain === chain
+  )
+
+  if (existingAccount) {
+    if (!existingAccount.address) {
path: clients/extension/src/background/handlers/accountsHandler.ts
user: login: rcoderdev
body: I don't believe we need to verify the address, as there should not be a situation where a coin entity is missing an address.
created_at: 2025-05-21T13:52:22Z
author_association: COLLABORATOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-05-20T16:11:45Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

<details>
<summary>📝 Walkthrough</summary>

"""
## Walkthrough

The logic in the `handleFindAccounts` function was updated to first check for existing fee coin addresses for a given vault and chain. If none are found, it now dynamically derives the address using vault and wallet core utilities. The `generateCosmosAccount` function was modified to select the current vault directly instead of searching by fee coin. Both functions maintain their original signatures.

## Changes

| File(s)                                                      | Change Summary                                                                                              |
|--------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------|
| clients/extension/src/background/handlers/accountsHandler.ts | Modified `handleFindAccounts` to first return stored addresses if available, otherwise derive them dynamically using `deriveAddress`. |
| clients/extension/src/background/utils/cosmosAccount.ts      | Updated `generateCosmosAccount` to select the current vault by ID instead of filtering vaults by fee coin; removed unused imports and added new utility imports. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Dapp
    participant AccountsHandler
    participant Vault
    participant WalletCore

    Dapp->>AccountsHandler: Request account addresses (vault, chain)
    AccountsHandler->>AccountsHandler: Check for existing fee coin addresses
    alt Addresses found
        AccountsHandler-->>Dapp: Return stored addresses
    else No addresses found
        AccountsHandler->>Vault: Retrieve vault and wallet core
        AccountsHandler->>WalletCore: getPublicKey
        AccountsHandler->>WalletCore: deriveAddress(publicKey, chain)
        alt Derivation successful
            AccountsHandler-->>Dapp: Return derived address
        else Derivation fails
            AccountsHandler-->>Dapp: Return empty array
        end
    end
```

## Assessment against linked issues

| Objective                                                                                                    | Addressed | Explanation                                                                                 |
|--------------------------------------------------------------------------------------------------------------|-----------|---------------------------------------------------------------------------------------------|
| Use deriveAddress to generate missing chain addresses on request-accounts method (#1636)                      | ✅        |                                                                                             |

## Poem

> In the warren of code where addresses hide,  
> Now coins are fetched, or derived with pride.  
> If none are found, we try anew—  
> With public keys, the magic grew!  
> No more empty paws, we hop with glee,  
> Dynamic addresses for all to see!  
> 🐇✨
"""

</details>

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**
**Cache: Disabled due to data retention organization setting**
**Knowledge Base: Disabled due to data retention organization setting**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 13de9961d5aae8e5dcdcad4d3eaae860b0124012 and c7c545e958a0772c8ab0b3b9933943ad9f02d923.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `clients/extension/src/background/handlers/accountsHandler.ts` (3 hunks)
* `clients/extension/src/background/utils/cosmosAccount.ts` (2 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* clients/extension/src/background/handlers/accountsHandler.ts

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (2)</summary>

* GitHub Check: lint
* GitHub Check: Analyze (javascript-typescript)

</details>

<details>
<summary>🔇 Additional comments (4)</summary><blockquote>

<details>
<summary>clients/extension/src/background/utils/cosmosAccount.ts (4)</summary>

`2-2`: **Imports align well with the implementation change.**

The added imports for `getCurrentVaultId` and `shouldBePresent` are appropriate for the approach of directly using the current vault rather than filtering by fee coins.



Also applies to: 8-8

---

`25-30`: **LGTM! Direct vault selection approach is cleaner.**

This implementation correctly addresses the issue mentioned in the PR comments by using the current vault directly instead of filtering by fee coins. The code is now more straightforward and should resolve the problem with retrieving Cosmos addresses when multiple chains are selected.

---

`38-39`: **Using currentVault properties correctly.**

The change correctly uses properties from the current vault for public key derivation, which aligns with the overall approach of using the current vault context.

---

`20-58`: **Verify this change works with the reported test case.**

The implementation looks sound, but make sure to verify that this change resolves the specific scenario mentioned in the PR comments where selecting all supported chains for CTRL on Thorswap was failing.

Could you confirm that this implementation has been tested with the scenario where multiple chains are selected in Thorswap? This was specifically mentioned as a failing case in the PR comments.

</details>

</blockquote></details>

</details>
<!-- internal state start -->


<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEYDEZyAAUASpETZWaCrKNwSPbABsvkCiQBHbGlcSHFcLzpIACJoNEQAa0hSUIZYbQxkfCx/IJD0BiZsDFC2XFhFaMg5SGxESkglCQAvXzRaWn9EeuQkBw8zAEYANgBmYY0YWA80zFIUDAl8LylkOvgMIjDpyAADds7pRAARSkkSXcgAM2KxeGyw/GSyZRp0Dq6e6/w+AS98BgJWYbRjpDbIcrUdD+SAYfCpbAUfwlLzyeqRMRREHlDzcH64K7Le6NREbLY4va5YKIXBgNCFfDFXCIS68AFHSZwVDYbi0ageMgOLrbKEHT7Sb58Yro0Q0ejAzLQjxoLyIJ6kchUOWQADu0xygWpcs5O1sjXwErhoUQ3FE8Cu8kw8m4znEDG8zmuJGoiOVVyusqiDjS6GQEjQ3lS/mo9wwABp7GRaGSE4gdWhuNwU/w+OQdaCMmB/F5+fQAz6uiaPEpEAwKPBuOIHhsGF5sDX0KDRAkvEhQoS+EwlH4SBJ4CQdQmmKx2Nn0xQMGTnpqY9kE5h6DQadmBHhYdkPPgrttpB51CRmMhnB5mM4ElF4gpmNxIsbIAA5J61/yCirMnPvMmTYYCqCglCQAAeoTXjwFD4GOSi0FWng+COeQ0igV7hvAJZ/B4A4jmOE7oKEFIAKo2AAMlwsC4Lg3CIBwAD0TFEOosDYAIGjTkxEiRkg8BELx/GIIJYA6hstD4DqiBMdw3heExIwACwTOYlgAMIsGwJTIA4TguG4OzTjp/4PBS8moVS+S8PAt71qiT74XBzAnrU9R8AAorAiCYGAvlUHyFAJnqTxkEQaCkPQEnlG5EblD8jSjq0I4RRQyabGEIRLhu1y3MBKrqPIfTUpM3m+Rg/nOO0nrxPe0XTDifAUrMmwSuk9ACCQZBZTSD4YPQ/h4hQ2qQqEeo9cmvl/DlqEKsgkEMCQjaQAAQtAGnoANvXiJlFJmpFGQYXAPxphm1RoiQGK7VsKq+PNCYtdk5B3A8Y5oG5GnQFRsHwfASh8Fc2iREhkAAGKIk1O1LogFQ6lEY1uXZL4Xuwq5YDqPwJMgTBIrKjkTVgW7ajFsCdqJmyRAWIK7v2wPIITibXbNvgOJm+JRPNkqQF9P0PCdFBndwGhGOVfkBTVfD/bOVzjpuOy9vejkMBG9T8MeFJAzhvqhltmH9Lq7Ejrg9ajkuWmIMw+BXh8RzSJMADKtoMPa8Aqz4sgJtiOy7EwlvWwAggyTIaMylyy5Ej3GYiyKhMjkSmej7kSrsuDOCkABqEZeLguwhQgIYZrazgQk8XW/Xht5umVPni9VQX2NgRCkH18tQkNJYu5lqfpyQuBZ5Glyk3s7p4yUA855c3szIoHiY94ZbwBBSPdMEkyfnlI3TIO2nsFkfBdHimQSlcLlxXgFR8PDML+LjiELG5bIIVEfJpyLBj6MY4BQEm6s4AQxAXhak5rvEoXBeD8GELKc4Mh5BDmUKodQWgdCfxMFALkvQrxYHioQDUrwQEzjAX4NA+Z9L2XkDUeBVBEGaG0LoMAhgv6mAMK2ccukmKQRoJkWMTFEAUAYExAQ9IEhEDgsUWgTF0gDUiILJi9Iii6QABIbhkaHRiBhoiaIMBYSAAcACSgCVzajIc4eQR4CxtUQEZDwuwpG0EiGDSSQcFFhzyhgV6GNHxW2TLLBGTxWrzApP8NiDBub+BNuOMcmUAwzxBGKO2yACIfTYlILA4ZIxbXlGCDAkwrD+DHIyRAqIvbHg+qPWOiZuixkgJBPsUQCIUj5JmL2oRwmInIPQO6XpYnYNtt0E+iUWox3YJAdJOdMnUywKfFg9gCD+HoGM1I+BwTr2klHfC+Vqmy0FqkaYgJeilIwPIGJCgQQNOMtkmpEE+y9CJjsRZAByPScyQHglSs4exRw/49FEtkSYejjzBjJpgAoLjMLfHES0427Tej/nid0B+H0KZEEiGAK6qMSjQioLIf5x44SzJ+PU7qpysC1JpIgKFAMYFxT6cgWgsgQLMDdndCh8hwmmyieSe52doLbXTD4PuCh/DrKwNOeSN1H6cV7KE+8jptrwrWMivYKQrBSrdgAaRILIS4uVdhUqkAHWluxcX7kYMMzFPzqlkrhaRBAyA8QbFwFCtpC4sE1OfLgR0SI0CyAANz8CahJeozq+4wrcvqh8tLEXYO9TiqYqAO70glBZesiVgluxFKEbIjkXUdIJfMklBzyZHCtdc1u64Og5WuHdIRgJHhJXrFIGlhwEWM3xTSQl8pllYFQIScR78dEBxzq8WMpdw2iBLFqUdf9ILDW1IleSM1QmznENIIwUAN4BOTU8CkokiAgVwL6LIx5Z0c3oAutVy6SjqHHAzSgN52gkGQjcdxwE9h2IcU44OulLj+FvG84eu60BsHsIJA9voRaaOiOu5hrC94cKgoKHhfCBG1pEWIgaTE8A4Vkn7K2iBnGMhKGojgGitGDoMXg4B9ATEuD/luqxBh3BuI8cqoB/ILb4cI0yIeXjFCuz8RY+YcM3JpwoCkUZPLwUykxKDPJZtCnFJQK0vuHKHyoUWVgzpc1u0UpPFgeozg0j1MSh9RZYE04bCrScpgIIq5pCXBSFJPUFQTMPtkNWCqTSoHTaE9MyA/3wToP8zINB2jrJY2+uE+ZBCWcVEMseoRzN6OOKM+AH1dgpA0ua/uPK9G0AABQAEo85elwMZq8GmeV6dysmW+ERjmSQhNyjJ9mEB7TtWaxLkmMkpfXP0kaS51D2q6GQJahtYq7FhoyLwtBVokHk/UEoxqpgeAcAIeoeRMV1EioeY85nX4fQIvqxzOxF3SsgLK+wMVjNljPimhTdRHJ9u2uZgg2xUDlJGYs/5z58QBauqWetFJAtSC7WcnCNB6yZV85AArmW+4T2ZFpcEOrtq7CQGDbqKOMC7CK7qe9SVXx0HXNtPMKA/sjUSYlBHuBsuJaR/ltH9ApsVAXvNxb7Ah6E4OMF1bI4MLmM1pstcCxWztiXPy18QqPDssiSqBM52M1Xf1ejUng1Q0LlmRQbAYhfQJj/UdWo7ipFRXfupXRw6p0eeBzsJQrZqrAWPVcudUQL1Lpqde1djGN3+NNxKa+D7hzvd3WBise23KnpGvUkXWAEdGJIJxwO37c6Qa0R/BhTCf7bXMTgwx+Cu2EM4MQ0hjhyEXQUADFQahaEoMz2gp8TLcAAH1/qIGb/k8c8NaDN5pK6ehjCG8AAZBgCGUgAJloEPgAnCQAAHMMYYVwACsyllJz+n8McfJAgYMAELQBgQ+SDj8GP6Pk0+h/b9GAPgwDfBijCUNPzfgxaDL7QGgefJBl8H4P+0ZStBRhvQP8F8h8BAR9x9lJwCb8G8GAAB2BgVfZfEgafZfOfNAIfWA2A8fBgNAsAgQUYAQJ/UYUYafZSUYdoafK4S/Wgafcfa/T+W/b+RvdQVvWgdvTvCcOgZvX+fQIAA= -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=vultisig/vultisig-windows&utm_content=1646):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: Ehsan-saradar
created_at: 2025-05-21T14:06:13Z
body: @devzl 
Were you able to test it?
I disabled all chains except BTC, then tested your PR against Thorswap by selecting all chains.
I tried to connect Vultisig via the CTRL provider, but it failed to connect.
author_association: COLLABORATOR
user: login: Ehsan-saradar
created_at: 2025-05-21T14:08:51Z
body: @devzl 
It works fine when tested with a single chain, but if you try it on Thorswap and select all supported chains for CTRL, it fails.


author_association: COLLABORATOR
user: login: Ehsan-saradar
created_at: 2025-05-21T14:32:24Z
body: I believe the issue is with retrieving cosmos addresses. In `cosmosAccount.ts` instead of the current implementation which is using `targetVault` just use the currentVault and the issue would be fixed.
author_association: COLLABORATOR