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

merge main to develop #4856

Open
wants to merge 31 commits into
base: develop
Choose a base branch
from
Open

merge main to develop #4856

wants to merge 31 commits into from

Conversation

lovelgeorge99
Copy link
Collaborator

@lovelgeorge99 lovelgeorge99 commented Oct 25, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced localization support for Catalan, English, and Spanish with new entries for "Q/acc" and "Q/acc News."
    • Added new links for Q/acc and Q/acc News in the footer for easier navigation.
  • Bug Fixes

    • Improved error handling in transaction processes for better user feedback.
  • Improvements

    • Updated token transfer logic to support USDT transactions more effectively.
    • Enhanced type safety and clarity in transaction handling and user address scoring.
    • Simplified rendering of staking options in the GIVfarm component.
    • Adjustments to donation processing to accommodate tokens with varying decimal configurations.
    • Enhanced handling of token balances and improved clarity in user interface components related to donations.
  • Chores

    • Updated dependency versions in package.json for improved stability.
    • Minor documentation updates for TypeScript configuration references.

Copy link

vercel bot commented Oct 25, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
giveth-dapps-v2 ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 2, 2024 9:41pm

@lovelgeorge99 lovelgeorge99 changed the title merge amin to develop merge main to develop Oct 25, 2024
Copy link
Contributor

coderabbitai bot commented Nov 11, 2024

Walkthrough

This pull request introduces new localization entries for the Q/acc feature across multiple language files (lang/ca.json, lang/en.json, and lang/es.json). It also updates the package.json dependencies, adds a new ABI file for USDT, and modifies various components and hooks to enhance functionality and type safety. Notably, the Footer component now includes links to Q/acc and Q/acc News, while the SelectTokenModal and other components have undergone minor adjustments for clarity and consistency.

Changes

File Change Summary
lang/ca.json, lang/en.json, lang/es.json Added new localization keys for Q/acc and Q/acc News.
package.json Updated dependency versions for viem and wagmi.
src/artifacts/usdtMainnetABI.json Introduced a new ABI file for the USDT smart contract.
src/components/Footer/Footer.tsx Added new links for Q/acc and Q/acc News in the Footer component.
src/components/views/donate/OneTime/SelectTokenModal/SelectTokenModal.tsx Updated filtering logic in the SelectTokenModal component for clarity.
src/components/views/givfarm/GIVfarmBottom.tsx Removed a wrapper component for simplification in the GIVfarmBottom component.
src/config/production.tsx Updated token configurations in the OPTIMISM_CONFIG section.
src/hooks/useCreateEvmDonation.tsx Updated txHash state type for broader compatibility.
src/hooks/usePassport.ts Enhanced type safety for address parameter in fetchUserMBDScore function.
src/lib/constants/links.ts Added new constants for QACC and QACC News URLs.
src/lib/helpers.ts Updated ERC20 transfer handling to use USDT ABI conditionally.
src/lib/transaction.ts Enhanced error handling and type safety in transaction functions.
src/wagmiConfigs.ts Modified transport handling in wagmiConfig based on chain ID.
src/components/views/donate/Recurring/ModifySuperToken/DepositSuperToken.tsx Updated deposit logic to handle token decimals accurately.
src/components/views/donate/Recurring/ModifySuperToken/ModifySuperTokenModal.tsx Updated selectedToken type to support both ISuperToken and IToken.
src/components/views/donate/Recurring/WithDrawSuperToken.tsx Updated token prop handling to reflect superToken usage.
src/components/views/donate/Recurring/RecurringDonationCard.tsx Introduced scaling factor for token calculations.
src/components/views/donate/Recurring/RecurringDonationModal/Item.tsx Streamlined access to token properties for formatting.
src/components/views/donate/Recurring/RecurringDonationModal/RecurringDonationModal.tsx Enhanced donation logic for handling tokens with different decimal configurations.
src/components/views/donate/Recurring/SelectTokenModal/SelectTokenModal.tsx Updated token balance handling to reflect underlying tokens accurately.
src/components/views/userProfile/donationsTab/recurringTab/StreamRow.tsx Added useMemo for efficient token retrieval and improved donation formatting.
src/components/views/donate/Recurring/SelectTokenModal/StreamInfo.tsx Updated to use superToken for displaying token information.

Possibly related PRs

  • fix: issue 4539, qf shown in donation success page for estimated matching amount #4775: This PR modifies the lang/ca.json, lang/en.json, and lang/es.json files to enhance localization strings related to donation matching notifications, which aligns with the new keys added in the main PR for the Catalan language localization.
  • Hotfix/usdt donation on mainnet #4831: This PR introduces a new JSON file usdtMainnetABI.json and updates the src/lib/helpers.ts file to improve transaction handling for USDT donations, which may relate to the overall functionality of the application that the main PR's localization changes support.

Suggested reviewers

  • Meriem-BM
  • MohammadPCh
  • lovelgeorge99
  • RamRamez

🐰 In the meadow, changes bloom,
New links and keys dispel the gloom.
For Q/acc we hop and cheer,
With clearer paths, we hold so dear!
In code we trust, with joy we play,
A brighter world, come what may! 🐇✨


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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

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

🧹 Outside diff range and nitpick comments (20)
src/wagmiConfigs.ts (1)

41-43: Address TODO: Implement proper transport management

The comment indicates this is a temporary solution. Consider implementing a more robust transport management system that:

  1. Handles all chains consistently
  2. Provides fallback mechanisms
  3. Implements proper error handling

Would you like assistance in designing a more comprehensive transport management solution?

src/lib/transaction.ts (1)

60-60: Type assertion looks good, consider adding runtime validation

