Use wasm to decode stake account

This commit is contained in:
Justin Starry 2020-05-14 15:30:33 +08:00 committed by Michael Vines
parent 29ec98d0a1
commit 26e4767fdd
43 changed files with 1522 additions and 129 deletions

View File

@ -1 +1,2 @@
build
wasm

View File

@ -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;
};

View File

@ -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",

View File

@ -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)\""
},

View File

@ -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) {
</div>
</div>
{pubkey && <InfoCard pubkey={pubkey} />}
{pubkey && <DetailsCard pubkey={pubkey} />}
{pubkey && <HistoryCard pubkey={pubkey} />}
</div>
);
}
type Wasm = {
StakeAccount: typeof StakeAccount;
};
function DetailsCard({ pubkey }: { pubkey: PublicKey }) {
const address = pubkey.toBase58();
const info = useAccountInfo(address);
const [Wasm, setWasm] = React.useState<Wasm | undefined>(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 <LoadingCard />;
} else {
const stakeAccount = Wasm.StakeAccount.fromAccountData(data);
return <StakeAccountDetailsCard account={stakeAccount} />;
}
}
} catch (err) {
console.error(err);
return <ErrorCard text="Failed to decode account data" />;
}
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 <RetryCard retry={() => refresh(pubkey)} text="Fetch Failed" />;
return <ErrorCard retry={() => refresh(pubkey)} text="Fetch Failed" />;
}
const { details, lamports } = info;
@ -138,7 +184,7 @@ function InfoCard({ pubkey }: { pubkey: PublicKey }) {
<td>Owner</td>
<td className="text-right">
<Copyable text={details.owner.toBase58()}>
<code>{displayAddress(details.owner)}</code>
<code>{displayAddress(details.owner.toBase58())}</code>
</Copyable>
</td>
</tr>
@ -166,7 +212,7 @@ function HistoryCard({ pubkey }: { pubkey: PublicKey }) {
return <LoadingCard />;
} else if (info.history === undefined) {
return (
<RetryCard
<ErrorCard
retry={() => refresh(pubkey)}
text="Failed to fetch transaction history"
/>
@ -175,9 +221,9 @@ function HistoryCard({ pubkey }: { pubkey: PublicKey }) {
if (info.history.size === 0) {
return (
<RetryCard
<ErrorCard
retry={() => refresh(pubkey)}
text="No transaction history found"
text="No recent transaction history found"
/>
);
}
@ -219,43 +265,3 @@ function HistoryCard({ pubkey }: { pubkey: PublicKey }) {
</div>
);
}
function LoadingCard() {
return (
<div className="card">
<div className="card-body text-center">
<span className="spinner-grow spinner-grow-sm mr-2"></span>
Loading
</div>
</div>
);
}
function RetryCard({ retry, text }: { retry: () => void; text: string }) {
return (
<div className="card">
<div className="card-body text-center">
{text}
<span className="btn btn-white ml-3 d-none d-md-inline" onClick={retry}>
Try Again
</span>
<div className="d-block d-md-none mt-4">
<hr></hr>
<span className="btn btn-white" onClick={retry}>
Try Again
</span>
</div>
</div>
</div>
);
}
function TableCardBody({ children }: { children: React.ReactNode }) {
return (
<div className="table-responsive mb-0">
<table className="table table-sm table-nowrap card-table">
<tbody className="list">{children}</tbody>
</table>
</div>
);
}

View File

@ -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 = "-";

View File

@ -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 <LoadingCard />;
} else if (status?.fetchStatus === FetchStatus.FetchFailed) {
return <RetryCard retry={() => refresh(signature)} text="Fetch Failed" />;
return <ErrorCard retry={() => refresh(signature)} text="Fetch Failed" />;
} else if (!status.info) {
return <RetryCard retry={() => refresh(signature)} text="Not Found" />;
return <ErrorCard retry={() => refresh(signature)} text="Not Found" />;
}
const { info } = status;
@ -191,7 +194,7 @@ function AccountsCard({ signature }: Props) {
return null;
} else if (!details) {
return (
<RetryCard
<ErrorCard
retry={refreshStatus}
text="Details are not available until the transaction reaches MAX confirmations"
/>
@ -199,14 +202,14 @@ function AccountsCard({ signature }: Props) {
} else if (details.fetchStatus === FetchStatus.Fetching) {
return <LoadingCard />;
} else if (details?.fetchStatus === FetchStatus.FetchFailed) {
return <RetryCard retry={refreshDetails} text="Fetch Failed" />;
return <ErrorCard retry={refreshDetails} text="Fetch Failed" />;
} else if (!details.transaction || !message) {
return <RetryCard retry={refreshDetails} text="Not Found" />;
return <ErrorCard retry={refreshDetails} text="Not Found" />;
}
const { meta } = details.transaction;
if (!meta) {
return <RetryCard retry={refreshDetails} text="Metadata Missing" />;
return <ErrorCard retry={refreshDetails} text="Metadata Missing" />;
}
const accountRows = message.accountKeys.map((pubkey, index) => {
@ -228,7 +231,7 @@ function AccountsCard({ signature }: Props) {
<tr key={key}>
<td>
<Copyable text={key}>
<code>{displayAddress(pubkey)}</code>
<code>{displayAddress(pubkey.toBase58())}</code>
</Copyable>
</td>
<td>{renderChange()}</td>
@ -284,7 +287,7 @@ function InstructionsSection({ signature }: Props) {
const { transaction } = details.transaction;
if (transaction.instructions.length === 0) {
return <RetryCard retry={refreshDetails} text="No instructions found" />;
return <ErrorCard retry={refreshDetails} text="No instructions found" />;
}
const result = status.info.result;
@ -313,43 +316,3 @@ function InstructionsSection({ signature }: Props) {
</>
);
}
function LoadingCard() {
return (
<div className="card">
<div className="card-body text-center">
<span className="spinner-grow spinner-grow-sm mr-2"></span>
Loading
</div>
</div>
);
}
function RetryCard({ retry, text }: { retry: () => void; text: string }) {
return (
<div className="card">
<div className="card-body text-center">
{text}
<span className="btn btn-white ml-3 d-none d-md-inline" onClick={retry}>
Try Again
</span>
<div className="d-block d-md-none mt-4">
<hr></hr>
<span className="btn btn-white" onClick={retry}>
Try Again
</span>
</div>
</div>
</div>
);
}
function TableCardBody({ children }: { children: React.ReactNode }) {
return (
<div className="table-responsive mb-0">
<table className="table table-sm table-nowrap card-table">
<tbody className="list">{children}</tbody>
</table>
</div>
);
}

View File

@ -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 (
<div className="card">
<div className="card-header">
<h3 className="card-header-title mb-0 d-flex align-items-center">
Stake Account
</h3>
</div>
<TableCardBody>
<tr>
<td>State</td>
<td className="text-right">{account.displayState()}</td>
</tr>
{meta && (
<>
<tr>
<td>Rent Reserve (SOL)</td>
<td className="text-right">
{lamportsToSolString(meta.rentExemptReserve)}
</td>
</tr>
<tr>
<td>Authorized Staker Address</td>
<td className="text-right">
<Copyable text={meta.authorized.staker.toBase58()}>
<code>{meta.authorized.staker.toBase58()}</code>
</Copyable>
</td>
</tr>
<tr>
<td>Authorized Withdrawer Address</td>
<td className="text-right">
<Copyable text={meta.authorized.withdrawer.toBase58()}>
<code>{meta.authorized.withdrawer.toBase58()}</code>
</Copyable>
</td>
</tr>
<tr>
<td>Lockup Expiry Epoch</td>
<td className="text-right">{meta.lockup.epoch}</td>
</tr>
<tr>
<td>Lockup Expiry Timestamp</td>
<td className="text-right">
{new Date(meta.lockup.unixTimestamp).toUTCString()}
</td>
</tr>
<tr>
<td>Lockup Custodian Address</td>
<td className="text-right">
<Copyable text={meta.lockup.custodian.toBase58()}>
<code>
{displayAddress(meta.lockup.custodian.toBase58())}
</code>
</Copyable>
</td>
</tr>
</>
)}
{stake && (
<>
<tr>
<td>Delegated Stake (SOL)</td>
<td className="text-right">
{lamportsToSolString(stake.delegation.stake)}
</td>
</tr>
<tr>
<td>Delegated Vote Address</td>
<td className="text-right">
<Copyable text={stake.delegation.voterPubkey.toBase58()}>
<code>
{displayAddress(stake.delegation.voterPubkey.toBase58())}
</code>
</Copyable>
</td>
</tr>
<tr>
<td>Activation Epoch</td>
<td className="text-right">
{stake.delegation.isBootstrapStake()
? "-"
: stake.delegation.activationEpoch}
</td>
</tr>
<tr>
<td>Deactivation Epoch</td>
<td className="text-right">
{stake.delegation.isDeactivated()
? stake.delegation.deactivationEpoch
: "-"}
</td>
</tr>
</>
)}
</TableCardBody>
</div>
);
}

View File

@ -0,0 +1,33 @@
import React from "react";
export default function ErrorCard({
retry,
text
}: {
retry?: () => void;
text: string;
}) {
return (
<div className="card">
<div className="card-body text-center">
{text}
{retry && (
<>
<span
className="btn btn-white ml-3 d-none d-md-inline"
onClick={retry}
>
Try Again
</span>
<div className="d-block d-md-none mt-4">
<hr></hr>
<span className="btn btn-white" onClick={retry}>
Try Again
</span>
</div>
</>
)}
</div>
</div>
);
}

View File

@ -0,0 +1,12 @@
import React from "react";
export default function LoadingCard() {
return (
<div className="card">
<div className="card-body text-center">
<span className="spinner-grow spinner-grow-sm mr-2"></span>
Loading
</div>
</div>
);
}

View File

@ -0,0 +1,15 @@
import React from "react";
export default function TableCardBody({
children
}: {
children: React.ReactNode;
}) {
return (
<div className="table-responsive mb-0">
<table className="table table-sm table-nowrap card-table">
<tbody className="list">{children}</tbody>
</table>
</div>
);
}

View File

@ -19,7 +19,7 @@ export function RawDetails({ ix }: { ix: TransactionInstruction }) {
<td>Program</td>
<td className="text-right">
<Copyable bottom text={ix.programId.toBase58()}>
<code>{displayAddress(ix.programId)}</code>
<code>{displayAddress(ix.programId.toBase58())}</code>
</Copyable>
</td>
</tr>

View File

@ -53,7 +53,7 @@ export function AuthorizeDetailsCard(props: {
<td>Program</td>
<td className="text-right">
<Copyable bottom text={StakeProgram.programId.toBase58()}>
<code>{displayAddress(StakeProgram.programId)}</code>
<code>{displayAddress(StakeProgram.programId.toBase58())}</code>
</Copyable>
</td>
</tr>

View File

@ -39,7 +39,7 @@ export function DeactivateDetailsCard(props: {
<td>Program</td>
<td className="text-right">
<Copyable bottom text={StakeProgram.programId.toBase58()}>
<code>{displayAddress(StakeProgram.programId)}</code>
<code>{displayAddress(StakeProgram.programId.toBase58())}</code>
</Copyable>
</td>
</tr>

View File

@ -40,7 +40,7 @@ export function DelegateDetailsCard(props: {
<td>Program</td>
<td className="text-right">
<Copyable bottom text={StakeProgram.programId.toBase58()}>
<code>{displayAddress(StakeProgram.programId)}</code>
<code>{displayAddress(StakeProgram.programId.toBase58())}</code>
</Copyable>
</td>
</tr>

View File

@ -40,7 +40,7 @@ export function InitializeDetailsCard(props: {
<td>Program</td>
<td className="text-right">
<Copyable bottom text={StakeProgram.programId.toBase58()}>
<code>{displayAddress(StakeProgram.programId)}</code>
<code>{displayAddress(StakeProgram.programId.toBase58())}</code>
</Copyable>
</td>
</tr>
@ -88,7 +88,7 @@ export function InitializeDetailsCard(props: {
<td>Lockup Custodian Address</td>
<td className="text-right">
<Copyable text={params.lockup.custodian.toBase58()}>
<code>{displayAddress(params.lockup.custodian)}</code>
<code>{displayAddress(params.lockup.custodian.toBase58())}</code>
</Copyable>
</td>
</tr>

View File

@ -36,7 +36,7 @@ export function SplitDetailsCard(props: {
<td>Program</td>
<td className="text-right">
<Copyable bottom text={StakeProgram.programId.toBase58()}>
<code>{displayAddress(StakeProgram.programId)}</code>
<code>{displayAddress(StakeProgram.programId.toBase58())}</code>
</Copyable>
</td>
</tr>

View File

@ -41,7 +41,7 @@ export function WithdrawDetailsCard(props: {
<td>Program</td>
<td className="text-right">
<Copyable bottom text={StakeProgram.programId.toBase58()}>
<code>{displayAddress(StakeProgram.programId)}</code>
<code>{displayAddress(StakeProgram.programId.toBase58())}</code>
</Copyable>
</td>
</tr>

View File

@ -38,7 +38,7 @@ export function AllocateDetailsCard(props: {
<td>Program</td>
<td className="text-right">
<Copyable bottom text={SystemProgram.programId.toBase58()}>
<code>{displayAddress(SystemProgram.programId)}</code>
<code>{displayAddress(SystemProgram.programId.toBase58())}</code>
</Copyable>
</td>
</tr>

View File

@ -39,7 +39,7 @@ export function AllocateWithSeedDetailsCard(props: {
<td>Program</td>
<td className="text-right">
<Copyable bottom text={SystemProgram.programId.toBase58()}>
<code>{displayAddress(SystemProgram.programId)}</code>
<code>{displayAddress(SystemProgram.programId.toBase58())}</code>
</Copyable>
</td>
</tr>
@ -80,7 +80,7 @@ export function AllocateWithSeedDetailsCard(props: {
<td>Assigned Owner</td>
<td className="text-right">
<Copyable text={params.programId.toBase58()}>
<code>{displayAddress(params.programId)}</code>
<code>{displayAddress(params.programId.toBase58())}</code>
</Copyable>
</td>
</tr>

View File

@ -38,7 +38,7 @@ export function AssignDetailsCard(props: {
<td>Program</td>
<td className="text-right">
<Copyable bottom text={SystemProgram.programId.toBase58()}>
<code>{displayAddress(SystemProgram.programId)}</code>
<code>{displayAddress(SystemProgram.programId.toBase58())}</code>
</Copyable>
</td>
</tr>
@ -56,7 +56,7 @@ export function AssignDetailsCard(props: {
<td>Assigned Owner</td>
<td className="text-right">
<Copyable text={params.programId.toBase58()}>
<code>{displayAddress(params.programId)}</code>
<code>{displayAddress(params.programId.toBase58())}</code>
</Copyable>
</td>
</tr>

View File

@ -39,7 +39,7 @@ export function AssignWithSeedDetailsCard(props: {
<td>Program</td>
<td className="text-right">
<Copyable bottom text={SystemProgram.programId.toBase58()}>
<code>{displayAddress(SystemProgram.programId)}</code>
<code>{displayAddress(SystemProgram.programId.toBase58())}</code>
</Copyable>
</td>
</tr>
@ -75,7 +75,7 @@ export function AssignWithSeedDetailsCard(props: {
<td>Assigned Owner</td>
<td className="text-right">
<Copyable text={params.programId.toBase58()}>
<code>{displayAddress(params.programId)}</code>
<code>{displayAddress(params.programId.toBase58())}</code>
</Copyable>
</td>
</tr>

View File

@ -40,7 +40,7 @@ export function CreateDetailsCard(props: {
<td>Program</td>
<td className="text-right">
<Copyable bottom text={SystemProgram.programId.toBase58()}>
<code>{displayAddress(SystemProgram.programId)}</code>
<code>{displayAddress(SystemProgram.programId.toBase58())}</code>
</Copyable>
</td>
</tr>
@ -77,7 +77,7 @@ export function CreateDetailsCard(props: {
<td>Assigned Owner</td>
<td className="text-right">
<Copyable text={params.programId.toBase58()}>
<code>{displayAddress(params.programId)}</code>
<code>{displayAddress(params.programId.toBase58())}</code>
</Copyable>
</td>
</tr>

View File

@ -41,7 +41,7 @@ export function CreateWithSeedDetailsCard(props: {
<td>Program</td>
<td className="text-right">
<Copyable bottom text={SystemProgram.programId.toBase58()}>
<code>{displayAddress(SystemProgram.programId)}</code>
<code>{displayAddress(SystemProgram.programId.toBase58())}</code>
</Copyable>
</td>
</tr>
@ -105,7 +105,7 @@ export function CreateWithSeedDetailsCard(props: {
<td>Assigned Owner</td>
<td className="text-right">
<Copyable text={params.programId.toBase58()}>
<code>{displayAddress(params.programId)}</code>
<code>{displayAddress(params.programId.toBase58())}</code>
</Copyable>
</td>
</tr>

View File

@ -39,7 +39,7 @@ export function NonceAdvanceDetailsCard(props: {
<td>Program</td>
<td className="text-right">
<Copyable bottom text={SystemProgram.programId.toBase58()}>
<code>{displayAddress(SystemProgram.programId)}</code>
<code>{displayAddress(SystemProgram.programId.toBase58())}</code>
</Copyable>
</td>
</tr>

View File

@ -40,7 +40,7 @@ export function NonceAuthorizeDetailsCard(props: {
<td>Program</td>
<td className="text-right">
<Copyable bottom text={SystemProgram.programId.toBase58()}>
<code>{displayAddress(SystemProgram.programId)}</code>
<code>{displayAddress(SystemProgram.programId.toBase58())}</code>
</Copyable>
</td>
</tr>

View File

@ -39,7 +39,7 @@ export function NonceInitializeDetailsCard(props: {
<td>Program</td>
<td className="text-right">
<Copyable bottom text={SystemProgram.programId.toBase58()}>
<code>{displayAddress(SystemProgram.programId)}</code>
<code>{displayAddress(SystemProgram.programId.toBase58())}</code>
</Copyable>
</td>
</tr>

View File

@ -42,7 +42,7 @@ export function NonceWithdrawDetailsCard(props: {
<td>Program</td>
<td className="text-right">
<Copyable bottom text={SystemProgram.programId.toBase58()}>
<code>{displayAddress(SystemProgram.programId)}</code>
<code>{displayAddress(SystemProgram.programId.toBase58())}</code>
</Copyable>
</td>
</tr>

View File

@ -34,7 +34,7 @@ export function TransferDetailsCard(props: {
<td>Program</td>
<td className="text-right">
<Copyable bottom text={SystemProgram.programId.toBase58()}>
<code>{displayAddress(SystemProgram.programId)}</code>
<code>{displayAddress(SystemProgram.programId.toBase58())}</code>
</Copyable>
</td>
</tr>

View File

@ -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);

View File

@ -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] ||

16
explorer/wasm/.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
/target/
**/*.rs.bk
.cargo
/config/
# log files
*.log
log-*.txt
log-*/
# intellij files
/.idea/
/solana.iml
/.vscode/

179
explorer/wasm/Cargo.lock generated Normal file
View File

@ -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"

27
explorer/wasm/Cargo.toml Normal file
View File

@ -0,0 +1,27 @@
[package]
name = "solana-sdk-wasm"
version = "1.2.0"
description = "Solana SDK Wasm"
authors = ["Solana Maintainers <maintainers@solana.com>"]
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"

15
explorer/wasm/README.md Normal file
View File

@ -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

View File

@ -0,0 +1,22 @@
{
"name": "solana-sdk-wasm",
"collaborators": [
"Solana Maintainers <maintainers@solana.com>"
],
"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
}

140
explorer/wasm/pkg/solana_sdk_wasm.d.ts vendored Normal file
View File

@ -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;
}

View File

@ -0,0 +1,2 @@
import * as wasm from "./solana_sdk_wasm_bg.wasm";
export * from "./solana_sdk_wasm_bg.js";

View File

@ -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;

View File

@ -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);
};

Binary file not shown.

3
explorer/wasm/src/lib.rs Normal file
View File

@ -0,0 +1,3 @@
mod stake_account;
pub use stake_account::*;

View File

@ -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<Meta>,
pub stake: Option<Stake>,
pub state: State,
}
impl From<StakeState> 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<StakeAccount, JsValue> {
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
}
}