diff --git a/package.json b/package.json index cbf3703..b4300ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "request-scan", - "version": "0.2.7", + "version": "0.2.8", "private": true, "scripts": { "dev": "next dev", diff --git a/src/app/request/[id]/page.tsx b/src/app/request/[id]/page.tsx index 5dc038f..dcb584e 100644 --- a/src/app/request/[id]/page.tsx +++ b/src/app/request/[id]/page.tsx @@ -1,18 +1,18 @@ /** @format */ -'use client'; +"use client"; -import { TransactionsAndPaymentsTable } from '@/components/transactions-and-payments-table'; -import { Button } from '@/components/ui/button'; +import { TransactionsAndPaymentsTable } from "@/components/transactions-and-payments-table"; +import { Button } from "@/components/ui/button"; import { Card, CardHeader, CardTitle, CardContent, CardFooter, -} from '@/components/ui/card'; -import { Skeleton } from '@/components/ui/skeleton'; -import { fetchRequest } from '@/lib/queries/channel'; -import { fetchRequestPayments } from '@/lib/queries/request-payments'; +} from "@/components/ui/card"; +import { Skeleton } from "@/components/ui/skeleton"; +import { fetchRequest } from "@/lib/queries/channel"; +import { fetchRequestPayments } from "@/lib/queries/request-payments"; import { calculateLongPaymentReference, calculateShortPaymentReference, @@ -25,16 +25,17 @@ import { getPaymentDataFromCreateTransaction, getTransactionCreateParameters, renderAddress, -} from '@/lib/utils'; -import { ActorInfo } from '@requestnetwork/data-format'; -import { useQuery } from '@tanstack/react-query'; -import { Copy, File, Loader2 } from 'lucide-react'; -import Link from 'next/link'; -import { redirect } from 'next/navigation'; -import TimeAgo from 'timeago-react'; -import { JsonEditor } from 'json-edit-react'; -import useExportPDF from '@/lib/hooks/use-export-pdf'; -import { useState } from 'react'; +} from "@/lib/utils"; +import { ActorInfo } from "@requestnetwork/data-format"; +import { useQuery } from "@tanstack/react-query"; +import { Copy, File, Loader2 } from "lucide-react"; +import Link from "next/link"; +import { redirect } from "next/navigation"; +import TimeAgo from "timeago-react"; +import { JsonEditor } from "json-edit-react"; +import useExportPDF from "@/lib/hooks/use-export-pdf"; +import { useState } from "react"; +import { Channel } from "@/lib/types"; interface RequestPageProps { params: { @@ -42,14 +43,21 @@ interface RequestPageProps { }; } +const getGateway = (request: Channel | null) => { + if (request?.source === "storage_sepolia") { + return "sepolia.gateway.request.network"; + } + return "gnosis.gateway.request.network"; +}; + const ActorInfoSection = ({ actorInfo }: { actorInfo?: ActorInfo }) => { if ( !actorInfo || Object.keys(actorInfo).every( - (k) => !Object.keys((actorInfo as any)[k]).length, + (k) => !Object.keys((actorInfo as any)[k]).length ) ) { - return 'N/A'; + return "N/A"; } return ( @@ -113,7 +121,7 @@ export default function RequestPage({ params: { id } }: RequestPageProps) { const [isDownloading, setIsDownloading] = useState(false); const { data: request, isLoading: isLoadingRequest } = useQuery({ - queryKey: ['request', id], + queryKey: ["request", id], queryFn: () => fetchRequest({ id }), ...commonQueryOptions, }); @@ -122,19 +130,19 @@ export default function RequestPage({ params: { id } }: RequestPageProps) { ? calculateShortPaymentReference( id, request?.transactions[0].dataObject.data.parameters.extensionsData[0] - .parameters.salt || '', + .parameters.salt || "", request?.transactions[0].dataObject.data.parameters.extensionsData[0] - .parameters.paymentAddress || '', + .parameters.paymentAddress || "" ) - : ''; + : ""; const longPaymentReference = shortPaymentReference ? calculateLongPaymentReference(shortPaymentReference) - : ''; + : ""; const { data: requestPayments, isLoading: isLoadingRequestPayments } = useQuery({ - queryKey: ['request-payments', longPaymentReference], + queryKey: ["request-payments", longPaymentReference], queryFn: () => fetchRequestPayments({ reference: longPaymentReference }), ...commonQueryOptions, }); @@ -144,7 +152,7 @@ export default function RequestPage({ params: { id } }: RequestPageProps) { } if (!request) { - redirect('/not-found'); + redirect("/not-found"); } const firstTransaction = request?.transactions[0]; @@ -160,14 +168,14 @@ export default function RequestPage({ params: { id } }: RequestPageProps) { const balanceCurrency = paymentData?.acceptedTokens?.length > 0 ? paymentData.acceptedTokens[0] - : ''; + : ""; const buyerData = contentData?.buyerInfo; const sellerData = contentData?.sellerInfo; const status = balance >= BigInt(createParameters.expectedAmount) - ? 'Paid' + ? "Paid" : lastTransaction?.dataObject?.data?.name; const modifiedTimestamp = @@ -190,7 +198,7 @@ export default function RequestPage({ params: { id } }: RequestPageProps) { paymentData, }); } catch (error) { - console.error('Error exporting PDF:', error); + console.error("Error exporting PDF:", error); } finally { setIsDownloading(false); } @@ -240,6 +248,10 @@ export default function RequestPage({ params: { id } }: RequestPageProps) { Status: {status} + + Gateway: + {getGateway(request)} + Payee: @@ -277,7 +289,7 @@ export default function RequestPage({ params: { id } }: RequestPageProps) { {getAmountWithCurrencySymbol( BigInt(createParameters.expectedAmount), - createParameters.currency.value, + createParameters.currency.value )} @@ -286,7 +298,7 @@ export default function RequestPage({ params: { id } }: RequestPageProps) { {getAmountWithCurrencySymbol( BigInt(balance), - balanceCurrency || '', + balanceCurrency || "" )} @@ -296,7 +308,7 @@ export default function RequestPage({ params: { id } }: RequestPageProps) { {' '} + />{" "} ({formatTimestamp(firstTransaction.blockTimestamp)}) @@ -306,7 +318,7 @@ export default function RequestPage({ params: { id } }: RequestPageProps) { {' '} + />{" "} ({formatTimestamp(modifiedTimestamp)}) @@ -319,7 +331,7 @@ export default function RequestPage({ params: { id } }: RequestPageProps) { {paymentData?.network ? capitalize(paymentData?.network) - : ''} + : ""} diff --git a/src/lib/queries/address-transactions.ts b/src/lib/queries/address-transactions.ts index 43f39cb..70b9518 100644 --- a/src/lib/queries/address-transactions.ts +++ b/src/lib/queries/address-transactions.ts @@ -1,10 +1,10 @@ /** @format */ -import { gql } from 'graphql-request'; -import { graphQLClient } from '../graphQlClient'; -import { Transaction } from '../types'; -import { groupBy } from '../utils'; -import { getAddress } from 'viem'; +import { gql } from "graphql-request"; +import { graphQLClient } from "../graphQlClient"; +import { Transaction } from "../types"; +import { groupBy } from "../utils"; +import { getAddress } from "viem"; export const ADDRESS_TRANSACTIONS_QUERY = gql` query AddressTransactionsQuery( @@ -37,6 +37,30 @@ export const ADDRESS_TRANSACTIONS_QUERY = gql` smartContractAddress } } + storage_sepolia { + transactions( + first: $first + skip: $skip + orderBy: blockNumber + orderDirection: desc + where: { + or: [ + { data_contains: $checksumAddress } + { data_contains: $lowercaseAddress } + ] + } + ) { + blockNumber + blockTimestamp + channelId + data + dataHash + hash + id + size + smartContractAddress + } + } } `; @@ -54,18 +78,29 @@ export const fetchAddressRequests = async (variables: { lowercaseAddress: variables.address.toLowerCase(), }; - const data: { storage: { transactions: Transaction[] } } = - await graphQLClient.request(ADDRESS_TRANSACTIONS_QUERY, formatedVariables); + const data: { + storage: { transactions: Transaction[] }; + storage_sepolia: { transactions: Transaction[] }; + } = await graphQLClient.request( + ADDRESS_TRANSACTIONS_QUERY, + formatedVariables + ); + + // Combine transactions from both networks + const allTransactions = [ + ...(data?.storage?.transactions || []), + ...(data?.storage_sepolia?.transactions || []), + ]; - return data?.storage.transactions + return allTransactions.length ? groupBy( - data?.storage.transactions.map((transaction: Transaction) => { + allTransactions.map((transaction: Transaction) => { return { ...transaction, dataObject: JSON.parse(transaction.data), }; }), - 'channelId', + "channelId" ) : []; }; diff --git a/src/lib/queries/channel.ts b/src/lib/queries/channel.ts index fe599a8..3452126 100644 --- a/src/lib/queries/channel.ts +++ b/src/lib/queries/channel.ts @@ -1,8 +1,8 @@ /** @format */ -import { gql } from 'graphql-request'; -import { graphQLClient } from '../graphQlClient'; -import { Channel, Transaction } from '../types'; +import { gql } from "graphql-request"; +import { graphQLClient } from "../graphQlClient"; +import { Channel, Transaction } from "../types"; export const CHANNEL_QUERY = gql` query ChannelQuery($id: ID!) @cached { @@ -29,35 +29,80 @@ export const CHANNEL_QUERY = gql` } } } + storage_sepolia { + channel(id: $id) { + id + topics + transactions { + data + blockNumber + blockTimestamp + channelId + dataHash + encryptedData + encryptedKeys + encryptionMethod + hash + id + publicKeys + size + smartContractAddress + topics + transactionHash + } + } + } } `; export const fetchRequest = async (variables: { id: string; }): Promise => { - const data: { storage: { channel: Channel } } = await graphQLClient.request( - CHANNEL_QUERY, - variables, - ); + const data: { + storage: { channel: Channel }; + storage_sepolia: { channel: Channel }; + } = await graphQLClient.request(CHANNEL_QUERY, variables); - if (!data?.storage.channel) { - return null; + // Check which storage has the channel + if (data?.storage?.channel) { + return { + ...data.storage.channel, + source: "storage", + transactions: data.storage.channel.transactions.map( + (transaction: Transaction) => { + try { + return { + ...transaction, + dataObject: JSON.parse(transaction.data), + }; + } catch (error: any) { + console.error(`Error parsing transaction data: ${error.message}`); + return transaction; + } + } + ), + }; } - return { - ...data.storage.channel, - transactions: data?.storage.channel.transactions.map( - (transaction: Transaction) => { - try { - return { - ...transaction, - dataObject: JSON.parse(transaction.data), - }; - } catch (error: any) { - console.error(`Error parsing transaction data: ${error.message}`); - return transaction; + if (data?.storage_sepolia?.channel) { + return { + ...data.storage_sepolia.channel, + source: "storage_sepolia", + transactions: data.storage_sepolia.channel.transactions.map( + (transaction: Transaction) => { + try { + return { + ...transaction, + dataObject: JSON.parse(transaction.data), + }; + } catch (error: any) { + console.error(`Error parsing transaction data: ${error.message}`); + return transaction; + } } - }, - ), - }; + ), + }; + } + + return null; }; diff --git a/src/lib/queries/transactions.ts b/src/lib/queries/transactions.ts index 5fdfc63..c17b3da 100644 --- a/src/lib/queries/transactions.ts +++ b/src/lib/queries/transactions.ts @@ -1,9 +1,9 @@ /** @format */ -import { gql } from 'graphql-request'; -import { graphQLClient } from '../graphQlClient'; -import { Transaction } from '../types'; -import { groupBy } from '../utils'; +import { gql } from "graphql-request"; +import { graphQLClient } from "../graphQlClient"; +import { Transaction } from "../types"; +import { groupBy } from "../utils"; export const TRANSACTIONS_QUERY = gql` query TransactionsQuery($first: Int, $skip: Int!) { @@ -26,6 +26,25 @@ export const TRANSACTIONS_QUERY = gql` smartContractAddress } } + storage_sepolia { + transactions( + first: $first + skip: $skip + orderBy: blockNumber + orderDirection: desc + where: { data_not: null } + ) { + blockNumber + blockTimestamp + channelId + data + dataHash + hash + id + size + smartContractAddress + } + } } `; @@ -35,18 +54,27 @@ export const fetchRequests = async (variables: { }): Promise<{ [channelId: string]: Transaction[]; }> => { - const data: { storage: { transactions: Transaction[] } } = - await graphQLClient.request(TRANSACTIONS_QUERY, variables); + const data: { + storage: { transactions: Transaction[] }; + storage_sepolia: { transactions: Transaction[] }; + } = await graphQLClient.request(TRANSACTIONS_QUERY, variables); + + const allTransactions = [ + ...(data?.storage?.transactions || []), + ...(data?.storage_sepolia?.transactions || []), + ].sort((a, b) => b.blockTimestamp - a.blockTimestamp); // Sort by timestamp, newest first + + const latestTransactions = allTransactions.slice(0, variables.first); - return data?.storage.transactions + return latestTransactions.length ? groupBy( - data?.storage.transactions.map((transaction: Transaction) => { + latestTransactions.map((transaction: Transaction) => { return { ...transaction, dataObject: JSON.parse(transaction.data), }; }), - 'channelId', + "channelId" ) : []; }; diff --git a/src/lib/types.ts b/src/lib/types.ts index 3e142c4..8c352b0 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -33,12 +33,14 @@ export interface Transaction { id: string; size: string; smartContractAddress: string; + network?: "gnosis" | "sepolia"; } export interface Channel { id: string; topics: string[]; transactions: Transaction[]; + source?: "storage" | "storage_sepolia"; } export interface PaymentData { acceptedTokens: string[];