Skip to content

Commit

Permalink
Use new history API in explorer (solana-labs#11449)
Browse files Browse the repository at this point in the history
  • Loading branch information
jstarry authored Aug 7, 2020
1 parent 4b52306 commit 4f2f9bd
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 271 deletions.
6 changes: 3 additions & 3 deletions explorer/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion explorer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"dependencies": {
"@react-hook/debounce": "^3.0.0",
"@solana/web3.js": "^0.66.0",
"@solana/web3.js": "^0.66.1",
"@testing-library/jest-dom": "^5.11.2",
"@testing-library/react": "^10.4.8",
"@testing-library/user-event": "^12.1.0",
Expand Down
51 changes: 25 additions & 26 deletions explorer/src/components/AccountDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,7 @@ function HistoryCard({ pubkey }: { pubkey: PublicKey }) {

if (!info || !history || info.lamports === undefined) {
return null;
} else if (
history.fetched === undefined ||
history.fetchedRange === undefined
) {
} else if (history.fetched === undefined) {
if (history.status === FetchStatus.Fetching) {
return <LoadingCard message="Loading history" />;
}
Expand All @@ -251,10 +248,8 @@ function HistoryCard({ pubkey }: { pubkey: PublicKey }) {
return (
<ErrorCard
retry={loadMore}
retryText="Look back further"
text={
"No transaction history found since slot " + history.fetchedRange.min
}
retryText="Try again"
text="No transaction history found"
/>
);
}
Expand All @@ -263,18 +258,18 @@ function HistoryCard({ pubkey }: { pubkey: PublicKey }) {
const transactions = history.fetched;

for (var i = 0; i < transactions.length; i++) {
const slot = transactions[i].status.slot;
const slot = transactions[i].slot;
const slotTransactions = [transactions[i]];
while (i + 1 < transactions.length) {
const nextSlot = transactions[i + 1].status.slot;
const nextSlot = transactions[i + 1].slot;
if (nextSlot !== slot) break;
slotTransactions.push(transactions[++i]);
}

slotTransactions.forEach(({ signature, status }, index) => {
slotTransactions.forEach(({ signature, err }) => {
let statusText;
let statusClass;
if (status.err) {
if (err) {
statusClass = "warning";
statusText = "Failed";
} else {
Expand Down Expand Up @@ -338,20 +333,24 @@ function HistoryCard({ pubkey }: { pubkey: PublicKey }) {
</div>

<div className="card-footer">
<button
className="btn btn-primary w-100"
onClick={loadMore}
disabled={fetching}
>
{fetching ? (
<>
<span className="spinner-grow spinner-grow-sm mr-2"></span>
Loading
</>
) : (
"Load More"
)}
</button>
{history.foundOldest ? (
<div className="text-muted text-center">Fetched full history</div>
) : (
<button
className="btn btn-primary w-100"
onClick={loadMore}
disabled={fetching}
>
{fetching ? (
<>
<span className="spinner-grow spinner-grow-sm mr-2"></span>
Loading
</>
) : (
"Load More"
)}
</button>
)}
</div>
</div>
);
Expand Down
117 changes: 71 additions & 46 deletions explorer/src/providers/accounts/history.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import React from "react";
import { PublicKey } from "@solana/web3.js";
import {
PublicKey,
ConfirmedSignatureInfo,
TransactionSignature,
Connection,
} from "@solana/web3.js";
import { useAccounts, FetchStatus } from "./index";
import { useCluster } from "../cluster";
import {
HistoryManager,
HistoricalTransaction,
SlotRange,
} from "./historyManager";

interface AccountHistory {
status: FetchStatus;
fetched?: HistoricalTransaction[];
fetchedRange?: SlotRange;
fetched?: ConfirmedSignatureInfo[];
foundOldest: boolean;
}

type State = { [address: string]: AccountHistory };
Expand All @@ -26,8 +26,9 @@ interface Update {
type: ActionType.Update;
pubkey: PublicKey;
status: FetchStatus;
fetched?: HistoricalTransaction[];
fetchedRange?: SlotRange;
fetched?: ConfirmedSignatureInfo[];
before?: TransactionSignature;
foundOldest?: boolean;
}

interface Add {
Expand All @@ -42,6 +43,24 @@ interface Clear {
type Action = Update | Add | Clear;
type Dispatch = (action: Action) => void;

function combineFetched(
fetched: ConfirmedSignatureInfo[] | undefined,
current: ConfirmedSignatureInfo[] | undefined,
before: TransactionSignature | undefined
) {
if (fetched === undefined) {
return current;
} else if (current === undefined) {
return fetched;
}

if (current.length > 0 && current[current.length - 1].signature === before) {
return current.concat(fetched);
} else {
return fetched;
}
}

function reducer(state: State, action: Action): State {
switch (action.type) {
case ActionType.Add: {
Expand All @@ -50,6 +69,7 @@ function reducer(state: State, action: Action): State {
if (!details[address]) {
details[address] = {
status: FetchStatus.Fetching,
foundOldest: false,
};
}
return details;
Expand All @@ -58,18 +78,16 @@ function reducer(state: State, action: Action): State {
case ActionType.Update: {
const address = action.pubkey.toBase58();
if (state[address]) {
const fetched = action.fetched
? action.fetched
: state[address].fetched;
const fetchedRange = action.fetchedRange
? action.fetchedRange
: state[address].fetchedRange;
return {
...state,
[address]: {
status: action.status,
fetched,
fetchedRange,
fetched: combineFetched(
action.fetched,
state[address].fetched,
action.before
),
foundOldest: action.foundOldest || state[address].foundOldest,
},
};
}
Expand All @@ -83,9 +101,6 @@ function reducer(state: State, action: Action): State {
return state;
}

const ManagerContext = React.createContext<HistoryManager | undefined>(
undefined
);
const StateContext = React.createContext<State | undefined>(undefined);
const DispatchContext = React.createContext<Dispatch | undefined>(undefined);

Expand All @@ -95,9 +110,7 @@ export function HistoryProvider({ children }: HistoryProviderProps) {
const { accounts, lastFetchedAddress } = useAccounts();
const { url } = useCluster();

const manager = React.useRef(new HistoryManager(url));
React.useEffect(() => {
manager.current = new HistoryManager(url);
dispatch({ type: ActionType.Clear });
}, [url]);

Expand All @@ -110,32 +123,27 @@ export function HistoryProvider({ children }: HistoryProviderProps) {
const noHistory = !state[lastFetchedAddress];
if (infoFetched && noHistory) {
dispatch({ type: ActionType.Add, address: lastFetchedAddress });
fetchAccountHistory(
dispatch,
new PublicKey(lastFetchedAddress),
manager.current,
true
);
fetchAccountHistory(dispatch, new PublicKey(lastFetchedAddress), url, {
limit: 10,
});
}
}
}, [accounts, lastFetchedAddress]); // eslint-disable-line react-hooks/exhaustive-deps

return (
<ManagerContext.Provider value={manager.current}>
<StateContext.Provider value={state}>
<DispatchContext.Provider value={dispatch}>
{children}
</DispatchContext.Provider>
</StateContext.Provider>
</ManagerContext.Provider>
<StateContext.Provider value={state}>
<DispatchContext.Provider value={dispatch}>
{children}
</DispatchContext.Provider>
</StateContext.Provider>
);
}

async function fetchAccountHistory(
dispatch: Dispatch,
pubkey: PublicKey,
manager: HistoryManager,
refresh?: boolean
url: string,
options: { before?: TransactionSignature; limit: number }
) {
dispatch({
type: ActionType.Update,
Expand All @@ -145,17 +153,27 @@ async function fetchAccountHistory(

let status;
let fetched;
let fetchedRange;
let foundOldest;
try {
await manager.fetchAccountHistory(pubkey, refresh || false);
fetched = manager.accountHistory.get(pubkey.toBase58()) || undefined;
fetchedRange = manager.accountRanges.get(pubkey.toBase58()) || undefined;
const connection = new Connection(url);
fetched = await connection.getConfirmedSignaturesForAddress2(
pubkey,
options
);
foundOldest = fetched.length < options.limit;
status = FetchStatus.Fetched;
} catch (error) {
console.error("Failed to fetch account history", error);
status = FetchStatus.FetchFailed;
}
dispatch({ type: ActionType.Update, status, fetched, fetchedRange, pubkey });
dispatch({
type: ActionType.Update,
status,
fetched,
before: options?.before,
pubkey,
foundOldest,
});
}

export function useAccountHistory(address: string) {
Expand All @@ -169,15 +187,22 @@ export function useAccountHistory(address: string) {
}

export function useFetchAccountHistory() {
const manager = React.useContext(ManagerContext);
const { url } = useCluster();
const state = React.useContext(StateContext);
const dispatch = React.useContext(DispatchContext);
if (!manager || !dispatch) {
if (!state || !dispatch) {
throw new Error(
`useFetchAccountHistory must be used within a AccountsProvider`
);
}

return (pubkey: PublicKey, refresh?: boolean) => {
fetchAccountHistory(dispatch, pubkey, manager, refresh);
const before = state[pubkey.toBase58()];
if (!refresh && before && before.fetched && before.fetched.length > 0) {
const oldest = before.fetched[before.fetched.length - 1].signature;
fetchAccountHistory(dispatch, pubkey, url, { before: oldest, limit: 25 });
} else {
fetchAccountHistory(dispatch, pubkey, url, { limit: 25 });
}
};
}
Loading

0 comments on commit 4f2f9bd

Please sign in to comment.