2021-06-21 16:53:06 -07:00
|
|
|
import React from "react";
|
|
|
|
import { PublicKey, SystemProgram } from "@solana/web3.js";
|
|
|
|
import { Address } from "components/common/Address";
|
|
|
|
import {
|
|
|
|
Account,
|
|
|
|
useAccountInfo,
|
|
|
|
useFetchAccountInfo,
|
|
|
|
} from "providers/accounts";
|
|
|
|
import { ClusterStatus, useCluster } from "providers/cluster";
|
|
|
|
import { addressLabel } from "utils/tx";
|
|
|
|
import { lamportsToSolString } from "utils";
|
|
|
|
|
|
|
|
type AccountValidator = (account: Account) => string | undefined;
|
|
|
|
|
|
|
|
export const createFeePayerValidator = (
|
|
|
|
feeLamports: number
|
|
|
|
): AccountValidator => {
|
|
|
|
return (account: Account): string | undefined => {
|
|
|
|
if (account.details === undefined) return "Account doesn't exist";
|
|
|
|
if (!account.details.owner.equals(SystemProgram.programId))
|
|
|
|
return "Only system-owned accounts can pay fees";
|
|
|
|
// TODO: Actually nonce accounts can pay fees too
|
|
|
|
if (account.details.space > 0)
|
|
|
|
return "Only unallocated accounts can pay fees";
|
|
|
|
if (account.lamports < feeLamports) {
|
|
|
|
return "Insufficient funds for fees";
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
export const programValidator = (account: Account): string | undefined => {
|
|
|
|
if (account.details === undefined) return "Account doesn't exist";
|
|
|
|
if (!account.details.executable)
|
|
|
|
return "Only executable accounts can be invoked";
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
|
|
|
export function AddressWithContext({
|
|
|
|
pubkey,
|
|
|
|
validator,
|
|
|
|
}: {
|
|
|
|
pubkey: PublicKey;
|
|
|
|
validator?: AccountValidator;
|
|
|
|
}) {
|
|
|
|
return (
|
|
|
|
<div className="d-flex align-items-end flex-column">
|
|
|
|
<Address pubkey={pubkey} link />
|
|
|
|
<AccountInfo pubkey={pubkey} validator={validator} />
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function AccountInfo({
|
|
|
|
pubkey,
|
|
|
|
validator,
|
|
|
|
}: {
|
|
|
|
pubkey: PublicKey;
|
|
|
|
validator?: AccountValidator;
|
|
|
|
}) {
|
|
|
|
const address = pubkey.toBase58();
|
|
|
|
const fetchAccount = useFetchAccountInfo();
|
|
|
|
const info = useAccountInfo(address);
|
|
|
|
const { cluster, status } = useCluster();
|
|
|
|
|
|
|
|
// Fetch account on load
|
|
|
|
React.useEffect(() => {
|
|
|
|
if (!info && status === ClusterStatus.Connected && pubkey) {
|
|
|
|
fetchAccount(pubkey);
|
|
|
|
}
|
|
|
|
}, [address, status]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
|
|
|
|
if (!info?.data)
|
|
|
|
return (
|
|
|
|
<span className="text-muted">
|
2021-11-28 12:49:22 -08:00
|
|
|
<span className="spinner-grow spinner-grow-sm me-2"></span>
|
2021-06-21 16:53:06 -07:00
|
|
|
Loading
|
|
|
|
</span>
|
|
|
|
);
|
|
|
|
|
|
|
|
const errorMessage = validator && validator(info.data);
|
|
|
|
if (errorMessage) return <span className="text-warning">{errorMessage}</span>;
|
|
|
|
|
|
|
|
if (info.data.details?.executable) {
|
|
|
|
return <span className="text-muted">Executable Program</span>;
|
|
|
|
}
|
|
|
|
|
|
|
|
const owner = info.data.details?.owner;
|
|
|
|
const ownerAddress = owner?.toBase58();
|
|
|
|
const ownerLabel = ownerAddress && addressLabel(ownerAddress, cluster);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<span className="text-muted">
|
|
|
|
{ownerAddress
|
|
|
|
? `Owned by ${
|
|
|
|
ownerLabel || ownerAddress
|
|
|
|
}. Balance is ${lamportsToSolString(info.data.lamports)} SOL`
|
|
|
|
: "Account doesn't exist"}
|
|
|
|
</span>
|
|
|
|
);
|
|
|
|
}
|