Add flags UI to Payment transaction.
This commit is contained in:
@@ -19,6 +19,7 @@ import { TxJson } from './json'
|
||||
import { TxUI } from './ui'
|
||||
import { default as _estimateFee } from '../../utils/estimateFee'
|
||||
import toast from 'react-hot-toast'
|
||||
import { combineFlags, extractFlags, transactionFlags } from '../../state/constants/flags'
|
||||
|
||||
export interface TransactionProps {
|
||||
header: string
|
||||
@@ -39,14 +40,17 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
|
||||
|
||||
const prepareOptions = useCallback(
|
||||
(state: Partial<TransactionState> = txState) => {
|
||||
const { selectedTransaction, selectedDestAccount, selectedAccount, txFields } = state
|
||||
const { selectedTransaction, selectedDestAccount, selectedAccount, txFields, selectedFlags } =
|
||||
state
|
||||
|
||||
const TransactionType = selectedTransaction?.value || null
|
||||
const Destination = selectedDestAccount?.value || txFields?.Destination
|
||||
const Account = selectedAccount?.value || null
|
||||
const Flags = combineFlags(selectedFlags?.map(flag => flag.value)) || txFields?.Flags
|
||||
|
||||
return prepareTransaction({
|
||||
...txFields,
|
||||
Flags,
|
||||
TransactionType,
|
||||
Destination,
|
||||
Account
|
||||
@@ -136,8 +140,13 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
|
||||
} else {
|
||||
fields.Destination = undefined
|
||||
}
|
||||
nwState.txFields = fields
|
||||
|
||||
if (transactionType?.value && transactionFlags[transactionType?.value] && fields.Flags) {
|
||||
nwState.selectedFlags = extractFlags(transactionType.value, fields.Flags)
|
||||
fields.Flags = undefined
|
||||
}
|
||||
|
||||
nwState.txFields = fields
|
||||
const state = modifyTxState(header, nwState, { replaceState: true })
|
||||
const editorValue = getJsonString(state)
|
||||
return setState({ editorValue })
|
||||
@@ -179,7 +188,12 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
|
||||
estimateFee={estimateFee}
|
||||
/>
|
||||
) : (
|
||||
<TxUI state={txState} setState={setState} estimateFee={estimateFee} />
|
||||
<TxUI
|
||||
state={txState}
|
||||
resetState={resetState}
|
||||
setState={setState}
|
||||
estimateFee={estimateFee}
|
||||
/>
|
||||
)}
|
||||
<Flex
|
||||
row
|
||||
|
||||
@@ -17,16 +17,19 @@ import state from '../../state'
|
||||
import { streamState } from '../DebugStream'
|
||||
import { Button } from '..'
|
||||
import Textarea from '../Textarea'
|
||||
import { transactionFlags } from '../../state/constants/flags'
|
||||
|
||||
interface UIProps {
|
||||
setState: (pTx?: Partial<TransactionState> | undefined) => TransactionState | undefined
|
||||
resetState: (tt?: SelectOption) => TransactionState | undefined
|
||||
state: TransactionState
|
||||
estimateFee?: (...arg: any) => Promise<string | undefined>
|
||||
}
|
||||
|
||||
export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) => {
|
||||
export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estimateFee }) => {
|
||||
const { accounts } = useSnapshot(state)
|
||||
const { selectedAccount, selectedDestAccount, selectedTransaction, txFields } = txState
|
||||
const { selectedAccount, selectedDestAccount, selectedTransaction, txFields, selectedFlags } =
|
||||
txState
|
||||
|
||||
const accountOptions: SelectOption[] = accounts.map(acc => ({
|
||||
label: acc.name,
|
||||
@@ -40,23 +43,15 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) =>
|
||||
}))
|
||||
.filter(acc => acc.value !== selectedAccount?.value)
|
||||
|
||||
const flagsOptions: SelectOption[] = Object.entries(
|
||||
transactionFlags[selectedTransaction?.value || ''] || {}
|
||||
).map(([label, value]) => ({
|
||||
label,
|
||||
value
|
||||
}))
|
||||
|
||||
const [feeLoading, setFeeLoading] = useState(false)
|
||||
|
||||
const resetFields = useCallback(
|
||||
(tt: string) => {
|
||||
const fields = getTxFields(tt)
|
||||
|
||||
if (fields.Destination !== undefined) {
|
||||
setState({ selectedDestAccount: null })
|
||||
fields.Destination = ''
|
||||
} else {
|
||||
fields.Destination = undefined
|
||||
}
|
||||
return setState({ txFields: fields })
|
||||
},
|
||||
[setState]
|
||||
)
|
||||
|
||||
const handleSetAccount = (acc: SelectOption) => {
|
||||
setState({ selectedAccount: acc })
|
||||
streamState.selectedAccount = acc
|
||||
@@ -92,11 +87,11 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) =>
|
||||
(tt: SelectOption) => {
|
||||
setState({ selectedTransaction: tt })
|
||||
|
||||
const newState = resetFields(tt.value)
|
||||
const newState = resetState(tt)
|
||||
|
||||
handleEstimateFee(newState, true)
|
||||
},
|
||||
[handleEstimateFee, resetFields, setState]
|
||||
[handleEstimateFee, resetState, setState]
|
||||
)
|
||||
|
||||
const switchToJson = () => setState({ viewType: 'json' })
|
||||
@@ -115,12 +110,16 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) =>
|
||||
[selectedTransaction?.value]
|
||||
)
|
||||
|
||||
const specialFields = ['TransactionType', 'Account']
|
||||
const richFields = ['TransactionType', 'Account']
|
||||
if (fields.Destination !== undefined) {
|
||||
specialFields.push('Destination')
|
||||
richFields.push('Destination')
|
||||
}
|
||||
|
||||
const otherFields = Object.keys(txFields).filter(k => !specialFields.includes(k)) as [
|
||||
if (flagsOptions.length) {
|
||||
richFields.push('Flags')
|
||||
}
|
||||
|
||||
const otherFields = Object.keys(txFields).filter(k => !richFields.includes(k)) as [
|
||||
keyof TxFields
|
||||
]
|
||||
|
||||
@@ -179,7 +178,7 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) =>
|
||||
onChange={(acc: any) => handleSetAccount(acc)} // TODO make react-select have correct types for acc
|
||||
/>
|
||||
</Flex>
|
||||
{fields.Destination !== undefined && (
|
||||
{richFields.includes('Destination') && (
|
||||
<Flex
|
||||
row
|
||||
fluid
|
||||
@@ -204,6 +203,36 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) =>
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
{richFields.includes('Flags') && (
|
||||
<Flex
|
||||
row
|
||||
fluid
|
||||
css={{
|
||||
justifyContent: 'flex-end',
|
||||
alignItems: 'center',
|
||||
mb: '$3',
|
||||
pr: '1px'
|
||||
}}
|
||||
>
|
||||
<Text muted css={{ mr: '$3' }}>
|
||||
Flags:{' '}
|
||||
</Text>
|
||||
<Select
|
||||
isClearable
|
||||
css={{ width: '70%' }}
|
||||
instanceId="flags"
|
||||
placeholder="Select flags to apply"
|
||||
menuPosition="fixed"
|
||||
value={selectedFlags}
|
||||
isMulti
|
||||
options={flagsOptions}
|
||||
onChange={flags => setState({ selectedFlags: flags as any })}
|
||||
closeMenuOnSelect={
|
||||
selectedFlags ? selectedFlags.length >= flagsOptions.length - 1 : false
|
||||
}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
{otherFields.map(field => {
|
||||
let _value = txFields[field]
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"DestinationTag": 13,
|
||||
"Fee": "2000000",
|
||||
"Sequence": 2470665,
|
||||
"Flags": 2147483648
|
||||
"Flags": "2147483648"
|
||||
},
|
||||
{
|
||||
"TransactionType": "AccountSet",
|
||||
@@ -48,7 +48,7 @@
|
||||
"Account": "rsUiUMpnrgxQp24dJYZDhmV4bE3aBtQyt8",
|
||||
"Authorize": "rEhxGqkqPPSxQ3P25J66ft5TwpzV14k2de",
|
||||
"Fee": "10",
|
||||
"Flags": 2147483648,
|
||||
"Flags": "2147483648",
|
||||
"Sequence": 2
|
||||
},
|
||||
{
|
||||
@@ -109,7 +109,9 @@
|
||||
"Fee": "10",
|
||||
"NFTokenOffers": {
|
||||
"$type": "json",
|
||||
"$value": ["4AAAEEA76E3C8148473CB3840CE637676E561FB02BD4CA22CA59729EA815B862"]
|
||||
"$value": [
|
||||
"4AAAEEA76E3C8148473CB3840CE637676E561FB02BD4CA22CA59729EA815B862"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -120,7 +122,7 @@
|
||||
"$value": "100",
|
||||
"$type": "xrp"
|
||||
},
|
||||
"Flags": 1,
|
||||
"Flags": "1",
|
||||
"Destination": "",
|
||||
"Fee": "10"
|
||||
},
|
||||
@@ -128,7 +130,7 @@
|
||||
"TransactionType": "OfferCancel",
|
||||
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"Fee": "12",
|
||||
"Flags": 0,
|
||||
"Flags": "0",
|
||||
"LastLedgerSequence": 7108629,
|
||||
"OfferSequence": 6,
|
||||
"Sequence": 7
|
||||
@@ -137,7 +139,7 @@
|
||||
"TransactionType": "OfferCreate",
|
||||
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"Fee": "12",
|
||||
"Flags": 0,
|
||||
"Flags": "0",
|
||||
"LastLedgerSequence": 7108682,
|
||||
"Sequence": 8,
|
||||
"TakerGets": "6000000",
|
||||
@@ -155,7 +157,7 @@
|
||||
"$type": "xrp"
|
||||
},
|
||||
"Fee": "12",
|
||||
"Flags": 2147483648,
|
||||
"Flags": "2147483648",
|
||||
"Sequence": 2
|
||||
},
|
||||
{
|
||||
@@ -185,14 +187,14 @@
|
||||
"Fee": "10"
|
||||
},
|
||||
{
|
||||
"Flags": 0,
|
||||
"Flags": "0",
|
||||
"TransactionType": "SetRegularKey",
|
||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"Fee": "12",
|
||||
"RegularKey": "rAR8rR8sUkBoCZFawhkWzY4Y5YoyuznwD"
|
||||
},
|
||||
{
|
||||
"Flags": 0,
|
||||
"Flags": "0",
|
||||
"TransactionType": "SignerListSet",
|
||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"Fee": "12",
|
||||
@@ -232,7 +234,7 @@
|
||||
"TransactionType": "TrustSet",
|
||||
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"Fee": "12",
|
||||
"Flags": 262144,
|
||||
"Flags": "262144",
|
||||
"LastLedgerSequence": 8007750,
|
||||
"LimitAmount": {
|
||||
"$type": "json",
|
||||
|
||||
33
state/constants/flags.ts
Normal file
33
state/constants/flags.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { SelectOption } from '../transactions';
|
||||
|
||||
interface Flags {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export const transactionFlags: { [key: /* TransactionType */ string]: Flags } = {
|
||||
Payment: {
|
||||
tfNoDirectRipple: "0x00010000",
|
||||
tfPartialPayment: "0x00020000",
|
||||
tfLimitQuality: "0x00040000",
|
||||
},
|
||||
// TODO Add more here
|
||||
}
|
||||
|
||||
|
||||
export function combineFlags(flags?: string[]): string | undefined {
|
||||
if (!flags) return
|
||||
|
||||
const num = flags.reduce((cumm, curr) => cumm | BigInt(curr), BigInt(0))
|
||||
return num.toString()
|
||||
}
|
||||
|
||||
export function extractFlags(transactionType: string, flags?: string | number,): SelectOption[] {
|
||||
const flagsObj = transactionFlags[transactionType]
|
||||
if (!flags || !flagsObj) return []
|
||||
|
||||
const extracted = Object.entries(flagsObj).reduce((cumm, [label, value]) => {
|
||||
return (BigInt(flags) & BigInt(value)) ? cumm.concat({ label, value }) : cumm
|
||||
}, [] as SelectOption[])
|
||||
|
||||
return extracted
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import transactionsData from '../content/transactions.json'
|
||||
import state from '.'
|
||||
import { showAlert } from '../state/actions/showAlert'
|
||||
import { parseJSON } from '../utils/json'
|
||||
import { extractFlags, transactionFlags } from './constants/flags'
|
||||
|
||||
export type SelectOption = {
|
||||
value: string
|
||||
@@ -14,6 +15,7 @@ export interface TransactionState {
|
||||
selectedTransaction: SelectOption | null
|
||||
selectedAccount: SelectOption | null
|
||||
selectedDestAccount: SelectOption | null
|
||||
selectedFlags: SelectOption[] | null
|
||||
txIsLoading: boolean
|
||||
txIsDisabled: boolean
|
||||
txFields: TxFields
|
||||
@@ -31,6 +33,7 @@ export const defaultTransaction: TransactionState = {
|
||||
selectedTransaction: null,
|
||||
selectedAccount: null,
|
||||
selectedDestAccount: null,
|
||||
selectedFlags: null,
|
||||
txIsLoading: false,
|
||||
txIsDisabled: false,
|
||||
txFields: {},
|
||||
@@ -128,8 +131,7 @@ export const prepareTransaction = (data: any) => {
|
||||
try {
|
||||
options[field] = JSON.parse(_value.$value)
|
||||
} catch (error) {
|
||||
const message = `Input error for json field '${field}': ${
|
||||
error instanceof Error ? error.message : ''
|
||||
const message = `Input error for json field '${field}': ${error instanceof Error ? error.message : ''
|
||||
}`
|
||||
console.error(message)
|
||||
options[field] = _value.$value
|
||||
@@ -205,6 +207,13 @@ export const prepareState = (value: string, transactionType?: string) => {
|
||||
rest.Destination = Destination
|
||||
}
|
||||
|
||||
if (transactionFlags[TransactionType] && rest.Flags) {
|
||||
const flags = extractFlags(TransactionType, rest.Flags)
|
||||
|
||||
rest.Flags = undefined
|
||||
tx.selectedFlags = flags
|
||||
}
|
||||
|
||||
Object.keys(rest).forEach(field => {
|
||||
const value = rest[field]
|
||||
const schemaVal = schema[field as keyof TxFields]
|
||||
|
||||
Reference in New Issue
Block a user