Skip to content

feat: enhance restricted address handling in App.vue and index.vue#775

Merged
kvhnuke merged 8 commits intodevop/package-updates-2-15from
devop/active-wallet-screen
Dec 17, 2025
Merged

feat: enhance restricted address handling in App.vue and index.vue#775
kvhnuke merged 8 commits intodevop/package-updates-2-15from
devop/active-wallet-screen

Conversation

@kvhnuke
Copy link
Contributor

@kvhnuke kvhnuke commented Dec 9, 2025

Summary by CodeRabbit

  • New Features

    • Detects address-specific restrictions and shows tailored messaging including the restricted address.
    • Adds a “Switch Address” action to move to an unrestricted account when blocked.
    • Disables Connect/Send actions for restricted addresses to prevent blocked transactions.
    • Updates restriction UI content, support text, and recovery/modal flows for clarity and consistency.
  • Chores

    • Removes the Bitrock network from available networks (no longer selectable).

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

@coderabbitai
Copy link

coderabbitai bot commented Dec 9, 2025

Caution

Review failed

Failed to post review comments

Walkthrough

Replaces NodeJS.ProcessEnv typings with a Window-based package-version declaration and expands address-restriction handling across the extension: a single-account reactive isAddressRestricted state, a checkAddress helper, a switchToUnrestrictedAddress action, and UI gating/props for restricted flows in multiple provider views.

Changes

