Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 96 additions & 30 deletions web/src/app/[cat]/InteractionClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { useAccount, useWriteContract, useWaitForTransactionReceipt } from "wagmi";
import { parseEther } from "viem";
import { showTransactionToast } from "@/components/ui/transaction-toast";

// Define supported chain IDs
type SupportedChainId = 1 | 137 | 534351 | 5115 | 61 | 2001;
Expand All @@ -35,6 +36,8 @@ export default function InteractionClient() {
const [newMaxSupply, setNewMaxSupply] = useState("");
const [newThresholdSupply, setNewThresholdSupply] = useState("");
const [newMaxExpansionRate, setNewMaxExpansionRate] = useState("");
const [transferRestricted, setTransferRestricted] = useState<boolean>(true);


const [tokenAddress, setTokenAddress] = useState<`0x${string}`>("0x0");
const [chainId, setChainId] = useState<SupportedChainId | null>(null);
Expand Down Expand Up @@ -126,6 +129,14 @@ export default function InteractionClient() {
transactionHash: tokenAddress,
timestamp: new Date().toISOString(),
});

const restricted = (await publicClient.readContract({
address: tokenAddress,
abi: CONTRIBUTION_ACCOUNTING_TOKEN_ABI,
functionName: "transferRestricted",
})) as boolean;
setTransferRestricted(restricted);

} catch (error) {
console.error("Error fetching token details:", error);
setError("Failed to fetch token details");
Expand All @@ -142,13 +153,9 @@ export default function InteractionClient() {

// Contract write hooks
const { writeContract: mint, data: mintData } = useWriteContract();

const { writeContract: reduceMaxSupply, data: reduceMaxSupplyData } = useWriteContract();

const { writeContract: reduceThresholdSupply, data: reduceThresholdSupplyData } = useWriteContract();

const { writeContract: reduceMaxExpansionRate, data: reduceMaxExpansionRateData } = useWriteContract();

const { writeContract: disableTransferRestriction, data: disableTransferRestrictionData } = useWriteContract();

// Transaction hooks
Expand All @@ -172,6 +179,56 @@ export default function InteractionClient() {
hash: disableTransferRestrictionData,
});

useEffect(() => {
if (mintData) {
showTransactionToast({
hash: mintData,
chainId: chainId!,
message: "Tokens minted successfully!",
});
}
}, [mintData, chainId]);
Comment on lines +182 to +190
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add null check for chainId to prevent runtime errors.

The non-null assertion operator (!) on chainId could cause runtime errors if chainId is null. Add a safety check:

 useEffect(() => {
-  if (mintData) {
+  if (mintData && chainId) {
     showTransactionToast({
       hash: mintData,
-      chainId: chainId!,
+      chainId: chainId,
       message: "Tokens minted successfully!",
     });
   }
 }, [mintData, chainId]);

This same issue appears in all the transaction toast useEffect hooks (lines 192-230). Apply the same fix to all of them.

📝 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
useEffect(() => {
if (mintData) {
showTransactionToast({
hash: mintData,
chainId: chainId!,
message: "Tokens minted successfully!",
});
}
}, [mintData, chainId]);
useEffect(() => {
if (mintData && chainId) {
showTransactionToast({
hash: mintData,
chainId: chainId,
message: "Tokens minted successfully!",
});
}
}, [mintData, chainId]);
🤖 Prompt for AI Agents
In web/src/app/[cat]/InteractionClient.tsx around lines 182 to 190 and also in
the useEffect hooks from lines 192 to 230, the code uses the non-null assertion
operator on chainId which can cause runtime errors if chainId is null. To fix
this, add a null check to ensure chainId is defined before calling
showTransactionToast. Wrap the showTransactionToast call inside an if statement
that checks if chainId is not null or undefined in all these useEffect hooks.


useEffect(() => {
if (reduceMaxSupplyData) {
showTransactionToast({
hash: reduceMaxSupplyData,
chainId: chainId!,
message: "Max supply updated successfully!",
});
}
}, [reduceMaxSupplyData, chainId]);

useEffect(() => {
if (reduceThresholdSupplyData) {
showTransactionToast({
hash: reduceThresholdSupplyData,
chainId: chainId!,
message: "Threshold supply updated successfully!",
});
}
}, [reduceThresholdSupplyData, chainId]);

useEffect(() => {
if (reduceMaxExpansionRateData) {
showTransactionToast({
hash: reduceMaxExpansionRateData,
chainId: chainId!,
message: "Max expansion rate updated successfully!",
});
}
}, [reduceMaxExpansionRateData, chainId]);

useEffect(() => {
if (disableTransferRestrictionData) {
showTransactionToast({
hash: disableTransferRestrictionData,
chainId: chainId!,
message: "Transfer restriction disabled successfully!",
});
}
}, [disableTransferRestrictionData, chainId]);

if (isLoading) {
return (
<div className="w-full h-screen flex items-center justify-center bg-gray-50 dark:bg-black">
Expand All @@ -195,7 +252,7 @@ export default function InteractionClient() {
<div className="max-w-7xl mx-auto space-y-8">
{/* Header Section */}
<div className="text-center mb-12">
<h1 className="text-3xl text-gray-600 dark:text-gray-400">
<h1 className="text-4xl text-gray-800 font-bold dark:text-gray-400">
{tokenDetails.tokenSymbol} Token Management
</h1>
</div>
Expand Down Expand Up @@ -261,7 +318,7 @@ export default function InteractionClient() {
<CardContent className="pt-6">
<div className="max-w-md mx-auto space-y-6">
<div className="space-y-2">
<Label htmlFor="mintAmount" className="text-lg">Amount to Mint</Label>
<Label htmlFor="mintAmount" className="text-lg font-bold">Amount to Mint</Label>
<Input
id="mintAmount"
type="number"
Expand Down Expand Up @@ -299,7 +356,7 @@ export default function InteractionClient() {
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="newMaxSupply" className="text-lg">New Max Supply</Label>
<Label htmlFor="newMaxSupply" className="text-lg font-bold">New Max Supply</Label>
<Input
id="newMaxSupply"
type="number"
Expand All @@ -323,7 +380,7 @@ export default function InteractionClient() {
</div>

<div className="space-y-2">
<Label htmlFor="newThresholdSupply" className="text-lg">New Threshold Supply</Label>
<Label htmlFor="newThresholdSupply" className="text-lg font-bold">New Threshold Supply</Label>
<Input
id="newThresholdSupply"
type="number"
Expand All @@ -349,7 +406,7 @@ export default function InteractionClient() {

<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="newMaxExpansionRate" className="text-lg">New Max Expansion Rate (%)</Label>
<Label htmlFor="newMaxExpansionRate" className="text-lg font-bold">New Max Expansion Rate (%)</Label>
<Input
id="newMaxExpansionRate"
type="number"
Expand All @@ -372,27 +429,36 @@ export default function InteractionClient() {
</Button>
</div>

<div className="space-y-2">
<Label className="text-lg">Transfer Restriction</Label>
<Button
onClick={() => disableTransferRestriction({
abi: CONTRIBUTION_ACCOUNTING_TOKEN_ABI,
address: tokenAddress,
functionName: "disableTransferRestriction"
})}
disabled={isDisablingTransferRestriction}
className="w-full h-12 text-lg"
>
{isDisablingTransferRestriction ? (
"Disabling..."
) : (
<div className="flex items-center gap-2">
<Unlock className="h-5 w-5" />
Disable Transfer Restriction
</div>
)}
</Button>
</div>
{transferRestricted ? (
<div className="space-y-2">
<Label className="text-lg font-bold">Transfer Restriction</Label>
<Button
onClick={() =>
disableTransferRestriction({
abi: CONTRIBUTION_ACCOUNTING_TOKEN_ABI,
address: tokenAddress,
functionName: "disableTransferRestriction",
})
}
disabled={isDisablingTransferRestriction}
className="w-full h-12 text-lg"
>
{isDisablingTransferRestriction ? (
"Disabling..."
) : (
<div className="flex items-center gap-2">
<Unlock className="h-5 w-5" />
Disable Transfer Restriction
</div>
)}
</Button>
</div>
) : (
<div className="space-y-2">
<Label className="text-lg font-bold">Transfer Restriction</Label>
<p className="text-lg">Transfer restriction is already disabled</p>
</div>
)}
</div>
</div>
</CardContent>
Expand Down
55 changes: 38 additions & 17 deletions web/src/app/create/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { useState } from "react";
import { useState, useEffect } from "react";
import Layout from "@/components/Layout";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
Expand All @@ -11,7 +11,7 @@ import { useRouter } from "next/navigation";
import { ClowderVaultFactories } from "@/utils/address";
import { useAccount } from "wagmi";
import { config } from "@/utils/config";
import { writeContract } from "@wagmi/core";
import { useWriteContract, useWaitForTransactionReceipt } from "wagmi";
import { CAT_FACTORY_ABI } from "@/contractsABI/CatFactoryABI";
import {
Card,
Expand All @@ -22,6 +22,8 @@ import {
} from "@/components/ui/card";
import { Info, Loader2 } from "lucide-react";
import { motion } from "framer-motion";
import { showTransactionToast } from "@/components/ui/transaction-toast";

interface DeployContractProps {
tokenName: string;
tokenSymbol: string;
Expand Down Expand Up @@ -67,6 +69,7 @@ const fields = [
description: "Maximum percentage the supply can expand (1-100)",
},
];

export default function CreateCAT() {
const [formData, setFormData] = useState<DeployContractProps>({
tokenName: "",
Expand All @@ -80,6 +83,11 @@ export default function CreateCAT() {
const { address, chainId } = useAccount();
const router = useRouter();

const { writeContract: deployCAT, data: deployData } = useWriteContract();
const { isLoading: isDeployingTx } = useWaitForTransactionReceipt({
hash: deployData,
});

const getTransactionHistory = () => {
const history = localStorage.getItem("transactionHistory");
return history ? JSON.parse(history) : [];
Expand Down Expand Up @@ -112,7 +120,7 @@ export default function CreateCAT() {
const formattedThresholdSupply = BigInt(thresholdSupply) * BigInt(1e18);
const formattedMaxExpansionRate = BigInt(maxExpansionRate) * BigInt(100);

const tx = await writeContract(config, {
deployCAT({
address: ClowderVaultFactories[chainId],
abi: CAT_FACTORY_ABI,
functionName: "createCAT",
Expand All @@ -124,27 +132,40 @@ export default function CreateCAT() {
tokenSymbol,
],
});
} catch (error) {
console.error("Error deploying CAT:", error);
showTransactionToast({
hash: "0x0" as `0x${string}`,
chainId: config.state.chainId,
success: false,
message: "Failed to deploy CAT contract",
});
setIsDeploying(false);
}
Comment on lines +135 to +144
Copy link

@coderabbitai coderabbitai bot May 31, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve error handling to avoid misleading transaction notifications.

The current error handling shows a transaction toast with a dummy hash "0x0" when deployment fails. This is misleading because it suggests a transaction was submitted when it may have failed before reaching the blockchain (e.g., user rejected the transaction, validation errors, etc.).

Consider handling pre-transaction errors differently from transaction failures:

 } catch (error) {
   console.error("Error deploying CAT:", error);
-  showTransactionToast({
-    hash: "0x0" as `0x${string}`,
-    chainId: config.state.chainId,
-    success: false,
-    message: "Failed to deploy CAT contract",
-  });
+  // Only show transaction toast if we have a transaction hash
+  // Otherwise, show a regular error toast
+  toast.error("Failed to deploy CAT contract. Please try again.");
   setIsDeploying(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
} catch (error) {
console.error("Error deploying CAT:", error);
showTransactionToast({
hash: "0x0" as `0x${string}`,
chainId: config.state.chainId,
success: false,
message: "Failed to deploy CAT contract",
});
setIsDeploying(false);
}
} catch (error) {
console.error("Error deploying CAT:", error);
// Only show a transaction toast when we have an actual tx hash.
// For pre-transaction failures, show a general error toast.
toast.error("Failed to deploy CAT contract. Please try again.");
setIsDeploying(false);
}
🤖 Prompt for AI Agents
In web/src/app/create/page.tsx around lines 135 to 144, the error handling shows
a transaction toast with a dummy hash "0x0" on deployment failure, which can
mislead users into thinking a transaction was submitted. To fix this,
differentiate between pre-transaction errors (like user rejection or validation
errors) and actual transaction failures. For pre-transaction errors, avoid
showing a transaction toast or show a distinct notification indicating no
transaction was submitted. Only show the transaction toast with a valid hash
when a transaction is actually sent and fails.

Copy link
Contributor

Choose a reason for hiding this comment

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

This makes sense.

Copy link

Choose a reason for hiding this comment

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

@ceilican Thank you for confirming! The fix will make the error handling much clearer for users by distinguishing between pre-transaction failures and actual transaction errors.

Would you like me to plan the code changes for this improvement, or are you planning to implement it yourself?

};

useEffect(() => {
if (deployData) {
const txDetails = {
tokenName,
tokenSymbol,
maxSupply,
thresholdSupply,
maxExpansionRate,
transactionHash: tx,
tokenName: formData.tokenName,
tokenSymbol: formData.tokenSymbol,
maxSupply: formData.maxSupply,
thresholdSupply: formData.thresholdSupply,
maxExpansionRate: formData.maxExpansionRate,
transactionHash: deployData,
timestamp: new Date().toISOString(),
};

saveTransaction(txDetails);
toast.success("CAT contract deployed successfully!");
showTransactionToast({
hash: deployData,
chainId: config.state.chainId,
message: "CAT contract deployed successfully!",
});
router.push("/my-cats");
} catch (error) {
console.error("Error deploying CAT:", error);
toast.error("Failed to deploy CAT contract");
} finally {
setIsDeploying(false);
}
};
}, [deployData, formData, router]);

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setFormData({
Expand Down Expand Up @@ -251,9 +272,9 @@ export default function CreateCAT() {
<Button
type="submit"
className="w-full h-12 text-lg font-medium transition-all duration-200 hover:scale-[1.02]"
disabled={isDeploying}
disabled={isDeploying || isDeployingTx}
>
{isDeploying ? (
{isDeploying || isDeployingTx ? (
<span className="flex items-center justify-center">
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Deploying...
Expand Down
24 changes: 23 additions & 1 deletion web/src/app/my-cats/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useEffect, useState } from "react";
import Layout from "@/components/Layout";
import Link from "next/link";
import { useAccount } from "wagmi";
import { useAccount, useWriteContract, useWaitForTransactionReceipt } from "wagmi";
import { ClowderVaultFactories } from "@/utils/address";
import { config } from "@/utils/config";
import { getPublicClient } from "@wagmi/core";
Expand All @@ -12,6 +12,7 @@ import detectEthereumProvider from "@metamask/detect-provider";
import { CONTRIBUTION_ACCOUNTING_TOKEN_ABI } from "@/contractsABI/ContributionAccountingTokenABI";
import { motion } from "framer-motion";
import { Loader2, AlertCircle } from "lucide-react";
import { showTransactionToast } from "@/components/ui/transaction-toast";

// Define supported chain IDs
type SupportedChainId = 1 | 137 | 534351 | 5115 | 61 | 2001;
Expand All @@ -37,6 +38,21 @@ export default function MyCATsPage() {
const [error, setError] = useState<string | null>(null);
const { address } = useAccount();

const { writeContract: fetchCATs, data: fetchData } = useWriteContract();
const { isLoading: isFetching } = useWaitForTransactionReceipt({
hash: fetchData,
});

useEffect(() => {
if (fetchData) {
showTransactionToast({
hash: fetchData,
chainId: config.state.chainId,
message: "CATs fetched successfully!",
});
}
}, [fetchData]);

const fetchCATsFromAllChains = async () => {
try {
setIsLoading(true);
Expand All @@ -55,6 +71,12 @@ export default function MyCATsPage() {
setOwnedCATs(allCATs);
} catch (error) {
console.error("Error fetching CATs:", error);
showTransactionToast({
hash: "0x0" as `0x${string}`,
chainId: config.state.chainId,
success: false,
message: "Failed to fetch CATs. Please try again later.",
});
Comment on lines +74 to +79
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid using dummy hash for error notifications.

Using "0x0" as a dummy hash for error cases could be misleading and may cause issues with the explorer URL generation.

Consider modifying the toast component to handle cases without transaction hashes:

      showTransactionToast({
-       hash: "0x0" as `0x${string}`,
+       hash: "0x0000000000000000000000000000000000000000000000000000000000000000" as `0x${string}`,
        chainId: config.state.chainId,
        success: false,
        message: "Failed to fetch CATs. Please try again later.",
      });

Or better yet, create a separate error toast function that doesn't require a transaction hash:

// In transaction-toast.tsx
export const showErrorToast = (message: string) => {
  toast.custom(
    (t) => (
      <div className={/* similar styling but without hash display */}>
        {/* Error content without transaction hash */}
      </div>
    ),
    { duration: 5000, position: "bottom-right" }
  );
};
🤖 Prompt for AI Agents
In web/src/app/my-cats/page.tsx around lines 74 to 79, avoid using the dummy
hash "0x0" for error notifications as it can mislead users and cause issues with
explorer URL generation. Instead, refactor the toast logic by creating a
separate error toast function that does not require a transaction hash, and
update the error handling code to call this new function with just the error
message.

setError("Failed to fetch CATs. Please try again later.");
} finally {
setIsLoading(false);
Expand Down
Loading