From bf53c9d345b6a68ffa64532e0e800c6227e1f5b1 Mon Sep 17 00:00:00 2001 From: hooperben Date: Tue, 20 Jan 2026 14:16:45 +1000 Subject: [PATCH] fix: increased the amount of retries in queries + added app state page --- client/src/App.tsx | 7 +- client/src/_constants/seo-config.ts | 17 + client/src/_hooks/use-chainlink-price.ts | 1 + client/src/_hooks/use-erc20-balance.ts | 1 + client/src/_hooks/use-indexer-leafs.ts | 4 +- client/src/_hooks/use-indexer-notes.ts | 2 +- client/src/_hooks/use-indexer-nullifiers.ts | 2 +- client/src/_hooks/use-private-balances.ts | 1 + client/src/_hooks/use-public-transfer.ts | 4 +- client/src/_hooks/use-user-asset-notes.ts | 1 + client/src/_hooks/use-user-notes.ts | 1 + client/src/lib/gas.ts | 4 +- client/src/pages/about.tsx | 4 +- client/src/pages/app-state.tsx | 511 ++++++++++++++ client/src/pages/home.tsx | 14 +- client/src/pages/settings.tsx | 19 +- client/src/pages/testing.tsx | 743 -------------------- 17 files changed, 570 insertions(+), 766 deletions(-) create mode 100644 client/src/pages/app-state.tsx delete mode 100644 client/src/pages/testing.tsx diff --git a/client/src/App.tsx b/client/src/App.tsx index 08be69f..2013b2f 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -3,6 +3,7 @@ import { InAppBrowserWarning } from "@/_components/status/in-app-browser-warning import { AboutPage } from "@/pages/about"; import AccountPage from "@/pages/account"; import AccountsPage from "@/pages/accounts"; +import AppStatePage from "@/pages/app-state"; import ContactsPage from "@/pages/contacts"; import DecryptPage from "@/pages/decrypt"; import EncryptPage from "@/pages/encrypt"; @@ -12,7 +13,6 @@ import SendPage from "@/pages/send"; import { SettingsPage } from "@/pages/settings"; import SharePage from "@/pages/share"; import StatusPage from "@/pages/status"; -import TestingPage from "@/pages/testing"; import TransactionsPage from "@/pages/transactions"; import { HashRouter, Route, Routes } from "react-router-dom"; import { AuthProvider } from "./_providers/auth-provider"; @@ -99,12 +99,11 @@ function App() { } /> } /> - {/* TODO remove once confirmed that everything works */} - + } /> diff --git a/client/src/_constants/seo-config.ts b/client/src/_constants/seo-config.ts index 19e907d..162be0a 100644 --- a/client/src/_constants/seo-config.ts +++ b/client/src/_constants/seo-config.ts @@ -181,6 +181,23 @@ export const PAGE_METADATA: Record = { "Ethereum development", ], }, + state: { + header: "App State", + title: "commbank.eth | App State", + description: "View account state and sync status", + path: "/state", + ogImage: OG_IMAGE, + ogImageAlt: OG_IMAGE_ALT, + keywords: [ + "commbank.eth account", + "open source finance", + "privacy technology", + "blockchain project", + "passkey", + "credentials management", + "Ethereum development", + ], + }, }; export function getPageUrl(path: string): string { diff --git a/client/src/_hooks/use-chainlink-price.ts b/client/src/_hooks/use-chainlink-price.ts index 918c0c6..6217e41 100644 --- a/client/src/_hooks/use-chainlink-price.ts +++ b/client/src/_hooks/use-chainlink-price.ts @@ -83,6 +83,7 @@ export const useChainlinkPrice = ( description: String(description), }; }, + retry: 3, refetchInterval: options?.refetchInterval ?? 60000, // Default: refetch every 60 seconds enabled: options?.enabled ?? true, }); diff --git a/client/src/_hooks/use-erc20-balance.ts b/client/src/_hooks/use-erc20-balance.ts index 3454f79..9100393 100644 --- a/client/src/_hooks/use-erc20-balance.ts +++ b/client/src/_hooks/use-erc20-balance.ts @@ -44,6 +44,7 @@ export const useERC20Balance = (asset?: SupportedAsset | null) => { return balance; }, enabled: !!address && !!asset, + retry: 3, }); return queryFn; diff --git a/client/src/_hooks/use-indexer-leafs.ts b/client/src/_hooks/use-indexer-leafs.ts index e47f074..28f9315 100644 --- a/client/src/_hooks/use-indexer-leafs.ts +++ b/client/src/_hooks/use-indexer-leafs.ts @@ -7,7 +7,7 @@ interface LeafInsertedResponse { envio_Commbankdoteth_LeafInserted: IndexerLeafInserted[]; } -const fetchIndexerLeafs = async ( +export const fetchIndexerLeafs = async ( limit: number = 50, offset: number = 0, ): Promise => { @@ -29,7 +29,7 @@ export const useIndexerLeafs = (limit: number = 50, offset: number = 0) => { return useQuery({ queryKey: ["indexer-leafs", limit, offset], queryFn: () => fetchIndexerLeafs(limit, offset), - retry: 1, + retry: 3, refetchInterval: 20_000, // refetch every 20 seconds }); }; diff --git a/client/src/_hooks/use-indexer-notes.ts b/client/src/_hooks/use-indexer-notes.ts index 9347126..75ea7ea 100644 --- a/client/src/_hooks/use-indexer-notes.ts +++ b/client/src/_hooks/use-indexer-notes.ts @@ -29,7 +29,7 @@ export const useIndexerNotes = (limit: number = 50, offset: number = 0) => { return useQuery({ queryKey: ["indexer-notes", limit, offset], queryFn: () => fetchIndexerNotes(limit, offset), - retry: 1, + retry: 3, refetchInterval: 20_000, // refetch every 20 seconds }); }; diff --git a/client/src/_hooks/use-indexer-nullifiers.ts b/client/src/_hooks/use-indexer-nullifiers.ts index 7488f94..e380f99 100644 --- a/client/src/_hooks/use-indexer-nullifiers.ts +++ b/client/src/_hooks/use-indexer-nullifiers.ts @@ -37,7 +37,7 @@ export const useIndexerNullifiers = ( return useQuery({ queryKey: ["indexer-nullifiers", limit, offset], queryFn: () => fetchIndexerNullifiers(limit, offset), - retry: 1, + retry: 3, refetchInterval: 20_000, // refetch every 20 seconds }); }; diff --git a/client/src/_hooks/use-private-balances.ts b/client/src/_hooks/use-private-balances.ts index 01102ce..1597087 100644 --- a/client/src/_hooks/use-private-balances.ts +++ b/client/src/_hooks/use-private-balances.ts @@ -31,6 +31,7 @@ export function usePrivateBalances(assets: SupportedAsset[]) { return balances; }, + retry: 3, enabled: assets.length > 0, }); } diff --git a/client/src/_hooks/use-public-transfer.ts b/client/src/_hooks/use-public-transfer.ts index b0cd714..8d12f6d 100644 --- a/client/src/_hooks/use-public-transfer.ts +++ b/client/src/_hooks/use-public-transfer.ts @@ -99,7 +99,9 @@ export function usePublicTransfer({ return useMutation({ mutationFn: async ({ amount, asset, recipient }: PublicTransferParams) => { if (!recipient.evmAddress) { - throw new Error("Recipient must have an EVM address for public transfer"); + throw new Error( + "Recipient must have an EVM address for public transfer", + ); } // Generate unique transaction ID upfront diff --git a/client/src/_hooks/use-user-asset-notes.ts b/client/src/_hooks/use-user-asset-notes.ts index 79ae85b..228531d 100644 --- a/client/src/_hooks/use-user-asset-notes.ts +++ b/client/src/_hooks/use-user-asset-notes.ts @@ -14,6 +14,7 @@ export function useUserAssetNotes(assetId: string | undefined) { (note) => BigInt(note.assetId) === BigInt(assetId ?? "0"), ); }, + retry: 3, enabled: !!assetId, }); } diff --git a/client/src/_hooks/use-user-notes.ts b/client/src/_hooks/use-user-notes.ts index 32e0784..cdcc7f7 100644 --- a/client/src/_hooks/use-user-notes.ts +++ b/client/src/_hooks/use-user-notes.ts @@ -8,5 +8,6 @@ export function useUserNotes() { return useQuery({ queryKey: ["notes"], queryFn: getAllNotes, + retry: 3, }); } diff --git a/client/src/lib/gas.ts b/client/src/lib/gas.ts index 18e3939..bf11427 100644 --- a/client/src/lib/gas.ts +++ b/client/src/lib/gas.ts @@ -18,6 +18,6 @@ export async function getAdjustedGasPrice( return parseUnits("2", "gwei"); } - // Add 50% buffer to handle base fee increases between estimation and inclusion - return (basePrice * 150n) / 100n; + // Add 20% buffer to handle base fee increases between estimation and inclusion + return (basePrice * 120n) / 100n; } diff --git a/client/src/pages/about.tsx b/client/src/pages/about.tsx index 2663f48..47972d4 100644 --- a/client/src/pages/about.tsx +++ b/client/src/pages/about.tsx @@ -87,11 +87,11 @@ In order to facilitate a smoother user experience with regards to submitting tra Your use of this software is your own choice, just like everything else. It might have some bugs or unexpected behaviours, so don't do anything unless it makes sense. -With regards to the legalities of commbank.eth's private unstoppable money - more documentation is on the way, but in the mean time that's a conversation I think is best done at the pub. +With regards to the legalities of commbank.eth's private unstoppable money - more documentation is on the way, but in the mean time that is a conversation I think is best had at the pub. If you're curious about how commbank.eth works - you can look at **all** of the [source code in github](https://github.com/hooperben/commbank.eth), all of which is free to use - **forever**. -Found a bug? Create an issue in the Github or reach out to the project on [twitter](https://twitter.com/commbankdoteth). +If you've found a bug? Create an issue in the Github or reach out to the project on [twitter](https://twitter.com/commbankdoteth). Thanks for reading, take it easy. `; diff --git a/client/src/pages/app-state.tsx b/client/src/pages/app-state.tsx new file mode 100644 index 0000000..3cae339 --- /dev/null +++ b/client/src/pages/app-state.tsx @@ -0,0 +1,511 @@ +import { Button } from "@/_components/ui/button"; +import { + Card, + CardContent, + CardHeader, + CardTitle, +} from "@/_components/ui/card"; +import { getNoteHash, getNullifier } from "@/_constants/notes"; +import { PAGE_METADATA } from "@/_constants/seo-config"; +import { useDBStats } from "@/_hooks/use-indexed-db"; +import { fetchIndexerLeafs } from "@/_hooks/use-indexer-leafs"; +import { fetchIndexerNotes } from "@/_hooks/use-indexer-notes"; +import { fetchIndexerNullifiers } from "@/_hooks/use-indexer-nullifiers"; +import { useAuth } from "@/_providers/auth-provider"; +import PageContainer from "@/_providers/page-container"; +import type { Note, Payload, TreeLeaf } from "@/_types"; +import { + addNote, + addPayload, + addTreeLeaf, + clearNotes, + clearPayloads, + clearTree, + findNoteByFields, + getAllTreeLeaves, + updatePayload, +} from "@/lib/db"; +import { AlertCircle, Loader2, Lock, RefreshCw, Trash2 } from "lucide-react"; +import { useState } from "react"; +import { NoteDecryption } from "shared/classes/Note"; +import { toast } from "sonner"; + +const AppStatePage = () => { + const [isPayloadsLoading, setIsPayloadsLoading] = useState(false); + const [isLeafsLoading, setIsLeafsLoading] = useState(false); + const [isNotesLoading, setIsNotesLoading] = useState(false); + const [decryptingPayloadId, setDecryptingPayloadId] = useState( + null, + ); + + const { getEnvelopeKey, refreshNotes, getMnemonic, privateAddress } = + useAuth(); + + const { + allNotes, + allTreeLeaves, + allPayloads, + isLoading: isDBLoading, + refresh: refreshDBStats, + db, + } = useDBStats(); + + // Clear payloads and re-fetch from indexer + const handleClearAndRefetchPayloads = async () => { + setIsPayloadsLoading(true); + try { + await clearPayloads(); + const indexerPayloads = await fetchIndexerNotes(100, 0); + + if (indexerPayloads) { + for (const payload of indexerPayloads) { + await addPayload({ + id: payload.id, + encryptedNote: payload.encryptedNote, + decryptAttempted: false, + }); + } + } + + await refreshDBStats(); + toast.success( + `Refetched ${indexerPayloads?.length || 0} payloads from indexer`, + ); + } catch (error) { + console.error("Error refetching payloads:", error); + toast.error("Failed to refetch payloads"); + } finally { + setIsPayloadsLoading(false); + } + }; + + // Clear tree leaves and re-fetch from indexer + const handleClearAndRefetchLeafs = async () => { + setIsLeafsLoading(true); + try { + await clearTree(); + const indexerLeafs = await fetchIndexerLeafs(100, 0); + + if (indexerLeafs) { + for (const leaf of indexerLeafs) { + await addTreeLeaf({ + id: leaf.id, + leafIndex: leaf.leafIndex, + leafValue: leaf.leafValue, + }); + } + } + + await refreshDBStats(); + toast.success( + `Refetched ${indexerLeafs?.length || 0} tree leaves from indexer`, + ); + } catch (error) { + console.error("Error refetching tree leaves:", error); + toast.error("Failed to refetch tree leaves"); + } finally { + setIsLeafsLoading(false); + } + }; + + // Clear notes, clear payloads, re-fetch payloads, then re-decrypt + const handleClearAndRedecryptNotes = async () => { + setIsNotesLoading(true); + try { + // Clear notes + await clearNotes(); + + // Clear payloads and re-fetch with decryptAttempted: false + await clearPayloads(); + const indexerPayloads = await fetchIndexerNotes(100, 0); + + if (indexerPayloads) { + for (const payload of indexerPayloads) { + await addPayload({ + id: payload.id, + encryptedNote: payload.encryptedNote, + decryptAttempted: false, + }); + } + } + + // Run refreshNotes to re-decrypt + const mnemonic = await getMnemonic(); + const newNotesCount = await refreshNotes(mnemonic ?? undefined); + + await refreshDBStats(); + toast.success(`Re-decrypted and found ${newNotesCount} notes`); + } catch (error) { + console.error("Error re-decrypting notes:", error); + toast.error("Failed to re-decrypt notes"); + } finally { + setIsNotesLoading(false); + } + }; + + // Decrypt a single payload and add to notes if successful + const handleDecryptPayload = async (payload: Payload) => { + setDecryptingPayloadId(payload.id); + try { + const envelopeKey = await getEnvelopeKey(); + if (!envelopeKey) { + toast.error("Please sign in to decrypt notes"); + return; + } + + const decrypted = await NoteDecryption.decryptEncryptedNote( + payload.encryptedNote, + envelopeKey, + ); + + // Check if note already exists + const existingNote = await findNoteByFields( + decrypted.asset_id, + decrypted.asset_amount, + decrypted.secret, + ); + + if (existingNote) { + await updatePayload({ ...payload, decryptAttempted: true }); + await refreshDBStats(); + toast.info("Note already exists in database"); + return; + } + + // Calculate nullifier to check if note is used + const parsedNote = { + assetId: decrypted.asset_id, + assetAmount: decrypted.asset_amount, + secret: decrypted.secret, + owner: decrypted.owner, + }; + const noteHash = getNoteHash(parsedNote); + + const leafs = await getAllTreeLeaves(); + const [leaf] = leafs.filter( + (item) => BigInt(item.leafValue) === BigInt(noteHash), + ); + + const nullifiers = await fetchIndexerNullifiers(100, 0); + const nullifier = getNullifier({ + leaf_index: leaf?.leafIndex, + owner: decrypted.owner, + secret: decrypted.secret, + asset_id: decrypted.asset_id, + asset_amount: decrypted.asset_amount, + owner_secret: "", + path: [], + path_indices: [], + }); + + // Add note to database + await addNote({ + id: payload.id, + assetId: decrypted.asset_id, + assetAmount: decrypted.asset_amount, + nullifier: payload.id, + secret: decrypted.secret, + entity_id: decrypted.owner, + isUsed: nullifiers.some( + (item) => BigInt(item.nullifier) === BigInt(nullifier), + ), + note_payload_id: payload.id, + }); + + // Mark payload as attempted + await updatePayload({ ...payload, decryptAttempted: true }); + + await refreshDBStats(); + toast.success("Note decrypted and added to database"); + console.log("Decrypted note:", decrypted); + } catch (error) { + // Mark payload as attempted even on failure + await updatePayload({ ...payload, decryptAttempted: true }); + await refreshDBStats(); + console.error("Decryption error:", error); + toast.error("Failed to decrypt note (not for this user)"); + } finally { + setDecryptingPayloadId(null); + } + }; + + // Show loading state while DB initializes + if (db.isInitializing) { + return ( +
+ +

