Filter explorer error reports when using custom cluster (#11911)

This commit is contained in:
Justin Starry
2020-08-30 22:19:39 +08:00
committed by GitHub
parent 998f9725d0
commit 8932e4821d
9 changed files with 112 additions and 63 deletions

View File

@ -6,7 +6,7 @@ import {
TransactionSignature, TransactionSignature,
Connection, Connection,
} from "@solana/web3.js"; } from "@solana/web3.js";
import { useCluster } from "../cluster"; import { useCluster, Cluster } from "../cluster";
import * as Cache from "providers/cache"; import * as Cache from "providers/cache";
import { ActionType, FetchStatus } from "providers/cache"; import { ActionType, FetchStatus } from "providers/cache";
@ -78,6 +78,7 @@ export function HistoryProvider({ children }: HistoryProviderProps) {
async function fetchAccountHistory( async function fetchAccountHistory(
dispatch: Dispatch, dispatch: Dispatch,
pubkey: PublicKey, pubkey: PublicKey,
cluster: Cluster,
url: string, url: string,
options: { before?: TransactionSignature; limit: number } options: { before?: TransactionSignature; limit: number }
) { ) {
@ -102,7 +103,9 @@ async function fetchAccountHistory(
}; };
status = FetchStatus.Fetched; status = FetchStatus.Fetched;
} catch (error) { } catch (error) {
if (cluster !== Cluster.Custom) {
Sentry.captureException(error, { tags: { url } }); Sentry.captureException(error, { tags: { url } });
}
status = FetchStatus.FetchFailed; status = FetchStatus.FetchFailed;
} }
dispatch({ dispatch({
@ -142,7 +145,7 @@ export function useAccountHistory(
} }
export function useFetchAccountHistory() { export function useFetchAccountHistory() {
const { url } = useCluster(); const { cluster, url } = useCluster();
const state = React.useContext(StateContext); const state = React.useContext(StateContext);
const dispatch = React.useContext(DispatchContext); const dispatch = React.useContext(DispatchContext);
if (!state || !dispatch) { if (!state || !dispatch) {
@ -151,15 +154,21 @@ export function useFetchAccountHistory() {
); );
} }
return (pubkey: PublicKey, refresh?: boolean) => { return React.useCallback(
(pubkey: PublicKey, refresh?: boolean) => {
const before = state.entries[pubkey.toBase58()]; const before = state.entries[pubkey.toBase58()];
if (!refresh && before?.data?.fetched && before.data.fetched.length > 0) { if (!refresh && before?.data?.fetched && before.data.fetched.length > 0) {
if (before.data.foundOldest) return; if (before.data.foundOldest) return;
const oldest = const oldest =
before.data.fetched[before.data.fetched.length - 1].signature; before.data.fetched[before.data.fetched.length - 1].signature;
fetchAccountHistory(dispatch, pubkey, url, { before: oldest, limit: 25 }); fetchAccountHistory(dispatch, pubkey, cluster, url, {
before: oldest,
limit: 25,
});
} else { } else {
fetchAccountHistory(dispatch, pubkey, url, { limit: 25 }); fetchAccountHistory(dispatch, pubkey, cluster, url, { limit: 25 });
} }
}; },
[state, dispatch, cluster, url]
);
} }

View File

@ -2,7 +2,7 @@ import React from "react";
import * as Sentry from "@sentry/react"; import * as Sentry from "@sentry/react";
import { StakeAccount as StakeAccountWasm } from "solana-sdk-wasm"; import { StakeAccount as StakeAccountWasm } from "solana-sdk-wasm";
import { PublicKey, Connection, StakeProgram } from "@solana/web3.js"; import { PublicKey, Connection, StakeProgram } from "@solana/web3.js";
import { useCluster } from "../cluster"; import { useCluster, Cluster } from "../cluster";
import { HistoryProvider } from "./history"; import { HistoryProvider } from "./history";
import { TokensProvider, TOKEN_PROGRAM_ID } from "./tokens"; import { TokensProvider, TOKEN_PROGRAM_ID } from "./tokens";
import { coerce } from "superstruct"; import { coerce } from "superstruct";
@ -72,6 +72,7 @@ export function AccountsProvider({ children }: AccountsProviderProps) {
async function fetchAccountInfo( async function fetchAccountInfo(
dispatch: Dispatch, dispatch: Dispatch,
pubkey: PublicKey, pubkey: PublicKey,
cluster: Cluster,
url: string url: string
) { ) {
dispatch({ dispatch({
@ -149,7 +150,9 @@ async function fetchAccountInfo(
data = { pubkey, lamports, details }; data = { pubkey, lamports, details };
fetchStatus = FetchStatus.Fetched; fetchStatus = FetchStatus.Fetched;
} catch (error) { } catch (error) {
if (cluster !== Cluster.Custom) {
Sentry.captureException(error, { tags: { url } }); Sentry.captureException(error, { tags: { url } });
}
fetchStatus = FetchStatus.FetchFailed; fetchStatus = FetchStatus.FetchFailed;
} }
dispatch({ dispatch({
@ -231,11 +234,11 @@ export function useFetchAccountInfo() {
); );
} }
const { url } = useCluster(); const { cluster, url } = useCluster();
return React.useCallback( return React.useCallback(
(pubkey: PublicKey) => { (pubkey: PublicKey) => {
fetchAccountInfo(dispatch, pubkey, url); fetchAccountInfo(dispatch, pubkey, cluster, url);
}, },
[dispatch, url] [dispatch, cluster, url]
); );
} }

View File

@ -4,7 +4,7 @@ import { Connection, PublicKey } from "@solana/web3.js";
import * as Cache from "providers/cache"; import * as Cache from "providers/cache";
import { ActionType, FetchStatus } from "providers/cache"; import { ActionType, FetchStatus } from "providers/cache";
import { TokenAccountInfo } from "validators/accounts/token"; import { TokenAccountInfo } from "validators/accounts/token";
import { useCluster } from "../cluster"; import { useCluster, Cluster } from "../cluster";
import { coerce } from "superstruct"; import { coerce } from "superstruct";
export type TokenInfoWithPubkey = { export type TokenInfoWithPubkey = {
@ -47,6 +47,7 @@ export const TOKEN_PROGRAM_ID = new PublicKey(
async function fetchAccountTokens( async function fetchAccountTokens(
dispatch: Dispatch, dispatch: Dispatch,
pubkey: PublicKey, pubkey: PublicKey,
cluster: Cluster,
url: string url: string
) { ) {
const key = pubkey.toBase58(); const key = pubkey.toBase58();
@ -73,7 +74,9 @@ async function fetchAccountTokens(
}; };
status = FetchStatus.Fetched; status = FetchStatus.Fetched;
} catch (error) { } catch (error) {
if (cluster !== Cluster.Custom) {
Sentry.captureException(error, { tags: { url } }); Sentry.captureException(error, { tags: { url } });
}
status = FetchStatus.FetchFailed; status = FetchStatus.FetchFailed;
} }
dispatch({ type: ActionType.Update, url, status, data, key }); dispatch({ type: ActionType.Update, url, status, data, key });
@ -101,8 +104,11 @@ export function useFetchAccountOwnedTokens() {
); );
} }
const { url } = useCluster(); const { cluster, url } = useCluster();
return (pubkey: PublicKey) => { return React.useCallback(
fetchAccountTokens(dispatch, pubkey, url); (pubkey: PublicKey) => {
}; fetchAccountTokens(dispatch, pubkey, cluster, url);
},
[dispatch, cluster, url]
);
} }

View File

@ -183,9 +183,11 @@ async function updateCluster(
firstAvailableBlock, firstAvailableBlock,
}); });
} catch (error) { } catch (error) {
if (cluster !== Cluster.Custom) {
Sentry.captureException(error, { Sentry.captureException(error, {
tags: { clusterUrl: clusterUrl(cluster, customUrl) }, tags: { clusterUrl: clusterUrl(cluster, customUrl) },
}); });
}
dispatch({ status: ClusterStatus.Failure, cluster, customUrl }); dispatch({ status: ClusterStatus.Failure, cluster, customUrl });
} }
} }

