From c6f4ddedda384196cdcd33e1a15eb452826a509c Mon Sep 17 00:00:00 2001 From: gconnect Date: Wed, 29 May 2024 16:30:44 +0100 Subject: [PATCH] uopdated the voucher code made it reuasable and added ERC1155 to the tabs in balance component --- .../frontend/next-app/app/cartesi/Inspect.tsx | 12 -- .../frontend/next-app/app/cartesi/Notices.tsx | 21 +- apps/frontend/next-app/app/cartesi/Portals.ts | 26 +++ .../frontend/next-app/app/cartesi/Reports.tsx | 4 +- .../next-app/app/cartesi/Vouchers.tsx | 199 +++--------------- .../app/cartesi/hooks/useInspectCall.tsx | 19 +- .../app/cartesi/hooks/useVouchers.tsx | 119 +++++++++++ .../next-app/app/component/Balance.tsx | 1 + .../next-app/app/component/Transfers.tsx | 2 +- apps/frontend/next-app/app/layout.tsx | 26 +++ .../next-app/app/utils/customAlert.tsx | 2 +- .../next-app/app/utils/rollupsProvider.tsx | 32 --- 12 files changed, 216 insertions(+), 247 deletions(-) create mode 100644 apps/frontend/next-app/app/cartesi/hooks/useVouchers.tsx delete mode 100644 apps/frontend/next-app/app/utils/rollupsProvider.tsx diff --git a/apps/frontend/next-app/app/cartesi/Inspect.tsx b/apps/frontend/next-app/app/cartesi/Inspect.tsx index f3bd1a9..b12ab93 100644 --- a/apps/frontend/next-app/app/cartesi/Inspect.tsx +++ b/apps/frontend/next-app/app/cartesi/Inspect.tsx @@ -1,15 +1,3 @@ -// Copyright 2022 Cartesi Pte. Ltd. - -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the license at http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - import React from "react"; import { ethers } from "ethers"; import { useInspectCall } from "../cartesi/hooks/useInspectCall"; diff --git a/apps/frontend/next-app/app/cartesi/Notices.tsx b/apps/frontend/next-app/app/cartesi/Notices.tsx index 31a4c38..0952a36 100644 --- a/apps/frontend/next-app/app/cartesi/Notices.tsx +++ b/apps/frontend/next-app/app/cartesi/Notices.tsx @@ -1,28 +1,15 @@ "use client" -// Copyright 2022 Cartesi Pte. Ltd. -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the license at http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -import { ethers } from "ethers"; import React, {useEffect} from "react"; import { useNotices } from "./hooks/useNotices"; -import { useRollups } from "./hooks/useRollups"; -import { DAPP_ADDRESS } from "../utils/constants"; + export const Notices: React.FC = () => { const {loading, error, data, notices, refetch } = useNotices() useEffect(() => { refetch({ requestPolicy: 'network-only' }); - }, []); + }, [refetch]); if (loading) return

Loading...

; if (error) return

Oh no... {error.message}

