Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
62 changes: 44 additions & 18 deletions src/Create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useForm, SubmitHandler } from "react-hook-form";
import factoryabi from "./abi/factoryabi.json";
import abi from "./abi/abi.json";
//import { sepolia } from "viem/chains";
import { parseEther } from "viem";
import { parseEther, isAddress } from "viem";
import { citreaTestnet } from "./CitreaTestnet";
type Inputs = {
fundingType: "ETH" | "ERC20";
Expand All @@ -20,7 +20,19 @@ type Inputs = {
ptaAmount: string;
rate: string;
developerPercentage: string;
fundingToken?: `0x${string}`; // Add fundingToken for ERC20 vaults
fundingToken?: `0x${string}`;
fundingTokenDecimals?: string;
};

// Custom validator for funding token address
const validateFundingToken = (fundingType: string) => (value?: `0x${string}`) => {
if (fundingType !== "ERC20") return true; // Only validate for ERC20 mode
if (!value) return "Funding token address is required for ERC20 mode";
if (!isAddress(value)) return "Invalid Ethereum address format";
if (value === "0x0000000000000000000000000000000000000000") {
return "Cannot use zero address for funding token";
}
return true;
};

const Create = () => {
Expand Down Expand Up @@ -79,6 +91,8 @@ const Create = () => {
}

return (


<div className="mx-auto max-w-7xl p-5">
<div className="py-3 flex flex-col gap-2">
<h1 className="text-2xl text-white">Create new Funding Vault</h1>
Expand All @@ -104,17 +118,21 @@ const Create = () => {
</label>
</div>
</div>
{watch("fundingType")==="ERC20" &&(
{watch("fundingType") === "ERC20" && (
<div className="pt-4">
<label className={`text-sm text-white`}>ERC20 Funding Token Address</label>
<input
id="fundingToken"
placeholder="Enter ERC20 token address"
className="bg-transparent p-2 text-sm w-full outline-none border border-slate-600 rounded-md text-white"
{...register("fundingToken", { required: watch("fundingType") === "ERC20" })}
/>
</div>

<label className={`text-sm text-white`}>ERC20 Funding Token Address</label>
<input
id="fundingToken"
placeholder="Enter ERC20 token contract address (e.g., 0x123...abc)"
className="bg-transparent p-2 text-sm w-full outline-none border border-slate-600 rounded-md text-white"
{...register("fundingToken", {
validate: validateFundingToken(watch("fundingType")),
})}
/>
{errors.fundingToken && (
<p className="text-red-500 text-sm mt-1">{errors.fundingToken.message}</p>
)}
</div>
)}

<form onSubmit={handleSubmit(onSubmit)} className="text-white">
Expand Down Expand Up @@ -244,10 +262,12 @@ const Create = () => {
validation={{}}
/> */}
<div>
<label className={`text-sm text-white`}>Minimum ETH Target</label>
<label className={`text-sm text-white`}>
Minimum {watch("fundingType") === "ERC20" ? "Token" : "ETH"} Target
</label>
<input
id="minEth"
placeholder="Specify the minimum amount of ETH required (e.g., 10 ETH)"
placeholder={`Specify the minimum amount of ${watch("fundingType") === "ERC20" ? "tokens" : "ETH"} required (e.g., 10)`}
className="bg-transparent p-2 text-sm w-full outline-none border border-slate-600 rounded-md"
{...register("minEth", { required: true })}
/>
Expand Down Expand Up @@ -306,11 +326,13 @@ const Create = () => {
/> */}

<div>
<label className={`text-sm text-white`}>Exchange Rate</label>
<label className={`text-sm text-white`}>
Exchange Rate
</label>
<input
id="rate"
type="number"
placeholder="Specify the exchange rate (e.g., 1 token = 0.01 ETH)"
placeholder={watch("fundingType") === "ERC20" ? "e.g., 100 (100 proof tokens per 1 funding token)" : "e.g., 0.01 (1 proof token = 0.01 ETH)"}
className="bg-transparent p-2 text-sm w-full outline-none border border-slate-600 rounded-md"
{...register("rate", { required: true })}
/>
Expand All @@ -334,7 +356,10 @@ const Create = () => {
type="date"
placeholder="Select the project deadline in mm/dd/yyyy format (e.g., 12/31/2024)"
className="bg-transparent text-white p-2 text-sm w-full outline-none border border-slate-600 rounded-md"
{...register("deadline", { required: true })}
{...register("deadline", {
required: "Deadline is required",
valueAsDate: true
})}
/>
</div>
</div>
Expand All @@ -352,6 +377,7 @@ const Create = () => {
</button>
</form>
</div>

);
};
}
export default Create;
32 changes: 30 additions & 2 deletions src/Details.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import vaultabi from "./abi/vaultabi.json";
import abi from "./abi/abi.json";
import erc20abi from "./abi/erc20abi.json";
import { useReadContract } from "wagmi";
//import { sepolia } from "viem/chains";
import { useParams } from "react-router-dom";
Expand Down Expand Up @@ -35,6 +36,28 @@ const Details = () => {
vaultDetails = response?.data as VaultDetailsType;
}

// Fetch the vault's funding token address
const { data: fundingToken } = useReadContract({
abi: vaultabi,
address: address as `0x${string}`,
functionName: "fundingToken",
chainId: citreaTestnet.id,
query: {
enabled: !!address,
},
}) as { data: `0x${string}` | undefined };

// Fetch ERC20 token symbol if a funding token is set
const { data: fundingTokenSymbol } = useReadContract({
abi: erc20abi,
address: fundingToken,
functionName: "symbol",
chainId: citreaTestnet.id,
query: {
enabled: !!fundingToken && fundingToken !== "0x0000000000000000000000000000000000000000",
},
}) as { data: string | undefined };

const result = useReadContract({
abi: abi,
address: address,
Expand All @@ -57,6 +80,11 @@ const Details = () => {
},
});
const symbol = _symbol?.data as string;

// Determine which currency symbol to display for funding
const isNativeCurrency = !fundingToken || fundingToken === "0x0000000000000000000000000000000000000000";
const fundingCurrencySymbol = isNativeCurrency ? balanceOfVault?.data?.symbol : fundingTokenSymbol;

console.log(VaultCAT);
//console.log(balanceOfVault?.data?.value, vaultDetails?.minFundingAmount);
return (
Expand Down Expand Up @@ -195,9 +223,9 @@ const Details = () => {
<h1 className="text-slate-400">Funds Collected</h1>
<p>
{formatEther(balanceOfVault?.data?.value as bigint)}{" "}
{balanceOfVault?.data?.symbol} Funds raised of{" "}
{fundingCurrencySymbol || balanceOfVault?.data?.symbol} Funds raised of{" "}
{formatEther(BigInt(vaultDetails.minFundingAmount))}{" "}
{balanceOfVault?.data?.symbol}
{fundingCurrencySymbol || balanceOfVault?.data?.symbol}
</p>
<div
className=" flex w-full h-2 rounded-full overflow-hidden bg-slate-950"
Expand Down
111 changes: 90 additions & 21 deletions src/VaultActions.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useState } from "react";
import { SubmitHandler, useForm } from "react-hook-form";
import { parseEther } from "viem";
import { useWriteContract } from "wagmi";
import { useWriteContract, useReadContract } from "wagmi";
import vaultabi from "./abi/vaultabi.json";
import erc20abi from "./abi/erc20abi.json";
import { useAccount } from "wagmi";
//import { sepolia } from "viem/chains";
import { useParams } from "react-router-dom";
Expand All @@ -23,6 +24,43 @@ const VaultActions: React.FC<{ withdrawalAddress?: string }> = ({
const account = useAccount();
const nativecurrency = account.chain?.nativeCurrency.name;

// Fetch the vault's funding token address
const { data: fundingToken } = useReadContract({
abi: vaultabi,
address: address as `0x${string}`,
functionName: "fundingToken",
chainId: citreaTestnet.id,
query: {
enabled: !!address,
},
}) as { data: `0x${string}` | undefined };

// Fetch ERC20 token symbol if a funding token is set
const { data: tokenSymbol } = useReadContract({
abi: erc20abi,
address: fundingToken,
functionName: "symbol",
chainId: citreaTestnet.id,
query: {
enabled: !!fundingToken && fundingToken !== "0x0000000000000000000000000000000000000000",
},
}) as { data: string | undefined };

// Fetch ERC20 token decimals if a funding token is set
const { data: tokenDecimals } = useReadContract({
abi: erc20abi,
address: fundingToken,
functionName: "decimals",
chainId: citreaTestnet.id,
query: {
enabled: !!fundingToken && fundingToken !== "0x0000000000000000000000000000000000000000",
},
}) as { data: number | undefined };

// Determine if using native currency or ERC20 token
const isNativeCurrency = !fundingToken || fundingToken === "0x0000000000000000000000000000000000000000";
const currencySymbol = isNativeCurrency ? nativecurrency : tokenSymbol;

const tabs = [
"Fund Project",
"Refund",
Expand All @@ -46,16 +84,47 @@ const VaultActions: React.FC<{ withdrawalAddress?: string }> = ({
} = useForm<Inputs>();
const onSubmitForm1: SubmitHandler<Inputs> = async (data) => {
try {
const tx1 = await writeContractAsync({
abi: vaultabi,
address: address as `0x${string}`,
functionName: "purchaseTokens",
value: parseEther(data.ethAmount),
chainId: citreaTestnet.id,
});
// Wait for approximately 6 seconds for 3 block confirmations
await new Promise((resolve) => setTimeout(resolve, 6000));
console.log("1st Transaction submitted:", tx1);
if (isNativeCurrency) {
// For native currency, use msg.value
const tx1 = await writeContractAsync({
abi: vaultabi,
address: address as `0x${string}`,
functionName: "purchaseTokens",
value: parseEther(data.ethAmount),
chainId: citreaTestnet.id,
});
// Wait for approximately 6 seconds for 3 block confirmations
await new Promise((resolve) => setTimeout(resolve, 6000));
console.log("1st Transaction submitted:", tx1);
} else {
// For ERC20 token, need to approve first
const tokenAmount = parseEther(data.ethAmount);

// Step 1: Approve the vault contract to spend tokens
const approveTx = await writeContractAsync({
abi: erc20abi,
address: fundingToken,
functionName: "approve",
args: [address as `0x${string}`, tokenAmount],
chainId: citreaTestnet.id,
});
console.log("Approval transaction submitted:", approveTx);

// Wait for approval to be confirmed
await new Promise((resolve) => setTimeout(resolve, 6000));

// Step 2: Call purchaseTokens with transferFrom
const tx1 = await writeContractAsync({
abi: vaultabi,
address: address as `0x${string}`,
functionName: "purchaseTokens",
args: [tokenAmount],
chainId: citreaTestnet.id,
});
// Wait for approximately 6 seconds for 3 block confirmations
await new Promise((resolve) => setTimeout(resolve, 6000));
console.log("Purchase transaction submitted:", tx1);
}
} catch (error) {
console.error("Transaction failed:", error);
}
Expand Down Expand Up @@ -181,21 +250,21 @@ const VaultActions: React.FC<{ withdrawalAddress?: string }> = ({
step="any"
{...register("ethAmount", { required: true })}
placeholder={
nativecurrency
? `Enter Amount to donate in ${nativecurrency}`
currencySymbol
? `Enter Amount to donate in ${currencySymbol}`
: "Connect Wallet to proceed"
}
disabled={!nativecurrency}
disabled={!currencySymbol}
/>
<button
disabled={!nativecurrency}
disabled={!currencySymbol}
className="flex h-[34px] min-w-60 overflow-hidden items-center font-medium focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-slate-950 text-white shadow hover:bg-black/90 px-4 py-2 max-w-52 whitespace-pre md:flex group relative w-full justify-center gap-2 rounded-md transition-all duration-300 ease-out border-2 border-purple-600/70 hover:border-purple-600 mt-3"
>
<span className="absolute right-0 h-32 w-8 translate-x-12 rotate-12 bg-white opacity-20 transition-all duration-1000 ease-out group-hover:-translate-x-40"></span>

<span className="text-white">
{nativecurrency
? `${isSubmitting ? "Processing..." : `Send ${nativecurrency}`}`
{currencySymbol
? `${isSubmitting ? "Processing..." : `Send ${currencySymbol}`}`
: "Connect Wallet"}
</span>
</button>
Expand All @@ -210,13 +279,13 @@ const VaultActions: React.FC<{ withdrawalAddress?: string }> = ({
</p>
<button
onClick={handleRefund}
disabled={!nativecurrency}
disabled={!account.address}
className="flex h-[34px] min-w-60 overflow-hidden items-center font-medium focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-slate-950 text-white shadow hover:bg-black/90 px-4 py-2 max-w-52 whitespace-pre md:flex group relative w-full justify-center gap-2 rounded-md transition-all duration-300 ease-out border-2 border-purple-600/70 hover:border-purple-600 mt-3"
>
<span className="absolute right-0 h-32 w-8 translate-x-12 rotate-12 bg-white opacity-20 transition-all duration-1000 ease-out group-hover:-translate-x-40"></span>

<span className="text-white">
{nativecurrency
{account.address
? `${isSubmitting ? "Processing..." : `Refund`}`
: "Connect Wallet"}
</span>
Expand All @@ -230,13 +299,13 @@ const VaultActions: React.FC<{ withdrawalAddress?: string }> = ({
<p className="">Redeem your vouchers for CAT</p>
<button
onClick={handleRedeem}
disabled={!nativecurrency}
disabled={!account.address}
className="flex h-[34px] min-w-60 overflow-hidden items-center font-medium focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-slate-950 text-white shadow hover:bg-black/90 px-4 py-2 max-w-52 whitespace-pre md:flex group relative w-full justify-center gap-2 rounded-md transition-all duration-300 ease-out border-2 border-purple-600/70 hover:border-purple-600 mt-3"
>
<span className="absolute right-0 h-32 w-8 translate-x-12 rotate-12 bg-white opacity-20 transition-all duration-1000 ease-out group-hover:-translate-x-40"></span>

<span className="text-white">
{nativecurrency
{account.address
? `${isSubmitting ? "Processing..." : `Redeem`}`
: "Connect Wallet"}
</span>
Expand Down
Loading