Compare commits
15 Commits
feat/testn
...
fix/tx
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fce9af77c | ||
|
|
55c68c580a | ||
|
|
832a7997d1 | ||
|
|
4528e5a16e | ||
|
|
38f064c6d8 | ||
|
|
fbf4565dbc | ||
|
|
9001c64fed | ||
|
|
03b768db4e | ||
|
|
825af0db89 | ||
|
|
31043f33ab | ||
|
|
39699a1cb9 | ||
|
|
b50b300307 | ||
|
|
82c06cbb12 | ||
|
|
423ee18e6a | ||
|
|
3bb26d0c9b |
@@ -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 => ({
|
||||||
|
|||||||
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,43 @@ 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,
|
||||||
|
memos
|
||||||
|
} = 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) }
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
const Memos = memos
|
||||||
|
? Object.entries(memos).reduce<SetHookData['Memos']>((acc, [_, { format, data, type }]) => {
|
||||||
|
return acc?.concat({
|
||||||
|
Memo: { MemoData: toHex(data), MemoFormat: toHex(format), MemoType: toHex(type) }
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
: undefined
|
||||||
|
|
||||||
return prepareTransaction({
|
return prepareTransaction({
|
||||||
...txFields,
|
...txFields,
|
||||||
|
HookParameters,
|
||||||
Flags,
|
Flags,
|
||||||
TransactionType,
|
TransactionType,
|
||||||
Destination,
|
Destination,
|
||||||
Account
|
Account,
|
||||||
|
Memos
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[txState]
|
[txState]
|
||||||
@@ -69,15 +93,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)
|
||||||
@@ -108,23 +146,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)
|
||||||
@@ -177,11 +210,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 +232,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 +253,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,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,18 +18,34 @@ 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'
|
||||||
|
import AccountSequence from '../Sequence'
|
||||||
|
|
||||||
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 {
|
||||||
txState
|
selectedAccount,
|
||||||
|
selectedDestAccount,
|
||||||
|
selectedTransaction,
|
||||||
|
txFields,
|
||||||
|
selectedFlags,
|
||||||
|
hookParameters,
|
||||||
|
memos
|
||||||
|
} = txState
|
||||||
|
|
||||||
const accountOptions: SelectOption[] = accounts.map(acc => ({
|
const accountOptions: SelectOption[] = accounts.map(acc => ({
|
||||||
label: acc.name,
|
label: acc.name,
|
||||||
@@ -94,8 +110,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
|
||||||
@@ -110,7 +124,7 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estima
|
|||||||
[selectedTransaction?.value]
|
[selectedTransaction?.value]
|
||||||
)
|
)
|
||||||
|
|
||||||
const richFields = ['TransactionType', 'Account']
|
const richFields = ['TransactionType', 'Account', 'HookParameters', 'Memos']
|
||||||
if (fields.Destination !== undefined) {
|
if (fields.Destination !== undefined) {
|
||||||
richFields.push('Destination')
|
richFields.push('Destination')
|
||||||
}
|
}
|
||||||
@@ -120,7 +134,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 +143,44 @@ 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>
|
||||||
|
<TxField label="Sequence">
|
||||||
|
<AccountSequence address={selectedAccount?.value} />
|
||||||
|
</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 +192,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,19 +214,7 @@ 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
|
|
||||||
row
|
|
||||||
fluid
|
|
||||||
css={{
|
|
||||||
justifyContent: 'flex-end',
|
|
||||||
alignItems: 'center',
|
|
||||||
position: 'relative'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text muted css={{ mr: '$3' }}>
|
|
||||||
{field + (isXrp ? ' (XRP)' : '')}:{' '}
|
|
||||||
</Text>
|
|
||||||
{isJson ? (
|
{isJson ? (
|
||||||
<Textarea
|
<Textarea
|
||||||
rows={rows}
|
rows={rows}
|
||||||
@@ -271,7 +222,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 +248,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 +282,171 @@ 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"
|
||||||
|
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"
|
||||||
|
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 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|||||||
@@ -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,28 +5,46 @@ 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 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
|
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 = {
|
||||||
@@ -34,6 +52,9 @@ export const defaultTransaction: TransactionState = {
|
|||||||
selectedAccount: null,
|
selectedAccount: null,
|
||||||
selectedDestAccount: null,
|
selectedDestAccount: null,
|
||||||
selectedFlags: null,
|
selectedFlags: null,
|
||||||
|
hookParameters: {},
|
||||||
|
memos: {},
|
||||||
|
editorIsSaved: true,
|
||||||
txIsLoading: false,
|
txIsLoading: false,
|
||||||
txIsDisabled: false,
|
txIsDisabled: false,
|
||||||
txFields: {},
|
txFields: {},
|
||||||
@@ -158,7 +179,7 @@ export const prepareState = (value: string, transactionType?: string) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const { Account, TransactionType, Destination, ...rest } = options
|
const { Account, TransactionType, Destination, HookParameters, Memos, ...rest } = options
|
||||||
let tx: Partial<TransactionState> = {}
|
let tx: Partial<TransactionState> = {}
|
||||||
const schema = getTxFields(transactionType)
|
const schema = getTxFields(transactionType)
|
||||||
|
|
||||||
@@ -188,6 +209,22 @@ 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 (Memos && Memos instanceof Array) {
|
||||||
|
tx.memos = Memos.reduce<TransactionState["memos"]>((acc, cur, idx) => {
|
||||||
|
const memo = { data: fromHex(cur.Memo?.MemoData || ""), type: fromHex(cur.Memo?.MemoType || ""), format: fromHex(cur.Memo?.MemoFormat || "") }
|
||||||
|
acc[idx] = memo;
|
||||||
|
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) {
|
||||||
@@ -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
|
||||||
@@ -23,7 +23,8 @@ export const tts = {
|
|||||||
ttNFTOKEN_BURN: 26,
|
ttNFTOKEN_BURN: 26,
|
||||||
ttNFTOKEN_CREATE_OFFER: 27,
|
ttNFTOKEN_CREATE_OFFER: 27,
|
||||||
ttNFTOKEN_CANCEL_OFFER: 28,
|
ttNFTOKEN_CANCEL_OFFER: 28,
|
||||||
ttNFTOKEN_ACCEPT_OFFER: 29
|
ttNFTOKEN_ACCEPT_OFFER: 29,
|
||||||
|
ttINVOKE: 99,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TTS = typeof tts
|
export type TTS = typeof tts
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -74,3 +81,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