Skip to content

Commit ca147fe

Browse files
authored
Merge pull request #421 from hypercerts-org/dev
Push to PRD
2 parents 975c254 + 9e52170 commit ca147fe

16 files changed

+1077
-157
lines changed

app/profile/[address]/hypercerts-tab-content.tsx

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ import { getHypercertsByCreator } from "@/hypercerts/getHypercertsByCreator";
22
import { getAllowListRecordsForAddressByClaimed } from "@/allowlists/getAllowListRecordsForAddressByClaimed";
33
import HypercertWindow from "@/components/hypercert/hypercert-window";
44
import { EmptySection } from "@/components/global/sections";
5-
import UnclaimedHypercertsList from "@/components/profile/unclaimed-hypercerts-list";
5+
import UnclaimedHypercertsList, {
6+
UnclaimedFraction,
7+
} from "@/components/profile/unclaimed-hypercerts-list";
68
import { Suspense } from "react";
79
import ExploreListSkeleton from "@/components/explore/explore-list-skeleton";
810
import { ProfileSubTabKey, subTabs } from "@/app/profile/[address]/tabs";
911
import { SubTabsWithCount } from "@/components/profile/sub-tabs-with-count";
1012
import { getHypercertsByOwner } from "@/hypercerts/getHypercertsByOwner";
13+
import { getHypercertMetadata } from "@/hypercerts/getHypercertMetadata";
1114

