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:
		@@ -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() });
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user