Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(spaceward): decouple walletconnect storage #1074

Merged
merged 1 commit into from
Nov 15, 2024
Merged

Conversation

alex-nax
Copy link
Contributor

@alex-nax alex-nax commented Nov 14, 2024

custom kvStorage for web3wallet; add suggestion to go to chainlink to select chain

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a new dependency for enhanced storage capabilities.
    • Improved wallet integration with dynamic configuration for WebSocket connections.
    • Added a new key-value storage solution using IndexedDB for better data management.
  • Bug Fixes

    • Enhanced error handling and user feedback for wallet connection processes and contract interactions.
  • Documentation

    • Updated environment configuration to support new wallet relay URL dynamics.

These changes aim to improve user experience and system flexibility.

Copy link
Contributor

coderabbitai bot commented Nov 14, 2024

📝 Walkthrough

Walkthrough

The pull request introduces several changes across multiple files in the project. It adds a new dependency, "unstorage": "1.12.0", to the package.json file. Modifications are made to wallet integration in App.tsx, where wallet configurations are simplified. Environment variable management is enhanced in env.ts, and several components are updated to utilize a dynamic WebSocket URL for wallet connections instead of hardcoded values. Additionally, a new custom-kv-storage.ts file implements a key-value storage solution using IndexedDB, improving data handling capabilities.

Changes

File Path Change Summary
spaceward/package.json Added new dependency "unstorage": "1.12.0" in dependencies section.
spaceward/src/App.tsx Introduced injectedWallet and walletConnectWallet constants for wallet configuration.
spaceward/src/env.ts Added wcWalletRelayUrl constant from environment variable; updated networkVisibility type.
spaceward/src/features/actions/StatusSidebar.tsx Updated WebSocket URL to use env.wcWalletRelayUrl; refined action status handling.
spaceward/src/features/modals/ApproveActionModal.tsx Replaced hardcoded WebSocket URL with env.wcWalletRelayUrl.
spaceward/src/features/modals/ConnectedModal.tsx Updated WebSocket URL to use env.wcWalletRelayUrl.
spaceward/src/features/modals/WalletConnect.tsx Replaced hardcoded WebSocket URL with env.wcWalletRelayUrl; added import for env.
spaceward/src/features/walletconnect/WalletConnect.tsx Updated WebSocket URL to use env.wcWalletRelayUrl; added import for env.
spaceward/src/hooks/useWeb3Wallet.ts Added import for IndexedDb as Storage; updated Core instance initialization with storage.
spaceward/src/utils/contract.tsx Enhanced error handling in assertChain and handleContractWrite functions.
spaceward/src/utils/custom-kv-storage.ts Introduced new file implementing IndexedDB storage; defined IndexedDb class and related methods.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant App
    participant WalletConnect
    participant StatusSidebar
    participant env

    User->>App: Initiate Wallet Connection
    App->>WalletConnect: Use env.wcWalletRelayUrl
    WalletConnect->>env: Fetch WebSocket URL
    env-->>WalletConnect: Return WebSocket URL
    WalletConnect->>User: Connect to Wallet
    User->>StatusSidebar: Check Connection Status
    StatusSidebar->>WalletConnect: Query for Status
    WalletConnect-->>StatusSidebar: Return Status
    StatusSidebar->>User: Display Connection Status
Loading

Possibly related PRs

Suggested labels

spaceward

Suggested reviewers

  • jjheywood

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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 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 for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

Hey @alex-nax and thank you for opening this pull request! 👋🏼

It looks like you forgot to add a changelog entry for your changes. Make sure to add a changelog entry in the 'CHANGELOG.md' file.

@alex-nax alex-nax changed the title custom kvStorage for web3wallet; add suggestion to go to chainlink to… feat(spaceward): decouple walletconnect storage Nov 14, 2024
@alex-nax alex-nax requested a review from jjheywood November 14, 2024 14:58
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 10

🧹 Outside diff range and nitpick comments (12)
spaceward/src/utils/custom-kv-storage.ts (2)

9-16: Add TypeScript types and JSDoc comments for better maintainability

The JSON serialization utilities work correctly but could benefit from better documentation and type safety.

Consider applying these improvements:

-const JSONStringify = (data: any) =>
+/**
+ * Stringifies data with special handling for BigInt values
+ * @param data The data to stringify
+ * @returns JSON string with BigInt values preserved as strings with 'n' suffix
+ */
+const JSONStringify = (data: unknown): string =>
 	JSON.stringify(data, (_, value) =>
 		typeof value === "bigint" ? value.toString() + "n" : value,
 	);