Cohort / File(s) Change Summary
Type Definitions
packages/extension/env.d.ts
Removed exported NodeJS.ProcessEnv ambient namespace (BROWSER, MINIFY). Added global interface Window { readonly __PACKAGE_VERSION__: string } and retained declare let __PACKAGE_VERSION__: string;.
App Component Logic
packages/extension/src/ui/action/App.vue
Replaced foundRestrictedAddress with isAddressRestricted (ref { isRestricted, address }); added checkAddress(activeAccount) helper; updated call sites (setNetwork, onSelectedAddressChanged); added switchToUnrestrictedAddress() to select a non-restricted account; removed KadenaNetwork import; updated template props/events to pass :is-address-restricted, :restricted-address and listen for @restricted:switch-account.
Restricted View Component
packages/extension/src/ui/action/views/restricted/index.vue
Added props isAddressRestricted: Boolean and restrictedAddress: String; declared emit restricted:switchAccount; changed messaging to show IP- or address-based restriction and added a clickable "Switch Address" action that emits the switch event.
Ethereum Provider UIs
packages/extension/src/providers/ethereum/ui/eth-connect-dapp.vue, packages/extension/src/providers/ethereum/ui/send-transaction/index.vue
Introduced isRestricted state via isWalletRestricted utility; gate UI (disable Connect/Send when restricted); run restriction checks on load and on selected-address change; update validation/error messaging to reflect restricted addresses.
Networks Removal
packages/extension/src/providers/ethereum/networks/bitrock.ts, packages/extension/src/providers/ethereum/networks/index.ts, packages/types/src/networks.ts
Removed bitrock.ts module, removed bitrock export from networks index, and removed Bitrock enum entries from NetworkNames and CoingeckoPlatform.
JSON Tree View Minor Change
packages/extension/src/libs/json-tree-view/JsonTreeViewItem.vue
Replaced .default(...) terminal handler with .otherwise(...) in switch-ts value-color mapping; behavior preserved.
Package Manifests (many packages)
package.json, packages/*/package.json
Multiple devDependency and dependency version bumps across packages (linting, TypeScript, polkadot, tooling, etc.) with no functional code changes.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant App as App.vue
participant Restricted as Restricted View
participant WalletUtil as isWalletRestricted
participant EthereumUI as Ethereum Provider UI
participant User as User

User->>App: selects account / loads extension
App->>WalletUtil: checkAddress(selectedAccount)
WalletUtil-->>App: { isRestricted, address }
alt isRestricted true
    App->>Restricted: render with :is-address-restricted=true, :restricted-address
    Restricted->>User: show address-restricted message + "Switch Address"
    User->>Restricted: click "Switch Address"
    Restricted->>App: emit restricted:switchAccount
    App->>App: switchToUnrestrictedAddress() (find non-restricted)
    App->>EthereumUI: onSelectedAddressChanged(newAccount)
    EthereumUI->>WalletUtil: isWalletRestricted(newAccount)
    WalletUtil-->>EthereumUI: { isRestricted:false }
    EthereumUI->>User: allow actions (Connect/Send)
else isRestricted false
    App->>EthereumUI: render normal flows
end

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Areas to focus on:
    • Event-name consistency: App listens for restricted:switch-account while Restricted emits restricted:switchAccount.
    • Edge cases in checkAddress() and switchToUnrestrictedAddress() (no available unrestricted accounts, async timing).
    • Correct wiring of isWalletRestricted in Ethereum UIs and its effect on validation/disabled state.
    • Removal of Bitrock: ensure no lingering references and enum consistency across consumers.
    • Type-definition impact from removing NodeJS.ProcessEnv entries.

Possibly related PRs

Suggested reviewers

  • olgakup
  • SemajaM
  • NickKelly1

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: enhanced restricted address handling across App.vue and the restricted view component, reflecting core modifications to restriction logic and UI presentation.
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 devop/active-wallet-screen

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.

@github-actions
Copy link

github-actions bot commented Dec 9, 2025

💼 Build Files
chrome: enkrypt-chrome-dacba521.zip
firefox: enkrypt-firefox-dacba521.zip

💉 Virus total analysis
chrome: dacba521
firefox: dacba521

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: 2

🧹 Nitpick comments (2)
packages/extension/src/ui/action/views/restricted/index.vue (1)

101-112: Simplify primitive prop defaults.

Arrow functions for primitive defaults add unnecessary overhead. Vue recommends direct values for Boolean and String types.

   isAddressRestricted: {
     type: Boolean,
-    default: () => {
-      return false;
-    },
+    default: false,
   },
   restrictedAddress: {
     type: String,
-    default: () => {
-      return '';
-    },
+    default: '',
   },
packages/extension/src/ui/action/App.vue (1)

439-453: Consider avoiding UI flash during restriction check.

Resetting isAddressRestricted to false (lines 440-443) before the async isWalletRestricted call completes could briefly show unrestricted UI to a restricted user. Consider deferring the reset or adding a loading state.

 const checkAddress = async (activeAccount: EnkryptAccount) => {
-  isAddressRestricted.value = {
-    isRestricted: false,
-    address: '',
-  };
   const isRestricted = await isWalletRestricted(
     currentNetwork.value.displayAddress(activeAccount.address),
   );
-  if (isRestricted) {
-    isAddressRestricted.value = {
-      isRestricted,
-      address: currentNetwork.value.displayAddress(activeAccount.address),
-    };
-  }
+  isAddressRestricted.value = {
+    isRestricted,
+    address: isRestricted
+      ? currentNetwork.value.displayAddress(activeAccount.address)
+      : '',
+  };
 };
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ad6990d and 119208d.

📒 Files selected for processing (3)
  • packages/extension/env.d.ts (1 hunks)
  • packages/extension/src/ui/action/App.vue (6 hunks)
  • packages/extension/src/ui/action/views/restricted/index.vue (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: buildAll
  • GitHub Check: test
  • GitHub Check: test
🔇 Additional comments (4)
packages/extension/env.d.ts (1)

17-20: LGTM!

The Window interface extension properly exposes __PACKAGE_VERSION__ for both direct and window. access patterns, which is a clean TypeScript approach.

packages/extension/src/ui/action/views/restricted/index.vue (2)

5-15: LGTM!

The conditional rendering cleanly separates the two restriction scenarios (geo/IP-based vs address-based), and displaying the restricted address helps users understand which account is affected.


31-40: LGTM!

The Switch Address block provides clear UX for users to escape a restricted address state. The event emission pattern is correctly typed.

packages/extension/src/ui/action/App.vue (1)

7-12: LGTM!

The Restricted component is correctly wired with the new props and event handler.

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: 1

♻️ Duplicate comments (1)
packages/extension/src/ui/action/App.vue (1)

502-509: switchToUnrestrictedAddress doesn’t verify new account is actually unrestricted

This helper only picks the first account whose display address differs from the currently restricted one and doesn’t:

  • Re-check whether that candidate is itself restricted, or
  • Provide feedback when all accounts are restricted (no candidate found).

That means:

  • You may switch the user from one restricted account straight into another.
  • If every account is restricted, clicking the “switch” action silently does nothing.

Consider something along these lines:

-const switchToUnrestrictedAddress = () => {
-  const newAccount = accountHeaderData.value.activeAccounts.find(
-    aa =>
-      currentNetwork.value.displayAddress(aa.address) !==
-      isAddressRestricted.value.address,
-  );
-  if (newAccount) onSelectedAddressChanged(newAccount);
-};
+const switchToUnrestrictedAddress = async () => {
+  for (const account of accountHeaderData.value.activeAccounts) {
+    const displayAddr = currentNetwork.value.displayAddress(account.address);
+    if (displayAddr === isAddressRestricted.value.address) continue;
+
+    const restricted = await isWalletRestricted(displayAddr);
+    if (!restricted) {
+      await onSelectedAddressChanged(account);
+      return;
+    }
+  }
+  // TODO: surface UX feedback (toast / alert) when no unrestricted account exists
+}

This preserves the intent while guaranteeing you only auto-switch to a truly unrestricted account and giving you a hook to explain the “no available account” case to the user.

🧹 Nitpick comments (1)
packages/extension/src/ui/action/App.vue (1)

461-464: Hooking checkAddress into address changes is good, but consider awaiting

Wiring onSelectedAddressChanged to checkAddress(newAccount) ensures the restricted banner tracks the currently selected account, which is the right behavior.

Given checkAddress is async, you might optionally await checkAddress(newAccount); here (and in setNetwork) or explicitly mark the call as fire‑and‑forget (void checkAddress(newAccount);) to make the intent clearer to future readers, but functionally this is fine.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 119208d and 68f5826.

📒 Files selected for processing (1)
  • packages/extension/src/ui/action/App.vue (6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: buildAll
  • GitHub Check: test
  • GitHub Check: test
🔇 Additional comments (2)
packages/extension/src/ui/action/App.vue (2)

5-12: Confirm Restricted event name matches child emit

The parent listens to @restricted:switch-account="switchToUnrestrictedAddress". Per the PR summary, the child Restricted view emits restricted:switchAccount. If the emit name is indeed restricted:switchAccount (camelCase), this listener will never fire because the strings don’t match.

Double‑check that:

  • The emitted event name in views/restricted/index.vue exactly matches the one used here, and
  • You stick to a single naming convention (prefer kebab-case like restricted:switch-account in both emit and listener for Vue templates).

353-355: Ordering of currentNetwork assignment and checkAddress looks correct

Assigning currentNetwork.value = network before invoking checkAddress(selectedAccount) ensures the restriction check uses the newly selected network’s displayAddress, fixing the stale-network concern that was previously raised.

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: 0

🧹 Nitpick comments (2)
packages/extension/src/ui/action/views/restricted/index.vue (2)

31-40: Consider adding keyboard accessibility to clickable elements.

The clickable header div lacks role="button", tabindex="0", and keyboard event handlers, which prevents keyboard-only users from activating this control. The same pattern exists in the other content block (line 42), but since this is new code, consider improving accessibility here.

-        <div
-          class="blocked-page__content__header"
-          @click="emit('restricted:switchAccount')"
-        >
+        <div
+          class="blocked-page__content__header"
+          role="button"
+          tabindex="0"
+          @click="emit('restricted:switchAccount')"
+          @keydown.enter="emit('restricted:switchAccount')"
+          @keydown.space.prevent="emit('restricted:switchAccount')"
+        >

101-112: Simplify primitive prop defaults.

For primitive types (Boolean, String), Vue 3 allows direct values without factory functions. The arrow function syntax is only required for Object/Array defaults to avoid shared references.

   isAddressRestricted: {
     type: Boolean,
-    default: () => {
-      return false;
-    },
+    default: false,
   },
   restrictedAddress: {
     type: String,
-    default: () => {
-      return '';
-    },
+    default: '',
   },
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 68f5826 and 68fd7f8.

📒 Files selected for processing (1)
  • packages/extension/src/ui/action/views/restricted/index.vue (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: buildAll
  • GitHub Check: test
  • GitHub Check: test
🔇 Additional comments (4)
packages/extension/src/ui/action/views/restricted/index.vue (4)

5-15: LGTM!

The conditional rendering for address-specific vs. IP/jurisdiction restriction messages is correctly implemented. Vue's template interpolation safely escapes the restrictedAddress value.


22-28: LGTM!

Good security practice using rel="noopener noreferrer" on the external link to prevent reverse tabnabbing.


57-70: LGTM!

Explicit prop and event bindings improve code clarity and maintainability.


76-81: LGTM!

Wiring both @window:back and @window:close to closeMnemonic provides consistent behavior and future-proofs the component if :has-back changes.

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: 0

🧹 Nitpick comments (5)
packages/extension/src/ui/action/views/restricted/index.vue (5)

5-13: Address/IP restriction messaging looks correct; consider empty-address fallback.

The conditional copy keyed off isAddressRestricted vs !isAddressRestricted is clean and mutually exclusive, and using mustache interpolation keeps restrictedAddress safe.

If there’s any chance isAddressRestricted can be true while restrictedAddress is an empty string, you’d end up with [] in the sentence. If that state is possible, consider a small guard (e.g., hide the brackets or show a shortened/placeholder address).

Also applies to: 16-19, 21-27


30-40: CTA gating looks good; improve accessibility of clickable headers.

The split between “Switch Address” for restricted wallets and “View My Secret Recovery Phrase” for non‑restricted ones is clear and matches the described flow. Emitting restricted:switchAccount from the header is also wired correctly.

Since these headers are effectively buttons, consider making them keyboard- and screen‑reader friendly (e.g., use <button> or add role="button", tabindex="0", and a key handler for Enter/Space).


56-63: Confirm intent of broad $attrs forwarding to modal-sign.

Using v-bind="$attrs" here will forward all attributes/listeners from <Restricted>’s parent to modal-sign, while they’ll also still be applied to the root element unless inheritAttrs: false is set.

If the goal is specifically to pass only certain props/events down to modal-sign, you might want to explicitly bind those instead of $attrs, or disable root inheritance via defineOptions({ inheritAttrs: false }) to avoid duplicated/accidental forwarding. If you intentionally rely on this passthrough for multiple modal props, current code is fine—just worth double‑checking.


65-69: Minor redundancy between v-if and :is-forgot.

modal-forgot is both v-if="isOpenForgot" and receives :is-forgot="isOpenForgot". If the prop is only used to indicate visibility, one of these may be redundant. Not a problem functionally, but you could simplify later for clarity.


100-111: Props for restriction state are well-defined.

isAddressRestricted and restrictedAddress are correctly typed and integrated with the template logic. If you ever revisit this, you could simplify boolean/string defaults to default: false / default: '' instead of arrow functions, but it’s purely stylistic.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 68fd7f8 and 9d9b997.

📒 Files selected for processing (1)
  • packages/extension/src/ui/action/views/restricted/index.vue (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: buildAll
  • GitHub Check: test
  • GitHub Check: test
🔇 Additional comments (2)
packages/extension/src/ui/action/views/restricted/index.vue (2)

75-80: Mnemonic modal wiring is consistent and safe.

Binding both window:back and window:close to closeMnemonic keeps the state reset path simple and aligned with other views. No issues here.


114-116: Typed emit for restricted:switchAccount is correct.

The defineEmits typing matches the template usage and keeps the restricted‑address flow explicit and type‑safe. No changes needed.

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: 4

♻️ Duplicate comments (3)
packages/extension/src/ui/action/App.vue (3)

354-354: Guard against empty activeAccounts array.

The past review comment about stale network reference has been resolved—currentNetwork.value is now set before calling checkAddress. However, selectedAccount could be undefined if activeAccounts is empty (line 340), which would cause checkAddress to crash when accessing activeAccount.address.

Apply this diff to add a guard:

   currentNetwork.value = network;
-  checkAddress(selectedAccount);
+  if (selectedAccount) {
+    checkAddress(selectedAccount);
+  }

Or combine with the null-safety fix in checkAddress itself (see comment on lines 439-453).


439-453: Add null-safety and prevent race conditions in checkAddress.

This function lacks null-safety for activeAccount and is vulnerable to race conditions when networks or accounts change during the async isWalletRestricted call. If activeAccounts is ever empty, selectedAccount will be undefined and accessing activeAccount.address will crash.

Apply the fix suggested in the past review comment: add a null-safety guard, snapshot the network and display address before the async call, and optionally verify the snapshot still matches after the async call completes.

-const checkAddress = async (activeAccount: EnkryptAccount) => {
+const checkAddress = async (activeAccount?: EnkryptAccount | null) => {
+  // reset before running any async work
   isAddressRestricted.value = {
     isRestricted: false,
     address: '',
   };
+
+  if (!activeAccount) return;
+
+  const network = currentNetwork.value;
+  const displayAddress = network.displayAddress(activeAccount.address);
+
-  const isRestricted = await isWalletRestricted(
-    currentNetwork.value.displayAddress(activeAccount.address),
-  );
+  const isRestricted = await isWalletRestricted(displayAddress);
+
+  // Optional: ignore stale results if network/account changed while awaiting
+  // if (
+  //   currentNetwork.value !== network ||
+  //   accountHeaderData.value.selectedAccount?.address !== activeAccount.address
+  // ) {
+  //   return;
+  // }
+
   if (isRestricted) {
     isAddressRestricted.value = {
-      isRestricted,
-      address: currentNetwork.value.displayAddress(activeAccount.address),
+      isRestricted: true,
+      address: displayAddress,
     };
   }
 };

504-511: Verify new account is actually unrestricted.

The function selects the first account with a different display address but doesn't verify it's unrestricted. If multiple accounts are restricted, the user could be switched to another restricted account. Additionally, if no unrestricted account exists, the function silently does nothing.

Apply the fix suggested in the past review comment: filter accounts through isWalletRestricted to find a truly unrestricted one and provide user feedback when all accounts are restricted.

-const switchToUnrestrictedAddress = () => {
+const switchToUnrestrictedAddress = async () => {
-  const newAccount = accountHeaderData.value.activeAccounts.find(
-    aa =>
-      currentNetwork.value.displayAddress(aa.address) !==
-      isAddressRestricted.value.address,
-  );
-  if (newAccount) onSelectedAddressChanged(newAccount);
+  for (const account of accountHeaderData.value.activeAccounts) {
+    const displayAddr = currentNetwork.value.displayAddress(account.address);
+    if (displayAddr === isAddressRestricted.value.address) continue;
+    const restricted = await isWalletRestricted(displayAddr);
+    if (!restricted) {
+      onSelectedAddressChanged(account);
+      return;
+    }
+  }
+  // TODO: Show user feedback that all accounts are restricted
 };
🧹 Nitpick comments (1)
packages/extension/src/providers/ethereum/ui/send-transaction/index.vue (1)

440-444: Consider i18n / consistent copy for “Restricted Address”.

If you have a shared restricted-flow message elsewhere (App.vue/index.vue per PR objective), reusing a constant/i18n key would keep wording consistent across screens.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 444872a and f9ce671.

📒 Files selected for processing (3)
  • packages/extension/src/providers/ethereum/ui/eth-connect-dapp.vue (5 hunks)
  • packages/extension/src/providers/ethereum/ui/send-transaction/index.vue (5 hunks)
  • packages/extension/src/ui/action/App.vue (6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: buildAll
  • GitHub Check: test
  • GitHub Check: test
🔇 Additional comments (6)
packages/extension/src/providers/ethereum/ui/send-transaction/index.vue (2)

178-178: Good integration point for restricted flow check.


440-444: Nice UX + safety: error messaging and send gating now honor restricted state.

Also applies to: 584-590

packages/extension/src/ui/action/App.vue (3)

5-12: Template bindings look correct.

The conditional rendering and props correctly map to the new isAddressRestricted state structure, and the event binding properly connects to switchToUnrestrictedAddress.


172-175: State declaration is clear and well-typed.

The isAddressRestricted ref uses an explicit type annotation and sensible initial values.


461-492: Conditional logic correctly gates on restriction status.

The function appropriately checks the address restriction before propagating the address change to the background and tabs. This prevents restricted addresses from being activated. The logic is sound assuming checkAddress is fixed to handle null-safety and race conditions as noted in the previous comment.

packages/extension/src/providers/ethereum/ui/eth-connect-dapp.vue (1)

68-68: LGTM: Connect button properly disabled for restricted addresses.

The button disable logic correctly prevents connection when the address is restricted. Ensure the race condition flagged in the earlier comment is addressed to make this security measure effective from the moment the component renders.

@kvhnuke kvhnuke changed the base branch from develop to devop/package-updates-2-15 December 17, 2025 18:05
@kvhnuke kvhnuke merged commit 8a95a34 into devop/package-updates-2-15 Dec 17, 2025
4 checks passed
@kvhnuke kvhnuke deleted the devop/active-wallet-screen branch December 17, 2025 18:07
This was referenced Dec 17, 2025
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