The explicit type assertion to 0x${string} improves type safety for the getTransaction call. However, since type assertions bypass TypeScript's type checking at runtime, consider adding a validation helper.

Consider adding a validation helper to ensure the hash format at runtime:

const isValidEthereumHash = (hash: string): hash is `0x${string}` => {
  return /^0x[0-9a-fA-F]{64}$/.test(hash);
};

// Usage in the function:
if (!isValidEthereumHash(txHash)) {
  throw new Error('Invalid Ethereum transaction hash format');
}
hash: txHash, // No type assertion needed if validation passes
src/lib/constants/links.ts (1)

59-60: Consider environment-based URL configuration.

While the current implementation works, consider moving service URLs to environment configuration for better flexibility across different environments (staging, development, etc.).

This would allow:

  • Easier environment management
  • Consistent URL handling across services
  • Simplified deployment to different environments

Example approach:

const BASE_QACC_URL = process.env.NEXT_PUBLIC_QACC_URL || 'https://qacc.giveth.io/';
const links = {
  // ...
  QACC: BASE_QACC_URL,
  QACC_NEWS: `${BASE_QACC_URL}news`,
  // ...
};
src/hooks/useCreateEvmDonation.tsx (5)

Line range hint 33-57: Complete the migration of onError handler

The commented out onError handler contains important logic for handling multi-sig transactions. This could lead to missing error handling for Safe transactions.

Would you like me to help implement the migrated version of the onError handler? This would ensure proper handling of multi-sig transaction errors.


Line range hint 59-143: Improve error handling in handleSaveDonation

  1. The catch block uses any type which masks specific error types.
  2. The backup API call uses potentially undefined variables (transaction?.hash, transaction?.nonce, transaction?.from).

Consider these improvements:

-} catch (e: any) {
+} catch (e: unknown) {
+  const error = e as Error;
   await postRequest('/api/donation-backup', true, {
     chainId: chainId || token.networkId,
-    txHash: transaction?.hash,
+    txHash: transaction?.hash ?? txHash,
     amount: amount,
     token,
     projectId,
     anonymous,
-    nonce: transaction?.nonce,
+    nonce: transaction?.nonce ?? undefined,
     chainvineReferred,
-    walletAddress: transaction?.from,
+    walletAddress: transaction?.from ?? undefined,
     symbol: token.symbol,
-    error: e.message,
+    error: error.message,
     useDonationBox,
     relevantDonationTxHash,
   });

Line range hint 145-167: Enhance error handling structure

The current error handling could be more robust with better error discrimination and structured error reporting.

Consider enhancing the error handling:

 const handleError = (
-  error: any,
+  error: Error & {
+    replacement?: { hash: string };
+    transactionHash?: string;
+  },
   donationId: number,
   setFailedModalType: (type: EDonationFailedType) => void,
 ) => {
   console.error('name', error.name);
   const localTxHash = error.replacement?.hash || error.transactionHash;
   setTxHash(localTxHash);

+  const errorDetails = {
+    name: error.name,
+    message: error.message,
+    txHash: localTxHash,
+    donationId,
+  };

   if (error.name === 'TransactionExecutionError') {
     setFailedModalType(EDonationFailedType.FAILED);
   } else {
     console.error('Rejected1', error);
     setFailedModalType(EDonationFailedType.REJECTED);
   }

   setDonationSaved(false);
   updateDonation(donationId, EDonationStatus.FAILED);
-  captureException(error, { tags: { section: 'confirmDonation' } });
+  captureException(error, {
+    tags: { section: 'confirmDonation' },
+    extra: errorDetails,
+  });
 };

Line range hint 169-249: Fix potential memory leak and improve error handling

  1. The Promise resolution logic could lead to memory leaks if the component unmounts before resolution
  2. Using donationId as 0 in error handling might be incorrect for failed transactions

Consider these improvements:

+ const [promiseResolver, setPromiseResolver] = useState<(() => void) | null>(null);

+ useEffect(() => {
+   return () => {
+     if (promiseResolver) {
+       promiseResolver();
+       setPromiseResolver(null);
+     }
+   };
+ }, [promiseResolver]);

 await new Promise(resolve => {
   if (status === 'success') {
-    setResolveState(null);
+    resolve();
   } else {
-    setResolveState(() => resolve);
+    setPromiseResolver(() => resolve);
   }
 });

- handleError(error, 0, setFailedModalType);
+ handleError(error, draftDonationData?.createDraftDonation ?? 0, setFailedModalType);

Line range hint 251-270: Fix missing effect dependencies

The effect hook is missing dependencies that it uses within its callback.

Add missing dependencies:

 useEffect(() => {
   if (status === 'success') {
     updateDonation(donationId, EDonationStatus.VERIFIED);
     setDonationMinted(true);
     if (resolveState) {
       resolveState();
       setResolveState(null);
     }
   }
   const comingFromSafe = isSafeEnv && txHash;
   if (status === 'error' && !comingFromSafe) {
     updateDonation(donationId, EDonationStatus.FAILED);
     setDonationSaved(false);
     createDonationProps?.setFailedModalType(EDonationFailedType.FAILED);
   }
- }, [status, donationId]);
+ }, [status, donationId, resolveState, isSafeEnv, txHash, createDonationProps]);
src/components/views/donate/OneTime/SelectTokenModal/SelectTokenModal.tsx (5)

151-157: Simplify token filtering logic by removing unnecessary variable.

The introduction of sQuery adds unnecessary complexity. The searchQuery variable can be used directly in the filtering logic.

Consider this simpler implementation:

