diff --git a/explorer/package-lock.json b/explorer/package-lock.json index 6250274365..3b05b337b5 100644 --- a/explorer/package-lock.json +++ b/explorer/package-lock.json @@ -1274,9 +1274,9 @@ "integrity": "sha512-DetpxZw1fzPD5xUBrIAoplLChO2VB8DlL5Gg+I1IR9b2wPqYIca2WSUxL5g1vLeR4MsQq1NeWriXAVffV+U1Fw==" }, "@solana/web3.js": { - "version": "0.56.0", - "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-0.56.0.tgz", - "integrity": "sha512-Vys4PY7wksHXLyMbjpTLJGMSs58zRGdDPaBmlxeTXfwjv27W5xcjanuC+sNQ8CaeKjUy9Rd0ptU2qSjBHp/rNw==", + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-0.61.0.tgz", + "integrity": "sha512-9A2uGyEW3zhsMWsUn73RLJrn2S50Wr2tlTn/eeszbX03FEzaFg8T1+n0iK4bE5sx+pme5aKzx0M6X1peQr07Eg==", "requires": { "@babel/runtime": "^7.3.1", "bn.js": "^5.0.0", @@ -1692,9 +1692,9 @@ "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==" }, "@types/lodash": { - "version": "4.14.152", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.152.tgz", - "integrity": "sha512-Vwf9YF2x1GE3WNeUMjT5bTHa2DqgUo87ocdgTScupY2JclZ5Nn7W2RLM/N0+oreexUk8uaVugR81NnTY/jNNXg==" + "version": "4.14.155", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.155.tgz", + "integrity": "sha512-vEcX7S7aPhsBCivxMwAANQburHBtfN9RdyXFk84IJmu2Z4Hkg1tOFgaslRiEqqvoLtbCBi6ika1EMspE+NZ9Lg==" }, "@types/minimatch": { "version": "3.0.3", @@ -7216,9 +7216,9 @@ } }, "jayson": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/jayson/-/jayson-3.3.0.tgz", - "integrity": "sha512-QrsIeK9spSBEsXEvRMIAQ3X9cfT7u0EVD0L7XVpX4JqzIRgziJeei4iRWs+bZovdqyrmg8WgKgY9zKO4HY6eYQ==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/jayson/-/jayson-3.3.3.tgz", + "integrity": "sha512-0bQ/vNvWyi+fzNoMvRK63m7BGU+PWwaJRVaGgJWZeAL5euOSqJtqzqyCrfiS3Sdw3OrvgKWF5f5N8ut87gxrng==", "requires": { "@types/connect": "^3.4.32", "@types/express-serve-static-core": "^4.16.9", @@ -12364,9 +12364,9 @@ } }, "rpc-websockets": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-5.1.1.tgz", - "integrity": "sha512-3REe5/7krS7JxE1Qf2MAXgWiiLjPl8tI6b9V8tf8Zf4v0ACvKHaA/4rqcvmoVMHGm1OdVGog+5cPSpKgELsx/w==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-5.2.2.tgz", + "integrity": "sha512-L/PWLm+WCqBOeMU+9wsu2u0tLmAd17tsOsyl2OhaaEicK9/mbXF6I2h891e9NJmRf2n5NqaEmxpF6zgoiohouQ==", "requires": { "@babel/runtime": "^7.8.7", "assert-args": "^1.2.1", @@ -13389,9 +13389,9 @@ } }, "superstruct": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.8.3.tgz", - "integrity": "sha512-LbtbFpktW1FcwxVIJlxdk7bCyBq/GzOx2FSFLRLTUhWIA1gHkYPIl3aXRG5mBdGZtnPNT6t+4eEcLDCMOuBHww==", + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.8.4.tgz", + "integrity": "sha512-48Ors8IVWZm/tMr8r0Si6+mJiB7mkD7jqvIzktjJ4+EnP5tBp0qOpiM1J8sCUorKx+TXWrfb3i1UcjdD1YK/wA==", "requires": { "kind-of": "^6.0.2", "tiny-invariant": "^1.0.6" diff --git a/explorer/package.json b/explorer/package.json index b094f57e60..9bc66294cb 100644 --- a/explorer/package.json +++ b/explorer/package.json @@ -4,7 +4,7 @@ "private": true, "dependencies": { "@react-hook/debounce": "^2.0.5", - "@solana/web3.js": "^0.56.0", + "@solana/web3.js": "^0.61.0", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", diff --git a/explorer/src/components/TransactionDetails.tsx b/explorer/src/components/TransactionDetails.tsx index 5a59c7fe47..abde935021 100644 --- a/explorer/src/components/TransactionDetails.tsx +++ b/explorer/src/components/TransactionDetails.tsx @@ -26,6 +26,7 @@ import LoadingCard from "./common/LoadingCard"; import TableCardBody from "./common/TableCardBody"; import { displayTimestamp } from "utils/date"; import InfoTooltip from "components/InfoTooltip"; +import { isCached } from "providers/transactions/cached"; type Props = { signature: TransactionSignature }; export default function TransactionDetails({ signature }: Props) { @@ -221,6 +222,7 @@ function StatusCard({ signature }: Props) { function AccountsCard({ signature }: Props) { const details = useTransactionDetails(signature); + const { url } = useCluster(); const fetchStatus = useFetchTransactionStatus(); const fetchDetails = useFetchTransactionDetails(); const refreshStatus = () => fetchStatus(signature); @@ -251,6 +253,9 @@ function AccountsCard({ signature }: Props) { const { meta } = details.transaction; if (!meta) { + if (isCached(url, signature)) { + return null; + } return ; } diff --git a/explorer/src/providers/transactions/cached.ts b/explorer/src/providers/transactions/cached.ts new file mode 100644 index 0000000000..7fbc552621 --- /dev/null +++ b/explorer/src/providers/transactions/cached.ts @@ -0,0 +1,237 @@ +/** + * TEMPORARY + * + * Adds the ability cache select transactions from mainnet-beta and return them as + * if they came from the RPC node itself, until we have a longer transaction history + */ + +import { TransactionStatusInfo } from "./index"; +import { + Transaction, + ConfirmedTransaction, + Message, + clusterApiUrl +} from "@solana/web3.js"; + +export const isCached = (url: string, signature: string): boolean => { + return url === clusterApiUrl("mainnet-beta") && signature in CACHED_STATUSES; +}; + +export const CACHED_STATUSES: { [key: string]: TransactionStatusInfo } = { + uQf4pS38FjRF294QFEXizhYkZFjSR9ZSBvvV6MV5b4VpdfRnK3PY9TWZ2qHMQKtte3XwKVLcWqsTF6wL9NEZMty: { + slot: 10440804, + result: { err: null }, + timestamp: 1589212180, + confirmations: "max" + }, + DYrfStEEzbV5sftX8LgUa54Nwnc5m5E1731cqBtiiC66TeXgKpfqZEQTuFY3vhHZ2K1BsaFM3X9FqisR28EtZr8: { + slot: 10451288, + result: { err: null }, + timestamp: 1589216984, + confirmations: "max" + }, + "3bLx2PLpkxCxJA5P7HVe8asFdSWXVAh1DrxfkqWE9bWvPRxXE2hqwj1vuSC858fUw3XAGQcHbJknhtNdxY2sehab": { + slot: 10516588, + result: { err: null }, + timestamp: 1589247117, + confirmations: "max" + }, + "3fE8xNgyxbwbvA5MX3wM87ahDDgCVEaaMMSa8UCWWNxojaRYBgrQyiKXLSxcryMWb7sEyVLBWyqUaRWnQCroSqjY": { + slot: 10575124, + result: { err: null }, + timestamp: 1589274236, + confirmations: "max" + }, + "5PWymGjKV7T1oqeqGn139EHFyjNM2dnNhHCUcfD2bmdj8cfF95HpY1uJ84W89c4sJQnmyZxXcYrcjumx2jHUvxZQ": { + slot: 12447825, + result: { err: null }, + timestamp: 15901860565, + confirmations: "max" + }, + "5K4KuqTTRNtzfpxWiwnkePzGfsa3tBEmpMy7vQFR3KWFAZNVY9tvoSaz1Yt5dKxcgsZPio2EsASVDGbQB1HvirGD": { + slot: 12450728, + result: { err: null }, + timestamp: 15901874549, + confirmations: "max" + } +}; + +export const CACHED_DETAILS: { [key: string]: ConfirmedTransaction } = { + uQf4pS38FjRF294QFEXizhYkZFjSR9ZSBvvV6MV5b4VpdfRnK3PY9TWZ2qHMQKtte3XwKVLcWqsTF6wL9NEZMty: { + meta: null, + slot: 10440804, + transaction: Transaction.populate( + new Message({ + accountKeys: [ + "2ojv9BAiHUrvsm9gxDe7fJSzbNZSJcxZvf8dqmWGHG8S", + "4C6NCcLPUgGuBBkV2dJW96mrptMUCp3RG1ft9rqwjFi9", + "11111111111111111111111111111111" + ], + header: { + numReadonlySignedAccounts: 0, + numReadonlyUnsignedAccounts: 1, + numRequiredSignatures: 1 + }, + instructions: [ + { accounts: [0, 1], data: "3Bxs411UBrj8QXUb", programIdIndex: 2 } + ], + recentBlockhash: "5Aw8MaMYdYtnfJyyrregWMWGgiMtWZ6GtRzeP6Ufo65Z" + }), + [ + "uQf4pS38FjRF294QFEXizhYkZFjSR9ZSBvvV6MV5b4VpdfRnK3PY9TWZ2qHMQKtte3XwKVLcWqsTF6wL9NEZMty" + ] + ) + }, + + DYrfStEEzbV5sftX8LgUa54Nwnc5m5E1731cqBtiiC66TeXgKpfqZEQTuFY3vhHZ2K1BsaFM3X9FqisR28EtZr8: { + meta: null, + slot: 10451288, + transaction: Transaction.populate( + new Message({ + accountKeys: [ + "2ojv9BAiHUrvsm9gxDe7fJSzbNZSJcxZvf8dqmWGHG8S", + "4C6NCcLPUgGuBBkV2dJW96mrptMUCp3RG1ft9rqwjFi9", + "11111111111111111111111111111111" + ], + header: { + numReadonlySignedAccounts: 0, + numReadonlyUnsignedAccounts: 1, + numRequiredSignatures: 1 + }, + instructions: [ + { + accounts: [0, 1], + data: "3Bxs3zwYHuDo723R", + programIdIndex: 2 + } + ], + recentBlockhash: "4hXYcBdfcadcjfWV17ZwMa4MXe8kbZHYHwr3GzfyqunL" + }), + [ + "DYrfStEEzbV5sftX8LgUa54Nwnc5m5E1731cqBtiiC66TeXgKpfqZEQTuFY3vhHZ2K1BsaFM3X9FqisR28EtZr8" + ] + ) + }, + + "3bLx2PLpkxCxJA5P7HVe8asFdSWXVAh1DrxfkqWE9bWvPRxXE2hqwj1vuSC858fUw3XAGQcHbJknhtNdxY2sehab": { + meta: null, + slot: 10516588, + transaction: Transaction.populate( + new Message({ + accountKeys: [ + "2ojv9BAiHUrvsm9gxDe7fJSzbNZSJcxZvf8dqmWGHG8S", + "4C6NCcLPUgGuBBkV2dJW96mrptMUCp3RG1ft9rqwjFi9", + "11111111111111111111111111111111" + ], + header: { + numReadonlySignedAccounts: 0, + numReadonlyUnsignedAccounts: 1, + numRequiredSignatures: 1 + }, + instructions: [ + { + accounts: [0, 1], + data: "3Bxs3zwYHuDo723R", + programIdIndex: 2 + } + ], + recentBlockhash: "HSzTGt3PJMeQtFr94gEdeZqTRaBxgS8Wf1zq3MDdNT3L" + }), + [ + "3bLx2PLpkxCxJA5P7HVe8asFdSWXVAh1DrxfkqWE9bWvPRxXE2hqwj1vuSC858fUw3XAGQcHbJknhtNdxY2sehab" + ] + ) + }, + + "3fE8xNgyxbwbvA5MX3wM87ahDDgCVEaaMMSa8UCWWNxojaRYBgrQyiKXLSxcryMWb7sEyVLBWyqUaRWnQCroSqjY": { + meta: null, + slot: 10575124, + transaction: Transaction.populate( + new Message({ + accountKeys: [ + "2ojv9BAiHUrvsm9gxDe7fJSzbNZSJcxZvf8dqmWGHG8S", + "4C6NCcLPUgGuBBkV2dJW96mrptMUCp3RG1ft9rqwjFi9", + "11111111111111111111111111111111" + ], + header: { + numReadonlySignedAccounts: 0, + numReadonlyUnsignedAccounts: 1, + numRequiredSignatures: 1 + }, + instructions: [ + { + accounts: [0, 1], + data: "3Bxs3zuKU6mRKSqD", + programIdIndex: 2 + } + ], + recentBlockhash: "6f6TBMhUoypfR5HHnEqC6VoooKxEcNad5W3Sf63j9MSD" + }), + [ + "3fE8xNgyxbwbvA5MX3wM87ahDDgCVEaaMMSa8UCWWNxojaRYBgrQyiKXLSxcryMWb7sEyVLBWyqUaRWnQCroSqjY" + ] + ) + }, + + "5PWymGjKV7T1oqeqGn139EHFyjNM2dnNhHCUcfD2bmdj8cfF95HpY1uJ84W89c4sJQnmyZxXcYrcjumx2jHUvxZQ": { + meta: null, + slot: 12447825, + transaction: Transaction.populate( + new Message({ + accountKeys: [ + "HCV5dGFJXRrJ3jhDYA4DCeb9TEDTwGGYXtT3wHksu2Zr", + "4C6NCcLPUgGuBBkV2dJW96mrptMUCp3RG1ft9rqwjFi9", + "11111111111111111111111111111111" + ], + header: { + numReadonlySignedAccounts: 0, + numReadonlyUnsignedAccounts: 1, + numRequiredSignatures: 1 + }, + instructions: [ + { + accounts: [0, 1], + data: "3Bxs3zrfhSqZJTR1", + programIdIndex: 2 + } + ], + recentBlockhash: "3HJNFraT7XGAqMrQs83EKwDGB6LpHVwUMQKGaYMNY49E" + }), + [ + "5PWymGjKV7T1oqeqGn139EHFyjNM2dnNhHCUcfD2bmdj8cfF95HpY1uJ84W89c4sJQnmyZxXcYrcjumx2jHUvxZQ" + ] + ) + }, + + "5K4KuqTTRNtzfpxWiwnkePzGfsa3tBEmpMy7vQFR3KWFAZNVY9tvoSaz1Yt5dKxcgsZPio2EsASVDGbQB1HvirGD": { + meta: null, + slot: 12450728, + transaction: Transaction.populate( + new Message({ + accountKeys: [ + "6yKHERk8rsbmJxvMpPuwPs1ct3hRiP7xaJF2tvnGU6nK", + "4C6NCcLPUgGuBBkV2dJW96mrptMUCp3RG1ft9rqwjFi9", + "3o6xgkJ9sTmDeQWyfj3sxwon18fXJB9PV5LDc8sfgR4a", + "11111111111111111111111111111111" + ], + header: { + numReadonlySignedAccounts: 0, + numReadonlyUnsignedAccounts: 1, + numRequiredSignatures: 2 + }, + instructions: [ + { + accounts: [1, 2], + data: "3Bxs3ztRCp3tH1yZ", + programIdIndex: 3 + } + ], + recentBlockhash: "8eXVUNRxrDgpsEuoTWyLay1LUh2djc3Y8cw2owXRN8cU" + }), + [ + "5K4KuqTTRNtzfpxWiwnkePzGfsa3tBEmpMy7vQFR3KWFAZNVY9tvoSaz1Yt5dKxcgsZPio2EsASVDGbQB1HvirGD", + "37tvpG1eAeEBizJPhJvmpC2BY8npwy6K1wrZdNwdRAfWSbkerY3ZwYAPMHbrzoq7tthvWC2qFU28niqLPxbukeXF" + ] + ) + } +}; diff --git a/explorer/src/providers/transactions/details.tsx b/explorer/src/providers/transactions/details.tsx index 106c1467fe..bc89ece2de 100644 --- a/explorer/src/providers/transactions/details.tsx +++ b/explorer/src/providers/transactions/details.tsx @@ -6,6 +6,7 @@ import { } from "@solana/web3.js"; import { useCluster } from "../cluster"; import { useTransactions, FetchStatus } from "./index"; +import { CACHED_DETAILS, isCached } from "./cached"; export interface Details { fetchStatus: FetchStatus; @@ -142,12 +143,19 @@ async function fetchDetails( let fetchStatus; let transaction = null; - try { - transaction = await new Connection(url).getConfirmedTransaction(signature); + if (isCached(url, signature)) { + transaction = CACHED_DETAILS[signature]; fetchStatus = FetchStatus.Fetched; - } catch (error) { - console.error("Failed to fetch confirmed transaction", error); - fetchStatus = FetchStatus.FetchFailed; + } else { + try { + transaction = await new Connection(url).getConfirmedTransaction( + signature + ); + fetchStatus = FetchStatus.Fetched; + } catch (error) { + console.error("Failed to fetch confirmed transaction", error); + fetchStatus = FetchStatus.FetchFailed; + } } dispatch({ type: ActionType.Update, fetchStatus, signature, transaction }); } diff --git a/explorer/src/providers/transactions/index.tsx b/explorer/src/providers/transactions/index.tsx index ad7808ec0e..10bc470b71 100644 --- a/explorer/src/providers/transactions/index.tsx +++ b/explorer/src/providers/transactions/index.tsx @@ -16,6 +16,7 @@ import { } from "./details"; import base58 from "bs58"; import { useFetchAccountInfo } from "../accounts"; +import { CACHED_STATUSES, isCached } from "./cached"; export enum FetchStatus { Fetching, @@ -186,8 +187,7 @@ async function createTestTransaction( const connection = new Connection(url, "recent"); const signature = await connection.requestAirdrop( testAccount.publicKey, - 100000, - "recent" + 100000 ); fetchTransactionStatus(dispatch, signature, url, clusterStatus); fetchAccount(testAccount.publicKey); @@ -206,7 +206,7 @@ async function createTestTransaction( connection, tx, [testAccount], - 1 + { confirmations: 1, skipPreflight: false } ); fetchTransactionStatus(dispatch, signature, url, clusterStatus); } catch (error) { @@ -230,34 +230,40 @@ export async function fetchTransactionStatus( let fetchStatus; let info: TransactionStatusInfo | undefined; - try { - const connection = new Connection(url); - const { value } = await connection.getSignatureStatus(signature, { - searchTransactionHistory: true - }); - - if (value !== null) { - let blockTime = await connection.getBlockTime(value.slot); - let timestamp: Timestamp = blockTime !== null ? blockTime : "unavailable"; - - let confirmations: Confirmations; - if (typeof value.confirmations === "number") { - confirmations = value.confirmations; - } else { - confirmations = "max"; - } - - info = { - slot: value.slot, - timestamp, - confirmations, - result: { err: value.err } - }; - } + if (isCached(url, signature)) { + info = CACHED_STATUSES[signature]; fetchStatus = FetchStatus.Fetched; - } catch (error) { - console.error("Failed to fetch transaction status", error); - fetchStatus = FetchStatus.FetchFailed; + } else { + try { + const connection = new Connection(url); + const { value } = await connection.getSignatureStatus(signature, { + searchTransactionHistory: true + }); + + if (value !== null) { + let blockTime = await connection.getBlockTime(value.slot); + let timestamp: Timestamp = + blockTime !== null ? blockTime : "unavailable"; + + let confirmations: Confirmations; + if (typeof value.confirmations === "number") { + confirmations = value.confirmations; + } else { + confirmations = "max"; + } + + info = { + slot: value.slot, + timestamp, + confirmations, + result: { err: value.err } + }; + } + fetchStatus = FetchStatus.Fetched; + } catch (error) { + console.error("Failed to fetch transaction status", error); + fetchStatus = FetchStatus.FetchFailed; + } } dispatch({