diff --git a/explorer/src/components/AccountsCard.tsx b/explorer/src/components/AccountsCard.tsx index 044342bbc7..9bf6afa634 100644 --- a/explorer/src/components/AccountsCard.tsx +++ b/explorer/src/components/AccountsCard.tsx @@ -29,7 +29,7 @@ function AccountsCard() { } dispatch({ type: ActionType.Input, pubkey }); - fetchAccountInfo(dispatch, idCounter + 1, pubkey, url); + fetchAccountInfo(dispatch, address, url); const inputEl = addressInput.current; if (inputEl) { diff --git a/explorer/src/components/TransactionsCard.tsx b/explorer/src/components/TransactionsCard.tsx index 7f51cfd1c9..8ad0b40956 100644 --- a/explorer/src/components/TransactionsCard.tsx +++ b/explorer/src/components/TransactionsCard.tsx @@ -35,7 +35,7 @@ function TransactionsCard() { } dispatch({ type: ActionType.InputSignature, signature }); - checkTransactionStatus(dispatch, idCounter + 1, signature, url); + checkTransactionStatus(dispatch, signature, url); const inputEl = signatureInput.current; if (inputEl) { @@ -141,7 +141,7 @@ const renderTransactionRow = (transaction: Transaction) => { const confirmationsText = `${transaction.confirmations || "-"}`; return ( - + {transaction.id} diff --git a/explorer/src/providers/accounts.tsx b/explorer/src/providers/accounts.tsx index aaaab67730..adecc3c5a3 100644 --- a/explorer/src/providers/accounts.tsx +++ b/explorer/src/providers/accounts.tsx @@ -29,7 +29,7 @@ export interface Account { details?: Details; } -type Accounts = { [id: number]: Account }; +type Accounts = { [address: string]: Account }; interface State { idCounter: number; accounts: Accounts; @@ -42,7 +42,7 @@ export enum ActionType { interface Update { type: ActionType.Update; - id: number; + address: string; status: Status; details?: Details; } @@ -58,10 +58,12 @@ type Dispatch = (action: Action) => void; function reducer(state: State, action: Action): State { switch (action.type) { case ActionType.Input: { + const address = action.pubkey.toBase58(); + if (!!state.accounts[address]) return state; const idCounter = state.idCounter + 1; const accounts = { ...state.accounts, - [idCounter]: { + [address]: { id: idCounter, status: Status.Checking, source: Source.Input, @@ -71,7 +73,7 @@ function reducer(state: State, action: Action): State { return { ...state, accounts, idCounter }; } case ActionType.Update: { - let account = state.accounts[action.id]; + let account = state.accounts[action.address]; if (account) { account = { ...account, @@ -80,7 +82,7 @@ function reducer(state: State, action: Action): State { }; const accounts = { ...state.accounts, - [action.id]: account + [action.address]: account }; return { ...state, accounts }; } @@ -90,9 +92,9 @@ function reducer(state: State, action: Action): State { return state; } -function urlPublicKeys(): Array { - const keys: Array = []; - return keys +function urlAddresses(): Array { + const addresses: Array = []; + return addresses .concat(findGetParameter("account")?.split(",") || []) .concat(findGetParameter("accounts")?.split(",") || []) .concat(findPathSegment("account")?.split(",") || []) @@ -100,21 +102,27 @@ function urlPublicKeys(): Array { .concat(findGetParameter("address")?.split(",") || []) .concat(findGetParameter("addresses")?.split(",") || []) .concat(findPathSegment("address")?.split(",") || []) - .concat(findPathSegment("addresses")?.split(",") || []) - .map(key => new PublicKey(key)); + .concat(findPathSegment("addresses")?.split(",") || []); } function initState(): State { let idCounter = 0; - const pubkeys = urlPublicKeys(); - const accounts = pubkeys.reduce((accounts: Accounts, pubkey) => { - const id = ++idCounter; - accounts[id] = { - id, - status: Status.Checking, - source: Source.Url, - pubkey - }; + const addresses = urlAddresses(); + const accounts = addresses.reduce((accounts: Accounts, address) => { + if (!!accounts[address]) return accounts; + try { + const pubkey = new PublicKey(address); + const id = ++idCounter; + accounts[address] = { + id, + status: Status.Checking, + source: Source.Url, + pubkey + }; + } catch (err) { + // TODO display to user + console.error(err); + } return accounts; }, {}); return { idCounter, accounts }; @@ -133,8 +141,8 @@ export function AccountsProvider({ children }: AccountsProviderProps) { React.useEffect(() => { if (status !== ClusterStatus.Connected) return; - Object.values(state.accounts).forEach(account => { - fetchAccountInfo(dispatch, account.id, account.pubkey, url); + Object.keys(state.accounts).forEach(address => { + fetchAccountInfo(dispatch, address, url); }); }, [status, url]); // eslint-disable-line react-hooks/exhaustive-deps @@ -149,20 +157,21 @@ export function AccountsProvider({ children }: AccountsProviderProps) { export async function fetchAccountInfo( dispatch: Dispatch, - id: number, - pubkey: PublicKey, + address: string, url: string ) { dispatch({ type: ActionType.Update, status: Status.Checking, - id + address }); let status; let details; try { - const result = await new Connection(url).getAccountInfo(pubkey); + const result = await new Connection(url).getAccountInfo( + new PublicKey(address) + ); details = { space: result.data.length, executable: result.executable, @@ -174,7 +183,7 @@ export async function fetchAccountInfo( console.error("Failed to fetch account info", error); status = Status.CheckFailed; } - dispatch({ type: ActionType.Update, status, details, id }); + dispatch({ type: ActionType.Update, status, details, address }); } export function useAccounts() { diff --git a/explorer/src/providers/transactions.tsx b/explorer/src/providers/transactions.tsx index 8c443410a2..5fb1af1ef2 100644 --- a/explorer/src/providers/transactions.tsx +++ b/explorer/src/providers/transactions.tsx @@ -27,7 +27,7 @@ export interface Transaction { signature: TransactionSignature; } -type Transactions = { [id: number]: Transaction }; +type Transactions = { [signature: string]: Transaction }; interface State { idCounter: number; transactions: Transactions; @@ -40,7 +40,7 @@ export enum ActionType { interface UpdateStatus { type: ActionType.UpdateStatus; - id: number; + signature: TransactionSignature; status: Status; slot?: number; confirmations?: Confirmations; @@ -57,10 +57,12 @@ type Dispatch = (action: Action) => void; function reducer(state: State, action: Action): State { switch (action.type) { case ActionType.InputSignature: { + if (!!state.transactions[action.signature]) return state; + const idCounter = state.idCounter + 1; const transactions = { ...state.transactions, - [idCounter]: { + [action.signature]: { id: idCounter, status: Status.Checking, source: Source.Input, @@ -70,7 +72,7 @@ function reducer(state: State, action: Action): State { return { ...state, transactions, idCounter }; } case ActionType.UpdateStatus: { - let transaction = state.transactions[action.id]; + let transaction = state.transactions[action.signature]; if (transaction) { transaction = { ...transaction, @@ -80,7 +82,7 @@ function reducer(state: State, action: Action): State { }; const transactions = { ...state.transactions, - [action.id]: transaction + [action.signature]: transaction }; return { ...state, transactions }; } @@ -108,12 +110,13 @@ function initState(): State { const signatures = urlSignatures(); const transactions = signatures.reduce( (transactions: Transactions, signature) => { - const id = ++idCounter; - transactions[id] = { - id, + if (!!transactions[signature]) return transactions; + idCounter++; + transactions[signature] = { + id: idCounter, + signature, status: Status.Checking, - source: Source.Url, - signature + source: Source.Url }; return transactions; }, @@ -140,8 +143,8 @@ export function TransactionsProvider({ children }: TransactionsProviderProps) { createDevTransaction(dispatch, url); } - Object.values(state.transactions).forEach(tx => { - checkTransactionStatus(dispatch, tx.id, tx.signature, url); + Object.keys(state.transactions).forEach(signature => { + checkTransactionStatus(dispatch, signature, url); }); }, [status, url]); // eslint-disable-line react-hooks/exhaustive-deps @@ -158,11 +161,12 @@ async function createDevTransaction(dispatch: Dispatch, url: string) { try { const connection = new Connection(url); const signature = await connection.requestAirdrop( - new PublicKey(0), + new PublicKey(1), 1, "recent" ); dispatch({ type: ActionType.InputSignature, signature }); + checkTransactionStatus(dispatch, signature, url); } catch (error) { console.error("Failed to create dev transaction", error); } @@ -170,14 +174,13 @@ async function createDevTransaction(dispatch: Dispatch, url: string) { export async function checkTransactionStatus( dispatch: Dispatch, - id: number, signature: TransactionSignature, url: string ) { dispatch({ type: ActionType.UpdateStatus, status: Status.Checking, - id + signature }); let status; @@ -206,7 +209,13 @@ export async function checkTransactionStatus( console.error("Failed to check transaction status", error); status = Status.CheckFailed; } - dispatch({ type: ActionType.UpdateStatus, status, slot, confirmations, id }); + dispatch({ + type: ActionType.UpdateStatus, + status, + slot, + confirmations, + signature + }); } export function useTransactions() {