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 0000000000..588775629c Binary files /dev/null and b/explorer/wasm/pkg/solana_sdk_wasm_bg.wasm differ diff --git a/explorer/wasm/src/lib.rs b/explorer/wasm/src/lib.rs new file mode 100644 index 0000000000..9363333a88 --- /dev/null +++ b/explorer/wasm/src/lib.rs @@ -0,0 +1,3 @@ +mod stake_account; + +pub use stake_account::*; diff --git a/explorer/wasm/src/stake_account.rs b/explorer/wasm/src/stake_account.rs new file mode 100644 index 0000000000..93a0509643 --- /dev/null +++ b/explorer/wasm/src/stake_account.rs @@ -0,0 +1,217 @@ +use serde::{Deserialize, Serialize}; +use wasm_bindgen::prelude::*; + +#[derive(Serialize, Deserialize, PartialEq, Clone, Copy)] +#[allow(clippy::large_enum_variant)] +pub enum StakeState { + Uninitialized, + Initialized(Meta), + Stake(Meta, Stake), + RewardsPool, +} + +#[wasm_bindgen(js_name = StakeState)] +#[derive(Copy, Clone)] +pub enum State { + Uninitialized, + Initialized, + Delegated, + RewardsPool, +} + +#[wasm_bindgen] +pub struct StakeAccount { + pub meta: Option, + 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 + } +}