explorer/: Deactivated stake accounts report as delegated (#12262)

* feat: add getStakeActivation to web3.js

* feat: add activation status to delegation card

* style: pretty

* feat: add epoch to getStakeActivation call

* feat: add unit test for getStakeActivation in web3.js

* feat: add test for getStakeActivation in web3.js

* feat: add getStakeActivation

* chore: add rollup watch

* feat: use string literal for stake activation state

* fix: dont display empty () for not delegated accounts

* fix: remove optional chaining due to issue with esdoc

* chore: remove optional_chaining

* feat: add live test for getStakeActivation

* feat: add active/inactive stake to account page

* feat: extend _buildArgs to support additional options, simplify unit test

* chore: update @solana/web3.js tp 0.76.0

* style: resolve linter issues

Co-authored-by: Justin Starry <justin@solana.com>
This commit is contained in:
Bartosz Lipinski 2020-09-22 00:41:39 -05:00 committed by GitHub
parent 7dd4de80eb
commit ef60d0f5ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 50 additions and 7 deletions

View File

@ -11,17 +11,20 @@ import {
StakeAccountType, StakeAccountType,
} from "validators/accounts/stake"; } from "validators/accounts/stake";
import BN from "bn.js"; import BN from "bn.js";
import { StakeActivationData } from "@solana/web3.js";
const MAX_EPOCH = new BN(2).pow(new BN(64)).sub(new BN(1)); const MAX_EPOCH = new BN(2).pow(new BN(64)).sub(new BN(1));
export function StakeAccountSection({ export function StakeAccountSection({
account, account,
stakeAccount, stakeAccount,
activation,
stakeAccountType, stakeAccountType,
}: { }: {
account: Account; account: Account;
stakeAccount: StakeAccountInfo | StakeAccountWasm; stakeAccount: StakeAccountInfo | StakeAccountWasm;
stakeAccountType: StakeAccountType; stakeAccountType: StakeAccountType;
activation?: StakeActivationData;
}) { }) {
return ( return (
<> <>
@ -35,6 +38,7 @@ export function StakeAccountSection({
<> <>
<DelegationCard <DelegationCard
stakeAccount={stakeAccount} stakeAccount={stakeAccount}
activation={activation}
stakeAccountType={stakeAccountType} stakeAccountType={stakeAccountType}
/> />
<AuthoritiesCard meta={stakeAccount.meta} /> <AuthoritiesCard meta={stakeAccount.meta} />
@ -129,17 +133,22 @@ function OverviewCard({
function DelegationCard({ function DelegationCard({
stakeAccount, stakeAccount,
stakeAccountType, stakeAccountType,
activation,
}: { }: {
stakeAccount: StakeAccountInfo | StakeAccountWasm; stakeAccount: StakeAccountInfo | StakeAccountWasm;
stakeAccountType: StakeAccountType; stakeAccountType: StakeAccountType;
activation?: StakeActivationData;
}) { }) {
const displayStatus = () => { const displayStatus = () => {
// TODO check epoch
let status = TYPE_NAMES[stakeAccountType]; let status = TYPE_NAMES[stakeAccountType];
let activationState = "";
if (stakeAccountType !== "delegated") { if (stakeAccountType !== "delegated") {
status = "Not delegated"; status = "Not delegated";
} else {
activationState = activation ? `(${activation.state})` : "";
} }
return status;
return [status, activationState].join(" ");
}; };
let voterPubkey, activationEpoch, deactivationEpoch; let voterPubkey, activationEpoch, deactivationEpoch;
@ -190,6 +199,24 @@ function DelegationCard({
</td> </td>
</tr> </tr>
{activation && (
<>
<tr>
<td>Active Stake (SOL)</td>
<td className="text-lg-right">
{lamportsToSolString(activation.active)}
</td>
</tr>
<tr>
<td>Inactive Stake (SOL)</td>
<td className="text-lg-right">
{lamportsToSolString(activation.inactive)}
</td>
</tr>
</>
)}
{voterPubkey && ( {voterPubkey && (
<tr> <tr>
<td>Delegated Vote Address</td> <td>Delegated Vote Address</td>

View File

@ -143,6 +143,7 @@ function DetailsSections({ pubkey, tab }: { pubkey: PublicKey; tab?: string }) {
function InfoSection({ account }: { account: Account }) { function InfoSection({ account }: { account: Account }) {
const data = account?.details?.data; const data = account?.details?.data;
if (data && data.program === "stake") { if (data && data.program === "stake") {
let stakeAccountType, stakeAccount; let stakeAccountType, stakeAccount;
if ("accountType" in data.parsed) { if ("accountType" in data.parsed) {
@ -157,6 +158,7 @@ function InfoSection({ account }: { account: Account }) {
<StakeAccountSection <StakeAccountSection
account={account} account={account}
stakeAccount={stakeAccount} stakeAccount={stakeAccount}
activation={data.activation}
stakeAccountType={stakeAccountType} stakeAccountType={stakeAccountType}
/> />
); );

View File

@ -1,6 +1,11 @@
import React from "react"; import React from "react";
import { StakeAccount as StakeAccountWasm } from "solana-sdk-wasm"; import { StakeAccount as StakeAccountWasm } from "solana-sdk-wasm";
import { PublicKey, Connection, StakeProgram } from "@solana/web3.js"; import {
PublicKey,
Connection,
StakeProgram,
StakeActivationData,
} from "@solana/web3.js";
import { useCluster, Cluster } from "../cluster"; import { useCluster, Cluster } from "../cluster";
import { HistoryProvider } from "./history"; import { HistoryProvider } from "./history";
import { TokensProvider, TOKEN_PROGRAM_ID } from "./tokens"; import { TokensProvider, TOKEN_PROGRAM_ID } from "./tokens";
@ -20,6 +25,7 @@ export { useAccountHistory } from "./history";
export type StakeProgramData = { export type StakeProgramData = {
program: "stake"; program: "stake";
parsed: StakeAccount | StakeAccountWasm; parsed: StakeAccount | StakeAccountWasm;
activation?: StakeActivationData;
}; };
export type TokenProgramData = { export type TokenProgramData = {
@ -85,9 +91,8 @@ async function fetchAccountInfo(
let data; let data;
let fetchStatus; let fetchStatus;
try { try {
const result = ( const connection = new Connection(url, "single");
await new Connection(url, "single").getParsedAccountInfo(pubkey) const result = (await connection.getParsedAccountInfo(pubkey)).value;
).value;
let lamports, details; let lamports, details;
if (result === null) { if (result === null) {
@ -104,17 +109,26 @@ async function fetchAccountInfo(
let data: ProgramData | undefined; let data: ProgramData | undefined;
if (result.owner.equals(StakeProgram.programId)) { if (result.owner.equals(StakeProgram.programId)) {
try { try {
let parsed; let parsed: StakeAccount | StakeAccountWasm;
let isDelegated: boolean = false;
if ("parsed" in result.data) { if ("parsed" in result.data) {
const info = coerce(result.data.parsed, ParsedInfo); const info = coerce(result.data.parsed, ParsedInfo);
parsed = coerce(info, StakeAccount); parsed = coerce(info, StakeAccount);
isDelegated = parsed.type === "delegated";
} else { } else {
const wasm = await import("solana-sdk-wasm"); const wasm = await import("solana-sdk-wasm");
parsed = wasm.StakeAccount.fromAccountData(result.data); parsed = wasm.StakeAccount.fromAccountData(result.data);
isDelegated = (parsed.accountType as any) === "delegated";
} }
const activation = isDelegated
? await connection.getStakeActivation(pubkey)
: undefined;
data = { data = {
program: "stake", program: "stake",
parsed, parsed,
activation,
}; };
} catch (err) { } catch (err) {
reportError(err, { url, address: pubkey.toBase58() }); reportError(err, { url, address: pubkey.toBase58() });