Initializing database...

+
+ ); + } + + // Show error if DB failed to initialize + if (db.error) { + return ( +
+ +
+

+ Database Initialization Failed +

+

+ {db.error.message} +

+
+
+ ); + } + + return ( + +
+

App State

+

+ View and manage all account state needed for private transactions. +

+ + {/* Payloads Table */} + + + Payloads ({allPayloads.length}) + + + + {isDBLoading ? ( +
+ +
+ ) : allPayloads.length > 0 ? ( +
+ + + + + + + + + + + {allPayloads.map((payload: Payload) => ( + + + + + + + ))} + +
IDEncrypted NoteAttemptedAction
{payload.id} + {payload.encryptedNote.slice(0, 50)}... + + + {payload.decryptAttempted ? "Yes" : "No"} + + + +
+
+ ) : ( +

+ No payloads in database +

+ )} +
+
+ + {/* Tree Leaves Table */} + + + Tree Leaves ({allTreeLeaves.length}) + + + + {isDBLoading ? ( +
+ +
+ ) : allTreeLeaves.length > 0 ? ( +
+ + + + + + + + + + {allTreeLeaves.map((leaf: TreeLeaf) => ( + + + + + + ))} + +
IDLeaf IndexLeaf Value
{leaf.id} + {leaf.leafIndex} + + {leaf.leafValue} +
+
+ ) : ( +

+ No tree leaves in database +

+ )} +
+
+ + {/* Notes Table */} + + + Notes ({allNotes.length}) + + + + {isDBLoading ? ( +
+ +
+ ) : allNotes.length > 0 ? ( +
+ + + + + + + + + + + + + {allNotes.map((note: Note) => ( + + + + + + + + + ))} + +
IDAsset IDAmountOwnedUsedPayload ID
{note.id} + {`0x${BigInt(note.assetId).toString(16)}`} + + {note.assetAmount} + + + {BigInt(note.entity_id) === BigInt(privateAddress!) + ? "Yes" + : "No"} + + + + {note.isUsed ? "Yes" : "No"} + + + {note.note_payload_id || "-"} +
+
+ ) : ( +

+ No notes in database +

+ )} +
+
+
+
+ ); +}; + +export default AppStatePage; diff --git a/client/src/pages/home.tsx b/client/src/pages/home.tsx index b91b268..63b1190 100644 --- a/client/src/pages/home.tsx +++ b/client/src/pages/home.tsx @@ -1,11 +1,11 @@ -import { BrowserNotSupportedWarning } from "@/_components/status/browser-not-supported-warning"; +import { AuthButton } from "@/_components/auth/auth-button"; import { Logo } from "@/_components/logo"; -import PageContainer from "@/_providers/page-container"; +import { BrowserNotSupportedWarning } from "@/_components/status/browser-not-supported-warning"; import { Button } from "@/_components/ui/button"; -import { useDeviceCompatible } from "@/_hooks/use-device-compatible"; import { PAGE_METADATA } from "@/_constants/seo-config"; +import { useDeviceCompatible } from "@/_hooks/use-device-compatible"; +import PageContainer from "@/_providers/page-container"; import { Link } from "react-router-dom"; -import { AuthButton } from "@/_components/auth/auth-button"; export const HomePage = () => { const { isPasskeySupported, isDBSupported } = useDeviceCompatible(); @@ -16,13 +16,13 @@ export const HomePage = () => {
-

+

commbank.eth

-
+

open source, privacy enhancing financial technologies

@@ -30,7 +30,7 @@ export const HomePage = () => { -
+
diff --git a/client/src/pages/settings.tsx b/client/src/pages/settings.tsx index 2773a26..1032e2c 100644 --- a/client/src/pages/settings.tsx +++ b/client/src/pages/settings.tsx @@ -2,15 +2,18 @@ import { AddressCard } from "@/_components/account/address-card"; import { BackupAccountModal } from "@/_components/settings/backup-account-modal"; import { DeleteAccountModal } from "@/_components/settings/delete-account-modal"; import { ResetAppModal } from "@/_components/settings/reset-app-modal"; -import PageContainer from "@/_providers/page-container"; +import { Button } from "@/_components/ui/button"; import { Card, CardContent, CardHeader, CardTitle, } from "@/_components/ui/card"; -import { useAuth } from "@/_providers/auth-provider"; import { PAGE_METADATA } from "@/_constants/seo-config"; +import { useAuth } from "@/_providers/auth-provider"; +import PageContainer from "@/_providers/page-container"; +import { ArrowRight } from "lucide-react"; +import { Link } from "react-router-dom"; export const SettingsPage = () => { const { address, privateAddress, signingKey } = useAuth(); @@ -48,8 +51,18 @@ export const SettingsPage = () => { Clear your local cache and re-sync your account data. Your credentials and contacts will be preserved.

-
+
+ +
diff --git a/client/src/pages/testing.tsx b/client/src/pages/testing.tsx deleted file mode 100644 index 7777012..0000000 --- a/client/src/pages/testing.tsx +++ /dev/null @@ -1,743 +0,0 @@ -import { Button } from "@/_components/ui/button"; -import { poseidon2Hash } from "@zkpassport/poseidon2"; -import { useEffect, useState } from "react"; -import { - Card, - CardContent, - CardHeader, - CardTitle, -} from "@/_components/ui/card"; -import { useDBStats } from "@/_hooks/use-indexed-db"; -import { useIndexerLeafs } from "@/_hooks/use-indexer-leafs"; -import { useIndexerNotes } from "@/_hooks/use-indexer-notes"; -import { useAuth } from "@/_providers/auth-provider"; -import { NoteDecryption } from "shared/classes/Note"; -import { - AlertCircle, - Database, - DatabaseZap, - Loader2, - Lock, -} from "lucide-react"; -import { Deposit } from "shared/classes/Deposit"; -import { Transact } from "shared/classes/Transact"; -import { Withdraw } from "shared/classes/Withdraw"; -import { toast } from "sonner"; - -// Component for indexer payload row -const IndexerPayloadRow = ({ - note, - checkInDB, - getEnvelopeKey, -}: { - note: { id: string; encryptedNote: string }; - checkInDB: (id: string) => Promise; - getEnvelopeKey: () => Promise; -}) => { - const [isInDB, setIsInDB] = useState(null); - const [isDecrypting, setIsDecrypting] = useState(false); - - useEffect(() => { - checkInDB(note.id).then(setIsInDB); - }, [note.id, checkInDB]); - - const handleDecrypt = async () => { - setIsDecrypting(true); - try { - const envelopeKey = await getEnvelopeKey(); - if (!envelopeKey) { - toast.error("Please sign in to decrypt notes"); - return; - } - - const decrypted = await NoteDecryption.decryptEncryptedNote( - note.encryptedNote, - envelopeKey, - ); - - console.log("Decrypted note:", decrypted); - toast.success("Note decrypted! Check console for details"); - } catch (error) { - console.error("Decryption error:", error); - toast.error("Failed to decrypt note"); - } finally { - setIsDecrypting(false); - } - }; - - return ( - - {note.id} - - {note.encryptedNote} - - - {isInDB === null ? ( - - ) : isInDB ? ( -
- - In DB -
- ) : ( -
- - Not in DB -
- )} - - -
- -
- - - ); -}; - -// Component for indexer leaf row -const IndexerLeafRow = ({ - leaf, - checkInDB, -}: { - leaf: { id: string; leafIndex: string; leafValue: string }; - checkInDB: (id: string) => Promise; -}) => { - const [isInDB, setIsInDB] = useState(null); - - useEffect(() => { - checkInDB(leaf.id).then(setIsInDB); - }, [leaf.id, checkInDB]); - - return ( - - {leaf.id} - {leaf.leafIndex} - - {leaf.leafValue} - - - {isInDB === null ? ( - - ) : isInDB ? ( -
- - In DB -
- ) : ( -
- - Not in DB -
- )} - - - ); -}; - -const TestingPage = () => { - const [isDepositLoading, setIsDepositLoading] = useState(false); - const [isTransferLoading, setIsTransferLoading] = useState(false); - const [isWithdrawLoading, setIsWithdrawLoading] = useState(false); - - // Get auth context - const { getEnvelopeKey } = useAuth(); - - // Use the IndexedDB hook - it handles initialization automatically - const { - stats: dbStats, - allNotes, - allTreeLeaves, - allPayloads, - isLoading: isDBLoading, - refresh: refreshDBStats, - db, - } = useDBStats(); - - // Use indexer hooks - const { - data: indexerNotes, - isLoading: isNotesLoading, - error: notesError, - } = useIndexerNotes(50, 0); - - const { - data: indexerLeafs, - isLoading: isLeafsLoading, - error: leafsError, - } = useIndexerLeafs(50, 0); - - // Check if indexer records exist in IndexedDB - const checkPayloadInDB = async (id: string): Promise => { - try { - const payload = await db.getPayload(id); - return !!payload; - } catch { - return false; - } - }; - - const checkLeafInDB = async (id: string): Promise => { - try { - const leaf = await db.getTreeLeaf(id); - return !!leaf; - } catch { - return false; - } - }; - - const handleDepositProof = async () => { - setIsDepositLoading(true); - try { - // const hash = - // 15877031116292595040191017675338240539290338653409019794000313907399651592164n; - const asset_id = 1096978651789611665652906124278561787240579697095n; - const amount = 5n; - const owner = - 10812186542955647827474372651967207045861174805371180171801345448553285386806n; - const secret = - 2389312107716289199307843900794656424062350252250388738019021107824217896920n; - - const deposit = new Deposit(); - - const noteHash = poseidon2Hash([ - BigInt(asset_id), - BigInt(amount), - BigInt(owner), - BigInt(secret), - ]); - - const noteHashN = BigInt(noteHash.toString()); - - await deposit.depositNoir.init(); - - const { witness } = await deposit.depositNoir.execute({ - hash: noteHashN.toString(), - asset_id: asset_id.toString(), - asset_amount: amount.toString(), - owner: owner.toString(), - secret: secret.toString(), - }); - - const proof = await deposit.depositBackend.generateProof(witness, { - keccak: true, - }); - - console.log("Deposit Proof:", proof); - } catch (err) { - console.error("Deposit error: ", err); - } finally { - setIsDepositLoading(false); - } - }; - - const handleTransferProof = async () => { - setIsTransferLoading(true); - try { - console.log("Starting transfer proof generation..."); - - const HEIGHT = 12; - - // Test data from transfer circuit main.nr - const root = - 4221110344891604176205088962198904729260430126413313722462390172704999703195n; - const asset_id = 1096978651789611665652906124278561787240579697095n; - - // Input note (Alice spending 5 tokens) - const input_note = { - asset_id: asset_id.toString(), - asset_amount: "5", - owner: - "10812186542955647827474372651967207045861174805371180171801345448553285386806", - owner_secret: - "10036677144260647934022413515521823129584317400947571241312859176539726523915", - secret: - "2389312107716289199307843900794656424062350252250388738019021107824217896920", - leaf_index: "0", - path: [ - "13640659629327953230197633652529006805891215582818597888084863207147219313784", - "19984673905358619496530873554532699316557532969285237470013525856790495658245", - "16054022188397161938956278061878851932956033792728066452148841350372709856325", - "5088416905632566847489144423785449560596474956704206833561295200206123281740", - "7133742548945823648162717112853949322814446130740022056636610844051076979955", - "15996976533936258369996214630141201173712053425083354410411158951568838211277", - "12856765864455281126306545538308148448222111081433610923407492298111988109924", - "4407863489559565071205165471845081321675763465852502126771740970311657294198", - "20448284296610764092326252358036828964180135505542140040145855516028834425624", - "7022843789375185322738689530892530453984779704784378294646894048972162829679", - "10906054357754859492130109809751867122631984061959461434096281674698176679467", - ], - path_indices: ["1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1"], - }; - - // Empty input notes - const empty_input_note = { - asset_id: "0", - asset_amount: "0", - owner: "0", - owner_secret: "0", - secret: "0", - leaf_index: "0", - path: Array(HEIGHT - 1).fill("0"), - path_indices: Array(HEIGHT - 1).fill("0"), - }; - - // Output notes: Alice gets 3, Bob gets 2 - const alice_output_note = { - owner: - "10812186542955647827474372651967207045861174805371180171801345448553285386806", - secret: - "19536471094918068928039225564664574556680178861106125446000998678966251111926", - asset_id: asset_id.toString(), - asset_amount: "3", - }; - - const bob_output_note = { - owner: - "6868973719921785236727144517868768664734231208097695530688003960085654392226", - secret: - "3957740128091467064337395812164919758932045173069261808814882570720300029469", - asset_id: asset_id.toString(), - asset_amount: "2", - }; - - const empty_output_note = { - owner: "0", - secret: "0", - asset_id: "0", - asset_amount: "0", - }; - - const nullifiers = [ - "3889730504789135603011318287331683111639714777739573239289638917879152395137", - "0", - "0", - ]; - - const output_hashes = [ - "8576856452718270547402366094981334736141859948414539161051536617849336979212", - "4033300113401483633011546954450009404136112133461230452107665732116532508739", - "0", - ]; - - const transact = new Transact(); - await transact.transactNoir.init(); - - const { witness } = await transact.transactNoir.execute({ - root: root.toString(), - input_notes: [input_note, empty_input_note, empty_input_note], - output_notes: [alice_output_note, bob_output_note, empty_output_note], - nullifiers, - output_hashes, - }); - - const proof = await transact.transactBackend.generateProof(witness, { - keccak: true, - }); - - console.log("Transfer Proof:", proof); - } catch (err) { - console.error("Transfer error: ", err); - } finally { - setIsTransferLoading(false); - } - }; - - const handleWithdrawProof = async () => { - setIsWithdrawLoading(true); - try { - console.log("Starting withdraw proof generation..."); - - const HEIGHT = 12; - - // Test data from withdraw circuit main.nr - const root = - 9770762522284292133040204594656801249089743659015207279808423545223243067226n; - const asset_id = 1096978651789611665652906124278561787240579697095n; - - // Bob's input note (withdrawing 2 tokens) - const bob_input_note = { - asset_id: asset_id.toString(), - asset_amount: "2", - owner: - "6868973719921785236727144517868768664734231208097695530688003960085654392226", - owner_secret: - "6955001134965379637962992480442037189090898019061077075663294923529403402038", - secret: - "3957740128091467064337395812164919758932045173069261808814882570720300029469", - leaf_index: "2", - path: [ - "13640659629327953230197633652529006805891215582818597888084863207147219313784", - "18380261439356865501884569257940638985761619337694138929913102368174989083576", - "16054022188397161938956278061878851932956033792728066452148841350372709856325", - "5088416905632566847489144423785449560596474956704206833561295200206123281740", - "7133742548945823648162717112853949322814446130740022056636610844051076979955", - "15996976533936258369996214630141201173712053425083354410411158951568838211277", - "12856765864455281126306545538308148448222111081433610923407492298111988109924", - "4407863489559565071205165471845081321675763465852502126771740970311657294198", - "20448284296610764092326252358036828964180135505542140040145855516028834425624", - "7022843789375185322738689530892530453984779704784378294646894048972162829679", - "10906054357754859492130109809751867122631984061959461434096281674698176679467", - ], - path_indices: ["1", "0", "1", "1", "1", "1", "1", "1", "1", "1", "1"], - }; - - const empty_input_note = { - asset_id: "0", - asset_amount: "0", - owner: "0", - owner_secret: "0", - secret: "0", - leaf_index: "0", - path: Array(HEIGHT - 1).fill("0"), - path_indices: Array(HEIGHT - 1).fill("0"), - }; - - const exit_addresses = [asset_id.toString(), "0", "0"]; - const exit_hash = poseidon2Hash([BigInt(asset_id)]); - const exit_address_hashes = [exit_hash.toString(), "0", "0"]; - - const nullifiers = [ - "4114950840897945428984428368446053738282984086981274614627600851726952485197", - "0", - "0", - ]; - - const exit_assets = [asset_id.toString(), "0", "0"]; - const exit_amounts = ["2", "0", "0"]; - - const withdraw = new Withdraw(); - await withdraw.withdrawNoir.init(); - - const { witness } = await withdraw.withdrawNoir.execute({ - root: root.toString(), - input_notes: [bob_input_note, empty_input_note, empty_input_note], - nullifiers, - exit_assets, - exit_amounts, - exit_addresses, - exit_address_hashes, - }); - - const proof = await withdraw.withdrawBackend.generateProof(witness, { - keccak: true, - }); - - console.log("Withdraw Proof:", proof); - } catch (err) { - console.error("Withdraw error: ", err); - } finally { - setIsWithdrawLoading(false); - } - }; - - // Show loading state while DB initializes - if (db.isInitializing) { - return ( -
- -

Initializing database...

-
- ); - } - - // Show error if DB failed to initialize - if (db.error) { - return ( -
- -
-

