diff --git a/components/profile/unclaimed-hypercert-batchClaim-button.tsx b/components/profile/unclaimed-hypercert-batchClaim-button.tsx index 8c7a9b39..c61d72c1 100644 --- a/components/profile/unclaimed-hypercert-batchClaim-button.tsx +++ b/components/profile/unclaimed-hypercert-batchClaim-button.tsx @@ -1,18 +1,14 @@ "use client"; import { AllowListRecord } from "@/allowlists/actions/getAllowListRecordsForAddressByClaimed"; -import { revalidatePathServerAction } from "@/app/actions/revalidatePathServerAction"; -import { useHypercertClient } from "@/hooks/use-hypercert-client"; -import { ChainFactory } from "@/lib/chainFactory"; -import { errorToast } from "@/lib/errorToast"; -import { useRouter } from "next/navigation"; -import { useState } from "react"; -import { ByteArray, getAddress, Hex } from "viem"; -import { waitForTransactionReceipt } from "viem/actions"; -import { useAccount, useSwitchChain, useWalletClient } from "wagmi"; -import { createExtraContent } from "../global/extra-content"; -import { useStepProcessDialogContext } from "../global/step-process-dialog"; import { Button } from "../ui/button"; +import { useAccount, useSwitchChain } from "wagmi"; +import { useState } from "react"; +import { getAddress, Hex, ByteArray } from "viem"; +import { errorToast } from "@/lib/errorToast"; +import { ChainFactory } from "@/lib/chainFactory"; +import { useClaimHypercertStrategy } from "@/hypercerts/hooks/useClaimHypercertStrategy"; +import { useAccountStore } from "@/lib/account-store"; interface TransformedClaimData { hypercertTokenIds: bigint[]; @@ -39,104 +35,39 @@ export default function UnclaimedHypercertBatchClaimButton({ allowListRecords: AllowListRecord[]; selectedChainId: number | null; }) { - const router = useRouter(); - const { client } = useHypercertClient(); - const { data: walletClient } = useWalletClient(); const account = useAccount(); const [isLoading, setIsLoading] = useState(false); - const { setDialogStep, setSteps, setOpen, setTitle, setExtraContent } = - useStepProcessDialogContext(); const { switchChain } = useSwitchChain(); + const getStrategy = useClaimHypercertStrategy(); + const { selectedAccount } = useAccountStore(); + const selectedChain = selectedChainId ? ChainFactory.getChain(selectedChainId) : null; - const refreshData = async (address: string) => { - const hypercertIds = allowListRecords.map((record) => record.hypercert_id); - - const hypercertViewInvalidationPaths = hypercertIds.map((id) => { - return `/hypercerts/${id}`; - }); - - await revalidatePathServerAction([ - `/profile/${address}`, - `/profile/${address}?tab`, - `/profile/${address}?tab=hypercerts-claimable`, - `/profile/${address}?tab=hypercerts-owned`, - ...hypercertViewInvalidationPaths, - ]).then(async () => { - setTimeout(() => { - // refresh after 5 seconds - router.refresh(); - - // push to the profile page with the hypercerts-claimable tab - // because revalidatePath will revalidate on the next page visit. - router.push(`/profile/${address}?tab=hypercerts-claimable`); - }, 5000); - }); - }; - const claimHypercert = async () => { setIsLoading(true); - setOpen(true); - setSteps([ - { id: "preparing", description: "Preparing to claim fractions..." }, - { id: "claiming", description: "Claiming fractions on-chain..." }, - { id: "confirming", description: "Waiting for on-chain confirmation" }, - { id: "done", description: "Claiming complete!" }, - ]); - setTitle("Claim fractions from Allowlist"); - if (!client) { - throw new Error("No client found"); - } - if (!walletClient) { - throw new Error("No wallet client found"); - } - if (!account) { - throw new Error("No address found"); - } - - const claimData = transformAllowListRecords(allowListRecords); - await setDialogStep("preparing, active"); try { - await setDialogStep("claiming", "active"); - const tx = await client.batchClaimFractionsFromAllowlists(claimData); - - if (!tx) { - await setDialogStep("claiming", "error"); - throw new Error("Failed to claim fractions"); - } - - await setDialogStep("confirming", "active"); - const receipt = await waitForTransactionReceipt(walletClient, { - hash: tx, - }); - - if (receipt.status == "success") { - await setDialogStep("done", "completed"); - const extraContent = createExtraContent({ - receipt, - chain: account?.chain!, - }); - setExtraContent(extraContent); - refreshData(getAddress(account.address!)); - } else if (receipt.status == "reverted") { - await setDialogStep("confirming", "error", "Transaction reverted"); - } + const claimData = transformAllowListRecords(allowListRecords); + const params = claimData.hypercertTokenIds.map((tokenId, index) => ({ + tokenId, + units: claimData.units[index], + proof: claimData.proofs[index] as `0x${string}`[], + })); + await getStrategy(params).execute(params); } catch (error) { - console.error("Claim error:", error); - await setDialogStep("claiming", "error", "Transaction failed"); + console.error(error); } finally { setIsLoading(false); } }; + const activeAddress = selectedAccount?.address || account.address; const isBatchClaimDisabled = isLoading || !allowListRecords.length || - !account || - !client || - account.address !== getAddress(allowListRecords[0].user_address as string); + !activeAddress || + activeAddress !== getAddress(allowListRecords[0].user_address as string); return ( <> diff --git a/components/profile/unclaimed-hypercert-claim-button.tsx b/components/profile/unclaimed-hypercert-claim-button.tsx index d0df0254..b6bec4aa 100644 --- a/components/profile/unclaimed-hypercert-claim-button.tsx +++ b/components/profile/unclaimed-hypercert-claim-button.tsx @@ -2,16 +2,11 @@ import { AllowListRecord } from "@/allowlists/actions/getAllowListRecordsForAddressByClaimed"; import { Button } from "../ui/button"; -import { useHypercertClient } from "@/hooks/use-hypercert-client"; -import { waitForTransactionReceipt } from "viem/actions"; -import { useAccount, useSwitchChain, useWalletClient } from "wagmi"; -import { useRouter } from "next/navigation"; +import { useAccount, useSwitchChain } from "wagmi"; import { Row } from "@tanstack/react-table"; -import { useStepProcessDialogContext } from "../global/step-process-dialog"; -import { createExtraContent } from "../global/extra-content"; -import { revalidatePathServerAction } from "@/app/actions/revalidatePathServerAction"; import { useState } from "react"; -import { getAddress } from "viem"; +import { useAccountStore } from "@/lib/account-store"; +import { useClaimHypercert } from "@/hypercerts/hooks/useClaimHypercert"; interface UnclaimedHypercertClaimButtonProps { allowListRecord: Row; @@ -20,102 +15,33 @@ interface UnclaimedHypercertClaimButtonProps { export default function UnclaimedHypercertClaimButton({ allowListRecord, }: UnclaimedHypercertClaimButtonProps) { - const { client } = useHypercertClient(); - const { data: walletClient } = useWalletClient(); - const account = useAccount(); - const { refresh } = useRouter(); + const { address, chain: currentChain } = useAccount(); + const { selectedAccount } = useAccountStore(); const [isLoading, setIsLoading] = useState(false); - const { setDialogStep, setSteps, setOpen, setTitle, setExtraContent } = - useStepProcessDialogContext(); const { switchChain } = useSwitchChain(); - const router = useRouter(); - const selectedHypercert = allowListRecord.original; const hypercertChainId = selectedHypercert?.hypercert_id?.split("-")[0]; + const activeAddress = selectedAccount?.address || (address as `0x${string}`); + const { mutateAsync: claimHypercert } = useClaimHypercert(); - const refreshData = async (address: string) => { - await revalidatePathServerAction([ - `/profile/${address}`, - `/profile/${address}?tab`, - `/profile/${address}?tab=hypercerts-claimable`, - `/profile/${address}?tab=hypercerts-owned`, - `/hypercerts/${selectedHypercert?.hypercert_id}`, - ]).then(() => { - setTimeout(() => { - // refresh after 5 seconds - router.refresh(); - // push to the profile page with the hypercerts-claimable tab - // because revalidatePath will revalidate on the next page visit. - router.push(`/profile/${address}?tab=hypercerts-claimable`); - }, 5000); - }); - }; - - const claimHypercert = async () => { + const handleClaim = async () => { setIsLoading(true); - setOpen(true); - setSteps([ - { id: "preparing", description: "Preparing to claim fraction..." }, - { id: "claiming", description: "Claiming fraction on-chain..." }, - { id: "confirming", description: "Waiting for on-chain confirmation" }, - { id: "route", description: "Creating your new fraction's link..." }, - { id: "done", description: "Claiming complete!" }, - ]); - - setTitle("Claim fraction from Allowlist"); - if (!client) { - throw new Error("No client found"); - } - - if (!walletClient) { - throw new Error("No wallet client found"); - } - - if (!account) { - throw new Error("No address found"); - } - - if ( - !selectedHypercert?.units || - !selectedHypercert?.proof || - !selectedHypercert?.token_id - ) { - throw new Error("Invalid allow list record"); - } - await setDialogStep("preparing, active"); - try { - await setDialogStep("claiming", "active"); - const tx = await client.mintClaimFractionFromAllowlist( - BigInt(selectedHypercert?.token_id), - BigInt(selectedHypercert?.units), - selectedHypercert?.proof as `0x${string}`[], - undefined, - ); - - if (!tx) { - await setDialogStep("claiming", "error"); - throw new Error("Failed to claim fraction"); + if ( + !selectedHypercert.token_id || + !selectedHypercert.units || + !selectedHypercert.proof + ) { + throw new Error("Invalid allow list record"); } - await setDialogStep("confirming", "active"); - const receipt = await waitForTransactionReceipt(walletClient, { - hash: tx, - }); - - if (receipt.status == "success") { - await setDialogStep("route", "active"); - const extraContent = createExtraContent({ - receipt: receipt, - hypercertId: selectedHypercert?.hypercert_id!, - chain: account.chain!, - }); - setExtraContent(extraContent); - await setDialogStep("done", "completed"); - await refreshData(getAddress(account.address!)); - } else if (receipt.status == "reverted") { - await setDialogStep("confirming", "error", "Transaction reverted"); - } + await claimHypercert([ + { + tokenId: BigInt(selectedHypercert.token_id), + units: BigInt(selectedHypercert.units), + proof: selectedHypercert.proof as `0x${string}`[], + }, + ]); } catch (error) { console.error(error); } finally { @@ -126,25 +52,25 @@ export default function UnclaimedHypercertClaimButton({ return ( ); } diff --git a/components/profile/unclaimed-table/unclaimed-fraction-table.tsx b/components/profile/unclaimed-table/unclaimed-fraction-table.tsx index f010117f..ac264617 100644 --- a/components/profile/unclaimed-table/unclaimed-fraction-table.tsx +++ b/components/profile/unclaimed-table/unclaimed-fraction-table.tsx @@ -29,6 +29,8 @@ import UnclaimedHypercertBatchClaimButton from "../unclaimed-hypercert-batchClai import { TableToolbar } from "./table-toolbar"; import { useMediaQuery } from "@/hooks/use-media-query"; import { UnclaimedFraction } from "../unclaimed-hypercerts-list"; +import { useAccountStore } from "@/lib/account-store"; +import { useRouter } from "next/navigation"; export interface DataTableProps { columns: ColumnDef[]; @@ -36,6 +38,8 @@ export interface DataTableProps { } export function UnclaimedFractionTable({ columns, data }: DataTableProps) { + const { selectedAccount } = useAccountStore(); + const router = useRouter(); const [sorting, setSorting] = useState([]); const [columnFilters, setColumnFilters] = useState([]); const [columnVisibility, setColumnVisibility] = useState({}); @@ -139,6 +143,11 @@ export function UnclaimedFractionTable({ columns, data }: DataTableProps) { setSelectedRecords(getSelectedRecords()); }, [rowSelection, getSelectedRecords]); + // Refresh the entire route when account changes + useEffect(() => { + router.refresh(); + }, [selectedAccount?.address, router]); + return (
diff --git a/hypercerts/ClaimHypercertStrategy.ts b/hypercerts/ClaimHypercertStrategy.ts new file mode 100644 index 00000000..be7cd25e --- /dev/null +++ b/hypercerts/ClaimHypercertStrategy.ts @@ -0,0 +1,25 @@ +import { Address, Chain } from "viem"; +import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime"; +import { HypercertClient } from "@hypercerts-org/sdk"; +import { UseWalletClientReturnType } from "wagmi"; + +import { useStepProcessDialogContext } from "@/components/global/step-process-dialog"; + +export interface ClaimHypercertParams { + tokenId: bigint; + units: bigint; + proof: `0x${string}`[]; +} + +export abstract class ClaimHypercertStrategy { + constructor( + protected address: Address, + protected chain: Chain, + protected client: HypercertClient, + protected dialogContext: ReturnType, + protected walletClient: UseWalletClientReturnType, + protected router: AppRouterInstance, + ) {} + + abstract execute(params: ClaimHypercertParams[]): Promise; +} diff --git a/hypercerts/EOABatchClaimHypercertStrategy.ts b/hypercerts/EOABatchClaimHypercertStrategy.ts new file mode 100644 index 00000000..7449b463 --- /dev/null +++ b/hypercerts/EOABatchClaimHypercertStrategy.ts @@ -0,0 +1,89 @@ +import { waitForTransactionReceipt } from "viem/actions"; + +import { createExtraContent } from "@/components/global/extra-content"; +import { revalidatePathServerAction } from "@/app/actions/revalidatePathServerAction"; + +import { + ClaimHypercertStrategy, + ClaimHypercertParams, +} from "./ClaimHypercertStrategy"; + +export class EOABatchClaimHypercertStrategy extends ClaimHypercertStrategy { + async execute(params: ClaimHypercertParams[]): Promise { + const { setDialogStep, setSteps, setOpen, setTitle, setExtraContent } = + this.dialogContext; + const { data: walletClient } = this.walletClient; + + if (!this.client) throw new Error("No client found"); + if (!walletClient) throw new Error("No wallet client found"); + + setOpen(true); + setSteps([ + { id: "preparing", description: "Preparing to claim fractions..." }, + { id: "claiming", description: "Claiming fractions on-chain..." }, + { id: "confirming", description: "Waiting for on-chain confirmation" }, + { id: "done", description: "Claiming complete!" }, + ]); + setTitle("Claim fractions from Allowlist"); + + try { + await setDialogStep("preparing", "active"); + await setDialogStep("claiming", "active"); + + const tx = await this.client.batchClaimFractionsFromAllowlists( + mapClaimParams(params), + ); + if (!tx) { + await setDialogStep("claiming", "error"); + throw new Error("Failed to claim fractions"); + } + + await setDialogStep("confirming", "active"); + const receipt = await waitForTransactionReceipt(walletClient, { + hash: tx, + }); + + if (receipt.status === "success") { + await setDialogStep("done", "completed"); + const extraContent = createExtraContent({ + receipt, + chain: this.chain, + }); + setExtraContent(extraContent); + + const hypercertViewInvalidationPaths = params.map((param) => { + return `/hypercerts/${param.tokenId}`; + }); + + // Revalidate all relevant paths + await revalidatePathServerAction([ + `/profile/${this.address}`, + `/profile/${this.address}?tab`, + `/profile/${this.address}?tab=hypercerts-claimable`, + `/profile/${this.address}?tab=hypercerts-owned`, + ...hypercertViewInvalidationPaths, + ]); + + // Wait 5 seconds before refreshing and navigating + setTimeout(() => { + this.router.refresh(); + this.router.push(`/profile/${this.address}?tab=hypercerts-claimable`); + }, 5000); + } else if (receipt.status === "reverted") { + await setDialogStep("confirming", "error", "Transaction reverted"); + } + } catch (error) { + console.error("Claim error:", error); + await setDialogStep("claiming", "error", "Transaction failed"); + throw error; + } + } +} + +function mapClaimParams(params: ClaimHypercertParams[]) { + return { + hypercertTokenIds: params.map((p) => p.tokenId), + units: params.map((p) => p.units), + proofs: params.map((p) => p.proof), + }; +} diff --git a/hypercerts/EOAClaimHypercertStrategy.ts b/hypercerts/EOAClaimHypercertStrategy.ts new file mode 100644 index 00000000..f567ee51 --- /dev/null +++ b/hypercerts/EOAClaimHypercertStrategy.ts @@ -0,0 +1,86 @@ +import { waitForTransactionReceipt } from "viem/actions"; +import assert from "assert"; + +import { createExtraContent } from "@/components/global/extra-content"; +import { revalidatePathServerAction } from "@/app/actions/revalidatePathServerAction"; + +import { + ClaimHypercertStrategy, + ClaimHypercertParams, +} from "./ClaimHypercertStrategy"; + +export class EOAClaimHypercertStrategy extends ClaimHypercertStrategy { + async execute(params: ClaimHypercertParams[]) { + assert(params.length === 1, "Only one claim params object allowed"); + + const { tokenId, units, proof } = params[0]; + const { setDialogStep, setSteps, setOpen, setTitle, setExtraContent } = + this.dialogContext; + const { data: walletClient } = this.walletClient; + + if (!this.client) throw new Error("No client found"); + if (!walletClient) throw new Error("No wallet client found"); + + setOpen(true); + setSteps([ + { id: "preparing", description: "Preparing to claim fraction..." }, + { id: "claiming", description: "Claiming fraction on-chain..." }, + { id: "confirming", description: "Waiting for on-chain confirmation" }, + { id: "route", description: "Creating your new fraction's link..." }, + { id: "done", description: "Claiming complete!" }, + ]); + setTitle("Claim fraction from Allowlist"); + + try { + await setDialogStep("preparing", "active"); + await setDialogStep("claiming", "active"); + const tx = await this.client.mintClaimFractionFromAllowlist( + tokenId, + units, + proof, + undefined, + ); + + if (!tx) { + await setDialogStep("claiming", "error"); + throw new Error("Failed to claim fraction"); + } + + await setDialogStep("confirming", "active"); + const receipt = await waitForTransactionReceipt(walletClient, { + hash: tx, + }); + + if (receipt.status === "success") { + await setDialogStep("route", "active"); + const extraContent = createExtraContent({ + receipt, + hypercertId: `${this.chain.id}-${tokenId}`, + chain: this.chain, + }); + setExtraContent(extraContent); + await setDialogStep("done", "completed"); + + // Revalidate all relevant paths + await revalidatePathServerAction([ + `/hypercerts/${this.chain.id}-${tokenId}`, + `/profile/${this.address}`, + `/profile/${this.address}?tab`, + `/profile/${this.address}?tab=hypercerts-claimable`, + `/profile/${this.address}?tab=hypercerts-owned`, + ]); + + // Wait 5 seconds before refreshing and navigating + setTimeout(() => { + this.router.refresh(); + this.router.push(`/profile/${this.address}?tab=hypercerts-claimable`); + }, 5000); + } else { + await setDialogStep("confirming", "error", "Transaction reverted"); + } + } catch (error) { + console.error(error); + throw error; + } + } +} diff --git a/hypercerts/SafeBatchClaimHypercertStrategy.tsx b/hypercerts/SafeBatchClaimHypercertStrategy.tsx new file mode 100644 index 00000000..11c6acba --- /dev/null +++ b/hypercerts/SafeBatchClaimHypercertStrategy.tsx @@ -0,0 +1,103 @@ +import { Chain } from "viem"; +import { ExternalLink } from "lucide-react"; + +import { Button } from "@/components/ui/button"; +import { generateSafeAppLink } from "@/lib/utils"; + +import { + ClaimHypercertStrategy, + ClaimHypercertParams, +} from "./ClaimHypercertStrategy"; + +function DialogFooter({ + chain, + safeAddress, +}: { + chain: Chain; + safeAddress: string; +}) { + return ( +
+

Success

+

+ We've submitted the transaction requests to the connected Safe. +

+
+ {chain && ( + + )} +
+
+ ); +} + +export class SafeBatchClaimHypercertStrategy extends ClaimHypercertStrategy { + async execute(params: ClaimHypercertParams[]): Promise { + console.log("[SafeBatchClaim] Starting execution with params:", params); + const { setDialogStep, setSteps, setOpen, setTitle, setExtraContent } = + this.dialogContext; + + if (!this.client) { + console.error("[SafeBatchClaim] No client found"); + setOpen(false); + throw new Error("No client found"); + } + + console.log("[SafeBatchClaim] Setting up dialog UI"); + setOpen(true); + setTitle("Claim fractions from Allowlist"); + setSteps([ + { id: "preparing", description: "Preparing to claim fractions..." }, + { id: "submitting", description: "Submitting to Safe..." }, + { id: "queued", description: "Transaction queued in Safe" }, + ]); + + await setDialogStep("preparing", "active"); + console.log("[SafeBatchClaim] Preparation step completed"); + + try { + console.log("[SafeBatchClaim] Starting submission to Safe"); + await setDialogStep("submitting", "active"); + const mappedParams = mapClaimParams(params); + console.log("[SafeBatchClaim] Mapped params:", mappedParams); + + await this.client.batchClaimFractionsFromAllowlists({ + ...mappedParams, + overrides: { + safeAddress: this.address as `0x${string}`, + }, + }); + + console.log("[SafeBatchClaim] Successfully queued transaction in Safe"); + await setDialogStep("queued", "completed"); + + setExtraContent(() => ( + + )); + } catch (error) { + console.error("[SafeBatchClaim] Error during execution:", error); + await setDialogStep( + "submitting", + "error", + error instanceof Error ? error.message : "Unknown error", + ); + throw error; + } + } +} + +function mapClaimParams(params: ClaimHypercertParams[]) { + return { + hypercertTokenIds: params.map((p) => p.tokenId), + units: params.map((p) => p.units), + proofs: params.map((p) => p.proof), + }; +} diff --git a/hypercerts/SafeClaimHypercertStrategy.tsx b/hypercerts/SafeClaimHypercertStrategy.tsx new file mode 100644 index 00000000..3afb56d2 --- /dev/null +++ b/hypercerts/SafeClaimHypercertStrategy.tsx @@ -0,0 +1,92 @@ +import { Chain } from "viem"; +import { ExternalLink } from "lucide-react"; +import assert from "assert"; + +import { Button } from "@/components/ui/button"; +import { generateSafeAppLink } from "@/lib/utils"; + +import { + ClaimHypercertStrategy, + ClaimHypercertParams, +} from "./ClaimHypercertStrategy"; + +export class SafeClaimHypercertStrategy extends ClaimHypercertStrategy { + async execute(params: ClaimHypercertParams[]) { + assert(params.length === 1, "Only one claim params object allowed"); + + const { tokenId, units, proof } = params[0]; + const { setDialogStep, setSteps, setOpen, setTitle, setExtraContent } = + this.dialogContext; + + if (!this.client) { + setOpen(false); + throw new Error("No client found"); + } + + setOpen(true); + setTitle("Claim fraction from Allowlist"); + setSteps([ + { id: "preparing", description: "Preparing to claim fraction..." }, + { id: "submitting", description: "Submitting to Safe..." }, + { id: "queued", description: "Transaction queued in Safe" }, + ]); + + await setDialogStep("preparing", "active"); + + try { + await setDialogStep("submitting", "active"); + await this.client.claimFractionFromAllowlist({ + hypercertTokenId: tokenId, + units, + proof, + overrides: { + safeAddress: this.address as `0x${string}`, + }, + }); + + await setDialogStep("queued", "completed"); + + setExtraContent(() => ( + + )); + } catch (error) { + console.error(error); + await setDialogStep( + "submitting", + "error", + error instanceof Error ? error.message : "Unknown error", + ); + throw error; + } + } +} + +function DialogFooter({ + chain, + safeAddress, +}: { + chain: Chain; + safeAddress: string; +}) { + return ( +
+

Success

+

+ We've submitted the claim request to the connected Safe. +

+
+ {chain && ( + + )} +
+
+ ); +} diff --git a/hypercerts/hooks/useClaimHypercert.ts b/hypercerts/hooks/useClaimHypercert.ts new file mode 100644 index 00000000..cfe10b91 --- /dev/null +++ b/hypercerts/hooks/useClaimHypercert.ts @@ -0,0 +1,24 @@ +import { useMutation } from "@tanstack/react-query"; +import { toast } from "@/components/ui/use-toast"; +import { ClaimHypercertParams } from "../ClaimHypercertStrategy"; + +import { useClaimHypercertStrategy } from "./useClaimHypercertStrategy"; + +export const useClaimHypercert = () => { + const getStrategy = useClaimHypercertStrategy(); + + return useMutation({ + mutationKey: ["CLAIM_HYPERCERT"], + onError: (e: Error) => { + console.error(e); + toast({ + title: "Error", + description: e.message, + duration: 5000, + }); + }, + mutationFn: async (params: ClaimHypercertParams[]) => { + return getStrategy(params).execute(params); + }, + }); +}; diff --git a/hypercerts/hooks/useClaimHypercertStrategy.ts b/hypercerts/hooks/useClaimHypercertStrategy.ts new file mode 100644 index 00000000..5e78a00f --- /dev/null +++ b/hypercerts/hooks/useClaimHypercertStrategy.ts @@ -0,0 +1,79 @@ +import { isAddress } from "viem"; +import { useAccount, useWalletClient } from "wagmi"; +import { useRouter } from "next/navigation"; + +import { useAccountStore } from "@/lib/account-store"; +import { useHypercertClient } from "@/hooks/use-hypercert-client"; +import { useStepProcessDialogContext } from "@/components/global/step-process-dialog"; + +import { + ClaimHypercertParams, + ClaimHypercertStrategy, +} from "../ClaimHypercertStrategy"; +import { EOAClaimHypercertStrategy } from "../EOAClaimHypercertStrategy"; +import { SafeClaimHypercertStrategy } from "../SafeClaimHypercertStrategy"; +import { EOABatchClaimHypercertStrategy } from "../EOABatchClaimHypercertStrategy"; +import { SafeBatchClaimHypercertStrategy } from "../SafeBatchClaimHypercertStrategy"; + +export const useClaimHypercertStrategy = (): (( + params: ClaimHypercertParams[], +) => ClaimHypercertStrategy) => { + const { address, chain } = useAccount(); + const { client } = useHypercertClient(); + const { selectedAccount } = useAccountStore(); + const dialogContext = useStepProcessDialogContext(); + const walletClient = useWalletClient(); + const router = useRouter(); + + return (params: ClaimHypercertParams[]) => { + const activeAddress = + selectedAccount?.address || (address as `0x${string}`); + + if (!activeAddress || !isAddress(activeAddress)) + throw new Error("No address found"); + if (!chain) throw new Error("No chain found"); + if (!client) throw new Error("No HypercertClient found"); + if (!walletClient) throw new Error("No walletClient found"); + if (!dialogContext) throw new Error("No dialogContext found"); + + const isBatch = params.length > 1; + + if (selectedAccount?.type === "safe") { + return isBatch + ? new SafeBatchClaimHypercertStrategy( + activeAddress, + chain, + client, + dialogContext, + walletClient, + router + ) + : new SafeClaimHypercertStrategy( + activeAddress, + chain, + client, + dialogContext, + walletClient, + router + ); + } + + return isBatch + ? new EOABatchClaimHypercertStrategy( + activeAddress, + chain, + client, + dialogContext, + walletClient, + router, + ) + : new EOAClaimHypercertStrategy( + activeAddress, + chain, + client, + dialogContext, + walletClient, + router, + ); + }; +}; diff --git a/package.json b/package.json index beb7b85a..5001cc6e 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "@hookform/resolvers": "^3.3.4", "@hypercerts-org/contracts": "2.0.0-alpha.12", "@hypercerts-org/marketplace-sdk": "0.5.0-alpha.0", - "@hypercerts-org/sdk": "2.6.0", + "@hypercerts-org/sdk": "2.7.0", "@next/env": "^14.2.10", "@openzeppelin/merkle-tree": "^1.0.6", "@radix-ui/react-accordion": "^1.2.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 04d7fb11..ce83dc0f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,8 +24,8 @@ importers: specifier: 0.5.0-alpha.0 version: 0.5.0-alpha.0(@safe-global/api-kit@2.5.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.7.3)(utf-8-validate@5.0.10)(zod@3.24.1))(@safe-global/protocol-kit@5.2.2(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@5.0.10)(zod@3.24.1))(@swc/helpers@0.5.5)(bufferutil@4.0.9)(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(graphql@16.10.0)(rollup@4.31.0)(ts-node@10.9.2(@swc/core@1.10.9(@swc/helpers@0.5.5))(@types/node@20.17.14)(typescript@5.7.3))(typescript@5.7.3)(utf-8-validate@5.0.10)(zod@3.24.1) '@hypercerts-org/sdk': - specifier: 2.6.0 - version: 2.6.0(@safe-global/api-kit@2.5.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.7.3)(utf-8-validate@5.0.10)(zod@3.24.1))(@safe-global/protocol-kit@5.2.2(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@5.0.10)(zod@3.24.1))(@safe-global/types-kit@1.0.2(typescript@5.7.3)(zod@3.24.1))(@swc/helpers@0.5.5)(bufferutil@4.0.9)(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(graphql@16.10.0)(rollup@4.31.0)(ts-node@10.9.2(@swc/core@1.10.9(@swc/helpers@0.5.5))(@types/node@20.17.14)(typescript@5.7.3))(typescript@5.7.3)(utf-8-validate@5.0.10) + specifier: 2.7.0 + version: 2.7.0(@safe-global/api-kit@2.5.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.7.3)(utf-8-validate@5.0.10)(zod@3.24.1))(@safe-global/protocol-kit@5.2.2(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@5.0.10)(zod@3.24.1))(@safe-global/types-kit@1.0.2(typescript@5.7.3)(zod@3.24.1))(@swc/helpers@0.5.5)(bufferutil@4.0.9)(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(graphql@16.10.0)(rollup@4.31.0)(ts-node@10.9.2(@swc/core@1.10.9(@swc/helpers@0.5.5))(@types/node@20.17.14)(typescript@5.7.3))(typescript@5.7.3)(utf-8-validate@5.0.10) '@next/env': specifier: ^14.2.10 version: 14.2.23 @@ -835,8 +835,8 @@ packages: '@hypercerts-org/sdk@2.4.0': resolution: {integrity: sha512-9vxQW3zBwi3WCOUBMwU1fWEk3z29eyxtDWlaIS7jdUlGwnCcN9IkPzIk7w/jHO96yiH8+vcL/EWFvOp06mtXAw==} - '@hypercerts-org/sdk@2.6.0': - resolution: {integrity: sha512-uq+9WzgW+GWazEKTUEhUPZr8sTxhORaNI6DfRfDTZ8w0FJtPEXSSLt5mr9x5VN8EghM1NsPvKzf38jkO8wBkZg==} + '@hypercerts-org/sdk@2.7.0': + resolution: {integrity: sha512-ciTZxuoHYgLPiXnAx65wTkURQVUk6QbJx6XKsfLV16i+i/ZxwxSx+Kk9AsDYoa0LkQzIFcJwnv+CroFyzL0cUQ==} peerDependencies: '@safe-global/api-kit': ^2.5.7 '@safe-global/protocol-kit': ^5.2.0 @@ -7293,7 +7293,7 @@ snapshots: - typescript - utf-8-validate - '@hypercerts-org/sdk@2.6.0(@safe-global/api-kit@2.5.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.7.3)(utf-8-validate@5.0.10)(zod@3.24.1))(@safe-global/protocol-kit@5.2.2(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@5.0.10)(zod@3.24.1))(@safe-global/types-kit@1.0.2(typescript@5.7.3)(zod@3.24.1))(@swc/helpers@0.5.5)(bufferutil@4.0.9)(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(graphql@16.10.0)(rollup@4.31.0)(ts-node@10.9.2(@swc/core@1.10.9(@swc/helpers@0.5.5))(@types/node@20.17.14)(typescript@5.7.3))(typescript@5.7.3)(utf-8-validate@5.0.10)': + '@hypercerts-org/sdk@2.7.0(@safe-global/api-kit@2.5.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.7.3)(utf-8-validate@5.0.10)(zod@3.24.1))(@safe-global/protocol-kit@5.2.2(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@5.0.10)(zod@3.24.1))(@safe-global/types-kit@1.0.2(typescript@5.7.3)(zod@3.24.1))(@swc/helpers@0.5.5)(bufferutil@4.0.9)(ethers@6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10))(graphql@16.10.0)(rollup@4.31.0)(ts-node@10.9.2(@swc/core@1.10.9(@swc/helpers@0.5.5))(@types/node@20.17.14)(typescript@5.7.3))(typescript@5.7.3)(utf-8-validate@5.0.10)': dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.10.0) '@hypercerts-org/contracts': 2.0.0-alpha.12(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.10.9(@swc/helpers@0.5.5))(@types/node@20.17.14)(typescript@5.7.3))(typescript@5.7.3)(utf-8-validate@5.0.10)