diff --git a/explorer/package-lock.json b/explorer/package-lock.json
index dc372d0d3b..fa55bec5db 100644
--- a/explorer/package-lock.json
+++ b/explorer/package-lock.json
@@ -2649,6 +2649,76 @@
"resolved": "https://registry.npmjs.org/@react-hook/latest/-/latest-1.0.2.tgz",
"integrity": "sha512-zLtOIToct1EBTbwldkMJsXC2eCsmWOOP7z6UG0M/sCgnPExtIjvVMCpPESvPnMbQzDZytXVy0nvMbUuK2gZs2A=="
},
+ "@sentry/browser": {
+ "version": "5.21.1",
+ "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.21.1.tgz",
+ "integrity": "sha512-sUxsW545klZxJE4iBAYQ8SuVS85HTOGNmIIIZWFUogB5oW3O0L+nJluXEqf/pHU82LnjDIzqsWCYQ0cRUaeYow==",
+ "requires": {
+ "@sentry/core": "5.21.1",
+ "@sentry/types": "5.21.1",
+ "@sentry/utils": "5.21.1",
+ "tslib": "^1.9.3"
+ }
+ },
+ "@sentry/core": {
+ "version": "5.21.1",
+ "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.21.1.tgz",
+ "integrity": "sha512-Luulwx3GLUiY0gmHOhU+4eSga28Ce8DwoBcRq9GkGuhPu9r80057d5urxrDLp/leIZBXVvpY7tvmSN/rMtvF9w==",
+ "requires": {
+ "@sentry/hub": "5.21.1",
+ "@sentry/minimal": "5.21.1",
+ "@sentry/types": "5.21.1",
+ "@sentry/utils": "5.21.1",
+ "tslib": "^1.9.3"
+ }
+ },
+ "@sentry/hub": {
+ "version": "5.21.1",
+ "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.21.1.tgz",
+ "integrity": "sha512-x5i9Ggi5ZYMhBYL5kyTu2fUJ6owjKH2tgJL3UExoZdRyZkbLAFZb+DtfSnteWgQ6wriGfgPD3r/hAIEdaomk2A==",
+ "requires": {
+ "@sentry/types": "5.21.1",
+ "@sentry/utils": "5.21.1",
+ "tslib": "^1.9.3"
+ }
+ },
+ "@sentry/minimal": {
+ "version": "5.21.1",
+ "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.21.1.tgz",
+ "integrity": "sha512-OBVPASZ+mcXMKajvJon9RjEZ+ny3+VGhOI66acoP1hmYxKvji1OC2bYEuP1r4qtHxWVLAdV7qFj3EQ9ckErZmQ==",
+ "requires": {
+ "@sentry/hub": "5.21.1",
+ "@sentry/types": "5.21.1",
+ "tslib": "^1.9.3"
+ }
+ },
+ "@sentry/react": {
+ "version": "5.21.1",
+ "resolved": "https://registry.npmjs.org/@sentry/react/-/react-5.21.1.tgz",
+ "integrity": "sha512-e60erzdQOwZ88+j6Hi1AnRlxex7ZmwQPGSoFjtihQJR3Xv9Esj5DBKVrWu6Z/QCDxTc8uaX08XjUyhse2YyutQ==",
+ "requires": {
+ "@sentry/browser": "5.21.1",
+ "@sentry/minimal": "5.21.1",
+ "@sentry/types": "5.21.1",
+ "@sentry/utils": "5.21.1",
+ "hoist-non-react-statics": "^3.3.2",
+ "tslib": "^1.9.3"
+ }
+ },
+ "@sentry/types": {
+ "version": "5.21.1",
+ "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.21.1.tgz",
+ "integrity": "sha512-hFN4aDduMpjj6vZSIIp+9kSr8MglcKO/UmbuUXN6hKLewhxt+Zj2wjXN7ulSs5OK5mjXP9QLA5YJvVQsl2//qw=="
+ },
+ "@sentry/utils": {
+ "version": "5.21.1",
+ "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.21.1.tgz",
+ "integrity": "sha512-p5vPuc7+GfOmW8CXxWd0samS77Q00YrN8q5TC/ztF8nBhEF18GiMeWAdQnlSwt3iWal3q3gSSrbF4c9guIugng==",
+ "requires": {
+ "@sentry/types": "5.21.1",
+ "tslib": "^1.9.3"
+ }
+ },
"@solana/web3.js": {
"version": "0.70.3",
"resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-0.70.3.tgz",
diff --git a/explorer/package.json b/explorer/package.json
index 534e2ad89f..43a2b867a6 100644
--- a/explorer/package.json
+++ b/explorer/package.json
@@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@react-hook/debounce": "^3.0.0",
+ "@sentry/react": "^5.21.1",
"@solana/web3.js": "^0.70.3",
"@testing-library/jest-dom": "^5.11.3",
"@testing-library/react": "^10.4.8",
diff --git a/explorer/src/components/account/TokenAccountSection.tsx b/explorer/src/components/account/TokenAccountSection.tsx
index 99a2f9a4bd..a8444686f6 100644
--- a/explorer/src/components/account/TokenAccountSection.tsx
+++ b/explorer/src/components/account/TokenAccountSection.tsx
@@ -1,4 +1,5 @@
import React from "react";
+import * as Sentry from "@sentry/react";
import { Account, useFetchAccountInfo } from "providers/accounts";
import {
TokenAccount,
@@ -37,7 +38,13 @@ export function TokenAccountSection({
return ;
}
}
- } catch (err) {}
+ } catch (err) {
+ Sentry.captureException(err, {
+ tags: {
+ address: account.pubkey.toBase58(),
+ },
+ });
+ }
return ;
}
diff --git a/explorer/src/components/instruction/stake/AuthorizeDetailsCard.tsx b/explorer/src/components/instruction/stake/AuthorizeDetailsCard.tsx
index c76389d1e8..012d0f611d 100644
--- a/explorer/src/components/instruction/stake/AuthorizeDetailsCard.tsx
+++ b/explorer/src/components/instruction/stake/AuthorizeDetailsCard.tsx
@@ -20,7 +20,6 @@ export function AuthorizeDetailsCard(props: {
try {
params = StakeInstruction.decodeAuthorize(ix);
} catch (err) {
- console.error(err);
return ;
}
diff --git a/explorer/src/components/instruction/token/TokenDetailsCard.tsx b/explorer/src/components/instruction/token/TokenDetailsCard.tsx
index a21dbcd1f6..d1634d2938 100644
--- a/explorer/src/components/instruction/token/TokenDetailsCard.tsx
+++ b/explorer/src/components/instruction/token/TokenDetailsCard.tsx
@@ -1,4 +1,5 @@
import React from "react";
+import * as Sentry from "@sentry/react";
import { coerce } from "superstruct";
import {
SignatureResult,
@@ -29,6 +30,11 @@ export function TokenDetailsCard(props: DetailsProps) {
const coerced = coerce(info, IX_STRUCTS[type] as any);
return ;
} catch (err) {
+ Sentry.captureException(err, {
+ tags: {
+ signature: props.tx.signatures[0],
+ },
+ });
return ;
}
}
diff --git a/explorer/src/index.tsx b/explorer/src/index.tsx
index d1716cd4a7..3bda904599 100644
--- a/explorer/src/index.tsx
+++ b/explorer/src/index.tsx
@@ -1,9 +1,9 @@
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router } from "react-router-dom";
+import * as Sentry from "@sentry/react";
import "./scss/theme-dark.scss";
import App from "./App";
-import * as serviceWorker from "./serviceWorker";
import { ClusterProvider } from "./providers/cluster";
import { RichListProvider } from "./providers/richList";
import { SupplyProvider } from "./providers/supply";
@@ -12,6 +12,11 @@ import { AccountsProvider } from "./providers/accounts";
import { StatsProvider } from "providers/stats";
import { MintsProvider } from "providers/mints";
+Sentry.init({
+ dsn:
+ "https://5efdc15b4828434fbe949b5daed472be@o434108.ingest.sentry.io/5390542",
+});
+
ReactDOM.render(
@@ -32,8 +37,3 @@ ReactDOM.render(
,
document.getElementById("root")
);
-
-// If you want your app to work offline and load faster, you can change
-// unregister() to register() below. Note this comes with some pitfalls.
-// Learn more about service workers: https://bit.ly/CRA-PWA
-serviceWorker.unregister();
diff --git a/explorer/src/providers/accounts/history.tsx b/explorer/src/providers/accounts/history.tsx
index 306b0557f0..f66faecc96 100644
--- a/explorer/src/providers/accounts/history.tsx
+++ b/explorer/src/providers/accounts/history.tsx
@@ -1,4 +1,5 @@
import React from "react";
+import * as Sentry from "@sentry/react";
import {
PublicKey,
ConfirmedSignatureInfo,
@@ -101,7 +102,7 @@ async function fetchAccountHistory(
};
status = FetchStatus.Fetched;
} catch (error) {
- console.error("Failed to fetch account history", error);
+ Sentry.captureException(error, { tags: { url } });
status = FetchStatus.FetchFailed;
}
dispatch({
diff --git a/explorer/src/providers/accounts/index.tsx b/explorer/src/providers/accounts/index.tsx
index 1118988f22..a0f9e3ecad 100644
--- a/explorer/src/providers/accounts/index.tsx
+++ b/explorer/src/providers/accounts/index.tsx
@@ -1,4 +1,5 @@
import React from "react";
+import * as Sentry from "@sentry/react";
import { StakeAccount as StakeAccountWasm } from "solana-sdk-wasm";
import { PublicKey, Connection, StakeProgram } from "@solana/web3.js";
import { useCluster } from "../cluster";
@@ -111,7 +112,9 @@ async function fetchAccountInfo(
parsed,
};
} catch (err) {
- console.error("Failed to parse stake account", err);
+ Sentry.captureException(err, {
+ tags: { url, address: pubkey.toBase58() },
+ });
// TODO store error state in Account info
}
} else if ("parsed" in result.data) {
@@ -124,6 +127,9 @@ async function fetchAccountInfo(
parsed,
};
} catch (err) {
+ Sentry.captureException(err, {
+ tags: { url, address: pubkey.toBase58() },
+ });
// TODO store error state in Account info
}
}
@@ -139,7 +145,7 @@ async function fetchAccountInfo(
data = { pubkey, lamports, details };
fetchStatus = FetchStatus.Fetched;
} catch (error) {
- console.error("Failed to fetch account info", error);
+ Sentry.captureException(error, { tags: { url } });
fetchStatus = FetchStatus.FetchFailed;
}
dispatch({
diff --git a/explorer/src/providers/accounts/tokens.tsx b/explorer/src/providers/accounts/tokens.tsx
index 3f2ee369ae..eb27f43b4b 100644
--- a/explorer/src/providers/accounts/tokens.tsx
+++ b/explorer/src/providers/accounts/tokens.tsx
@@ -1,4 +1,5 @@
import React from "react";
+import * as Sentry from "@sentry/react";
import { Connection, PublicKey } from "@solana/web3.js";
import * as Cache from "providers/cache";
import { ActionType, FetchStatus } from "providers/cache";
@@ -72,6 +73,7 @@ async function fetchAccountTokens(
};
status = FetchStatus.Fetched;
} catch (error) {
+ Sentry.captureException(error, { tags: { url } });
status = FetchStatus.FetchFailed;
}
dispatch({ type: ActionType.Update, url, status, data, key });
diff --git a/explorer/src/providers/cluster.tsx b/explorer/src/providers/cluster.tsx
index ee7cc1b1b4..9591e5d5ec 100644
--- a/explorer/src/providers/cluster.tsx
+++ b/explorer/src/providers/cluster.tsx
@@ -1,4 +1,5 @@
import React from "react";
+import * as Sentry from "@sentry/react";
import { clusterApiUrl, Connection } from "@solana/web3.js";
import { useQuery } from "../utils/url";
import { useHistory, useLocation } from "react-router-dom";
@@ -182,7 +183,9 @@ async function updateCluster(
firstAvailableBlock,
});
} catch (error) {
- console.error("Failed to update cluster", error);
+ Sentry.captureException(error, {
+ tags: { clusterUrl: clusterUrl(cluster, customUrl) },
+ });
dispatch({ status: ClusterStatus.Failure, cluster, customUrl });
}
}
diff --git a/explorer/src/providers/mints/largest.tsx b/explorer/src/providers/mints/largest.tsx
index bfacaf0ba0..7595abf68e 100644
--- a/explorer/src/providers/mints/largest.tsx
+++ b/explorer/src/providers/mints/largest.tsx
@@ -1,4 +1,5 @@
import React from "react";
+import * as Sentry from "@sentry/react";
import { useCluster } from "providers/cluster";
import * as Cache from "providers/cache";
import { ActionType, FetchStatus } from "providers/cache";
@@ -59,6 +60,7 @@ async function fetchLargestAccounts(
};
fetchStatus = FetchStatus.Fetched;
} catch (error) {
+ Sentry.captureException(error, { tags: { url } });
fetchStatus = FetchStatus.FetchFailed;
}
dispatch({
diff --git a/explorer/src/providers/mints/supply.tsx b/explorer/src/providers/mints/supply.tsx
index bd51070c50..1d3a45ee03 100644
--- a/explorer/src/providers/mints/supply.tsx
+++ b/explorer/src/providers/mints/supply.tsx
@@ -1,4 +1,5 @@
import React from "react";
+import * as Sentry from "@sentry/react";
import { useCluster } from "providers/cluster";
import * as Cache from "providers/cache";
import { ActionType, FetchStatus } from "providers/cache";
@@ -44,6 +45,7 @@ async function fetchSupply(dispatch: Dispatch, pubkey: PublicKey, url: string) {
fetchStatus = FetchStatus.Fetched;
} catch (error) {
+ Sentry.captureException(error, { tags: { url } });
fetchStatus = FetchStatus.FetchFailed;
}
dispatch({
diff --git a/explorer/src/providers/richList.tsx b/explorer/src/providers/richList.tsx
index fdb0d7b58e..ecfa8c0457 100644
--- a/explorer/src/providers/richList.tsx
+++ b/explorer/src/providers/richList.tsx
@@ -1,4 +1,5 @@
import React from "react";
+import * as Sentry from "@sentry/react";
import { AccountBalancePair, Connection } from "@solana/web3.js";
import { useCluster, ClusterStatus } from "./cluster";
@@ -70,7 +71,7 @@ async function fetch(dispatch: Dispatch, url: string) {
return { total, circulating, nonCirculating };
});
} catch (err) {
- console.error("Failed to fetch", err);
+ Sentry.captureException(err, { tags: { url } });
dispatch("Failed to fetch top accounts");
}
}
diff --git a/explorer/src/providers/supply.tsx b/explorer/src/providers/supply.tsx
index c601efd830..e7a6bad709 100644
--- a/explorer/src/providers/supply.tsx
+++ b/explorer/src/providers/supply.tsx
@@ -1,4 +1,5 @@
import React from "react";
+import * as Sentry from "@sentry/react";
import { Supply, Connection } from "@solana/web3.js";
import { useCluster, ClusterStatus } from "./cluster";
@@ -50,7 +51,7 @@ async function fetch(dispatch: Dispatch, url: string) {
return supply;
});
} catch (err) {
- console.error("Failed to fetch", err);
+ Sentry.captureException(err, { tags: { url } });
dispatch("Failed to fetch supply");
}
}
diff --git a/explorer/src/providers/transactions/details.tsx b/explorer/src/providers/transactions/details.tsx
index 77e1fbbf4c..623f36e77a 100644
--- a/explorer/src/providers/transactions/details.tsx
+++ b/explorer/src/providers/transactions/details.tsx
@@ -1,4 +1,5 @@
import React from "react";
+import * as Sentry from "@sentry/react";
import {
Connection,
TransactionSignature,
@@ -63,7 +64,7 @@ async function fetchDetails(
);
fetchStatus = FetchStatus.Fetched;
} catch (error) {
- console.error("Failed to fetch confirmed transaction", error);
+ Sentry.captureException(error, { tags: { url } });
fetchStatus = FetchStatus.FetchFailed;
}
}
diff --git a/explorer/src/providers/transactions/index.tsx b/explorer/src/providers/transactions/index.tsx
index 764b4e0040..5f65974f31 100644
--- a/explorer/src/providers/transactions/index.tsx
+++ b/explorer/src/providers/transactions/index.tsx
@@ -1,4 +1,5 @@
import React from "react";
+import * as Sentry from "@sentry/react";
import {
TransactionSignature,
Connection,
@@ -85,12 +86,7 @@ export async function fetchTransactionStatus(
try {
blockTime = await connection.getBlockTime(value.slot);
} catch (error) {
- console.error(
- "Failed to fetch block time for slot ",
- value.slot,
- ":",
- error
- );
+ Sentry.captureException(error, { tags: { slot: `${value.slot}` } });
}
let timestamp: Timestamp =
blockTime !== null ? blockTime : "unavailable";
@@ -112,7 +108,7 @@ export async function fetchTransactionStatus(
data = { signature, info };
fetchStatus = FetchStatus.Fetched;
} catch (error) {
- console.error("Failed to fetch transaction status", error);
+ Sentry.captureException(error, { tags: { url } });
fetchStatus = FetchStatus.FetchFailed;
}
}