From 6580e4d19151f2a8e373888934637560d183c34e Mon Sep 17 00:00:00 2001 From: Bruno Menezes Date: Tue, 7 May 2024 22:07:38 +1200 Subject: [PATCH] Feature: ERC-1155 Transfers (#143) --- apps/web/graphql/queries.graphql | 6 + apps/web/src/app/layout.tsx | 6 +- apps/web/src/components/BlockExplorerLink.tsx | 45 + apps/web/src/components/sendTransaction.tsx | 76 +- apps/web/src/hooks/useSearchMultiTokens.tsx | 23 + apps/web/src/providers/styleProvider.tsx | 2 - apps/web/src/providers/theme.tsx | 6 + apps/web/src/providers/walletProvider.tsx | 5 +- .../components/BlockExplorerLink.test.tsx | 71 + .../test/components/sendTransaction.mocks.ts | 291 ++++ .../test/components/sendTransaction.test.tsx | 153 +- apps/web/vitest.config.mts | 1 + packages/rollups-wagmi/abi/ERC1155.ts | 379 +++++ packages/rollups-wagmi/wagmi.config.ts | 7 +- packages/ui/package.json | 2 + packages/ui/src/DepositFormTypes.ts | 8 + .../src/ERC1155DepositForm/AdvancedFields.tsx | 31 + .../ERC1155DepositForm/DepositBatchReview.tsx | 96 ++ .../ERC1155DepositForm/DepositFormBatch.tsx | 416 ++++++ .../ERC1155DepositForm/DepositFormSingle.tsx | 394 +++++ .../ui/src/ERC1155DepositForm/TokenFields.tsx | 343 +++++ .../ui/src/ERC1155DepositForm/context.tsx | 46 + packages/ui/src/ERC1155DepositForm/index.tsx | 30 + packages/ui/src/ERC1155DepositForm/types.ts | 11 + .../useGetTokenMetadata.tsx | 175 +++ .../ui/src/ERC1155DepositForm/validations.ts | 111 ++ packages/ui/src/ERC165Identifiers.ts | 1 + packages/ui/src/TransactionState.ts | 22 + packages/ui/src/index.tsx | 8 +- .../ERC1155DepositForm/BatchDeposit.test.tsx | 1265 +++++++++++++++++ .../ERC1155DepositForm/SingleDeposit.test.tsx | 1040 ++++++++++++++ packages/ui/test/ERC1155DepositForm/stubs.ts | 28 + 32 files changed, 4963 insertions(+), 135 deletions(-) create mode 100644 apps/web/src/components/BlockExplorerLink.tsx create mode 100644 apps/web/src/hooks/useSearchMultiTokens.tsx create mode 100644 apps/web/test/components/BlockExplorerLink.test.tsx create mode 100644 apps/web/test/components/sendTransaction.mocks.ts create mode 100644 packages/rollups-wagmi/abi/ERC1155.ts create mode 100644 packages/ui/src/DepositFormTypes.ts create mode 100644 packages/ui/src/ERC1155DepositForm/AdvancedFields.tsx create mode 100644 packages/ui/src/ERC1155DepositForm/DepositBatchReview.tsx create mode 100644 packages/ui/src/ERC1155DepositForm/DepositFormBatch.tsx create mode 100644 packages/ui/src/ERC1155DepositForm/DepositFormSingle.tsx create mode 100644 packages/ui/src/ERC1155DepositForm/TokenFields.tsx create mode 100644 packages/ui/src/ERC1155DepositForm/context.tsx create mode 100644 packages/ui/src/ERC1155DepositForm/index.tsx create mode 100644 packages/ui/src/ERC1155DepositForm/types.ts create mode 100644 packages/ui/src/ERC1155DepositForm/useGetTokenMetadata.tsx create mode 100644 packages/ui/src/ERC1155DepositForm/validations.ts create mode 100644 packages/ui/src/ERC165Identifiers.ts create mode 100644 packages/ui/src/TransactionState.ts create mode 100644 packages/ui/test/ERC1155DepositForm/BatchDeposit.test.tsx create mode 100644 packages/ui/test/ERC1155DepositForm/SingleDeposit.test.tsx create mode 100644 packages/ui/test/ERC1155DepositForm/stubs.ts diff --git a/apps/web/graphql/queries.graphql b/apps/web/graphql/queries.graphql index bcd825002..26f7224b7 100644 --- a/apps/web/graphql/queries.graphql +++ b/apps/web/graphql/queries.graphql @@ -85,6 +85,12 @@ query tokens($limit: Int, $where: TokenWhereInput) { } } +query multiTokens($limit: Int, $where: MultiTokenWhereInput) { + multiTokens(limit: $limit, where: $where) { + id + } +} + fragment ApplicationItem on Application { id owner diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 7320cb442..39bf7678d 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -1,5 +1,6 @@ import { ColorSchemeScript } from "@mantine/core"; import "@mantine/core/styles.css"; +import { Notifications } from "@mantine/notifications"; import "@mantine/notifications/styles.css"; import { Analytics } from "@vercel/analytics/react"; import { Metadata } from "next"; @@ -34,7 +35,10 @@ const Layout: FC = ({ children }) => { - {children} + <> + + {children} + diff --git a/apps/web/src/components/BlockExplorerLink.tsx b/apps/web/src/components/BlockExplorerLink.tsx new file mode 100644 index 000000000..c3f3f1ce1 --- /dev/null +++ b/apps/web/src/components/BlockExplorerLink.tsx @@ -0,0 +1,45 @@ +import { Anchor, Group, Text, rem } from "@mantine/core"; +import { anyPass, equals } from "ramda"; +import { FC } from "react"; +import { TbExternalLink } from "react-icons/tb"; +import { useConfig } from "wagmi"; + +interface BlockExplorerLinkProps { + value: string; + type: "tx" | "block" | "address"; +} + +const isTxOrAddress = anyPass([equals("tx"), equals("address")]); + +/** + * + * Works in conjuction with Wagmi. It requires a Wagmi-Provider to work as expected. + * When running devnet it will not render a block-explorer link. + * + */ +export const BlockExplorerLink: FC = ({ + value, + type, +}) => { + const config = useConfig(); + const explorerUrl = config.chains[0].blockExplorers?.default.url; + + if (!explorerUrl) return; + + const shouldShorten = isTxOrAddress(type); + + const text = shouldShorten + ? `${value.slice(0, 8)}...${value.slice(-6)}` + : value; + + const href = `${explorerUrl}/${type}/${value}`; + + return ( + + + {text} + + + + ); +}; diff --git a/apps/web/src/components/sendTransaction.tsx b/apps/web/src/components/sendTransaction.tsx index 191f299e4..30afe9f32 100644 --- a/apps/web/src/components/sendTransaction.tsx +++ b/apps/web/src/components/sendTransaction.tsx @@ -1,5 +1,7 @@ "use client"; import { + DepositFormSuccessData, + ERC1155DepositForm, ERC20DepositForm, ERC721DepositForm, EtherDepositForm, @@ -7,16 +9,19 @@ import { } from "@cartesi/rollups-explorer-ui"; import { Select } from "@mantine/core"; import { useDebouncedValue } from "@mantine/hooks"; +import { notifications } from "@mantine/notifications"; import { FC, useCallback, useState } from "react"; import { useSearchApplications } from "../hooks/useSearchApplications"; +import { useSearchMultiTokens } from "../hooks/useSearchMultiTokens"; import { useSearchTokens } from "../hooks/useSearchTokens"; -import { notifications } from "@mantine/notifications"; +import { BlockExplorerLink } from "./BlockExplorerLink"; export type DepositType = | "ether" | "erc20" | "erc721" | "erc1155" + | "erc1155Batch" | "relay" | "input"; @@ -24,21 +29,34 @@ interface DepositProps { initialDepositType?: DepositType; } +const DEBOUNCE_TIME = 400 as const; + const SendTransaction: FC = ({ initialDepositType = "ether", }) => { const [depositType, setDepositType] = useState(initialDepositType); const [applicationId, setApplicationId] = useState(""); + const [multiTokenId, setMultiTokenId] = useState(""); const [tokenId, setTokenId] = useState(""); - const [debouncedApplicationId] = useDebouncedValue(applicationId, 400); - const [debouncedTokenId] = useDebouncedValue(tokenId, 400); + const [debouncedApplicationId] = useDebouncedValue( + applicationId, + DEBOUNCE_TIME, + ); + const [debouncedTokenId] = useDebouncedValue(tokenId, DEBOUNCE_TIME); + const [debouncedMultiTokenId] = useDebouncedValue( + multiTokenId, + DEBOUNCE_TIME, + ); const { applications, fetching } = useSearchApplications({ address: debouncedApplicationId, }); const { tokens } = useSearchTokens({ address: debouncedTokenId, }); + const { multiTokens } = useSearchMultiTokens({ + address: debouncedMultiTokenId, + }); const onDepositErc721 = useCallback(() => { notifications.show({ @@ -48,6 +66,33 @@ const SendTransaction: FC = ({ }); }, []); + const onSuccess = useCallback( + ({ receipt, type }: DepositFormSuccessData) => { + const message = receipt?.transactionHash + ? { + message: ( + + ), + title: `${type} transfer completed`, + } + : { message: `${type} transfer completed.` }; + + notifications.show({ + withCloseButton: true, + autoClose: false, + color: "green", + ...message, + }); + + setMultiTokenId(""); + setApplicationId(""); + }, + [], + ); + return ( <>