Skip to content

Commit 9486997

Browse files
authored
Merge pull request #527 from hypercerts-org/feature/transfer-restriction-labels
feat(creator): add transfer restrictions label to hypercert component
2 parents 70762ee + 858b89e commit 9486997

File tree

4 files changed

+138
-28
lines changed

4 files changed

+138
-28
lines changed

components/hypercert/creator.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import { Cuboid } from "lucide-react";
33
import { SUPPORTED_CHAINS, SupportedChainIdType } from "@/configs/constants";
44
import CopyableHypercertId from "@/components/copyable-hypercert-id";
55
import { HypercertState } from "@/hypercerts/fragments/hypercert-state.fragment";
6+
import TransferRestrictionsLabel from "./transfer-restrictions-label";
67

78
export default function Creator({ hypercert }: { hypercert: HypercertState }) {
89
if (!hypercert) return null;
910
return (
10-
<div className="flex flex-wrap items-center space-x-1 text-sm text-slate-600 font-medium">
11+
<div className="flex flex-wrap items-center space-x-1 text-sm text-slate-600 font-medium space-y-1">
1112
{hypercert.hypercert_id && (
1213
<CopyableHypercertId id={hypercert.hypercert_id} />
1314
)}
@@ -17,6 +18,12 @@ export default function Creator({ hypercert }: { hypercert: HypercertState }) {
1718
<EthAddress address={hypercert.creator_address} showEnsName />
1819
</div>
1920
)}
21+
<div className="flex space-x-1 items-center">
22+
<TransferRestrictionsLabel
23+
hypercertId={hypercert.hypercert_id || ""}
24+
showSeparator
25+
/>
26+
</div>
2027
{hypercert.contract?.chain_id && (
2128
<div className="flex space-x-2 items-center">
2229
<span className="text-slate-400"></span>
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"use client";
2+
3+
import {
4+
parseClaimOrFractionId,
5+
TransferRestrictions,
6+
} from "@hypercerts-org/sdk";
7+
import { getAddress } from "viem";
8+
import { useReadContract } from "wagmi";
9+
import {
10+
Popover,
11+
PopoverContent,
12+
PopoverTrigger,
13+
} from "@/components/ui/popover";
14+
import { useState } from "react";
15+
import { useReadTransferRestrictions } from "@/hooks/use-read-transfer-restrictions";
16+
17+
export default function TransferRestrictionsLabel({
18+
hypercertId,
19+
showSeparator = false,
20+
}: {
21+
hypercertId: string;
22+
showSeparator?: boolean;
23+
}) {
24+
const [isOpen, setIsOpen] = useState(false);
25+
const transferRestrictions = useReadTransferRestrictions(hypercertId);
26+
27+
if (transferRestrictions === undefined) return null;
28+
return (
29+
<>
30+
{showSeparator && <span className="text-slate-400"></span>}
31+
<div className="flex items-center gap-2 content-center px-1 py-0.5 bg-slate-100 rounded-md w-max text-sm">
32+
<Popover open={isOpen} onOpenChange={setIsOpen}>
33+
<PopoverTrigger asChild>
34+
<span
35+
className="cursor-help"
36+
onMouseEnter={() => setIsOpen(true)}
37+
onMouseLeave={() => setIsOpen(false)}
38+
>
39+
{getTransferRestrictionsText(transferRestrictions)}
40+
</span>
41+
</PopoverTrigger>
42+
<PopoverContent className="w-80 p-3" side="top">
43+
<p className="text-sm text-slate-700">
44+
{getTransferRestrictionsLabel(transferRestrictions)}
45+
</p>
46+
</PopoverContent>
47+
</Popover>
48+
</div>
49+
</>
50+
);
51+
}
52+
53+
export const getTransferRestrictionsText = (
54+
transferRestrictions: TransferRestrictions,
55+
) => {
56+
switch (transferRestrictions) {
57+
case TransferRestrictions.AllowAll:
58+
return "Transferable";
59+
case TransferRestrictions.DisallowAll:
60+
return "Not transferable";
61+
case TransferRestrictions.FromCreatorOnly:
62+
return "Transferable-once";
63+
}
64+
};
65+
66+
export const getTransferRestrictionsLabel = (
67+
transferRestrictions: TransferRestrictions,
68+
) => {
69+
switch (transferRestrictions) {
70+
case TransferRestrictions.AllowAll:
71+
return "Fractions can be transferred without limitations.";
72+
case TransferRestrictions.DisallowAll:
73+
return "Fractions can not be transferred";
74+
case TransferRestrictions.FromCreatorOnly:
75+
return "Fractions can be transferred once from the creator to another user.";
76+
}
77+
};

components/marketplace/list-for-sale-button.tsx

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import { useState } from "react";
1818
import { getAddress } from "viem";
1919

2020
import { isChainIdSupported } from "@/lib/isChainIdSupported";
21+
import { useReadTransferRestrictions } from "@/hooks/use-read-transfer-restrictions";
22+
import { TransferRestrictions } from "@hypercerts-org/sdk";
2123

2224
export function ListForSaleButton({
2325
hypercert,
@@ -34,29 +36,9 @@ export function ListForSaleButton({
3436
const { chain_id: chainId, contract_address: contractAddress } =
3537
hypercert.contract || {};
3638

37-
const { data: transferRestrictions } = useReadContract({
38-
abi: [
39-
{
40-
inputs: [{ internalType: "uint256", name: "tokenID", type: "uint256" }],
41-
name: "readTransferRestriction",
42-
outputs: [
43-
{
44-
internalType: "string",
45-
name: "",
46-
type: "string",
47-
},
48-
],
49-
stateMutability: "view",
50-
type: "function",
51-
},
52-
],
53-
address: getAddress(contractAddress || ""),
54-
functionName: "readTransferRestriction",
55-
args: [tokenId!],
56-
query: {
57-
enabled: !!contractAddress && !!tokenId,
58-
},
59-
});
39+
const transferRestrictions = useReadTransferRestrictions(
40+
hypercert.hypercert_id || "",
41+
);
6042

6143
const [isOpen, setIsOpen] = useState(false);
6244

@@ -87,8 +69,8 @@ export function ListForSaleButton({
8769
!client ||
8870
!client.isClaimOrFractionOnConnectedChain(hypercertId) ||
8971
!fractionsOwnedByUser.length ||
90-
transferRestrictions === "DisallowAll" ||
91-
(transferRestrictions === "FromCreatorOnly" &&
72+
transferRestrictions === TransferRestrictions.DisallowAll ||
73+
(transferRestrictions === TransferRestrictions.FromCreatorOnly &&
9274
address?.toLowerCase() !== hypercert.creator_address?.toLowerCase());
9375

9476
const getToolTipMessage = () => {
@@ -114,11 +96,11 @@ export function ListForSaleButton({
11496
return "You do not own any fractions of this hypercert";
11597
}
11698

117-
if (transferRestrictions === "DisallowAll") {
99+
if (transferRestrictions === TransferRestrictions.DisallowAll) {
118100
return "Secondary sales are not allowed for this hypercert";
119101
}
120102

121-
if (transferRestrictions === "FromCreatorOnly") {
103+
if (transferRestrictions === TransferRestrictions.FromCreatorOnly) {
122104
return "Only the creator can sell this hypercert";
123105
}
124106

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import {
2+
parseClaimOrFractionId,
3+
TransferRestrictions,
4+
} from "@hypercerts-org/sdk";
5+
import { getAddress } from "viem";
6+
import { useReadContract } from "wagmi";
7+
8+
export const useReadTransferRestrictions = (hypercertId: string) => {
9+
const { contractAddress, id } = parseClaimOrFractionId(hypercertId);
10+
const { data: transferRestrictions } = useReadContract({
11+
abi: [
12+
{
13+
inputs: [{ internalType: "uint256", name: "tokenID", type: "uint256" }],
14+
name: "readTransferRestriction",
15+
outputs: [
16+
{
17+
internalType: "string",
18+
name: "",
19+
type: "string",
20+
},
21+
],
22+
stateMutability: "view",
23+
type: "function",
24+
},
25+
],
26+
address: getAddress(contractAddress || ""),
27+
functionName: "readTransferRestriction",
28+
args: [id],
29+
query: {
30+
enabled: !!contractAddress && !!id,
31+
select: (data) => {
32+
if (data === "AllowAll") {
33+
return TransferRestrictions.AllowAll;
34+
} else if (data === "DisallowAll") {
35+
return TransferRestrictions.DisallowAll;
36+
} else if (data === "FromCreatorOnly") {
37+
return TransferRestrictions.FromCreatorOnly;
38+
}
39+
},
40+
},
41+
});
42+
43+
return transferRestrictions;
44+
};

0 commit comments

Comments
 (0)