-				const sQuery: string = searchQuery;
				const filtered = tokens.filter(
					token =>
						token.symbol
							.toLowerCase()
-							.includes(sQuery.toLowerCase()) ||
-						token.name.toLowerCase().includes(sQuery.toLowerCase()),
+							.includes(searchQuery.toLowerCase()) ||
+						token.name.toLowerCase().includes(searchQuery.toLowerCase()),
				);

Line range hint 183-191: Improve error handling for balance fetching.

The error from fetchBalances is only logged to console. Users should be notified when balance fetching fails, similar to how token data fetching errors are handled.

Consider adding error notification:

			} catch (error) {
				console.error('error on fetchTokenBalances', { error });
+				setBalanceIsLoading(false);
+				showToastError('Failed to fetch token balances');
			}

Line range hint 164-191: Optimize balance fetching performance.

The balance fetching logic currently runs on every filter change due to filteredTokens dependency. Consider caching the balances and only updating the filtered view.

Consider this approach:

  1. Fetch balances only for tokens (not filteredTokens)
  2. Store full balance map
  3. Filter the displayed tokens using the cached balances
// Add these state variables
const [tokenBalanceMap, setTokenBalanceMap] = useState<Record<string, bigint>>({});
const [isInitialBalanceFetch, setIsInitialBalanceFetch] = useState(true);

// Modify the fetch logic
useEffect(() => {
    const fetchBalances = async () => {
        if (!isConnected || !tokens || !isInitialBalanceFetch) return;
        try {
            setBalanceIsLoading(true);
            const balances = isOnEVM
                ? await fetchEVMTokenBalances(tokens, walletAddress)
                : await Promise.all(/* ... */);
            
            const balanceMap = balances.reduce((acc, {token, balance}) => ({
                ...acc,
                [token.address]: balance
            }), {});
            
            setTokenBalanceMap(balanceMap);
            setIsInitialBalanceFetch(false);
            setBalanceIsLoading(false);
        } catch (error) {
            console.error('error on fetchTokenBalances', { error });
            setBalanceIsLoading(false);
            showToastError('Failed to fetch token balances');
        }
    };
    fetchBalances();
}, [tokens, isConnected, isOnEVM, walletAddress, isInitialBalanceFetch]);

// Use the balance map when displaying tokens
const sortedTokens = filteredTokens.map(token => ({
    token,
    balance: tokenBalanceMap[token.address]
})).sort(/* ... */);

Line range hint 62-108: Add warning for custom token support.

When users input custom token addresses, they should be warned about potential risks of interacting with unknown tokens.

