From b3a3da92701e3b89fc4ba4d6449c03b9c7e49977 Mon Sep 17 00:00:00 2001 From: Evan Gray Date: Tue, 19 Mar 2024 15:17:28 -0400 Subject: [PATCH] dashboard: simple accountant transfer search --- dashboard/package.json | 1 + dashboard/src/components/Accountant.tsx | 342 +++++++++++++++++------- package-lock.json | 19 ++ 3 files changed, 261 insertions(+), 101 deletions(-) diff --git a/dashboard/package.json b/dashboard/package.json index 574d7fc1..5fef559d 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -31,6 +31,7 @@ "react-scripts": "5.0.1", "react-timeago": "^7.1.0", "typescript": "^4.7.4", + "use-debounce": "^10.0.0", "web-vitals": "^2.1.4", "web3": "^1.7.5" }, diff --git a/dashboard/src/components/Accountant.tsx b/dashboard/src/components/Accountant.tsx index 9412d116..497f17e2 100644 --- a/dashboard/src/components/Accountant.tsx +++ b/dashboard/src/components/Accountant.tsx @@ -5,6 +5,11 @@ import { AccordionSummary, Box, Card, + CircularProgress, + Dialog, + DialogContent, + DialogTitle, + IconButton, InputAdornment, LinearProgress, TextField, @@ -20,14 +25,19 @@ import { getSortedRowModel, useReactTable, } from '@tanstack/react-table'; -import { useCallback, useMemo, useState } from 'react'; +import { memo, useCallback, useEffect, useMemo, useState } from 'react'; import { CloudGovernorInfo } from '../hooks/useCloudGovernorInfo'; import useGetAccountantAccounts, { Account } from '../hooks/useGetAccountantAccounts'; import useGetAccountantPendingTransfers, { PendingTransfer, } from '../hooks/useGetAccountantPendingTransfers'; import chainIdToName from '../utils/chainIdToName'; -import { CHAIN_ICON_MAP, GUARDIAN_SET_3 } from '../utils/consts'; +import { + ACCOUNTANT_CONTRACT_ADDRESS, + CHAIN_ICON_MAP, + GUARDIAN_SET_3, + WORMCHAIN_URL, +} from '../utils/consts'; import { CHAIN_INFO_MAP } from '@wormhole-foundation/wormhole-monitor-common'; import CollapsibleSection from './CollapsibleSection'; import Table from './Table'; @@ -35,6 +45,8 @@ import useTokenData, { TokenDataEntry } from '../hooks/useTokenData'; import numeral from 'numeral'; import { useCurrentEnvironment } from '../contexts/NetworkContext'; import { ExplorerTxHash } from './ExplorerTxHash'; +import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'; +import { useDebounce } from 'use-debounce'; type PendingTransferForAcct = PendingTransfer & { isEnqueuedInGov: boolean }; type AccountWithTokenData = Account & { @@ -263,6 +275,110 @@ const overviewColumns = [ }), ]; +function AccountantSearch() { + const [raw_emitter_chain, setChain] = useState(); + const [raw_emitter_address, setAddress] = useState(''); + const [raw_sequence, setSequence] = useState(); + const [response, setResponse] = useState(null); + const handleChain = useCallback((event: any) => { + if (!event.target.value) { + setChain(undefined); + } + try { + const n = parseInt(event.target.value); + if (!isNaN(n)) { + setChain(n); + } + } catch (e) {} + }, []); + const handleAddress = useCallback((event: any) => { + setAddress(event.target.value); + }, []); + const handleSequence = useCallback((event: any) => { + if (!event.target.value) { + setSequence(undefined); + } + try { + const n = parseInt(event.target.value); + if (!isNaN(n)) { + setSequence(n); + } + } catch (e) {} + }, []); + const [emitter_chain] = useDebounce(raw_emitter_chain, 500); + const [emitter_address] = useDebounce(raw_emitter_address, 500); + const [sequence] = useDebounce(raw_sequence, 500); + useEffect(() => { + if (emitter_chain && emitter_address && sequence) { + setResponse(null); + let cancelled = false; + (async () => { + try { + const cosmWasmClient = await CosmWasmClient.connect(WORMCHAIN_URL); + const response = await cosmWasmClient.queryContractSmart(ACCOUNTANT_CONTRACT_ADDRESS, { + transfer_status: { + emitter_chain, + emitter_address, + sequence, + }, + }); + if (!cancelled) { + setResponse(response); + } + } catch (error) { + if (!cancelled) { + setResponse({}); + } + console.error(error); + } + })(); + return () => { + cancelled = true; + }; + } + }, [emitter_chain, emitter_address, sequence]); + return ( + <> + Transfer Key + + + + {emitter_chain && emitter_address && sequence ? ( + response ? ( +
{JSON.stringify(response, undefined, 2)}
+ ) : ( + + ) + ) : ( + Enter a transfer key above + )} + + ); +} + +const MemoizedAccountantSearch = memo(AccountantSearch); + function Accountant({ governorInfo, accountantAddress, @@ -272,6 +388,15 @@ function Accountant({ accountantAddress: string; isNTT?: boolean; }) { + const [open, setOpen] = useState(false); + const handleOpen = useCallback((event: any) => { + event.stopPropagation(); + setOpen(true); + }, []); + const handleClose = useCallback((event: any) => { + setOpen(false); + }, []); + const pendingTransferInfo = useGetAccountantPendingTransfers(accountantAddress); const accountsInfo = useGetAccountantAccounts(accountantAddress); @@ -426,111 +551,126 @@ function Accountant({ ); const network = useCurrentEnvironment(); return ( - - {isNTT ? 'NTT ' : ''}Accountant - - - {Object.keys(pendingByChain) - .sort() - .map((chainId) => ( - - - {CHAIN_ICON_MAP[chainId] ? ( - {CHAIN_INFO_MAP[network][chainId].name} - ) : ( - {chainId} - )} + <> + + {isNTT ? 'NTT ' : ''}Accountant + {isNTT ? null : ( + + + + + + )} + + + {Object.keys(pendingByChain) + .sort() + .map((chainId) => ( + + + {CHAIN_ICON_MAP[chainId] ? ( + {CHAIN_INFO_MAP[network][chainId].name} + ) : ( + {chainId} + )} + + + {pendingByChain[Number(chainId)]} + - - {pendingByChain[Number(chainId)]} - - - ))} + ))} + - - } - > - {pendingTransferInfo.length ? ( + } + > + {pendingTransferInfo.length ? ( + + + table={guardianSigning} /> + + + ) : null} - table={guardianSigning} /> + + table={pendingTransfer} + paginated={!!pendingTransferInfo.length} + showRowCount={!!pendingTransferInfo.length} + /> + {pendingTransferInfo.length === 0 ? ( + + No pending transfers + + ) : null} - ) : null} - - - - table={pendingTransfer} - paginated={!!pendingTransferInfo.length} - showRowCount={!!pendingTransferInfo.length} - /> - {pendingTransferInfo.length === 0 ? ( - - No pending transfers - - ) : null} - - - - - - }> - Overview - - - table={overview} /> - - - - - - - - }> - Accounts ({accountsInfo.length}) - - - - - - ), - }} - placeholder="Search Token" - /> - table={accounts} paginated noWrap /> - - - - - + + + + }> + Overview + + + table={overview} /> + + + + + + + + }> + Accounts ({accountsInfo.length}) + + + + + + ), + }} + placeholder="Search Token" + /> + table={accounts} paginated noWrap /> + + + + + + + Accountant Transfer Search + + + + + ); } export default Accountant; diff --git a/package-lock.json b/package-lock.json index 763079b5..b2abc7a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -277,6 +277,7 @@ "react-scripts": "5.0.1", "react-timeago": "^7.1.0", "typescript": "^4.7.4", + "use-debounce": "^10.0.0", "web-vitals": "^2.1.4", "web3": "^1.7.5" }, @@ -24958,6 +24959,17 @@ "dev": true, "license": "MIT" }, + "node_modules/use-debounce": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.0.0.tgz", + "integrity": "sha512-XRjvlvCB46bah9IBXVnq/ACP2lxqXyZj0D9hj4K5OzNroMDpTEBg8Anuh1/UfRTRs7pLhQ+RiNxxwZu9+MVl1A==", + "engines": { + "node": ">= 16.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/utf-8-validate": { "version": "5.0.10", "hasInstallScript": true, @@ -33125,6 +33137,7 @@ "tty-browserify": "^0.0.1", "typescript": "^4.7.4", "url": "^0.11.0", + "use-debounce": "^10.0.0", "util": "^0.12.4", "vm-browserify": "^1.1.2", "web-vitals": "^2.1.4", @@ -44839,6 +44852,12 @@ "url-set-query": { "version": "1.0.0" }, + "use-debounce": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.0.0.tgz", + "integrity": "sha512-XRjvlvCB46bah9IBXVnq/ACP2lxqXyZj0D9hj4K5OzNroMDpTEBg8Anuh1/UfRTRs7pLhQ+RiNxxwZu9+MVl1A==", + "requires": {} + }, "utf-8-validate": { "version": "5.0.10", "requires": {