HookParameters UI for transactions.
This commit is contained in:
		@@ -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,16 +41,30 @@ 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,
 | 
				
			||||||
 | 
					        selectedDestAccount,
 | 
				
			||||||
 | 
					        selectedAccount,
 | 
				
			||||||
 | 
					        txFields,
 | 
				
			||||||
 | 
					        selectedFlags,
 | 
				
			||||||
 | 
					        hookParameters
 | 
				
			||||||
 | 
					      } = state
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const TransactionType = selectedTransaction?.value || null
 | 
					      const TransactionType = selectedTransaction?.value || null
 | 
				
			||||||
      const Destination = selectedDestAccount?.value || txFields?.Destination
 | 
					      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: toHex(value) }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      }, [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return prepareTransaction({
 | 
					      return prepareTransaction({
 | 
				
			||||||
        ...txFields,
 | 
					        ...txFields,
 | 
				
			||||||
 | 
					        HookParameters,
 | 
				
			||||||
        Flags,
 | 
					        Flags,
 | 
				
			||||||
        TransactionType,
 | 
					        TransactionType,
 | 
				
			||||||
        Destination,
 | 
					        Destination,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,8 +29,14 @@ interface UIProps {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estimateFee }) => {
 | 
					export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estimateFee }) => {
 | 
				
			||||||
  const { accounts } = useSnapshot(state)
 | 
					  const { accounts } = useSnapshot(state)
 | 
				
			||||||
  const { selectedAccount, selectedDestAccount, selectedTransaction, txFields, selectedFlags } =
 | 
					  const {
 | 
				
			||||||
    txState
 | 
					    selectedAccount,
 | 
				
			||||||
 | 
					    selectedDestAccount,
 | 
				
			||||||
 | 
					    selectedTransaction,
 | 
				
			||||||
 | 
					    txFields,
 | 
				
			||||||
 | 
					    selectedFlags,
 | 
				
			||||||
 | 
					    hookParameters
 | 
				
			||||||
 | 
					  } = txState
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const accountOptions: SelectOption[] = accounts.map(acc => ({
 | 
					  const accountOptions: SelectOption[] = accounts.map(acc => ({
 | 
				
			||||||
    label: acc.name,
 | 
					    label: acc.name,
 | 
				
			||||||
@@ -111,7 +117,7 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estima
 | 
				
			|||||||
    [selectedTransaction?.value]
 | 
					    [selectedTransaction?.value]
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const richFields = ['TransactionType', 'Account']
 | 
					  const richFields = ['TransactionType', 'Account', 'HookParameters']
 | 
				
			||||||
  if (fields.Destination !== undefined) {
 | 
					  if (fields.Destination !== undefined) {
 | 
				
			||||||
    richFields.push('Destination')
 | 
					    richFields.push('Destination')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -121,8 +127,6 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estima
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  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 hookParams = [{ id: 1 }]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Container
 | 
					    <Container
 | 
				
			||||||
      css={{
 | 
					      css={{
 | 
				
			||||||
@@ -271,15 +275,73 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estima
 | 
				
			|||||||
            </TxField>
 | 
					            </TxField>
 | 
				
			||||||
          )
 | 
					          )
 | 
				
			||||||
        })}
 | 
					        })}
 | 
				
			||||||
 | 
					        <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"
 | 
				
			||||||
 | 
					                    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>
 | 
				
			||||||
      </Flex>
 | 
					      </Flex>
 | 
				
			||||||
    </Container>
 | 
					    </Container>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const TxField: FC<{ label: string; children: ReactNode; isMulti?: boolean }> = ({
 | 
					export const TxField: FC<{ label: string; children: ReactNode; multiLine?: boolean }> = ({
 | 
				
			||||||
  label,
 | 
					  label,
 | 
				
			||||||
  children,
 | 
					  children,
 | 
				
			||||||
  isMulti = false
 | 
					  multiLine = false
 | 
				
			||||||
}) => {
 | 
					}) => {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Flex
 | 
					    <Flex
 | 
				
			||||||
@@ -287,14 +349,14 @@ export const TxField: FC<{ label: string; children: ReactNode; isMulti?: boolean
 | 
				
			|||||||
      fluid
 | 
					      fluid
 | 
				
			||||||
      css={{
 | 
					      css={{
 | 
				
			||||||
        justifyContent: 'flex-end',
 | 
					        justifyContent: 'flex-end',
 | 
				
			||||||
        alignItems: isMulti ? 'flex-start' : 'center',
 | 
					        alignItems: multiLine ? 'flex-start' : 'center',
 | 
				
			||||||
        position: 'relative',
 | 
					        position: 'relative',
 | 
				
			||||||
        mb: '$3',
 | 
					        mb: '$3',
 | 
				
			||||||
        mt: '1px',
 | 
					        mt: '1px',
 | 
				
			||||||
        pr: '1px'
 | 
					        pr: '1px'
 | 
				
			||||||
      }}
 | 
					      }}
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <Text muted css={{ mr: '$3', mt: isMulti ? '$2' : 0 }}>
 | 
					      <Text muted css={{ mr: '$3', mt: multiLine ? '$2' : 0 }}>
 | 
				
			||||||
        {label}:{' '}
 | 
					        {label}:{' '}
 | 
				
			||||||
      </Text>
 | 
					      </Text>
 | 
				
			||||||
      <Flex css={{ width: '70%', alignItems: 'center' }}>{children}</Flex>
 | 
					      <Flex css={{ width: '70%', alignItems: 'center' }}>{children}</Flex>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,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,17 +5,23 @@ 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'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type SelectOption = {
 | 
					export type SelectOption = {
 | 
				
			||||||
  value: string
 | 
					  value: string
 | 
				
			||||||
  label: string
 | 
					  label: string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type HookParameters = {
 | 
				
			||||||
 | 
					  [key: string]: SelectOption
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface TransactionState {
 | 
					export interface TransactionState {
 | 
				
			||||||
  selectedTransaction: SelectOption | null
 | 
					  selectedTransaction: SelectOption | null
 | 
				
			||||||
  selectedAccount: SelectOption | null
 | 
					  selectedAccount: SelectOption | null
 | 
				
			||||||
  selectedDestAccount: SelectOption | null
 | 
					  selectedDestAccount: SelectOption | null
 | 
				
			||||||
  selectedFlags: SelectOption[] | null
 | 
					  selectedFlags: SelectOption[] | null
 | 
				
			||||||
 | 
					  hookParameters: HookParameters
 | 
				
			||||||
  txIsLoading: boolean
 | 
					  txIsLoading: boolean
 | 
				
			||||||
  txIsDisabled: boolean
 | 
					  txIsDisabled: boolean
 | 
				
			||||||
  txFields: TxFields
 | 
					  txFields: TxFields
 | 
				
			||||||
@@ -36,6 +42,7 @@ export const defaultTransaction: TransactionState = {
 | 
				
			|||||||
  selectedAccount: null,
 | 
					  selectedAccount: null,
 | 
				
			||||||
  selectedDestAccount: null,
 | 
					  selectedDestAccount: null,
 | 
				
			||||||
  selectedFlags: null,
 | 
					  selectedFlags: null,
 | 
				
			||||||
 | 
					  hookParameters: {},
 | 
				
			||||||
  txIsLoading: false,
 | 
					  txIsLoading: false,
 | 
				
			||||||
  txIsDisabled: false,
 | 
					  txIsDisabled: false,
 | 
				
			||||||
  txFields: {},
 | 
					  txFields: {},
 | 
				
			||||||
@@ -160,7 +167,7 @@ export const prepareState = (value: string, transactionType?: string) => {
 | 
				
			|||||||
    return
 | 
					    return
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const { Account, TransactionType, Destination, ...rest } = options
 | 
					  const { Account, TransactionType, Destination, HookParameters, ...rest } = options
 | 
				
			||||||
  let tx: Partial<TransactionState> = {}
 | 
					  let tx: Partial<TransactionState> = {}
 | 
				
			||||||
  const schema = getTxFields(transactionType)
 | 
					  const schema = getTxFields(transactionType)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -190,6 +197,14 @@ export const prepareState = (value: string, transactionType?: string) => {
 | 
				
			|||||||
    tx.selectedTransaction = null
 | 
					    tx.selectedTransaction = null
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (HookParameters && HookParameters instanceof Array) {
 | 
				
			||||||
 | 
					    tx.hookParameters = HookParameters.reduce<TransactionState["hookParameters"]>((acc, cur, idx) => {
 | 
				
			||||||
 | 
					      const param = { label: fromHex(cur.HookParameter?.HookParameterName || ""), value: fromHex(cur.HookParameter?.HookParameterValue || "") }
 | 
				
			||||||
 | 
					      acc[idx] = param;
 | 
				
			||||||
 | 
					      return acc;
 | 
				
			||||||
 | 
					    }, {})
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (schema.Destination !== undefined) {
 | 
					  if (schema.Destination !== undefined) {
 | 
				
			||||||
    const dest = state.accounts.find(acc => acc.address === Destination)
 | 
					    const dest = state.accounts.find(acc => acc.address === Destination)
 | 
				
			||||||
    if (dest) {
 | 
					    if (dest) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,3 +74,19 @@ export const getInvokeOptions = (content?: string) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  return invokeOptions
 | 
					  return invokeOptions
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function toHex(str: string) {
 | 
				
			||||||
 | 
					  var result = ''
 | 
				
			||||||
 | 
					  for (var i = 0; i < str.length; i++) {
 | 
				
			||||||
 | 
					    result += str.charCodeAt(i).toString(16)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  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