From 0cafa0ae39ca2af98d7ca162ca4e14c579fc2c0f Mon Sep 17 00:00:00 2001 From: Zacheus Date: Thu, 24 Jul 2025 22:54:16 +0100 Subject: [PATCH 1/2] feat: implemented useValidatorCheck and useManuallyUpdatePoolStatus hooks --- src/app/hooks/useManuallyUpdatePoolStatus.ts | 89 ++++++++++++++++++++ src/app/hooks/useValidatorCheck.ts | 29 +++++++ src/constants/functionNames.ts | 1 + 3 files changed, 119 insertions(+) create mode 100644 src/app/hooks/useManuallyUpdatePoolStatus.ts create mode 100644 src/app/hooks/useValidatorCheck.ts diff --git a/src/app/hooks/useManuallyUpdatePoolStatus.ts b/src/app/hooks/useManuallyUpdatePoolStatus.ts new file mode 100644 index 0000000..4f3ec4a --- /dev/null +++ b/src/app/hooks/useManuallyUpdatePoolStatus.ts @@ -0,0 +1,89 @@ +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 { myProvider } from "@/lib/utils"; +import toast from "react-hot-toast"; + +export interface PoolStatus { + active?: string; + locked?: string; + settled?: string; + closed?: string; +} + +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: 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" From 64c240a10f234d84f77d5ed3501f8a1c618b95c8 Mon Sep 17 00:00:00 2001 From: Zacheus Date: Wed, 30 Jul 2025 20:53:21 +0100 Subject: [PATCH 2/2] chore: created mock UI to test out manually update pool status --- .../[id]/components/manuallyUpdateModal.tsx | 111 ++++++++++++++++++ .../[id]/components/poolDetails.tsx | 40 +++++-- src/app/hooks/useManuallyUpdatePoolStatus.ts | 11 +- 3 files changed, 147 insertions(+), 15 deletions(-) create mode 100644 src/app/dashboard/pool-market/[id]/components/manuallyUpdateModal.tsx 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 2794ced..23bf170 100644 --- a/src/app/dashboard/pool-market/[id]/components/poolDetails.tsx +++ b/src/app/dashboard/pool-market/[id]/components/poolDetails.tsx @@ -1,9 +1,13 @@ +"use client;" +import useManuallyUpdatePoolState, { PoolStatus } from "@/app/hooks/useManuallyUpdatePoolStatus"; +import useValidatorCheck from "@/app/hooks/useValidatorCheck"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; +import { Button } from "@/components/ui/button"; import { formatTimeDiffFromNow, formatToGMTPlusOne, @@ -11,7 +15,10 @@ import { } from "@/lib/utils"; import { Users, Banknote } from "lucide-react"; import Image from "next/image"; +import { useParams } from "next/navigation"; import { useMemo, useState } from "react"; +import { ManuallyUpdateModal } from "./manuallyUpdateModal"; + interface PoolCardDetailsProps { title: string; creator: string; @@ -30,7 +37,13 @@ export function PoolCardDetails({ category, poolImage, }: PoolCardDetailsProps) { + const { id } = useParams(); + const poolId = Array.isArray(id) ? id[0] : id; + const [isOpen, setIsOpen] = useState(false); + const creatorAddress = creator as `0x${string}`; + const { isLoading, isValidator } = useValidatorCheck(); + return (
@@ -66,14 +79,27 @@ export function PoolCardDetails({
- - 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 index 4f3ec4a..9ccd996 100644 --- a/src/app/hooks/useManuallyUpdatePoolStatus.ts +++ b/src/app/hooks/useManuallyUpdatePoolStatus.ts @@ -3,15 +3,10 @@ import { PREDIFI_ABI } from "../abi/predifi_abi"; import { PREDIFI_CONTRACT_ADDRESS } from "@/static"; import { useState } from "react"; import { cairo, CallData } from "starknet"; -import { myProvider } from "@/lib/utils"; +import { createCairoEnum, myProvider } from "@/lib/utils"; import toast from "react-hot-toast"; -export interface PoolStatus { - active?: string; - locked?: string; - settled?: string; - closed?: string; -} +export type PoolStatus = "Active" | "Locked" | "Settled" | "Closed" | "Suspended"; interface UseManuallyUpdatePoolStateOptions { onSuccess?: (data: any) => void; @@ -50,7 +45,7 @@ const useManuallyUpdatePoolState = ({ const poolIdU256 = cairo.uint256(poolId.toString()); const callData = CallData.compile({ pool_id: poolIdU256, - new_status: newStatus, + new_status: createCairoEnum(newStatus), }); const result = await account?.execute({