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): reduce calls to evm endpoint #1068

Merged
merged 1 commit into from
Nov 13, 2024
Merged

Conversation

alex-nax
Copy link
Contributor

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

add json-rpc batch;
tweak queryClient settings;
add invalidate cache on user transactions;

Summary by CodeRabbit

  • New Features

    • Introduced a new module for integrating WAGMI with Web3-Onboard, enhancing wallet connections and management.
    • Added support for query invalidation in various components to ensure up-to-date data display.
  • Bug Fixes

    • Improved error handling for transaction processes and address validations in several components.
  • Documentation

    • Updated type definitions and validation schemas for better clarity and structure in the codebase.
  • Refactor

    • Enhanced state management and query handling across multiple components for improved performance and reliability.

Copy link
Contributor

coderabbitai bot commented Nov 13, 2024

📝 Walkthrough

Walkthrough

The pull request introduces significant updates across multiple files, primarily focusing on enhancing dependency management, improving state handling, and refining transaction processes. Key changes include the addition of new dependencies in package.json, modifications to query client configurations, and updates to components for better integration with the query client and blockchain interactions. The introduction of new types and functions, particularly related to WAGMI and Web3-Onboard, further supports improved wallet management and transaction handling.

Changes

File Path Change Summary
spaceward/package.json Added dependencies: @wagmi/core (2.10.4), @web3-onboard/common (2.4.2), eventemitter3 (5.0.1), joi (17.13.3); removed @web3-onboard/wagmi (2.0.1).
spaceward/src/App.tsx Updated QueryClient configuration: replaced refetchInterval with staleTime; modified Web3Onboard initialization to use an object for transports.
spaceward/src/features/actions/Action.tsx Added queryKey parameter to Action function; integrated queryClient to invalidate queries post-vote.
spaceward/src/features/actions/Actions.tsx Updated Action component to accept queryKey prop from useActionsByAddress hook.
spaceward/src/features/actions/ApproveSidebar.tsx Added queryKey from useActionsByAddress and integrated queryClient for cache invalidation post-vote.
spaceward/src/features/actions/StatusSidebar.tsx Enhanced transaction handling and error reporting; added asynchronous checkAction function for action status querying.
spaceward/src/features/actions/hooks.ts Introduced getActionByIdQueryOptions function and updated QueuedAction interface to include new properties.
spaceward/src/features/actions/util.ts Added getStatusDefault function; refined getActionHandler to include queryKeys.
spaceward/src/features/intents/hooks.ts Updated useRules hook to include queryClient for query invalidation after rule updates.
spaceward/src/features/modals/SendAssets.tsx Streamlined address validation; updated error handling in submit function.
spaceward/src/features/modals/util.ts Refined buildTransaction function with improved error handling and gas limit calculations.
spaceward/src/features/spaces/NoSpaces.tsx Added queryKey prop to NoSpaces component; included useEffect for balance query invalidation.
spaceward/src/features/spaces/SpaceSelector.tsx Updated sendMsgNewSpace to await contract write and invalidate queries post-operation.
spaceward/src/hooks/query/warden.ts Enhanced useReadContract with useWatchContractEvent; updated useSpaces functions for clarity.
spaceward/src/pages/Root.tsx Updated useSpacesByOwner to return queryKey, enhancing interaction with NoSpaces component.
spaceward/src/utils/contract.ts Updated handleContractWrite to use waitForTransactionReceipt directly, improving transaction handling.
spaceward/src/utils/web3-onboard/wagmi/index.ts Introduced module for WAGMI integration with Web3-Onboard, including various wallet management functions.
spaceward/src/utils/web3-onboard/wagmi/types.ts Defined TypeScript types for WAGMI integration.
spaceward/src/utils/web3-onboard/wagmi/utils.ts Added utility functions for wallet and chain management.
spaceward/src/utils/web3-onboard/wagmi/validation.ts Created validation schema for WAGMI initialization options using Joi.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant App
    participant QueryClient
    participant Web3Onboard

    User->>App: Initiate Action
    App->>QueryClient: Update State
    App->>Web3Onboard: Connect Wallet
    Web3Onboard-->>App: Connection Established
    App->>QueryClient: Invalidate Queries
    QueryClient-->>App: State Updated
    App-->>User: Action Complete
Loading

Possibly related PRs

Suggested labels

spaceward, protocol, wardenjs

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 requested a review from jjheywood November 13, 2024 14:43
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: 16

🧹 Outside diff range and nitpick comments (25)
spaceward/src/utils/web3-onboard/wagmi/validation.ts (1)

5-12: Add JSDoc comments and consider function parameter validation

While the schema correctly enforces the presence of required functions, it would benefit from:

  1. JSDoc comments explaining the purpose and expected behavior of each required function
  2. Validation for function parameters using Joi.function().arity() to ensure correct parameter count

Example enhancement:

 const wagmiInitOptionsSchema = Joi.object({
+  /** Request user's accounts from the wallet */
-  requestAccounts: Joi.function().required(),
+  requestAccounts: Joi.function().arity(0).required(),
+  /** Get the current chain ID from the wallet */
-  getChainId: Joi.function().required(),
+  getChainId: Joi.function().arity(0).required(),
   // Add similar documentation and arity validation for other functions
 });
spaceward/src/utils/web3-onboard/wagmi/types.ts (2)

14-27: Consider enhancing type safety and documentation.

While the type definitions are functional, consider these improvements:

  1. Replace Promise<unknown> with more specific return types for disconnect and switchChain
  2. Add JSDoc comments to document complex parameters and possible errors

