Compare commits

...

18 Commits

Author SHA1 Message Date
muzam1l
dc37b1911a Take tx memo data and parameter value as hex. 2023-03-29 15:04:14 +05:30
muzamil
c348868c89 Merge pull request #302 from XRPLF/fix/hook-on
Fix HookOn initial value.
2023-03-28 16:28:35 +05:30
muzam1l
2c3cfebe3a Fix HookOn initial value. 2023-03-28 15:14:43 +05:30
muzamil
6265a9cdbf Merge pull request #300 from XRPLF/fix/to-hex
Fix hex logic.
2023-03-27 21:20:08 +05:30
muzamil
1321b498cf Merge pull request #299 from XRPLF/fix/invoke-tx
Add `Destination` field to Invoke transaction.
2023-03-27 21:18:35 +05:30
muzam1l
801d9778cb Fix hex logic. 2023-03-27 19:21:14 +05:30
muzam1l
2cf18ef61c Add Destination field to Invoke transaction. 2023-03-27 15:32:25 +05:30
muzamil
4d2dc16ce5 Merge pull request #296 from XRPLF/feat/account-ui
Extend transactions Account UI.
2023-03-23 20:45:27 +05:30
muzam1l
9ecf5478e6 Remove tx Destination default values. 2023-03-23 19:39:56 +05:30
muzam1l
6d1ef110b7 Token Issuer account UI. 2023-03-23 17:52:17 +05:30
muzam1l
b9edfcd63b Fix: nextjs build. 2023-03-23 16:55:32 +05:30
muzam1l
b653d9a9cb Fix: Remove last traces of custom Destination handling! 2023-03-23 16:47:25 +05:30
muzam1l
da28e0a7d1 Use "creatable select" for accounts. 2023-03-23 16:45:24 +05:30
muzam1l
8a5b83d57f Fix: Don't remove fields in JSON mode if empty. 2023-03-23 16:26:29 +05:30
muzam1l
025eff6cf2 Add Select UI for account. 2023-03-23 16:12:35 +05:30
muzam1l
62d521b2cc Add owner field to NFTokenCreateOffer. 2023-03-23 15:51:36 +05:30
muzam1l
7aafca21df Remove Destination as special field. 2023-03-23 15:49:17 +05:30
muzamil
80f58e903c Merge pull request #294 from XRPLF/feat/amount-ui
Transaction amount UI.
2023-03-20 14:04:30 +05:30
9 changed files with 264 additions and 207 deletions

View File