View File

@ -1,6 +1,6 @@
import React from "react"; import React from "react";
import * as Sentry from "@sentry/react"; import * as Sentry from "@sentry/react";
import { useCluster } from "providers/cluster"; import { useCluster, Cluster } from "providers/cluster";
import * as Cache from "providers/cache"; import * as Cache from "providers/cache";
import { ActionType, FetchStatus } from "providers/cache"; import { ActionType, FetchStatus } from "providers/cache";
import { import {
@ -41,6 +41,7 @@ export function LargestAccountsProvider({ children }: ProviderProps) {
async function fetchLargestAccounts( async function fetchLargestAccounts(
dispatch: Dispatch, dispatch: Dispatch,
pubkey: PublicKey, pubkey: PublicKey,
cluster: Cluster,
url: string url: string
) { ) {
dispatch({ dispatch({
@ -60,7 +61,9 @@ async function fetchLargestAccounts(
}; };
fetchStatus = FetchStatus.Fetched; fetchStatus = FetchStatus.Fetched;
} catch (error) { } catch (error) {
if (cluster !== Cluster.Custom) {
Sentry.captureException(error, { tags: { url } }); Sentry.captureException(error, { tags: { url } });
}
fetchStatus = FetchStatus.FetchFailed; fetchStatus = FetchStatus.FetchFailed;
} }
dispatch({ dispatch({
@ -80,10 +83,13 @@ export function useFetchTokenLargestAccounts() {
); );
} }
const { url } = useCluster(); const { cluster, url } = useCluster();
return (pubkey: PublicKey) => { return React.useCallback(
fetchLargestAccounts(dispatch, pubkey, url); (pubkey: PublicKey) => {
}; fetchLargestAccounts(dispatch, pubkey, cluster, url);
},
[dispatch, cluster, url]
);
} }
export function useTokenLargestTokens( export function useTokenLargestTokens(

View File

@ -2,7 +2,7 @@ import React from "react";
import * as Sentry from "@sentry/react"; import * as Sentry from "@sentry/react";
import { AccountBalancePair, Connection } from "@solana/web3.js"; import { AccountBalancePair, Connection } from "@solana/web3.js";
import { useCluster, ClusterStatus } from "./cluster"; import { useCluster, ClusterStatus, Cluster } from "./cluster";
export enum Status { export enum Status {
Idle, Idle,
@ -25,7 +25,7 @@ const DispatchContext = React.createContext<Dispatch | undefined>(undefined);
type Props = { children: React.ReactNode }; type Props = { children: React.ReactNode };
export function RichListProvider({ children }: Props) { export function RichListProvider({ children }: Props) {
const [state, setState] = React.useState<State>(Status.Idle); const [state, setState] = React.useState<State>(Status.Idle);
const { status: clusterStatus, url } = useCluster(); const { status: clusterStatus, cluster, url } = useCluster();
React.useEffect(() => { React.useEffect(() => {
if (state !== Status.Idle) { if (state !== Status.Idle) {
@ -35,12 +35,12 @@ export function RichListProvider({ children }: Props) {
break; break;
} }
case ClusterStatus.Connected: { case ClusterStatus.Connected: {
fetch(setState, url); fetch(setState, cluster, url);
break; break;
} }
} }
} }
}, [clusterStatus, url]); // eslint-disable-line react-hooks/exhaustive-deps }, [clusterStatus, cluster, url]); // eslint-disable-line react-hooks/exhaustive-deps
return ( return (
<StateContext.Provider value={state}> <StateContext.Provider value={state}>
@ -51,7 +51,7 @@ export function RichListProvider({ children }: Props) {
); );
} }
async function fetch(dispatch: Dispatch, url: string) { async function fetch(dispatch: Dispatch, cluster: Cluster, url: string) {
dispatch(Status.Connecting); dispatch(Status.Connecting);
try { try {
@ -71,7 +71,9 @@ async function fetch(dispatch: Dispatch, url: string) {
return { total, circulating, nonCirculating }; return { total, circulating, nonCirculating };
}); });
} catch (err) { } catch (err) {
if (cluster !== Cluster.Custom) {
Sentry.captureException(err, { tags: { url } }); Sentry.captureException(err, { tags: { url } });
}
dispatch("Failed to fetch top accounts"); dispatch("Failed to fetch top accounts");
} }
} }
@ -90,6 +92,8 @@ export function useFetchRichList() {
throw new Error(`useFetchRichList must be used within a RichListProvider`); throw new Error(`useFetchRichList must be used within a RichListProvider`);
} }
const { url } = useCluster(); const { cluster, url } = useCluster();
return () => fetch(dispatch, url); return React.useCallback(() => {
fetch(dispatch, cluster, url);
}, [dispatch, cluster, url]);
} }

View File

@ -2,7 +2,7 @@ import React from "react";
import * as Sentry from "@sentry/react"; import * as Sentry from "@sentry/react";
import { Supply, Connection } from "@solana/web3.js"; import { Supply, Connection } from "@solana/web3.js";
import { useCluster, ClusterStatus } from "./cluster"; import { useCluster, ClusterStatus, Cluster } from "./cluster";
export enum Status { export enum Status {
Idle, Idle,
@ -19,15 +19,16 @@ const DispatchContext = React.createContext<Dispatch | undefined>(undefined);
type Props = { children: React.ReactNode }; type Props = { children: React.ReactNode };
export function SupplyProvider({ children }: Props) { export function SupplyProvider({ children }: Props) {
const [state, setState] = React.useState<State>(Status.Idle); const [state, setState] = React.useState<State>(Status.Idle);
const { status: clusterStatus, url } = useCluster(); const { status: clusterStatus, cluster, url } = useCluster();
React.useEffect(() => { React.useEffect(() => {
if (state !== Status.Idle) { if (state !== Status.Idle) {
if (clusterStatus === ClusterStatus.Connecting) if (clusterStatus === ClusterStatus.Connecting)
setState(Status.Disconnected); setState(Status.Disconnected);
if (clusterStatus === ClusterStatus.Connected) fetch(setState, url); if (clusterStatus === ClusterStatus.Connected)
fetch(setState, cluster, url);
} }
}, [clusterStatus, url]); // eslint-disable-line react-hooks/exhaustive-deps }, [clusterStatus, cluster, url]); // eslint-disable-line react-hooks/exhaustive-deps
return ( return (
<StateContext.Provider value={state}> <StateContext.Provider value={state}>
@ -38,7 +39,7 @@ export function SupplyProvider({ children }: Props) {
); );
} }
async function fetch(dispatch: Dispatch, url: string) { async function fetch(dispatch: Dispatch, cluster: Cluster, url: string) {
dispatch(Status.Connecting); dispatch(Status.Connecting);
try { try {
@ -51,7 +52,9 @@ async function fetch(dispatch: Dispatch, url: string) {
return supply; return supply;
}); });
} catch (err) { } catch (err) {
if (cluster !== Cluster.Custom) {
Sentry.captureException(err, { tags: { url } }); Sentry.captureException(err, { tags: { url } });
}
dispatch("Failed to fetch supply"); dispatch("Failed to fetch supply");
} }
} }
@ -70,6 +73,8 @@ export function useFetchSupply() {
throw new Error(`useFetchSupply must be used within a SupplyProvider`); throw new Error(`useFetchSupply must be used within a SupplyProvider`);
} }
const { url } = useCluster(); const { cluster, url } = useCluster();
return () => fetch(dispatch, url); return React.useCallback(() => {
fetch(dispatch, cluster, url);
}, [dispatch, cluster, url]);
} }

View File

@ -5,7 +5,7 @@ import {
TransactionSignature, TransactionSignature,
ParsedConfirmedTransaction, ParsedConfirmedTransaction,
} from "@solana/web3.js"; } from "@solana/web3.js";
import { useCluster } from "../cluster"; import { useCluster, Cluster } from "../cluster";
import { CACHED_DETAILS, isCached } from "./cached"; import { CACHED_DETAILS, isCached } from "./cached";
import * as Cache from "providers/cache"; import * as Cache from "providers/cache";
import { ActionType, FetchStatus } from "providers/cache"; import { ActionType, FetchStatus } from "providers/cache";
@ -43,6 +43,7 @@ export function DetailsProvider({ children }: DetailsProviderProps) {
async function fetchDetails( async function fetchDetails(
dispatch: Dispatch, dispatch: Dispatch,
signature: TransactionSignature, signature: TransactionSignature,
cluster: Cluster,
url: string url: string
) { ) {
dispatch({ dispatch({
@ -64,7 +65,9 @@ async function fetchDetails(
); );
fetchStatus = FetchStatus.Fetched; fetchStatus = FetchStatus.Fetched;
} catch (error) { } catch (error) {
if (cluster !== Cluster.Custom) {
Sentry.captureException(error, { tags: { url } }); Sentry.captureException(error, { tags: { url } });
}
fetchStatus = FetchStatus.FetchFailed; fetchStatus = FetchStatus.FetchFailed;
} }
} }
@ -85,10 +88,13 @@ export function useFetchTransactionDetails() {
); );
} }
const { url } = useCluster(); const { cluster, url } = useCluster();
return (signature: TransactionSignature) => { return React.useCallback(
url && fetchDetails(dispatch, signature, url); (signature: TransactionSignature) => {
}; url && fetchDetails(dispatch, signature, cluster, url);
},
[dispatch, cluster, url]
);
} }
export function useTransactionDetails( export function useTransactionDetails(

View File

@ -5,7 +5,7 @@ import {
Connection, Connection,
SignatureResult, SignatureResult,
} from "@solana/web3.js"; } from "@solana/web3.js";
import { useCluster } from "../cluster"; import { useCluster, Cluster } from "../cluster";
import { DetailsProvider } from "./details"; import { DetailsProvider } from "./details";
import * as Cache from "providers/cache"; import * as Cache from "providers/cache";
import { ActionType, FetchStatus } from "providers/cache"; import { ActionType, FetchStatus } from "providers/cache";
@ -56,6 +56,7 @@ export function TransactionsProvider({ children }: TransactionsProviderProps) {
export async function fetchTransactionStatus( export async function fetchTransactionStatus(
dispatch: Dispatch, dispatch: Dispatch,
signature: TransactionSignature, signature: TransactionSignature,
cluster: Cluster,
url: string url: string
) { ) {
dispatch({ dispatch({
@ -84,7 +85,9 @@ export async function fetchTransactionStatus(
try { try {
blockTime = await connection.getBlockTime(value.slot); blockTime = await connection.getBlockTime(value.slot);
} catch (error) { } catch (error) {
Sentry.captureException(error, { tags: { slot: `${value.slot}` } }); Sentry.captureException(error, {
tags: { slot: `${value.slot}`, url },
});
} }
let timestamp: Timestamp = let timestamp: Timestamp =
blockTime !== null ? blockTime : "unavailable"; blockTime !== null ? blockTime : "unavailable";
@ -106,7 +109,9 @@ export async function fetchTransactionStatus(
data = { signature, info }; data = { signature, info };
fetchStatus = FetchStatus.Fetched; fetchStatus = FetchStatus.Fetched;
} catch (error) { } catch (error) {
if (cluster !== Cluster.Custom) {
Sentry.captureException(error, { tags: { url } }); Sentry.captureException(error, { tags: { url } });
}
fetchStatus = FetchStatus.FetchFailed; fetchStatus = FetchStatus.FetchFailed;
} }
} }
@ -156,8 +161,11 @@ export function useFetchTransactionStatus() {
); );
} }
const { url } = useCluster(); const { cluster, url } = useCluster();
return (signature: TransactionSignature) => { return React.useCallback(
fetchTransactionStatus(dispatch, signature, url); (signature: TransactionSignature) => {
}; fetchTransactionStatus(dispatch, signature, cluster, url);
},
[dispatch, cluster, url]
);
} }