; @@ -31,8 +18,8 @@ export const Notices: React.FC = () => { return (
- diff --git a/apps/frontend/next-app/app/cartesi/Portals.ts b/apps/frontend/next-app/app/cartesi/Portals.ts index 14537e5..427b23b 100644 --- a/apps/frontend/next-app/app/cartesi/Portals.ts +++ b/apps/frontend/next-app/app/cartesi/Portals.ts @@ -393,3 +393,29 @@ export const transferErc1155BatchToPortal = async ( } }; +export const executeVoucher = async ( + rollups: RollupsContracts | undefined, + signer: JsonRpcSigner | undefined, + setVoucherToExecute: Function, + voucher: any + ) => { + if (rollups && !!voucher.proof) { + + const newVoucherToExecute = {...voucher}; + try { + const tx = await rollups.dappContract.executeVoucher( voucher.destination,voucher.payload,voucher.proof); + const trans = await signer?.sendTransaction(tx) + const receipt = await trans?.wait(1); + newVoucherToExecute.msg = `voucher executed! (tx="${receipt?.hash}")`; + const event = (await rollups.dappContract.queryFilter(rollups.dappContract.filters.VoucherExecuted(), receipt?.blockHash)).pop(); + if (event) { + newVoucherToExecute.msg = `${newVoucherToExecute.msg} - resulting events: ${JSON.stringify(receipt?.hash)}`; + newVoucherToExecute.executed = await rollups.dappContract.wasVoucherExecuted(toBigInt(voucher.input.index),toBigInt(voucher.index)); + } + } catch (e) { + newVoucherToExecute.msg = `COULD NOT EXECUTE VOUCHER: ${JSON.stringify(e)}`; + console.log(`COULD NOT EXECUTE VOUCHER: ${JSON.stringify(e)}`); + } + setVoucherToExecute(newVoucherToExecute); + } +} \ No newline at end of file diff --git a/apps/frontend/next-app/app/cartesi/Reports.tsx b/apps/frontend/next-app/app/cartesi/Reports.tsx index a91d469..2ef9dac 100644 --- a/apps/frontend/next-app/app/cartesi/Reports.tsx +++ b/apps/frontend/next-app/app/cartesi/Reports.tsx @@ -55,12 +55,12 @@ export const Reports: React.FC = () => { - {reports.length === 0 && ( + {reports && reports.length === 0 && ( )} - {reports.map((n: any) => ( + {reports && reports.map((n: any) => ( {/* */} diff --git a/apps/frontend/next-app/app/cartesi/Vouchers.tsx b/apps/frontend/next-app/app/cartesi/Vouchers.tsx index 94e81e8..f50097c 100644 --- a/apps/frontend/next-app/app/cartesi/Vouchers.tsx +++ b/apps/frontend/next-app/app/cartesi/Vouchers.tsx @@ -1,19 +1,7 @@ "use client" -// Copyright 2022 Cartesi Pte. Ltd. -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the license at http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -import { ethers } from "ethers"; +import { ethers, toBigInt } from "ethers"; import React, { useEffect } from "react"; -import { useVouchersQuery, useVoucherQuery } from "./generated/graphql"; import { useRollups } from "./hooks/useRollups"; import { Table, @@ -26,176 +14,50 @@ import { Text } from '@chakra-ui/react' import { useEthersSigner } from "../utils/useEtherSigner"; - -type Voucher = { - id: string; - index: number; - destination: string; - input: any, //{index: number; epoch: {index: number; } - payload: string; - proof: any; - executed: any; -}; - -interface IVoucherPropos { +import { Voucher, useVouchers } from "./hooks/useVouchers"; +import { executeVoucher } from "./Portals"; +interface IVoucherProps { dappAddress: string } -export const Vouchers: React.FC = (propos) => { - const [result,reexecuteQuery] = useVouchersQuery(); +export const Vouchers: React.FC = (props) => { + const {loading, error, data, vouchers, voucherResult, refetch } = useVouchers() + const [voucherToFetch, setVoucherToFetch] = React.useState([0,0]); - const [voucherResult,reexecuteVoucherQuery] = useVoucherQuery({ - variables: { voucherIndex: voucherToFetch[0], inputIndex: voucherToFetch[1] }//, pause: !!voucherIdToFetch - }); + const [voucherToExecute, setVoucherToExecute] = React.useState(); - const { data, fetching, error } = result; - const rollups = useRollups(propos.dappAddress); + const rollups = useRollups(props.dappAddress); const signer = useEthersSigner() - const provider = signer?.provider const getProof = async (voucher: Voucher) => { - setVoucherToFetch([voucher.index,voucher.input.index]); - reexecuteVoucherQuery({ requestPolicy: 'network-only' }); + setVoucherToFetch([voucher.index, voucher.input.index]); + refetch({ requestPolicy: 'network-only' }); }; - const executeVoucher = async (voucher: any) => { - if (rollups && !!voucher.proof) { - - const newVoucherToExecute = {...voucher}; - try { - const tx = await rollups.dappContract.executeVoucher( voucher.destination,voucher.payload,voucher.proof); - const trans = await signer?.sendTransaction(tx) - const receipt = await trans?.wait(1); - newVoucherToExecute.msg = `voucher executed! (tx="${receipt?.hash}")`; - if (receipt.events) { - newVoucherToExecute.msg = `${newVoucherToExecute.msg} - resulting events: ${JSON.stringify(receipt.events)}`; - newVoucherToExecute.executed = await rollups.dappContract.wasVoucherExecuted(BigNumber.from(voucher.input.index),BigNumber.from(voucher.index)); - } - } catch (e) { - newVoucherToExecute.msg = `COULD NOT EXECUTE VOUCHER: ${JSON.stringify(e)}`; - console.log(`COULD NOT EXECUTE VOUCHER: ${JSON.stringify(e)}`); - } - setVoucherToExecute(newVoucherToExecute); - } - } - useEffect( () => { - const setVoucher = async (voucher: any) => { + const setVoucher = async (voucher: Voucher) => { if (rollups) { - voucher.executed = await rollups.dappContract.wasVoucherExecuted(BigNumber.from(voucher.input.index),BigNumber.from(voucher.index)); + voucher.executed = await rollups.dappContract.wasVoucherExecuted(toBigInt(voucher.input.index),toBigInt(voucher.index)); } + console.log( voucher.executed) setVoucherToExecute(voucher); } - if (!voucherResult.fetching && voucherResult.data){ - setVoucher(voucherResult.data.voucher); + if (!voucherResult?.executed && voucherResult){ + setVoucher(voucherResult); } - },[voucherResult, rollups]); - - if (fetching) return

Loading...

; - if (error) return

Oh no... {error.message}

; + },[voucherResult, voucherToFetch, rollups]); + - if (!data || !data.vouchers) return

No vouchers

; + if (loading) return

Loading...

; + if (error) return

Oh no... {error.message}

; - const vouchers: Voucher[] = data.vouchers.edges.map((node: any) => { - const n = node.node; - let payload = n?.payload; - let inputPayload = n?.input.payload; - if (inputPayload) { - try { - inputPayload = ethers.toUtf8String(inputPayload); - } catch (e) { - inputPayload = inputPayload + " (hex)"; - } - } else { - inputPayload = "(empty)"; - } - if (payload) { - const decoder = new ethers.AbiCoder(); - const selector = decoder.decode(["bytes4"], payload)[0]; - payload = ethers.dataSlice(payload,4); - try { - switch(selector) { - case '0xa9059cbb': { - // erc20 transfer; - const decode = decoder.decode(["address","uint256"], payload); - payload = `Erc20 Transfer - Amount: ${ethers.formatEther(decode[1])} - Address: ${decode[0]}`; - break; - } - case '0x42842e0e': { - //erc721 safe transfer; - const decode = decoder.decode(["address","address","uint256"], payload); - payload = `Erc721 Transfer - Id: ${decode[2]} - Address: ${decode[1]}`; - break; - } - case '0x522f6815': { - //ether transfer; - const decode2 = decoder.decode(["address", "uint256"], payload) - payload = `Ether Transfer - Amount: ${ethers.formatEther(decode2[1])} (Native eth) - Address: ${decode2[0]}`; - break; - } - case '0xf242432a': { - //erc155 single safe transfer; - const decode = decoder.decode(["address","address","uint256","uint256"], payload); - payload = `Erc1155 Single Transfer - Id: ${decode[2]} Amount: ${decode[3]} - Address: ${decode[1]}`; - break; - } - case '0x2eb2c2d6': { - //erc155 Batch safe transfer; - const decode = decoder.decode(["address","address","uint256[]","uint256[]"], payload); - payload = `Erc1155 Batch Transfer - Ids: ${decode[2]} Amounts: ${decode[3]} - Address: ${decode[1]}`; - break; - } - case '0xd0def521': { - //erc721 mint; - const decode = decoder.decode(["address","string"], payload); - payload = `Mint Erc721 - String: ${decode[1]} - Address: ${decode[0]}`; - break; - } - case '0x755edd17': { - //erc721 mintTo; - const decode = decoder.decode(["address"], payload); - payload = `Mint Erc721 - Address: ${decode[0]}`; - break; - } - case '0x6a627842': { - //erc721 mint; - const decode = decoder.decode(["address"], payload); - payload = `Mint Erc721 - Address: ${decode[0]}`; - break; - } - default: { - break; - } - } - } catch (e) { - console.log(e); - } - } else { - payload = "(empty)"; - } - return { - id: `${n?.id}`, - index: parseInt(n?.index), - destination: `${n?.destination ?? ""}`, - payload: `${payload}`, - input: n ? {index:n.input.index,payload: inputPayload} : {}, - proof: null, - executed: null, - }; - }).sort((b: any, a: any) => { - if (a.input.index === b.input.index) { - return b.index - a.index; - } else { - return b.input.index - a.input.index; - } - }); + if (!data || !data.vouchers) return

No vouchers

; - // const forceUpdate = useForceUpdate(); return ( -
+

- {voucherToExecute ? @@ -213,13 +75,16 @@ export const Vouchers: React.FC = (propos) => {
- - + + {/* */} - + {/* */} {/* */} {/* */} @@ -241,12 +106,12 @@ export const Vouchers: React.FC = (propos) => { - {vouchers.length === 0 && ( + {vouchers && vouchers.length === 0 && ( )} - {vouchers.map((n: any) => ( + {vouchers && vouchers.map((n: any) => ( {/* diff --git a/apps/frontend/next-app/app/cartesi/hooks/useInspectCall.tsx b/apps/frontend/next-app/app/cartesi/hooks/useInspectCall.tsx index 18404d9..640d28b 100644 --- a/apps/frontend/next-app/app/cartesi/hooks/useInspectCall.tsx +++ b/apps/frontend/next-app/app/cartesi/hooks/useInspectCall.tsx @@ -3,7 +3,7 @@ import configFile from '../config.json'; import { toHex } from 'viem'; import { useState } from 'react'; import { useAccount } from 'wagmi'; -import toast from 'react-hot-toast'; +import { errorAlert, successAlert } from '@/app/utils/customAlert'; const config: any = configFile; @@ -23,7 +23,7 @@ export const useInspectCall = () => { const inspectCall = async (payload: string) => { try { if (hexData) { - const uint8array = ethers.utils.arrayify(payload); + const uint8array = ethers.getBytes(payload); payload = new TextDecoder().decode(uint8array); } @@ -61,21 +61,10 @@ export const useInspectCall = () => { const reportData: any = JSON.parse(decode); console.log("Report data: ", reportData); setDecodedReports(reportData); - - toast.success(reportData, { - position: 'bottom-right', - style: { - paddingRight: '40px', - }, - }) + successAlert(reportData) } catch (error: any) { console.error("Error fetching inspect data:", error) - toast.error(error.message, { - position: 'bottom-right', - style: { - paddingRight: '40px', - }, - }); + errorAlert(error) } }; diff --git a/apps/frontend/next-app/app/cartesi/hooks/useVouchers.tsx b/apps/frontend/next-app/app/cartesi/hooks/useVouchers.tsx new file mode 100644 index 0000000..9d5cf2b --- /dev/null +++ b/apps/frontend/next-app/app/cartesi/hooks/useVouchers.tsx @@ -0,0 +1,119 @@ +"use client" +import { useQuery } from '@apollo/client' +import { ethers } from 'ethers' +import { useState } from 'react' +import { VouchersByInputDocument } from '../generated/graphql' + +export type Voucher = { + id: string; + index: number; + destination: string; + input: any, //{index: number; epoch: {index: number; } + payload: string; + proof: any; + executed: any; +}; + +export const useVouchers = () => { + const [cursor] = useState(null) + const { loading, error, data, refetch } = useQuery(VouchersByInputDocument, { + variables: { cursor }, + pollInterval: 0, + }) + + const vouchers: Voucher[] = data && data.vouchers.edges.map((node: any) => { + const n = node.node; + let payload = n?.payload; + let inputPayload = n?.input.payload; + if (inputPayload) { + try { + inputPayload = ethers.toUtf8String(inputPayload); + } catch (e) { + inputPayload = inputPayload + " (hex)"; + } + } else { + inputPayload = "(empty)"; + } + if (payload) { + const decoder = new ethers.AbiCoder(); + const selector = decoder.decode(["bytes4"], payload)[0]; + payload = ethers.dataSlice(payload,4); + try { + switch(selector) { + case '0xa9059cbb': { + // erc20 transfer; + const decode = decoder.decode(["address","uint256"], payload); + payload = `Erc20 Transfer - Amount: ${ethers.formatEther(decode[1])} - Address: ${decode[0]}`; + break; + } + case '0x42842e0e': { + //erc721 safe transfer; + const decode = decoder.decode(["address","address","uint256"], payload); + payload = `Erc721 Transfer - Id: ${decode[2]} - Address: ${decode[1]}`; + break; + } + case '0x522f6815': { + //ether transfer; + const decode2 = decoder.decode(["address", "uint256"], payload) + payload = `Ether Transfer - Amount: ${ethers.formatEther(decode2[1])} (Native eth) - Address: ${decode2[0]}`; + break; + } + case '0xf242432a': { + //erc155 single safe transfer; + const decode = decoder.decode(["address","address","uint256","uint256"], payload); + payload = `Erc1155 Single Transfer - Id: ${decode[2]} Amount: ${decode[3]} - Address: ${decode[1]}`; + break; + } + case '0x2eb2c2d6': { + //erc155 Batch safe transfer; + const decode = decoder.decode(["address","address","uint256[]","uint256[]"], payload); + payload = `Erc1155 Batch Transfer - Ids: ${decode[2]} Amounts: ${decode[3]} - Address: ${decode[1]}`; + break; + } + case '0xd0def521': { + //erc721 mint; + const decode = decoder.decode(["address","string"], payload); + payload = `Mint Erc721 - String: ${decode[1]} - Address: ${decode[0]}`; + break; + } + case '0x755edd17': { + //erc721 mintTo; + const decode = decoder.decode(["address"], payload); + payload = `Mint Erc721 - Address: ${decode[0]}`; + break; + } + case '0x6a627842': { + //erc721 mint; + const decode = decoder.decode(["address"], payload); + payload = `Mint Erc721 - Address: ${decode[0]}`; + break; + } + default: { + break; + } + } + } catch (e) { + console.log(e); + } + } else { + payload = "(empty)"; + } + return { + id: `${n?.id}`, + index: parseInt(n?.index), + destination: `${n?.destination ?? ""}`, + payload: `${payload}`, + input: n ? {index:n.input.index,payload: inputPayload} : {}, + proof: null, + executed: null, + }; +}).sort((b: any, a: any) => { + if (a.input.index === b.input.index) { + return b.index - a.index; + } else { + return b.input.index - a.input.index; + } +}); + const voucherResult = vouchers && vouchers[0] + return { loading, error, data, vouchers, voucherResult, refetch } +} diff --git a/apps/frontend/next-app/app/component/Balance.tsx b/apps/frontend/next-app/app/component/Balance.tsx index 84ab7bd..259b8ad 100644 --- a/apps/frontend/next-app/app/component/Balance.tsx +++ b/apps/frontend/next-app/app/component/Balance.tsx @@ -27,6 +27,7 @@ export const Balance: React.FC = () => { + diff --git a/apps/frontend/next-app/app/component/Transfers.tsx b/apps/frontend/next-app/app/component/Transfers.tsx index 550e895..e617695 100644 --- a/apps/frontend/next-app/app/component/Transfers.tsx +++ b/apps/frontend/next-app/app/component/Transfers.tsx @@ -360,7 +360,7 @@ const clear1155Batch = () => {
} - {dappRelayedAddress && } + {dappRelayedAddress && } diff --git a/apps/frontend/next-app/app/layout.tsx b/apps/frontend/next-app/app/layout.tsx index 51a35dd..ea3b817 100644 --- a/apps/frontend/next-app/app/layout.tsx +++ b/apps/frontend/next-app/app/layout.tsx @@ -2,6 +2,7 @@ import type { Metadata } from "next"; import { Inter } from "next/font/google"; import "./globals.css"; +import { useMemo } from "react"; import '@rainbow-me/rainbowkit/styles.css'; import { Providers } from './utils/providers'; import Header from "./component/Header"; @@ -10,6 +11,14 @@ import { ChakraProvider } from "@chakra-ui/react"; import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client' import { Toaster } from 'react-hot-toast' import { API_BASE_URL } from "./utils/constants"; +import { + UrqlProvider, + ssrExchange, + cacheExchange, + fetchExchange, + createClient, +} from '@urql/next'; + const inter = Inter({ subsets: ["latin"] }); // export const metadata: Metadata = { @@ -28,10 +37,26 @@ export default function RootLayout({ cache: new InMemoryCache(), }) + const [client, ssr] = useMemo(() => { + const ssr = ssrExchange({ + isClient: typeof window !== 'undefined', + }); + const client = createClient({ + url: API_BASE_URL, + exchanges: [cacheExchange, ssr, fetchExchange], + suspense: true, + }); + + return [client, ssr]; + }, []); + + + return ( +
@@ -39,6 +64,7 @@ export default function RootLayout({
+ diff --git a/apps/frontend/next-app/app/utils/customAlert.tsx b/apps/frontend/next-app/app/utils/customAlert.tsx index 112b8d8..0b18d47 100644 --- a/apps/frontend/next-app/app/utils/customAlert.tsx +++ b/apps/frontend/next-app/app/utils/customAlert.tsx @@ -12,7 +12,7 @@ export const successAlert = (message: string | undefined | Message | any) => { } export const errorAlert = (errorMessage: Message | any) => { - toast.error(errorMessage, { + toast.error(errorMessage.message, { position: 'bottom-right', style: { paddingRight: '40px', diff --git a/apps/frontend/next-app/app/utils/rollupsProvider.tsx b/apps/frontend/next-app/app/utils/rollupsProvider.tsx deleted file mode 100644 index 48e40b5..0000000 --- a/apps/frontend/next-app/app/utils/rollupsProvider.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React, { createContext, useContext, useState, useEffect } from 'react'; -import { ethers, JsonRpcApiProvider, JsonRpcSigner } from 'ethers'; -import { RollupsContracts, useRollups } from '../cartesi/hooks/useRollups'; -import { useEthersSigner } from '../utils/useEtherSigner'; - -interface RollupsContextProps { - rollups: RollupsContracts | undefined; - signer: JsonRpcSigner | undefined; - provider: JsonRpcApiProvider | undefined; -} - -const RollupsContext = createContext(undefined); - -export const RollupsProvider: React.FC<{ dappAddress: string; children: React.ReactNode }> = ({ dappAddress, children }) => { - const rollups = useRollups(dappAddress); - const signer = useEthersSigner(); - const provider = signer?.provider; - - return ( - - {children} - - ); -}; - -export const useRollupsContext = () => { - const context = useContext(RollupsContext); - if (!context) { - throw new Error('useRollupsContext must be used within a RollupsProvider'); - } - return context; -};
-
{n.input.index} {n.index}
{voucherToExecute.input.index}
{voucherToExecute && voucherToExecute.input.index}{voucherToExecute.destination} - + {voucherToExecute.index}{voucherToExecute && voucherToExecute.index}{voucherToExecute.payload}{voucherToExecute.proof}{voucherToExecute.input.payload}
-
{n.input.index} {n.index}Ether ERC-20 ERC-721ERC-1155