@@ -3,13 +3,11 @@ import { mauve, mauveDark, purple, purpleDark } from '@radix-ui/colors'
import { useTheme } from 'next-themes'
import { styled } from '../stitches.config'
import dynamic from 'next/dynamic'
import type { Props } from 'react-select'
import type { Props, StylesConfig } from 'react-select'
const SelectInput = dynamic(() => import('react-select'), { ssr: false })
const CreatableSelectInput = dynamic(() => import('react-select/creatable'), { ssr: false })
// eslint-disable-next-line react/display-name
const Select = forwardRef<any, Props>((props, ref) => {
const { theme } = useTheme()
const isDark = theme === 'dark'
const getColors = (isDark: boolean) => {
const colors: any = {
// primary: pink.pink9,
active: isDark ? purpleDark.purple9 : purple.purple9,
@@ -30,95 +28,136 @@ const Select = forwardRef<any, Props>((props, ref) => {
}
colors.outline = colors.background
colors.selected = colors.secondary
return colors
}
const getStyles = (isDark: boolean) => {
const colors = getColors(isDark)
const styles: StylesConfig = {
container: provided => {
return {
...provided,
position: 'relative',
width: '100%'
}
},
singleValue: provided => ({
...provided,
color: colors.mauve12
}),
menu: provided => ({
...provided,
backgroundColor: colors.dropDownBg
}),
control: (provided, state) => {
return {
...provided,
minHeight: 0,
border: '0px',
backgroundColor: colors.mauve4,
boxShadow: `0 0 0 1px ${state.isFocused ? colors.border : colors.secondary}`
}
},
input: provided => {
return {
...provided,
color: '$text'
}
},
multiValue: provided => {
return {
...provided,
backgroundColor: colors.mauve8
}
},
multiValueLabel: provided => {
return {
...provided,
color: colors.mauve12
}
},
multiValueRemove: provided => {
return {
...provided,
':hover': {
background: colors.mauve9
}
}
},
option: (provided, state) => {
return {
...provided,
color: colors.searchText,
backgroundColor: state.isFocused ? colors.activeLight : colors.dropDownBg,
':hover': {
backgroundColor: colors.active,
color: '#ffffff'
},
':selected': {
backgroundColor: 'red'
}
}
},
indicatorSeparator: provided => {
return {
...provided,
backgroundColor: colors.secondary
}
},
dropdownIndicator: (provided, state) => {
return {
...provided,
padding: 6,
color: state.isFocused ? colors.border : colors.secondary,
':hover': {
color: colors.border
}
}
},
clearIndicator: provided => {
return {
...provided,
padding: 6,
color: colors.secondary,
':hover': {
color: colors.border
}
}
}
}
return styles
}
// eslint-disable-next-line react/display-name
const Select = forwardRef<any, Props>((props, ref) => {
const { theme } = useTheme()
const isDark = theme === 'dark'
const styles = getStyles(isDark)
return (
<SelectInput
ref={ref}
menuPosition={props.menuPosition || 'fixed'}
styles={{
container: provided => {
return {
...provided,
position: 'relative',
width: '100%',
}
},
singleValue: provided => ({
...provided,
color: colors.mauve12
}),
menu: provided => ({
...provided,
backgroundColor: colors.dropDownBg
}),
control: (provided, state) => {
return {
...provided,
minHeight: 0,
border: '0px',
backgroundColor: colors.mauve4,
boxShadow: `0 0 0 1px ${state.isFocused ? colors.border : colors.secondary}`
}
},
input: provided => {
return {
...provided,
color: '$text'
}
},
multiValue: provided => {
return {
...provided,
backgroundColor: colors.mauve8
}
},
multiValueLabel: provided => {
return {
...provided,
color: colors.mauve12
}
},
multiValueRemove: provided => {
return {
...provided,
':hover': {
background: colors.mauve9
}
}
},
option: (provided, state) => {
return {
...provided,
color: colors.searchText,
backgroundColor: state.isFocused ? colors.activeLight : colors.dropDownBg,
':hover': {
backgroundColor: colors.active,
color: '#ffffff'
},
':selected': {
backgroundColor: 'red'
}
}
},
indicatorSeparator: provided => {
return {
...provided,
backgroundColor: colors.secondary
}
},
dropdownIndicator: (provided, state) => {
return {
...provided,
padding: 6,
color: state.isFocused ? colors.border : colors.secondary,
':hover': {
color: colors.border,
}
}
}
}}
styles={styles}
{...props}
/>
)
})
// eslint-disable-next-line react/display-name
const Creatable = forwardRef<any, Props>((props, ref) => {
const { theme } = useTheme()
const isDark = theme === 'dark'
const styles = getStyles(isDark)
return (
<CreatableSelectInput
ref={ref}
formatCreateLabel={label => `Enter "${label}"`}
menuPosition={props.menuPosition || 'fixed'}
styles={styles}
{...props}
/>
)
})
export default styled(Select, {})
export const CreatableSelect = styled(Creatable, {})

View File

@@ -43,7 +43,6 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
(state: Partial<TransactionState> = txState) => {
const {
selectedTransaction,
selectedDestAccount,
selectedAccount,
txFields,
selectedFlags,
@@ -52,20 +51,19 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
} = 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
const HookParameters = Object.entries(hookParameters || {}).reduce<
SetHookData['HookParameters']
>((acc, [_, { label, value }]) => {
return acc.concat({
HookParameter: { HookParameterName: toHex(label), HookParameterValue: toHex(value) }
HookParameter: { HookParameterName: toHex(label), HookParameterValue: 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) }
Memo: { MemoData: data, MemoFormat: toHex(format), MemoType: toHex(type) }
})
}, [])
: undefined
@@ -75,7 +73,6 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
HookParameters,
Flags,
TransactionType,
Destination,
Account,
Memos
})
@@ -128,11 +125,12 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
throw Error('Account must be selected from imported accounts!')
}
const options = prepareOptions(st)
const fields = getTxFields(options.TransactionType)
if (fields.Destination && !options.Destination) {
throw Error('Destination account is required!')
}
// delete unnecessary fields
Object.keys(options).forEach(field => {
if (!options[field]) {
delete options[field]
}
})
await sendTransaction(account, options, { logPrefix })
} catch (error) {
@@ -167,13 +165,6 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
selectedTransaction: transactionType
}
if (fields.Destination !== undefined) {
nwState.selectedDestAccount = null
fields.Destination = ''
} else {
fields.Destination = undefined
}
if (transactionType?.value && transactionFlags[transactionType?.value] && fields.Flags) {
nwState.selectedFlags = extractFlags(transactionType.value, fields.Flags)
fields.Flags = undefined

View File

@@ -1,15 +1,14 @@
import { FC, ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
import { FC, ReactNode, useCallback, useEffect, useState } from 'react'
import Container from '../Container'
import Flex from '../Flex'
import Input from '../Input'
import Select from '../Select'
import Select, { CreatableSelect } from '../Select'
import Text from '../Text'
import {
SelectOption,
TransactionState,
transactionsOptions,
TxFields,
getTxFields,
defaultTransactionType
} from '../../state/transactions'
import { useSnapshot } from 'valtio'
@@ -20,7 +19,7 @@ import Textarea from '../Textarea'
import { getFlags } from '../../state/constants/flags'
import { Plus, Trash } from 'phosphor-react'
import AccountSequence from '../Sequence'
import { typeIs } from '../../utils/helpers'
import { capitalize, typeIs } from '../../utils/helpers'
interface UIProps {
setState: (pTx?: Partial<TransactionState> | undefined) => TransactionState | undefined
@@ -38,28 +37,14 @@ export const TxUI: FC<UIProps> = ({
switchToJson
}) => {
const { accounts } = useSnapshot(state)
const {
selectedAccount,
selectedDestAccount,
selectedTransaction,
txFields,
selectedFlags,
hookParameters,
memos
} = txState
const { selectedAccount, selectedTransaction, txFields, selectedFlags, hookParameters, memos } =
txState
const accountOptions: SelectOption[] = accounts.map(acc => ({
label: acc.name,
value: acc.address
}))
const destAccountOptions: SelectOption[] = accounts
.map(acc => ({
label: acc.name,
value: acc.address
}))
.filter(acc => acc.value !== selectedAccount?.value)
const flagsOptions: SelectOption[] = Object.entries(
getFlags(selectedTransaction?.value) || {}
).map(([label, value]) => ({
@@ -136,15 +121,7 @@ export const TxUI: FC<UIProps> = ({
}
}, [handleChangeTxType, selectedTransaction?.value])
const fields = useMemo(
() => getTxFields(selectedTransaction?.value),
[selectedTransaction?.value]
)
const richFields = ['TransactionType', 'Account', 'HookParameters', 'Memos']
if (fields.Destination !== undefined) {
richFields.push('Destination')
}
if (flagsOptions.length) {
richFields.push('Flags')
@@ -192,18 +169,6 @@ export const TxUI: FC<UIProps> = ({
<TxField label="Sequence">
<AccountSequence address={selectedAccount?.value} />
</TxField>
{richFields.includes('Destination') && (
<TxField label="Destination account">
<Select
instanceId="to-account"
placeholder="Select the destination account"
options={destAccountOptions}
value={selectedDestAccount}
isClearable
onChange={(acc: any) => setState({ selectedDestAccount: acc })}
/>
</TxField>
)}
{richFields.includes('Flags') && (
<TxField label="Flags">
<Select
@@ -235,6 +200,7 @@ export const TxUI: FC<UIProps> = ({
value = _value?.toString()
}
const isAccount = typeIs(_value, 'object') && _value.$type === 'account'
const isXrpAmount = typeIs(_value, 'object') && _value.$type === 'amount.xrp'
const isTokenAmount = typeIs(_value, 'object') && _value.$type === 'amount.token'
const isJson = typeof _value === 'object' && _value.$type === 'json'
@@ -255,8 +221,14 @@ export const TxUI: FC<UIProps> = ({
<TxField key={field} label={field}>
<Flex fluid css={{ alignItems: 'center' }}>
{isTokenAmount ? (
<Flex fluid row align="center" justify="space-between">
<Input
<Flex
fluid
row
align="center"
justify="space-between"
css={{ position: 'relative' }}
>
{/* <Input
type="text"
placeholder="Issuer"
value={tokenAmount.issuer}
@@ -266,9 +238,8 @@ export const TxUI: FC<UIProps> = ({
issuer: e.target.value
})
}
/>
/> */}
<Input
css={{ mx: '$1' }}
type="text"
value={tokenAmount.currency}
placeholder="Currency"
@@ -280,6 +251,7 @@ export const TxUI: FC<UIProps> = ({
}}
/>
<Input
css={{ mx: '$1' }}
type="number"
value={tokenAmount.value}
placeholder="Value"
@@ -290,6 +262,19 @@ export const TxUI: FC<UIProps> = ({
})
}}
/>
<Box css={{ width: '50%' }}>
<CreatableAccount
value={tokenAmount.issuer}
field={'Issuer' as any}
placeholder="Issuer"
setField={(_, value = '') => {
setRawField(field, 'amount.token', {
...tokenAmount,
issuer: value
})
}}
/>
</Box>
</Flex>
) : (
<Input
@@ -323,6 +308,13 @@ export const TxUI: FC<UIProps> = ({
</TxField>
)
}
if (isAccount) {
return (
<TxField key={field} label={field}>
<CreatableAccount value={value} field={field} setField={handleSetField} />
</TxField>
)
}
return (
<TxField key={field} label={field}>
{isJson ? (
@@ -414,7 +406,7 @@ export const TxUI: FC<UIProps> = ({
/>
<Input
css={{ mx: '$2' }}
placeholder="Value"
placeholder="Value (hex-quoted)"
value={value}
onChange={e => {
setState({
@@ -477,7 +469,7 @@ export const TxUI: FC<UIProps> = ({
}}
/>
<Input
placeholder="Data"
placeholder="Data (hex-quoted)"
css={{ mx: '$2' }}
value={memo.data}
onChange={e => {
@@ -535,6 +527,35 @@ export const TxUI: FC<UIProps> = ({
)
}
export const CreatableAccount: FC<{
value: string | undefined
field: keyof TxFields
placeholder?: string
setField: (field: keyof TxFields, value: string, opFields?: TxFields) => void
}> = ({ value, field, setField, placeholder }) => {
const { accounts } = useSnapshot(state)
const accountOptions: SelectOption[] = accounts.map(acc => ({
label: acc.name,
value: acc.address
}))
const label = accountOptions.find(a => a.value === value)?.label || value
const val = {
value,
label
}
placeholder = placeholder || `${capitalize(field)} account`
return (
<CreatableSelect
isClearable
instanceId={field}
placeholder={placeholder}
options={accountOptions}
value={value ? val : undefined}
onChange={(acc: any) => setField(field, acc?.value)}
/>
)
}
export const TxField: FC<{ label: string; children: ReactNode; multiLine?: boolean }> = ({
label,
children,

View File

@@ -2,7 +2,10 @@
{
"TransactionType": "AccountDelete",
"Account": "rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm",
"Destination": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
"Destination": {
"$type": "account",
"$value": ""
},
"DestinationTag": 13,
"Fee": "2000000",
"Sequence": 2470665,
@@ -40,7 +43,10 @@
{
"TransactionType": "CheckCreate",
"Account": "rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo",
"Destination": "rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy",
"Destination": {
"$type": "account",
"$value": ""
},
"SendMax": "100000000",
"Expiration": 570113521,
"InvoiceID": "6F1DFD1D0FE8A32E40E1F2C05CF1C15545BAB56B617F9C6C2D63A6B704BEF59B",
@@ -58,7 +64,10 @@
{
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"TransactionType": "EscrowCancel",
"Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Owner": {
"$type": "account",
"$value": ""
},
"OfferSequence": 7,
"Fee": "10"
},
@@ -69,7 +78,10 @@
"$value": "100",
"$type": "amount.xrp"
},
"Destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
"Destination": {
"$type": "account",
"$value": ""
},
"CancelAfter": 533257958,
"FinishAfter": 533171558,
"Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
@@ -80,7 +92,10 @@
{
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"TransactionType": "EscrowFinish",
"Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Owner": {
"$type": "account",
"$value": ""
},
"OfferSequence": 7,
"Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
"Fulfillment": "A0028000",
@@ -127,7 +142,14 @@
"$type": "amount.xrp"
},
"Flags": "1",
"Destination": "",
"Destination": {
"$type": "account",
"$value": ""
},
"Owner": {
"$type": "account",
"$value": ""
},
"Fee": "10"
},
{
@@ -162,7 +184,10 @@
{
"TransactionType": "Payment",
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Destination": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"Destination": {
"$type": "account",
"$value": ""
},
"Amount": {
"$value": "100",
"$type": "amount.xrp"
@@ -178,7 +203,10 @@
"$value": "100",
"$type": "amount.xrp"
},
"Destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
"Destination": {
"$type": "account",
"$value": ""
},
"SettleDelay": 86400,
"PublicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A",
"CancelAfter": 533171558,
@@ -259,6 +287,10 @@
},
{
"TransactionType": "Invoke",
"Destination": {
"$type": "account",
"$value": ""
},
"Fee": "12"
},
{

View File

@@ -14,11 +14,4 @@ export const deleteAccount = (addr?: string) => {
if (!acc) return
acc.label = acc.value
})
transactionsState.transactions
.filter(t => t.state.selectedDestAccount?.value === addr)
.forEach(t => {
const acc = t.state.selectedDestAccount
if (!acc) return
acc.label = acc.value
})
}

View File

@@ -10,7 +10,6 @@ interface TransactionOptions {
TransactionType: string
Account?: string
Fee?: string
Destination?: string
[index: string]: any
}
interface OtherOptions {

View File

@@ -28,7 +28,6 @@ export type Memos = {
export interface TransactionState {
selectedTransaction: SelectOption | null
selectedAccount: SelectOption | null
selectedDestAccount: SelectOption | null
selectedFlags: SelectOption[] | null
hookParameters: HookParameters
memos: Memos
@@ -51,7 +50,6 @@ export type TxFields = Omit<
export const defaultTransaction: TransactionState = {
selectedTransaction: null,
selectedAccount: null,
selectedDestAccount: null,
selectedFlags: null,
hookParameters: {},
memos: {},
@@ -131,7 +129,6 @@ export const modifyTxState = (
return tx.state
}
// state to tx options
export const prepareTransaction = (data: any) => {
let options = { ...data }
@@ -143,10 +140,9 @@ export const prepareTransaction = (data: any) => {
if (_value.$value) {
options[field] = (+(_value as any).$value * 1000000 + '')
} else {
options[field] = undefined
options[field] = ""
}
}
// amount.token
if (_value.$type === 'amount.token') {
if (typeIs(_value.$value, 'string')) {
@@ -157,7 +153,10 @@ export const prepareTransaction = (data: any) => {
options[field] = undefined
}
}
// account
if (_value.$type === 'account') {
options[field] = (_value.$value as any)?.toString() || ""
}
// json
if (_value.$type === 'json') {
const val = _value.$value;
@@ -172,17 +171,9 @@ export const prepareTransaction = (data: any) => {
}
})
// delete unnecessary fields
Object.keys(options).forEach(field => {
if (!options[field]) {
delete options[field]
}
})
return options
}
// editor value to state
export const prepareState = (value: string, transactionType?: string) => {
const options = parseJSON(value)
if (!options) {
@@ -192,7 +183,7 @@ export const prepareState = (value: string, transactionType?: string) => {
return
}
const { Account, TransactionType, Destination, HookParameters, Memos, ...rest } = options
const { Account, TransactionType, HookParameters, Memos, ...rest } = options
let tx: Partial<TransactionState> = {}
const schema = getTxFields(transactionType)
@@ -224,7 +215,7 @@ export const prepareState = (value: string, transactionType?: string) => {
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 || "") }
const param = { label: fromHex(cur.HookParameter?.HookParameterName || ""), value: cur.HookParameter?.HookParameterValue || "" }
acc[idx] = param;
return acc;
}, {})
@@ -232,30 +223,12 @@ export const prepareState = (value: string, transactionType?: string) => {
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 || "") }
const memo = { data: cur.Memo?.MemoData || "", type: fromHex(cur.Memo?.MemoType || ""), format: fromHex(cur.Memo?.MemoFormat || "") }
acc[idx] = memo;
return acc;
}, {})
}
if (schema.Destination !== undefined) {
const dest = state.accounts.find(acc => acc.address === Destination)
if (dest) {
tx.selectedDestAccount = {
label: dest.name,
value: dest.address
}
} else if (Destination) {
tx.selectedDestAccount = {
label: Destination,
value: Destination
}
} else {
tx.selectedDestAccount = null
}
} else if (Destination) {
rest.Destination = Destination
}
if (getFlags(TransactionType) && rest.Flags) {
const flags = extractFlags(TransactionType, rest.Flags)
@@ -271,19 +244,27 @@ export const prepareState = (value: string, transactionType?: string) => {
const isAmount = schemaVal &&
typeIs(schemaVal, "object") &&
schemaVal.$type.startsWith('amount.');
const isAccount = schemaVal &&
typeIs(schemaVal, "object") &&
schemaVal.$type.startsWith("account");
if (isAmount && ["number", "string"].includes(typeof value)) {
rest[field] = {
$type: 'amount.xrp', // Maybe have $type map or something
$type: 'amount.xrp', // TODO narrow typed $type.
$value: +value / 1000000 // ! maybe use bigint?
}
}
else if (isAmount && typeof value === 'object') {
} else if (isAmount && typeof value === 'object') {
rest[field] = {
$type: 'amount.token',
$value: value
}
} else if (typeof value === 'object') {
} else if (isAccount) {
rest[field] = {
$type: "account",
$value: value?.toString() || ""
}
}
else if (typeof value === 'object') {
rest[field] = {
$type: 'json',
$value: value

View File

@@ -30,7 +30,7 @@ export const tts = {
export type TTS = typeof tts
const calculateHookOn = (arr: (keyof TTS)[]) => {
let s = '0x3e3ff5bf'
let s = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff'
arr.forEach(n => {
let v = BigInt(s)
v ^= BigInt(1) << BigInt(tts[n])

View File

@@ -85,7 +85,8 @@ export const getInvokeOptions = (content?: string) => {
export function toHex(str: string) {
var result = ''
for (var i = 0; i < str.length; i++) {
result += str.charCodeAt(i).toString(16)
const hex = str.charCodeAt(i).toString(16)
result += hex.padStart(2, '0')
}
return result.toUpperCase()
}