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:
parent
7dd4de80eb
commit
ef60d0f5ba
|
@ -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>
|
||||||
|
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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() });
|
||||||
|
|
Loading…
Reference in New Issue