diff --git a/src/app/dashboard/pool-market/[id]/components/manuallyUpdateModal.tsx b/src/app/dashboard/pool-market/[id]/components/manuallyUpdateModal.tsx new file mode 100644 index 0000000..24ca1bc --- /dev/null +++ b/src/app/dashboard/pool-market/[id]/components/manuallyUpdateModal.tsx @@ -0,0 +1,111 @@ +import useManuallyUpdatePoolState, { + PoolStatus, +} from "@/app/hooks/useManuallyUpdatePoolStatus"; +import { useEffect, useState } from "react"; + +interface ModalProps { + isOpen: boolean; + onClose: () => void; + currentStatus: PoolStatus; + id: string; +} + +export const ManuallyUpdateModal = ({ + isOpen, + onClose, + currentStatus, + id, +}: ModalProps) => { + const [selectedStatus, setSelectedStatus] = useState( + currentStatus || "Active" + ); + const statusOptions = [ + { value: "Active", label: "Active" }, + { + value: "Locked", + label: "Locked", + }, + { value: "Settled", label: "Settled" }, + { value: "Closed", label: "Closed" }, + { + value: "Suspended", + label: "Suspended", + }, + ]; + + const { updatePoolState, isLoading: isUpdating } = + useManuallyUpdatePoolState(); + + useEffect(() => { + if (!isUpdating) { + setSelectedStatus(currentStatus); + onClose(); + } + }, [isUpdating]); + + if (!isOpen) return null; + + return ( +
+
+
+

+ Manually Update Pool Status +

+
+

+ Select the new status for pool {id} +

+
+
+ + +
+ +
+ + +
+
+
+
+ ); +}; diff --git a/src/app/dashboard/pool-market/[id]/components/poolDetails.tsx b/src/app/dashboard/pool-market/[id]/components/poolDetails.tsx index 54e4ba6..82dd68a 100644 --- a/src/app/dashboard/pool-market/[id]/components/poolDetails.tsx +++ b/src/app/dashboard/pool-market/[id]/components/poolDetails.tsx @@ -1,3 +1,6 @@ +"use client;" +import useManuallyUpdatePoolState, { PoolStatus } from "@/app/hooks/useManuallyUpdatePoolStatus"; +import useValidatorCheck from "@/app/hooks/useValidatorCheck"; import { PREDIFI_ABI } from "@/app/abi/predifi_abi"; import { PREDIFI_CONTRACT_ADDRESS } from "@/static"; import { @@ -6,6 +9,7 @@ import { AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; +import { Button } from "@/components/ui/button"; import { formatTimeDiffFromNow, formatToGMTPlusOne, @@ -14,10 +18,10 @@ import { import { Users, Banknote } from "lucide-react"; import Image from "next/image"; import { useParams } from "next/navigation"; +import { ManuallyUpdateModal } from "./manuallyUpdateModal"; import { useContract, useAccount, } from "@starknet-react/core"; import { useCallback, useMemo, useState } from "react"; import toast from "react-hot-toast"; -import { Button } from "@/components/ui/button"; interface PoolCardDetailsProps { title: string; @@ -37,7 +41,11 @@ export function PoolCardDetails({ category, poolImage, }: PoolCardDetailsProps) { + const [isOpen, setIsOpen] = useState(false); + const creatorAddress = creator as `0x${string}`; + const { isLoading, isValidator } = useValidatorCheck(); + const { id: poolId } = useParams() as { id: string }; const poolIdUint256 = useMemo(() => { @@ -126,15 +134,28 @@ const { id: poolId } = useParams() as { id: string }; -
- - Begins in{" "} - - {formatTimeDiffFromNow(Number(startTime))}{" "} - - -

{formatToGMTPlusOne(Number(startTime))}

+
+
+ + Begins in{" "} + + {formatTimeDiffFromNow(Number(startTime))}{" "} + + +

{formatToGMTPlusOne(Number(startTime))}

+
+ {!isLoading && isValidator && ( + + )}
+ setIsOpen(false)} + currentStatus={status as PoolStatus} + />
); } diff --git a/src/app/hooks/useManuallyUpdatePoolStatus.ts b/src/app/hooks/useManuallyUpdatePoolStatus.ts new file mode 100644 index 0000000..9ccd996 --- /dev/null +++ b/src/app/hooks/useManuallyUpdatePoolStatus.ts @@ -0,0 +1,84 @@ +import { useContract, useAccount } from "@starknet-react/core"; +import { PREDIFI_ABI } from "../abi/predifi_abi"; +import { PREDIFI_CONTRACT_ADDRESS } from "@/static"; +import { useState } from "react"; +import { cairo, CallData } from "starknet"; +import { createCairoEnum, myProvider } from "@/lib/utils"; +import toast from "react-hot-toast"; + +export type PoolStatus = "Active" | "Locked" | "Settled" | "Closed" | "Suspended"; + +interface UseManuallyUpdatePoolStateOptions { + onSuccess?: (data: any) => void; + onError?: (error: any) => void; +} + +const useManuallyUpdatePoolState = ({ + onSuccess, + onError, +}: UseManuallyUpdatePoolStateOptions = {}) => { + const { account } = useAccount(); + const { contract } = useContract({ + abi: PREDIFI_ABI, + address: PREDIFI_CONTRACT_ADDRESS, + }); + + const [isLoading, setIsLoading] = useState(false); + + const updatePoolState = async ( + poolId: string | number, + newStatus: PoolStatus + ) => { + if (!account) { + const errorMsg = "Wallet not connected"; + onError?.(errorMsg); + } + + if (!contract) { + const errorMsg = "Contract not available"; + onError?.(errorMsg); + } + + setIsLoading(true); + + try { + const poolIdU256 = cairo.uint256(poolId.toString()); + const callData = CallData.compile({ + pool_id: poolIdU256, + new_status: createCairoEnum(newStatus), + }); + + const result = await account?.execute({ + contractAddress: PREDIFI_CONTRACT_ADDRESS, + entrypoint: "manually_update_pool_state", + calldata: callData, + }); + + // await account.waitForTransaction(result.transaction_hash); + const status = await myProvider.waitForTransaction( + result?.transaction_hash as string + ); + + onSuccess?.(result); + setIsLoading(false); + + if (status.isSuccess()) { + toast.success("Success! 🎉 Pool status has been updated."); + setIsLoading(false); + } + } catch (err: any) { + const errorMessage = + err?.message || "Failed to update pool state. Try again."; + toast.error(errorMessage); + setIsLoading(false); + onError?.(err); + } + }; + + return { + updatePoolState, + isLoading, + }; +}; + +export default useManuallyUpdatePoolState; diff --git a/src/app/hooks/useValidatorCheck.ts b/src/app/hooks/useValidatorCheck.ts new file mode 100644 index 0000000..6fa2c89 --- /dev/null +++ b/src/app/hooks/useValidatorCheck.ts @@ -0,0 +1,29 @@ +import { useReadContract, useAccount } from "@starknet-react/core"; +import { PREDIFI_ABI } from "../abi/predifi_abi"; +import { PREDIFI_CONTRACT_ADDRESS } from "@/static"; + +interface UseAdminCheckOptions { + enabled?: boolean; +} + +const useValidatorCheck = ({ enabled = true }: UseAdminCheckOptions = {}) => { + const { address } = useAccount(); + + const { data: isValidator, isLoading: isValidatorLoading } = useReadContract({ + abi: PREDIFI_ABI, + functionName: "is_validator", + address: PREDIFI_CONTRACT_ADDRESS, + args: [address], + enabled: enabled && !!address, + }); + + const isLoading = isValidatorLoading; + + return { + isValidator: Boolean(isValidator), + isLoading, + address, + }; +}; + +export default useValidatorCheck; diff --git a/src/constants/functionNames.ts b/src/constants/functionNames.ts index 081c6d3..c995e7b 100644 --- a/src/constants/functionNames.ts +++ b/src/constants/functionNames.ts @@ -3,4 +3,5 @@ export const GET_SETTLED_POOLS = "get_settled_pools" export const GET_LOCKED_POOLS = "get_locked_pools"; export const GET_ACTIVE_POOLS = "get_active_pools"; export const GET_CLOSED_POOLS = "get_closed_pools" +export const MANUALLY_UPDATE_POOL_STATE = "manually_update_pool_state"