- Database Initialization Failed -

-

- {db.error.message} -

-
-
- ); - } - - return ( -
-

Testing Page

- - {/* Proof Testing Section */} - - - Proof Testing - - - - - -
-

- Open the browser console to see proof generation results -

-
-
-
- - {/* IndexedDB Testing Section */} - - - IndexedDB Testing - - - {/* Action buttons */} -
- -
- - {/* Stats display */} - {isDBLoading ? ( -
- -
- ) : ( - dbStats && ( -
-
-

Total Notes

-

{dbStats.notes}

-
-
-

Unused Notes

-

{dbStats.unusedNotes}

-
-
-

Tree Leaves

-

{dbStats.treeLeaves}

-
-
-

Payloads

-

{dbStats.payloads}

-
-
- ) - )} - - {/* Data display */} -
- {/* Notes */} - {allNotes.length > 0 && ( -
-

Notes

-
- {allNotes.map((note) => ( -
-

- ID: {note.id} -

-

- Asset: {note.assetId}{" "} - ({note.assetAmount}) -

-

- Entity:{" "} - {note.entity_id} -

-

- Used:{" "} - {note.isUsed ? "Yes" : "No"} -

-
- ))} -
-
- )} - - {/* Tree Leaves */} - {allTreeLeaves.length > 0 && ( -
-

Tree Leaves

-
- {allTreeLeaves.map((leaf) => ( -
-

- ID: {leaf.id} -

-

- Index:{" "} - {leaf.leafIndex} -

-

- Value:{" "} - {leaf.leafValue} -

-
- ))} -
-
- )} - - {/* Payloads */} - {allPayloads.length > 0 && ( -
-

Payloads

-
- {allPayloads.map((payload) => ( -
-

- ID: {payload.id} -

-

- Note:{" "} - {payload.encryptedNote} -

-
- ))} -
-
- )} -
-
-
- - {/* Indexer Data Section */} - - - Indexer Data - - - {/* Indexer Notes Table */} -
-

- Note Payloads ({indexerNotes?.length || 0}) -

- {isNotesLoading ? ( -
- -
- ) : notesError ? ( -
- - Error loading notes: {notesError.message} -
- ) : indexerNotes && indexerNotes.length > 0 ? ( -
- - - - - - - - - - - {indexerNotes.map((note) => ( - - ))} - -
IDEncrypted NoteStatusAction
-
- ) : ( -

- No note payloads found -

- )} -
- - {/* Indexer Leafs Table */} -
-

- Tree Leaves ({indexerLeafs?.length || 0}) -

- {isLeafsLoading ? ( -
- -
- ) : leafsError ? ( -
- - Error loading leafs: {leafsError.message} -
- ) : indexerLeafs && indexerLeafs.length > 0 ? ( -
- - - - - - - - - - - - {indexerLeafs.map((leaf) => ( - - ))} - -
IDLeaf IndexLeaf ValueStatusAction
-
- ) : ( -

- No tree leaves found -

- )} -
-
-
-
- ); -}; - -export default TestingPage;