Example enhancement:

 export type WagmiInitOptions = {
+  /** Disconnects the wallet with the specified label
+   * @param options.label The label of the wallet to disconnect
+   * @returns Promise resolving to the disconnection result
+   * @throws WalletDisconnectionError if disconnection fails
+   */
-  disconnect: (options: { label: string }) => Promise<unknown>;
+  disconnect: (options: { label: string }) => Promise<boolean>;
   // ... other methods
 };

29-51: Consider architectural improvements for better error handling.

The API design is solid, but consider these architectural improvements:

  1. Consider using a Result type for better error handling instead of undefined
  2. Group related methods into sub-namespaces for better organization

Example approach:

type Result<T> = { success: true; data: T } | { success: false; error: Error };

export type WagmiModuleAPI = {
  config: {
    build: (
      chains: Chain[],
      walletData?: {
        label: string;
        provider: EIP1193Provider;
      },
    ) => Promise<Result<Config>>;
  };
  wallet: {
    connect: (
      label: string,
      provider: EIP1193Provider,
    ) => Promise<Result<ConnectReturnType<Config>>>;
    disconnect: (label: string) => Promise<Result<Config>>;
    // ... other wallet methods
  };
  // ... other namespaces
};
spaceward/src/utils/contract.ts (2)

3-3: Good architectural choice using viem's modular actions!

The switch to importing waitForTransactionReceipt directly and deriving the Client type from its parameters is a good architectural decision. This approach:

  • Follows viem's modular design philosophy
  • Could potentially reduce bundle size through better tree-shaking
  • Provides more precise type safety

Also applies to: 10-10


Line range hint 78-91: Address TODO comment and improve receipt status check

Two issues need attention:

  1. There's an unimplemented TODO in the error handling
  2. The receipt status check uses a string literal 'success' which might be network-dependent

Consider this improvement:

