Merge branch 'main' into main
This commit is contained in:
		@@ -3,13 +3,11 @@ import { mauve, mauveDark, purple, purpleDark } from '@radix-ui/colors'
 | 
				
			|||||||
import { useTheme } from 'next-themes'
 | 
					import { useTheme } from 'next-themes'
 | 
				
			||||||
import { styled } from '../stitches.config'
 | 
					import { styled } from '../stitches.config'
 | 
				
			||||||
import dynamic from 'next/dynamic'
 | 
					import dynamic from 'next/dynamic'
 | 
				
			||||||
import type { Props } from 'react-select'
 | 
					import type { Props, StylesConfig } from 'react-select'
 | 
				
			||||||
const SelectInput = dynamic(() => import('react-select'), { ssr: false })
 | 
					const SelectInput = dynamic(() => import('react-select'), { ssr: false })
 | 
				
			||||||
 | 
					const CreatableSelectInput = dynamic(() => import('react-select/creatable'), { ssr: false })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// eslint-disable-next-line react/display-name
 | 
					const getColors = (isDark: boolean) => {
 | 
				
			||||||
const Select = forwardRef<any, Props>((props, ref) => {
 | 
					 | 
				
			||||||
  const { theme } = useTheme()
 | 
					 | 
				
			||||||
  const isDark = theme === 'dark'
 | 
					 | 
				
			||||||
  const colors: any = {
 | 
					  const colors: any = {
 | 
				
			||||||
    // primary: pink.pink9,
 | 
					    // primary: pink.pink9,
 | 
				
			||||||
    active: isDark ? purpleDark.purple9 : purple.purple9,
 | 
					    active: isDark ? purpleDark.purple9 : purple.purple9,
 | 
				
			||||||
@@ -30,15 +28,17 @@ const Select = forwardRef<any, Props>((props, ref) => {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  colors.outline = colors.background
 | 
					  colors.outline = colors.background
 | 
				
			||||||
  colors.selected = colors.secondary
 | 
					  colors.selected = colors.secondary
 | 
				
			||||||
  return (
 | 
					  return colors
 | 
				
			||||||
    <SelectInput
 | 
					}
 | 
				
			||||||
      ref={ref}
 | 
					
 | 
				
			||||||
      menuPosition={props.menuPosition || 'fixed'}
 | 
					const getStyles = (isDark: boolean) => {
 | 
				
			||||||
      styles={{
 | 
					  const colors = getColors(isDark)
 | 
				
			||||||
 | 
					  const styles: StylesConfig = {
 | 
				
			||||||
    container: provided => {
 | 
					    container: provided => {
 | 
				
			||||||
      return {
 | 
					      return {
 | 
				
			||||||
        ...provided,
 | 
					        ...provided,
 | 
				
			||||||
            position: 'relative'
 | 
					        position: 'relative',
 | 
				
			||||||
 | 
					        width: '100%'
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    singleValue: provided => ({
 | 
					    singleValue: provided => ({
 | 
				
			||||||
@@ -107,16 +107,57 @@ const Select = forwardRef<any, Props>((props, ref) => {
 | 
				
			|||||||
    dropdownIndicator: (provided, state) => {
 | 
					    dropdownIndicator: (provided, state) => {
 | 
				
			||||||
      return {
 | 
					      return {
 | 
				
			||||||
        ...provided,
 | 
					        ...provided,
 | 
				
			||||||
 | 
					        padding: 6,
 | 
				
			||||||
        color: state.isFocused ? colors.border : colors.secondary,
 | 
					        color: state.isFocused ? colors.border : colors.secondary,
 | 
				
			||||||
        ':hover': {
 | 
					        ':hover': {
 | 
				
			||||||
          color: colors.border
 | 
					          color: colors.border
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    clearIndicator: provided => {
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        ...provided,
 | 
				
			||||||
 | 
					        padding: 6,
 | 
				
			||||||
 | 
					        color: colors.secondary,
 | 
				
			||||||
 | 
					        ':hover': {
 | 
				
			||||||
 | 
					          color: colors.border
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }}
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return styles
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// eslint-disable-next-line react/display-name
 | 
				
			||||||
 | 
					const Select = forwardRef<any, Props>((props, ref) => {
 | 
				
			||||||
 | 
					  const { theme } = useTheme()
 | 
				
			||||||
 | 
					  const isDark = theme === 'dark'
 | 
				
			||||||
 | 
					  const styles = getStyles(isDark)
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <SelectInput
 | 
				
			||||||
 | 
					      ref={ref}
 | 
				
			||||||
 | 
					      menuPosition={props.menuPosition || 'fixed'}
 | 
				
			||||||
 | 
					      styles={styles}
 | 
				
			||||||
 | 
					      {...props}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// eslint-disable-next-line react/display-name
 | 
				
			||||||
 | 
					const Creatable = forwardRef<any, Props>((props, ref) => {
 | 
				
			||||||
 | 
					  const { theme } = useTheme()
 | 
				
			||||||
 | 
					  const isDark = theme === 'dark'
 | 
				
			||||||
 | 
					  const styles = getStyles(isDark)
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <CreatableSelectInput
 | 
				
			||||||
 | 
					      ref={ref}
 | 
				
			||||||
 | 
					      formatCreateLabel={label => `Enter "${label}"`}
 | 
				
			||||||
 | 
					      menuPosition={props.menuPosition || 'fixed'}
 | 
				
			||||||
 | 
					      styles={styles}
 | 
				
			||||||
      {...props}
 | 
					      {...props}
 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default styled(Select, {})
 | 
					export default styled(Select, {})
 | 
				
			||||||
 | 
					export const CreatableSelect = styled(Creatable, {})
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										71
									
								
								components/Sequence.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								components/Sequence.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
				
			|||||||
 | 
					import { FC, useCallback, useState } from 'react'
 | 
				
			||||||
 | 
					import state from '../state'
 | 
				
			||||||
 | 
					import { Flex, Input, Button } from '.'
 | 
				
			||||||
 | 
					import fetchAccountInfo from '../utils/accountInfo'
 | 
				
			||||||
 | 
					import { useSnapshot } from 'valtio'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface AccountSequenceProps {
 | 
				
			||||||
 | 
					  address?: string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					const AccountSequence: FC<AccountSequenceProps> = ({ address }) => {
 | 
				
			||||||
 | 
					  const { accounts } = useSnapshot(state)
 | 
				
			||||||
 | 
					  const account = accounts.find(acc => acc.address === address)
 | 
				
			||||||
 | 
					  const [isLoading, setIsLoading] = useState(false)
 | 
				
			||||||
 | 
					  const setSequence = useCallback(
 | 
				
			||||||
 | 
					    (sequence: number) => {
 | 
				
			||||||
 | 
					      const acc = state.accounts.find(acc => acc.address == address)
 | 
				
			||||||
 | 
					      if (!acc) return
 | 
				
			||||||
 | 
					      acc.sequence = sequence
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    [address]
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  const handleUpdateSequence = useCallback(
 | 
				
			||||||
 | 
					    async (silent?: boolean) => {
 | 
				
			||||||
 | 
					      if (!account) return
 | 
				
			||||||
 | 
					      setIsLoading(true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const info = await fetchAccountInfo(account.address, { silent })
 | 
				
			||||||
 | 
					      if (info) {
 | 
				
			||||||
 | 
					        setSequence(info.Sequence)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      setIsLoading(false)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    [account, setSequence]
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  const disabled = !account
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Flex row align="center" fluid>
 | 
				
			||||||
 | 
					      <Input
 | 
				
			||||||
 | 
					        placeholder="Account sequence"
 | 
				
			||||||
 | 
					        value={account?.sequence || ""}
 | 
				
			||||||
 | 
					        disabled={!account}
 | 
				
			||||||
 | 
					        type="number"
 | 
				
			||||||
 | 
					        readOnly={true}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <Button
 | 
				
			||||||
 | 
					        size="xs"
 | 
				
			||||||
 | 
					        variant="primary"
 | 
				
			||||||
 | 
					        type="button"
 | 
				
			||||||
 | 
					        outline
 | 
				
			||||||
 | 
					        disabled={disabled}
 | 
				
			||||||
 | 
					        isDisabled={disabled}
 | 
				
			||||||
 | 
					        isLoading={isLoading}
 | 
				
			||||||
 | 
					        css={{
 | 
				
			||||||
 | 
					          background: '$backgroundAlt',
 | 
				
			||||||
 | 
					          position: 'absolute',
 | 
				
			||||||
 | 
					          right: '$2',
 | 
				
			||||||
 | 
					          fontSize: '$xs',
 | 
				
			||||||
 | 
					          cursor: 'pointer',
 | 
				
			||||||
 | 
					          alignContent: 'center',
 | 
				
			||||||
 | 
					          display: 'flex'
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					        onClick={() => handleUpdateSequence()}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        Update
 | 
				
			||||||
 | 
					      </Button>
 | 
				
			||||||
 | 
					    </Flex>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default AccountSequence
 | 
				
			||||||
@@ -21,6 +21,7 @@ import { prepareDeployHookTx, sha256 } from '../state/actions/deployHook'
 | 
				
			|||||||
import estimateFee from '../utils/estimateFee'
 | 
					import estimateFee from '../utils/estimateFee'
 | 
				
			||||||
import { getParameters, getInvokeOptions, transactionOptions, SetHookData } from '../utils/setHook'
 | 
					import { getParameters, getInvokeOptions, transactionOptions, SetHookData } from '../utils/setHook'
 | 
				
			||||||
import { capitalize } from '../utils/helpers'
 | 
					import { capitalize } from '../utils/helpers'
 | 
				
			||||||
 | 
					import AccountSequence from './Sequence'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
 | 
					export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
 | 
				
			||||||
  ({ accountAddress }) => {
 | 
					  ({ accountAddress }) => {
 | 
				
			||||||
@@ -190,6 +191,10 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
 | 
				
			|||||||
                    onChange={(acc: any) => setSelectedAccount(acc)}
 | 
					                    onChange={(acc: any) => setSelectedAccount(acc)}
 | 
				
			||||||
                  />
 | 
					                  />
 | 
				
			||||||
                </Box>
 | 
					                </Box>
 | 
				
			||||||
 | 
					                <Box css={{ width: '100%', position: 'relative' }}>
 | 
				
			||||||
 | 
					                  <Label>Sequence</Label>
 | 
				
			||||||
 | 
					                  <AccountSequence address={selectedAccount?.value} />
 | 
				
			||||||
 | 
					                </Box>
 | 
				
			||||||
                <Box css={{ width: '100%' }}>
 | 
					                <Box css={{ width: '100%' }}>
 | 
				
			||||||
                  <Label>Invoke on transactions</Label>
 | 
					                  <Label>Invoke on transactions</Label>
 | 
				
			||||||
                  <Controller
 | 
					                  <Controller
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,7 @@ import { TxUI } from './ui'
 | 
				
			|||||||
import { default as _estimateFee } from '../../utils/estimateFee'
 | 
					import { default as _estimateFee } from '../../utils/estimateFee'
 | 
				
			||||||
import toast from 'react-hot-toast'
 | 
					import toast from 'react-hot-toast'
 | 
				
			||||||
import { combineFlags, extractFlags, transactionFlags } from '../../state/constants/flags'
 | 
					import { combineFlags, extractFlags, transactionFlags } from '../../state/constants/flags'
 | 
				
			||||||
 | 
					import { SetHookData, toHex } from '../../utils/setHook'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface TransactionProps {
 | 
					export interface TransactionProps {
 | 
				
			||||||
  header: string
 | 
					  header: string
 | 
				
			||||||
@@ -40,20 +41,40 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const prepareOptions = useCallback(
 | 
					  const prepareOptions = useCallback(
 | 
				
			||||||
    (state: Partial<TransactionState> = txState) => {
 | 
					    (state: Partial<TransactionState> = txState) => {
 | 
				
			||||||
      const { selectedTransaction, selectedDestAccount, selectedAccount, txFields, selectedFlags } =
 | 
					      const {
 | 
				
			||||||
        state
 | 
					        selectedTransaction,
 | 
				
			||||||
 | 
					        selectedAccount,
 | 
				
			||||||
 | 
					        txFields,
 | 
				
			||||||
 | 
					        selectedFlags,
 | 
				
			||||||
 | 
					        hookParameters,
 | 
				
			||||||
 | 
					        memos
 | 
				
			||||||
 | 
					      } = state
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const TransactionType = selectedTransaction?.value || null
 | 
					      const TransactionType = selectedTransaction?.value || null
 | 
				
			||||||
      const Destination = selectedDestAccount?.value || txFields?.Destination
 | 
					 | 
				
			||||||
      const Account = selectedAccount?.value || null
 | 
					      const Account = selectedAccount?.value || null
 | 
				
			||||||
      const Flags = combineFlags(selectedFlags?.map(flag => flag.value)) || txFields?.Flags
 | 
					      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: value }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      }, [])
 | 
				
			||||||
 | 
					      const Memos = memos
 | 
				
			||||||
 | 
					        ? Object.entries(memos).reduce<SetHookData['Memos']>((acc, [_, { format, data, type }]) => {
 | 
				
			||||||
 | 
					            return acc?.concat({
 | 
				
			||||||
 | 
					              Memo: { MemoData: data, MemoFormat: toHex(format), MemoType: toHex(type) }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					          }, [])
 | 
				
			||||||
 | 
					        : undefined
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return prepareTransaction({
 | 
					      return prepareTransaction({
 | 
				
			||||||
        ...txFields,
 | 
					        ...txFields,
 | 
				
			||||||
 | 
					        HookParameters,
 | 
				
			||||||
        Flags,
 | 
					        Flags,
 | 
				
			||||||
        TransactionType,
 | 
					        TransactionType,
 | 
				
			||||||
        Destination,
 | 
					        Account,
 | 
				
			||||||
        Account
 | 
					        Memos
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    [txState]
 | 
					    [txState]
 | 
				
			||||||
@@ -69,15 +90,29 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }, [selectedAccount?.value, selectedTransaction?.value, setState, txIsLoading])
 | 
					  }, [selectedAccount?.value, selectedTransaction?.value, setState, txIsLoading])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const getJsonString = useCallback(
 | 
				
			||||||
 | 
					    (state?: Partial<TransactionState>) =>
 | 
				
			||||||
 | 
					      JSON.stringify(prepareOptions?.(state) || {}, null, editorSettings.tabSize),
 | 
				
			||||||
 | 
					    [editorSettings.tabSize, prepareOptions]
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const saveEditorState = useCallback(
 | 
				
			||||||
 | 
					    (value: string = '', transactionType?: string) => {
 | 
				
			||||||
 | 
					      const pTx = prepareState(value, transactionType)
 | 
				
			||||||
 | 
					      if (pTx) {
 | 
				
			||||||
 | 
					        pTx.editorValue = getJsonString(pTx)
 | 
				
			||||||
 | 
					        return setState(pTx)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    [getJsonString, setState]
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const submitTest = useCallback(async () => {
 | 
					  const submitTest = useCallback(async () => {
 | 
				
			||||||
    let st: TransactionState | undefined
 | 
					    let st: TransactionState | undefined
 | 
				
			||||||
    const tt = txState.selectedTransaction?.value
 | 
					    const tt = txState.selectedTransaction?.value
 | 
				
			||||||
    if (viewType === 'json') {
 | 
					    if (viewType === 'json') {
 | 
				
			||||||
      // save the editor state first
 | 
					      st = saveEditorState(editorValue, tt)
 | 
				
			||||||
      const pst = prepareState(editorValue || '', tt)
 | 
					      if (!st) return
 | 
				
			||||||
      if (!pst) return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      st = setState(pst)
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const account = accounts.find(acc => acc.address === selectedAccount?.value)
 | 
					    const account = accounts.find(acc => acc.address === selectedAccount?.value)
 | 
				
			||||||
@@ -90,11 +125,12 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
 | 
				
			|||||||
        throw Error('Account must be selected from imported accounts!')
 | 
					        throw Error('Account must be selected from imported accounts!')
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      const options = prepareOptions(st)
 | 
					      const options = prepareOptions(st)
 | 
				
			||||||
 | 
					      // delete unnecessary fields
 | 
				
			||||||
      const fields = getTxFields(options.TransactionType)
 | 
					      Object.keys(options).forEach(field => {
 | 
				
			||||||
      if (fields.Destination && !options.Destination) {
 | 
					        if (!options[field]) {
 | 
				
			||||||
        throw Error('Destination account is required!')
 | 
					          delete options[field]
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await sendTransaction(account, options, { logPrefix })
 | 
					      await sendTransaction(account, options, { logPrefix })
 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
@@ -108,23 +144,18 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    setState({ txIsLoading: false })
 | 
					    setState({ txIsLoading: false })
 | 
				
			||||||
  }, [
 | 
					  }, [
 | 
				
			||||||
 | 
					    txState.selectedTransaction?.value,
 | 
				
			||||||
    viewType,
 | 
					    viewType,
 | 
				
			||||||
    accounts,
 | 
					    accounts,
 | 
				
			||||||
    txIsDisabled,
 | 
					    txIsDisabled,
 | 
				
			||||||
    setState,
 | 
					    setState,
 | 
				
			||||||
    header,
 | 
					    header,
 | 
				
			||||||
 | 
					    saveEditorState,
 | 
				
			||||||
    editorValue,
 | 
					    editorValue,
 | 
				
			||||||
    txState,
 | 
					 | 
				
			||||||
    selectedAccount?.value,
 | 
					    selectedAccount?.value,
 | 
				
			||||||
    prepareOptions
 | 
					    prepareOptions
 | 
				
			||||||
  ])
 | 
					  ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const getJsonString = useCallback(
 | 
					 | 
				
			||||||
    (state?: Partial<TransactionState>) =>
 | 
					 | 
				
			||||||
      JSON.stringify(prepareOptions?.(state) || {}, null, editorSettings.tabSize),
 | 
					 | 
				
			||||||
    [editorSettings.tabSize, prepareOptions]
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const resetState = useCallback(
 | 
					  const resetState = useCallback(
 | 
				
			||||||
    (transactionType: SelectOption | undefined = defaultTransactionType) => {
 | 
					    (transactionType: SelectOption | undefined = defaultTransactionType) => {
 | 
				
			||||||
      const fields = getTxFields(transactionType?.value)
 | 
					      const fields = getTxFields(transactionType?.value)
 | 
				
			||||||
@@ -134,13 +165,6 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
 | 
				
			|||||||
        selectedTransaction: transactionType
 | 
					        selectedTransaction: transactionType
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (fields.Destination !== undefined) {
 | 
					 | 
				
			||||||
        nwState.selectedDestAccount = null
 | 
					 | 
				
			||||||
        fields.Destination = ''
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        fields.Destination = undefined
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (transactionType?.value && transactionFlags[transactionType?.value] && fields.Flags) {
 | 
					      if (transactionType?.value && transactionFlags[transactionType?.value] && fields.Flags) {
 | 
				
			||||||
        nwState.selectedFlags = extractFlags(transactionType.value, fields.Flags)
 | 
					        nwState.selectedFlags = extractFlags(transactionType.value, fields.Flags)
 | 
				
			||||||
        fields.Flags = undefined
 | 
					        fields.Flags = undefined
 | 
				
			||||||
@@ -177,11 +201,21 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
 | 
				
			|||||||
    [accounts, prepareOptions, setState, txState]
 | 
					    [accounts, prepareOptions, setState, txState]
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const switchToJson = useCallback(() => {
 | 
				
			||||||
 | 
					    const editorValue = getJsonString()
 | 
				
			||||||
 | 
					    setState({ viewType: 'json', editorValue })
 | 
				
			||||||
 | 
					  }, [getJsonString, setState])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const switchToUI = useCallback(() => {
 | 
				
			||||||
 | 
					    setState({ viewType: 'ui' })
 | 
				
			||||||
 | 
					  }, [setState])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Box css={{ position: 'relative', height: 'calc(100% - 28px)' }} {...props}>
 | 
					    <Box css={{ position: 'relative', height: 'calc(100% - 28px)' }} {...props}>
 | 
				
			||||||
      {viewType === 'json' ? (
 | 
					      {viewType === 'json' ? (
 | 
				
			||||||
        <TxJson
 | 
					        <TxJson
 | 
				
			||||||
          getJsonString={getJsonString}
 | 
					          getJsonString={getJsonString}
 | 
				
			||||||
 | 
					          saveEditorState={saveEditorState}
 | 
				
			||||||
          header={header}
 | 
					          header={header}
 | 
				
			||||||
          state={txState}
 | 
					          state={txState}
 | 
				
			||||||
          setState={setState}
 | 
					          setState={setState}
 | 
				
			||||||
@@ -189,6 +223,7 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
 | 
				
			|||||||
        />
 | 
					        />
 | 
				
			||||||
      ) : (
 | 
					      ) : (
 | 
				
			||||||
        <TxUI
 | 
					        <TxUI
 | 
				
			||||||
 | 
					          switchToJson={switchToJson}
 | 
				
			||||||
          state={txState}
 | 
					          state={txState}
 | 
				
			||||||
          resetState={resetState}
 | 
					          resetState={resetState}
 | 
				
			||||||
          setState={setState}
 | 
					          setState={setState}
 | 
				
			||||||
@@ -209,8 +244,8 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
 | 
				
			|||||||
        <Button
 | 
					        <Button
 | 
				
			||||||
          onClick={() => {
 | 
					          onClick={() => {
 | 
				
			||||||
            if (viewType === 'ui') {
 | 
					            if (viewType === 'ui') {
 | 
				
			||||||
              setState({ viewType: 'json' })
 | 
					              switchToJson()
 | 
				
			||||||
            } else setState({ viewType: 'ui' })
 | 
					            } else switchToUI()
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
          outline
 | 
					          outline
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react'
 | 
					import { FC, useCallback, useEffect, useState } from 'react'
 | 
				
			||||||
import { useSnapshot } from 'valtio'
 | 
					import { useSnapshot } from 'valtio'
 | 
				
			||||||
import state, { prepareState, transactionsData, TransactionState } from '../../state'
 | 
					import state, { transactionsData, TransactionState } from '../../state'
 | 
				
			||||||
import Text from '../Text'
 | 
					import Text from '../Text'
 | 
				
			||||||
import { Flex, Link } from '..'
 | 
					import { Flex, Link } from '..'
 | 
				
			||||||
import { showAlert } from '../../state/actions/showAlert'
 | 
					import { showAlert } from '../../state/actions/showAlert'
 | 
				
			||||||
@@ -11,27 +11,27 @@ import Monaco from '../Monaco'
 | 
				
			|||||||
import type monaco from 'monaco-editor'
 | 
					import type monaco from 'monaco-editor'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface JsonProps {
 | 
					interface JsonProps {
 | 
				
			||||||
  getJsonString?: (state?: Partial<TransactionState>) => string
 | 
					  getJsonString: (st?: Partial<TransactionState>) => string
 | 
				
			||||||
 | 
					  saveEditorState: (val?: string, tt?: string) => TransactionState | undefined
 | 
				
			||||||
  header?: string
 | 
					  header?: string
 | 
				
			||||||
  setState: (pTx?: Partial<TransactionState> | undefined) => void
 | 
					  setState: (pTx?: Partial<TransactionState> | undefined) => void
 | 
				
			||||||
  state: TransactionState
 | 
					  state: TransactionState
 | 
				
			||||||
  estimateFee?: () => Promise<string | undefined>
 | 
					  estimateFee?: () => Promise<string | undefined>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const TxJson: FC<JsonProps> = ({ getJsonString, state: txState, header, setState }) => {
 | 
					export const TxJson: FC<JsonProps> = ({
 | 
				
			||||||
 | 
					  getJsonString,
 | 
				
			||||||
 | 
					  state: txState,
 | 
				
			||||||
 | 
					  header,
 | 
				
			||||||
 | 
					  setState,
 | 
				
			||||||
 | 
					  saveEditorState
 | 
				
			||||||
 | 
					}) => {
 | 
				
			||||||
  const { editorSettings, accounts } = useSnapshot(state)
 | 
					  const { editorSettings, accounts } = useSnapshot(state)
 | 
				
			||||||
  const { editorValue, estimatedFee } = txState
 | 
					  const { editorValue, estimatedFee, editorIsSaved } = txState
 | 
				
			||||||
  const [currTxType, setCurrTxType] = useState<string | undefined>(
 | 
					  const [currTxType, setCurrTxType] = useState<string | undefined>(
 | 
				
			||||||
    txState.selectedTransaction?.value
 | 
					    txState.selectedTransaction?.value
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					 | 
				
			||||||
    setState({
 | 
					 | 
				
			||||||
      editorValue: getJsonString?.()
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    // eslint-disable-next-line react-hooks/exhaustive-deps
 | 
					 | 
				
			||||||
  }, [])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    const parsed = parseJSON(editorValue)
 | 
					    const parsed = parseJSON(editorValue)
 | 
				
			||||||
    if (!parsed) return
 | 
					    if (!parsed) return
 | 
				
			||||||
@@ -44,29 +44,19 @@ export const TxJson: FC<JsonProps> = ({ getJsonString, state: txState, header, s
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }, [editorValue])
 | 
					  }, [editorValue])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const saveState = (value: string, transactionType?: string) => {
 | 
					 | 
				
			||||||
    const tx = prepareState(value, transactionType)
 | 
					 | 
				
			||||||
    if (tx) {
 | 
					 | 
				
			||||||
      setState(tx)
 | 
					 | 
				
			||||||
      setState({
 | 
					 | 
				
			||||||
        editorValue: getJsonString?.(tx)
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const discardChanges = () => {
 | 
					  const discardChanges = () => {
 | 
				
			||||||
    showAlert('Confirm', {
 | 
					    showAlert('Confirm', {
 | 
				
			||||||
      body: 'Are you sure to discard these changes?',
 | 
					      body: 'Are you sure to discard these changes?',
 | 
				
			||||||
      confirmText: 'Yes',
 | 
					      confirmText: 'Yes',
 | 
				
			||||||
      onCancel: () => {},
 | 
					      onCancel: () => {},
 | 
				
			||||||
      onConfirm: () => setState({ editorValue: getJsonString?.() })
 | 
					      onConfirm: () => setState({ editorValue: getJsonString() })
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const onExit = (value: string) => {
 | 
					  const onExit = (value: string) => {
 | 
				
			||||||
    const options = parseJSON(value)
 | 
					    const options = parseJSON(value)
 | 
				
			||||||
    if (options) {
 | 
					    if (options) {
 | 
				
			||||||
      saveState(value, currTxType)
 | 
					      saveEditorState(value, currTxType)
 | 
				
			||||||
      return
 | 
					      return
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    showAlert('Error!', {
 | 
					    showAlert('Error!', {
 | 
				
			||||||
@@ -163,8 +153,6 @@ export const TxJson: FC<JsonProps> = ({ getJsonString, state: txState, header, s
 | 
				
			|||||||
    })
 | 
					    })
 | 
				
			||||||
  }, [getSchemas, monacoInst])
 | 
					  }, [getSchemas, monacoInst])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const hasUnsaved = useMemo(() => editorValue !== getJsonString?.(), [editorValue, getJsonString])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Monaco
 | 
					    <Monaco
 | 
				
			||||||
      rootProps={{
 | 
					      rootProps={{
 | 
				
			||||||
@@ -174,7 +162,7 @@ export const TxJson: FC<JsonProps> = ({ getJsonString, state: txState, header, s
 | 
				
			|||||||
      id={header}
 | 
					      id={header}
 | 
				
			||||||
      height="100%"
 | 
					      height="100%"
 | 
				
			||||||
      value={editorValue}
 | 
					      value={editorValue}
 | 
				
			||||||
      onChange={val => setState({ editorValue: val })}
 | 
					      onChange={val => setState({ editorValue: val, editorIsSaved: false })}
 | 
				
			||||||
      onMount={(editor, monaco) => {
 | 
					      onMount={(editor, monaco) => {
 | 
				
			||||||
        editor.updateOptions({
 | 
					        editor.updateOptions({
 | 
				
			||||||
          minimap: { enabled: false },
 | 
					          minimap: { enabled: false },
 | 
				
			||||||
@@ -190,12 +178,12 @@ export const TxJson: FC<JsonProps> = ({ getJsonString, state: txState, header, s
 | 
				
			|||||||
        model?.onWillDispose(() => onExit(model.getValue()))
 | 
					        model?.onWillDispose(() => onExit(model.getValue()))
 | 
				
			||||||
      }}
 | 
					      }}
 | 
				
			||||||
      overlay={
 | 
					      overlay={
 | 
				
			||||||
        hasUnsaved ? (
 | 
					        !editorIsSaved ? (
 | 
				
			||||||
          <Flex row align="center" css={{ fontSize: '$xs', color: '$textMuted', ml: 'auto' }}>
 | 
					          <Flex row align="center" css={{ fontSize: '$xs', color: '$textMuted', ml: 'auto' }}>
 | 
				
			||||||
            <Text muted small>
 | 
					            <Text muted small>
 | 
				
			||||||
              This file has unsaved changes.
 | 
					              This file has unsaved changes.
 | 
				
			||||||
            </Text>
 | 
					            </Text>
 | 
				
			||||||
            <Link css={{ ml: '$1' }} onClick={() => saveState(editorValue || '', currTxType)}>
 | 
					            <Link css={{ ml: '$1' }} onClick={() => saveEditorState(editorValue, currTxType)}>
 | 
				
			||||||
              save
 | 
					              save
 | 
				
			||||||
            </Link>
 | 
					            </Link>
 | 
				
			||||||
            <Link css={{ ml: '$1' }} onClick={discardChanges}>
 | 
					            <Link css={{ ml: '$1' }} onClick={discardChanges}>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,34 +1,43 @@
 | 
				
			|||||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react'
 | 
					import { FC, ReactNode, useCallback, useEffect, useState } from 'react'
 | 
				
			||||||
import Container from '../Container'
 | 
					import Container from '../Container'
 | 
				
			||||||
import Flex from '../Flex'
 | 
					import Flex from '../Flex'
 | 
				
			||||||
import Input from '../Input'
 | 
					import Input from '../Input'
 | 
				
			||||||
import Select from '../Select'
 | 
					import Select, { CreatableSelect } from '../Select'
 | 
				
			||||||
import Text from '../Text'
 | 
					import Text from '../Text'
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  SelectOption,
 | 
					  SelectOption,
 | 
				
			||||||
  TransactionState,
 | 
					  TransactionState,
 | 
				
			||||||
  transactionsOptions,
 | 
					  transactionsOptions,
 | 
				
			||||||
  TxFields,
 | 
					  TxFields,
 | 
				
			||||||
  getTxFields,
 | 
					 | 
				
			||||||
  defaultTransactionType
 | 
					  defaultTransactionType
 | 
				
			||||||
} from '../../state/transactions'
 | 
					} from '../../state/transactions'
 | 
				
			||||||
import { useSnapshot } from 'valtio'
 | 
					import { useSnapshot } from 'valtio'
 | 
				
			||||||
import state from '../../state'
 | 
					import state from '../../state'
 | 
				
			||||||
import { streamState } from '../DebugStream'
 | 
					import { streamState } from '../DebugStream'
 | 
				
			||||||
import { Button } from '..'
 | 
					import { Box, Button } from '..'
 | 
				
			||||||
import Textarea from '../Textarea'
 | 
					import Textarea from '../Textarea'
 | 
				
			||||||
import { getFlags } from '../../state/constants/flags'
 | 
					import { getFlags } from '../../state/constants/flags'
 | 
				
			||||||
 | 
					import { Plus, Trash } from 'phosphor-react'
 | 
				
			||||||
 | 
					import AccountSequence from '../Sequence'
 | 
				
			||||||
 | 
					import { capitalize, typeIs } from '../../utils/helpers'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface UIProps {
 | 
					interface UIProps {
 | 
				
			||||||
  setState: (pTx?: Partial<TransactionState> | undefined) => TransactionState | undefined
 | 
					  setState: (pTx?: Partial<TransactionState> | undefined) => TransactionState | undefined
 | 
				
			||||||
  resetState: (tt?: SelectOption) => TransactionState | undefined
 | 
					  resetState: (tt?: SelectOption) => TransactionState | undefined
 | 
				
			||||||
  state: TransactionState
 | 
					  state: TransactionState
 | 
				
			||||||
  estimateFee?: (...arg: any) => Promise<string | undefined>
 | 
					  estimateFee?: (...arg: any) => Promise<string | undefined>
 | 
				
			||||||
 | 
					  switchToJson: () => void
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estimateFee }) => {
 | 
					export const TxUI: FC<UIProps> = ({
 | 
				
			||||||
 | 
					  state: txState,
 | 
				
			||||||
 | 
					  setState,
 | 
				
			||||||
 | 
					  resetState,
 | 
				
			||||||
 | 
					  estimateFee,
 | 
				
			||||||
 | 
					  switchToJson
 | 
				
			||||||
 | 
					}) => {
 | 
				
			||||||
  const { accounts } = useSnapshot(state)
 | 
					  const { accounts } = useSnapshot(state)
 | 
				
			||||||
  const { selectedAccount, selectedDestAccount, selectedTransaction, txFields, selectedFlags } =
 | 
					  const { selectedAccount, selectedTransaction, txFields, selectedFlags, hookParameters, memos } =
 | 
				
			||||||
    txState
 | 
					    txState
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const accountOptions: SelectOption[] = accounts.map(acc => ({
 | 
					  const accountOptions: SelectOption[] = accounts.map(acc => ({
 | 
				
			||||||
@@ -36,13 +45,6 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estima
 | 
				
			|||||||
    value: acc.address
 | 
					    value: acc.address
 | 
				
			||||||
  }))
 | 
					  }))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const destAccountOptions: SelectOption[] = accounts
 | 
					 | 
				
			||||||
    .map(acc => ({
 | 
					 | 
				
			||||||
      label: acc.name,
 | 
					 | 
				
			||||||
      value: acc.address
 | 
					 | 
				
			||||||
    }))
 | 
					 | 
				
			||||||
    .filter(acc => acc.value !== selectedAccount?.value)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const flagsOptions: SelectOption[] = Object.entries(
 | 
					  const flagsOptions: SelectOption[] = Object.entries(
 | 
				
			||||||
    getFlags(selectedTransaction?.value) || {}
 | 
					    getFlags(selectedTransaction?.value) || {}
 | 
				
			||||||
  ).map(([label, value]) => ({
 | 
					  ).map(([label, value]) => ({
 | 
				
			||||||
@@ -71,6 +73,22 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estima
 | 
				
			|||||||
    [setState, txFields]
 | 
					    [setState, txFields]
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const setRawField = useCallback(
 | 
				
			||||||
 | 
					    (field: keyof TxFields, type: string, value: any) => {
 | 
				
			||||||
 | 
					      // TODO $type should be a narrowed type
 | 
				
			||||||
 | 
					      setState({
 | 
				
			||||||
 | 
					        txFields: {
 | 
				
			||||||
 | 
					          ...txFields,
 | 
				
			||||||
 | 
					          [field]: {
 | 
				
			||||||
 | 
					            $type: type,
 | 
				
			||||||
 | 
					            $value: value
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    [setState, txFields]
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleEstimateFee = useCallback(
 | 
					  const handleEstimateFee = useCallback(
 | 
				
			||||||
    async (state?: TransactionState, silent?: boolean) => {
 | 
					    async (state?: TransactionState, silent?: boolean) => {
 | 
				
			||||||
      setFeeLoading(true)
 | 
					      setFeeLoading(true)
 | 
				
			||||||
@@ -94,8 +112,6 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estima
 | 
				
			|||||||
    [handleEstimateFee, resetState, setState]
 | 
					    [handleEstimateFee, resetState, setState]
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const switchToJson = () => setState({ viewType: 'json' })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // default tx
 | 
					  // default tx
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    if (selectedTransaction?.value) return
 | 
					    if (selectedTransaction?.value) return
 | 
				
			||||||
@@ -105,22 +121,23 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estima
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }, [handleChangeTxType, selectedTransaction?.value])
 | 
					  }, [handleChangeTxType, selectedTransaction?.value])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const fields = useMemo(
 | 
					  const richFields = ['TransactionType', 'Account', 'HookParameters', 'Memos']
 | 
				
			||||||
    () => getTxFields(selectedTransaction?.value),
 | 
					 | 
				
			||||||
    [selectedTransaction?.value]
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const richFields = ['TransactionType', 'Account']
 | 
					 | 
				
			||||||
  if (fields.Destination !== undefined) {
 | 
					 | 
				
			||||||
    richFields.push('Destination')
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (flagsOptions.length) {
 | 
					  if (flagsOptions.length) {
 | 
				
			||||||
    richFields.push('Flags')
 | 
					    richFields.push('Flags')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const otherFields = Object.keys(txFields).filter(k => !richFields.includes(k)) as [keyof TxFields]
 | 
					  const otherFields = Object.keys(txFields).filter(k => !richFields.includes(k)) as [keyof TxFields]
 | 
				
			||||||
 | 
					  const amountOptions = [
 | 
				
			||||||
 | 
					    { label: 'XRP', value: 'xrp' },
 | 
				
			||||||
 | 
					    { label: 'Token', value: 'token' }
 | 
				
			||||||
 | 
					  ] as const
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const defaultTokenAmount = {
 | 
				
			||||||
 | 
					    value: '0',
 | 
				
			||||||
 | 
					    currency: '',
 | 
				
			||||||
 | 
					    issuer: ''
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Container
 | 
					    <Container
 | 
				
			||||||
      css={{
 | 
					      css={{
 | 
				
			||||||
@@ -130,94 +147,32 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estima
 | 
				
			|||||||
      }}
 | 
					      }}
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <Flex column fluid css={{ height: '100%', overflowY: 'auto', pr: '$1' }}>
 | 
					      <Flex column fluid css={{ height: '100%', overflowY: 'auto', pr: '$1' }}>
 | 
				
			||||||
        <Flex
 | 
					        <TxField label="Transaction type">
 | 
				
			||||||
          row
 | 
					 | 
				
			||||||
          fluid
 | 
					 | 
				
			||||||
          css={{
 | 
					 | 
				
			||||||
            justifyContent: 'flex-end',
 | 
					 | 
				
			||||||
            alignItems: 'center',
 | 
					 | 
				
			||||||
            mb: '$3',
 | 
					 | 
				
			||||||
            mt: '1px',
 | 
					 | 
				
			||||||
            pr: '1px'
 | 
					 | 
				
			||||||
          }}
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
          <Text muted css={{ mr: '$3' }}>
 | 
					 | 
				
			||||||
            Transaction type:{' '}
 | 
					 | 
				
			||||||
          </Text>
 | 
					 | 
				
			||||||
          <Select
 | 
					          <Select
 | 
				
			||||||
            instanceId="transactionsType"
 | 
					            instanceId="transactionsType"
 | 
				
			||||||
            placeholder="Select transaction type"
 | 
					            placeholder="Select transaction type"
 | 
				
			||||||
            options={transactionsOptions}
 | 
					            options={transactionsOptions}
 | 
				
			||||||
            hideSelectedOptions
 | 
					            hideSelectedOptions
 | 
				
			||||||
            css={{ width: '70%' }}
 | 
					 | 
				
			||||||
            value={selectedTransaction}
 | 
					            value={selectedTransaction}
 | 
				
			||||||
            onChange={(tt: any) => handleChangeTxType(tt)}
 | 
					            onChange={(tt: any) => handleChangeTxType(tt)}
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
        </Flex>
 | 
					        </TxField>
 | 
				
			||||||
        <Flex
 | 
					        <TxField label="Account">
 | 
				
			||||||
          row
 | 
					 | 
				
			||||||
          fluid
 | 
					 | 
				
			||||||
          css={{
 | 
					 | 
				
			||||||
            justifyContent: 'flex-end',
 | 
					 | 
				
			||||||
            alignItems: 'center',
 | 
					 | 
				
			||||||
            mb: '$3',
 | 
					 | 
				
			||||||
            pr: '1px'
 | 
					 | 
				
			||||||
          }}
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
          <Text muted css={{ mr: '$3' }}>
 | 
					 | 
				
			||||||
            Account:{' '}
 | 
					 | 
				
			||||||
          </Text>
 | 
					 | 
				
			||||||
          <Select
 | 
					          <Select
 | 
				
			||||||
            instanceId="from-account"
 | 
					            instanceId="from-account"
 | 
				
			||||||
            placeholder="Select your account"
 | 
					            placeholder="Select your account"
 | 
				
			||||||
            css={{ width: '70%' }}
 | 
					 | 
				
			||||||
            options={accountOptions}
 | 
					            options={accountOptions}
 | 
				
			||||||
            value={selectedAccount}
 | 
					            value={selectedAccount}
 | 
				
			||||||
            onChange={(acc: any) => handleSetAccount(acc)} // TODO make react-select have correct types for acc
 | 
					            onChange={(acc: any) => handleSetAccount(acc)} // TODO make react-select have correct types for acc
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
        </Flex>
 | 
					        </TxField>
 | 
				
			||||||
        {richFields.includes('Destination') && (
 | 
					        <TxField label="Sequence">
 | 
				
			||||||
          <Flex
 | 
					          <AccountSequence address={selectedAccount?.value} />
 | 
				
			||||||
            row
 | 
					        </TxField>
 | 
				
			||||||
            fluid
 | 
					 | 
				
			||||||
            css={{
 | 
					 | 
				
			||||||
              justifyContent: 'flex-end',
 | 
					 | 
				
			||||||
              alignItems: 'center',
 | 
					 | 
				
			||||||
              mb: '$3',
 | 
					 | 
				
			||||||
              pr: '1px'
 | 
					 | 
				
			||||||
            }}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            <Text muted css={{ mr: '$3', textAlign: 'end' }}>
 | 
					 | 
				
			||||||
              Destination account:{' '}
 | 
					 | 
				
			||||||
            </Text>
 | 
					 | 
				
			||||||
            <Select
 | 
					 | 
				
			||||||
              instanceId="to-account"
 | 
					 | 
				
			||||||
              placeholder="Select the destination account"
 | 
					 | 
				
			||||||
              css={{ width: '70%' }}
 | 
					 | 
				
			||||||
              options={destAccountOptions}
 | 
					 | 
				
			||||||
              value={selectedDestAccount}
 | 
					 | 
				
			||||||
              isClearable
 | 
					 | 
				
			||||||
              onChange={(acc: any) => setState({ selectedDestAccount: acc })}
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
          </Flex>
 | 
					 | 
				
			||||||
        )}
 | 
					 | 
				
			||||||
        {richFields.includes('Flags') && (
 | 
					        {richFields.includes('Flags') && (
 | 
				
			||||||
          <Flex
 | 
					          <TxField label="Flags">
 | 
				
			||||||
            row
 | 
					 | 
				
			||||||
            fluid
 | 
					 | 
				
			||||||
            css={{
 | 
					 | 
				
			||||||
              justifyContent: 'flex-end',
 | 
					 | 
				
			||||||
              alignItems: 'center',
 | 
					 | 
				
			||||||
              mb: '$3',
 | 
					 | 
				
			||||||
              pr: '1px'
 | 
					 | 
				
			||||||
            }}
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            <Text muted css={{ mr: '$3' }}>
 | 
					 | 
				
			||||||
              Flags:{' '}
 | 
					 | 
				
			||||||
            </Text>
 | 
					 | 
				
			||||||
            <Select
 | 
					            <Select
 | 
				
			||||||
              isClearable
 | 
					              isClearable
 | 
				
			||||||
              css={{ width: '70%' }}
 | 
					 | 
				
			||||||
              instanceId="flags"
 | 
					              instanceId="flags"
 | 
				
			||||||
              placeholder="Select flags to apply"
 | 
					              placeholder="Select flags to apply"
 | 
				
			||||||
              menuPosition="fixed"
 | 
					              menuPosition="fixed"
 | 
				
			||||||
@@ -229,41 +184,139 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estima
 | 
				
			|||||||
                selectedFlags ? selectedFlags.length >= flagsOptions.length - 1 : false
 | 
					                selectedFlags ? selectedFlags.length >= flagsOptions.length - 1 : false
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
          </Flex>
 | 
					          </TxField>
 | 
				
			||||||
        )}
 | 
					        )}
 | 
				
			||||||
        {otherFields.map(field => {
 | 
					        {otherFields.map(field => {
 | 
				
			||||||
          let _value = txFields[field]
 | 
					          let _value = txFields[field]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          let value: string | undefined
 | 
					          let value: string | undefined
 | 
				
			||||||
          if (typeof _value === 'object') {
 | 
					          if (typeIs(_value, 'object')) {
 | 
				
			||||||
            if (_value.$type === 'json' && typeof _value.$value === 'object') {
 | 
					            if (_value.$type === 'json' && typeIs(_value.$value, ['object', 'array'])) {
 | 
				
			||||||
              value = JSON.stringify(_value.$value, null, 2)
 | 
					              value = JSON.stringify(_value.$value, null, 2)
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
              value = _value.$value.toString()
 | 
					              value = _value.$value?.toString()
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            value = _value?.toString()
 | 
					            value = _value?.toString()
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          const isXrp = typeof _value === 'object' && _value.$type === 'xrp'
 | 
					          const isAccount = typeIs(_value, 'object') && _value.$type === 'account'
 | 
				
			||||||
 | 
					          const isXrpAmount = typeIs(_value, 'object') && _value.$type === 'amount.xrp'
 | 
				
			||||||
 | 
					          const isTokenAmount = typeIs(_value, 'object') && _value.$type === 'amount.token'
 | 
				
			||||||
          const isJson = typeof _value === 'object' && _value.$type === 'json'
 | 
					          const isJson = typeof _value === 'object' && _value.$type === 'json'
 | 
				
			||||||
          const isFee = field === 'Fee'
 | 
					          const isFee = field === 'Fee'
 | 
				
			||||||
          let rows = isJson ? (value?.match(/\n/gm)?.length || 0) + 1 : undefined
 | 
					          let rows = isJson ? (value?.match(/\n/gm)?.length || 0) + 1 : undefined
 | 
				
			||||||
          if (rows && rows > 5) rows = 5
 | 
					          if (rows && rows > 5) rows = 5
 | 
				
			||||||
 | 
					          let tokenAmount = defaultTokenAmount
 | 
				
			||||||
 | 
					          if (isTokenAmount && typeIs(_value, 'object') && typeIs(_value.$value, 'object')) {
 | 
				
			||||||
 | 
					            tokenAmount = {
 | 
				
			||||||
 | 
					              value: _value.$value.value,
 | 
				
			||||||
 | 
					              currency: _value.$value.currency,
 | 
				
			||||||
 | 
					              issuer: _value.$value.issuer
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if (isXrpAmount || isTokenAmount) {
 | 
				
			||||||
            return (
 | 
					            return (
 | 
				
			||||||
            <Flex column key={field} css={{ mb: '$2', pr: '1px' }}>
 | 
					              <TxField key={field} label={field}>
 | 
				
			||||||
 | 
					                <Flex fluid css={{ alignItems: 'center' }}>
 | 
				
			||||||
 | 
					                  {isTokenAmount ? (
 | 
				
			||||||
                    <Flex
 | 
					                    <Flex
 | 
				
			||||||
                row
 | 
					 | 
				
			||||||
                      fluid
 | 
					                      fluid
 | 
				
			||||||
 | 
					                      row
 | 
				
			||||||
 | 
					                      align="center"
 | 
				
			||||||
 | 
					                      justify="space-between"
 | 
				
			||||||
 | 
					                      css={{ position: 'relative' }}
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                      {/*  <Input
 | 
				
			||||||
 | 
					                        type="text"
 | 
				
			||||||
 | 
					                        placeholder="Issuer"
 | 
				
			||||||
 | 
					                        value={tokenAmount.issuer}
 | 
				
			||||||
 | 
					                        onChange={e =>
 | 
				
			||||||
 | 
					                          setRawField(field, 'amount.token', {
 | 
				
			||||||
 | 
					                            ...tokenAmount,
 | 
				
			||||||
 | 
					                            issuer: e.target.value
 | 
				
			||||||
 | 
					                          })
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                      /> */}
 | 
				
			||||||
 | 
					                      <Input
 | 
				
			||||||
 | 
					                        type="text"
 | 
				
			||||||
 | 
					                        value={tokenAmount.currency}
 | 
				
			||||||
 | 
					                        placeholder="Currency"
 | 
				
			||||||
 | 
					                        onChange={e => {
 | 
				
			||||||
 | 
					                          setRawField(field, 'amount.token', {
 | 
				
			||||||
 | 
					                            ...tokenAmount,
 | 
				
			||||||
 | 
					                            currency: e.target.value
 | 
				
			||||||
 | 
					                          })
 | 
				
			||||||
 | 
					                        }}
 | 
				
			||||||
 | 
					                      />
 | 
				
			||||||
 | 
					                      <Input
 | 
				
			||||||
 | 
					                        css={{ mx: '$1' }}
 | 
				
			||||||
 | 
					                        type="number"
 | 
				
			||||||
 | 
					                        value={tokenAmount.value}
 | 
				
			||||||
 | 
					                        placeholder="Value"
 | 
				
			||||||
 | 
					                        onChange={e => {
 | 
				
			||||||
 | 
					                          setRawField(field, 'amount.token', {
 | 
				
			||||||
 | 
					                            ...tokenAmount,
 | 
				
			||||||
 | 
					                            value: e.target.value
 | 
				
			||||||
 | 
					                          })
 | 
				
			||||||
 | 
					                        }}
 | 
				
			||||||
 | 
					                      />
 | 
				
			||||||
 | 
					                      <Box css={{ width: '50%' }}>
 | 
				
			||||||
 | 
					                        <CreatableAccount
 | 
				
			||||||
 | 
					                          value={tokenAmount.issuer}
 | 
				
			||||||
 | 
					                          field={'Issuer' as any}
 | 
				
			||||||
 | 
					                          placeholder="Issuer"
 | 
				
			||||||
 | 
					                          setField={(_, value = '') => {
 | 
				
			||||||
 | 
					                            setRawField(field, 'amount.token', {
 | 
				
			||||||
 | 
					                              ...tokenAmount,
 | 
				
			||||||
 | 
					                              issuer: value
 | 
				
			||||||
 | 
					                            })
 | 
				
			||||||
 | 
					                          }}
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
 | 
					                      </Box>
 | 
				
			||||||
 | 
					                    </Flex>
 | 
				
			||||||
 | 
					                  ) : (
 | 
				
			||||||
 | 
					                    <Input
 | 
				
			||||||
 | 
					                      css={{ flex: 'inherit' }}
 | 
				
			||||||
 | 
					                      type="number"
 | 
				
			||||||
 | 
					                      value={value}
 | 
				
			||||||
 | 
					                      onChange={e => handleSetField(field, e.target.value)}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                  )}
 | 
				
			||||||
 | 
					                  <Box
 | 
				
			||||||
                    css={{
 | 
					                    css={{
 | 
				
			||||||
                  justifyContent: 'flex-end',
 | 
					                      ml: '$2',
 | 
				
			||||||
                  alignItems: 'center',
 | 
					                      width: '150px'
 | 
				
			||||||
                  position: 'relative'
 | 
					 | 
				
			||||||
                    }}
 | 
					                    }}
 | 
				
			||||||
                  >
 | 
					                  >
 | 
				
			||||||
                <Text muted css={{ mr: '$3' }}>
 | 
					                    <Select
 | 
				
			||||||
                  {field + (isXrp ? ' (XRP)' : '')}:{' '}
 | 
					                      instanceId="currency-type"
 | 
				
			||||||
                </Text>
 | 
					                      options={amountOptions}
 | 
				
			||||||
 | 
					                      value={isXrpAmount ? amountOptions['0'] : amountOptions['1']}
 | 
				
			||||||
 | 
					                      onChange={(e: any) => {
 | 
				
			||||||
 | 
					                        const opt = e as typeof amountOptions[number]
 | 
				
			||||||
 | 
					                        if (opt.value === 'xrp') {
 | 
				
			||||||
 | 
					                          setRawField(field, 'amount.xrp', '0')
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                          setRawField(field, 'amount.token', defaultTokenAmount)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                      }}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                  </Box>
 | 
				
			||||||
 | 
					                </Flex>
 | 
				
			||||||
 | 
					              </TxField>
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (isAccount) {
 | 
				
			||||||
 | 
					            return (
 | 
				
			||||||
 | 
					              <TxField key={field} label={field}>
 | 
				
			||||||
 | 
					                <CreatableAccount value={value} field={field} setField={handleSetField} />
 | 
				
			||||||
 | 
					              </TxField>
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          return (
 | 
				
			||||||
 | 
					            <TxField key={field} label={field}>
 | 
				
			||||||
              {isJson ? (
 | 
					              {isJson ? (
 | 
				
			||||||
                <Textarea
 | 
					                <Textarea
 | 
				
			||||||
                  rows={rows}
 | 
					                  rows={rows}
 | 
				
			||||||
@@ -271,7 +324,6 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estima
 | 
				
			|||||||
                  spellCheck={false}
 | 
					                  spellCheck={false}
 | 
				
			||||||
                  onChange={switchToJson}
 | 
					                  onChange={switchToJson}
 | 
				
			||||||
                  css={{
 | 
					                  css={{
 | 
				
			||||||
                      width: '70%',
 | 
					 | 
				
			||||||
                    flex: 'inherit',
 | 
					                    flex: 'inherit',
 | 
				
			||||||
                    resize: 'vertical'
 | 
					                    resize: 'vertical'
 | 
				
			||||||
                  }}
 | 
					                  }}
 | 
				
			||||||
@@ -298,7 +350,6 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estima
 | 
				
			|||||||
                      : undefined
 | 
					                      : undefined
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
                  css={{
 | 
					                  css={{
 | 
				
			||||||
                      width: '70%',
 | 
					 | 
				
			||||||
                    flex: 'inherit',
 | 
					                    flex: 'inherit',
 | 
				
			||||||
                    '-moz-appearance': 'textfield',
 | 
					                    '-moz-appearance': 'textfield',
 | 
				
			||||||
                    '&::-webkit-outer-spin-button': {
 | 
					                    '&::-webkit-outer-spin-button': {
 | 
				
			||||||
@@ -333,11 +384,200 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estima
 | 
				
			|||||||
                  Suggest
 | 
					                  Suggest
 | 
				
			||||||
                </Button>
 | 
					                </Button>
 | 
				
			||||||
              )}
 | 
					              )}
 | 
				
			||||||
              </Flex>
 | 
					            </TxField>
 | 
				
			||||||
            </Flex>
 | 
					 | 
				
			||||||
          )
 | 
					          )
 | 
				
			||||||
        })}
 | 
					        })}
 | 
				
			||||||
 | 
					        <TxField multiLine label="Hook parameters">
 | 
				
			||||||
 | 
					          <Flex column fluid>
 | 
				
			||||||
 | 
					            {Object.entries(hookParameters).map(([id, { label, value }]) => (
 | 
				
			||||||
 | 
					              <Flex column key={id} css={{ mb: '$2' }}>
 | 
				
			||||||
 | 
					                <Flex row>
 | 
				
			||||||
 | 
					                  <Input
 | 
				
			||||||
 | 
					                    placeholder="Parameter name"
 | 
				
			||||||
 | 
					                    value={label}
 | 
				
			||||||
 | 
					                    onChange={e => {
 | 
				
			||||||
 | 
					                      setState({
 | 
				
			||||||
 | 
					                        hookParameters: {
 | 
				
			||||||
 | 
					                          ...hookParameters,
 | 
				
			||||||
 | 
					                          [id]: { label: e.target.value, value }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                      })
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                  />
 | 
				
			||||||
 | 
					                  <Input
 | 
				
			||||||
 | 
					                    css={{ mx: '$2' }}
 | 
				
			||||||
 | 
					                    placeholder="Value (hex-quoted)"
 | 
				
			||||||
 | 
					                    value={value}
 | 
				
			||||||
 | 
					                    onChange={e => {
 | 
				
			||||||
 | 
					                      setState({
 | 
				
			||||||
 | 
					                        hookParameters: {
 | 
				
			||||||
 | 
					                          ...hookParameters,
 | 
				
			||||||
 | 
					                          [id]: { label, value: e.target.value }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                      })
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                  />
 | 
				
			||||||
 | 
					                  <Button
 | 
				
			||||||
 | 
					                    onClick={() => {
 | 
				
			||||||
 | 
					                      const { [id]: _, ...rest } = hookParameters
 | 
				
			||||||
 | 
					                      setState({ hookParameters: rest })
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                    variant="destroy"
 | 
				
			||||||
 | 
					                  >
 | 
				
			||||||
 | 
					                    <Trash weight="regular" size="16px" />
 | 
				
			||||||
 | 
					                  </Button>
 | 
				
			||||||
 | 
					                </Flex>
 | 
				
			||||||
 | 
					              </Flex>
 | 
				
			||||||
 | 
					            ))}
 | 
				
			||||||
 | 
					            <Button
 | 
				
			||||||
 | 
					              outline
 | 
				
			||||||
 | 
					              fullWidth
 | 
				
			||||||
 | 
					              type="button"
 | 
				
			||||||
 | 
					              onClick={() => {
 | 
				
			||||||
 | 
					                const id = Object.keys(hookParameters).length
 | 
				
			||||||
 | 
					                setState({
 | 
				
			||||||
 | 
					                  hookParameters: { ...hookParameters, [id]: { label: '', value: '' } }
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              <Plus size="16px" />
 | 
				
			||||||
 | 
					              Add Hook Parameter
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
 | 
					          </Flex>
 | 
				
			||||||
 | 
					        </TxField>
 | 
				
			||||||
 | 
					        <TxField multiLine label="Memos">
 | 
				
			||||||
 | 
					          <Flex column fluid>
 | 
				
			||||||
 | 
					            {Object.entries(memos).map(([id, memo]) => (
 | 
				
			||||||
 | 
					              <Flex column key={id} css={{ mb: '$2' }}>
 | 
				
			||||||
 | 
					                <Flex
 | 
				
			||||||
 | 
					                  row
 | 
				
			||||||
 | 
					                  css={{
 | 
				
			||||||
 | 
					                    flexWrap: 'wrap',
 | 
				
			||||||
 | 
					                    width: '100%'
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  <Input
 | 
				
			||||||
 | 
					                    placeholder="Memo type"
 | 
				
			||||||
 | 
					                    value={memo.type}
 | 
				
			||||||
 | 
					                    onChange={e => {
 | 
				
			||||||
 | 
					                      setState({
 | 
				
			||||||
 | 
					                        memos: {
 | 
				
			||||||
 | 
					                          ...memos,
 | 
				
			||||||
 | 
					                          [id]: { ...memo, type: e.target.value }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                      })
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                  />
 | 
				
			||||||
 | 
					                  <Input
 | 
				
			||||||
 | 
					                    placeholder="Data (hex-quoted)"
 | 
				
			||||||
 | 
					                    css={{ mx: '$2' }}
 | 
				
			||||||
 | 
					                    value={memo.data}
 | 
				
			||||||
 | 
					                    onChange={e => {
 | 
				
			||||||
 | 
					                      setState({
 | 
				
			||||||
 | 
					                        memos: {
 | 
				
			||||||
 | 
					                          ...memos,
 | 
				
			||||||
 | 
					                          [id]: { ...memo, data: e.target.value }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                      })
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                  />
 | 
				
			||||||
 | 
					                  <Input
 | 
				
			||||||
 | 
					                    placeholder="Format"
 | 
				
			||||||
 | 
					                    value={memo.format}
 | 
				
			||||||
 | 
					                    onChange={e => {
 | 
				
			||||||
 | 
					                      setState({
 | 
				
			||||||
 | 
					                        memos: {
 | 
				
			||||||
 | 
					                          ...memos,
 | 
				
			||||||
 | 
					                          [id]: { ...memo, format: e.target.value }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                      })
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                  />
 | 
				
			||||||
 | 
					                  <Button
 | 
				
			||||||
 | 
					                    css={{ ml: '$2' }}
 | 
				
			||||||
 | 
					                    onClick={() => {
 | 
				
			||||||
 | 
					                      const { [id]: _, ...rest } = memos
 | 
				
			||||||
 | 
					                      setState({ memos: rest })
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                    variant="destroy"
 | 
				
			||||||
 | 
					                  >
 | 
				
			||||||
 | 
					                    <Trash weight="regular" size="16px" />
 | 
				
			||||||
 | 
					                  </Button>
 | 
				
			||||||
 | 
					                </Flex>
 | 
				
			||||||
 | 
					              </Flex>
 | 
				
			||||||
 | 
					            ))}
 | 
				
			||||||
 | 
					            <Button
 | 
				
			||||||
 | 
					              outline
 | 
				
			||||||
 | 
					              fullWidth
 | 
				
			||||||
 | 
					              type="button"
 | 
				
			||||||
 | 
					              onClick={() => {
 | 
				
			||||||
 | 
					                const id = Object.keys(memos).length
 | 
				
			||||||
 | 
					                setState({
 | 
				
			||||||
 | 
					                  memos: { ...memos, [id]: { data: '', format: '', type: '' } }
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              <Plus size="16px" />
 | 
				
			||||||
 | 
					              Add Memo
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
 | 
					          </Flex>
 | 
				
			||||||
 | 
					        </TxField>
 | 
				
			||||||
      </Flex>
 | 
					      </Flex>
 | 
				
			||||||
    </Container>
 | 
					    </Container>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const CreatableAccount: FC<{
 | 
				
			||||||
 | 
					  value: string | undefined
 | 
				
			||||||
 | 
					  field: keyof TxFields
 | 
				
			||||||
 | 
					  placeholder?: string
 | 
				
			||||||
 | 
					  setField: (field: keyof TxFields, value: string, opFields?: TxFields) => void
 | 
				
			||||||
 | 
					}> = ({ value, field, setField, placeholder }) => {
 | 
				
			||||||
 | 
					  const { accounts } = useSnapshot(state)
 | 
				
			||||||
 | 
					  const accountOptions: SelectOption[] = accounts.map(acc => ({
 | 
				
			||||||
 | 
					    label: acc.name,
 | 
				
			||||||
 | 
					    value: acc.address
 | 
				
			||||||
 | 
					  }))
 | 
				
			||||||
 | 
					  const label = accountOptions.find(a => a.value === value)?.label || value
 | 
				
			||||||
 | 
					  const val = {
 | 
				
			||||||
 | 
					    value,
 | 
				
			||||||
 | 
					    label
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  placeholder = placeholder || `${capitalize(field)} account`
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <CreatableSelect
 | 
				
			||||||
 | 
					      isClearable
 | 
				
			||||||
 | 
					      instanceId={field}
 | 
				
			||||||
 | 
					      placeholder={placeholder}
 | 
				
			||||||
 | 
					      options={accountOptions}
 | 
				
			||||||
 | 
					      value={value ? val : undefined}
 | 
				
			||||||
 | 
					      onChange={(acc: any) => setField(field, acc?.value)}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const TxField: FC<{ label: string; children: ReactNode; multiLine?: boolean }> = ({
 | 
				
			||||||
 | 
					  label,
 | 
				
			||||||
 | 
					  children,
 | 
				
			||||||
 | 
					  multiLine = false
 | 
				
			||||||
 | 
					}) => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Flex
 | 
				
			||||||
 | 
					      row
 | 
				
			||||||
 | 
					      fluid
 | 
				
			||||||
 | 
					      css={{
 | 
				
			||||||
 | 
					        justifyContent: 'flex-end',
 | 
				
			||||||
 | 
					        alignItems: multiLine ? 'flex-start' : 'center',
 | 
				
			||||||
 | 
					        position: 'relative',
 | 
				
			||||||
 | 
					        mb: '$2',
 | 
				
			||||||
 | 
					        mt: '1px',
 | 
				
			||||||
 | 
					        pr: '1px'
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <Text muted css={{ mr: '$3', mt: multiLine ? '$2' : 0 }}>
 | 
				
			||||||
 | 
					        {label}:{' '}
 | 
				
			||||||
 | 
					      </Text>
 | 
				
			||||||
 | 
					      <Flex css={{ width: '70%', alignItems: 'center' }}>{children}</Flex>
 | 
				
			||||||
 | 
					    </Flex>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,10 @@
 | 
				
			|||||||
  {
 | 
					  {
 | 
				
			||||||
    "TransactionType": "AccountDelete",
 | 
					    "TransactionType": "AccountDelete",
 | 
				
			||||||
    "Account": "rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm",
 | 
					    "Account": "rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm",
 | 
				
			||||||
    "Destination": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
 | 
					    "Destination": {
 | 
				
			||||||
 | 
					      "$type": "account",
 | 
				
			||||||
 | 
					      "$value": ""
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "DestinationTag": 13,
 | 
					    "DestinationTag": 13,
 | 
				
			||||||
    "Fee": "2000000",
 | 
					    "Fee": "2000000",
 | 
				
			||||||
    "Sequence": 2470665,
 | 
					    "Sequence": 2470665,
 | 
				
			||||||
@@ -28,7 +31,11 @@
 | 
				
			|||||||
    "TransactionType": "CheckCash",
 | 
					    "TransactionType": "CheckCash",
 | 
				
			||||||
    "Amount": {
 | 
					    "Amount": {
 | 
				
			||||||
      "$value": "100",
 | 
					      "$value": "100",
 | 
				
			||||||
      "$type": "xrp"
 | 
					      "$type": "amount.xrp"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "DeliverMin": {
 | 
				
			||||||
 | 
					      "$value": "",
 | 
				
			||||||
 | 
					      "$type": "amount.xrp"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "CheckID": "838766BA2B995C00744175F69A1B11E32C3DBC40E64801A4056FCBD657F57334",
 | 
					    "CheckID": "838766BA2B995C00744175F69A1B11E32C3DBC40E64801A4056FCBD657F57334",
 | 
				
			||||||
    "Fee": "12"
 | 
					    "Fee": "12"
 | 
				
			||||||
@@ -36,7 +43,10 @@
 | 
				
			|||||||
  {
 | 
					  {
 | 
				
			||||||
    "TransactionType": "CheckCreate",
 | 
					    "TransactionType": "CheckCreate",
 | 
				
			||||||
    "Account": "rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo",
 | 
					    "Account": "rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo",
 | 
				
			||||||
    "Destination": "rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy",
 | 
					    "Destination": {
 | 
				
			||||||
 | 
					      "$type": "account",
 | 
				
			||||||
 | 
					      "$value": ""
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "SendMax": "100000000",
 | 
					    "SendMax": "100000000",
 | 
				
			||||||
    "Expiration": 570113521,
 | 
					    "Expiration": 570113521,
 | 
				
			||||||
    "InvoiceID": "6F1DFD1D0FE8A32E40E1F2C05CF1C15545BAB56B617F9C6C2D63A6B704BEF59B",
 | 
					    "InvoiceID": "6F1DFD1D0FE8A32E40E1F2C05CF1C15545BAB56B617F9C6C2D63A6B704BEF59B",
 | 
				
			||||||
@@ -54,7 +64,10 @@
 | 
				
			|||||||
  {
 | 
					  {
 | 
				
			||||||
    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
					    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
				
			||||||
    "TransactionType": "EscrowCancel",
 | 
					    "TransactionType": "EscrowCancel",
 | 
				
			||||||
    "Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
					    "Owner": {
 | 
				
			||||||
 | 
					      "$type": "account",
 | 
				
			||||||
 | 
					      "$value": ""
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "OfferSequence": 7,
 | 
					    "OfferSequence": 7,
 | 
				
			||||||
    "Fee": "10"
 | 
					    "Fee": "10"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
@@ -63,9 +76,12 @@
 | 
				
			|||||||
    "TransactionType": "EscrowCreate",
 | 
					    "TransactionType": "EscrowCreate",
 | 
				
			||||||
    "Amount": {
 | 
					    "Amount": {
 | 
				
			||||||
      "$value": "100",
 | 
					      "$value": "100",
 | 
				
			||||||
      "$type": "xrp"
 | 
					      "$type": "amount.xrp"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "Destination": {
 | 
				
			||||||
 | 
					      "$type": "account",
 | 
				
			||||||
 | 
					      "$value": ""
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "Destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
 | 
					 | 
				
			||||||
    "CancelAfter": 533257958,
 | 
					    "CancelAfter": 533257958,
 | 
				
			||||||
    "FinishAfter": 533171558,
 | 
					    "FinishAfter": 533171558,
 | 
				
			||||||
    "Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
 | 
					    "Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
 | 
				
			||||||
@@ -76,12 +92,66 @@
 | 
				
			|||||||
  {
 | 
					  {
 | 
				
			||||||
    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
					    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
				
			||||||
    "TransactionType": "EscrowFinish",
 | 
					    "TransactionType": "EscrowFinish",
 | 
				
			||||||
    "Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
					    "Owner": {
 | 
				
			||||||
 | 
					      "$type": "account",
 | 
				
			||||||
 | 
					      "$value": ""
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "OfferSequence": 7,
 | 
					    "OfferSequence": 7,
 | 
				
			||||||
    "Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
 | 
					    "Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
 | 
				
			||||||
    "Fulfillment": "A0028000",
 | 
					    "Fulfillment": "A0028000",
 | 
				
			||||||
    "Fee": "10"
 | 
					    "Fee": "10"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "TransactionType": "NFTokenMint",
 | 
				
			||||||
 | 
					    "Account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
 | 
				
			||||||
 | 
					    "Fee": "10",
 | 
				
			||||||
 | 
					    "NFTokenTaxon": 0,
 | 
				
			||||||
 | 
					    "URI": "697066733A2F2F516D614374444B5A4656767666756676626479346573745A626851483744586831364354707631686F776D424779"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "TransactionType": "NFTokenBurn",
 | 
				
			||||||
 | 
					    "Account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
 | 
				
			||||||
 | 
					    "Fee": "10",
 | 
				
			||||||
 | 
					    "NFTokenID": "000B013A95F14B0044F78A264E41713C64B5F89242540EE208C3098E00000D65"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "TransactionType": "NFTokenAcceptOffer",
 | 
				
			||||||
 | 
					    "Account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
 | 
				
			||||||
 | 
					    "Fee": "10",
 | 
				
			||||||
 | 
					    "NFTokenSellOffer": "A2FA1A9911FE2AEF83DAB05F437768E26A301EF899BD31EB85E704B3D528FF18",
 | 
				
			||||||
 | 
					    "NFTokenBuyOffer": "4AAAEEA76E3C8148473CB3840CE637676E561FB02BD4CA22CA59729EA815B862",
 | 
				
			||||||
 | 
					    "NFTokenBrokerFee": "10"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "TransactionType": "NFTokenCancelOffer",
 | 
				
			||||||
 | 
					    "Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
 | 
				
			||||||
 | 
					    "Fee": "10",
 | 
				
			||||||
 | 
					    "NFTokenOffers": {
 | 
				
			||||||
 | 
					      "$type": "json",
 | 
				
			||||||
 | 
					      "$value": [
 | 
				
			||||||
 | 
					        "4AAAEEA76E3C8148473CB3840CE637676E561FB02BD4CA22CA59729EA815B862"
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "TransactionType": "NFTokenCreateOffer",
 | 
				
			||||||
 | 
					    "Account": "rs8jBmmfpwgmrSPgwMsh7CvKRmRt1JTVSX",
 | 
				
			||||||
 | 
					    "NFTokenID": "000100001E962F495F07A990F4ED55ACCFEEF365DBAA76B6A048C0A200000007",
 | 
				
			||||||
 | 
					    "Amount": {
 | 
				
			||||||
 | 
					      "$value": "100",
 | 
				
			||||||
 | 
					      "$type": "amount.xrp"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "Flags": "1",
 | 
				
			||||||
 | 
					    "Destination": {
 | 
				
			||||||
 | 
					      "$type": "account",
 | 
				
			||||||
 | 
					      "$value": ""
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "Owner": {
 | 
				
			||||||
 | 
					      "$type": "account",
 | 
				
			||||||
 | 
					      "$value": ""
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "Fee": "10"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "TransactionType": "OfferCancel",
 | 
					    "TransactionType": "OfferCancel",
 | 
				
			||||||
    "Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
 | 
					    "Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
 | 
				
			||||||
@@ -98,19 +168,29 @@
 | 
				
			|||||||
    "Flags": "0",
 | 
					    "Flags": "0",
 | 
				
			||||||
    "LastLedgerSequence": 7108682,
 | 
					    "LastLedgerSequence": 7108682,
 | 
				
			||||||
    "Sequence": 8,
 | 
					    "Sequence": 8,
 | 
				
			||||||
    "TakerGets": "6000000",
 | 
					    "TakerGets": {
 | 
				
			||||||
    "Amount": {
 | 
					      "$type": "amount.xrp",
 | 
				
			||||||
      "$value": "100",
 | 
					      "$value": "6000000"
 | 
				
			||||||
      "$type": "xrp"
 | 
					    },
 | 
				
			||||||
 | 
					    "TakerPays": {
 | 
				
			||||||
 | 
					      "$type": "amount.token",
 | 
				
			||||||
 | 
					      "$value": {
 | 
				
			||||||
 | 
					        "currency": "GKO",
 | 
				
			||||||
 | 
					        "issuer": "ruazs5h1qEsqpke88pcqnaseXdm6od2xc",
 | 
				
			||||||
 | 
					        "value": "2"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "TransactionType": "Payment",
 | 
					    "TransactionType": "Payment",
 | 
				
			||||||
    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
					    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
				
			||||||
    "Destination": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
 | 
					    "Destination": {
 | 
				
			||||||
 | 
					      "$type": "account",
 | 
				
			||||||
 | 
					      "$value": ""
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "Amount": {
 | 
					    "Amount": {
 | 
				
			||||||
      "$value": "100",
 | 
					      "$value": "100",
 | 
				
			||||||
      "$type": "xrp"
 | 
					      "$type": "amount.xrp"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "Fee": "12",
 | 
					    "Fee": "12",
 | 
				
			||||||
    "Flags": "2147483648",
 | 
					    "Flags": "2147483648",
 | 
				
			||||||
@@ -121,9 +201,12 @@
 | 
				
			|||||||
    "TransactionType": "PaymentChannelCreate",
 | 
					    "TransactionType": "PaymentChannelCreate",
 | 
				
			||||||
    "Amount": {
 | 
					    "Amount": {
 | 
				
			||||||
      "$value": "100",
 | 
					      "$value": "100",
 | 
				
			||||||
      "$type": "xrp"
 | 
					      "$type": "amount.xrp"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "Destination": {
 | 
				
			||||||
 | 
					      "$type": "account",
 | 
				
			||||||
 | 
					      "$value": ""
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "Destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
 | 
					 | 
				
			||||||
    "SettleDelay": 86400,
 | 
					    "SettleDelay": 86400,
 | 
				
			||||||
    "PublicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A",
 | 
					    "PublicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A",
 | 
				
			||||||
    "CancelAfter": 533171558,
 | 
					    "CancelAfter": 533171558,
 | 
				
			||||||
@@ -137,7 +220,7 @@
 | 
				
			|||||||
    "Channel": "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198",
 | 
					    "Channel": "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198",
 | 
				
			||||||
    "Amount": {
 | 
					    "Amount": {
 | 
				
			||||||
      "$value": "200",
 | 
					      "$value": "200",
 | 
				
			||||||
      "$type": "xrp"
 | 
					      "$type": "amount.xrp"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "Expiration": 543171558,
 | 
					    "Expiration": 543171558,
 | 
				
			||||||
    "Fee": "10"
 | 
					    "Fee": "10"
 | 
				
			||||||
@@ -193,7 +276,7 @@
 | 
				
			|||||||
    "Flags": "262144",
 | 
					    "Flags": "262144",
 | 
				
			||||||
    "LastLedgerSequence": 8007750,
 | 
					    "LastLedgerSequence": 8007750,
 | 
				
			||||||
    "LimitAmount": {
 | 
					    "LimitAmount": {
 | 
				
			||||||
      "$type": "json",
 | 
					      "$type": "amount.token",
 | 
				
			||||||
      "$value": {
 | 
					      "$value": {
 | 
				
			||||||
        "currency": "USD",
 | 
					        "currency": "USD",
 | 
				
			||||||
        "issuer": "rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc",
 | 
					        "issuer": "rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc",
 | 
				
			||||||
@@ -204,6 +287,10 @@
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "TransactionType": "Invoke",
 | 
					    "TransactionType": "Invoke",
 | 
				
			||||||
 | 
					    "Destination": {
 | 
				
			||||||
 | 
					      "$type": "account",
 | 
				
			||||||
 | 
					      "$value": ""
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "Fee": "12"
 | 
					    "Fee": "12"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
import toast from 'react-hot-toast'
 | 
					import toast from 'react-hot-toast'
 | 
				
			||||||
import state, { FaucetAccountRes } from '../index'
 | 
					import state, { FaucetAccountRes } from '../index'
 | 
				
			||||||
 | 
					import fetchAccountInfo from '../../utils/accountInfo';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const names = [
 | 
					export const names = [
 | 
				
			||||||
  'Alice',
 | 
					  'Alice',
 | 
				
			||||||
@@ -35,31 +36,28 @@ export const addFaucetAccount = async (name?: string, showToast: boolean = false
 | 
				
			|||||||
  })
 | 
					  })
 | 
				
			||||||
  const json: FaucetAccountRes | { error: string } = await res.json()
 | 
					  const json: FaucetAccountRes | { error: string } = await res.json()
 | 
				
			||||||
  if ('error' in json) {
 | 
					  if ('error' in json) {
 | 
				
			||||||
    if (showToast) {
 | 
					    if (!showToast) return;
 | 
				
			||||||
    return toast.error(json.error, { id: toastId })
 | 
					    return toast.error(json.error, { id: toastId })
 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      return
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  } else {
 | 
					 | 
				
			||||||
    if (showToast) {
 | 
					 | 
				
			||||||
      toast.success('New account created', { id: toastId })
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  const currNames = state.accounts.map(acc => acc.name)
 | 
					  const currNames = state.accounts.map(acc => acc.name)
 | 
				
			||||||
 | 
					  const info = await fetchAccountInfo(json.address, { silent: true })
 | 
				
			||||||
  state.accounts.push({
 | 
					  state.accounts.push({
 | 
				
			||||||
    name: name || names.filter(name => !currNames.includes(name))[0],
 | 
					    name: name || names.filter(name => !currNames.includes(name))[0],
 | 
				
			||||||
    xrp: (json.xrp || 0 * 1000000).toString(),
 | 
					    xrp: (json.xrp || 0 * 1000000).toString(),
 | 
				
			||||||
    address: json.address,
 | 
					    address: json.address,
 | 
				
			||||||
    secret: json.secret,
 | 
					    secret: json.secret,
 | 
				
			||||||
      sequence: 1,
 | 
					    sequence: info?.Sequence || 1,
 | 
				
			||||||
    hooks: [],
 | 
					    hooks: [],
 | 
				
			||||||
    isLoading: false,
 | 
					    isLoading: false,
 | 
				
			||||||
    version: '2'
 | 
					    version: '2'
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					  if (showToast) {
 | 
				
			||||||
 | 
					    toast.success('New account created', { id: toastId })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// fetch initial faucets
 | 
					  // fetch initial faucets
 | 
				
			||||||
;(async function fetchFaucets() {
 | 
					  ; (async function fetchFaucets() {
 | 
				
			||||||
    if (typeof window !== 'undefined') {
 | 
					    if (typeof window !== 'undefined') {
 | 
				
			||||||
      if (state.accounts.length === 0) {
 | 
					      if (state.accounts.length === 0) {
 | 
				
			||||||
        await addFaucetAccount()
 | 
					        await addFaucetAccount()
 | 
				
			||||||
@@ -68,7 +66,7 @@ export const addFaucetAccount = async (name?: string, showToast: boolean = false
 | 
				
			|||||||
        // }, 10000);
 | 
					        // }, 10000);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
})()
 | 
					  })()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const addFunds = async (address: string) => {
 | 
					export const addFunds = async (address: string) => {
 | 
				
			||||||
  const toastId = toast.loading('Requesting funds')
 | 
					  const toastId = toast.loading('Requesting funds')
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,11 +14,4 @@ export const deleteAccount = (addr?: string) => {
 | 
				
			|||||||
      if (!acc) return
 | 
					      if (!acc) return
 | 
				
			||||||
      acc.label = acc.value
 | 
					      acc.label = acc.value
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  transactionsState.transactions
 | 
					 | 
				
			||||||
    .filter(t => t.state.selectedDestAccount?.value === addr)
 | 
					 | 
				
			||||||
    .forEach(t => {
 | 
					 | 
				
			||||||
      const acc = t.state.selectedDestAccount
 | 
					 | 
				
			||||||
      if (!acc) return
 | 
					 | 
				
			||||||
      acc.label = acc.value
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@ import calculateHookOn, { TTS } from '../../utils/hookOnCalculator'
 | 
				
			|||||||
import { Link } from '../../components'
 | 
					import { Link } from '../../components'
 | 
				
			||||||
import { ref } from 'valtio'
 | 
					import { ref } from 'valtio'
 | 
				
			||||||
import estimateFee from '../../utils/estimateFee'
 | 
					import estimateFee from '../../utils/estimateFee'
 | 
				
			||||||
import { SetHookData } from '../../utils/setHook'
 | 
					import { SetHookData, toHex } from '../../utils/setHook'
 | 
				
			||||||
import ResultLink from '../../components/ResultLink'
 | 
					import ResultLink from '../../components/ResultLink'
 | 
				
			||||||
import { xrplSend } from './xrpl-client'
 | 
					import { xrplSend } from './xrpl-client'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -18,13 +18,6 @@ export const sha256 = async (string: string) => {
 | 
				
			|||||||
  return hashHex
 | 
					  return hashHex
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function toHex(str: string) {
 | 
					 | 
				
			||||||
  var result = ''
 | 
					 | 
				
			||||||
  for (var i = 0; i < str.length; i++) {
 | 
					 | 
				
			||||||
    result += str.charCodeAt(i).toString(16)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return result.toUpperCase()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
function arrayBufferToHex(arrayBuffer?: ArrayBuffer | null) {
 | 
					function arrayBufferToHex(arrayBuffer?: ArrayBuffer | null) {
 | 
				
			||||||
  if (!arrayBuffer) {
 | 
					  if (!arrayBuffer) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,6 @@ interface TransactionOptions {
 | 
				
			|||||||
  TransactionType: string
 | 
					  TransactionType: string
 | 
				
			||||||
  Account?: string
 | 
					  Account?: string
 | 
				
			||||||
  Fee?: string
 | 
					  Fee?: string
 | 
				
			||||||
  Destination?: string
 | 
					 | 
				
			||||||
  [index: string]: any
 | 
					  [index: string]: any
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
interface OtherOptions {
 | 
					interface OtherOptions {
 | 
				
			||||||
@@ -31,6 +30,10 @@ export const sendTransaction = async (
 | 
				
			|||||||
    ...opts
 | 
					    ...opts
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  const { logPrefix = '' } = options || {}
 | 
					  const { logPrefix = '' } = options || {}
 | 
				
			||||||
 | 
					  state.transactionLogs.push({
 | 
				
			||||||
 | 
					    type: 'log',
 | 
				
			||||||
 | 
					    message: `${logPrefix}${JSON.stringify(tx, null, 2)}`
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    const signedAccount = derive.familySeed(account.secret)
 | 
					    const signedAccount = derive.familySeed(account.secret)
 | 
				
			||||||
    const { signedTransaction } = sign(tx, signedAccount)
 | 
					    const { signedTransaction } = sign(tx, signedAccount)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,35 +5,55 @@ import state from '.'
 | 
				
			|||||||
import { showAlert } from '../state/actions/showAlert'
 | 
					import { showAlert } from '../state/actions/showAlert'
 | 
				
			||||||
import { parseJSON } from '../utils/json'
 | 
					import { parseJSON } from '../utils/json'
 | 
				
			||||||
import { extractFlags, getFlags } from './constants/flags'
 | 
					import { extractFlags, getFlags } from './constants/flags'
 | 
				
			||||||
 | 
					import { fromHex } from '../utils/setHook'
 | 
				
			||||||
 | 
					import { typeIs } from '../utils/helpers'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type SelectOption = {
 | 
					export type SelectOption = {
 | 
				
			||||||
  value: string
 | 
					  value: string
 | 
				
			||||||
  label: string
 | 
					  label: string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type HookParameters = {
 | 
				
			||||||
 | 
					  [key: string]: SelectOption
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type Memos = {
 | 
				
			||||||
 | 
					  [key: string]: {
 | 
				
			||||||
 | 
					    type: string
 | 
				
			||||||
 | 
					    format: string
 | 
				
			||||||
 | 
					    data: string
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface TransactionState {
 | 
					export interface TransactionState {
 | 
				
			||||||
  selectedTransaction: SelectOption | null
 | 
					  selectedTransaction: SelectOption | null
 | 
				
			||||||
  selectedAccount: SelectOption | null
 | 
					  selectedAccount: SelectOption | null
 | 
				
			||||||
  selectedDestAccount: SelectOption | null
 | 
					 | 
				
			||||||
  selectedFlags: SelectOption[] | null
 | 
					  selectedFlags: SelectOption[] | null
 | 
				
			||||||
 | 
					  hookParameters: HookParameters
 | 
				
			||||||
 | 
					  memos: Memos
 | 
				
			||||||
  txIsLoading: boolean
 | 
					  txIsLoading: boolean
 | 
				
			||||||
  txIsDisabled: boolean
 | 
					  txIsDisabled: boolean
 | 
				
			||||||
  txFields: TxFields
 | 
					  txFields: TxFields
 | 
				
			||||||
  viewType: 'json' | 'ui'
 | 
					  viewType: 'json' | 'ui'
 | 
				
			||||||
  editorValue?: string
 | 
					  editorValue?: string
 | 
				
			||||||
 | 
					  editorIsSaved: boolean
 | 
				
			||||||
  estimatedFee?: string
 | 
					  estimatedFee?: string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const commonFields = ['TransactionType', 'Account', 'Sequence', "HookParameters"] as const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TxFields = Omit<
 | 
					export type TxFields = Omit<
 | 
				
			||||||
  Partial<typeof transactionsData[0]>,
 | 
					  Partial<typeof transactionsData[0]>,
 | 
				
			||||||
  'Account' | 'Sequence' | 'TransactionType'
 | 
					  typeof commonFields[number]
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const defaultTransaction: TransactionState = {
 | 
					export const defaultTransaction: TransactionState = {
 | 
				
			||||||
  selectedTransaction: null,
 | 
					  selectedTransaction: null,
 | 
				
			||||||
  selectedAccount: null,
 | 
					  selectedAccount: null,
 | 
				
			||||||
  selectedDestAccount: null,
 | 
					 | 
				
			||||||
  selectedFlags: null,
 | 
					  selectedFlags: null,
 | 
				
			||||||
 | 
					  hookParameters: {},
 | 
				
			||||||
 | 
					  memos: {},
 | 
				
			||||||
 | 
					  editorIsSaved: true,
 | 
				
			||||||
  txIsLoading: false,
 | 
					  txIsLoading: false,
 | 
				
			||||||
  txIsDisabled: false,
 | 
					  txIsDisabled: false,
 | 
				
			||||||
  txFields: {},
 | 
					  txFields: {},
 | 
				
			||||||
@@ -109,46 +129,51 @@ export const modifyTxState = (
 | 
				
			|||||||
  return tx.state
 | 
					  return tx.state
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// state to tx options
 | 
					 | 
				
			||||||
export const prepareTransaction = (data: any) => {
 | 
					export const prepareTransaction = (data: any) => {
 | 
				
			||||||
  let options = { ...data }
 | 
					  let options = { ...data }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Object.keys(options).forEach(field => {
 | 
					  Object.keys(options).forEach(field => {
 | 
				
			||||||
    let _value = options[field]
 | 
					    let _value = options[field]
 | 
				
			||||||
    // convert xrp
 | 
					    if (!typeIs(_value, 'object')) return
 | 
				
			||||||
    if (_value && typeof _value === 'object' && _value.$type === 'xrp') {
 | 
					    // amount.xrp
 | 
				
			||||||
      if (+_value.$value) {
 | 
					    if (_value.$type === 'amount.xrp') {
 | 
				
			||||||
        options[field] = (+_value.$value * 1000000 + '') as any
 | 
					      if (_value.$value) {
 | 
				
			||||||
 | 
					        options[field] = (+(_value as any).$value * 1000000 + '')
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        options[field] = undefined // 👇 💀
 | 
					        options[field] = ""
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    // handle type: `json`
 | 
					    // amount.token
 | 
				
			||||||
    if (_value && typeof _value === 'object' && _value.$type === 'json') {
 | 
					    if (_value.$type === 'amount.token') {
 | 
				
			||||||
      if (typeof _value.$value === 'object') {
 | 
					      if (typeIs(_value.$value, 'string')) {
 | 
				
			||||||
 | 
					        options[field] = parseJSON(_value.$value)
 | 
				
			||||||
 | 
					      } else if (typeIs(_value.$value, 'object')) {
 | 
				
			||||||
        options[field] = _value.$value
 | 
					        options[field] = _value.$value
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        try {
 | 
					        options[field] = undefined
 | 
				
			||||||
          options[field] = JSON.parse(_value.$value)
 | 
					 | 
				
			||||||
        } catch (error) {
 | 
					 | 
				
			||||||
          const message = `Input error for json field '${field}': ${error instanceof Error ? error.message : ''
 | 
					 | 
				
			||||||
            }`
 | 
					 | 
				
			||||||
          console.error(message)
 | 
					 | 
				
			||||||
          options[field] = _value.$value
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    // account
 | 
				
			||||||
 | 
					    if (_value.$type === 'account') {
 | 
				
			||||||
 | 
					      options[field] = (_value.$value as any)?.toString() || ""
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // json
 | 
				
			||||||
 | 
					    if (_value.$type === 'json') {
 | 
				
			||||||
 | 
					      const val = _value.$value;
 | 
				
			||||||
 | 
					      let res: any = val;
 | 
				
			||||||
 | 
					      if (typeIs(val, ["object", "array"])) {
 | 
				
			||||||
 | 
					        options[field] = res
 | 
				
			||||||
 | 
					      } else if (typeIs(val, "string") && (res = parseJSON(val))) {
 | 
				
			||||||
 | 
					        options[field] = res;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        options[field] = res;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // delete unnecessary fields
 | 
					 | 
				
			||||||
    if (!options[field]) {
 | 
					 | 
				
			||||||
      delete options[field]
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return options
 | 
					  return options
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// editor value to state
 | 
					 | 
				
			||||||
export const prepareState = (value: string, transactionType?: string) => {
 | 
					export const prepareState = (value: string, transactionType?: string) => {
 | 
				
			||||||
  const options = parseJSON(value)
 | 
					  const options = parseJSON(value)
 | 
				
			||||||
  if (!options) {
 | 
					  if (!options) {
 | 
				
			||||||
@@ -158,7 +183,7 @@ export const prepareState = (value: string, transactionType?: string) => {
 | 
				
			|||||||
    return
 | 
					    return
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const { Account, TransactionType, Destination, ...rest } = options
 | 
					  const { Account, TransactionType, HookParameters, Memos, ...rest } = options
 | 
				
			||||||
  let tx: Partial<TransactionState> = {}
 | 
					  let tx: Partial<TransactionState> = {}
 | 
				
			||||||
  const schema = getTxFields(transactionType)
 | 
					  const schema = getTxFields(transactionType)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -188,25 +213,23 @@ export const prepareState = (value: string, transactionType?: string) => {
 | 
				
			|||||||
    tx.selectedTransaction = null
 | 
					    tx.selectedTransaction = null
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (schema.Destination !== undefined) {
 | 
					  if (HookParameters && HookParameters instanceof Array) {
 | 
				
			||||||
    const dest = state.accounts.find(acc => acc.address === Destination)
 | 
					    tx.hookParameters = HookParameters.reduce<TransactionState["hookParameters"]>((acc, cur, idx) => {
 | 
				
			||||||
    if (dest) {
 | 
					      const param = { label: fromHex(cur.HookParameter?.HookParameterName || ""), value: cur.HookParameter?.HookParameterValue || "" }
 | 
				
			||||||
      tx.selectedDestAccount = {
 | 
					      acc[idx] = param;
 | 
				
			||||||
        label: dest.name,
 | 
					      return acc;
 | 
				
			||||||
        value: dest.address
 | 
					    }, {})
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    } else if (Destination) {
 | 
					
 | 
				
			||||||
      tx.selectedDestAccount = {
 | 
					  if (Memos && Memos instanceof Array) {
 | 
				
			||||||
        label: Destination,
 | 
					    tx.memos = Memos.reduce<TransactionState["memos"]>((acc, cur, idx) => {
 | 
				
			||||||
        value: Destination
 | 
					      const memo = { data: cur.Memo?.MemoData || "", type: fromHex(cur.Memo?.MemoType || ""), format: fromHex(cur.Memo?.MemoFormat || "") }
 | 
				
			||||||
      }
 | 
					      acc[idx] = memo;
 | 
				
			||||||
    } else {
 | 
					      return acc;
 | 
				
			||||||
      tx.selectedDestAccount = null
 | 
					    }, {})
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  } else if (Destination) {
 | 
					 | 
				
			||||||
    rest.Destination = Destination
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (getFlags(TransactionType) && rest.Flags) {
 | 
					  if (getFlags(TransactionType) && rest.Flags) {
 | 
				
			||||||
    const flags = extractFlags(TransactionType, rest.Flags)
 | 
					    const flags = extractFlags(TransactionType, rest.Flags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -217,17 +240,31 @@ export const prepareState = (value: string, transactionType?: string) => {
 | 
				
			|||||||
  Object.keys(rest).forEach(field => {
 | 
					  Object.keys(rest).forEach(field => {
 | 
				
			||||||
    const value = rest[field]
 | 
					    const value = rest[field]
 | 
				
			||||||
    const schemaVal = schema[field as keyof TxFields]
 | 
					    const schemaVal = schema[field as keyof TxFields]
 | 
				
			||||||
    const isXrp =
 | 
					
 | 
				
			||||||
      typeof value !== 'object' &&
 | 
					    const isAmount = schemaVal &&
 | 
				
			||||||
      schemaVal &&
 | 
					      typeIs(schemaVal, "object") &&
 | 
				
			||||||
      typeof schemaVal === 'object' &&
 | 
					      schemaVal.$type.startsWith('amount.');
 | 
				
			||||||
      schemaVal.$type === 'xrp'
 | 
					    const isAccount = schemaVal &&
 | 
				
			||||||
    if (isXrp) {
 | 
					      typeIs(schemaVal, "object") &&
 | 
				
			||||||
 | 
					      schemaVal.$type.startsWith("account");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (isAmount && ["number", "string"].includes(typeof value)) {
 | 
				
			||||||
      rest[field] = {
 | 
					      rest[field] = {
 | 
				
			||||||
        $type: 'xrp',
 | 
					        $type: 'amount.xrp', // TODO narrow typed $type.
 | 
				
			||||||
        $value: +value / 1000000 // ! maybe use bigint?
 | 
					        $value: +value / 1000000 // ! maybe use bigint?
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } else if (typeof value === 'object') {
 | 
					    } else if (isAmount && typeof value === 'object') {
 | 
				
			||||||
 | 
					      rest[field] = {
 | 
				
			||||||
 | 
					        $type: 'amount.token',
 | 
				
			||||||
 | 
					        $value: value
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else if (isAccount) {
 | 
				
			||||||
 | 
					      rest[field] = {
 | 
				
			||||||
 | 
					        $type: "account",
 | 
				
			||||||
 | 
					        $value: value?.toString() || ""
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (typeof value === 'object') {
 | 
				
			||||||
      rest[field] = {
 | 
					      rest[field] = {
 | 
				
			||||||
        $type: 'json',
 | 
					        $type: 'json',
 | 
				
			||||||
        $value: value
 | 
					        $value: value
 | 
				
			||||||
@@ -236,6 +273,7 @@ export const prepareState = (value: string, transactionType?: string) => {
 | 
				
			|||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  tx.txFields = rest
 | 
					  tx.txFields = rest
 | 
				
			||||||
 | 
					  tx.editorIsSaved = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return tx
 | 
					  return tx
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -246,12 +284,12 @@ export const getTxFields = (tt?: string) => {
 | 
				
			|||||||
  if (!txFields) return {}
 | 
					  if (!txFields) return {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let _txFields = Object.keys(txFields)
 | 
					  let _txFields = Object.keys(txFields)
 | 
				
			||||||
    .filter(key => !['TransactionType', 'Account', 'Sequence'].includes(key))
 | 
					    .filter(key => !commonFields.includes(key as any))
 | 
				
			||||||
    .reduce<TxFields>((tf, key) => ((tf[key as keyof TxFields] = (txFields as any)[key]), tf), {})
 | 
					    .reduce<TxFields>((tf, key) => ((tf[key as keyof TxFields] = (txFields as any)[key]), tf), {})
 | 
				
			||||||
  return _txFields
 | 
					  return _txFields
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { transactionsData }
 | 
					export { transactionsData, commonFields }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const transactionsOptions = transactionsData.map(tx => ({
 | 
					export const transactionsOptions = transactionsData.map(tx => ({
 | 
				
			||||||
  value: tx.TransactionType,
 | 
					  value: tx.TransactionType,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										31
									
								
								utils/accountInfo.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								utils/accountInfo.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					import toast from 'react-hot-toast'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { xrplSend } from '../state/actions/xrpl-client'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface AccountInfo {
 | 
				
			||||||
 | 
					    Account: string,
 | 
				
			||||||
 | 
					    Sequence: number,
 | 
				
			||||||
 | 
					    Flags: number,
 | 
				
			||||||
 | 
					    Balance?: string,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const fetchAccountInfo = async (
 | 
				
			||||||
 | 
					    address: string,
 | 
				
			||||||
 | 
					    opts: { silent?: boolean } = {}
 | 
				
			||||||
 | 
					): Promise<AccountInfo | undefined> => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					        const res = await xrplSend({
 | 
				
			||||||
 | 
					            id: `hooks-builder-req-info-${address}`,
 | 
				
			||||||
 | 
					            command: 'account_info',
 | 
				
			||||||
 | 
					            account: address
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        return res.account_data;
 | 
				
			||||||
 | 
					    } catch (err) {
 | 
				
			||||||
 | 
					        if (!opts.silent) {
 | 
				
			||||||
 | 
					            console.error(err)
 | 
				
			||||||
 | 
					            toast.error('Could not fetch account info!')
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default fetchAccountInfo
 | 
				
			||||||
@@ -24,6 +24,9 @@ const estimateFee = async (
 | 
				
			|||||||
    const { signedTransaction } = sign(copyTx, keypair)
 | 
					    const { signedTransaction } = sign(copyTx, keypair)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const res = await xrplSend({ command: 'fee', tx_blob: signedTransaction })
 | 
					    const res = await xrplSend({ command: 'fee', tx_blob: signedTransaction })
 | 
				
			||||||
 | 
					    if (res.error) {
 | 
				
			||||||
 | 
					      throw new Error(`[${res.error}] ${res.error_exception}.`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    if (res && res.drops) {
 | 
					    if (res && res.drops) {
 | 
				
			||||||
      return res.drops
 | 
					      return res.drops
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -31,7 +34,8 @@ const estimateFee = async (
 | 
				
			|||||||
  } catch (err) {
 | 
					  } catch (err) {
 | 
				
			||||||
    if (!opts.silent) {
 | 
					    if (!opts.silent) {
 | 
				
			||||||
      console.error(err)
 | 
					      console.error(err)
 | 
				
			||||||
      toast.error('Cannot estimate fee.') // ? Some better msg
 | 
					      const msg = err instanceof Error ? err.message : 'Error estimating fee!';
 | 
				
			||||||
 | 
					      toast.error(msg);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,3 +19,17 @@ export const getFileExtention = (filename?: string): string | undefined => {
 | 
				
			|||||||
  const ext = (filename.includes('.') && filename.split('.').pop()) || undefined
 | 
					  const ext = (filename.includes('.') && filename.split('.').pop()) || undefined
 | 
				
			||||||
  return ext
 | 
					  return ext
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Type = "array" | "undefined" | "object" | "string" | "number" | "bigint" | "boolean" | "symbol" | "function"
 | 
				
			||||||
 | 
					type obj = Record<string | number | symbol, unknown>
 | 
				
			||||||
 | 
					type arr = unknown[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const typeIs = <T extends Type>(arg: any, t: T | T[]): arg is unknown & (T extends "array" ? arr : T extends "undefined" ? undefined | null : T extends "object" ? obj : T extends "string" ? string : T extends "number" ? number : T extends "bigint" ? bigint : T extends "boolean" ? boolean : T extends "symbol" ? symbol : T extends "function" ? Function : never) => {
 | 
				
			||||||
 | 
					  const types = Array.isArray(t) ? t : [t]
 | 
				
			||||||
 | 
					  return types.includes(typeOf(arg) as T);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const typeOf = (arg: any): Type => {
 | 
				
			||||||
 | 
					  const type = arg instanceof Array ? 'array' : arg === null ? 'undefined' : typeof arg
 | 
				
			||||||
 | 
					  return type;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,9 +1,8 @@
 | 
				
			|||||||
 | 
					import { typeIs, typeOf } from './helpers'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const extractSchemaProps = <O extends object>(obj: O) =>
 | 
					export const extractSchemaProps = <O extends object>(obj: O) =>
 | 
				
			||||||
  Object.entries(obj).reduce((prev, [key, val]) => {
 | 
					  Object.entries(obj).reduce((prev, [key, val]) => {
 | 
				
			||||||
    const typeOf = <T>(arg: T) =>
 | 
					    const value = typeIs(val, "object") && '$type' in val && '$value' in val ? val?.$value : val
 | 
				
			||||||
      arg instanceof Array ? 'array' : arg === null ? 'undefined' : typeof arg
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const value = typeOf(val) === 'object' && '$type' in val && '$value' in val ? val?.$value : val
 | 
					 | 
				
			||||||
    const type = typeOf(value)
 | 
					    const type = typeOf(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let schema: any = {
 | 
					    let schema: any = {
 | 
				
			||||||
@@ -12,19 +11,19 @@ export const extractSchemaProps = <O extends object>(obj: O) =>
 | 
				
			|||||||
      default: value
 | 
					      default: value
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (typeOf(value) === 'array') {
 | 
					    if (typeIs(value, "array")) {
 | 
				
			||||||
      const item = value[0] // TODO merge other item schema's into one
 | 
					      const item = value[0] // TODO merge other item schema's into one
 | 
				
			||||||
      if (typeOf(item) !== 'object') {
 | 
					      if (typeIs(item, "object")) {
 | 
				
			||||||
        schema.items = {
 | 
					        schema.items = {
 | 
				
			||||||
          type: 'object',
 | 
					          type: 'object',
 | 
				
			||||||
          properties: extractSchemaProps(item),
 | 
					          properties: extractSchemaProps(item),
 | 
				
			||||||
          default: item
 | 
					          default: item
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      // TODO support primitive-value arrays
 | 
					      // TODO primitive-value arrays
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (typeOf(value) === 'object') {
 | 
					    if (typeIs(value, "object")) {
 | 
				
			||||||
      schema.properties = extractSchemaProps(value)
 | 
					      schema.properties = extractSchemaProps(value)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,13 @@ export type SetHookData = {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    $metaData?: any
 | 
					    $metaData?: any
 | 
				
			||||||
  }[]
 | 
					  }[]
 | 
				
			||||||
 | 
					  Memos?: {
 | 
				
			||||||
 | 
					    Memo: {
 | 
				
			||||||
 | 
					      MemoType: string,
 | 
				
			||||||
 | 
					      MemoData: string
 | 
				
			||||||
 | 
					      MemoFormat: string
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }[]
 | 
				
			||||||
  // HookGrants: {
 | 
					  // HookGrants: {
 | 
				
			||||||
  //   HookGrant: {
 | 
					  //   HookGrant: {
 | 
				
			||||||
  //     Authorize: string;
 | 
					  //     Authorize: string;
 | 
				
			||||||
@@ -67,3 +74,20 @@ export const getInvokeOptions = (content?: string) => {
 | 
				
			|||||||
  }))
 | 
					  }))
 | 
				
			||||||
  return invokeOptions
 | 
					  return invokeOptions
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function toHex(str: string) {
 | 
				
			||||||
 | 
					  var result = ''
 | 
				
			||||||
 | 
					  for (var i = 0; i < str.length; i++) {
 | 
				
			||||||
 | 
					    const hex = str.charCodeAt(i).toString(16)
 | 
				
			||||||
 | 
					    result += hex.padStart(2, '0')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return result.toUpperCase()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function fromHex(hex: string) {
 | 
				
			||||||
 | 
					  var str = ''
 | 
				
			||||||
 | 
					  for (var i = 0; i < hex.length; i += 2) {
 | 
				
			||||||
 | 
					    str += String.fromCharCode(parseInt(hex.substring(i, i + 2), 16))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return str
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user