diff --git a/explorer/.travis.yml b/explorer/.travis.yml
index 2b66949476..71f18f5a84 100644
--- a/explorer/.travis.yml
+++ b/explorer/.travis.yml
@@ -6,4 +6,5 @@ branches:
- master
script:
- npm run build
+ - npm run test
- npm run format
diff --git a/explorer/package-lock.json b/explorer/package-lock.json
index 37dc2586de..422a89c2bd 100644
--- a/explorer/package-lock.json
+++ b/explorer/package-lock.json
@@ -2843,6 +2843,14 @@
"@babel/types": "^7.3.0"
}
},
+ "@types/bn.js": {
+ "version": "4.11.6",
+ "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz",
+ "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==",
+ "requires": {
+ "@types/node": "*"
+ }
+ },
"@types/bs58": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/bs58/-/bs58-4.0.1.tgz",
@@ -2851,6 +2859,11 @@
"base-x": "^3.0.6"
}
},
+ "@types/chai": {
+ "version": "4.2.12",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.12.tgz",
+ "integrity": "sha512-aN5IAC8QNtSUdQzxu7lGBgYAOuU1tmRU4c9dIq5OKGf/SBVjXo+ffM2wEjudAWbgpOhy60nLoAGH1xm8fpCKFQ=="
+ },
"@types/color-name": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
@@ -3791,6 +3804,11 @@
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
},
+ "assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw=="
+ },
"assign-symbols": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
@@ -5087,6 +5105,34 @@
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
},
+ "chai": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
+ "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
+ "requires": {
+ "assertion-error": "^1.1.0",
+ "check-error": "^1.0.2",
+ "deep-eql": "^3.0.1",
+ "get-func-name": "^2.0.0",
+ "pathval": "^1.1.0",
+ "type-detect": "^4.0.5"
+ },
+ "dependencies": {
+ "deep-eql": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
+ "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
+ "requires": {
+ "type-detect": "^4.0.0"
+ }
+ },
+ "type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="
+ }
+ }
+ },
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -5102,6 +5148,11 @@
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
},
+ "check-error": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII="
+ },
"cheerio": {
"version": "0.22.0",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz",
@@ -7882,6 +7933,11 @@
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
"integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w=="
},
+ "get-func-name": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
+ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE="
+ },
"get-own-enumerable-property-symbols": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz",
@@ -11737,6 +11793,11 @@
"pinkie-promise": "^2.0.0"
}
},
+ "pathval": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
+ "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA="
+ },
"pbkdf2": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz",
diff --git a/explorer/package.json b/explorer/package.json
index d7f94c77e3..28978df12f 100644
--- a/explorer/package.json
+++ b/explorer/package.json
@@ -8,7 +8,9 @@
"@testing-library/jest-dom": "^5.11.2",
"@testing-library/react": "^10.4.8",
"@testing-library/user-event": "^12.1.0",
+ "@types/bn.js": "^4.11.6",
"@types/bs58": "^4.0.1",
+ "@types/chai": "^4.2.12",
"@types/jest": "^26.0.9",
"@types/node": "^14.0.27",
"@types/react": "^16.9.44",
@@ -16,8 +18,10 @@
"@types/react-router-dom": "^5.1.5",
"@types/react-select": "^3.0.16",
"@types/socket.io-client": "^1.4.33",
+ "bn.js": "^5.1.2",
"bootstrap": "^4.5.1",
"bs58": "^4.0.1",
+ "chai": "^4.2.0",
"humanize-duration-ts": "^2.1.1",
"node-sass": "^4.14.1",
"prettier": "^2.0.5",
diff --git a/explorer/src/__tests__/lamportsToSol.ts b/explorer/src/__tests__/lamportsToSol.ts
new file mode 100644
index 0000000000..fdca7f63e9
--- /dev/null
+++ b/explorer/src/__tests__/lamportsToSol.ts
@@ -0,0 +1,28 @@
+import { expect } from "chai";
+import { lamportsToSol, LAMPORTS_PER_SOL } from "utils";
+import BN from "bn.js";
+
+describe("lamportsToSol", () => {
+ it("0 lamports", () => {
+ expect(lamportsToSol(new BN(0))).to.eq(0.0);
+ });
+
+ it("1 lamport", () => {
+ expect(lamportsToSol(new BN(1))).to.eq(0.000000001);
+ expect(lamportsToSol(new BN(-1))).to.eq(-0.000000001);
+ });
+
+ it("1 SOL", () => {
+ expect(lamportsToSol(new BN(LAMPORTS_PER_SOL))).to.eq(1.0);
+ expect(lamportsToSol(new BN(-LAMPORTS_PER_SOL))).to.eq(-1.0);
+ });
+
+ it("u64::MAX lamports", () => {
+ expect(lamportsToSol(new BN(2).pow(new BN(64)))).to.eq(
+ 18446744073.709551615
+ );
+ expect(lamportsToSol(new BN(2).pow(new BN(64)).neg())).to.eq(
+ -18446744073.709551615
+ );
+ });
+});
diff --git a/explorer/src/components/account/StakeAccountSection.tsx b/explorer/src/components/account/StakeAccountSection.tsx
index dada3735f1..daf89f6b30 100644
--- a/explorer/src/components/account/StakeAccountSection.tsx
+++ b/explorer/src/components/account/StakeAccountSection.tsx
@@ -1,29 +1,54 @@
import React from "react";
-import { StakeAccount, Meta } from "solana-sdk-wasm";
+import { StakeAccount as StakeAccountWasm, Meta } from "solana-sdk-wasm";
import { TableCardBody } from "components/common/TableCardBody";
import { lamportsToSolString } from "utils";
import { displayTimestamp } from "utils/date";
import { Account, useFetchAccountInfo } from "providers/accounts";
import { Address } from "components/common/Address";
+import {
+ StakeAccountInfo,
+ StakeMeta,
+ StakeAccountType,
+} from "providers/accounts/types";
+import BN from "bn.js";
+
+const MAX_EPOCH = new BN(2).pow(new BN(64));
export function StakeAccountSection({
account,
stakeAccount,
+ stakeAccountType,
}: {
account: Account;
- stakeAccount: StakeAccount;
+ stakeAccount: StakeAccountInfo | StakeAccountWasm;
+ stakeAccountType: StakeAccountType;
}) {
return (
<>
-
- {stakeAccount.meta && }
- {stakeAccount.meta && }
+
+ {stakeAccount.meta && (
+ <>
+
+
+ >
+ )}
>
);
}
-function LockupCard({ stakeAccount }: { stakeAccount: StakeAccount }) {
+function LockupCard({
+ stakeAccount,
+}: {
+ stakeAccount: StakeAccountInfo | StakeAccountWasm;
+}) {
const unixTimestamp = stakeAccount.meta?.lockup.unixTimestamp;
if (unixTimestamp && unixTimestamp > 0) {
const prettyTimestamp = displayTimestamp(unixTimestamp * 1000);
@@ -37,12 +62,21 @@ function LockupCard({ stakeAccount }: { stakeAccount: StakeAccount }) {
}
}
+const TYPE_NAMES = {
+ uninitialized: "Uninitialized",
+ initialized: "Initialized",
+ delegated: "Delegated",
+ rewardsPool: "RewardsPool",
+};
+
function OverviewCard({
account,
stakeAccount,
+ stakeAccountType,
}: {
account: Account;
- stakeAccount: StakeAccount;
+ stakeAccount: StakeAccountInfo | StakeAccountWasm;
+ stakeAccountType: StakeAccountType;
}) {
const refresh = useFetchAccountInfo();
return (
@@ -84,7 +118,7 @@ function OverviewCard({
{!stakeAccount.meta && (
State |
- {stakeAccount.displayState()} |
+ {TYPE_NAMES[stakeAccountType]} |
)}
@@ -92,16 +126,48 @@ function OverviewCard({
);
}
-function DelegationCard({ stakeAccount }: { stakeAccount: StakeAccount }) {
- const { stake } = stakeAccount;
+function DelegationCard({
+ stakeAccount,
+ stakeAccountType,
+}: {
+ stakeAccount: StakeAccountInfo | StakeAccountWasm;
+ stakeAccountType: StakeAccountType;
+}) {
const displayStatus = () => {
- let status = stakeAccount.displayState();
- if (status !== "Delegated") {
+ // TODO check epoch
+ let status = TYPE_NAMES[stakeAccountType];
+ if (stakeAccountType !== "delegated") {
status = "Not delegated";
}
return status;
};
+ let voterPubkey, activationEpoch, deactivationEpoch;
+ if ("accountType" in stakeAccount) {
+ const delegation = stakeAccount?.stake?.delegation;
+ if (delegation) {
+ voterPubkey = delegation.voterPubkey;
+ activationEpoch = delegation.isBootstrapStake()
+ ? "-"
+ : delegation.activationEpoch;
+ deactivationEpoch = delegation.isDeactivated()
+ ? delegation.deactivationEpoch
+ : "-";
+ }
+ } else {
+ const delegation = stakeAccount?.stake?.delegation;
+ if (delegation) {
+ voterPubkey = delegation.voter;
+ activationEpoch = delegation.activationEpoch.eq(MAX_EPOCH)
+ ? "-"
+ : delegation.activationEpoch.toString();
+ deactivationEpoch = delegation.deactivationEpoch.eq(MAX_EPOCH)
+ ? "-"
+ : delegation.deactivationEpoch.toString();
+ }
+ }
+
+ const { stake } = stakeAccount;
return (
@@ -124,33 +190,23 @@ function DelegationCard({ stakeAccount }: { stakeAccount: StakeAccount }) {
-
- Delegated Vote Address |
-
-
- |
-
+ {voterPubkey && (
+
+ Delegated Vote Address |
+
+
+ |
+
+ )}
Activation Epoch |
-
- {stake.delegation.isBootstrapStake()
- ? "-"
- : stake.delegation.activationEpoch}
- |
+ {activationEpoch} |
Deactivation Epoch |
-
- {stake.delegation.isDeactivated()
- ? stake.delegation.deactivationEpoch
- : "-"}
- |
+ {deactivationEpoch} |
>
)}
@@ -159,7 +215,7 @@ function DelegationCard({ stakeAccount }: { stakeAccount: StakeAccount }) {
);
}
-function AuthoritiesCard({ meta }: { meta: Meta }) {
+function AuthoritiesCard({ meta }: { meta: Meta | StakeMeta }) {
const hasLockup = meta && meta.lockup.unixTimestamp > 0;
return (
diff --git a/explorer/src/components/account/UnknownAccountCard.tsx b/explorer/src/components/account/UnknownAccountCard.tsx
index e0ff9f3ce8..115f3cd143 100644
--- a/explorer/src/components/account/UnknownAccountCard.tsx
+++ b/explorer/src/components/account/UnknownAccountCard.tsx
@@ -28,7 +28,7 @@ export function UnknownAccountCard({ account }: { account: Account }) {
- {details && (
+ {details?.space !== undefined && (
Data (Bytes) |
{details.space} |
diff --git a/explorer/src/components/instruction/token/TokenDetailsCard.tsx b/explorer/src/components/instruction/token/TokenDetailsCard.tsx
index 385401c1c4..5e20853b31 100644
--- a/explorer/src/components/instruction/token/TokenDetailsCard.tsx
+++ b/explorer/src/components/instruction/token/TokenDetailsCard.tsx
@@ -10,7 +10,8 @@ import {
import { UnknownDetailsCard } from "../UnknownDetailsCard";
import { InstructionCard } from "../InstructionCard";
import { Address } from "components/common/Address";
-import { ParsedInstructionInfo, IX_STRUCTS } from "./types";
+import { IX_STRUCTS, TokenInstructionType } from "./types";
+import { ParsedInfo } from "validators";
const IX_TITLES = {
initializeMint: "Initialize Mint",
@@ -34,8 +35,9 @@ type DetailsProps = {
export function TokenDetailsCard(props: DetailsProps) {
try {
- const parsed = coerce(props.ix.parsed, ParsedInstructionInfo);
- const { type, info } = parsed;
+ const parsed = coerce(props.ix.parsed, ParsedInfo);
+ const { type: rawType, info } = parsed;
+ const type = coerce(rawType, TokenInstructionType);
const title = `Token: ${IX_TITLES[type]}`;
const coerced = coerce(info, IX_STRUCTS[type] as any);
return ;
diff --git a/explorer/src/components/instruction/token/types.ts b/explorer/src/components/instruction/token/types.ts
index a16939cf60..b7bcb47311 100644
--- a/explorer/src/components/instruction/token/types.ts
+++ b/explorer/src/components/instruction/token/types.ts
@@ -1,21 +1,12 @@
import {
enums,
object,
- any,
StructType,
- coercion,
- struct,
number,
optional,
array,
} from "superstruct";
-import { PublicKey } from "@solana/web3.js";
-
-const PubkeyValue = struct("Pubkey", (value) => value instanceof PublicKey);
-const Pubkey = coercion(PubkeyValue, (value) => {
- if (typeof value === "string") return new PublicKey(value);
- throw new Error("invalid pubkey");
-});
+import { Pubkey } from "validators/pubkey";
const InitializeMint = object({
mint: Pubkey,
@@ -95,8 +86,8 @@ const CloseAccount = object({
signers: optional(array(Pubkey)),
});
-type TokenInstructionType = StructType;
-const TokenInstructionType = enums([
+export type TokenInstructionType = StructType;
+export const TokenInstructionType = enums([
"initializeMint",
"initializeAccount",
"initializeMultisig",
@@ -121,9 +112,3 @@ export const IX_STRUCTS = {
burn: Burn,
closeAccount: CloseAccount,
};
-
-export type ParsedInstructionInfo = StructType;
-export const ParsedInstructionInfo = object({
- type: TokenInstructionType,
- info: any(),
-});
diff --git a/explorer/src/pages/AccountDetailsPage.tsx b/explorer/src/pages/AccountDetailsPage.tsx
index 3b14fbe348..2627ccaf59 100644
--- a/explorer/src/pages/AccountDetailsPage.tsx
+++ b/explorer/src/pages/AccountDetailsPage.tsx
@@ -68,7 +68,22 @@ function InfoSection({ pubkey }: { pubkey: PublicKey }) {
const owner = info.details?.owner;
const data = info.details?.data;
if (data && owner && owner.equals(StakeProgram.programId)) {
- return ;
+ let stakeAccountType, stakeAccount;
+ if ("accountType" in data) {
+ stakeAccount = data;
+ stakeAccountType = data.accountType as any;
+ } else {
+ stakeAccount = data.info;
+ stakeAccountType = data.type;
+ }
+
+ return (
+
+ );
} else {
return ;
}
diff --git a/explorer/src/providers/accounts/index.tsx b/explorer/src/providers/accounts/index.tsx
index 45a35f0f41..1b58fa897b 100644
--- a/explorer/src/providers/accounts/index.tsx
+++ b/explorer/src/providers/accounts/index.tsx
@@ -1,9 +1,12 @@
import React from "react";
-import { StakeAccount } from "solana-sdk-wasm";
+import { StakeAccount as StakeAccountWasm } from "solana-sdk-wasm";
import { PublicKey, Connection, StakeProgram } from "@solana/web3.js";
import { useCluster } from "../cluster";
import { HistoryProvider } from "./history";
import { TokensProvider } from "./tokens";
+import { coerce } from "superstruct";
+import { ParsedInfo } from "validators";
+import { StakeAccount } from "./types";
export { useAccountHistory } from "./history";
export enum FetchStatus {
@@ -15,8 +18,8 @@ export enum FetchStatus {
export interface Details {
executable: boolean;
owner: PublicKey;
- space: number;
- data?: StakeAccount;
+ space?: number;
+ data?: StakeAccount | StakeAccountWasm;
}
export interface Account {
@@ -156,18 +159,30 @@ async function fetchAccountInfo(
let details;
let lamports;
try {
- const result = await new Connection(url, "recent").getAccountInfo(pubkey);
+ const result = (
+ await new Connection(url, "single").getParsedAccountInfo(pubkey)
+ ).value;
if (result === null) {
lamports = 0;
} else {
lamports = result.lamports;
- let data = undefined;
// Only save data in memory if we can decode it
+ let space;
+ if (!("parsed" in result.data)) {
+ space = result.data.length;
+ }
+
+ let data;
if (result.owner.equals(StakeProgram.programId)) {
try {
- const wasm = await import("solana-sdk-wasm");
- data = wasm.StakeAccount.fromAccountData(result.data);
+ if ("parsed" in result.data) {
+ const info = coerce(result.data.parsed, ParsedInfo);
+ data = coerce(info, StakeAccount);
+ } else {
+ const wasm = await import("solana-sdk-wasm");
+ data = wasm.StakeAccount.fromAccountData(result.data);
+ }
} catch (err) {
console.error("Unexpected error loading wasm", err);
// TODO store error state in Account info
@@ -175,7 +190,7 @@ async function fetchAccountInfo(
}
details = {
- space: result.data.length,
+ space,
executable: result.executable,
owner: result.owner,
data,
diff --git a/explorer/src/providers/accounts/types.ts b/explorer/src/providers/accounts/types.ts
new file mode 100644
index 0000000000..6dae5ab143
--- /dev/null
+++ b/explorer/src/providers/accounts/types.ts
@@ -0,0 +1,48 @@
+import { object, StructType, number, optional, enums } from "superstruct";
+import { Pubkey } from "validators/pubkey";
+import { BigNum } from "validators/bignum";
+
+export type StakeAccountType = StructType;
+export const StakeAccountType = enums([
+ "uninitialized",
+ "initialized",
+ "delegated",
+ "rewardsPool",
+]);
+
+export type StakeMeta = StructType;
+export const StakeMeta = object({
+ rentExemptReserve: BigNum,
+ authorized: object({
+ staker: Pubkey,
+ withdrawer: Pubkey,
+ }),
+ lockup: object({
+ unixTimestamp: number(),
+ epoch: number(),
+ custodian: Pubkey,
+ }),
+});
+
+export type StakeAccountInfo = StructType;
+export const StakeAccountInfo = object({
+ meta: StakeMeta,
+ stake: optional(
+ object({
+ delegation: object({
+ voter: Pubkey,
+ stake: BigNum,
+ activationEpoch: BigNum,
+ deactivationEpoch: BigNum,
+ warmupCooldownRate: number(),
+ }),
+ creditsObserved: number(),
+ })
+ ),
+});
+
+export type StakeAccount = StructType;
+export const StakeAccount = object({
+ type: StakeAccountType,
+ info: StakeAccountInfo,
+});
diff --git a/explorer/src/utils/index.tsx b/explorer/src/utils/index.tsx
index e7fb806bfb..e2997412ac 100644
--- a/explorer/src/utils/index.tsx
+++ b/explorer/src/utils/index.tsx
@@ -1,10 +1,12 @@
-import React from "react";
-import { LAMPORTS_PER_SOL } from "@solana/web3.js";
+import React, { ReactNode } from "react";
+import BN from "bn.js";
import {
HumanizeDuration,
HumanizeDurationLanguage,
} from "humanize-duration-ts";
-import { ReactNode } from "react";
+
+// Switch to web3 constant when web3 updates superstruct
+export const LAMPORTS_PER_SOL = 1000000000;
export const NUM_TICKS_PER_SECOND = 160;
export const DEFAULT_TICKS_PER_SLOT = 64;
@@ -16,11 +18,31 @@ export function assertUnreachable(x: never): never {
throw new Error("Unreachable!");
}
+export function lamportsToSol(lamports: number | BN): number {
+ if (typeof lamports === "number") {
+ return Math.abs(lamports) / LAMPORTS_PER_SOL;
+ }
+
+ let signMultiplier = 1;
+ if (lamports.isNeg()) {
+ signMultiplier = -1;
+ }
+
+ const absLamports = lamports.abs();
+ const lamportsString = absLamports.toString(10).padStart(10, "0");
+ const splitIndex = lamportsString.length - 9;
+ const solString =
+ lamportsString.slice(0, splitIndex) +
+ "." +
+ lamportsString.slice(splitIndex);
+ return signMultiplier * parseFloat(solString);
+}
+
export function lamportsToSolString(
- lamports: number,
+ lamports: number | BN,
maximumFractionDigits: number = 9
): ReactNode {
- const sol = Math.abs(lamports) / LAMPORTS_PER_SOL;
+ const sol = lamportsToSol(lamports);
return (
<>
◎
diff --git a/explorer/src/validators/bignum.ts b/explorer/src/validators/bignum.ts
new file mode 100644
index 0000000000..14d201d041
--- /dev/null
+++ b/explorer/src/validators/bignum.ts
@@ -0,0 +1,8 @@
+import { coercion, struct, Struct } from "superstruct";
+import BN from "bn.js";
+
+const BigNumValue = struct("BigNum", (value) => value instanceof BN);
+export const BigNum: Struct = coercion(BigNumValue, (value) => {
+ if (typeof value === "string") return new BN(value, 10);
+ throw new Error("invalid big num");
+});
diff --git a/explorer/src/validators/index.ts b/explorer/src/validators/index.ts
new file mode 100644
index 0000000000..fc8ecb242a
--- /dev/null
+++ b/explorer/src/validators/index.ts
@@ -0,0 +1,7 @@
+import { object, any, StructType, string } from "superstruct";
+
+export type ParsedInfo = StructType;
+export const ParsedInfo = object({
+ type: string(),
+ info: any(),
+});
diff --git a/explorer/src/validators/pubkey.ts b/explorer/src/validators/pubkey.ts
new file mode 100644
index 0000000000..b4a19fee32
--- /dev/null
+++ b/explorer/src/validators/pubkey.ts
@@ -0,0 +1,8 @@
+import { coercion, struct, Struct } from "superstruct";
+import { PublicKey } from "@solana/web3.js";
+
+const PubkeyValue = struct("Pubkey", (value) => value instanceof PublicKey);
+export const Pubkey: Struct = coercion(PubkeyValue, (value) => {
+ if (typeof value === "string") return new PublicKey(value);
+ throw new Error("invalid pubkey");
+});
diff --git a/explorer/wasm/pkg/package.json b/explorer/wasm/pkg/package.json
index f0ea2ed836..be4838bad3 100644
--- a/explorer/wasm/pkg/package.json
+++ b/explorer/wasm/pkg/package.json
@@ -4,7 +4,7 @@
"Solana Maintainers "
],
"description": "Solana SDK Wasm",
- "version": "1.2.0",
+ "version": "1.4.0",
"license": "Apache-2.0",
"repository": {
"type": "git",
diff --git a/explorer/wasm/pkg/solana_sdk_wasm.d.ts b/explorer/wasm/pkg/solana_sdk_wasm.d.ts
index fc17502628..510281750e 100644
--- a/explorer/wasm/pkg/solana_sdk_wasm.d.ts
+++ b/explorer/wasm/pkg/solana_sdk_wasm.d.ts
@@ -124,7 +124,7 @@ export class StakeAccount {
/**
* @returns {string}
*/
- displayState(): string;
+ accountType(): string;
/**
* @returns {Meta | undefined}
*/
diff --git a/explorer/wasm/pkg/solana_sdk_wasm_bg.d.ts b/explorer/wasm/pkg/solana_sdk_wasm_bg.d.ts
index 32ac915c54..a646f9608a 100644
--- a/explorer/wasm/pkg/solana_sdk_wasm_bg.d.ts
+++ b/explorer/wasm/pkg/solana_sdk_wasm_bg.d.ts
@@ -9,7 +9,7 @@ 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 stakeaccount_accountType(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;
diff --git a/explorer/wasm/pkg/solana_sdk_wasm_bg.js b/explorer/wasm/pkg/solana_sdk_wasm_bg.js
index 2fb23b7893..27ed5fbdbc 100644
--- a/explorer/wasm/pkg/solana_sdk_wasm_bg.js
+++ b/explorer/wasm/pkg/solana_sdk_wasm_bg.js
@@ -456,9 +456,9 @@ export class StakeAccount {
/**
* @returns {string}
*/
- displayState() {
+ accountType() {
try {
- wasm.stakeaccount_displayState(8, this.ptr);
+ wasm.stakeaccount_accountType(8, this.ptr);
var r0 = getInt32Memory0()[8 / 4 + 0];
var r1 = getInt32Memory0()[8 / 4 + 1];
return getStringFromWasm0(r0, r1);
diff --git a/explorer/wasm/pkg/solana_sdk_wasm_bg.wasm b/explorer/wasm/pkg/solana_sdk_wasm_bg.wasm
index 588775629c..7f2e361953 100644
Binary files a/explorer/wasm/pkg/solana_sdk_wasm_bg.wasm and b/explorer/wasm/pkg/solana_sdk_wasm_bg.wasm differ
diff --git a/explorer/wasm/src/stake_account.rs b/explorer/wasm/src/stake_account.rs
index 93a0509643..530e8faa2b 100644
--- a/explorer/wasm/src/stake_account.rs
+++ b/explorer/wasm/src/stake_account.rs
@@ -62,13 +62,13 @@ impl StakeAccount {
return Ok(stake_state.into());
}
- #[wasm_bindgen(js_name = displayState)]
- pub fn display_state(&self) -> String {
+ #[wasm_bindgen(js_name = accountType)]
+ pub fn account_type(&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(),
+ State::Uninitialized => "uninitialized".to_string(),
+ State::Initialized => "initialized".to_string(),
+ State::Delegated => "delegated".to_string(),
+ State::RewardsPool => "rewardsPool".to_string(),
}
}
}