227 lines
7.7 KiB
TypeScript
227 lines
7.7 KiB
TypeScript
import produce from 'immer';
|
|
import type { Transaction } from './connectors';
|
|
import {
|
|
isWithdrawTransaction,
|
|
isOrderSubmissionTransaction,
|
|
isOrderCancellationTransaction,
|
|
isOrderAmendmentTransaction,
|
|
isBatchMarketInstructionsTransaction,
|
|
isTransferTransaction,
|
|
isStopOrdersSubmissionTransaction,
|
|
isStopOrdersCancellationTransaction,
|
|
} from './connectors';
|
|
import { determineId } from './utils';
|
|
|
|
import { create } from 'zustand';
|
|
import type {
|
|
TransactionEventFieldsFragment,
|
|
WithdrawalBusEventFieldsFragment,
|
|
OrderTxUpdateFieldsFragment,
|
|
} from './__generated__/TransactionResult';
|
|
|
|
import type { WithdrawalApprovalQuery } from './__generated__/WithdrawalApproval';
|
|
import { subscribeWithSelector } from 'zustand/middleware';
|
|
import type { VegaTxState } from './types';
|
|
import { VegaTxStatus } from './types';
|
|
|
|
export interface VegaStoredTxState extends VegaTxState {
|
|
id: number;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
body: Transaction;
|
|
transactionResult?: TransactionEventFieldsFragment;
|
|
withdrawal?: WithdrawalBusEventFieldsFragment;
|
|
withdrawalApproval?: WithdrawalApprovalQuery['erc20WithdrawalApproval'];
|
|
order?: OrderTxUpdateFieldsFragment;
|
|
}
|
|
|
|
export interface VegaTransactionStore {
|
|
transactions: (VegaStoredTxState | undefined)[];
|
|
create: (tx: Transaction, order?: OrderTxUpdateFieldsFragment) => number;
|
|
update: (
|
|
index: number,
|
|
update: Partial<
|
|
Pick<VegaStoredTxState, 'status' | 'txHash' | 'signature' | 'error'>
|
|
>
|
|
) => void;
|
|
dismiss: (index: number) => void;
|
|
delete: (index: number) => void;
|
|
updateWithdrawal: (
|
|
withdrawal: NonNullable<VegaStoredTxState['withdrawal']>,
|
|
withdrawalApproval: NonNullable<VegaStoredTxState['withdrawalApproval']>
|
|
) => void;
|
|
updateOrder: (order: OrderTxUpdateFieldsFragment) => void;
|
|
updateTransactionResult: (
|
|
transactionResult: TransactionEventFieldsFragment
|
|
) => void;
|
|
}
|
|
|
|
export const useVegaTransactionStore = create<VegaTransactionStore>()(
|
|
subscribeWithSelector((set, get) => ({
|
|
transactions: [] as VegaStoredTxState[],
|
|
create: (body: Transaction, order?: OrderTxUpdateFieldsFragment) => {
|
|
const transactions = get().transactions;
|
|
const now = new Date();
|
|
const transaction: VegaStoredTxState = {
|
|
id: transactions.length,
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
body,
|
|
error: null,
|
|
txHash: null,
|
|
signature: null,
|
|
status: VegaTxStatus.Requested,
|
|
dialogOpen: true,
|
|
order,
|
|
};
|
|
set({ transactions: transactions.concat(transaction) });
|
|
|
|
return transaction.id;
|
|
},
|
|
update: (index: number, update: Partial<VegaStoredTxState>) => {
|
|
set(
|
|
produce((state: VegaTransactionStore) => {
|
|
const transaction = state.transactions[index];
|
|
if (transaction) {
|
|
Object.assign(transaction, update);
|
|
transaction.dialogOpen = true;
|
|
transaction.updatedAt = new Date();
|
|
}
|
|
})
|
|
);
|
|
},
|
|
dismiss: (index: number) => {
|
|
set(
|
|
produce((state: VegaTransactionStore) => {
|
|
const transaction = state.transactions[index];
|
|
if (transaction) {
|
|
transaction.dialogOpen = false;
|
|
transaction.updatedAt = new Date();
|
|
}
|
|
})
|
|
);
|
|
},
|
|
delete: (index: number) => {
|
|
set(
|
|
produce((state: VegaTransactionStore) => {
|
|
delete state.transactions[index];
|
|
})
|
|
);
|
|
},
|
|
updateWithdrawal: (
|
|
withdrawal: NonNullable<VegaStoredTxState['withdrawal']>,
|
|
withdrawalApproval: NonNullable<VegaStoredTxState['withdrawalApproval']>
|
|
) => {
|
|
set(
|
|
produce((state: VegaTransactionStore) => {
|
|
const transaction = state.transactions.find(
|
|
(transaction) =>
|
|
transaction &&
|
|
transaction.status === VegaTxStatus.Pending &&
|
|
transaction.signature &&
|
|
isWithdrawTransaction(transaction?.body) &&
|
|
withdrawal.id === determineId(transaction.signature)
|
|
);
|
|
if (transaction) {
|
|
transaction.withdrawal = withdrawal;
|
|
transaction.withdrawalApproval = withdrawalApproval;
|
|
transaction.status = VegaTxStatus.Complete;
|
|
transaction.dialogOpen = true;
|
|
transaction.updatedAt = new Date();
|
|
}
|
|
})
|
|
);
|
|
},
|
|
updateOrder: (order) => {
|
|
set(
|
|
produce((state: VegaTransactionStore) => {
|
|
const transaction = state.transactions.find((transaction) => {
|
|
if (!transaction || transaction.status !== VegaTxStatus.Pending) {
|
|
return false;
|
|
}
|
|
if (
|
|
isOrderSubmissionTransaction(transaction?.body) &&
|
|
transaction.signature &&
|
|
order.id === determineId(transaction.signature)
|
|
) {
|
|
return true;
|
|
}
|
|
if (
|
|
isOrderCancellationTransaction(transaction?.body) &&
|
|
order.id === transaction.body.orderCancellation.orderId
|
|
) {
|
|
return true;
|
|
}
|
|
if (
|
|
isOrderAmendmentTransaction(transaction?.body) &&
|
|
order.id === transaction.body.orderAmendment.orderId
|
|
) {
|
|
return true;
|
|
}
|
|
if (
|
|
isBatchMarketInstructionsTransaction(transaction?.body) &&
|
|
transaction.signature &&
|
|
order.id === determineId(transaction.signature)
|
|
) {
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
if (transaction) {
|
|
// TODO: handle multiple orders from batch market instructions
|
|
// Note: If multiple orders are submitted the first order ID is determined by hashing the signature of the transaction
|
|
// (see determineId function). For each subsequent order's ID, a hash of the previous orders ID is used
|
|
transaction.order =
|
|
isOrderAmendmentTransaction(transaction?.body) &&
|
|
transaction.order
|
|
? transaction.order // the transaction.order would be the original order amended
|
|
: order;
|
|
transaction.status = VegaTxStatus.Complete;
|
|
transaction.dialogOpen = true;
|
|
transaction.updatedAt = new Date();
|
|
}
|
|
})
|
|
);
|
|
},
|
|
updateTransactionResult: (
|
|
transactionResult: TransactionEventFieldsFragment
|
|
) => {
|
|
set(
|
|
produce((state: VegaTransactionStore) => {
|
|
const transaction = state.transactions.find(
|
|
(transaction) =>
|
|
transaction?.txHash &&
|
|
transaction.txHash.toLowerCase() ===
|
|
transactionResult.hash.toLowerCase()
|
|
);
|
|
if (transaction) {
|
|
transaction.transactionResult = transactionResult;
|
|
|
|
const isConfirmedOrderCancellation =
|
|
isOrderCancellationTransaction(transaction.body) &&
|
|
!transaction.body.orderCancellation.orderId;
|
|
const isConfirmedTransfer = isTransferTransaction(transaction.body);
|
|
const isConfirmedStopOrderCancellation =
|
|
isStopOrdersCancellationTransaction(transaction.body);
|
|
const isConfirmedStopOrderSubmission =
|
|
isStopOrdersSubmissionTransaction(transaction.body);
|
|
|
|
if (
|
|
(isConfirmedOrderCancellation ||
|
|
isConfirmedTransfer ||
|
|
isConfirmedStopOrderCancellation ||
|
|
isConfirmedStopOrderSubmission) &&
|
|
!transactionResult.error &&
|
|
transactionResult.status
|
|
) {
|
|
transaction.status = VegaTxStatus.Complete;
|
|
}
|
|
transaction.dialogOpen = true;
|
|
transaction.updatedAt = new Date();
|
|
}
|
|
})
|
|
);
|
|
},
|
|
}))
|
|
);
|