From 26e4767fddc1c410add284cd53e20cfbf4981cb6 Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Thu, 14 May 2020 15:30:33 +0800 Subject: [PATCH] Use wasm to decode stake account --- explorer/.prettierignore | 1 + explorer/config-overrides.js | 25 + explorer/package-lock.json | 59 +++ explorer/package.json | 12 +- explorer/src/components/AccountDetails.tsx | 98 ++-- explorer/src/components/AccountsCard.tsx | 2 +- .../src/components/TransactionDetails.tsx | 59 +-- .../account/StakeAccountDetailsCard.tsx | 121 +++++ explorer/src/components/common/ErrorCard.tsx | 33 ++ .../src/components/common/LoadingCard.tsx | 12 + .../src/components/common/TableCardBody.tsx | 15 + .../src/components/instruction/RawDetails.tsx | 2 +- .../stake/AuthorizeDetailsCard.tsx | 2 +- .../stake/DeactivateDetailsCard.tsx | 2 +- .../instruction/stake/DelegateDetailsCard.tsx | 2 +- .../stake/InitializeDetailsCard.tsx | 4 +- .../instruction/stake/SplitDetailsCard.tsx | 2 +- .../instruction/stake/WithdrawDetailsCard.tsx | 2 +- .../system/AllocateDetailsCard.tsx | 2 +- .../system/AllocateWithSeedDetailsCard.tsx | 4 +- .../instruction/system/AssignDetailsCard.tsx | 4 +- .../system/AssignWithSeedDetailsCard.tsx | 4 +- .../instruction/system/CreateDetailsCard.tsx | 4 +- .../system/CreateWithSeedDetailsCard.tsx | 4 +- .../system/NonceAdvanceDetailsCard.tsx | 2 +- .../system/NonceAuthorizeDetailsCard.tsx | 2 +- .../system/NonceInitializeDetailsCard.tsx | 2 +- .../system/NonceWithdrawDetailsCard.tsx | 2 +- .../system/TransferDetailsCard.tsx | 2 +- explorer/src/providers/accounts.tsx | 14 +- explorer/src/utils/tx.ts | 4 +- explorer/wasm/.gitignore | 16 + explorer/wasm/Cargo.lock | 179 +++++++ explorer/wasm/Cargo.toml | 27 + explorer/wasm/README.md | 15 + explorer/wasm/pkg/package.json | 22 + explorer/wasm/pkg/solana_sdk_wasm.d.ts | 140 +++++ explorer/wasm/pkg/solana_sdk_wasm.js | 2 + explorer/wasm/pkg/solana_sdk_wasm_bg.d.ts | 44 ++ explorer/wasm/pkg/solana_sdk_wasm_bg.js | 483 ++++++++++++++++++ explorer/wasm/pkg/solana_sdk_wasm_bg.wasm | Bin 0 -> 73523 bytes explorer/wasm/src/lib.rs | 3 + explorer/wasm/src/stake_account.rs | 217 ++++++++ 43 files changed, 1522 insertions(+), 129 deletions(-) create mode 100644 explorer/config-overrides.js create mode 100644 explorer/src/components/account/StakeAccountDetailsCard.tsx create mode 100644 explorer/src/components/common/ErrorCard.tsx create mode 100644 explorer/src/components/common/LoadingCard.tsx create mode 100644 explorer/src/components/common/TableCardBody.tsx create mode 100644 explorer/wasm/.gitignore create mode 100644 explorer/wasm/Cargo.lock create mode 100644 explorer/wasm/Cargo.toml create mode 100644 explorer/wasm/README.md create mode 100644 explorer/wasm/pkg/package.json create mode 100644 explorer/wasm/pkg/solana_sdk_wasm.d.ts create mode 100644 explorer/wasm/pkg/solana_sdk_wasm.js create mode 100644 explorer/wasm/pkg/solana_sdk_wasm_bg.d.ts create mode 100644 explorer/wasm/pkg/solana_sdk_wasm_bg.js create mode 100644 explorer/wasm/pkg/solana_sdk_wasm_bg.wasm create mode 100644 explorer/wasm/src/lib.rs create mode 100644 explorer/wasm/src/stake_account.rs diff --git a/explorer/.prettierignore b/explorer/.prettierignore index 378eac25d3..980932254d 100644 --- a/explorer/.prettierignore +++ b/explorer/.prettierignore @@ -1 +1,2 @@ build +wasm diff --git a/explorer/config-overrides.js b/explorer/config-overrides.js new file mode 100644 index 0000000000..6d27645b41 --- /dev/null +++ b/explorer/config-overrides.js @@ -0,0 +1,25 @@ +const path = require("path"); + +module.exports = function override(config, env) { + const wasmExtensionRegExp = /\.wasm$/; + + config.resolve.extensions.push(".wasm"); + + config.module.rules.forEach(rule => { + (rule.oneOf || []).forEach(oneOf => { + if (oneOf.loader && oneOf.loader.indexOf("file-loader") >= 0) { + // Make file-loader ignore WASM files + oneOf.exclude.push(wasmExtensionRegExp); + } + }); + }); + + // Add a dedicated loader for WASM + config.module.rules.push({ + test: wasmExtensionRegExp, + include: path.resolve(__dirname, "src"), + use: [{ loader: require.resolve("wasm-loader"), options: {} }] + }); + + return config; +}; diff --git a/explorer/package-lock.json b/explorer/package-lock.json index 0efb2789da..280b8d77f6 100644 --- a/explorer/package-lock.json +++ b/explorer/package-lock.json @@ -8902,6 +8902,11 @@ "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.7.tgz", "integrity": "sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A==" }, + "long": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", + "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=" + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -11554,6 +11559,14 @@ "whatwg-fetch": "^3.0.0" } }, + "react-app-rewired": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/react-app-rewired/-/react-app-rewired-2.1.6.tgz", + "integrity": "sha512-06flj0kK5tf/RN4naRv/sn6j3sQd7rsURoRLKLpffXDzJeNiAaTNic+0I8Basojy5WDwREkTqrMLewSAjcb13w==", + "requires": { + "semver": "^5.6.0" + } + }, "react-dev-utils": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.2.0.tgz", @@ -12959,6 +12972,9 @@ } } }, + "solana-sdk-wasm": { + "version": "file:wasm/pkg" + }, "sort-keys": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", @@ -14194,6 +14210,34 @@ "makeerror": "1.0.x" } }, + "wasm-dce": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wasm-dce/-/wasm-dce-1.0.2.tgz", + "integrity": "sha512-Fq1+nu43ybsjSnBquLrW/cULmKs61qbv9k8ep13QUe0nABBezMoNAA+j6QY66MW0/eoDVDp1rjXDqQ2VKyS/Xg==", + "requires": { + "@babel/core": "^7.0.0-beta.39", + "@babel/traverse": "^7.0.0-beta.39", + "@babel/types": "^7.0.0-beta.39", + "babylon": "^7.0.0-beta.39", + "webassembly-interpreter": "0.0.30" + }, + "dependencies": { + "babylon": { + "version": "7.0.0-beta.47", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.47.tgz", + "integrity": "sha512-+rq2cr4GDhtToEzKFD6KZZMDBXhjFAr9JjPw9pAppZACeEWqNM294j+NdBzkSHYXwzzBmVjZ3nEVJlOhbR2gOQ==" + } + } + }, + "wasm-loader": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/wasm-loader/-/wasm-loader-1.3.0.tgz", + "integrity": "sha512-R4s75XH+o8qM+WaRrAU9S2rbAMDzob18/S3V8R9ZoFpZkPWLAohWWlzWAp1ybeTkOuuku/X1zJtxiV0pBYxZww==", + "requires": { + "loader-utils": "^1.1.0", + "wasm-dce": "^1.0.0" + } + }, "watchpack": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", @@ -14770,6 +14814,21 @@ "minimalistic-assert": "^1.0.0" } }, + "webassembly-floating-point-hex-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/webassembly-floating-point-hex-parser/-/webassembly-floating-point-hex-parser-0.1.2.tgz", + "integrity": "sha512-TUf1H++8U10+stJbFydnvrpG5Sznz5Rilez/oZlV5zI0C/e4cSxd8rALAJ8VpTvjVWxLmL3SVSJUK6Ap9AoiNg==" + }, + "webassembly-interpreter": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/webassembly-interpreter/-/webassembly-interpreter-0.0.30.tgz", + "integrity": "sha512-+Jdy2piEvz9T5j751mOE8+rBO12p+nNW6Fg4kJZ+zP1oUfsm+151sbAbM8AFxWTURmWCGP+r8Lxwfv3pzN1bCQ==", + "requires": { + "@babel/code-frame": "^7.0.0-beta.36", + "long": "^3.2.0", + "webassembly-floating-point-hex-parser": "0.1.2" + } + }, "webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", diff --git a/explorer/package.json b/explorer/package.json index 4da2a4e011..e19274b2e4 100644 --- a/explorer/package.json +++ b/explorer/package.json @@ -18,16 +18,18 @@ "node-sass": "^4.13.1", "prettier": "^1.19.1", "react": "^16.13.0", + "react-app-rewired": "^2.1.6", "react-dom": "^16.13.0", "react-router-dom": "^5.1.2", "react-scripts": "3.4.0", - "typescript": "^3.8.0" + "solana-sdk-wasm": "file:wasm/pkg", + "typescript": "^3.8.0", + "wasm-loader": "^1.3.0" }, "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject", + "start": "react-app-rewired start", + "build": "react-app-rewired build", + "test": "react-app-rewired test", "format": "prettier -c \"**/*.+(js|jsx|ts|tsx|json|css|md)\"", "format:fix": "prettier --write \"**/*.+(js|jsx|ts|tsx|json|css|md)\"" }, diff --git a/explorer/src/components/AccountDetails.tsx b/explorer/src/components/AccountDetails.tsx index 51b6647864..616657ecd5 100644 --- a/explorer/src/components/AccountDetails.tsx +++ b/explorer/src/components/AccountDetails.tsx @@ -1,6 +1,7 @@ import React from "react"; +import { StakeAccount } from "solana-sdk-wasm"; import { useClusterModal } from "providers/cluster"; -import { PublicKey } from "@solana/web3.js"; +import { PublicKey, StakeProgram } from "@solana/web3.js"; import ClusterStatusButton from "components/ClusterStatusButton"; import { useHistory, useLocation } from "react-router-dom"; import { @@ -12,6 +13,10 @@ import { import { lamportsToSolString } from "utils"; import Copyable from "./Copyable"; import { displayAddress } from "utils/tx"; +import { StakeAccountDetailsCard } from "components/account/StakeAccountDetailsCard"; +import ErrorCard from "components/common/ErrorCard"; +import LoadingCard from "components/common/LoadingCard"; +import TableCardBody from "components/common/TableCardBody"; type Props = { address: string }; export default function AccountDetails({ address }: Props) { @@ -84,11 +89,52 @@ export default function AccountDetails({ address }: Props) { {pubkey && } + {pubkey && } {pubkey && } ); } +type Wasm = { + StakeAccount: typeof StakeAccount; +}; + +function DetailsCard({ pubkey }: { pubkey: PublicKey }) { + const address = pubkey.toBase58(); + const info = useAccountInfo(address); + const [Wasm, setWasm] = React.useState(undefined); + + React.useEffect(() => { + (async () => { + try { + setWasm(await import("solana-sdk-wasm")); + } catch (err) { + console.error("Unexpected error loading wasm", err); + } + })(); + }, []); + + if (!info || !info.details || !info.details.data) { + return null; + } + + const { data, owner } = info.details; + try { + if (owner.equals(StakeProgram.programId)) { + if (Wasm === undefined) { + return ; + } else { + const stakeAccount = Wasm.StakeAccount.fromAccountData(data); + return ; + } + } + } catch (err) { + console.error(err); + return ; + } + return null; +} + function InfoCard({ pubkey }: { pubkey: PublicKey }) { const address = pubkey.toBase58(); const info = useAccountInfo(address); @@ -100,7 +146,7 @@ function InfoCard({ pubkey }: { pubkey: PublicKey }) { info.status === Status.CheckFailed || info.lamports === undefined ) { - return refresh(pubkey)} text="Fetch Failed" />; + return refresh(pubkey)} text="Fetch Failed" />; } const { details, lamports } = info; @@ -138,7 +184,7 @@ function InfoCard({ pubkey }: { pubkey: PublicKey }) { Owner - {displayAddress(details.owner)} + {displayAddress(details.owner.toBase58())} @@ -166,7 +212,7 @@ function HistoryCard({ pubkey }: { pubkey: PublicKey }) { return ; } else if (info.history === undefined) { return ( - refresh(pubkey)} text="Failed to fetch transaction history" /> @@ -175,9 +221,9 @@ function HistoryCard({ pubkey }: { pubkey: PublicKey }) { if (info.history.size === 0) { return ( - refresh(pubkey)} - text="No transaction history found" + text="No recent transaction history found" /> ); } @@ -219,43 +265,3 @@ function HistoryCard({ pubkey }: { pubkey: PublicKey }) { ); } - -function LoadingCard() { - return ( -
-
- - Loading -
-
- ); -} - -function RetryCard({ retry, text }: { retry: () => void; text: string }) { - return ( -
-
- {text} - - Try Again - -
-
- - Try Again - -
-
-
- ); -} - -function TableCardBody({ children }: { children: React.ReactNode }) { - return ( -
- - {children} -
-
- ); -} diff --git a/explorer/src/components/AccountsCard.tsx b/explorer/src/components/AccountsCard.tsx index ce8d0300cf..56c59b80c2 100644 --- a/explorer/src/components/AccountsCard.tsx +++ b/explorer/src/components/AccountsCard.tsx @@ -140,7 +140,7 @@ const renderAccountRow = (account: Account) => { let owner = "-"; if (account.details) { data = `${account.details.space}`; - owner = displayAddress(account.details.owner); + owner = displayAddress(account.details.owner.toBase58()); } let balance = "-"; diff --git a/explorer/src/components/TransactionDetails.tsx b/explorer/src/components/TransactionDetails.tsx index 37dcba8f4e..caa99e9de4 100644 --- a/explorer/src/components/TransactionDetails.tsx +++ b/explorer/src/components/TransactionDetails.tsx @@ -21,6 +21,9 @@ import { useHistory, useLocation } from "react-router-dom"; import { UnknownDetailsCard } from "./instruction/UnknownDetailsCard"; import { SystemDetailsCard } from "./instruction/system/SystemDetailsCard"; import { StakeDetailsCard } from "./instruction/stake/StakeDetailsCard"; +import ErrorCard from "./common/ErrorCard"; +import LoadingCard from "./common/LoadingCard"; +import TableCardBody from "./common/TableCardBody"; type Props = { signature: TransactionSignature }; export default function TransactionDetails({ signature }: Props) { @@ -100,9 +103,9 @@ function StatusCard({ signature }: Props) { if (!status || status.fetchStatus === FetchStatus.Fetching) { return ; } else if (status?.fetchStatus === FetchStatus.FetchFailed) { - return refresh(signature)} text="Fetch Failed" />; + return refresh(signature)} text="Fetch Failed" />; } else if (!status.info) { - return refresh(signature)} text="Not Found" />; + return refresh(signature)} text="Not Found" />; } const { info } = status; @@ -191,7 +194,7 @@ function AccountsCard({ signature }: Props) { return null; } else if (!details) { return ( - @@ -199,14 +202,14 @@ function AccountsCard({ signature }: Props) { } else if (details.fetchStatus === FetchStatus.Fetching) { return ; } else if (details?.fetchStatus === FetchStatus.FetchFailed) { - return ; + return ; } else if (!details.transaction || !message) { - return ; + return ; } const { meta } = details.transaction; if (!meta) { - return ; + return ; } const accountRows = message.accountKeys.map((pubkey, index) => { @@ -228,7 +231,7 @@ function AccountsCard({ signature }: Props) { - {displayAddress(pubkey)} + {displayAddress(pubkey.toBase58())} {renderChange()} @@ -284,7 +287,7 @@ function InstructionsSection({ signature }: Props) { const { transaction } = details.transaction; if (transaction.instructions.length === 0) { - return ; + return ; } const result = status.info.result; @@ -313,43 +316,3 @@ function InstructionsSection({ signature }: Props) { ); } - -function LoadingCard() { - return ( -
-
- - Loading -
-
- ); -} - -function RetryCard({ retry, text }: { retry: () => void; text: string }) { - return ( -
-
- {text} - - Try Again - -
-
- - Try Again - -
-
-
- ); -} - -function TableCardBody({ children }: { children: React.ReactNode }) { - return ( -
- - {children} -
-
- ); -} diff --git a/explorer/src/components/account/StakeAccountDetailsCard.tsx b/explorer/src/components/account/StakeAccountDetailsCard.tsx new file mode 100644 index 0000000000..862cedad80 --- /dev/null +++ b/explorer/src/components/account/StakeAccountDetailsCard.tsx @@ -0,0 +1,121 @@ +import React from "react"; +import { StakeAccount } from "solana-sdk-wasm"; +import TableCardBody from "components/common/TableCardBody"; +import { lamportsToSolString } from "utils"; +import Copyable from "components/Copyable"; +import { displayAddress } from "utils/tx"; + +export function StakeAccountDetailsCard({ + account +}: { + account: StakeAccount; +}) { + const { meta, stake } = account; + return ( +
+
+

+ Stake Account +

+
+ + + State + {account.displayState()} + + + {meta && ( + <> + + Rent Reserve (SOL) + + {lamportsToSolString(meta.rentExemptReserve)} + + + + + Authorized Staker Address + + + {meta.authorized.staker.toBase58()} + + + + + + Authorized Withdrawer Address + + + {meta.authorized.withdrawer.toBase58()} + + + + + + Lockup Expiry Epoch + {meta.lockup.epoch} + + + + Lockup Expiry Timestamp + + {new Date(meta.lockup.unixTimestamp).toUTCString()} + + + + + Lockup Custodian Address + + + + {displayAddress(meta.lockup.custodian.toBase58())} + + + + + + )} + + {stake && ( + <> + + Delegated Stake (SOL) + + {lamportsToSolString(stake.delegation.stake)} + + + + + Delegated Vote Address + + + + {displayAddress(stake.delegation.voterPubkey.toBase58())} + + + + + + + Activation Epoch + + {stake.delegation.isBootstrapStake() + ? "-" + : stake.delegation.activationEpoch} + + + + + Deactivation Epoch + + {stake.delegation.isDeactivated() + ? stake.delegation.deactivationEpoch + : "-"} + + + + )} + +
+ ); +} diff --git a/explorer/src/components/common/ErrorCard.tsx b/explorer/src/components/common/ErrorCard.tsx new file mode 100644 index 0000000000..81838ab394 --- /dev/null +++ b/explorer/src/components/common/ErrorCard.tsx @@ -0,0 +1,33 @@ +import React from "react"; + +export default function ErrorCard({ + retry, + text +}: { + retry?: () => void; + text: string; +}) { + return ( +
+
+ {text} + {retry && ( + <> + + Try Again + +
+
+ + Try Again + +
+ + )} +
+
+ ); +} diff --git a/explorer/src/components/common/LoadingCard.tsx b/explorer/src/components/common/LoadingCard.tsx new file mode 100644 index 0000000000..f0088dd074 --- /dev/null +++ b/explorer/src/components/common/LoadingCard.tsx @@ -0,0 +1,12 @@ +import React from "react"; + +export default function LoadingCard() { + return ( +
+
+ + Loading +
+
+ ); +} diff --git a/explorer/src/components/common/TableCardBody.tsx b/explorer/src/components/common/TableCardBody.tsx new file mode 100644 index 0000000000..b2e35f2d13 --- /dev/null +++ b/explorer/src/components/common/TableCardBody.tsx @@ -0,0 +1,15 @@ +import React from "react"; + +export default function TableCardBody({ + children +}: { + children: React.ReactNode; +}) { + return ( +
+ + {children} +
+
+ ); +} diff --git a/explorer/src/components/instruction/RawDetails.tsx b/explorer/src/components/instruction/RawDetails.tsx index e4a86ff81a..412f2b46aa 100644 --- a/explorer/src/components/instruction/RawDetails.tsx +++ b/explorer/src/components/instruction/RawDetails.tsx @@ -19,7 +19,7 @@ export function RawDetails({ ix }: { ix: TransactionInstruction }) { Program - {displayAddress(ix.programId)} + {displayAddress(ix.programId.toBase58())} diff --git a/explorer/src/components/instruction/stake/AuthorizeDetailsCard.tsx b/explorer/src/components/instruction/stake/AuthorizeDetailsCard.tsx index 6da67477ab..00f760dc01 100644 --- a/explorer/src/components/instruction/stake/AuthorizeDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/AuthorizeDetailsCard.tsx @@ -53,7 +53,7 @@ export function AuthorizeDetailsCard(props: { Program - {displayAddress(StakeProgram.programId)} + {displayAddress(StakeProgram.programId.toBase58())} diff --git a/explorer/src/components/instruction/stake/DeactivateDetailsCard.tsx b/explorer/src/components/instruction/stake/DeactivateDetailsCard.tsx index 156c2f59e9..d348be4e93 100644 --- a/explorer/src/components/instruction/stake/DeactivateDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/DeactivateDetailsCard.tsx @@ -39,7 +39,7 @@ export function DeactivateDetailsCard(props: { Program - {displayAddress(StakeProgram.programId)} + {displayAddress(StakeProgram.programId.toBase58())} diff --git a/explorer/src/components/instruction/stake/DelegateDetailsCard.tsx b/explorer/src/components/instruction/stake/DelegateDetailsCard.tsx index f15cb97c52..f6de80dfb0 100644 --- a/explorer/src/components/instruction/stake/DelegateDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/DelegateDetailsCard.tsx @@ -40,7 +40,7 @@ export function DelegateDetailsCard(props: { Program - {displayAddress(StakeProgram.programId)} + {displayAddress(StakeProgram.programId.toBase58())} diff --git a/explorer/src/components/instruction/stake/InitializeDetailsCard.tsx b/explorer/src/components/instruction/stake/InitializeDetailsCard.tsx index 428f1fe702..05fd5ca114 100644 --- a/explorer/src/components/instruction/stake/InitializeDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/InitializeDetailsCard.tsx @@ -40,7 +40,7 @@ export function InitializeDetailsCard(props: { Program - {displayAddress(StakeProgram.programId)} + {displayAddress(StakeProgram.programId.toBase58())} @@ -88,7 +88,7 @@ export function InitializeDetailsCard(props: { Lockup Custodian Address - {displayAddress(params.lockup.custodian)} + {displayAddress(params.lockup.custodian.toBase58())} diff --git a/explorer/src/components/instruction/stake/SplitDetailsCard.tsx b/explorer/src/components/instruction/stake/SplitDetailsCard.tsx index 6ab8edaa25..cf51d8f4e0 100644 --- a/explorer/src/components/instruction/stake/SplitDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/SplitDetailsCard.tsx @@ -36,7 +36,7 @@ export function SplitDetailsCard(props: { Program - {displayAddress(StakeProgram.programId)} + {displayAddress(StakeProgram.programId.toBase58())} diff --git a/explorer/src/components/instruction/stake/WithdrawDetailsCard.tsx b/explorer/src/components/instruction/stake/WithdrawDetailsCard.tsx index 68be0e0914..39ccd31e19 100644 --- a/explorer/src/components/instruction/stake/WithdrawDetailsCard.tsx +++ b/explorer/src/components/instruction/stake/WithdrawDetailsCard.tsx @@ -41,7 +41,7 @@ export function WithdrawDetailsCard(props: { Program - {displayAddress(StakeProgram.programId)} + {displayAddress(StakeProgram.programId.toBase58())} diff --git a/explorer/src/components/instruction/system/AllocateDetailsCard.tsx b/explorer/src/components/instruction/system/AllocateDetailsCard.tsx index 55fd133992..858be1a850 100644 --- a/explorer/src/components/instruction/system/AllocateDetailsCard.tsx +++ b/explorer/src/components/instruction/system/AllocateDetailsCard.tsx @@ -38,7 +38,7 @@ export function AllocateDetailsCard(props: { Program - {displayAddress(SystemProgram.programId)} + {displayAddress(SystemProgram.programId.toBase58())} diff --git a/explorer/src/components/instruction/system/AllocateWithSeedDetailsCard.tsx b/explorer/src/components/instruction/system/AllocateWithSeedDetailsCard.tsx index 644690ad68..fd9eaac88e 100644 --- a/explorer/src/components/instruction/system/AllocateWithSeedDetailsCard.tsx +++ b/explorer/src/components/instruction/system/AllocateWithSeedDetailsCard.tsx @@ -39,7 +39,7 @@ export function AllocateWithSeedDetailsCard(props: { Program - {displayAddress(SystemProgram.programId)} + {displayAddress(SystemProgram.programId.toBase58())} @@ -80,7 +80,7 @@ export function AllocateWithSeedDetailsCard(props: { Assigned Owner - {displayAddress(params.programId)} + {displayAddress(params.programId.toBase58())} diff --git a/explorer/src/components/instruction/system/AssignDetailsCard.tsx b/explorer/src/components/instruction/system/AssignDetailsCard.tsx index 6a2a1c94f3..2518fc89a4 100644 --- a/explorer/src/components/instruction/system/AssignDetailsCard.tsx +++ b/explorer/src/components/instruction/system/AssignDetailsCard.tsx @@ -38,7 +38,7 @@ export function AssignDetailsCard(props: { Program - {displayAddress(SystemProgram.programId)} + {displayAddress(SystemProgram.programId.toBase58())} @@ -56,7 +56,7 @@ export function AssignDetailsCard(props: { Assigned Owner - {displayAddress(params.programId)} + {displayAddress(params.programId.toBase58())} diff --git a/explorer/src/components/instruction/system/AssignWithSeedDetailsCard.tsx b/explorer/src/components/instruction/system/AssignWithSeedDetailsCard.tsx index e98f639fc8..2d9459e13e 100644 --- a/explorer/src/components/instruction/system/AssignWithSeedDetailsCard.tsx +++ b/explorer/src/components/instruction/system/AssignWithSeedDetailsCard.tsx @@ -39,7 +39,7 @@ export function AssignWithSeedDetailsCard(props: { Program - {displayAddress(SystemProgram.programId)} + {displayAddress(SystemProgram.programId.toBase58())} @@ -75,7 +75,7 @@ export function AssignWithSeedDetailsCard(props: { Assigned Owner - {displayAddress(params.programId)} + {displayAddress(params.programId.toBase58())} diff --git a/explorer/src/components/instruction/system/CreateDetailsCard.tsx b/explorer/src/components/instruction/system/CreateDetailsCard.tsx index 4b979b1d12..f03143e4af 100644 --- a/explorer/src/components/instruction/system/CreateDetailsCard.tsx +++ b/explorer/src/components/instruction/system/CreateDetailsCard.tsx @@ -40,7 +40,7 @@ export function CreateDetailsCard(props: { Program - {displayAddress(SystemProgram.programId)} + {displayAddress(SystemProgram.programId.toBase58())} @@ -77,7 +77,7 @@ export function CreateDetailsCard(props: { Assigned Owner - {displayAddress(params.programId)} + {displayAddress(params.programId.toBase58())} diff --git a/explorer/src/components/instruction/system/CreateWithSeedDetailsCard.tsx b/explorer/src/components/instruction/system/CreateWithSeedDetailsCard.tsx index e914420aa0..6fe24125b9 100644 --- a/explorer/src/components/instruction/system/CreateWithSeedDetailsCard.tsx +++ b/explorer/src/components/instruction/system/CreateWithSeedDetailsCard.tsx @@ -41,7 +41,7 @@ export function CreateWithSeedDetailsCard(props: { Program - {displayAddress(SystemProgram.programId)} + {displayAddress(SystemProgram.programId.toBase58())} @@ -105,7 +105,7 @@ export function CreateWithSeedDetailsCard(props: { Assigned Owner - {displayAddress(params.programId)} + {displayAddress(params.programId.toBase58())} diff --git a/explorer/src/components/instruction/system/NonceAdvanceDetailsCard.tsx b/explorer/src/components/instruction/system/NonceAdvanceDetailsCard.tsx index c606b71fd0..6ba33213a7 100644 --- a/explorer/src/components/instruction/system/NonceAdvanceDetailsCard.tsx +++ b/explorer/src/components/instruction/system/NonceAdvanceDetailsCard.tsx @@ -39,7 +39,7 @@ export function NonceAdvanceDetailsCard(props: { Program - {displayAddress(SystemProgram.programId)} + {displayAddress(SystemProgram.programId.toBase58())} diff --git a/explorer/src/components/instruction/system/NonceAuthorizeDetailsCard.tsx b/explorer/src/components/instruction/system/NonceAuthorizeDetailsCard.tsx index 6cd399c77f..0f33f7da7f 100644 --- a/explorer/src/components/instruction/system/NonceAuthorizeDetailsCard.tsx +++ b/explorer/src/components/instruction/system/NonceAuthorizeDetailsCard.tsx @@ -40,7 +40,7 @@ export function NonceAuthorizeDetailsCard(props: { Program - {displayAddress(SystemProgram.programId)} + {displayAddress(SystemProgram.programId.toBase58())} diff --git a/explorer/src/components/instruction/system/NonceInitializeDetailsCard.tsx b/explorer/src/components/instruction/system/NonceInitializeDetailsCard.tsx index d1773568a2..fbd922ae86 100644 --- a/explorer/src/components/instruction/system/NonceInitializeDetailsCard.tsx +++ b/explorer/src/components/instruction/system/NonceInitializeDetailsCard.tsx @@ -39,7 +39,7 @@ export function NonceInitializeDetailsCard(props: { Program - {displayAddress(SystemProgram.programId)} + {displayAddress(SystemProgram.programId.toBase58())} diff --git a/explorer/src/components/instruction/system/NonceWithdrawDetailsCard.tsx b/explorer/src/components/instruction/system/NonceWithdrawDetailsCard.tsx index e2a8269175..3fa90bc699 100644 --- a/explorer/src/components/instruction/system/NonceWithdrawDetailsCard.tsx +++ b/explorer/src/components/instruction/system/NonceWithdrawDetailsCard.tsx @@ -42,7 +42,7 @@ export function NonceWithdrawDetailsCard(props: { Program - {displayAddress(SystemProgram.programId)} + {displayAddress(SystemProgram.programId.toBase58())} diff --git a/explorer/src/components/instruction/system/TransferDetailsCard.tsx b/explorer/src/components/instruction/system/TransferDetailsCard.tsx index 160e80e389..ad2cba05fd 100644 --- a/explorer/src/components/instruction/system/TransferDetailsCard.tsx +++ b/explorer/src/components/instruction/system/TransferDetailsCard.tsx @@ -34,7 +34,7 @@ export function TransferDetailsCard(props: { Program - {displayAddress(SystemProgram.programId)} + {displayAddress(SystemProgram.programId.toBase58())} diff --git a/explorer/src/providers/accounts.tsx b/explorer/src/providers/accounts.tsx index 93df5571be..47c0e9abaf 100644 --- a/explorer/src/providers/accounts.tsx +++ b/explorer/src/providers/accounts.tsx @@ -4,7 +4,8 @@ import { Connection, TransactionSignature, TransactionError, - SignatureStatus + SignatureStatus, + StakeProgram } from "@solana/web3.js"; import { useQuery } from "../utils/url"; import { useCluster, ClusterStatus } from "./cluster"; @@ -27,6 +28,7 @@ export interface Details { executable: boolean; owner: PublicKey; space: number; + data?: Buffer; } export interface Account { @@ -192,10 +194,18 @@ async function fetchAccountInfo( fetchStatus = Status.NotFound; } else { lamports = result.lamports; + let data = undefined; + + // Only save data in memory if we can decode it + if (result.owner.equals(StakeProgram.programId)) { + data = result.data; + } + details = { space: result.data.length, executable: result.executable, - owner: result.owner + owner: result.owner, + data }; fetchStatus = Status.FetchingHistory; fetchAccountHistory(dispatch, pubkey, url); diff --git a/explorer/src/utils/tx.ts b/explorer/src/utils/tx.ts index 7f00dbd688..8721f98e3e 100644 --- a/explorer/src/utils/tx.ts +++ b/explorer/src/utils/tx.ts @@ -1,5 +1,4 @@ import { - PublicKey, SystemProgram, StakeProgram, VOTE_PROGRAM_ID, @@ -40,8 +39,7 @@ const SYSVAR_IDS = { [SYSVAR_STAKE_HISTORY_PUBKEY.toBase58()]: "SYSVAR_STAKE_HISTORY" }; -export function displayAddress(pubkey: PublicKey): string { - const address = pubkey.toBase58(); +export function displayAddress(address: string): string { return ( PROGRAM_IDS[address] || LOADER_IDS[address] || diff --git a/explorer/wasm/.gitignore b/explorer/wasm/.gitignore new file mode 100644 index 0000000000..7f384f961b --- /dev/null +++ b/explorer/wasm/.gitignore @@ -0,0 +1,16 @@ +/target/ + +**/*.rs.bk +.cargo + +/config/ + +# log files +*.log +log-*.txt +log-*/ + +# intellij files +/.idea/ +/solana.iml +/.vscode/ diff --git a/explorer/wasm/Cargo.lock b/explorer/wasm/Cargo.lock new file mode 100644 index 0000000000..86ed5fbbaf --- /dev/null +++ b/explorer/wasm/Cargo.lock @@ -0,0 +1,179 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "bincode" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" +dependencies = [ + "byteorder", + "serde", +] + +[[package]] +name = "bs58" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" + +[[package]] +name = "bumpalo" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "js-sys" +version = "0.3.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa5a448de267e7358beaf4a5d849518fe9a0c13fce7afd44b06e68550e5562a7" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "proc-macro2" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42934bc9c8ab0d3b273a16d8551c8f0fcff46be73276ca083ec2414c15c4ba5e" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "solana-sdk-wasm" +version = "1.2.0" +dependencies = [ + "bincode", + "bs58", + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "syn" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4696caa4048ac7ce2bcd2e484b3cef88c1004e41b8e945a277e2c25dc0b72060" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "wasm-bindgen" +version = "0.2.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c7d40d09cdbf0f4895ae58cf57d92e1e57a9dd8ed2e8390514b54a47cc5551" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3972e137ebf830900db522d6c8fd74d1900dcfc733462e9a12e942b00b4ac94" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cd85aa2c579e8892442954685f0d801f9129de24fa2136b2c6a539c76b65776" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb197bd3a47553334907ffd2f16507b4f4f01bbec3ac921a7719e0decdfe72a" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a91c2916119c17a8e316507afaaa2dd94b47646048014bbdf6bef098c1bb58ad" diff --git a/explorer/wasm/Cargo.toml b/explorer/wasm/Cargo.toml new file mode 100644 index 0000000000..70d712e0a1 --- /dev/null +++ b/explorer/wasm/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "solana-sdk-wasm" +version = "1.2.0" +description = "Solana SDK Wasm" +authors = ["Solana Maintainers "] +repository = "https://github.com/solana-labs/solana" +homepage = "https://solana.com/" +license = "Apache-2.0" +edition = "2018" + +[lib] +name = "solana_sdk_wasm" +crate-type = ["cdylib", "rlib"] + +[dependencies] +bincode = "1.2.1" +bs58 = "0.3.1" +serde = { version = "1.0", features = ["derive"] } +wasm-bindgen = "0.2" +js-sys = "0.3" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[profile.release] +# Tell `rustc` to optimize for small code size. +opt-level = "s" \ No newline at end of file diff --git a/explorer/wasm/README.md b/explorer/wasm/README.md new file mode 100644 index 0000000000..7074e7fe6e --- /dev/null +++ b/explorer/wasm/README.md @@ -0,0 +1,15 @@ +# solana-sdk-wasm + +Temporary location for a Solana SDK for decoding account data in WebAssembly + +### Install + +cargo install wasm-pack + +### Build + +wasm-pack build + +### Release + +Add built files in `./pkg` to source control \ No newline at end of file diff --git a/explorer/wasm/pkg/package.json b/explorer/wasm/pkg/package.json new file mode 100644 index 0000000000..f0ea2ed836 --- /dev/null +++ b/explorer/wasm/pkg/package.json @@ -0,0 +1,22 @@ +{ + "name": "solana-sdk-wasm", + "collaborators": [ + "Solana Maintainers " + ], + "description": "Solana SDK Wasm", + "version": "1.2.0", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/solana-labs/solana" + }, + "files": [ + "solana_sdk_wasm_bg.wasm", + "solana_sdk_wasm.js", + "solana_sdk_wasm.d.ts" + ], + "module": "solana_sdk_wasm.js", + "homepage": "https://solana.com/", + "types": "solana_sdk_wasm.d.ts", + "sideEffects": false +} \ No newline at end of file diff --git a/explorer/wasm/pkg/solana_sdk_wasm.d.ts b/explorer/wasm/pkg/solana_sdk_wasm.d.ts new file mode 100644 index 0000000000..fc17502628 --- /dev/null +++ b/explorer/wasm/pkg/solana_sdk_wasm.d.ts @@ -0,0 +1,140 @@ +/* tslint:disable */ +/* eslint-disable */ +/** +*/ +export enum StakeState { + Uninitialized, + Initialized, + Delegated, + RewardsPool, +} +/** +*/ +export class Authorized { + free(): void; +/** +* @returns {Pubkey} +*/ + staker: Pubkey; +/** +* @returns {Pubkey} +*/ + withdrawer: Pubkey; +} +/** +*/ +export class Delegation { + free(): void; +/** +* @returns {boolean} +*/ + isBootstrapStake(): boolean; +/** +* @returns {boolean} +*/ + isDeactivated(): boolean; +/** +* @returns {number} +*/ + readonly activationEpoch: number; +/** +* @returns {number} +*/ + readonly deactivationEpoch: number; +/** +* @returns {number} +*/ + readonly stake: number; +/** +* @returns {Pubkey} +*/ + readonly voterPubkey: Pubkey; +/** +* @returns {number} +*/ + readonly warmupCooldownRate: number; +} +/** +*/ +export class Lockup { + free(): void; +/** +* custodian signature on a transaction exempts the operation from +* lockup constraints +* @returns {Pubkey} +*/ + custodian: Pubkey; +/** +* @returns {number} +*/ + readonly epoch: number; +/** +* @returns {number} +*/ + readonly unixTimestamp: number; +} +/** +*/ +export class Meta { + free(): void; +/** +* @returns {Authorized} +*/ + authorized: Authorized; +/** +* @returns {Lockup} +*/ + lockup: Lockup; +/** +* @returns {number} +*/ + readonly rentExemptReserve: number; +} +/** +*/ +export class Pubkey { + free(): void; +/** +* @returns {string} +*/ + toBase58(): string; +} +/** +*/ +export class Stake { + free(): void; +/** +* @returns {number} +*/ + readonly creditsObserved: number; +/** +* @returns {Delegation} +*/ + delegation: Delegation; +} +/** +*/ +export class StakeAccount { + free(): void; +/** +* @param {Uint8Array} data +* @returns {StakeAccount} +*/ + static fromAccountData(data: Uint8Array): StakeAccount; +/** +* @returns {string} +*/ + displayState(): string; +/** +* @returns {Meta | undefined} +*/ + meta?: Meta; +/** +* @returns {Stake | undefined} +*/ + stake?: Stake; +/** +* @returns {number} +*/ + state: number; +} diff --git a/explorer/wasm/pkg/solana_sdk_wasm.js b/explorer/wasm/pkg/solana_sdk_wasm.js new file mode 100644 index 0000000000..b51a09e8e7 --- /dev/null +++ b/explorer/wasm/pkg/solana_sdk_wasm.js @@ -0,0 +1,2 @@ +import * as wasm from "./solana_sdk_wasm_bg.wasm"; +export * from "./solana_sdk_wasm_bg.js"; \ No newline at end of file diff --git a/explorer/wasm/pkg/solana_sdk_wasm_bg.d.ts b/explorer/wasm/pkg/solana_sdk_wasm_bg.d.ts new file mode 100644 index 0000000000..32ac915c54 --- /dev/null +++ b/explorer/wasm/pkg/solana_sdk_wasm_bg.d.ts @@ -0,0 +1,44 @@ +/* tslint:disable */ +/* eslint-disable */ +export const memory: WebAssembly.Memory; +export function __wbg_stakeaccount_free(a: number): void; +export function __wbg_get_stakeaccount_meta(a: number): number; +export function __wbg_set_stakeaccount_meta(a: number, b: number): void; +export function __wbg_get_stakeaccount_stake(a: number): number; +export function __wbg_set_stakeaccount_stake(a: number, b: number): void; +export function __wbg_get_stakeaccount_state(a: number): number; +export function __wbg_set_stakeaccount_state(a: number, b: number): void; +export function stakeaccount_fromAccountData(a: number, b: number): number; +export function stakeaccount_displayState(a: number, b: number): void; +export function __wbg_lockup_free(a: number): void; +export function __wbg_get_lockup_custodian(a: number): number; +export function __wbg_set_lockup_custodian(a: number, b: number): void; +export function lockup_unix_timestamp(a: number): number; +export function lockup_epoch(a: number): number; +export function __wbg_pubkey_free(a: number): void; +export function pubkey_toBase58(a: number, b: number): void; +export function __wbg_authorized_free(a: number): void; +export function __wbg_get_authorized_staker(a: number): number; +export function __wbg_set_authorized_staker(a: number, b: number): void; +export function __wbg_get_authorized_withdrawer(a: number): number; +export function __wbg_set_authorized_withdrawer(a: number, b: number): void; +export function __wbg_meta_free(a: number): void; +export function __wbg_get_meta_authorized(a: number): number; +export function __wbg_set_meta_authorized(a: number, b: number): void; +export function __wbg_get_meta_lockup(a: number): number; +export function __wbg_set_meta_lockup(a: number, b: number): void; +export function meta_rent_exempt_reserve(a: number): number; +export function __wbg_stake_free(a: number): void; +export function __wbg_get_stake_delegation(a: number): number; +export function __wbg_set_stake_delegation(a: number, b: number): void; +export function stake_credits_observed(a: number): number; +export function __wbg_delegation_free(a: number): void; +export function delegation_voter_pubkey(a: number): number; +export function delegation_stake(a: number): number; +export function delegation_isBootstrapStake(a: number): number; +export function delegation_isDeactivated(a: number): number; +export function delegation_activation_epoch(a: number): number; +export function delegation_deactivation_epoch(a: number): number; +export function delegation_warmup_cooldown_rate(a: number): number; +export function __wbindgen_malloc(a: number): number; +export function __wbindgen_free(a: number, b: number): void; diff --git a/explorer/wasm/pkg/solana_sdk_wasm_bg.js b/explorer/wasm/pkg/solana_sdk_wasm_bg.js new file mode 100644 index 0000000000..2fb23b7893 --- /dev/null +++ b/explorer/wasm/pkg/solana_sdk_wasm_bg.js @@ -0,0 +1,483 @@ +import * as wasm from './solana_sdk_wasm_bg.wasm'; + +const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder; + +let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true }); + +cachedTextDecoder.decode(); + +let cachegetUint8Memory0 = null; +function getUint8Memory0() { + if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) { + cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer); + } + return cachegetUint8Memory0; +} + +function getStringFromWasm0(ptr, len) { + return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); +} + +const heap = new Array(32).fill(undefined); + +heap.push(undefined, null, true, false); + +let heap_next = heap.length; + +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} + +function getObject(idx) { return heap[idx]; } + +function dropObject(idx) { + if (idx < 36) return; + heap[idx] = heap_next; + heap_next = idx; +} + +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} + +function isLikeNone(x) { + return x === undefined || x === null; +} + +function _assertClass(instance, klass) { + if (!(instance instanceof klass)) { + throw new Error(`expected instance of ${klass.name}`); + } + return instance.ptr; +} + +let WASM_VECTOR_LEN = 0; + +function passArray8ToWasm0(arg, malloc) { + const ptr = malloc(arg.length * 1); + getUint8Memory0().set(arg, ptr / 1); + WASM_VECTOR_LEN = arg.length; + return ptr; +} + +let cachegetInt32Memory0 = null; +function getInt32Memory0() { + if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) { + cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer); + } + return cachegetInt32Memory0; +} +/** +*/ +export const StakeState = Object.freeze({ Uninitialized:0,Initialized:1,Delegated:2,RewardsPool:3, }); +/** +*/ +export class Authorized { + + static __wrap(ptr) { + const obj = Object.create(Authorized.prototype); + obj.ptr = ptr; + + return obj; + } + + free() { + const ptr = this.ptr; + this.ptr = 0; + + wasm.__wbg_authorized_free(ptr); + } + /** + * @returns {Pubkey} + */ + get staker() { + var ret = wasm.__wbg_get_authorized_staker(this.ptr); + return Pubkey.__wrap(ret); + } + /** + * @param {Pubkey} arg0 + */ + set staker(arg0) { + _assertClass(arg0, Pubkey); + var ptr0 = arg0.ptr; + arg0.ptr = 0; + wasm.__wbg_set_authorized_staker(this.ptr, ptr0); + } + /** + * @returns {Pubkey} + */ + get withdrawer() { + var ret = wasm.__wbg_get_authorized_withdrawer(this.ptr); + return Pubkey.__wrap(ret); + } + /** + * @param {Pubkey} arg0 + */ + set withdrawer(arg0) { + _assertClass(arg0, Pubkey); + var ptr0 = arg0.ptr; + arg0.ptr = 0; + wasm.__wbg_set_authorized_withdrawer(this.ptr, ptr0); + } +} +/** +*/ +export class Delegation { + + static __wrap(ptr) { + const obj = Object.create(Delegation.prototype); + obj.ptr = ptr; + + return obj; + } + + free() { + const ptr = this.ptr; + this.ptr = 0; + + wasm.__wbg_delegation_free(ptr); + } + /** + * @returns {Pubkey} + */ + get voterPubkey() { + var ret = wasm.delegation_voter_pubkey(this.ptr); + return Pubkey.__wrap(ret); + } + /** + * @returns {number} + */ + get stake() { + var ret = wasm.delegation_stake(this.ptr); + return ret; + } + /** + * @returns {boolean} + */ + isBootstrapStake() { + var ret = wasm.delegation_isBootstrapStake(this.ptr); + return ret !== 0; + } + /** + * @returns {boolean} + */ + isDeactivated() { + var ret = wasm.delegation_isDeactivated(this.ptr); + return ret !== 0; + } + /** + * @returns {number} + */ + get activationEpoch() { + var ret = wasm.delegation_activation_epoch(this.ptr); + return ret; + } + /** + * @returns {number} + */ + get deactivationEpoch() { + var ret = wasm.delegation_deactivation_epoch(this.ptr); + return ret; + } + /** + * @returns {number} + */ + get warmupCooldownRate() { + var ret = wasm.delegation_warmup_cooldown_rate(this.ptr); + return ret; + } +} +/** +*/ +export class Lockup { + + static __wrap(ptr) { + const obj = Object.create(Lockup.prototype); + obj.ptr = ptr; + + return obj; + } + + free() { + const ptr = this.ptr; + this.ptr = 0; + + wasm.__wbg_lockup_free(ptr); + } + /** + * custodian signature on a transaction exempts the operation from + * lockup constraints + * @returns {Pubkey} + */ + get custodian() { + var ret = wasm.__wbg_get_lockup_custodian(this.ptr); + return Pubkey.__wrap(ret); + } + /** + * custodian signature on a transaction exempts the operation from + * lockup constraints + * @param {Pubkey} arg0 + */ + set custodian(arg0) { + _assertClass(arg0, Pubkey); + var ptr0 = arg0.ptr; + arg0.ptr = 0; + wasm.__wbg_set_lockup_custodian(this.ptr, ptr0); + } + /** + * @returns {number} + */ + get unixTimestamp() { + var ret = wasm.lockup_unix_timestamp(this.ptr); + return ret; + } + /** + * @returns {number} + */ + get epoch() { + var ret = wasm.lockup_epoch(this.ptr); + return ret; + } +} +/** +*/ +export class Meta { + + static __wrap(ptr) { + const obj = Object.create(Meta.prototype); + obj.ptr = ptr; + + return obj; + } + + free() { + const ptr = this.ptr; + this.ptr = 0; + + wasm.__wbg_meta_free(ptr); + } + /** + * @returns {Authorized} + */ + get authorized() { + var ret = wasm.__wbg_get_meta_authorized(this.ptr); + return Authorized.__wrap(ret); + } + /** + * @param {Authorized} arg0 + */ + set authorized(arg0) { + _assertClass(arg0, Authorized); + var ptr0 = arg0.ptr; + arg0.ptr = 0; + wasm.__wbg_set_meta_authorized(this.ptr, ptr0); + } + /** + * @returns {Lockup} + */ + get lockup() { + var ret = wasm.__wbg_get_meta_lockup(this.ptr); + return Lockup.__wrap(ret); + } + /** + * @param {Lockup} arg0 + */ + set lockup(arg0) { + _assertClass(arg0, Lockup); + var ptr0 = arg0.ptr; + arg0.ptr = 0; + wasm.__wbg_set_meta_lockup(this.ptr, ptr0); + } + /** + * @returns {number} + */ + get rentExemptReserve() { + var ret = wasm.meta_rent_exempt_reserve(this.ptr); + return ret; + } +} +/** +*/ +export class Pubkey { + + static __wrap(ptr) { + const obj = Object.create(Pubkey.prototype); + obj.ptr = ptr; + + return obj; + } + + free() { + const ptr = this.ptr; + this.ptr = 0; + + wasm.__wbg_pubkey_free(ptr); + } + /** + * @returns {string} + */ + toBase58() { + try { + wasm.pubkey_toBase58(8, this.ptr); + var r0 = getInt32Memory0()[8 / 4 + 0]; + var r1 = getInt32Memory0()[8 / 4 + 1]; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_free(r0, r1); + } + } +} +/** +*/ +export class Stake { + + static __wrap(ptr) { + const obj = Object.create(Stake.prototype); + obj.ptr = ptr; + + return obj; + } + + free() { + const ptr = this.ptr; + this.ptr = 0; + + wasm.__wbg_stake_free(ptr); + } + /** + * @returns {Delegation} + */ + get delegation() { + var ret = wasm.__wbg_get_stake_delegation(this.ptr); + return Delegation.__wrap(ret); + } + /** + * @param {Delegation} arg0 + */ + set delegation(arg0) { + _assertClass(arg0, Delegation); + var ptr0 = arg0.ptr; + arg0.ptr = 0; + wasm.__wbg_set_stake_delegation(this.ptr, ptr0); + } + /** + * @returns {number} + */ + get creditsObserved() { + var ret = wasm.stake_credits_observed(this.ptr); + return ret; + } +} +/** +*/ +export class StakeAccount { + + static __wrap(ptr) { + const obj = Object.create(StakeAccount.prototype); + obj.ptr = ptr; + + return obj; + } + + free() { + const ptr = this.ptr; + this.ptr = 0; + + wasm.__wbg_stakeaccount_free(ptr); + } + /** + * @returns {Meta | undefined} + */ + get meta() { + var ret = wasm.__wbg_get_stakeaccount_meta(this.ptr); + return ret === 0 ? undefined : Meta.__wrap(ret); + } + /** + * @param {Meta | undefined} arg0 + */ + set meta(arg0) { + let ptr0 = 0; + if (!isLikeNone(arg0)) { + _assertClass(arg0, Meta); + ptr0 = arg0.ptr; + arg0.ptr = 0; + } + wasm.__wbg_set_stakeaccount_meta(this.ptr, ptr0); + } + /** + * @returns {Stake | undefined} + */ + get stake() { + var ret = wasm.__wbg_get_stakeaccount_stake(this.ptr); + return ret === 0 ? undefined : Stake.__wrap(ret); + } + /** + * @param {Stake | undefined} arg0 + */ + set stake(arg0) { + let ptr0 = 0; + if (!isLikeNone(arg0)) { + _assertClass(arg0, Stake); + ptr0 = arg0.ptr; + arg0.ptr = 0; + } + wasm.__wbg_set_stakeaccount_stake(this.ptr, ptr0); + } + /** + * @returns {number} + */ + get state() { + var ret = wasm.__wbg_get_stakeaccount_state(this.ptr); + return ret >>> 0; + } + /** + * @param {number} arg0 + */ + set state(arg0) { + wasm.__wbg_set_stakeaccount_state(this.ptr, arg0); + } + /** + * @param {Uint8Array} data + * @returns {StakeAccount} + */ + static fromAccountData(data) { + var ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc); + var len0 = WASM_VECTOR_LEN; + var ret = wasm.stakeaccount_fromAccountData(ptr0, len0); + return StakeAccount.__wrap(ret); + } + /** + * @returns {string} + */ + displayState() { + try { + wasm.stakeaccount_displayState(8, this.ptr); + var r0 = getInt32Memory0()[8 / 4 + 0]; + var r1 = getInt32Memory0()[8 / 4 + 1]; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_free(r0, r1); + } + } +} + +export const __wbindgen_string_new = function(arg0, arg1) { + var ret = getStringFromWasm0(arg0, arg1); + return addHeapObject(ret); +}; + +export const __wbindgen_throw = function(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); +}; + +export const __wbindgen_rethrow = function(arg0) { + throw takeObject(arg0); +}; + diff --git a/explorer/wasm/pkg/solana_sdk_wasm_bg.wasm b/explorer/wasm/pkg/solana_sdk_wasm_bg.wasm new file mode 100644 index 0000000000000000000000000000000000000000..588775629cfa58fca39d6671400f5b02a10dcd06 GIT binary patch literal 73523 zcmdqK3zS^fS?7E9IaSrwUES)EEZeP@b*c~(Ik97faU{piklsD9Y(;VGfblh9W*jHh zIPUgKEm?L#OKQi8qr5<244B{n0U>t42Ellk0Orz31USS2B^WSV-$4Yy^@XgtW>#1$ zcgBP7@Bi(6PMzv5N$o&btZOZIoyUHB``h1pe|w+g<~?_~BuU&|tKOL&Jm?PUZ}QIU zpj|sXaPdBQXN{-a@O18-bzO6l#K%_99iaLF9&*>P0%{;TIH#W)KX!XSKk0{DcK&NG z*>l(JH{W^lO?z&cx#?a&x#?ZEzIJ-gxi{T(@4Ifh^Ojq8-FefVy|cI7dFxGg?z%Tg zPFKENDZlqUvv=K_xYHF~RVg~VD-^C}f9d|hW%Z`3H`{4#ypg1JZqpTsbIqilB&h>M zjikYCy^*ZsQV(4CqsPu=S>`h5>in%IT=1kOyoPJ%Mjef$NmB*uSuLxpGXITOGcK{} zRL<(WarHDCsU>xn)Koql8XC&7q59BJ;*y5-lh($P8vog!TAHLI)*!I*UzQC6GZ(et zVMmoLajmS?s<~R--R-Wt5+IY6jbWzI&(AL;t)V|}zPfS8t~>6UeQ%naF9h8R+HaoO zb@RL5eb+sA?!D>X&+gilJn7bjSGVrk+yDBGU3+g%-WK2P8Tj^=^{1(8{Ukf%>m`-L z>)Xyyd+)B~r`;K<^ZrNN`bERJ>kc1=_U4-b{hBp>uWz|+&)v7*{NC^0doy+4vN8aE z`(5vzx##YHiC=YVd!vaj-hI!Wy?5Pm+s$_-KEBjgmb`rDx$)6Gciwj2O?z*N`{bk8^W0hNPy7`{H z@40LCw)gG2B^3Sj>SR@3+CXQMD=U+=^zFaf*c)Eu?Y+0{ea|hkH{ZK!HkpbgG{(Ut z-ZdWD5N!mI{d@`~i1f zc=qnuUANq}ch60Cz03N!CHct$>RwZ!x2N3sl}G#T+PiD^rZBC^ziwCFSi8WxuJY`* zJyUnxwHJ|i^WEPq22DQUCi-5#8J^vH+de4wmgL9#s>bJx=%#|iHdLzKQdH~}IO;Z5 z3fz11>>V(`yYIT|_FL|{_s*MU#T`Er$TW)TJ8r(68AyK0+1$s6CObawhTrEB?^RBAGtqsC*7aB%Pq0Z}R_w`^|fE z!2KupqwY0daDT~*f8V|DtL~TFrI-H~B=;%zX}9lB-81gi≪k;FSBSyWp||7k|Y) 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 wOS}0-3, + pub stake: Option, + pub state: State, +} + +impl From for StakeAccount { + fn from(state: StakeState) -> Self { + match state { + StakeState::Uninitialized => StakeAccount { + state: State::Uninitialized, + meta: None, + stake: None, + }, + StakeState::Initialized(meta) => StakeAccount { + state: State::Initialized, + meta: Some(meta), + stake: None, + }, + StakeState::Stake(meta, stake) => StakeAccount { + state: State::Delegated, + meta: Some(meta), + stake: Some(stake), + }, + StakeState::RewardsPool => StakeAccount { + state: State::RewardsPool, + meta: None, + stake: None, + }, + } + } +} + +#[wasm_bindgen] +impl StakeAccount { + #[wasm_bindgen(js_name = fromAccountData)] + pub fn from_account_data(data: &[u8]) -> Result { + let stake_state: StakeState = bincode::deserialize(data) + .map_err(|_| JsValue::from_str("invalid stake account data"))?; + return Ok(stake_state.into()); + } + + #[wasm_bindgen(js_name = displayState)] + pub fn display_state(&self) -> String { + match self.state { + State::Uninitialized => "Uninitialized".to_string(), + State::Initialized => "Initialized".to_string(), + State::Delegated => "Delegated".to_string(), + State::RewardsPool => "RewardsPool".to_string(), + } + } +} + +/// UnixTimestamp is an approximate measure of real-world time, +/// expressed as Unix time (ie. seconds since the Unix epoch) +pub type UnixTimestamp = i64; + +#[wasm_bindgen] +#[derive(Serialize, Deserialize, PartialEq, Clone, Copy)] +pub struct Lockup { + /// UnixTimestamp at which this stake will allow withdrawal, or + /// changes to authorized staker or withdrawer, unless the + /// transaction is signed by the custodian + unix_timestamp: UnixTimestamp, + /// epoch height at which this stake will allow withdrawal, or + /// changes to authorized staker or withdrawer, unless the + /// transaction is signed by the custodian + /// to the custodian + epoch: Epoch, + /// custodian signature on a transaction exempts the operation from + /// lockup constraints + pub custodian: Pubkey, +} + +#[wasm_bindgen] +impl Lockup { + #[wasm_bindgen(getter = unixTimestamp)] + pub fn unix_timestamp(&self) -> f64 { + self.unix_timestamp as f64 + } + + #[wasm_bindgen(getter)] + pub fn epoch(&self) -> f64 { + self.epoch as f64 + } +} + +/// Epoch is a unit of time a given leader schedule is honored, +/// some number of Slots. +pub type Epoch = u64; + +#[wasm_bindgen] +#[repr(transparent)] +#[derive(Serialize, Deserialize, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct Pubkey([u8; 32]); + +#[wasm_bindgen] +impl Pubkey { + #[wasm_bindgen(js_name = toBase58)] + pub fn to_base_58(&self) -> String { + bs58::encode(&self.0).into_string() + } +} + +#[wasm_bindgen] +#[derive(Serialize, Deserialize, PartialEq, Clone, Copy)] +pub struct Authorized { + pub staker: Pubkey, + pub withdrawer: Pubkey, +} + +#[wasm_bindgen] +#[derive(Serialize, Deserialize, PartialEq, Clone, Copy)] +pub struct Meta { + rent_exempt_reserve: u64, + pub authorized: Authorized, + pub lockup: Lockup, +} + +#[wasm_bindgen] +impl Meta { + #[wasm_bindgen(getter = rentExemptReserve)] + pub fn rent_exempt_reserve(&self) -> f64 { + self.rent_exempt_reserve as f64 + } +} + +#[wasm_bindgen] +#[derive(Serialize, Deserialize, PartialEq, Clone, Copy)] +pub struct Stake { + pub delegation: Delegation, + /// credits observed is credits from vote account state when delegated or redeemed + credits_observed: u64, +} + +#[wasm_bindgen] +impl Stake { + #[wasm_bindgen(getter = creditsObserved)] + pub fn credits_observed(&self) -> f64 { + self.credits_observed as f64 + } +} + +#[wasm_bindgen] +#[derive(Serialize, Deserialize, PartialEq, Clone, Copy)] +pub struct Delegation { + /// to whom the stake is delegated + voter_pubkey: Pubkey, + /// activated stake amount, set at delegate() time + stake: u64, + /// epoch at which this stake was activated, std::Epoch::MAX if is a bootstrap stake + activation_epoch: Epoch, + /// epoch the stake was deactivated, std::Epoch::MAX if not deactivated + deactivation_epoch: Epoch, + /// how much stake we can activate per-epoch as a fraction of currently effective stake + warmup_cooldown_rate: f64, +} + +#[wasm_bindgen] +impl Delegation { + #[wasm_bindgen(getter = voterPubkey)] + pub fn voter_pubkey(&self) -> Pubkey { + self.voter_pubkey + } + + #[wasm_bindgen(getter)] + pub fn stake(&self) -> f64 { + self.stake as f64 + } + + #[wasm_bindgen(js_name = isBootstrapStake)] + pub fn is_bootstrap_stake(&self) -> bool { + self.activation_epoch == Epoch::MAX + } + + #[wasm_bindgen(js_name = isDeactivated)] + pub fn is_deactivated(&self) -> bool { + self.deactivation_epoch != Epoch::MAX + } + + #[wasm_bindgen(getter = activationEpoch)] + pub fn activation_epoch(&self) -> f64 { + self.activation_epoch as f64 + } + + #[wasm_bindgen(getter = deactivationEpoch)] + pub fn deactivation_epoch(&self) -> f64 { + self.deactivation_epoch as f64 + } + + #[wasm_bindgen(getter = warmupCooldownRate)] + pub fn warmup_cooldown_rate(&self) -> f64 { + self.warmup_cooldown_rate + } +}