- if (receipt.status === "success") {
+ if (receipt.status === "success" || receipt.status === 1) {

Also, please implement the TODO in the error handling to provide more detailed error information.

spaceward/src/features/spaces/NoSpaces.tsx (2)

29-38: Consider adding error boundary for balance query invalidation

The block number monitoring and cache invalidation logic is well implemented. However, consider wrapping the invalidation in a try-catch block to handle potential query client errors gracefully.

 useEffect(() => {
   if (blockNumber) {
+    try {
       queryClient.invalidateQueries({ queryKey: balance.queryKey });
+    } catch (error) {
+      console.error('Failed to invalidate balance query:', error);
+    }
   }
 }, [blockNumber]);

Line range hint 75-91: Enhance error handling in contract write flow

While the query invalidation after contract write is well implemented, consider improving the error handling to provide better user feedback and ensure the query cache remains consistent even in error cases.

 try {
   await handleContractWrite(() => writeContractAsync({
     // ... existing config
   }), client);
   queryClient.invalidateQueries({ queryKey: props.queryKey });
 } catch (e) {
-  console.log("error", e);
+  console.error("Failed to create space:", e);
+  // Ensure cache consistency even on error
+  queryClient.invalidateQueries({ queryKey: props.queryKey });
+  // Consider showing user feedback
+  throw e; // Re-throw to be handled by error boundary
 }
spaceward/src/features/actions/Actions.tsx (2)

Line range hint 19-58: Consider memoizing expensive computations

The groups reduction operation and subsequent array mapping could benefit from memoization to prevent unnecessary recalculations on re-renders.

Consider wrapping the computation in useMemo:

- const groups: { [key: string]: ActionModel[] } =
-   q.data?.actions.reduce(
+const groups = useMemo(() => 
+  q.data?.actions?.reduce(
      (groups, action) => {
        const date = timestampToDate(action.createdAt)
          .toISOString()
          .split("T")[0];
        if (!groups[date]) {
          groups[date] = [];
        }
        groups[date].push(action);
        return groups;
      },
      {} as { [key: string]: ActionModel[] },
-   ) || {};
+   ) || {},
+   [q.data?.actions]
+);

-const actionsArrays = Object.keys(groups).map((date) => {
+const actionsArrays = useMemo(() => Object.keys(groups).map((date) => {
    return {
      date,
      actions: groups[date],
    };
-});
+}), [groups]);

Line range hint 1-15: Add type safety for the queryKey prop

To ensure type safety and better maintainability, consider defining the type for the queryKey prop in the Action component's props interface.

Add a type definition:

interface ActionProps {
  action: ActionModel;
  queryKey: readonly unknown[];
}
spaceward/src/features/actions/ApproveSidebar.tsx (2)

47-48: Add error handling for cache invalidation

While the cache invalidation is correctly placed after the contract write, consider adding error handling to ensure the invalidation doesn't fail silently if queryKey is undefined.

-		queryClient.invalidateQueries({ queryKey });
+		try {
+			if (!queryKey) {
+				console.warn('Query key is undefined, skipping cache invalidation');
+				return;
+			}
+			await queryClient.invalidateQueries({ queryKey });
+		} catch (error) {
+			console.error('Failed to invalidate queries:', error);
+		}

Line range hint 33-48: Consider adding loading states during voting

The voteFor function performs async operations but doesn't reflect the loading state in the UI. This could lead to a poor user experience if the transaction takes time.

Consider tracking the loading state:

+	const [isVoting, setIsVoting] = useState(false);

	async function voteFor(actionId: bigint, voteType: ActionVoteType) {
+		setIsVoting(true);
+		try {
			await assertChain(chains, connectedChain, setChain);

			await handleContractWrite(
				() => writeContractAsync({
					address: PRECOMPILE_ACT_ADDRESS,
					abi: actPrecompileAbi,
					functionName: "voteForAction",
					args: [actionId, voteType],
					account: address,
					connector: wallet?.wagmiConnector,
				}),
				client,
			);

			await queryClient.invalidateQueries({ queryKey });
+		} finally {
+			setIsVoting(false);
+		}
	}

Then use isVoting to disable buttons and show loading indicators in the UI.

spaceward/package.json (1)

100-100: Use fixed version for joi dependency

For consistency with other dependencies and to prevent unexpected updates, consider using a fixed version for joi instead of the caret range (^).

-		"joi": "^17.13.3",
+		"joi": "17.13.3",
spaceward/src/features/actions/Action.tsx (1)

Line range hint 28-40: Consider batch transaction optimization.

Given the PR's objective to reduce EVM endpoint calls, the voteFor function could potentially be optimized to handle multiple votes in a batch if needed in the future.

Consider adding a comment about potential future batch optimization:

  async function voteFor(voteType: ActionVoteType) {
+   // TODO: Consider implementing batch voting when multiple actions need to be voted on
    await assertChain(chains, connectedChain, setChain);
spaceward/src/features/spaces/SpaceSelector.tsx (1)

Line range hint 32-51: Enhance error handling for better user experience

While the contract interaction and cache invalidation are well implemented, the error handling could be improved to provide better user feedback.

Consider implementing this enhancement:

 try {
   await handleContractWrite(
     () => writeContractAsync({
       address: PRECOMPILE_WARDEN_ADDRESS,
       abi: wardenPrecompileAbi,
       functionName: "newSpace",
       args: [BigInt(0), BigInt(0), BigInt(0), BigInt(0), []],
       account: address,
       connector: wallet?.wagmiConnector,
     }),
     client,
   );
   queryClient.invalidateQueries({ queryKey: spacesQuery.queryKey });
 } catch (e) {
-  console.error("error", e);
+  console.error("Failed to create new space:", e);
+  // Consider using a toast notification or error state to inform the user
+  throw new Error("Failed to create new space. Please try again.");
 }
spaceward/src/hooks/query/warden.ts (2)

285-293: Consider adding staleTime to optimize EVM calls

The refactoring to store the query result is good. To further reduce EVM calls (PR objective), consider adding staleTime to the query options. This would prevent unnecessary refetches of space data that doesn't change frequently.

 const query = useReadContract({
   address: enabled ? PRECOMPILE_WARDEN_ADDRESS : undefined,
   args: enabled ? [pagination] : undefined,
   abi: wardenPrecompileAbi,
   functionName: "spaces",
   chainId,
+  query: {
+    staleTime: 30_000, // Consider data fresh for 30 seconds
+  },
 });

Line range hint 1-314: Consider implementing a custom QueryBatcher

To fully achieve the PR objective of "Addition of JSON-RPC Batch", consider implementing a custom QueryBatcher that can combine multiple useReadContract calls into a single JSON-RPC request. This would significantly reduce the number of EVM endpoint calls when multiple hooks are used simultaneously.

Example approach:

  1. Create a QueryBatcher context
  2. Collect read calls within a small time window (e.g., 50ms)
  3. Batch them into a single JSON-RPC request
  4. Distribute responses back to individual hooks

Would you like me to provide a detailed implementation example?

spaceward/src/features/intents/hooks.ts (2)

69-69: Consider moving queryClient initialization higher

The queryClient initialization should ideally be placed with other hook calls at the beginning of the useRules hook for better code organization and to follow the React hooks rules of hooks pattern.

export const useRules = () => {
	const [{ wallet }] = useConnectWallet();
+	const queryClient = useQueryClient();
	const address = wallet?.accounts[0].address;
	const { setData: setModal } = useModalState();
	const { spaceId } = useSpaceId();
	const client = usePublicClient();
-	const queryClient = useQueryClient();
	const { writeContractAsync } = useWriteContract();

182-188: Optimize query invalidation by batching

Consider batching the query invalidations to reduce the number of re-renders.

-queryClient.invalidateQueries({ queryKey: rules.queryKey });
-queryClient.invalidateQueries({
-	queryKey: expectedApproveQueryKey,
-});
-queryClient.invalidateQueries({ queryKey: expectedRejectQueryKey });
+queryClient.invalidateQueries({ 
+	queryKey: [
+		rules.queryKey,
+		expectedApproveQueryKey,
+		expectedRejectQueryKey
+	] 
+});
spaceward/src/features/actions/util.ts (2)

38-42: Add JSDoc documentation for the getStatusDefault function

Consider adding JSDoc documentation to explain the purpose and return value structure of this utility function.

+/**
+ * Returns a default status object indicating a completed action with no errors
+ * @returns {Promise<{pending: boolean, error: boolean, done: boolean}>}
+ */
const getStatusDefault = async () => ({

63-65: Add error handling for message decoding

The toBytes conversion and message decoding could fail. Consider adding try-catch blocks to handle potential errors gracefully.

-const { id } =
-  warden.warden.v1beta3.MsgNewSignRequestResponse.decode(
-    toBytes(value),
-  );
+const { id } = await (async () => {
+  try {
+    return warden.warden.v1beta3.MsgNewSignRequestResponse.decode(
+      toBytes(value)
+    );
+  } catch (error) {
+    throw new Error(`Failed to decode sign request response: ${error.message}`);
+  }
+})();

Also applies to: 138-140

spaceward/src/utils/web3-onboard/wagmi/utils.ts (1)

9-10: Enhance createWalletId to ensure uniqueness and safety

The createWalletId function generates an ID by removing whitespace and appending "Id" to the wallet label. Consider additional processing to ensure that the generated IDs are unique and free of special characters that might cause issues in downstream systems.

spaceward/src/features/actions/hooks.ts (1)

67-70: Simplify the Action type definition for better readability

The current Action type is complex and may impact readability:

type Action = Awaited<ReturnType<ActionQueryOptions["queryFn"]>>["action"] | undefined;

Consider defining an explicit interface or type for Action to enhance clarity and maintainability.

spaceward/src/utils/web3-onboard/wagmi/index.ts (2)

175-176: Update misleading error message

The error message at line 176 in the getWagmiConnector function incorrectly mentions "disconnecting wallet". Update it to accurately reflect the operation.

Apply this diff to correct the error message:

-         console.error("error disconnecting wallet from wagmi", e);
+         console.error("Error getting wagmi connector", e);

230-233: Handle asynchronous disconnect operations

The wagmiDisconnectWallet function is asynchronous but isn't awaited in the disconnect and onDisconnect methods. This could lead to unhandled promises. Consider making these methods asynchronous and awaiting the calls.

Apply this diff to handle the asynchronous operations:

 disconnect: async () => {
-     wagmiDisconnectWallet(label);
-     coreProviderMethods.disconnect({ label });
+     await wagmiDisconnectWallet(label);
+     await coreProviderMethods.disconnect({ label });
 },

 onDisconnect: async () => {
-     wagmiDisconnectWallet(label);
-     coreProviderMethods.disconnect({ label });
+     await wagmiDisconnectWallet(label);
+     await coreProviderMethods.disconnect({ label });
 },

Also applies to: 300-303

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

67-68: Refactor nested ternary operator for better readability

The nested ternary operator reduces code readability and can be difficult to maintain. Consider refactoring it to improve clarity.

Apply this diff to refactor the code:

-const type = typeof item.keyThemeIndex !== "undefined" ?
-    "key" : item.wc ?
-        "wc" : item.snap ?
-            "snap" : undefined
+let type;
+if (typeof item.keyThemeIndex !== "undefined") {
+  type = "key";
+} else if (item.wc) {
+  type = "wc";
+} else if (item.snap) {
+  type = "snap";
+} else {
+  type = undefined;
+}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between a09c802 and 903f97c.

⛔ Files ignored due to path filters (1)
  • spaceward/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (20)
  • spaceward/package.json (2 hunks)
  • spaceward/src/App.tsx (2 hunks)
  • spaceward/src/features/actions/Action.tsx (2 hunks)
  • spaceward/src/features/actions/Actions.tsx (1 hunks)
  • spaceward/src/features/actions/ApproveSidebar.tsx (3 hunks)
  • spaceward/src/features/actions/StatusSidebar.tsx (6 hunks)
  • spaceward/src/features/actions/hooks.ts (5 hunks)
  • spaceward/src/features/actions/util.ts (7 hunks)
  • spaceward/src/features/intents/hooks.ts (4 hunks)
  • spaceward/src/features/modals/SendAssets.tsx (0 hunks)
  • spaceward/src/features/modals/util.ts (0 hunks)
  • spaceward/src/features/spaces/NoSpaces.tsx (4 hunks)
  • spaceward/src/features/spaces/SpaceSelector.tsx (3 hunks)
  • spaceward/src/hooks/query/warden.ts (3 hunks)
  • spaceward/src/pages/Root.tsx (2 hunks)
  • spaceward/src/utils/contract.ts (3 hunks)
  • spaceward/src/utils/web3-onboard/wagmi/index.ts (1 hunks)
  • spaceward/src/utils/web3-onboard/wagmi/types.ts (1 hunks)
  • spaceward/src/utils/web3-onboard/wagmi/utils.ts (1 hunks)
  • spaceward/src/utils/web3-onboard/wagmi/validation.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • spaceward/src/features/modals/SendAssets.tsx
  • spaceward/src/features/modals/util.ts
🧰 Additional context used
🪛 Biome
spaceward/src/utils/web3-onboard/wagmi/index.ts

[error] 184-184: Invalid typeof comparison value: this expression is not a string literal

not a string literal
Unsafe fix: Compare the result of typeof with a valid type name

(lint/suspicious/useValidTypeof)

🔇 Additional comments (24)
spaceward/src/utils/web3-onboard/wagmi/validation.ts (1)

1-3: LGTM! Clean and focused imports

The imports are well-organized and each serves a specific purpose in the validation implementation.

spaceward/src/utils/web3-onboard/wagmi/types.ts (2)

1-12: LGTM! Well-structured imports following TypeScript best practices.

The imports are properly organized and use type-only imports for optimal tree-shaking.


1-51: Verify integration with JSON-RPC batching functionality.

While the type definitions provide a solid foundation for wallet management, ensure that the JSON-RPC batching mentioned in the PR objectives is properly integrated with these types.

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

50-50: Verify transaction receipt handling across different networks

The implementation change to use waitForTransactionReceipt looks good and aligns with the PR's objective to optimize EVM endpoint calls. However, let's verify the behavior across different networks.

Also applies to: 74-77

✅ Verification successful

Transaction receipt handling verified across different networks

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other transaction receipt handling patterns to ensure consistency
rg -t typescript "waitForTransactionReceipt|\.wait\(\)" --no-heading

Length of output: 105


Script:

#!/bin/bash
# Search for other transaction receipt handling patterns to ensure consistency
rg -t ts "waitForTransactionReceipt|\.wait\(\)" --no-heading

Length of output: 583

spaceward/src/features/spaces/NoSpaces.tsx (2)

1-1: LGTM: Clean import additions and proper type safety

The new imports and prop type changes properly support the cache invalidation strategy.

Also applies to: 10-10, 12-12, 14-14


20-28: LGTM: Efficient balance monitoring setup

Good optimization: The balance check is used to conditionally control block number monitoring, which helps reduce unnecessary EVM endpoint calls.

spaceward/src/features/actions/Actions.tsx (1)

126-126: LGTM! The queryKey prop addition aligns with cache invalidation requirements.

This change supports the PR's objective of invalidating cache on user transactions by passing the queryKey to child Action components.

spaceward/src/features/actions/ApproveSidebar.tsx (1)

14-14: LGTM! Query client integration looks good.

The addition of query client and extraction of queryKey aligns well with the PR's objective to improve state management. The implementation follows React hooks best practices.

Also applies to: 21-25, 30-30

spaceward/package.json (2)

76-76: Verify wagmi and viem version compatibility

The project uses both wagmi v2.9.7 and viem v2.12.0. Since wagmi depends on viem, ensure these versions are compatible to prevent potential runtime issues.

Also applies to: 131-131

✅ Verification successful

Wagmi v2.9.7 is compatible with Viem v2.12.0

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check wagmi's viem peer dependency version requirement

# Get wagmi's peer dependencies
npm info wagmi@2.9.7 peerDependencies --json | jq '."viem"'

# Get viem's latest version compatible with wagmi 2.9.7
npm info viem@"$(npm info wagmi@2.9.7 peerDependencies --json | jq -r '."viem"')" version

Length of output: 4211


76-76: Verify new dependencies support JSON-RPC batching

The PR aims to reduce EVM endpoint calls through JSON-RPC batching. Verify that the new dependencies (@wagmi/core, @web3-onboard/common) support this feature efficiently.

Also applies to: 83-83, 99-100

spaceward/src/pages/Root.tsx (2)

49-49: LGTM! Good addition of queryKey.

The addition of queryKey to the hook's destructured values enables proper cache invalidation management.


154-160: LGTM! Verify cache invalidation behavior.

The addition of queryKey prop to NoSpaces component is well-structured and maintains consistent conditional rendering.

Let's verify how the queryKey is used for cache invalidation:

✅ Verification successful

Cache Invalidation Behavior Verified

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check how NoSpaces component uses queryKey for cache invalidation

# Search for NoSpaces component implementation
ast-grep --pattern 'export function NoSpaces($_) {
  $$$
}'

# Search for queryKey usage in cache invalidation
rg -A 5 'queryKey.*invalidate|invalidateQueries'

Length of output: 13517

spaceward/src/features/actions/Action.tsx (3)

17-17: LGTM: Required import for query invalidation.

The import of QueryKey and useQueryClient is necessary for the new cache invalidation feature.


24-24: LGTM: QueryClient initialization.

Proper initialization of queryClient using the useQueryClient hook.


19-19: Verify prop usage in parent components.

The addition of the required queryKey prop changes the component's contract.

spaceward/src/features/spaces/SpaceSelector.tsx (1)

20-20: LGTM: Query client setup is properly implemented

The addition of useQueryClient hook is aligned with the PR's objective to improve query management and cache invalidation.

Also applies to: 30-30

spaceward/src/features/intents/hooks.ts (2)

83-94: LGTM! Well-structured action handler setup

The action handler setup is well-organized with clear separation of query keys for different operations.


127-139: Verify cache invalidation impact on EVM calls

The implementation of query invalidation aligns with the PR objective of reducing EVM endpoint calls. However, let's verify the actual impact.

✅ Verification successful

Cache invalidation properly scoped and aligns with PR objectives

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for all query invalidations to ensure they're necessary
# and not causing excessive refetches

# Search for all query invalidations
echo "Searching for query invalidations:"
rg -n "invalidateQueries" --type ts

# Search for all useQuery hooks to compare with invalidations
echo "\nSearching for useQuery hooks:"
rg -n "useQuery[^C]" --type ts

Length of output: 31157

spaceward/src/App.tsx (2)

59-59: LGTM: Well-organized Web3 configuration

The import change properly organizes Web3-related configurations into a dedicated utility file, improving code organization and maintainability.


67-68: Verify cache invalidation behavior with new staleTime

The switch from refetchInterval to staleTime with custom key hashing changes the caching behavior:

  • Queries will now be considered fresh for 15 seconds
  • After 15 seconds, data becomes stale but won't auto-refetch
  • Manual invalidation is required for immediate updates

Let's verify the implementation of the custom hash function:

✅ Verification successful

Cache invalidation behavior verified with new staleTime and queryKeyHashFn

The custom hashQueryKey implementation correctly manages cache invalidation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check the queryKeyHash implementation
rg -A 5 "export.*hashQueryKey" 

Length of output: 453


Script:

#!/bin/bash
# Find ensureQueryKeyArray implementation
rg "function\s+ensureQueryKeyArray" 

# Find stableValueHash implementation
rg "function\s+stableValueHash"

Length of output: 254


Script:

#!/bin/bash
# Display the implementation of ensureQueryKeyArray
cat spaceward/src/utils/queryKeyHash.ts | sed -n '/function\s\+ensureQueryKeyArray/,/}/p'

# Display the implementation of stableValueHash
cat spaceward/src/utils/queryKeyHash.ts | sed -n '/function\s\+stableValueHash/,/}/p'

Length of output: 721

spaceward/src/features/actions/util.ts (4)

7-7: LGTM: New imports support JSON-RPC batching

The added imports properly support the new functionality for JSON-RPC batching and contract querying.

Also applies to: 9-9, 12-12, 24-26


52-52: LGTM: Improved query cache management

The addition of queryKeys array and its inclusion in the return value enables efficient cache invalidation, aligning with the PR's objective to reduce EVM endpoint calls.

Also applies to: 199-199


175-192: LGTM: Well-structured space-related message handling

The consolidated handling of space-related messages (MsgUpdateSpaceResponse, MsgAddSpaceOwnerResponse, MsgRemoveSpaceOwnerResponse) with shared query key logic improves code maintainability and reduces duplication.


128-135: Verify the impact on EVM endpoint calls

The implementation of query keys for contract calls looks good. Let's verify the reduction in EVM endpoint calls.

Also applies to: 181-189

✅ Verification successful

Impact on EVM endpoint calls verified

No unnecessary direct EVM endpoint calls detected. Batching is appropriately implemented using readContractQueryKey.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the usage of readContractQueryKey to ensure proper batching

# Look for patterns that might indicate direct EVM calls without batching
rg -n "provider\.(call|send)" --type ts

# Check for proper implementation of contract query keys
ast-grep --pattern 'readContractQueryKey({
  $$$
})'

Length of output: 1120

Comment on lines +14 to +19
export const validateWagmiInit = (request: WagmiInitOptions): ValidateReturn =>
validate(
// @ts-expect-error conflicting types fixme
wagmiInitOptionsSchema,
request,
);
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

⚠️ Potential issue

Address the type conflict in validateWagmiInit function

The @ts-expect-error comment in validation.ts indicates a type conflict that should be properly resolved instead of being suppressed.

  • File: spaceward/src/utils/web3-onboard/wagmi/validation.ts
🔗 Analysis chain

Investigate and properly fix the type conflict

The @ts-expect-error comment indicates an unresolved type conflict between the schema and validation function. This should be investigated and properly fixed rather than suppressed.

Let's check for similar patterns in the codebase:

Consider:

  1. Checking if there's a type mismatch between @web3-onboard/common and joi types
  2. Creating proper type definitions if needed
  3. Updating the comment with specific details about the conflict if suppression is absolutely necessary
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other instances where @web3-onboard/common validate is used
rg -A 2 "from '@web3-onboard/common'" --type typescript

# Search for similar type conflict suppressions
rg "@ts-expect-error.*conflicting types" --type typescript

Length of output: 194


Script:

#!/bin/bash
# Search for other instances where @web3-onboard/common validate is used
rg -A 2 "from '@web3-onboard/common'" --glob '*.ts' --glob '*.tsx'

# Search for similar type conflict suppressions
rg "@ts-expect-error.*conflicting types" --glob '*.ts' --glob '*.tsx'

Length of output: 245

Comment on lines +41 to +42

queryClient.invalidateQueries({ queryKey });
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

Consider error handling for cache invalidation.

The cache invalidation is placed after the contract write operation but before confirming the transaction success. This could lead to premature cache invalidation if the transaction fails.

Consider wrapping the cache invalidation in the success callback:

  await handleContractWrite(
    () => writeContractAsync({
      address: PRECOMPILE_ACT_ADDRESS,
      abi: actPrecompileAbi,
      functionName: "voteForAction",
      args: [action.id, voteType],
      account: address,
      connector: wallet?.wagmiConnector,
    }),
    client,
+   {
+     onSuccess: () => {
+       queryClient.invalidateQueries({ queryKey });
+     }
+   }
  );
-  queryClient.invalidateQueries({ queryKey });
📝 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
queryClient.invalidateQueries({ queryKey });
await handleContractWrite(
() => writeContractAsync({
address: PRECOMPILE_ACT_ADDRESS,
abi: actPrecompileAbi,
functionName: "voteForAction",
args: [action.id, voteType],
account: address,
connector: wallet?.wagmiConnector,
}),
client,
{
onSuccess: () => {
queryClient.invalidateQueries({ queryKey });
}
}
);

@@ -43,7 +45,7 @@ export function SpaceSelector() {
client,
);

console.log("receipt", receipt);
queryClient.invalidateQueries({ queryKey: spacesQuery.queryKey });
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

Incomplete query invalidation for related space queries

The following files do not call invalidateQueries with spacesQuery.queryKey, which may lead to stale data:

  • wardenjs/src/codegen/warden/warden/v1beta3/query.rpc.Query.ts
  • spaceward/src/pages/Root.tsx
  • spaceward/src/features/modals/WCBindSpace.tsx
  • spaceward/src/hooks/query/warden.ts
  • wardenjs/src/codegen/hooks.ts
  • wardenjs/src/codegen/cosmos/params/v1beta1/query.rpc.Query.ts
🔗 Analysis chain

Verify query invalidation scope

The query invalidation implementation looks correct, but let's verify that it properly invalidates all related space queries.


Verify all related queries are properly invalidated

Ensure that invalidateQueries is invoked with the correct spacesQuery.queryKey in all related files.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other space-related queries that might need invalidation
rg -l "useSpacesByOwner|spacesQuery" --type ts

Length of output: 361


Script:

#!/bin/bash
# Search for invalidateQueries calls with spacesQuery.queryKey in the identified files
rg "invalidateQueries\s*\(\s*\{\s*queryKey\s*:\s*spacesQuery\.queryKey\s*\}\s*\)" --type ts

Length of output: 211

Comment on lines +306 to +314
const query = useReadContract({
address: enabled ? PRECOMPILE_WARDEN_ADDRESS : undefined,
args: enabled ? [pagination, request.owner!] : undefined,
abi: wardenPrecompileAbi,
functionName: "spacesByOwner",
chainId,
});

return query;
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 cache invalidation for owner's spaces

To align with the PR objective of "Invalidate Cache on User Transactions", this hook should invalidate its cache when the owner makes transactions that affect their spaces.

 const query = useReadContract({
   address: enabled ? PRECOMPILE_WARDEN_ADDRESS : undefined,
   args: enabled ? [pagination, request.owner!] : undefined,
   abi: wardenPrecompileAbi,
   functionName: "spacesByOwner",
   chainId,
+  query: {
+    staleTime: 30_000,
+    gcTime: 5 * 60_000,
+  },
 });

Additionally, implement cache invalidation in the write hooks:

// In your write mutation hook
const queryClient = useQueryClient();
// After successful transaction
queryClient.invalidateQueries({ 
  queryKey: ['spacesByOwner', owner]
});

@@ -2,8 +1,10 @@
import { useReadContract, useWatchContractEvent } from "wagmi";
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

Remove unused imports useWatchContractEvent and useQueryClient

Both useWatchContractEvent and useQueryClient are imported in spaceward/src/hooks/query/warden.ts but are not utilized within the file.

🔗 Analysis chain

Verify the usage of newly imported hooks

The newly imported hooks useWatchContractEvent and useQueryClient are not currently used in this file. These imports suggest planned features for:

  1. Watching contract events (potentially for real-time updates)
  2. Managing query cache (aligning with PR objective of cache invalidation)

Also applies to: 7-7

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if these hooks are used elsewhere in the codebase
echo "Checking useWatchContractEvent usage:"
rg "useWatchContractEvent" --type ts

echo -e "\nChecking useQueryClient usage:"
rg "useQueryClient" --type ts

Length of output: 1810

Comment on lines +236 to +242
return {
add,
expectedApproveExpression,
expectedApproveQueryKey: approveTemplate.queryKey,
expectedRejectExpression,
expectedRejectQueryKey: rejectTemplate.queryKey,
};
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

Handle possible undefined values when accessing queryKey properties

When returning approveTemplate.queryKey and rejectTemplate.queryKey, ensure that these properties are defined. If approveTemplate or rejectTemplate could be undefined, accessing queryKey might lead to runtime errors.

Consider using optional chaining or default values to handle potential undefined cases.

Apply this diff to safely access the properties:

 return {
   add,
   expectedApproveExpression,
-  expectedApproveQueryKey: approveTemplate.queryKey,
   expectedRejectExpression,
-  expectedRejectQueryKey: rejectTemplate.queryKey,
+  expectedApproveQueryKey: approveTemplate?.queryKey ?? null,
+  expectedRejectQueryKey: rejectTemplate?.queryKey ?? null,
 };
📝 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
return {
add,
expectedApproveExpression,
expectedApproveQueryKey: approveTemplate.queryKey,
expectedRejectExpression,
expectedRejectQueryKey: rejectTemplate.queryKey,
};
return {
add,
expectedApproveExpression,
expectedApproveQueryKey: approveTemplate?.queryKey ?? null,
expectedRejectExpression,
expectedRejectQueryKey: rejectTemplate?.queryKey ?? null,
};

provider: EIP1193Provider,
coreProviderMethods: WagmiInitOptions,
): Connector => {
if (!coreProviderMethods || typeof coreProviderMethods === undefined) {
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

Correct the typeof comparison

At line 184, the expression typeof coreProviderMethods === undefined is invalid because typeof returns a string. It should be compared to the string 'undefined'.

Apply this diff to fix the comparison:

-     if (!coreProviderMethods || typeof coreProviderMethods === undefined) {
+     if (!coreProviderMethods || typeof coreProviderMethods === 'undefined') {
📝 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
if (!coreProviderMethods || typeof coreProviderMethods === undefined) {
if (!coreProviderMethods || typeof coreProviderMethods === 'undefined') {
🧰 Tools
🪛 Biome

[error] 184-184: Invalid typeof comparison value: this expression is not a string literal

not a string literal
Unsafe fix: Compare the result of typeof with a valid type name

(lint/suspicious/useValidTypeof)

Comment on lines +184 to +189
if (!coreProviderMethods || typeof coreProviderMethods === undefined) {
console.error(
"Required provider methods not defined for Wagmi Module API",
);
return {} as Connector;
} else {
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

Avoid returning an empty Connector on error

Returning an empty object cast as a Connector can lead to runtime errors due to missing properties. Instead, throw an error to handle this case gracefully.

Apply this diff to throw an error:

-         return {} as Connector;
+         throw new Error("Required provider methods not defined for Wagmi Module API");
📝 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
if (!coreProviderMethods || typeof coreProviderMethods === undefined) {
console.error(
"Required provider methods not defined for Wagmi Module API",
);
return {} as Connector;
} else {
if (!coreProviderMethods || typeof coreProviderMethods === undefined) {
console.error(
"Required provider methods not defined for Wagmi Module API",
);
throw new Error("Required provider methods not defined for Wagmi Module API");
} else {
🧰 Tools
🪛 Biome

[error] 184-184: Invalid typeof comparison value: this expression is not a string literal

not a string literal
Unsafe fix: Compare the result of typeof with a valid type name

(lint/suspicious/useValidTypeof)

Comment on lines +252 to +290
switchChain: ({ chainId }: { chainId: number }) => {
const hexChainId = toHex(chainId);
try {
coreProviderMethods
.switchChain(provider, hexChainId)
.then(() => {
return chainId;
});
} catch (error) {
const { code } = error as { code: number };
if (
code === ProviderRpcErrorCode.CHAIN_NOT_ADDED ||
code === ProviderRpcErrorCode.UNRECOGNIZED_CHAIN_ID
) {
if (!chainsList)
throw new Error("Chains list not defined");
// chain has not been added to wallet
const targetChain = chainsList.find(
({ id }) => id === hexChainId,
);
if (!targetChain)
throw new Error("Chain not found in chains list");
coreProviderMethods.updateChain(targetChain);

// add chain to wallet
coreProviderMethods
.addOrSwitchChain(provider, targetChain)
.then(
// @ts-expect-error
(id: string) => {
return fromHex(
id as `0x${string}`,
"number",
);
},
);
}
}
},
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

Ensure switchChain returns a Promise resolving to the chain ID

The switchChain method should return a Promise that resolves to the new chain ID. Currently, it doesn't return any value, which may cause unexpected behavior.

Apply this diff to fix the switchChain method:

 switchChain: ({ chainId }: { chainId: number }) => {
     const hexChainId = toHex(chainId);
-    try {
-        coreProviderMethods
-            .switchChain(provider, hexChainId)
-            .then(() => {
-                return chainId;
-            });
-    } catch (error) {
+    return coreProviderMethods
+        .switchChain(provider, hexChainId)
+        .then(() => chainId)
+        .catch((error) => {
             const { code } = error as { code: number };
             if (
                 code === ProviderRpcErrorCode.CHAIN_NOT_ADDED ||
                 code === ProviderRpcErrorCode.UNRECOGNIZED_CHAIN_ID
             ) {
                 if (!chainsList)
                     throw new Error("Chains list not defined");
                 // chain has not been added to wallet
                 const targetChain = chainsList.find(
                     ({ id }) => id === hexChainId,
                 );
                 if (!targetChain)
                     throw new Error("Chain not found in chains list");
                 coreProviderMethods.updateChain(targetChain);

                 // add chain to wallet
-                coreProviderMethods
-                    .addOrSwitchChain(provider, targetChain)
-                    .then(
-                        (id: string) => {
-                            return fromHex(
-                                id as `0x${string}`,
-                                "number",
-                            );
-                        },
-                    );
+                return coreProviderMethods
+                    .addOrSwitchChain(provider, targetChain)
+                    .then((id: string) =>
+                        fromHex(id as `0x${string}`, "number"),
+                    );
             }
+            throw error;
+        });
 },
📝 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
switchChain: ({ chainId }: { chainId: number }) => {
const hexChainId = toHex(chainId);
try {
coreProviderMethods
.switchChain(provider, hexChainId)
.then(() => {
return chainId;
});
} catch (error) {
const { code } = error as { code: number };
if (
code === ProviderRpcErrorCode.CHAIN_NOT_ADDED ||
code === ProviderRpcErrorCode.UNRECOGNIZED_CHAIN_ID
) {
if (!chainsList)
throw new Error("Chains list not defined");
// chain has not been added to wallet
const targetChain = chainsList.find(
({ id }) => id === hexChainId,
);
if (!targetChain)
throw new Error("Chain not found in chains list");
coreProviderMethods.updateChain(targetChain);
// add chain to wallet
coreProviderMethods
.addOrSwitchChain(provider, targetChain)
.then(
// @ts-expect-error
(id: string) => {
return fromHex(
id as `0x${string}`,
"number",
);
},
);
}
}
},
switchChain: ({ chainId }: { chainId: number }) => {
const hexChainId = toHex(chainId);
return coreProviderMethods
.switchChain(provider, hexChainId)
.then(() => chainId)
.catch((error) => {
const { code } = error as { code: number };
if (
code === ProviderRpcErrorCode.CHAIN_NOT_ADDED ||
code === ProviderRpcErrorCode.UNRECOGNIZED_CHAIN_ID
) {
if (!chainsList)
throw new Error("Chains list not defined");
// chain has not been added to wallet
const targetChain = chainsList.find(
({ id }) => id === hexChainId,
);
if (!targetChain)
throw new Error("Chain not found in chains list");
coreProviderMethods.updateChain(targetChain);
// add chain to wallet
return coreProviderMethods
.addOrSwitchChain(provider, targetChain)
.then((id: string) =>
fromHex(id as `0x${string}`, "number"),
);
}
throw error;
});
},

Comment on lines +227 to +230
const res = await queryClient.fetchQuery(
// @ts-expect-error fixme update @tanstack/react-query version to > 5
queryOptions
);
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

Avoid suppressing TypeScript errors with @ts-expect-error

Using // @ts-expect-error to suppress TypeScript errors can hide potential issues and make the codebase harder to maintain. Consider updating @tanstack/react-query to a compatible version (greater than 5) or refactoring the code to address the TypeScript error without suppressing it.

Would you like assistance in updating the dependency or refactoring the code to handle the TypeScript error properly?

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