Consider adding a warning message when a custom token is detected:

					if (existingToken) {
						setCustomToken(undefined);
						setFilteredTokens([existingToken]);
					} else if (isOnEVM && acceptCustomToken) {
+						// Show warning for custom tokens
+						showToastError(
+							'Warning: Custom tokens may be unsafe. Please verify the token contract carefully.',
+							{ type: 'warning', duration: 6000 }
+						);
						const initialToken = {
							address: searchQuery,
							decimals: 18,
							name: shortenAddress(searchQuery),
							symbol: shortenAddress(searchQuery),

Line range hint 256-267: Enhance input field accessibility.

The search input field lacks proper accessibility attributes which are essential for screen readers and keyboard navigation.

Add ARIA attributes and labels:

 const Input = styled.input`
+	/* existing styles */
 	padding: 8px 16px;
 	padding-right: 48px;
 	width: 100%;
 	border: none;
 	outline: none;
 	font-size: 16px;
 	background: transparent;
 	line-height: 24px;
 `;
+
+// Usage in JSX:
+<Input
+	placeholder='Search name or paste an address'
+	value={searchQuery}
+	onChange={e => setSearchQuery(e.target.value)}
+	aria-label="Search tokens"
+	role="searchbox"
+/>
src/hooks/usePassport.ts (3)

238-240: LGTM! Consider adding type validation

The type assertion for the Ethereum address improves type safety. However, consider adding runtime validation to ensure the address format is correct before the assertion.

Consider adding validation before the assertion:

+ if (!address.match(/^0x[a-fA-F0-9]{40}$/)) {
+   throw new Error('Invalid Ethereum address format');
+ }
const userAddressScore = await scoreUserAddress(
  address as `0x${string}`,
);

Line range hint 391-391: Remove debugging console.log statements

There are multiple console.log statements that appear to be debugging code. These should be removed as they:

  1. Expose sensitive user data and addresses
  2. Add noise to the browser console
  3. May impact performance in production

Remove all console.log statements or replace them with proper logging if needed:

- console.log('******0', address, isUserFullFilled, user);
- console.log('******1', address, isUserFullFilled, user);
- console.log('******2', address, isUserFullFilled, user);
- console.log('******3', address, isUserFullFilled, user);
- console.log('******4', address, isUserFullFilled, user);
- console.log('******5', address, isUserFullFilled, user);
- console.log('******6', address, isUserFullFilled, user);
- console.log('******7', address, isUserFullFilled, user);

Also applies to: 393-393, 396-396, 398-398, 400-400, 402-402, 404-404, 406-406, 408-408


Line range hint 242-245: Enhance error handling with more context

The current error handling could be improved to provide more context about what went wrong during the score fetching process.

Consider enhancing the error handling:

 } catch (error) {
-    console.error('Failed to fetch user address score:', error);
+    const errorMessage = error instanceof Error ? error.message : 'Unknown error';
+    console.error(`Failed to fetch MBD score for address ${address}:`, {
+      error: errorMessage,
+      context: { address, activeQFRound: activeQFRound?.id }
+    });
     updateState(user!);
 }
src/lib/helpers.ts (3)

379-381: Improve the comment about the viem package issue

The current comment about the viem package issue is not very clear. Consider adding more context or a reference to the issue.

-// 'viem' ABI contract for USDT donation fails on mainnet
-// so we use the USDT mainnet ABI instead and put inside usdtMainnetABI.json file
-// update for 'viem' package to fix this issue doesn't work
+// Using custom USDT mainnet ABI due to incompatibility with viem's default ERC20 ABI
+// See: [Add link to the viem issue here]
+// TODO: Remove this workaround once the viem package issue is resolved

391-400: Improve type safety for decimals handling

The current implementation could be improved to handle decimals more safely:

  1. Add type assertion for the readContract result
  2. Add validation for the decimals value
-let decimals = await readContract(wagmiConfig, {
+const decimalsResult = await readContract(wagmiConfig, {
   ...baseProps,
   functionName: 'decimals',
-});
+}) as bigint | number;

-if (typeof decimals === 'bigint') {
-  decimals = Number(decimals.toString());
+const decimals = typeof decimalsResult === 'bigint' 
+  ? Number(decimalsResult.toString())
+  : decimalsResult;
+
+if (decimals < 0 || decimals > 18) {
+  throw new Error('Invalid decimals value');
}

const value = parseUnits(params.value, decimals);

379-400: Add specific error handling for common ERC20 transfer failures

The function should handle common ERC20 transfer failures more specifically to provide better error messages to users.

 async function handleErc20Transfer(
   params: TransactionParams,
   contractAddress: Address,
 ): Promise<Address> {
+  try {
     const ABItoUse =
       contractAddress === '0xdac17f958d2ee523a2206206994597c13d831ec7'
         ? usdtMainnetABI
         : erc20Abi;
 
     const baseProps = {
       address: contractAddress,
       abi: ABItoUse,
     };
 
     // ... rest of the function ...
+  } catch (error: any) {
+    // Handle specific ERC20 errors
+    if (error.message?.includes('insufficient allowance')) {
+      throw new Error('Insufficient token allowance');
+    }
+    if (error.message?.includes('insufficient balance')) {
+      throw new Error('Insufficient token balance');
+    }
+    // Re-throw other errors
+    throw error;
+  }
 }
lang/ca.json (1)

101-102: The new entries should be translated to Catalan

The new entries "Q/acc" and "Q/acc News" are in English, while this is a Catalan localization file. Consider translating these entries to maintain consistency with the rest of the translations.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 4c6bd33 and 7c3e93d.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (15)
  • lang/ca.json (1 hunks)
  • lang/en.json (1 hunks)
  • lang/es.json (1 hunks)
  • package.json (1 hunks)
  • src/artifacts/usdtMainnetABI.json (1 hunks)
  • src/components/Footer/Footer.tsx (2 hunks)
  • src/components/views/donate/OneTime/SelectTokenModal/SelectTokenModal.tsx (1 hunks)
  • src/components/views/givfarm/GIVfarmBottom.tsx (1 hunks)
  • src/config/production.tsx (1 hunks)
  • src/hooks/useCreateEvmDonation.tsx (2 hunks)
  • src/hooks/usePassport.ts (1 hunks)
  • src/lib/constants/links.ts (1 hunks)
  • src/lib/helpers.ts (2 hunks)
  • src/lib/transaction.ts (1 hunks)
  • src/wagmiConfigs.ts (2 hunks)
✅ Files skipped from review due to trivial changes (3)
  • package.json
  • src/artifacts/usdtMainnetABI.json
  • src/config/production.tsx
🔇 Additional comments (11)
src/wagmiConfigs.ts (2)

5-5: LGTM: Clean import addition

The import of mainnet from viem/chains is appropriate for the chain identification logic.


46-49: Verify the impact of using public transport for non-mainnet chains

The current implementation falls back to public transport for non-mainnet chains, which could lead to rate limiting or reliability issues in production.

Let's check for other chain configurations in the codebase:

✅ Verification successful

Based on the search results, I can now provide a definitive response about the impact of using public transport for non-mainnet chains:

Using public transport for non-mainnet chains is an intentional and safe design choice

The implementation in wagmiConfigs.ts is actually following best practices because:

  1. The code specifically checks for mainnet to use Infura, while other networks use their own dedicated RPC endpoints configured in their respective network configs (as seen in the search results showing RPC configurations for various chains).

  2. The "public transport" for non-mainnet chains refers to the chain's own RPC endpoints (e.g., for Optimism, Gnosis, etc.) which are properly configured in the network configs and are production-ready.

  3. The codebase shows careful handling of different networks with proper RPC configurations, including fallback providers and network-specific endpoints.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for chain configurations and RPC endpoints
echo "Searching for chain configurations..."
rg -l "chains.*=.*\[" --type ts

echo "Searching for RPC URL configurations..."
rg -l "rpc.*url" --type ts --type json

echo "Searching for Infura usage..."
rg -l "infura" --type ts --type json --type env

Length of output: 475


Script:

#!/bin/bash
# Let's search for chain configurations and HTTP transport setup more thoroughly
echo "Searching for chain and transport configurations..."
rg -A 5 "chain.*=|transport.*=|http\(.*\)" --type ts

echo "Searching for files containing mainnet or network configurations..."
rg -A 5 "mainnet|chain\.id" --type ts

echo "Searching for RPC related configurations..."
rg -A 5 "rpc|provider" --type ts

Length of output: 211090

src/lib/constants/links.ts (2)

59-60: LGTM! New QACC constants follow existing patterns.

The new QACC-related constants are well-structured and follow the established naming conventions.


59-60: Verify Footer component integration.

Let's verify that these new links are properly integrated into the Footer component with correct localization.

✅ Verification successful

Links are properly integrated with localization support

The QACC links are correctly integrated in the Footer component with:

  • Proper usage of links.QACC and links.QACC_NEWS constants
  • Translation keys component.title.qacc and component.title.qacc_news implemented in multiple language files (en, es, ca)
  • Correct implementation of formatMessage for localization
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify Footer component integration and localization

# Check Footer component for QACC links usage
echo "Checking Footer component for QACC links..."
rg -A 2 "QACC|QACC_NEWS" "src/components/Footer"

# Verify localization keys exist
echo "Verifying localization entries..."
rg -A 1 "qacc" "lang/*.json"

Length of output: 719


Script:

#!/bin/bash
# Let's check for localization files in the correct directory structure
echo "Finding localization files..."
fd -e json -d 3 "en|es|de"

# Once we find the location, let's check for QACC related translations
echo "Checking translations in public directory..."
rg -A 1 "qacc" "public/locales/"

# Let's also verify the import and usage pattern in Footer
echo "Checking Footer component structure..."
cat "src/components/Footer/Footer.tsx"

Length of output: 8185


Script:

#!/bin/bash
# Let's check for QACC related translations in the lang directory
echo "Checking translations in lang directory..."
rg -A 1 "qacc" "lang/"

# Let's also check the translation IDs used in Footer for QACC
echo "Checking specific translation keys used..."
rg "id: 'component.title.qacc" "src/components/Footer/Footer.tsx"

Length of output: 862

src/components/views/givfarm/GIVfarmBottom.tsx (2)

191-191: LGTM: Simplified pool rendering structure

The removal of the extra Col wrapper simplifies the component structure. The grid layout is still maintained through the renderPool function which wraps each pool with appropriate Col components.


191-191: Verify StakeTogetherCard removal impact

The removal of StakeTogetherCard from this component might affect staking functionality. Please ensure this feature is properly relocated or intentionally removed.

✅ Verification successful

Let me gather more information about the component's usage and potential relocation.


Let me check where this component is being used and how it's being rendered in the application.


Based on the git history and code changes, I can now provide a definitive response about the StakeTogetherCard removal.

StakeTogetherCard removal is intentional and safe

The removal of StakeTogetherCard from GIVfarmBottom was an intentional change (commit 9f67e61) that aligns with the codebase's evolution. The component was:

  • First added on April 10, 2024 (commit 59c04ab)
  • Actively maintained with features like conditional links and translations
  • Deliberately removed on October 23, 2024 (commit 9f67e61)

The removal is safe because:

  • The component still exists in the codebase at its original location (src/components/cards/StakingCards/BaseStakingCard/StakeTogetherCard.tsx)
  • The removal was a targeted change to only remove it from the GIVfarm view
  • No other components were depending on its presence in GIVfarmBottom
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for StakeTogetherCard usage in the codebase
echo "Checking if StakeTogetherCard is used elsewhere:"
rg "StakeTogetherCard" -l

# Search for any new staking-related components that might replace it
echo -e "\nChecking for potential replacement components:"
rg -i "stake.*card" -l

Length of output: 719


Script:

#!/bin/bash
# Check the git history for StakeTogetherCard changes
echo "Checking recent changes to StakeTogetherCard:"
git log -p -- src/components/cards/StakingCards/BaseStakingCard/StakeTogetherCard.tsx

# Check for imports of StakeTogetherCard
echo -e "\nChecking for StakeTogetherCard imports:"
rg "import.*StakeTogetherCard" -A 1

# Check the content of StakeTogetherCard component
echo -e "\nChecking StakeTogetherCard component content:"
cat src/components/cards/StakingCards/BaseStakingCard/StakeTogetherCard.tsx

Length of output: 9063


Script:

#!/bin/bash
# Check for StakeTogetherCard imports and usage
echo "Checking for StakeTogetherCard imports and usage:"
rg "StakeTogetherCard" -B 2 -A 2

# Check the GIVfarmBottom component's history
echo -e "\nChecking GIVfarmBottom recent changes:"
git log -p -- src/components/views/givfarm/GIVfarmBottom.tsx

Length of output: 76058

src/components/Footer/Footer.tsx (1)

244-244: Verify the layout impact of gap change

The gap property change from what appears to be 72px to 0px in the laptopL media query might affect the spacing between link columns in the footer.

Please verify that this change:

  1. Doesn't cause overlapping of link columns
  2. Maintains consistent spacing with other footer elements
  3. Looks correct across different screen sizes

Consider providing screenshots of the footer layout before and after this change to confirm the visual impact.

src/hooks/useCreateEvmDonation.tsx (1)

21-21: ⚠️ Potential issue

Potential type safety issue with txHash assertion

The broadened type of txHash to include Address combined with the forced type assertion to 0x${string} in useWaitForTransactionReceipt could lead to runtime errors if txHash is actually an Address.

Consider adding a type guard:

-hash: txHash as `0x${string}`,
+hash: txHash?.startsWith('0x') ? txHash as `0x${string}` : undefined,

Also applies to: 33-33

src/lib/helpers.ts (1)

22-22: Verify the USDT mainnet ABI file

Let's ensure the imported ABI file exists and contains the correct USDT contract interface.

✅ Verification successful

USDT Mainnet ABI file verified successfully

The ABI file exists at the expected location and contains all the required ERC20 functions (transfer, balanceOf, decimals) needed for USDT token operations.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify USDT mainnet ABI file existence and content

# Check if the ABI file exists
if [ -f "src/artifacts/usdtMainnetABI.json" ]; then
    echo "File exists. Checking content..."
    
    # Verify it's a valid JSON and contains essential USDT functions
    jq -e '
        . as $abi |
        ["transfer", "balanceOf", "decimals"] as $required |
        $required - [($abi[] | select(.type == "function") | .name)] | length == 0
    ' src/artifacts/usdtMainnetABI.json
else
    echo "ABI file not found at expected location"
    # Search for the file in case it's in a different location
    fd -t f "usdtMainnetABI.json"
fi

Length of output: 340

lang/en.json (1)

100-101: LGTM! The new Q/acc localization entries are properly formatted.

The new entries follow the established naming conventions and are correctly placed in alphabetical order with other component titles.

lang/es.json (1)

709-710: LGTM! The new Q/acc related translations are properly added.

The new entries follow the established patterns for component titles and appropriately preserve the branded terms without translation.

// TODO: we must manage this for all chains in a better way
// basically to stop using viem's public transport
// leaving this as a hotfix to keep it quick
const infuraKey = process.env.NEXT_PUBLIC_INFURA_API_KEY;
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

Add environment variable validation and optimize transport creation

Several improvements are needed for production readiness:

  1. The Infura key should be validated at startup
  2. The HTTP transport instance could be memoized

Consider this implementation:

- const infuraKey = process.env.NEXT_PUBLIC_INFURA_API_KEY;
+ const infuraKey = process.env.NEXT_PUBLIC_INFURA_API_KEY;
+ if (!infuraKey) {
+   console.warn('Infura API key not found, falling back to public transport');
+ }
+ 
+ // Memoize transports
+ const mainnetTransport = infuraKey ? http(`https://mainnet.infura.io/v3/${infuraKey}`) : http();
+ const defaultTransport = http();
+ 
  const customHttp =
    chain.id === mainnet.id && infuraKey
-     ? http(`https://mainnet.infura.io/v3/${infuraKey}`)
-     : http();
+     ? mainnetTransport
+     : defaultTransport;

Also applies to: 46-51

Comment on lines +146 to +152
<a href={links.QACC} target='_blank'>
<LinkItem color={textColor}>
{formatMessage({
id: 'component.title.qacc',
})}
</LinkItem>
</a>
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

Add security attributes to external links

The new external links should include rel="noreferrer noopener" attributes to prevent potential security vulnerabilities when using target="_blank".

Apply this diff:

-						<a href={links.QACC} target='_blank'>
+						<a href={links.QACC} target='_blank' rel='noreferrer noopener'>

-						<a href={links.QACC_NEWS} target='_blank'>
+						<a href={links.QACC_NEWS} target='_blank' rel='noreferrer noopener'>

Also applies to: 153-159

Comment on lines +379 to +385
// 'viem' ABI contract for USDT donation fails on mainnet
// so we use the USDT mainnet ABI instead and put inside usdtMainnetABI.json file
// update for 'viem' package to fix this issue doesn't work
const ABItoUse =
contractAddress === '0xdac17f958d2ee523a2206206994597c13d831ec7'
? usdtMainnetABI
: erc20Abi;
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

Move the USDT contract address to constants

The hardcoded USDT contract address should be moved to a constants file for better maintainability and reusability.

+// In constants.ts
+export const USDT_MAINNET_ADDRESS = '0xdac17f958d2ee523a2206206994597c13d831ec7' as const;

-const ABItoUse =
-  contractAddress === '0xdac17f958d2ee523a2206206994597c13d831ec7'
+const ABItoUse =
+  contractAddress === USDT_MAINNET_ADDRESS

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

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

🧹 Outside diff range and nitpick comments (7)
src/components/views/userProfile/donationsTab/recurringTab/StreamRow.tsx (3)

22-28: LGTM! Consider adding error handling.

The memoization of superToken lookup is a good performance optimization. However, consider adding a fallback for cases where the token is not found in the config.

 const superToken = useMemo(
   () =>
     config.OPTIMISM_CONFIG.SUPER_FLUID_TOKENS.find(
       s => s.id === tokenStream[0].token.id,
-    ),
+    ) ?? null,
   [tokenStream],
 );

67-67: Add a fallback for undefined symbol.

While the optional chaining is good, consider adding a fallback value for better user experience when the symbol is undefined.

-<P>{superToken?.symbol}</P>
+<P>{superToken?.symbol ?? 'Unknown Token'}</P>

Line range hint 1-141: Consider extracting business logic to custom hooks.

The component currently handles both UI rendering and complex business logic. Consider extracting the following into custom hooks for better maintainability and reusability:

  1. Token balance and flow rate calculations
  2. Chain switching logic
  3. Super token lookup and validation

Example structure:

// useStreamCalculations.ts
export const useStreamCalculations = (tokenStream: ISuperfluidStream[]) => {
  const totalFlowRate = useMemo(() => 
    tokenStream.reduce((acc, curr) => acc + BigInt(curr.currentFlowRate), 0n),
    [tokenStream]
  );
  // ... other calculations
  return { totalFlowRate, monthlyFlowRate, runOutMonth };
};

// In StreamRow.tsx
const { totalFlowRate, monthlyFlowRate, runOutMonth } = useStreamCalculations(tokenStream);
src/components/views/donate/Recurring/RecurringDonationModal/RecurringDonationModal.tsx (1)

231-252: Consider generalizing token decimal conversions for tokens with varying decimals

Currently, the code handles tokens with 6 decimals by converting amounts to 18 decimals. To enhance flexibility and future-proof the code, consider generalizing this logic to handle tokens with any number of decimals by adjusting amounts based on the difference between the token's decimals and the standard 18 decimals.

src/components/views/donate/Recurring/ModifySuperToken/DepositSuperToken.tsx (1)

137-143: Handling tokens with 6 decimals by adjusting amount

The added logic converts the amount to 18 decimals if the token has 6 decimals, ensuring compatibility with the Superfluid upgrade operation. Consider generalizing this to handle tokens with any number of decimals.

src/components/views/donate/Recurring/SelectTokenModal/SelectTokenModal.tsx (2)

88-90: Consider using case-insensitive comparison for token symbols

The token filtering logic uses case-sensitive comparison which could lead to matching issues. Consider converting symbols to lowercase for comparison.

 const filteredTokens = superTokens.filter(token => {
-  return !(newBalances[token.underlyingToken.symbol] > 0n);
+  return !(newBalances[token.underlyingToken.symbol.toLowerCase()] > 0n);
 });

192-223: Simplify the underlying tokens rendering logic

The Fragment wrapper (<>) is unnecessary since we're already mapping over the tokens. Also, the key prop should be moved to the TokenInfo component.

 {underlyingTokens.length > 0 ? (
   underlyingTokens.map(token => (
-    <>
       <TokenInfo
-        key={token.underlyingToken?.symbol}
+        key={token.underlyingToken.symbol}
         token={token.underlyingToken}
         balance={balances[token.underlyingToken.symbol]}
         disable={
           balances[token.underlyingToken.symbol] === undefined ||
           balances[token.underlyingToken.symbol] === 0n
         }
         onClick={() => {
           setSelectedRecurringToken({
             token: token.underlyingToken,
             balance: balances[token.underlyingToken.symbol],
           });
           setShowModal(false);
         }}
       />
-    </>
   ))
 )
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 7c3e93d and 83737bf.

⛔ Files ignored due to path filters (2)
  • public/images/tokens/cbBTC.png is excluded by !**/*.png
  • public/images/tokens/cbBTC.svg is excluded by !**/*.svg
📒 Files selected for processing (9)
  • src/components/views/donate/Recurring/ModifySuperToken/DepositSuperToken.tsx (2 hunks)
  • src/components/views/donate/Recurring/ModifySuperToken/ModifySuperTokenModal.tsx (1 hunks)
  • src/components/views/donate/Recurring/ModifySuperToken/WithDrawSuperToken.tsx (1 hunks)
  • src/components/views/donate/Recurring/RecurringDonationCard.tsx (3 hunks)
  • src/components/views/donate/Recurring/RecurringDonationModal/Item.tsx (2 hunks)
  • src/components/views/donate/Recurring/RecurringDonationModal/RecurringDonationModal.tsx (4 hunks)
  • src/components/views/donate/Recurring/SelectTokenModal/SelectTokenModal.tsx (5 hunks)
  • src/components/views/userProfile/donationsTab/recurringTab/StreamRow.tsx (6 hunks)
  • src/config/production.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/config/production.tsx
🔇 Additional comments (17)
src/components/views/userProfile/donationsTab/recurringTab/StreamRow.tsx (2)

119-126: LGTM! Good type safety improvement.

The addition of the superToken existence check before rendering the modal and passing it as a prop improves type safety and prevents potential runtime errors.


71-72: Verify decimal places handling for all super tokens.

The code assumes 18 decimals for formatting the monthly flow rate. This might not be correct for all super tokens.

src/components/views/donate/Recurring/RecurringDonationModal/RecurringDonationModal.tsx (4)

13-13: Importing ethers library for token amount conversions

The addition of import { ethers } from 'ethers'; is necessary for handling token amount conversions using ethers.utils.parseUnits in the code below.


256-256: Updating upgrade operation with adjusted amount

Using newAmount.toString() in the upgrade operation ensures that the correct, decimal-adjusted token amount is utilized during the upgrade process.


271-272: Flow rate calculation uses adjusted token amount

The flow rate _flowRate is calculated using newPerMonthAmount, which accounts for tokens with different decimals. This ensures accurate flow rates for tokens after decimal adjustment.


305-305: Calculating Giveth donation flow rate with adjusted amount

The calculation of Giveth's flow rate using newPerMonthAmount correctly accounts for tokens with adjusted decimals, ensuring accurate donation amounts.

src/components/views/donate/Recurring/RecurringDonationModal/Item.tsx (3)

32-32: Accurate formatting of token amounts using token.decimals

By using token.decimals directly in formatUnits, the amount is formatted accurately based on the token's actual decimal precision.


34-34: Correct token symbol resolution

The expression {token.underlyingToken?.symbol || token.symbol} ensures that the underlying token symbol is displayed if available, otherwise it falls back to the super token's symbol.


47-47: Consistent use of token.decimals in USD value calculation

Using token.decimals in formatting the USD value maintains consistency and accuracy in the displayed amounts.

src/components/views/donate/Recurring/ModifySuperToken/ModifySuperTokenModal.tsx (1)

16-16: Updating selectedToken type for enhanced flexibility

Changing the type of selectedToken to ISuperToken | IToken allows the modal to handle both super tokens and regular tokens, improving the component's flexibility.

src/components/views/donate/Recurring/ModifySuperToken/WithDrawSuperToken.tsx (1)

190-190: Passing superToken to Item component

Updating the Item component to receive superToken instead of token ensures that the correct token properties are used when displaying withdrawal information.

src/components/views/donate/Recurring/ModifySuperToken/DepositSuperToken.tsx (2)

5-5: Importing ethers library for token amount conversions

The import of ethers is necessary to utilize ethers.utils.parseUnits for converting token amounts based on decimal precision.


147-147: Using adjusted amount in upgrade operation

Updating the upgrade method to use newAmount.toString() ensures the correct, decimal-adjusted amount is used during the token upgrade process.

src/components/views/donate/Recurring/SelectTokenModal/SelectTokenModal.tsx (1)

132-134: LGTM! Good use of case-insensitive comparison

The case-insensitive comparison for token IDs is a good practice to prevent matching issues.

src/components/views/donate/Recurring/RecurringDonationCard.tsx (3)

145-148: LGTM! Good handling of token decimals

The introduction of special handling for 6 decimal tokens and the scaling factor is a good practice to ensure accurate calculations across different token standards.

Also applies to: 153-156


158-158: LGTM! Proper scaling of calculations

The calculations for totalPerSec and totalStreamPerMonth correctly use the scaling factor to maintain precision.

Also applies to: 180-181


163-164: LGTM! Consistent case-insensitive token handling

The use of lowercase for token IDs is consistent with the approach in SelectTokenModal, preventing case-sensitivity issues.

Also applies to: 780-782

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

🧹 Outside diff range and nitpick comments (3)
src/components/views/donate/Recurring/SelectTokenModal/StreamInfo.tsx (1)

Line range hint 39-41: Remove redundant token lookup

The findTokenByAddress call seems unnecessary since we now have the superToken prop that contains the token information.

-const token = findTokenByAddress(stream[0].token.id);
-const underlyingToken = token?.underlyingToken;
+const underlyingToken = superToken.underlyingToken;
src/components/views/donate/Recurring/SelectTokenModal/SelectTokenModal.tsx (2)

88-90: Simplify token filtering condition

The current filtering condition can be simplified for better readability.

 const filteredTokens = superTokens.filter(token => {
-  return !(newBalances[token.underlyingToken.symbol] > 0n);
+  return !newBalances[token.underlyingToken.symbol];
 });

54-54: Consider memoizing underlyingTokens

Since underlyingTokens is derived from the constant superTokens, consider using useMemo to prevent unnecessary re-renders.

-const [underlyingTokens, setUnderlyingTokens] = useState<ISuperToken[]>([]);
+const underlyingTokens = useMemo(() => superTokens, []);

// Remove this line
-setUnderlyingTokens(superTokens);

Also applies to: 93-93

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 83737bf and 7eafe7a.

📒 Files selected for processing (3)
  • src/components/views/donate/Recurring/RecurringDonationCard.tsx (5 hunks)
  • src/components/views/donate/Recurring/SelectTokenModal/SelectTokenModal.tsx (5 hunks)
  • src/components/views/donate/Recurring/SelectTokenModal/StreamInfo.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/views/donate/Recurring/RecurringDonationCard.tsx
🔇 Additional comments (2)
src/components/views/donate/Recurring/SelectTokenModal/StreamInfo.tsx (1)

14-14: LGTM: Props interface enhancement

The addition of the superToken prop improves type safety and makes the token information flow more explicit.

Also applies to: 24-24

src/components/views/donate/Recurring/SelectTokenModal/SelectTokenModal.tsx (1)

131-133: LGTM: Case-insensitive token ID comparison

The case-insensitive comparison is a good defensive programming practice that prevents issues with token ID casing mismatches.

Comment on lines +191 to +222
{underlyingTokens.length > 0 ? (
underlyingTokens.map(token => (
<>
<TokenInfo
key={token.underlyingToken?.symbol}
token={token.underlyingToken}
balance={
balances[
token.underlyingToken.symbol
]
}
disable={
balances[
token.underlyingToken.symbol
] === undefined ||
balances[
token.underlyingToken.symbol
] === 0n
}
onClick={() => {
setSelectedRecurringToken({
token: token.underlyingToken,
balance:
balances[
token.underlyingToken
.symbol
],
});
setShowModal(false);
}}
/>
</>
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 Fragment usage and key placement

The current implementation has several issues:

  1. Unnecessary Fragment wrapping
  2. Key prop on Fragment instead of TokenInfo
  3. Deeply nested balance access pattern
 {underlyingTokens.length > 0 ? (
   underlyingTokens.map(token => (
-    <>
       <TokenInfo
-        key={token.underlyingToken?.symbol}
+        key={token.underlyingToken.id}
         token={token.underlyingToken}
-        balance={balances[token.underlyingToken.symbol]}
+        balance={balances[token.underlyingToken.symbol] || 0n}
         disable={
-          balances[token.underlyingToken.symbol] === undefined ||
-          balances[token.underlyingToken.symbol] === 0n
+          !balances[token.underlyingToken.symbol]
         }
         onClick={() => {
           setSelectedRecurringToken({
             token: token.underlyingToken,
-            balance: balances[token.underlyingToken.symbol],
+            balance: balances[token.underlyingToken.symbol] || 0n,
           });
           setShowModal(false);
         }}
       />
-    </>
   ))
📝 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
{underlyingTokens.length > 0 ? (
underlyingTokens.map(token => (
<>
<TokenInfo
key={token.underlyingToken?.symbol}
token={token.underlyingToken}
balance={
balances[
token.underlyingToken.symbol
]
}
disable={
balances[
token.underlyingToken.symbol
] === undefined ||
balances[
token.underlyingToken.symbol
] === 0n
}
onClick={() => {
setSelectedRecurringToken({
token: token.underlyingToken,
balance:
balances[
token.underlyingToken
.symbol
],
});
setShowModal(false);
}}
/>
</>
{underlyingTokens.length > 0 ? (
underlyingTokens.map(token => (
<TokenInfo
key={token.underlyingToken.id}
token={token.underlyingToken}
balance={balances[token.underlyingToken.symbol] || 0n}
disable={!balances[token.underlyingToken.symbol]}
onClick={() => {
setSelectedRecurringToken({
token: token.underlyingToken,
balance: balances[token.underlyingToken.symbol] || 0n,
});
setShowModal(false);
}}
/>
))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

5 participants