Merge pull request #286 from XRPLF/feat/tx-params-ui
HookParameters UI for transactions.
This commit is contained in:
@@ -38,7 +38,8 @@ const Select = forwardRef<any, Props>((props, ref) => {
|
|||||||
container: provided => {
|
container: provided => {
|
||||||
return {
|
return {
|
||||||
...provided,
|
...provided,
|
||||||
position: 'relative'
|
position: 'relative',
|
||||||
|
width: '100%'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
singleValue: provided => ({
|
singleValue: provided => ({
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react'
|
import { FC, ReactNode, useCallback, useEffect, useMemo, 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'
|
||||||
@@ -18,6 +18,7 @@ import { streamState } from '../DebugStream'
|
|||||||
import { Button } from '..'
|
import { 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'
|
||||||
|
|
||||||
interface UIProps {
|
interface UIProps {
|
||||||
setState: (pTx?: Partial<TransactionState> | undefined) => TransactionState | undefined
|
setState: (pTx?: Partial<TransactionState> | undefined) => TransactionState | undefined
|
||||||
@@ -28,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,
|
||||||
@@ -110,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')
|
||||||
}
|
}
|
||||||
@@ -120,7 +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]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
css={{
|
css={{
|
||||||
@@ -130,94 +136,41 @@ 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') && (
|
{richFields.includes('Destination') && (
|
||||||
<Flex
|
<TxField label="Destination account">
|
||||||
row
|
|
||||||
fluid
|
|
||||||
css={{
|
|
||||||
justifyContent: 'flex-end',
|
|
||||||
alignItems: 'center',
|
|
||||||
mb: '$3',
|
|
||||||
pr: '1px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text muted css={{ mr: '$3', textAlign: 'end' }}>
|
|
||||||
Destination account:{' '}
|
|
||||||
</Text>
|
|
||||||
<Select
|
<Select
|
||||||
instanceId="to-account"
|
instanceId="to-account"
|
||||||
placeholder="Select the destination account"
|
placeholder="Select the destination account"
|
||||||
css={{ width: '70%' }}
|
|
||||||
options={destAccountOptions}
|
options={destAccountOptions}
|
||||||
value={selectedDestAccount}
|
value={selectedDestAccount}
|
||||||
isClearable
|
isClearable
|
||||||
onChange={(acc: any) => setState({ selectedDestAccount: acc })}
|
onChange={(acc: any) => setState({ selectedDestAccount: acc })}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</TxField>
|
||||||
)}
|
)}
|
||||||
{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,7 +182,7 @@ 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]
|
||||||
@@ -251,93 +204,162 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estima
|
|||||||
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
|
||||||
return (
|
return (
|
||||||
<Flex column key={field} css={{ mb: '$2', pr: '1px' }}>
|
<TxField key={field} label={field + (isXrp ? ' (XRP)' : '')}>
|
||||||
<Flex
|
{isJson ? (
|
||||||
row
|
<Textarea
|
||||||
fluid
|
rows={rows}
|
||||||
css={{
|
value={value}
|
||||||
justifyContent: 'flex-end',
|
spellCheck={false}
|
||||||
alignItems: 'center',
|
onChange={switchToJson}
|
||||||
position: 'relative'
|
css={{
|
||||||
}}
|
flex: 'inherit',
|
||||||
>
|
resize: 'vertical'
|
||||||
<Text muted css={{ mr: '$3' }}>
|
}}
|
||||||
{field + (isXrp ? ' (XRP)' : '')}:{' '}
|
/>
|
||||||
</Text>
|
) : (
|
||||||
{isJson ? (
|
<Input
|
||||||
<Textarea
|
type={isFee ? 'number' : 'text'}
|
||||||
rows={rows}
|
value={value}
|
||||||
value={value}
|
onChange={e => {
|
||||||
spellCheck={false}
|
if (isFee) {
|
||||||
onChange={switchToJson}
|
const val = e.target.value.replaceAll('.', '').replaceAll(',', '')
|
||||||
css={{
|
handleSetField(field, val)
|
||||||
width: '70%',
|
} else {
|
||||||
flex: 'inherit',
|
handleSetField(field, e.target.value)
|
||||||
resize: 'vertical'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Input
|
|
||||||
type={isFee ? 'number' : 'text'}
|
|
||||||
value={value}
|
|
||||||
onChange={e => {
|
|
||||||
if (isFee) {
|
|
||||||
const val = e.target.value.replaceAll('.', '').replaceAll(',', '')
|
|
||||||
handleSetField(field, val)
|
|
||||||
} else {
|
|
||||||
handleSetField(field, e.target.value)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onKeyPress={
|
|
||||||
isFee
|
|
||||||
? e => {
|
|
||||||
if (e.key === '.' || e.key === ',') {
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
: undefined
|
|
||||||
}
|
}
|
||||||
css={{
|
}}
|
||||||
width: '70%',
|
onKeyPress={
|
||||||
flex: 'inherit',
|
isFee
|
||||||
'-moz-appearance': 'textfield',
|
? e => {
|
||||||
'&::-webkit-outer-spin-button': {
|
if (e.key === '.' || e.key === ',') {
|
||||||
'-webkit-appearance': 'none',
|
e.preventDefault()
|
||||||
margin: 0
|
}
|
||||||
},
|
}
|
||||||
'&::-webkit-inner-spin-button ': {
|
: undefined
|
||||||
'-webkit-appearance': 'none',
|
}
|
||||||
margin: 0
|
css={{
|
||||||
}
|
flex: 'inherit',
|
||||||
}}
|
'-moz-appearance': 'textfield',
|
||||||
/>
|
'&::-webkit-outer-spin-button': {
|
||||||
)}
|
'-webkit-appearance': 'none',
|
||||||
{isFee && (
|
margin: 0
|
||||||
<Button
|
},
|
||||||
size="xs"
|
'&::-webkit-inner-spin-button ': {
|
||||||
variant="primary"
|
'-webkit-appearance': 'none',
|
||||||
outline
|
margin: 0
|
||||||
disabled={txState.txIsDisabled}
|
}
|
||||||
isDisabled={txState.txIsDisabled}
|
}}
|
||||||
isLoading={feeLoading}
|
/>
|
||||||
css={{
|
)}
|
||||||
position: 'absolute',
|
{isFee && (
|
||||||
right: '$2',
|
<Button
|
||||||
fontSize: '$xs',
|
size="xs"
|
||||||
cursor: 'pointer',
|
variant="primary"
|
||||||
alignContent: 'center',
|
outline
|
||||||
display: 'flex'
|
disabled={txState.txIsDisabled}
|
||||||
}}
|
isDisabled={txState.txIsDisabled}
|
||||||
onClick={() => handleEstimateFee()}
|
isLoading={feeLoading}
|
||||||
>
|
css={{
|
||||||
Suggest
|
position: 'absolute',
|
||||||
</Button>
|
right: '$2',
|
||||||
)}
|
fontSize: '$xs',
|
||||||
</Flex>
|
cursor: 'pointer',
|
||||||
</Flex>
|
alignContent: 'center',
|
||||||
|
display: 'flex'
|
||||||
|
}}
|
||||||
|
onClick={() => handleEstimateFee()}
|
||||||
|
>
|
||||||
|
Suggest
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</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; multiLine?: boolean }> = ({
|
||||||
|
label,
|
||||||
|
children,
|
||||||
|
multiLine = false
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
row
|
||||||
|
fluid
|
||||||
|
css={{
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
alignItems: multiLine ? 'flex-start' : 'center',
|
||||||
|
position: 'relative',
|
||||||
|
mb: '$3',
|
||||||
|
mt: '1px',
|
||||||
|
pr: '1px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text muted css={{ mr: '$3', mt: multiLine ? '$2' : 0 }}>
|
||||||
|
{label}:{' '}
|
||||||
|
</Text>
|
||||||
|
<Flex css={{ width: '70%', alignItems: 'center' }}>{children}</Flex>
|
||||||
|
</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
|
||||||
@@ -24,9 +30,11 @@ export interface TransactionState {
|
|||||||
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 = {
|
||||||
@@ -34,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: {},
|
||||||
@@ -158,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)
|
||||||
|
|
||||||
@@ -188,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) {
|
||||||
@@ -246,12 +263,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,
|
||||||
|
|||||||
@@ -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