1215
const HypercertsTabContentInner = async ({
1316
address,
@@ -27,7 +30,35 @@ const HypercertsTabContentInner = async ({
2730
const claimableHypercerts = await getAllowListRecordsForAddressByClaimed(
2831
address,
2932
false,
30-
);
33+
).then(async (res) => {
34+
if (!res?.data) {
35+
return {
36+
data: [],
37+
count: 0,
38+
};
39+
}
40+
const hypercertsWithMetadata = await Promise.all(
41+
res.data.map(async (record): Promise<UnclaimedFraction> => {
42+
const metadata = await getHypercertMetadata(
43+
record.hypercert_id as string,
44+
);
45+
if (!metadata) {
46+
return {
47+
...record,
48+
metadata: null,
49+
};
50+
}
51+
return {
52+
...record,
53+
metadata: metadata?.data,
54+
};
55+
}),
56+
);
57+
return {
58+
data: hypercertsWithMetadata,
59+
count: res?.count,
60+
};
61+
});
3162

3263
const showCreatedHypercerts =
3364
createdHypercerts?.data && createdHypercerts.data.length > 0;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { Button } from "@/components/ui/button";
2+
import type { Chain, TransactionReceipt } from "viem";
3+
import { generateBlockExplorerLink } from "@/lib/utils";
4+
5+
export const createExtraContent = ({
6+
receipt,
7+
hypercertId,
8+
chain,
9+
}: {
10+
receipt: TransactionReceipt;
11+
hypercertId?: string;
12+
chain: Chain;
13+
}) => {
14+
const receiptButton = receipt && (
15+
<>
16+
<a
17+
href={generateBlockExplorerLink(chain, receipt.transactionHash)}
18+
target="_blank"
19+
rel="noopener noreferrer"
20+
>
21+
<Button size="default" variant={"secondary"}>
22+
View transaction
23+
</Button>
24+
</a>
25+
</>
26+
);
27+
28+
const hypercertButton = hypercertId && (
29+
<>
30+
<a
31+
href={`/hypercerts/${hypercertId}`}
32+
target="_blank"
33+
rel="noopener noreferrer"
34+
>
35+
<Button size="default" variant={"default"}>
36+
View hypercert
37+
</Button>
38+
</a>
39+
</>
40+
);
41+
42+
return (
43+
<div className="flex flex-col space-y-2">
44+
<p className="text-sm font-medium">
45+
Your hypercert has been minted successfully!
46+
</p>
47+
<div className="flex space-x-4">
48+
{receiptButton}
49+
{hypercertButton}
50+
</div>
51+
</div>
52+
);
53+
};

components/global/footer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const footerIcons = [
2222
},
2323
{
2424
label: "Discord",
25-
url: "https://discord.gg/VVSyKg75",
25+
url: "https://discord.gg/azPgDcSQWw",
2626
icon: "/social-icons/discord.svg",
2727
},
2828
{
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
"use client";
2+
3+
import { AllowListRecord } from "@/allowlists/getAllowListRecordsForAddressByClaimed";
4+
import { Button } from "../ui/button";
5+
import { useHypercertClient } from "@/hooks/use-hypercert-client";
6+
import { waitForTransactionReceipt } from "viem/actions";
7+
import { useAccount, useSwitchChain, useWalletClient } from "wagmi";
8+
import { useRouter } from "next/navigation";
9+
import { useStepProcessDialogContext } from "../global/step-process-dialog";
10+
import { revalidatePathServerAction } from "@/app/actions/revalidatePathServerAction";
11+
import { useState } from "react";
12+
import { Hex, ByteArray, getAddress } from "viem";
13+
import { errorToast } from "@/lib/errorToast";
14+
import { ChainFactory } from "@/lib/chainFactory";
15+
import { createExtraContent } from "../global/extra-content";
16+
17+
interface TransformedClaimData {
18+
hypercertTokenIds: bigint[];
19+
units: bigint[];
20+
proofs: (Hex | ByteArray)[][];
21+
roots?: (Hex | ByteArray)[];
22+
}
23+
24+
function transformAllowListRecords(
25+
records: AllowListRecord[],
26+
): TransformedClaimData {
27+
return {
28+
hypercertTokenIds: records.map((record) => BigInt(record.token_id!)),
29+
units: records.map((record) => BigInt(record.units!)),
30+
proofs: records.map((record) => record.proof as (Hex | ByteArray)[]),
31+
roots: records.map((record) => record.root as Hex | ByteArray),
32+
};
33+
}
34+
35+
export default function UnclaimedHypercertBatchClaimButton({
36+
allowListRecords,
37+
selectedChainId,
38+
}: {
39+
allowListRecords: AllowListRecord[];
40+
selectedChainId: number | null;
41+
}) {
42+
const { client } = useHypercertClient();
43+
const { data: walletClient } = useWalletClient();
44+
const account = useAccount();
45+
const { refresh } = useRouter();
46+
const [isLoading, setIsLoading] = useState(false);
47+
const { setDialogStep, setSteps, setOpen, setTitle, setExtraContent } =
48+
useStepProcessDialogContext();
49+
const { switchChain } = useSwitchChain();
50+
51+
const selectedChain = selectedChainId
52+
? ChainFactory.getChain(selectedChainId)
53+
: null;
54+
55+
const claimHypercert = async () => {
56+
setIsLoading(true);
57+
setOpen(true);
58+
setSteps([
59+
{ id: "preparing", description: "Preparing to claim fractions..." },
60+
{ id: "claiming", description: "Claiming fractions on-chain..." },
61+
{ id: "confirming", description: "Waiting for on-chain confirmation" },
62+
{ id: "done", description: "Claiming complete!" },
63+
]);
64+
setTitle("Claim fractions from Allowlist");
65+
if (!client) {
66+
throw new Error("No client found");
67+
}
68+
if (!walletClient) {
69+
throw new Error("No wallet client found");
70+
}
71+
if (!account) {
72+
throw new Error("No address found");
73+
}
74+
75+
const claimData = transformAllowListRecords(allowListRecords);
76+
await setDialogStep("preparing, active");
77+
try {
78+
await setDialogStep("claiming", "active");
79+
80+
const tx = await client.batchClaimFractionsFromAllowlists(claimData);
81+
if (!tx) {
82+
await setDialogStep("claiming", "error");
83+
throw new Error("Failed to claim fractions");
84+
}
85+
await setDialogStep("confirming", "active");
86+
const receipt = await waitForTransactionReceipt(walletClient, {
87+
hash: tx,
88+
});
89+
if (receipt.status == "success") {
90+
await setDialogStep("done", "completed");
91+
const extraContent = createExtraContent({
92+
receipt,
93+
chain: account?.chain!,
94+
});
95+
setExtraContent(extraContent);
96+
await revalidatePathServerAction([
97+
`/profile/${account.address}?tab=hypercerts-claimable`,
98+
`/profile/${account.address}?tab=hypercerts-owned`,
99+
]);
100+
} else if (receipt.status == "reverted") {
101+
await setDialogStep("confirming", "error", "Transaction reverted");
102+
}
103+
setTimeout(() => {
104+
refresh();
105+
}, 5000);
106+
} catch (error) {
107+
console.error(error);
108+
} finally {
109+
setIsLoading(false);
110+
}
111+
};
112+
113+
const isBatchClaimDisabled =
114+
isLoading ||
115+
!allowListRecords.length ||
116+
!account ||
117+
!client ||
118+
account.address !== getAddress(allowListRecords[0].user_address as string);
119+
120+
return (
121+
<>
122+
{account.chainId === selectedChainId ? (
123+
<Button
124+
variant={"default"}
125+
size={"sm"}
126+
onClick={claimHypercert}
127+
disabled={isBatchClaimDisabled}
128+
>
129+
Claim Selected
130+
</Button>
131+
) : (
132+
<Button
133+
variant={"outline"}
134+
size="sm"
135+
disabled={!account.isConnected || !selectedChainId}
136+
onClick={() => {
137+
if (!selectedChainId) return errorToast("Fraction is not selected");
138+
switchChain({ chainId: selectedChainId });
139+
}}
140+
>
141+
{selectedChainId
142+
? `Switch to ${selectedChain?.name}`
143+
: "Select fraction"}
144+
</Button>
145+
)}
146+
</>
147+
);
148+
}

0 commit comments

Comments
 (0)