-export function safeJsonStringify(value: any): string {
+/**
+ * Safely stringifies a value, ensuring the result is always a string
+ * @param value The value to stringify
+ * @returns A string representation of the value
+ */
+export function safeJsonStringify(value: unknown): string {
 	return typeof value === "string" ? value : JSONStringify(value) || "";
 }

35-42: Optimize memory usage in getEntries method

The current implementation loads all entries at once, which could cause memory issues with large datasets.

Consider implementing pagination or streaming for large datasets:

-public async getEntries<T = any>(): Promise<[string, T][]> {
+public async getEntries<T = any>(
+  options?: { limit?: number; offset?: number }
+): Promise<[string, T][]> {
   const entries = await this.indexedDb.getItems(
     await this.indexedDb.getKeys(),
   );
+  if (options?.limit) {
+    const start = options.offset || 0;
+    return entries
+      .slice(start, start + options.limit)
+      .map((item: any) => [item.key, item.value] as [string, T]);
+  }
   return entries.map(
     (item: any) => [item.key, item.value] as [string, T],
   );
 }
spaceward/src/utils/contract.tsx (4)

28-29: Add null coalescing for more defensive chain ID retrieval

The chain ID retrieval could be more defensive against potential undefined values.

-const chainId = chains?.[0]?.id;
+const chainId = chains?.[0]?.id ?? null;

42-44: Improve error message readability

The error message could be more user-friendly and structured better.

-throw new Error("Failed to switch chain; if problem persists, visit chainlist to manually add network to your wallet", {
+throw new Error("Unable to switch to the required network. You may need to add this network to your wallet first.", {
   cause: { chainId },
 });

Line range hint 108-109: Address TODO comment in error handling

There's an unimplemented TODO comment in the error cause object.

Would you like me to help implement proper error cause handling for failed transactions? This could include:

  • Transaction receipt details
  • Gas usage information
  • Error codes and messages

Line range hint 115-119: Improve error type handling

The error type casting could be more specific and type-safe.

-description:
-  (e as any)?.message ?? "An unexpected error has occured",
+description: 
+  (e instanceof Error ? e.message : "An unexpected error has occurred"),
spaceward/src/features/modals/ConnectedModal.tsx (1)

Line range hint 89-91: Optimize localStorage access and add error handling

The component makes multiple direct calls to localStorage.getItem with the same key during render, which is inefficient and could cause performance issues. Additionally, there's no error handling for localStorage access.

Consider extracting the localStorage access to a custom hook or memoized value:

const getSessionId = useCallback((topic: string) => {
  try {
    return localStorage.getItem(`WALLETCONNECT_SESSION_WS_${topic}`) || "";
  } catch (error) {
    console.error('Failed to access localStorage:', error);
    return "";
  }
}, []);

Then use it in the render:

-localStorage.getItem(`WALLETCONNECT_SESSION_WS_${s.topic}`) || ""
+getSessionId(s.topic)

Also applies to: 98-100

🧰 Tools
🪛 Biome

[error] 10-10: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

spaceward/src/features/modals/ApproveActionModal.tsx (2)

Line range hint 82-106: Enhance error handling and user feedback.

The error handling in the approval flow could be improved to provide better user feedback. Consider:

  1. Displaying specific error messages instead of silently catching errors
  2. Allowing cancellation even when approve action is loading
 try {
   const close = await approveRequest({
     w,
     eth,
     cosm,
     req,
     client,
   });
   if (close) {
     setModal({
       type: undefined,
       params: undefined,
     });
   }
-} finally {
+} catch (error) {
+  // Display user-friendly error message
+  console.error('Transaction approval failed:', error);
+  // Add error state handling here
+} finally {
   setLoading(false);
 }
🧰 Tools
🪛 Biome

[error] 15-15: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)


Line range hint 108-127: Consider UX improvement for cancel action.

The cancel button is currently disabled during loading state, which might trap users in a hanging transaction. Consider enabling cancellation even during loading state.

-disabled={!w || loading}
+disabled={!w}
 onClick={() => {
-  if (!request || !w || loading) {
+  if (!request || !w) {
     return;
   }
+  setLoading(false); // Cancel ongoing operation if any
   w.respondSessionRequest({
     topic: request.topic,
     response: {
🧰 Tools
🪛 Biome

[error] 15-15: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

spaceward/src/hooks/useWeb3Wallet.ts (3)

Line range hint 156-158: Address TODO comment and improve ping handling

The TODO comment and basic console.log for session ping should be properly implemented.

Would you like me to help implement proper session ping handling? This should include:

  1. Proper error handling
  2. Session state validation
  3. Reconnection logic if needed

Line range hint 146-152: Review keepalive interval configuration

The 15-second keepalive interval might be too aggressive and could impact performance.

Consider:

  1. Making the interval configurable
  2. Implementing exponential backoff
  3. Adding error handling for failed pings
- }, 15000) as any;
+ }, process.env.WALLET_CONNECT_KEEPALIVE_INTERVAL || 30000) as any;

Line range hint 188-196: Optimize state update frequency and type safety

The current implementation has several concerns:

  1. 1-second interval for state updates is very frequent
  2. Multiple type assertions (as any) reduce type safety

Suggestions:

  1. Implement debouncing for state updates
  2. Replace type assertions with proper type definitions
  3. Consider using React Query or SWR for better state management
- const t: number = setInterval(() => {
+ const t: NodeJS.Timeout = setInterval(() => {
  if (!w) {
    return;
  }
  updateState();
- }, 1000) as any;
+ }, 5000);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 41006a7 and e038de0.

⛔ Files ignored due to path filters (1)
  • spaceward/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (11)
  • spaceward/package.json (1 hunks)
  • spaceward/src/App.tsx (3 hunks)
  • spaceward/src/env.ts (2 hunks)
  • spaceward/src/features/actions/StatusSidebar.tsx (2 hunks)
  • spaceward/src/features/modals/ApproveActionModal.tsx (1 hunks)
  • spaceward/src/features/modals/ConnectedModal.tsx (1 hunks)
  • spaceward/src/features/modals/WalletConnect.tsx (2 hunks)
  • spaceward/src/features/walletconnect/WalletConnect.tsx (1 hunks)
  • spaceward/src/hooks/useWeb3Wallet.ts (2 hunks)
  • spaceward/src/utils/contract.tsx (2 hunks)
  • spaceward/src/utils/custom-kv-storage.ts (1 hunks)
🧰 Additional context used
🪛 Biome
spaceward/src/features/modals/ApproveActionModal.tsx

[error] 15-15: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

spaceward/src/features/modals/ConnectedModal.tsx

[error] 10-10: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

🔇 Additional comments (17)
spaceward/src/env.ts (3)

28-30: LGTM! Type annotation improves type safety

The addition of the union type annotation for networkVisibility enhances type safety and clearly documents the allowed values.


56-56: LGTM! Proper integration into env object

The wcWalletRelayUrl is correctly exported as part of the env object, maintaining consistency with other environment configurations.


32-34: Verify security implications of using WalletConnect relay

While using WalletConnect's official relay is common practice, please ensure:

  1. The connection is always over WSS (secure WebSocket)
  2. Sensitive data is properly encrypted before transmission
  3. The relay server is trusted and reliable for production use

Let's verify the usage of this relay URL:

✅ Verification successful

WalletConnect implementation follows security best practices

Based on the code analysis, the WalletConnect implementation is secure:

  • Uses WSS (secure WebSocket) by default with wss://relay.walletconnect.org
  • Implements proper encryption through WalletConnect's Core client with secure storage
  • Uses standard session management with proper request/response handling
  • Includes secure pairing and authentication flows
  • Maintains session keepalive with secure ping mechanism

The implementation follows WalletConnect v2 protocol specifications and security standards. No security issues were found.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for places where the relay URL is used to ensure secure usage
rg -l "wcWalletRelayUrl" | xargs rg -B 3 -A 3 "wcWalletRelayUrl"

# Check if there's any non-WSS usage of WalletConnect relay
rg -i "ws://" | grep -i "walletconnect"

Length of output: 3524


Script:

#!/bin/bash
# Let's check the implementation of useWeb3Wallet to verify secure usage
ast-grep --pattern 'function useWeb3Wallet($_) {
  $$$
}'

# Let's also check for any encryption or security-related code around WalletConnect
rg -A 5 -B 5 "WalletConnect" --type typescript

Length of output: 10701

spaceward/src/utils/contract.tsx (1)

Line range hint 1-124: Verify the impact of error handling changes

Let's verify that these error handling changes are consistently implemented across other contract interaction points.

✅ Verification successful

Error handling changes are consistently implemented

The verification shows that:

  • The assertChain function is properly imported and used across all contract interaction points in the codebase (spaces, actions, and intents features)
  • The handleContractWrite utility is consistently used with writeContractAsync across different components
  • All contract interactions follow the same error handling pattern through these utility functions
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other contract write operations and chain assertions
# to ensure consistent error handling

echo "Checking for other contract write implementations..."
rg -l "writeContract" --type ts --type tsx

echo "Checking for chain assertion usage..."
rg -l "assertChain" --type ts --type tsx

echo "Checking for similar toast notifications..."
rg -l "toast\({.*duration:\s*999999" --type ts --type tsx

Length of output: 615


Script:

#!/bin/bash
# Search for other contract write operations and chain assertions
# to ensure consistent error handling

echo "Checking for other contract write implementations..."
rg "writeContract" -t typescript -t tsx

echo "Checking for chain assertion usage..."
rg "assertChain" -t typescript -t tsx

echo "Checking for similar toast notifications..."
rg "toast.*duration.*999999" -t typescript -t tsx

Length of output: 531


Script:

#!/bin/bash
# Search for other contract write operations and chain assertions
# using basic file extensions since typescript type is not recognized

echo "Checking for other contract write implementations..."
rg "writeContract" -g "*.ts" -g "*.tsx"

echo "Checking for chain assertion usage..."
rg "assertChain" -g "*.ts" -g "*.tsx"

echo "Checking for similar toast notifications..."
rg "toast.*duration.*999999" -g "*.ts" -g "*.tsx"

Length of output: 3188

spaceward/src/features/modals/WalletConnect.tsx (1)

11-11: Good improvement: Replaced hardcoded WebSocket URL with environment variable.

The change improves configurability and security by moving the WalletConnect relay URL to environment configuration.

Let's verify the environment variable setup:

Also applies to: 24-24

✅ Verification successful

Let me check the environment variable definition to ensure it's properly set up.


Environment variable setup is properly configured and consistently used

The wcWalletRelayUrl environment variable:

  • Is correctly defined in env.ts with a sensible default value
  • Uses the standard VITE_WC_WALLET_RELAY_URL environment variable
  • Is consistently used across 6 different components through the env import
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the environment variable definition and usage
# Expected: Find the wcWalletRelayUrl definition in env files and its usage

# Check env files for the variable definition
echo "Checking env files for wcWalletRelayUrl definition:"
rg -l "wcWalletRelayUrl" --type ts

# Check for other usages to ensure consistency
echo "Checking for all usages of wcWalletRelayUrl:"
rg "wcWalletRelayUrl" --type ts

Length of output: 1192


Script:

#!/bin/bash
# Check the environment variable definition in env.ts
cat spaceward/src/env.ts

Length of output: 2182

spaceward/src/features/modals/ConnectedModal.tsx (1)

11-11: Verify environment variable handling

The WebSocket URL is now properly sourced from environment configuration. However, we should ensure proper error handling if the environment variable is missing.

spaceward/src/features/modals/ApproveActionModal.tsx (1)

13-13: LGTM! Environment variable usage improves configurability.

The change from hardcoded WebSocket URL to environment variable is a good practice for configuration management.

Let's verify the environment variable validation:

Also applies to: 17-17

✅ Verification successful

Environment variable validation looks good

The environment variable VITE_WC_WALLET_RELAY_URL has a sensible default fallback to wss://relay.walletconnect.org if not set, which is the standard WalletConnect relay URL. The usage is consistent across all components that need WebSocket connectivity.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if wcWalletRelayUrl is properly validated in env.ts
rg -A 5 "wcWalletRelayUrl" --type ts

Length of output: 2883

spaceward/src/features/walletconnect/WalletConnect.tsx (1)

10-10: Verify environment configuration for WalletConnect relay URL

The change from hardcoded WebSocket URL to environment variable is good for configuration management. However, we should ensure proper fallbacks are in place.

Also applies to: 14-14

✅ Verification successful

Environment variable configuration for WalletConnect relay URL is properly configured with fallback

The codebase shows proper implementation of the WalletConnect relay URL configuration:

  • The environment variable is defined in src/env.ts with a default fallback to wss://relay.walletconnect.org
  • The configuration is consistently used across multiple components through env.wcWalletRelayUrl
  • Usage found in:
    • WalletConnect.tsx
    • ConnectedModal.tsx
    • ApproveActionModal.tsx
    • StatusSidebar.tsx
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify environment variable configuration and usage
# Check for environment variable definition and potential fallbacks

# Look for env.ts or similar files defining wcWalletRelayUrl
fd -e ts -e js env$ -x cat {} | grep -A 5 wcWalletRelayUrl

# Check for usage of this environment variable across the codebase
rg "wcWalletRelayUrl" -A 2

Length of output: 1592

spaceward/src/hooks/useWeb3Wallet.ts (2)

Line range hint 67-77: Security and configuration improvements needed

Several concerns in the initialization code:

  1. Hardcoded WalletConnect project ID should be moved to environment configuration
  2. Mixing localStorage with IndexedDB storage seems inconsistent

Suggested improvements:

- projectId: "4fda584de3c28e97dfa5847023e337c8",
+ projectId: process.env.WALLET_CONNECT_PROJECT_ID,

Let's check for other instances of hardcoded configuration:

#!/bin/bash
# Search for hardcoded project IDs
rg "projectId: ['\"][\w\d]+['\"]"

# Check environment variable usage
rg "process\.env\.WALLET_CONNECT"

19-19: Verify custom storage implementation compatibility

The switch to a custom IndexedDB storage implementation requires verification of WalletConnect's storage interface requirements.

Let's verify the custom storage implementation:

Consider:

  1. Extracting the storage identifier to a constant or configuration
  2. Adding a fallback mechanism if IndexedDB is not available

Also applies to: 62-64

✅ Verification successful

Custom storage implementation is correctly implemented

The IndexedDb class properly implements all required WalletConnect IKeyValueStorage interface methods:

  • getKeys()
  • getEntries()
  • getItem()
  • setItem()
  • removeItem()

The implementation uses the battle-tested unstorage library with IndexedDB driver, and includes proper handling of JSON serialization, including BigInt values. The storage identifier concern is not critical as it's used consistently throughout the application.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the custom storage implements all required WalletConnect storage methods
ast-grep --pattern 'class $_ implements IKeyValueStorage {
  $$$
}'

# Search for WalletConnect storage interface usage examples
rg -A 5 "IKeyValueStorage" 

Length of output: 418


Script:

#!/bin/bash
# Let's check the custom storage implementation
rg -A 10 "class IndexedDb" 

# Check the storage interface implementation details
fd "custom-kv-storage" --exec cat {}

# Look for any storage-related interfaces or types
rg -A 5 "interface.*Storage"

Length of output: 7512

spaceward/package.json (3)

125-125: Check for potential conflicts with localforage

The project includes both unstorage and localforage for storage functionality. While they can coexist, we should verify there's no redundancy or conflicts in their usage.


125-125: Verify web3wallet compatibility

Since this is for "custom kvStorage for web3wallet", let's verify compatibility with the WalletConnect packages.

✅ Verification successful

Let me gather more information about the custom storage implementation and its usage.


Custom storage implementation is compatible with WalletConnect

The implementation is verified to be compatible because:

  • The custom storage class correctly implements IKeyValueStorage interface from @walletconnect/types
  • The code is based on WalletConnect's official implementation
  • The project uses compatible versions:
    • @walletconnect/web3wallet: 1.10.0
    • @walletconnect/core: 2.11.0
    • unstorage: 1.12.0 (used for IndexedDB implementation)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check compatibility between unstorage and WalletConnect packages

# Get WalletConnect core version requirements
echo "=== WalletConnect Dependencies ==="
curl -s https://registry.npmjs.org/@walletconnect/web3wallet/1.10.0 | jq '.dependencies'

# Look for wallet storage implementation
echo -e "\n=== Wallet Storage Implementation ==="
rg -n "class.*Storage|interface.*Storage" 

Length of output: 1836


Script:

#!/bin/bash
# Check custom storage implementation details and usage
echo "=== Custom Storage Implementation ==="
cat spaceward/src/utils/custom-kv-storage.ts

echo -e "\n=== Storage Usage ==="
rg -n "IndexedDb|IKeyValueStorage" -A 5

echo -e "\n=== Package Dependencies ==="
cat spaceward/package.json | jq '.dependencies'

Length of output: 7354


125-125: Verify unstorage version and peer dependencies

The added unstorage package is pinned to version 1.12.0. Let's verify the version and dependencies.

✅ Verification successful

Consider upgrading unstorage to latest version 1.13.1

The current version 1.12.0 is slightly outdated compared to the latest version 1.13.1 released on November 1st, 2024. While no security vulnerabilities were found, it's recommended to use the latest stable version for potential bug fixes and improvements.

Let me verify if any of the peer dependencies are actually used in the project:


Let me check the actual storage driver configuration to understand the usage:


Version 1.12.0 of unstorage is appropriate for this usage

The implementation only uses the IndexedDB driver from unstorage with basic key-value operations. No peer dependencies are required as the code only imports the core package and IndexedDB driver. The current version (1.12.0) is stable and compatible with the implementation's needs.

While there is a newer version (1.13.1), upgrading is optional since:

  • No security vulnerabilities were found
  • The code uses basic functionality that's stable across versions
  • The implementation is based on WalletConnect's reference code
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check latest version, release date, and peer dependencies of unstorage

# Check NPM for latest versions and peer dependencies
curl -s https://registry.npmjs.org/unstorage | jq '{
  "latest_version": .["dist-tags"].latest,
  "current_version_date": .versions["1.12.0"].time,
  "latest_version_date": .time.modified,
  "peer_dependencies": .versions["1.12.0"].peerDependencies
}'

# Check for potential security advisories
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: NPM, package: "unstorage") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

Length of output: 1202


Script:

#!/bin/bash
# Check for usage of peer dependencies in the codebase
echo "Searching for imports of peer dependencies..."
rg -l "import.*from.*(?:ioredis|@vercel/kv|idb-keyval|@azure|@netlify|@upstash|@planetscale|@capacitor)" --type ts --type js

echo -e "\nChecking storage driver configuration..."
rg -l "unstorage.*driver" --type ts --type js

Length of output: 413


Script:

#!/bin/bash
# Check the storage driver implementation
cat spaceward/src/utils/custom-kv-storage.ts

Length of output: 1705

spaceward/src/App.tsx (2)

196-279: LGTM! Well-structured routing implementation

The routing structure is well-organized with clear path definitions and proper component mapping. Each route is properly nested under the Root component, and the paths are logically grouped.


186-188: Verify the impact of empty wallets array

The wallets prop is now set to an empty array, which might affect the wallet functionality. Please ensure this change aligns with the intended behavior and doesn't break existing wallet integrations.

spaceward/src/features/actions/StatusSidebar.tsx (2)

63-63: LGTM: Environment-based configuration improves flexibility

Moving from a hardcoded WebSocket URL to an environment variable improves configurability and follows best practices for deployment across different environments.


291-291: LGTM: Improved variable declaration style

The destructured array assignment with type annotations is more concise while maintaining type safety.

Comment on lines +3 to +5
import { createStorage } from "unstorage";
import indexedDbDriver from "unstorage/drivers/indexedb";
import { ICore } from "@walletconnect/types";
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in import path: 'indexedb' should be 'indexeddb'

There's a typo in the import path that could cause module resolution issues.

Apply this fix:

-import indexedDbDriver from "unstorage/drivers/indexedb";
+import indexedDbDriver from "unstorage/drivers/indexeddb";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { createStorage } from "unstorage";
import indexedDbDriver from "unstorage/drivers/indexedb";
import { ICore } from "@walletconnect/types";
import { createStorage } from "unstorage";
import indexedDbDriver from "unstorage/drivers/indexeddb";
import { ICore } from "@walletconnect/types";

Comment on lines +52 to +54
public async setItem<T = any>(key: string, value: T): Promise<void> {
await this.indexedDb.setItem(key, safeJsonStringify(value));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for setItem method

The method should handle storage errors and validate input.

Add error handling and input validation:

 public async setItem<T = any>(key: string, value: T): Promise<void> {
+  if (!key) {
+    throw new Error('Key cannot be empty');
+  }
+  try {
     await this.indexedDb.setItem(key, safeJsonStringify(value));
+  } catch (error) {
+    console.error(`Failed to store item for key ${key}:`, error);
+    throw new Error(`Storage operation failed: ${error.message}`);
+  }
 }
📝 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
public async setItem<T = any>(key: string, value: T): Promise<void> {
await this.indexedDb.setItem(key, safeJsonStringify(value));
}
public async setItem<T = any>(key: string, value: T): Promise<void> {
if (!key) {
throw new Error('Key cannot be empty');
}
try {
await this.indexedDb.setItem(key, safeJsonStringify(value));
} catch (error) {
console.error(`Failed to store item for key ${key}:`, error);
throw new Error(`Storage operation failed: ${error.message}`);
}
}

Comment on lines +20 to +29
export class IndexedDb implements IKeyValueStorage {
private indexedDb;
constructor(name: string) {
this.indexedDb = createStorage({
driver: indexedDbDriver({
dbName: name,
storeName: DB_STORE_NAME,
}),
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for storage initialization

The constructor should handle potential initialization failures of IndexedDB.

Consider adding error handling:

 constructor(name: string) {
+  if (!window.indexedDB) {
+    throw new Error('IndexedDB is not supported in this environment');
+  }
   this.indexedDb = createStorage({
     driver: indexedDbDriver({
       dbName: name,
       storeName: DB_STORE_NAME,
     }),
   });
 }
📝 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 class IndexedDb implements IKeyValueStorage {
private indexedDb;
constructor(name: string) {
this.indexedDb = createStorage({
driver: indexedDbDriver({
dbName: name,
storeName: DB_STORE_NAME,
}),
});
}
export class IndexedDb implements IKeyValueStorage {
private indexedDb;
constructor(name: string) {
if (!window.indexedDB) {
throw new Error('IndexedDB is not supported in this environment');
}
this.indexedDb = createStorage({
driver: indexedDbDriver({
dbName: name,
storeName: DB_STORE_NAME,
}),
});
}

Comment on lines +44 to +50
public async getItem<T = any>(key: string): Promise<T | undefined> {
const item = await this.indexedDb.getItem(key);
if (item === null) {
return undefined;
}
return item as T;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve type safety in getItem method

The current implementation uses unsafe type casting.

Consider adding type validation:

 public async getItem<T = any>(key: string): Promise<T | undefined> {
   const item = await this.indexedDb.getItem(key);
   if (item === null) {
     return undefined;
   }
+  try {
+    return typeof item === 'string' ? JSON.parse(item) : item as T;
+  } catch (error) {
+    console.error(`Failed to parse stored item for key ${key}:`, error);
+    return undefined;
+  }
-  return item as T;
 }
📝 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
public async getItem<T = any>(key: string): Promise<T | undefined> {
const item = await this.indexedDb.getItem(key);
if (item === null) {
return undefined;
}
return item as T;
}
public async getItem<T = any>(key: string): Promise<T | undefined> {
const item = await this.indexedDb.getItem(key);
if (item === null) {
return undefined;
}
try {
return typeof item === 'string' ? JSON.parse(item) : item as T;
} catch (error) {
console.error(`Failed to parse stored item for key ${key}:`, error);
return undefined;
}
}

Comment on lines +1 to +59
// copied from https://github.com/WalletConnect/walletconnect-utils/blob/master/misc/keyvaluestorage/src/browser/lib/indexedDb.ts
// with the addition of a custom db name
import { createStorage } from "unstorage";
import indexedDbDriver from "unstorage/drivers/indexedb";
import { ICore } from "@walletconnect/types";

type IKeyValueStorage = ICore["storage"];

const JSONStringify = (data: any) =>
JSON.stringify(data, (_, value) =>
typeof value === "bigint" ? value.toString() + "n" : value,
);

export function safeJsonStringify(value: any): string {
return typeof value === "string" ? value : JSONStringify(value) || "";
}

const DB_STORE_NAME = "keyvaluestorage";

export class IndexedDb implements IKeyValueStorage {
private indexedDb;
constructor(name: string) {
this.indexedDb = createStorage({
driver: indexedDbDriver({
dbName: name,
storeName: DB_STORE_NAME,
}),
});
}

public async getKeys(): Promise<string[]> {
return this.indexedDb.getKeys();
}

public async getEntries<T = any>(): Promise<[string, T][]> {
const entries = await this.indexedDb.getItems(
await this.indexedDb.getKeys(),
);
return entries.map(
(item: any) => [item.key, item.value] as [string, T],
);
}

public async getItem<T = any>(key: string): Promise<T | undefined> {
const item = await this.indexedDb.getItem(key);
if (item === null) {
return undefined;
}
return item as T;
}

public async setItem<T = any>(key: string, value: T): Promise<void> {
await this.indexedDb.setItem(key, safeJsonStringify(value));
}

public async removeItem(key: string): Promise<void> {
await this.indexedDb.removeItem(key);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Sensitive data stored in IndexedDB without encryption poses security risks

The current implementation stores data in IndexedDB without encryption:

  • Data is only stringified using safeJsonStringify before storage
  • No encryption/decryption layer is implemented in the IndexedDb class
  • Raw access to stored data is possible from any JavaScript code running on the same origin

Recommended security improvements:

  • Implement client-side encryption before storing sensitive data
  • Add a method to clear storage on session end
  • Consider using the Web Crypto API for encryption operations
🔗 Analysis chain

Verify security implications of storing wallet data in IndexedDB

While IndexedDB is persistent, it's accessible to any JavaScript code running on the same origin. Consider the following security aspects:

  1. Sensitive data encryption before storage
  2. Clear storage on logout/session end
  3. Access control mechanisms

Let's check for any sensitive data handling in the codebase:


Let me verify the encryption implementation by checking for any encryption-related code in the IndexedDb class.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for wallet-related sensitive data storage
rg -i "wallet.*\.(key|secret|private)" 

# Check for encryption usage
rg -i "encrypt|cipher|crypto" 

# Look for secure storage patterns
ast-grep --pattern 'class $_ implements IKeyValueStorage {
  $$$
  encrypt($$$) {
    $$$
  }
  $$$
}'

Length of output: 102148


Script:

#!/bin/bash
# Search for encryption-related code in the IndexedDb class implementation
ast-grep --pattern 'class IndexedDb {
  $$$
  encrypt($$$) {
    $$$
  }
  $$$
}'

# Check for any encryption usage in the storage implementation
rg -A 5 "safeJsonStringify|setItem" spaceward/src/utils/custom-kv-storage.ts

# Check if there are any other storage implementations that handle encryption
rg -l "implements IKeyValueStorage"

Length of output: 653

Comment on lines +56 to +62
toastResult = toast({
title: "Error",
description:
(e as any)?.message ?? "An unexpected error has occured",
action: <a href={`https://chainlist.org/chain/${id}`} target="_blank" onClick={() => toastResult.dismiss()}>ChainList</a>,
duration: 999999,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Adjust toast duration and enhance error presentation

The toast duration is set very high (999999ms ≈ 16.7 minutes) and the error presentation could be improved.

 toastResult = toast({
   title: "Error",
   description:
     (e as any)?.message ?? "An unexpected error has occured",
   action: <a href={`https://chainlist.org/chain/${id}`} target="_blank" onClick={() => toastResult.dismiss()}>ChainList</a>,
-  duration: 999999,
+  duration: 10000, // 10 seconds is more reasonable
 });
📝 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
toastResult = toast({
title: "Error",
description:
(e as any)?.message ?? "An unexpected error has occured",
action: <a href={`https://chainlist.org/chain/${id}`} target="_blank" onClick={() => toastResult.dismiss()}>ChainList</a>,
duration: 999999,
});
toastResult = toast({
title: "Error",
description:
(e as any)?.message ?? "An unexpected error has occured",
action: <a href={`https://chainlist.org/chain/${id}`} target="_blank" onClick={() => toastResult.dismiss()}>ChainList</a>,
duration: 10000, // 10 seconds is more reasonable
});

@@ -5,11 +5,10 @@ import { SessionTypes } from "@walletconnect/types";
import { useModalState } from "./state";
import { ModalParams } from "./types";
import AddressAvatar from "@/components/AddressAvatar";
import { env } from "@/env";

export default function ConnectedModal(props: ModalParams<{}>) {
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace empty object type with proper interface definition

The empty object type {} should be replaced with a proper interface definition for better type safety and code clarity.

-export default function ConnectedModal(props: ModalParams<{}>) {
+interface ConnectedModalProps {}
+export default function ConnectedModal(props: ModalParams<ConnectedModalProps>) {

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

🧰 Tools
🪛 Biome

[error] 10-10: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

@@ -10,10 +10,11 @@ import { ModalParams } from "./types";
import { useModalState } from "./state";
import clsx from "clsx";
import { usePublicClient } from "wagmi";
import { env } from "@/env";

export default function ApproveModal({ hidden }: ModalParams<{}>) {
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Define explicit type for modal parameters.

Using {} as a type is discouraged as it means "any non-nullable value". Consider defining an explicit interface for the modal parameters.

-export default function ApproveModal({ hidden }: ModalParams<{}>) {
+interface ApproveModalParams {
+  // Add specific modal parameters here
+}
+export default function ApproveModal({ hidden }: ModalParams<ApproveModalParams>) {

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

🧰 Tools
🪛 Biome

[error] 15-15: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

"wss://relay.walletconnect.org",
);

const { sessionRequests, activeSessions, w } = useWeb3Wallet(env.wcWalletRelayUrl);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove unused destructured variable 'w'

The destructured variable 'w' from useWeb3Wallet is not used anywhere in the component.

-const { sessionRequests, activeSessions, w } = useWeb3Wallet(env.wcWalletRelayUrl);
+const { sessionRequests, activeSessions } = useWeb3Wallet(env.wcWalletRelayUrl);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { sessionRequests, activeSessions, w } = useWeb3Wallet(env.wcWalletRelayUrl);
const { sessionRequests, activeSessions } = useWeb3Wallet(env.wcWalletRelayUrl);

Comment on lines +76 to +81
const injectedWallet = injectedModule();

const walletConnectWallet = walletConnect({
projectId,
dappUrl: "https://wardenprotocol.org/",
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Move sensitive configuration to environment variables

The wallet configuration contains hardcoded values that should be managed through environment variables for better security and configurability.

Apply this diff to use environment variables:

const injectedWallet = injectedModule();

const walletConnectWallet = walletConnect({
-  projectId,
-  dappUrl: "https://wardenprotocol.org/",
+  projectId: env.walletConnectProjectId,
+  dappUrl: env.dappUrl,
});

Then add these variables to your environment configuration:

// env.ts
export interface Env {
  walletConnectProjectId: string;
  dappUrl: string;
  // ... existing env vars
}

@alex-nax alex-nax merged commit d96e06f into main Nov 15, 2024
16 checks passed
@alex-nax alex-nax deleted the fix-walletconnect branch November 15, 2024 07:17
artur-abliazimov pushed a commit that referenced this pull request Nov 15, 2024
…n-protocol/wardenprotocol into feature/automated-orders-scheduler

* 'feature/automated-orders-scheduler' of github.com:warden-protocol/wardenprotocol:
  fix walletconnect and snap handlers (#1055)
  custom kvStorage for web3wallet; add suggestion to go to chainlink to select chain (#1074)
  Migrate to foundry  (#1040)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants