import { Play } from 'phosphor-react' import { FC, useCallback, useEffect } from 'react' import { useSnapshot } from 'valtio' import state from '../../state' import { defaultTransactionType, getTxFields, modifyTxState, prepareState, prepareTransaction, SelectOption, TransactionState } from '../../state/transactions' import { sendTransaction } from '../../state/actions' import Box from '../Box' import Button from '../Button' import Flex from '../Flex' import { TxJson } from './json' import { TxUI } from './ui' import { default as _estimateFee } from '../../utils/estimateFee' import toast from 'react-hot-toast' import { combineFlags, extractFlags, transactionFlags } from '../../state/constants/flags' import { SetHookData, toHex } from '../../utils/setHook' export interface TransactionProps { header: string state: TransactionState } const Transaction: FC = ({ header, state: txState, ...props }) => { const { accounts, editorSettings } = useSnapshot(state) const { selectedAccount, selectedTransaction, txIsDisabled, txIsLoading, viewType, editorValue } = txState const setState = useCallback( (pTx?: Partial) => { return modifyTxState(header, pTx) }, [header] ) const prepareOptions = useCallback( (state: Partial = txState) => { const { selectedTransaction, selectedDestAccount, selectedAccount, txFields, selectedFlags, hookParameters } = state const TransactionType = selectedTransaction?.value || null const Destination = selectedDestAccount?.value || txFields?.Destination const Account = selectedAccount?.value || null const Flags = combineFlags(selectedFlags?.map(flag => flag.value)) || txFields?.Flags const HookParameters = Object.entries(hookParameters || {}).reduce< SetHookData['HookParameters'] >((acc, [_, { label, value }]) => { return acc.concat({ HookParameter: { HookParameterName: toHex(label), HookParameterValue: toHex(value) } }) }, []) return prepareTransaction({ ...txFields, HookParameters, Flags, TransactionType, Destination, Account }) }, [txState] ) useEffect(() => { const transactionType = selectedTransaction?.value const account = selectedAccount?.value if (!account || !transactionType || txIsLoading) { setState({ txIsDisabled: true }) } else { setState({ txIsDisabled: false }) } }, [selectedAccount?.value, selectedTransaction?.value, setState, txIsLoading]) const submitTest = useCallback(async () => { let st: TransactionState | undefined const tt = txState.selectedTransaction?.value if (viewType === 'json') { // save the editor state first const pst = prepareState(editorValue || '', tt) if (!pst) return st = setState(pst) } const account = accounts.find(acc => acc.address === selectedAccount?.value) if (txIsDisabled) return setState({ txIsLoading: true }) const logPrefix = header ? `${header.split('.')[0]}: ` : undefined try { if (!account) { throw Error('Account must be selected from imported accounts!') } const options = prepareOptions(st) const fields = getTxFields(options.TransactionType) if (fields.Destination && !options.Destination) { throw Error('Destination account is required!') } await sendTransaction(account, options, { logPrefix }) } catch (error) { console.error(error) if (error instanceof Error) { state.transactionLogs.push({ type: 'error', message: `${logPrefix}${error.message}` }) } } setState({ txIsLoading: false }) }, [ viewType, accounts, txIsDisabled, setState, header, editorValue, txState, selectedAccount?.value, prepareOptions ]) const getJsonString = useCallback( (state?: Partial) => JSON.stringify(prepareOptions?.(state) || {}, null, editorSettings.tabSize), [editorSettings.tabSize, prepareOptions] ) const resetState = useCallback( (transactionType: SelectOption | undefined = defaultTransactionType) => { const fields = getTxFields(transactionType?.value) const nwState: Partial = { viewType, selectedTransaction: transactionType } if (fields.Destination !== undefined) { nwState.selectedDestAccount = null fields.Destination = '' } else { fields.Destination = undefined } if (transactionType?.value && transactionFlags[transactionType?.value] && fields.Flags) { nwState.selectedFlags = extractFlags(transactionType.value, fields.Flags) fields.Flags = undefined } nwState.txFields = fields const state = modifyTxState(header, nwState, { replaceState: true }) const editorValue = getJsonString(state) return setState({ editorValue }) }, [getJsonString, header, setState, viewType] ) const estimateFee = useCallback( async (st?: TransactionState, opts?: { silent?: boolean }) => { const state = st || txState const ptx = prepareOptions(state) const account = accounts.find(acc => acc.address === state.selectedAccount?.value) if (!account) { if (!opts?.silent) { toast.error('Please select account from the list.') } return } ptx.Account = account.address ptx.Sequence = account.sequence const res = await _estimateFee(ptx, account, opts) const fee = res?.base_fee setState({ estimatedFee: fee }) return fee }, [accounts, prepareOptions, setState, txState] ) return ( {viewType === 'json' ? ( ) : ( )} ) } export default Transaction