Refactor transaction provider
This commit is contained in:
parent
f3e677eaab
commit
6eff7f35c9
|
@ -3,15 +3,15 @@ import {
|
||||||
useDetails,
|
useDetails,
|
||||||
useTransactions,
|
useTransactions,
|
||||||
useTransactionsDispatch,
|
useTransactionsDispatch,
|
||||||
ActionType,
|
ActionType
|
||||||
Selected
|
|
||||||
} from "../providers/transactions";
|
} from "../providers/transactions";
|
||||||
import { displayAddress, decodeCreate, decodeTransfer } from "../utils/tx";
|
import { displayAddress, decodeCreate, decodeTransfer } from "../utils/tx";
|
||||||
import {
|
import {
|
||||||
LAMPORTS_PER_SOL,
|
LAMPORTS_PER_SOL,
|
||||||
TransferParams,
|
TransferParams,
|
||||||
CreateAccountParams,
|
CreateAccountParams,
|
||||||
TransactionInstruction
|
TransactionInstruction,
|
||||||
|
TransactionSignature
|
||||||
} from "@solana/web3.js";
|
} from "@solana/web3.js";
|
||||||
import Copyable from "./Copyable";
|
import Copyable from "./Copyable";
|
||||||
import Overlay from "./Overlay";
|
import Overlay from "./Overlay";
|
||||||
|
@ -36,7 +36,7 @@ function TransactionModal() {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TransactionDetails selected={selected} />
|
<TransactionDetails signature={selected} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,8 +53,12 @@ function TransactionModal() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function TransactionDetails({ selected }: { selected: Selected }) {
|
function TransactionDetails({
|
||||||
const details = useDetails(selected.signature);
|
signature
|
||||||
|
}: {
|
||||||
|
signature: TransactionSignature;
|
||||||
|
}) {
|
||||||
|
const details = useDetails(signature);
|
||||||
|
|
||||||
const renderError = (content: React.ReactNode) => {
|
const renderError = (content: React.ReactNode) => {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -4,8 +4,8 @@ import {
|
||||||
useTransactionsDispatch,
|
useTransactionsDispatch,
|
||||||
checkTransactionStatus,
|
checkTransactionStatus,
|
||||||
ActionType,
|
ActionType,
|
||||||
Transaction,
|
TransactionState,
|
||||||
Status
|
FetchStatus
|
||||||
} from "../providers/transactions";
|
} from "../providers/transactions";
|
||||||
import bs58 from "bs58";
|
import bs58 from "bs58";
|
||||||
import { assertUnreachable } from "../utils";
|
import { assertUnreachable } from "../utils";
|
||||||
|
@ -115,50 +115,50 @@ const renderHeader = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderTransactionRow = (
|
const renderTransactionRow = (
|
||||||
transaction: Transaction,
|
transaction: TransactionState,
|
||||||
dispatch: any,
|
dispatch: any,
|
||||||
url: string
|
url: string
|
||||||
) => {
|
) => {
|
||||||
|
const { fetchStatus, transactionStatus } = transaction;
|
||||||
|
|
||||||
let statusText;
|
let statusText;
|
||||||
let statusClass;
|
let statusClass;
|
||||||
switch (transaction.status) {
|
switch (fetchStatus) {
|
||||||
case Status.CheckFailed:
|
case FetchStatus.FetchFailed:
|
||||||
statusClass = "dark";
|
statusClass = "dark";
|
||||||
statusText = "Cluster Error";
|
statusText = "Cluster Error";
|
||||||
break;
|
break;
|
||||||
case Status.Checking:
|
case FetchStatus.Fetching:
|
||||||
statusClass = "info";
|
statusClass = "info";
|
||||||
statusText = "Checking";
|
statusText = "Fetching";
|
||||||
break;
|
break;
|
||||||
case Status.Success:
|
case FetchStatus.Fetched: {
|
||||||
statusClass = "success";
|
if (!transactionStatus) {
|
||||||
statusText = "Success";
|
|
||||||
break;
|
|
||||||
case Status.Failure:
|
|
||||||
statusClass = "danger";
|
|
||||||
statusText = "Failed";
|
|
||||||
break;
|
|
||||||
case Status.Missing:
|
|
||||||
statusClass = "warning";
|
statusClass = "warning";
|
||||||
statusText = "Not Found";
|
statusText = "Not Found";
|
||||||
|
} else if (transactionStatus.result.err) {
|
||||||
|
statusClass = "danger";
|
||||||
|
statusText = "Failed";
|
||||||
|
} else {
|
||||||
|
statusClass = "success";
|
||||||
|
statusText = "Success";
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return assertUnreachable(transaction.status);
|
return assertUnreachable(fetchStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
let slotText = "-";
|
let slotText = "-";
|
||||||
if (transaction.slot !== undefined) {
|
|
||||||
slotText = `${transaction.slot}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
let confirmationsText = "-";
|
let confirmationsText = "-";
|
||||||
if (transaction.confirmations !== undefined) {
|
if (transactionStatus) {
|
||||||
confirmationsText = `${transaction.confirmations}`;
|
slotText = `${transactionStatus.slot}`;
|
||||||
|
confirmationsText = `${transactionStatus.confirmations}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderDetails = () => {
|
const renderDetails = () => {
|
||||||
let onClick, icon;
|
let onClick, icon;
|
||||||
if (transaction.confirmations === "max") {
|
if (transactionStatus?.confirmations === "max") {
|
||||||
icon = "more-horizontal";
|
icon = "more-horizontal";
|
||||||
onClick = () =>
|
onClick = () =>
|
||||||
dispatch({
|
dispatch({
|
||||||
|
|
|
@ -97,9 +97,9 @@ export function DetailsProvider({ children }: DetailsProviderProps) {
|
||||||
if (status !== ClusterStatus.Connected) return;
|
if (status !== ClusterStatus.Connected) return;
|
||||||
|
|
||||||
const fetchSignatures = new Set<string>();
|
const fetchSignatures = new Set<string>();
|
||||||
transactions.forEach(tx => {
|
transactions.forEach(({ signature, transactionStatus }) => {
|
||||||
if (tx.slot && tx.confirmations === "max" && !state[tx.signature])
|
if (transactionStatus?.confirmations === "max" && !state[signature])
|
||||||
fetchSignatures.add(tx.signature);
|
fetchSignatures.add(signature);
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchList: string[] = [];
|
const fetchList: string[] = [];
|
||||||
|
|
|
@ -3,7 +3,8 @@ import {
|
||||||
TransactionSignature,
|
TransactionSignature,
|
||||||
Connection,
|
Connection,
|
||||||
SystemProgram,
|
SystemProgram,
|
||||||
Account
|
Account,
|
||||||
|
SignatureResult
|
||||||
} from "@solana/web3.js";
|
} from "@solana/web3.js";
|
||||||
import { findGetParameter, findPathSegment } from "../../utils/url";
|
import { findGetParameter, findPathSegment } from "../../utils/url";
|
||||||
import { useCluster, ClusterStatus } from "../cluster";
|
import { useCluster, ClusterStatus } from "../cluster";
|
||||||
|
@ -20,12 +21,10 @@ import {
|
||||||
ActionType as AccountsActionType
|
ActionType as AccountsActionType
|
||||||
} from "../accounts";
|
} from "../accounts";
|
||||||
|
|
||||||
export enum Status {
|
export enum FetchStatus {
|
||||||
Checking,
|
Fetching,
|
||||||
CheckFailed,
|
FetchFailed,
|
||||||
Success,
|
Fetched
|
||||||
Failure,
|
|
||||||
Missing
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Source {
|
enum Source {
|
||||||
|
@ -35,24 +34,24 @@ enum Source {
|
||||||
|
|
||||||
export type Confirmations = number | "max";
|
export type Confirmations = number | "max";
|
||||||
|
|
||||||
export interface Transaction {
|
export interface TransactionStatus {
|
||||||
id: number;
|
|
||||||
status: Status;
|
|
||||||
source: Source;
|
|
||||||
slot?: number;
|
|
||||||
confirmations?: Confirmations;
|
|
||||||
signature: TransactionSignature;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Selected {
|
|
||||||
slot: number;
|
slot: number;
|
||||||
signature: TransactionSignature;
|
result: SignatureResult;
|
||||||
|
confirmations: Confirmations;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Transactions = { [signature: string]: Transaction };
|
export interface TransactionState {
|
||||||
|
id: number;
|
||||||
|
source: Source;
|
||||||
|
fetchStatus: FetchStatus;
|
||||||
|
signature: TransactionSignature;
|
||||||
|
transactionStatus?: TransactionStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Transactions = { [signature: string]: TransactionState };
|
||||||
interface State {
|
interface State {
|
||||||
idCounter: number;
|
idCounter: number;
|
||||||
selected?: Selected;
|
selected?: TransactionSignature;
|
||||||
transactions: Transactions;
|
transactions: Transactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,9 +74,8 @@ interface DeselectTransaction {
|
||||||
interface UpdateStatus {
|
interface UpdateStatus {
|
||||||
type: ActionType.UpdateStatus;
|
type: ActionType.UpdateStatus;
|
||||||
signature: TransactionSignature;
|
signature: TransactionSignature;
|
||||||
status: Status;
|
fetchStatus: FetchStatus;
|
||||||
slot?: number;
|
transactionStatus?: TransactionStatus;
|
||||||
confirmations?: Confirmations;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InputSignature {
|
interface InputSignature {
|
||||||
|
@ -90,6 +88,7 @@ type Action =
|
||||||
| InputSignature
|
| InputSignature
|
||||||
| SelectTransaction
|
| SelectTransaction
|
||||||
| DeselectTransaction;
|
| DeselectTransaction;
|
||||||
|
|
||||||
type Dispatch = (action: Action) => void;
|
type Dispatch = (action: Action) => void;
|
||||||
|
|
||||||
function reducer(state: State, action: Action): State {
|
function reducer(state: State, action: Action): State {
|
||||||
|
@ -99,37 +98,33 @@ function reducer(state: State, action: Action): State {
|
||||||
}
|
}
|
||||||
case ActionType.Select: {
|
case ActionType.Select: {
|
||||||
const tx = state.transactions[action.signature];
|
const tx = state.transactions[action.signature];
|
||||||
if (!tx.slot) return state;
|
return { ...state, selected: tx.signature };
|
||||||
const selected = {
|
|
||||||
slot: tx.slot,
|
|
||||||
signature: tx.signature
|
|
||||||
};
|
|
||||||
return { ...state, selected };
|
|
||||||
}
|
}
|
||||||
case ActionType.InputSignature: {
|
case ActionType.InputSignature: {
|
||||||
if (!!state.transactions[action.signature]) return state;
|
if (!!state.transactions[action.signature]) return state;
|
||||||
|
|
||||||
const idCounter = state.idCounter + 1;
|
const nextId = state.idCounter + 1;
|
||||||
const transactions = {
|
const transactions = {
|
||||||
...state.transactions,
|
...state.transactions,
|
||||||
[action.signature]: {
|
[action.signature]: {
|
||||||
id: idCounter,
|
id: nextId,
|
||||||
status: Status.Checking,
|
|
||||||
source: Source.Input,
|
source: Source.Input,
|
||||||
signature: action.signature
|
signature: action.signature,
|
||||||
|
fetchStatus: FetchStatus.Fetching
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return { ...state, transactions, idCounter };
|
return { ...state, transactions, idCounter: nextId };
|
||||||
}
|
}
|
||||||
case ActionType.UpdateStatus: {
|
case ActionType.UpdateStatus: {
|
||||||
let transaction = state.transactions[action.signature];
|
let transaction = state.transactions[action.signature];
|
||||||
if (transaction) {
|
if (transaction) {
|
||||||
transaction = {
|
transaction = {
|
||||||
...transaction,
|
...transaction,
|
||||||
status: action.status,
|
fetchStatus: action.fetchStatus
|
||||||
slot: action.slot,
|
|
||||||
confirmations: action.confirmations
|
|
||||||
};
|
};
|
||||||
|
if (action.transactionStatus) {
|
||||||
|
transaction.transactionStatus = action.transactionStatus;
|
||||||
|
}
|
||||||
const transactions = {
|
const transactions = {
|
||||||
...state.transactions,
|
...state.transactions,
|
||||||
[action.signature]: transaction
|
[action.signature]: transaction
|
||||||
|
@ -171,13 +166,14 @@ function initState(): State {
|
||||||
const transactions = signatures.reduce(
|
const transactions = signatures.reduce(
|
||||||
(transactions: Transactions, signature) => {
|
(transactions: Transactions, signature) => {
|
||||||
if (!!transactions[signature]) return transactions;
|
if (!!transactions[signature]) return transactions;
|
||||||
idCounter++;
|
const nextId = idCounter + 1;
|
||||||
transactions[signature] = {
|
transactions[signature] = {
|
||||||
id: idCounter,
|
id: nextId,
|
||||||
|
source: Source.Url,
|
||||||
signature,
|
signature,
|
||||||
status: Status.Checking,
|
fetchStatus: FetchStatus.Fetching
|
||||||
source: Source.Url
|
|
||||||
};
|
};
|
||||||
|
idCounter++;
|
||||||
return transactions;
|
return transactions;
|
||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
|
@ -269,44 +265,41 @@ export async function checkTransactionStatus(
|
||||||
) {
|
) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ActionType.UpdateStatus,
|
type: ActionType.UpdateStatus,
|
||||||
status: Status.Checking,
|
signature,
|
||||||
signature
|
fetchStatus: FetchStatus.Fetching
|
||||||
});
|
});
|
||||||
|
|
||||||
let status;
|
let fetchStatus;
|
||||||
let slot;
|
let transactionStatus: TransactionStatus | undefined;
|
||||||
let confirmations: Confirmations | undefined;
|
|
||||||
try {
|
try {
|
||||||
const { value } = await new Connection(url).getSignatureStatus(signature, {
|
const { value } = await new Connection(url).getSignatureStatus(signature, {
|
||||||
searchTransactionHistory: true
|
searchTransactionHistory: true
|
||||||
});
|
});
|
||||||
|
|
||||||
if (value === null) {
|
if (value !== null) {
|
||||||
status = Status.Missing;
|
let confirmations: Confirmations;
|
||||||
} else {
|
|
||||||
slot = value.slot;
|
|
||||||
if (typeof value.confirmations === "number") {
|
if (typeof value.confirmations === "number") {
|
||||||
confirmations = value.confirmations;
|
confirmations = value.confirmations;
|
||||||
} else {
|
} else {
|
||||||
confirmations = "max";
|
confirmations = "max";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.err) {
|
transactionStatus = {
|
||||||
status = Status.Failure;
|
slot: value.slot,
|
||||||
} else {
|
confirmations,
|
||||||
status = Status.Success;
|
result: { err: value.err }
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
fetchStatus = FetchStatus.Fetched;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to check transaction status", error);
|
console.error("Failed to fetch transaction status", error);
|
||||||
status = Status.CheckFailed;
|
fetchStatus = FetchStatus.FetchFailed;
|
||||||
}
|
}
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ActionType.UpdateStatus,
|
type: ActionType.UpdateStatus,
|
||||||
status,
|
signature,
|
||||||
slot,
|
fetchStatus,
|
||||||
confirmations,
|
transactionStatus
|
||||||
signature
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue