fix: expand vote details card to cover all instructions (#15602)
This commit is contained in:
parent
d679eff3fa
commit
8c73187b1e
|
@ -1,91 +1,127 @@
|
|||
import React from "react";
|
||||
import { ParsedInstruction, SignatureResult } from "@solana/web3.js";
|
||||
import { coerce } from "superstruct";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { coerce, Struct } from "superstruct";
|
||||
import { ParsedInfo } from "validators";
|
||||
import { VoteInfo } from "./types";
|
||||
import {
|
||||
UpdateCommissionInfo,
|
||||
UpdateValidatorInfo,
|
||||
VoteInfo,
|
||||
VoteSwitchInfo,
|
||||
WithdrawInfo,
|
||||
AuthorizeInfo,
|
||||
} from "./types";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import { Address } from "components/common/Address";
|
||||
import { displayTimestamp } from "utils/date";
|
||||
import { UnknownDetailsCard } from "../UnknownDetailsCard";
|
||||
import { InstructionDetailsProps } from "components/transaction/InstructionsSection";
|
||||
import { camelToTitleCase } from "utils";
|
||||
import { useCluster } from "providers/cluster";
|
||||
import { reportError } from "utils/sentry";
|
||||
|
||||
export function VoteDetailsCard(props: {
|
||||
ix: ParsedInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
const { ix, index, result, innerCards, childIndex } = props;
|
||||
export function VoteDetailsCard(props: InstructionDetailsProps) {
|
||||
const { url } = useCluster();
|
||||
|
||||
try {
|
||||
const parsed = coerce(props.ix.parsed, ParsedInfo);
|
||||
const info = coerce(parsed.info, VoteInfo);
|
||||
|
||||
switch (parsed.type) {
|
||||
case "vote":
|
||||
return renderDetails<VoteInfo>(props, parsed, VoteInfo);
|
||||
case "authorize":
|
||||
return renderDetails<AuthorizeInfo>(props, parsed, AuthorizeInfo);
|
||||
case "withdraw":
|
||||
return renderDetails<WithdrawInfo>(props, parsed, WithdrawInfo);
|
||||
case "updateValidator":
|
||||
return renderDetails<UpdateValidatorInfo>(
|
||||
props,
|
||||
parsed,
|
||||
UpdateValidatorInfo
|
||||
);
|
||||
case "updateCommission":
|
||||
return renderDetails<UpdateCommissionInfo>(
|
||||
props,
|
||||
parsed,
|
||||
UpdateCommissionInfo
|
||||
);
|
||||
case "voteSwitch":
|
||||
return renderDetails<VoteSwitchInfo>(props, parsed, VoteSwitchInfo);
|
||||
}
|
||||
} catch (error) {
|
||||
reportError(error, {
|
||||
url,
|
||||
});
|
||||
}
|
||||
|
||||
return <UnknownDetailsCard {...props} />;
|
||||
}
|
||||
|
||||
function renderDetails<T>(
|
||||
props: InstructionDetailsProps,
|
||||
parsed: ParsedInfo,
|
||||
struct: Struct<T>
|
||||
) {
|
||||
const info = coerce(parsed.info, struct);
|
||||
const attributes: JSX.Element[] = [];
|
||||
|
||||
for (let [key, value] of Object.entries(info)) {
|
||||
if (value instanceof PublicKey) {
|
||||
value = <Address pubkey={value} alignRight link />;
|
||||
}
|
||||
|
||||
if (key === "vote") {
|
||||
attributes.push(
|
||||
<tr key="vote-hash">
|
||||
<td>Vote Hash</td>
|
||||
<td className="text-lg-right">
|
||||
<pre className="d-inline-block text-left mb-0">{value.hash}</pre>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
if (value.timestamp) {
|
||||
attributes.push(
|
||||
<tr>
|
||||
<td>Timestamp</td>
|
||||
<td className="text-lg-right text-monospace">
|
||||
{displayTimestamp(value.timestamp * 1000)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
attributes.push(
|
||||
<tr key="vote-slots">
|
||||
<td>Slots</td>
|
||||
<td className="text-lg-right text-monospace">
|
||||
<pre className="d-inline-block text-left mb-0">
|
||||
{value.slots.join("\n")}
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
} else {
|
||||
attributes.push(
|
||||
<tr key={key}>
|
||||
<td>{camelToTitleCase(key)} </td>
|
||||
<td className="text-lg-right">{value}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title="Vote"
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
{...props}
|
||||
title={`Vote: ${camelToTitleCase(parsed.type)}`}
|
||||
>
|
||||
<tr>
|
||||
<td>Program</td>
|
||||
<td className="text-lg-right">
|
||||
<Address pubkey={ix.programId} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Vote Account</td>
|
||||
<td className="text-lg-right">
|
||||
<Address pubkey={info.voteAccount} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Vote Authority</td>
|
||||
<td className="text-lg-right">
|
||||
<Address pubkey={info.voteAuthority} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Clock Sysvar</td>
|
||||
<td className="text-lg-right">
|
||||
<Address pubkey={info.clockSysvar} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Slot Hashes Sysvar</td>
|
||||
<td className="text-lg-right">
|
||||
<Address pubkey={info.slotHashesSysvar} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Vote Hash</td>
|
||||
<td className="text-lg-right">
|
||||
<pre className="d-inline-block text-left mb-0">{info.vote.hash}</pre>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{info.vote.timestamp && (
|
||||
<tr>
|
||||
<td>Timestamp</td>
|
||||
<td className="text-lg-right text-monospace">
|
||||
{displayTimestamp(info.vote.timestamp * 1000)}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
<tr>
|
||||
<td>Slots</td>
|
||||
<td className="text-lg-right text-monospace">
|
||||
<pre className="d-inline-block text-left mb-0">
|
||||
{info.vote.slots.join("\n")}
|
||||
</pre>
|
||||
<Address pubkey={props.ix.programId} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
{attributes}
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,26 @@
|
|||
import { array, number, optional, pick, string, StructType } from "superstruct";
|
||||
import { Pubkey } from "validators/pubkey";
|
||||
|
||||
export type InitializeInfo = StructType<typeof InitializeInfo>;
|
||||
export const InitializeInfo = pick({
|
||||
voteAccount: Pubkey,
|
||||
rentSysvar: Pubkey,
|
||||
clockSysvar: Pubkey,
|
||||
node: Pubkey,
|
||||
authorizedVoter: Pubkey,
|
||||
authorizedWithdrawer: Pubkey,
|
||||
commission: number(),
|
||||
});
|
||||
|
||||
export type AuthorizeInfo = StructType<typeof AuthorizeInfo>;
|
||||
export const AuthorizeInfo = pick({
|
||||
voteAccount: Pubkey,
|
||||
clockSysvar: Pubkey,
|
||||
authority: Pubkey,
|
||||
newAuthority: Pubkey,
|
||||
authorityType: number(),
|
||||
});
|
||||
|
||||
export type VoteInfo = StructType<typeof VoteInfo>;
|
||||
export const VoteInfo = pick({
|
||||
clockSysvar: Pubkey,
|
||||
|
@ -15,3 +35,39 @@ export const VoteInfo = pick({
|
|||
timestamp: optional(number()),
|
||||
}),
|
||||
});
|
||||
|
||||
export type WithdrawInfo = StructType<typeof WithdrawInfo>;
|
||||
export const WithdrawInfo = pick({
|
||||
voteAccount: Pubkey,
|
||||
destination: Pubkey,
|
||||
withdrawAuthority: Pubkey,
|
||||
lamports: number(),
|
||||
});
|
||||
|
||||
export type UpdateValidatorInfo = StructType<typeof UpdateValidatorInfo>;
|
||||
export const UpdateValidatorInfo = pick({
|
||||
voteAccount: Pubkey,
|
||||
newValidatorIdentity: Pubkey,
|
||||
withdrawAuthority: Pubkey,
|
||||
});
|
||||
|
||||
export type UpdateCommissionInfo = StructType<typeof UpdateCommissionInfo>;
|
||||
export const UpdateCommissionInfo = pick({
|
||||
voteAccount: Pubkey,
|
||||
withdrawAuthority: Pubkey,
|
||||
commission: number(),
|
||||
});
|
||||
|
||||
export type VoteSwitchInfo = StructType<typeof VoteSwitchInfo>;
|
||||
export const VoteSwitchInfo = pick({
|
||||
voteAccount: Pubkey,
|
||||
slotHashesSysvar: Pubkey,
|
||||
clockSysvar: Pubkey,
|
||||
voteAuthority: Pubkey,
|
||||
vote: pick({
|
||||
hash: string(),
|
||||
slots: array(number()),
|
||||
timestamp: number(),
|
||||
}),
|
||||
hash: string(),
|
||||
});
|
||||
|
|
|
@ -34,8 +34,17 @@ import {
|
|||
useTransactionStatus,
|
||||
} from "providers/transactions";
|
||||
import { Cluster, useCluster } from "providers/cluster";
|
||||
// import { VoteDetailsCard } from "components/instruction/vote/VoteDetailsCard";
|
||||
import { UpgradeableBpfLoaderDetailsCard } from "components/instruction/upgradeable-bpf-loader/UpgradeableBpfLoaderDetailsCard";
|
||||
import { VoteDetailsCard } from "components/instruction/vote/VoteDetailsCard";
|
||||
|
||||
export type InstructionDetailsProps = {
|
||||
tx: ParsedTransaction;
|
||||
ix: ParsedInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
};
|
||||
|
||||
export function InstructionsSection({ signature }: SignatureProps) {
|
||||
const status = useTransactionStatus(signature);
|
||||
|
@ -171,8 +180,8 @@ function renderInstructionCard({
|
|||
return <StakeDetailsCard {...props} />;
|
||||
case "spl-memo":
|
||||
return <MemoDetailsCard {...props} />;
|
||||
/*case "vote":
|
||||
return <VoteDetailsCard {...props} />;*/
|
||||
case "vote":
|
||||
return <VoteDetailsCard {...props} />;
|
||||
default:
|
||||
return <UnknownDetailsCard {...props} />;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue