Add support for parsed stake accounts (#11469)
This commit is contained in:
parent
102d15f081
commit
c544116cf2
|
@ -6,4 +6,5 @@ branches:
|
|||
- master
|
||||
script:
|
||||
- npm run build
|
||||
- npm run test
|
||||
- npm run format
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
);
|
||||
});
|
||||
});
|
|
@ -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 (
|
||||
<>
|
||||
<LockupCard stakeAccount={stakeAccount} />
|
||||
<OverviewCard account={account} stakeAccount={stakeAccount} />
|
||||
{stakeAccount.meta && <DelegationCard stakeAccount={stakeAccount} />}
|
||||
{stakeAccount.meta && <AuthoritiesCard meta={stakeAccount.meta} />}
|
||||
<OverviewCard
|
||||
account={account}
|
||||
stakeAccount={stakeAccount}
|
||||
stakeAccountType={stakeAccountType}
|
||||
/>
|
||||
{stakeAccount.meta && (
|
||||
<>
|
||||
<DelegationCard
|
||||
stakeAccount={stakeAccount}
|
||||
stakeAccountType={stakeAccountType}
|
||||
/>
|
||||
<AuthoritiesCard 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 && (
|
||||
<tr>
|
||||
<td>State</td>
|
||||
<td className="text-lg-right">{stakeAccount.displayState()}</td>
|
||||
<td className="text-lg-right">{TYPE_NAMES[stakeAccountType]}</td>
|
||||
</tr>
|
||||
)}
|
||||
</TableCardBody>
|
||||
|
@ -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 (
|
||||
<div className="card">
|
||||
<div className="card-header">
|
||||
|
@ -124,33 +190,23 @@ function DelegationCard({ stakeAccount }: { stakeAccount: StakeAccount }) {
|
|||
</td>
|
||||
</tr>
|
||||
|
||||
{voterPubkey && (
|
||||
<tr>
|
||||
<td>Delegated Vote Address</td>
|
||||
<td className="text-lg-right">
|
||||
<Address
|
||||
pubkey={stake.delegation.voterPubkey}
|
||||
alignRight
|
||||
link
|
||||
/>
|
||||
<Address pubkey={voterPubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
<tr>
|
||||
<td>Activation Epoch</td>
|
||||
<td className="text-lg-right">
|
||||
{stake.delegation.isBootstrapStake()
|
||||
? "-"
|
||||
: stake.delegation.activationEpoch}
|
||||
</td>
|
||||
<td className="text-lg-right">{activationEpoch}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Deactivation Epoch</td>
|
||||
<td className="text-lg-right">
|
||||
{stake.delegation.isDeactivated()
|
||||
? stake.delegation.deactivationEpoch
|
||||
: "-"}
|
||||
</td>
|
||||
<td className="text-lg-right">{deactivationEpoch}</td>
|
||||
</tr>
|
||||
</>
|
||||
)}
|
||||
|
@ -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 (
|
||||
<div className="card">
|
||||
|
|
|
@ -28,7 +28,7 @@ export function UnknownAccountCard({ account }: { account: Account }) {
|
|||
</td>
|
||||
</tr>
|
||||
|
||||
{details && (
|
||||
{details?.space !== undefined && (
|
||||
<tr>
|
||||
<td>Data (Bytes)</td>
|
||||
<td className="text-lg-right">{details.space}</td>
|
||||
|
|
|
@ -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 <TokenInstruction title={title} info={coerced} {...props} />;
|
||||
|
|
|
@ -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<typeof TokenInstructionType>;
|
||||
const TokenInstructionType = enums([
|
||||
export type TokenInstructionType = StructType<typeof TokenInstructionType>;
|
||||
export const TokenInstructionType = enums([
|
||||
"initializeMint",
|
||||
"initializeAccount",
|
||||
"initializeMultisig",
|
||||
|
@ -121,9 +112,3 @@ export const IX_STRUCTS = {
|
|||
burn: Burn,
|
||||
closeAccount: CloseAccount,
|
||||
};
|
||||
|
||||
export type ParsedInstructionInfo = StructType<typeof ParsedInstructionInfo>;
|
||||
export const ParsedInstructionInfo = object({
|
||||
type: TokenInstructionType,
|
||||
info: any(),
|
||||
});
|
||||
|
|
|
@ -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 <StakeAccountSection account={info} stakeAccount={data} />;
|
||||
let stakeAccountType, stakeAccount;
|
||||
if ("accountType" in data) {
|
||||
stakeAccount = data;
|
||||
stakeAccountType = data.accountType as any;
|
||||
} else {
|
||||
stakeAccount = data.info;
|
||||
stakeAccountType = data.type;
|
||||
}
|
||||
|
||||
return (
|
||||
<StakeAccountSection
|
||||
account={info}
|
||||
stakeAccount={stakeAccount}
|
||||
stakeAccountType={stakeAccountType}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return <UnknownAccountCard account={info} />;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
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,
|
||||
|
|
|
@ -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<typeof StakeAccountType>;
|
||||
export const StakeAccountType = enums([
|
||||
"uninitialized",
|
||||
"initialized",
|
||||
"delegated",
|
||||
"rewardsPool",
|
||||
]);
|
||||
|
||||
export type StakeMeta = StructType<typeof StakeMeta>;
|
||||
export const StakeMeta = object({
|
||||
rentExemptReserve: BigNum,
|
||||
authorized: object({
|
||||
staker: Pubkey,
|
||||
withdrawer: Pubkey,
|
||||
}),
|
||||
lockup: object({
|
||||
unixTimestamp: number(),
|
||||
epoch: number(),
|
||||
custodian: Pubkey,
|
||||
}),
|
||||
});
|
||||
|
||||
export type StakeAccountInfo = StructType<typeof StakeAccountInfo>;
|
||||
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<typeof StakeAccount>;
|
||||
export const StakeAccount = object({
|
||||
type: StakeAccountType,
|
||||
info: StakeAccountInfo,
|
||||
});
|
|
@ -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 (
|
||||
<>
|
||||
◎
|
||||
|
|
|
@ -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<BN, any> = coercion(BigNumValue, (value) => {
|
||||
if (typeof value === "string") return new BN(value, 10);
|
||||
throw new Error("invalid big num");
|
||||
});
|
|
@ -0,0 +1,7 @@
|
|||
import { object, any, StructType, string } from "superstruct";
|
||||
|
||||
export type ParsedInfo = StructType<typeof ParsedInfo>;
|
||||
export const ParsedInfo = object({
|
||||
type: string(),
|
||||
info: any(),
|
||||
});
|
|
@ -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<PublicKey, any> = coercion(PubkeyValue, (value) => {
|
||||
if (typeof value === "string") return new PublicKey(value);
|
||||
throw new Error("invalid pubkey");
|
||||
});
|
|
@ -4,7 +4,7 @@
|
|||
"Solana Maintainers <maintainers@solana.com>"
|
||||
],
|
||||
"description": "Solana SDK Wasm",
|
||||
"version": "1.2.0",
|
||||
"version": "1.4.0",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
@ -124,7 +124,7 @@ export class StakeAccount {
|
|||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
displayState(): string;
|
||||
accountType(): string;
|
||||
/**
|
||||
* @returns {Meta | undefined}
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Binary file not shown.
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue