explorer: Add block rewards to block details page (#14786)
* explorer: add block rewards * add key to tr map
This commit is contained in:
65
explorer/src/components/block/BlockHistoryCard.tsx
Normal file
65
explorer/src/components/block/BlockHistoryCard.tsx
Normal file
@ -0,0 +1,65 @@
|
||||
import React from "react";
|
||||
import { ConfirmedBlock } from "@solana/web3.js";
|
||||
import { ErrorCard } from "components/common/ErrorCard";
|
||||
import { Signature } from "components/common/Signature";
|
||||
import bs58 from "bs58";
|
||||
|
||||
export function BlockHistoryCard({ block }: { block: ConfirmedBlock }) {
|
||||
if (block.transactions.length === 0) {
|
||||
return <ErrorCard text="This block has no transactions" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="card">
|
||||
<div className="card-header align-items-center">
|
||||
<h3 className="card-header-title">Block Transactions</h3>
|
||||
</div>
|
||||
|
||||
<div className="table-responsive mb-0">
|
||||
<table className="table table-sm table-nowrap card-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="text-muted">Result</th>
|
||||
<th className="text-muted">Transaction Signature</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="list">
|
||||
{block.transactions.map((tx, i) => {
|
||||
let statusText;
|
||||
let statusClass;
|
||||
let signature: React.ReactNode;
|
||||
if (tx.meta?.err || !tx.transaction.signature) {
|
||||
statusClass = "warning";
|
||||
statusText = "Failed";
|
||||
} else {
|
||||
statusClass = "success";
|
||||
statusText = "Success";
|
||||
}
|
||||
|
||||
if (tx.transaction.signature) {
|
||||
signature = (
|
||||
<Signature
|
||||
signature={bs58.encode(tx.transaction.signature)}
|
||||
link
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<tr key={i}>
|
||||
<td>
|
||||
<span className={`badge badge-soft-${statusClass}`}>
|
||||
{statusText}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td>{signature}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
import bs58 from "bs58";
|
||||
import React from "react";
|
||||
import { TableCardBody } from "components/common/TableCardBody";
|
||||
import { useBlock, useFetchBlock, FetchStatus } from "providers/block";
|
||||
import { Signature } from "components/common/Signature";
|
||||
import { ErrorCard } from "components/common/ErrorCard";
|
||||
import { LoadingCard } from "components/common/LoadingCard";
|
||||
import { Slot } from "components/common/Slot";
|
||||
import { ClusterStatus, useCluster } from "providers/cluster";
|
||||
import { BlockHistoryCard } from "./BlockHistoryCard";
|
||||
import { BlockRewardsCard } from "./BlockRewardsCard";
|
||||
|
||||
export function BlockHistoryCard({ slot }: { slot: number }) {
|
||||
export function BlockOverviewCard({ slot }: { slot: number }) {
|
||||
const confirmedBlock = useBlock(slot);
|
||||
const fetchBlock = useFetchBlock();
|
||||
const { status } = useCluster();
|
||||
@ -67,61 +67,8 @@ export function BlockHistoryCard({ slot }: { slot: number }) {
|
||||
</TableCardBody>
|
||||
</div>
|
||||
|
||||
{block.transactions.length === 0 ? (
|
||||
<ErrorCard text="This block has no transactions" />
|
||||
) : (
|
||||
<div className="card">
|
||||
<div className="card-header align-items-center">
|
||||
<h3 className="card-header-title">Block Transactions</h3>
|
||||
</div>
|
||||
|
||||
<div className="table-responsive mb-0">
|
||||
<table className="table table-sm table-nowrap card-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="text-muted">Result</th>
|
||||
<th className="text-muted">Transaction Signature</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="list">
|
||||
{block.transactions.map((tx, i) => {
|
||||
let statusText;
|
||||
let statusClass;
|
||||
let signature: React.ReactNode;
|
||||
if (tx.meta?.err || !tx.transaction.signature) {
|
||||
statusClass = "warning";
|
||||
statusText = "Failed";
|
||||
} else {
|
||||
statusClass = "success";
|
||||
statusText = "Success";
|
||||
}
|
||||
|
||||
if (tx.transaction.signature) {
|
||||
signature = (
|
||||
<Signature
|
||||
signature={bs58.encode(tx.transaction.signature)}
|
||||
link
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<tr key={i}>
|
||||
<td>
|
||||
<span className={`badge badge-soft-${statusClass}`}>
|
||||
{statusText}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td>{signature}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<BlockRewardsCard block={block} />
|
||||
<BlockHistoryCard block={block} />
|
||||
</>
|
||||
);
|
||||
}
|
59
explorer/src/components/block/BlockRewardsCard.tsx
Normal file
59
explorer/src/components/block/BlockRewardsCard.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import React from "react";
|
||||
import { lamportsToSolString } from "utils";
|
||||
import { ConfirmedBlock, PublicKey } from "@solana/web3.js";
|
||||
import { Address } from "components/common/Address";
|
||||
|
||||
export function BlockRewardsCard({ block }: { block: ConfirmedBlock }) {
|
||||
if (block.rewards.length < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="card">
|
||||
<div className="card-header align-items-center">
|
||||
<h3 className="card-header-title">Block Rewards</h3>
|
||||
</div>
|
||||
|
||||
<div className="table-responsive mb-0">
|
||||
<table className="table table-sm table-nowrap card-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="text-muted">Address</th>
|
||||
<th className="text-muted">Type</th>
|
||||
<th className="text-muted">Amount</th>
|
||||
<th className="text-muted">New Balance</th>
|
||||
<th className="text-muted">Percent Change</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{block.rewards.map((reward) => {
|
||||
let percentChange;
|
||||
if (reward.postBalance !== null && reward.postBalance !== 0) {
|
||||
percentChange = (
|
||||
(Math.abs(reward.lamports) /
|
||||
(reward.postBalance - reward.lamports)) *
|
||||
100
|
||||
).toFixed(9);
|
||||
}
|
||||
return (
|
||||
<tr key={reward.pubkey + reward.rewardType}>
|
||||
<td>
|
||||
<Address pubkey={new PublicKey(reward.pubkey)} link />
|
||||
</td>
|
||||
<td>{reward.rewardType}</td>
|
||||
<td>{lamportsToSolString(reward.lamports)}</td>
|
||||
<td>
|
||||
{reward.postBalance
|
||||
? lamportsToSolString(reward.postBalance)
|
||||
: "-"}
|
||||
</td>
|
||||
<td>{percentChange ? percentChange + "%" : "-"}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
|
||||
import { BlockHistoryCard } from "components/account/BlockHistoryCard";
|
||||
import { ErrorCard } from "components/common/ErrorCard";
|
||||
import { BlockOverviewCard } from "components/block/BlockOverviewCard";
|
||||
|
||||
type Props = { slot: string };
|
||||
|
||||
@ -9,7 +9,7 @@ export function BlockDetailsPage({ slot }: Props) {
|
||||
let output = <ErrorCard text={`Block ${slot} is not valid`} />;
|
||||
|
||||
if (!isNaN(Number(slot))) {
|
||||
output = <BlockHistoryCard slot={Number(slot)} />;
|
||||
output = <BlockOverviewCard slot={Number(slot)} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
Reference in New Issue
Block a user