Skip to content

Commit

Permalink
Feature/multichain support (#42)
Browse files Browse the repository at this point in the history
* (feat): add migration to update chain ids, update packes, update hooks

* (feat): add basic multichain support

* (fix): add seeding and update for multichain sdk

* (feat): add badges for chains and disable ui if connected to the wrong chain

* (feat): use alpha version of hypercerts sdk, fix backwards compatibility for allowlists

* (feat): updating seeding and multi-chain hyperboard fetching

* (fix): ignore typing error for widget root rendering

* (feat): multi-chain ui improvements

* (feat): multi-chain marketplace compatibility

* (feat): multi-chain marketplace compatibility

* (feat): marketplace qa
  • Loading branch information
Jipperism authored Apr 15, 2024
1 parent 4b79271 commit 77546fc
Show file tree
Hide file tree
Showing 28 changed files with 4,220 additions and 3,297 deletions.
2 changes: 1 addition & 1 deletion components/admin/create-registry-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export const CreateRegistryModal = ({
display_size,
}) => {
const claim = await client.indexer.claimById(hypercert_id);
if (!claim.claim) {
if (!claim?.claim) {
throw new Error("Claim not found");
}
return {
Expand Down
16 changes: 16 additions & 0 deletions components/chain-badge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Badge, BadgeProps } from "@chakra-ui/react";
import { chainIdToName } from "@/utils/formatting";
import { parseClaimOrFractionId } from "@hypercerts-org/sdk";

export const ChainBadge = ({
hypercertId,
...badgeProps
}: { hypercertId: string } & BadgeProps) => {
const { chainId } = parseClaimOrFractionId(hypercertId);
const name = chainIdToName(chainId);
return (
<Badge width={"fit-content"} height={"fit-content"} {...badgeProps}>
{name}
</Badge>
);
};
16 changes: 15 additions & 1 deletion components/marketplace/buy-hypercert-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import React from "react";
import { BalanceOverview } from "@/components/balance-overview";
import { useFetchHypercertById } from "@/hooks/useFetchHypercertById";
import { OwnershipStats } from "@/components/marketplace/ownership-stats";
import { useHypercertClient } from "@/components/providers";

type Props = {
hypercertId: string;
Expand All @@ -34,6 +35,8 @@ export const BuyHypercertButton = React.forwardRef<HTMLButtonElement, Props>(
id: "buy-hypercert-button",
});

const client = useHypercertClient();

const { data: orderData } =
useFetchMarketplaceOrdersForHypercert(hypercertId);
const { data: hypercert } = useFetchHypercertById(hypercertId);
Expand All @@ -55,9 +58,20 @@ export const BuyHypercertButton = React.forwardRef<HTMLButtonElement, Props>(
? orderData?.orders[boughtFractionId]
: undefined;

const disabled =
!client ||
!client.isClaimOrFractionOnConnectedChain(hypercertId) ||
Object.keys(orderData?.orders ?? {}).length === 0;

return (
<>
<Button variant="blackAndWhite" onClick={onOpen} ref={ref} {...props}>
<Button
variant="blackAndWhite"
onClick={onOpen}
ref={ref}
isDisabled={disabled}
{...props}
>
{text}
</Button>
<Modal isOpen={isOpen} onClose={onClose}>
Expand Down
10 changes: 9 additions & 1 deletion components/marketplace/hypercert-tile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useHover } from "@uidotdev/usehooks";
import { Box, Flex, Image } from "@chakra-ui/react";
import { useFetchHypercertById } from "@/hooks/useFetchHypercertById";
import { MarketplaceStats } from "@/components/marketplace/marketplace-stats";
import { ChainBadge } from "@/components/chain-badge";

export const HypercertTile = ({ hypercertId }: { hypercertId: string }) => {
const [ref, isHovered] = useHover();
Expand All @@ -14,18 +15,25 @@ export const HypercertTile = ({ hypercertId }: { hypercertId: string }) => {
flexDir={"column"}
outline={isHovered ? "1px solid black" : "none"}
cursor={"pointer"}
position={"relative"}
>
<Image
alt="Detail image for hypercert"
src={hypercert?.metadata?.image}
width={"100%"}
height={"100%"}
objectFit={"cover"}
backgroundColor={isHovered ? "black" : "none"}
backgroundColor={isHovered ? "black" : undefined}
/>
<Box backgroundColor={isHovered ? "white" : "none"} py={5} px={3}>
<MarketplaceStats hypercertId={hypercertId} />
</Box>
<ChainBadge
hypercertId={hypercertId}
position={"absolute"}
right={2}
top={2}
/>
</Flex>
);
};
38 changes: 30 additions & 8 deletions components/marketplace/list-for-sale-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
TabPanel,
TabPanels,
Tabs,
Tooltip,
useDisclosure,
} from "@chakra-ui/react";
import { useFetchMarketplaceOrdersForHypercert } from "@/hooks/marketplace/useFetchMarketplaceOrdersForHypercert";
Expand All @@ -19,6 +20,7 @@ import {
import { CreateOrderForm } from "@/components/marketplace/create-order-form";
import React from "react";
import { CreateFractionalOrderForm } from "@/components/marketplace/create-fractional-order-form";
import { useHypercertClient } from "@/components/providers";

type Props = {
hypercertId: string;
Expand Down Expand Up @@ -54,16 +56,36 @@ export const ListForSaleButton = React.forwardRef<HTMLButtonElement, Props>(
onOpen();
};

const client = useHypercertClient();

const disabled =
!client || !client.isClaimOrFractionOnConnectedChain(hypercertId);

const getToolTipMessage = () => {
if (!client) {
return "Please connect your wallet to list for sale";
}

if (!client.isClaimOrFractionOnConnectedChain(hypercertId)) {
return "This hypercert is not on the connected chain";
}

return "";
};

return (
<>
<Button
ref={ref}
variant="blackAndWhite"
onClick={onClickButton}
{...props}
>
{text}
</Button>
<Tooltip label={getToolTipMessage()}>
<Button
ref={ref}
isDisabled={disabled}
variant="blackAndWhite"
onClick={onClickButton}
{...props}
>
{text}
</Button>
</Tooltip>
<Modal isOpen={isOpen} onClose={onClose} isCentered>
<ModalOverlay />
<ModalContent maxH={"80vh"} overflow={"auto"}>
Expand Down
29 changes: 23 additions & 6 deletions components/minting/blueprint-minter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ import {
import { useInteractionModal } from "@/components/interaction-modal";
import { useAddress } from "@/hooks/useAddress";
import { useGetAuthenticatedClient } from "@/hooks/useGetAuthenticatedClient";
import { useChainId } from "wagmi";
import { useChainId, useWalletClient } from "wagmi";
import { Alert, AlertDescription, AlertIcon } from "@chakra-ui/alert";
import { TransactionReceipt } from "viem";
import { NUMBER_OF_UNITS_IN_HYPERCERT } from "@/config";
import { useEthersProvider } from "@/hooks/useEthersProvider";
import { constructClaimIdFromCreateClaimContractReceipt } from "@/utils/constructClaimIdFromCreateClaimContractReceipt";
import { waitForTransactionReceipt } from "viem/actions";

const formValuesToHypercertMetadata = (
values: MintingFormValues,
Expand Down Expand Up @@ -99,9 +99,9 @@ export const BlueprintMinter = ({
const { onOpen, setStep, onClose } = useInteractionModal();
const address = useAddress();
const getClient = useGetAuthenticatedClient();
const provider = useEthersProvider();

const { data: walletClient } = useWalletClient();
const chainId = useChainId();

const isCorrectChain = chainId === blueprint?.data?.registries?.chain_id;

const [previewImageSrc, setPreviewImageSrc] = useState<string | undefined>(
Expand Down Expand Up @@ -152,6 +152,17 @@ export const BlueprintMinter = ({
return;
}

if (!walletClient) {
toast({
title: "Error",
description: "Wallet client not initialized",
status: "error",
duration: 9000,
isClosable: true,
});
return;
}

const supabase = await getClient();

if (!supabase) {
Expand Down Expand Up @@ -206,8 +217,13 @@ export const BlueprintMinter = ({
NUMBER_OF_UNITS_IN_HYPERCERT,
TransferRestrictions.FromCreatorOnly,
);
// @ts-ignore
transactionReceipt = await provider.waitForTransaction(transactionHash);

if (!transactionHash) {
throw new Error("No transaction hash");
}
transactionReceipt = await waitForTransactionReceipt(walletClient, {
hash: transactionHash,
});
} catch (e) {
console.error(e);
toast({
Expand All @@ -226,6 +242,7 @@ export const BlueprintMinter = ({
try {
claimId = constructClaimIdFromCreateClaimContractReceipt(
transactionReceipt!,
chainId,
);
} catch (e) {
console.error(e);
Expand Down
27 changes: 17 additions & 10 deletions components/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,30 +133,37 @@ export const HypercertClientProvider = ({
const chainId = useChainId();
const { data: walletClient } = useWalletClient({});
const publicClient = usePublicClient({});
const defaultChain = 11155111;

const [client, setClient] = useState<HypercertClient>();

useEffect(() => {
if (!chainId) {
return;
}
const chainToUse = () => {
if (chainOverride) {
return chainOverride;
}

if (chainId === mainnet.id) {
// Temporary fix, remove when mainnet is supported
// Mainnet had to be enabled to get ENS lookup working
setClient(undefined);
return;
}
if (chainId === 1) {
return defaultChain;
}

if (chainId) {
return chainId;
}

return defaultChain;
};

const hypercertClient = new HypercertClient({
chain: { id: chainOverride || chainId },
chain: { id: chainToUse() },
nftStorageToken: NFT_STORAGE_TOKEN,
web3StorageToken: WEB3_STORAGE_TOKEN,
easContractAddress: EAS_CONTRACT_ADDRESS,
// @ts-ignore
walletClient,
// @ts-ignore
publicClient,
indexerEnvironment: "all",
});

setClient(hypercertClient);
Expand Down
18 changes: 12 additions & 6 deletions hooks/marketplace/useBuyFractionalMakerAsk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const useBuyFractionalMakerAsk = () => {
const provider = useEthersProvider();
const signer = useEthersSigner();
const address = useAddress();
const currentAllowance = useGetCurrentERC20Allowance();
const getCurrentERC20Allowance = useGetCurrentERC20Allowance();

return useMutation({
mutationKey: ["buyFractionalMakerAsk"],
Expand Down Expand Up @@ -47,14 +47,17 @@ export const useBuyFractionalMakerAsk = () => {
description: "Setting up order execution",
},
{
title: "Setting approval",
title: "ERC20",
description: "Setting approval",
},
{
title: "Transfer manager",
description: "Approving transfer manager",
},
{
title: "Awaiting buy signature",
description: "Awaiting buy signature",
},

{
title: "Awaiting confirmation",
description: "Awaiting confirmation",
Expand All @@ -76,21 +79,24 @@ export const useBuyFractionalMakerAsk = () => {
);

try {
setStep("Setting approval");
setStep("ERC20");
const currentAllowance = await getCurrentERC20Allowance(
order.currency as `0x${string}`,
);
if (currentAllowance < BigInt(order.price) * BigInt(unitAmount)) {
const approveTx = await hypercertExchangeClient.approveErc20(
hypercertExchangeClient.addresses.WETH,
order.currency,
BigInt(order.price) * BigInt(unitAmount),
);
await waitForTransactionReceipt(walletClientData, {
hash: approveTx.hash as `0x${string}`,
});
}

setStep("Transfer manager");
const isTransferManagerApproved =
await hypercertExchangeClient.isTransferManagerApproved();
if (!isTransferManagerApproved) {
setStep("Setting approval");
const transferManagerApprove = await hypercertExchangeClient
.grantTransferManagerApproval()
.call();
Expand Down
16 changes: 11 additions & 5 deletions hooks/marketplace/useBuyMakerBid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const useBuyMakerBid = () => {
const provider = useEthersProvider();
const signer = useEthersSigner();
const address = useAddress();
const currentAllowance = useGetCurrentERC20Allowance();
const getCurrentAllowance = useGetCurrentERC20Allowance();

return useMutation({
mutationKey: ["buyMakerBid"],
Expand All @@ -38,14 +38,17 @@ export const useBuyMakerBid = () => {
description: "Setting up order execution",
},
{
title: "Setting approval",
title: "ERC20",
description: "Setting approval",
},
{
title: "Transfer manager",
description: "Approving transfer manager",
},
{
title: "Awaiting buy signature",
description: "Awaiting buy signature",
},

{
title: "Awaiting confirmation",
description: "Awaiting confirmation",
Expand All @@ -58,7 +61,10 @@ export const useBuyMakerBid = () => {
const takerOrder = lr.createTaker(order, address);

try {
setStep("Setting approval");
const currentAllowance = await getCurrentAllowance(
order.currency as `0x${string}`,
);
setStep("ERC20");
if (currentAllowance < BigInt(order.price)) {
const approveTx = await lr.approveErc20(
lr.addresses.WETH,
Expand All @@ -71,7 +77,7 @@ export const useBuyMakerBid = () => {

const isTransferManagerApproved = await lr.isTransferManagerApproved();
if (!isTransferManagerApproved) {
setStep("Setting approval");
setStep("Transfer manager");
const transferManagerApprove = await lr
.grantTransferManagerApproval()
.call();
Expand Down
Loading

0 comments on commit 77546fc

Please sign in to comment.