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 React from "react";
|
||||||
import { ParsedInstruction, SignatureResult } from "@solana/web3.js";
|
import { PublicKey } from "@solana/web3.js";
|
||||||
import { coerce } from "superstruct";
|
import { coerce, Struct } from "superstruct";
|
||||||
import { ParsedInfo } from "validators";
|
import { ParsedInfo } from "validators";
|
||||||
import { VoteInfo } from "./types";
|
import {
|
||||||
|
UpdateCommissionInfo,
|
||||||
|
UpdateValidatorInfo,
|
||||||
|
VoteInfo,
|
||||||
|
VoteSwitchInfo,
|
||||||
|
WithdrawInfo,
|
||||||
|
AuthorizeInfo,
|
||||||
|
} from "./types";
|
||||||
import { InstructionCard } from "../InstructionCard";
|
import { InstructionCard } from "../InstructionCard";
|
||||||
import { Address } from "components/common/Address";
|
import { Address } from "components/common/Address";
|
||||||
import { displayTimestamp } from "utils/date";
|
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: {
|
export function VoteDetailsCard(props: InstructionDetailsProps) {
|
||||||
ix: ParsedInstruction;
|
const { url } = useCluster();
|
||||||
index: number;
|
|
||||||
result: SignatureResult;
|
try {
|
||||||
innerCards?: JSX.Element[];
|
const parsed = coerce(props.ix.parsed, ParsedInfo);
|
||||||
childIndex?: number;
|
|
||||||
}) {
|
switch (parsed.type) {
|
||||||
const { ix, index, result, innerCards, childIndex } = props;
|
case "vote":
|
||||||
const parsed = coerce(props.ix.parsed, ParsedInfo);
|
return renderDetails<VoteInfo>(props, parsed, VoteInfo);
|
||||||
const info = coerce(parsed.info, 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 (
|
return (
|
||||||
<InstructionCard
|
<InstructionCard
|
||||||
ix={ix}
|
{...props}
|
||||||
index={index}
|
title={`Vote: ${camelToTitleCase(parsed.type)}`}
|
||||||
result={result}
|
|
||||||
title="Vote"
|
|
||||||
innerCards={innerCards}
|
|
||||||
childIndex={childIndex}
|
|
||||||
>
|
>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Program</td>
|
<td>Program</td>
|
||||||
<td className="text-lg-right">
|
<td className="text-lg-right">
|
||||||
<Address pubkey={ix.programId} alignRight link />
|
<Address pubkey={props.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>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{attributes}
|
||||||
</InstructionCard>
|
</InstructionCard>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,26 @@
|
||||||
import { array, number, optional, pick, string, StructType } from "superstruct";
|
import { array, number, optional, pick, string, StructType } from "superstruct";
|
||||||
import { Pubkey } from "validators/pubkey";
|
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 type VoteInfo = StructType<typeof VoteInfo>;
|
||||||
export const VoteInfo = pick({
|
export const VoteInfo = pick({
|
||||||
clockSysvar: Pubkey,
|
clockSysvar: Pubkey,
|
||||||
|
@ -15,3 +35,39 @@ export const VoteInfo = pick({
|
||||||
timestamp: optional(number()),
|
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,
|
useTransactionStatus,
|
||||||
} from "providers/transactions";
|
} from "providers/transactions";
|
||||||
import { Cluster, useCluster } from "providers/cluster";
|
import { Cluster, useCluster } from "providers/cluster";
|
||||||
// import { VoteDetailsCard } from "components/instruction/vote/VoteDetailsCard";
|
|
||||||
import { UpgradeableBpfLoaderDetailsCard } from "components/instruction/upgradeable-bpf-loader/UpgradeableBpfLoaderDetailsCard";
|
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) {
|
export function InstructionsSection({ signature }: SignatureProps) {
|
||||||
const status = useTransactionStatus(signature);
|
const status = useTransactionStatus(signature);
|
||||||
|
@ -171,8 +180,8 @@ function renderInstructionCard({
|
||||||
return <StakeDetailsCard {...props} />;
|
return <StakeDetailsCard {...props} />;
|
||||||
case "spl-memo":
|
case "spl-memo":
|
||||||
return <MemoDetailsCard {...props} />;
|
return <MemoDetailsCard {...props} />;
|
||||||
/*case "vote":
|
case "vote":
|
||||||
return <VoteDetailsCard {...props} />;*/
|
return <VoteDetailsCard {...props} />;
|
||||||
default:
|
default:
|
||||||
return <UnknownDetailsCard {...props} />;
|
return <UnknownDetailsCard {...props} />;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue