Compare commits

..

2 Commits

Author SHA1 Message Date
Mariusz Pilarek
9f868da636 Post-review updates. 2023-01-09 10:57:10 +01:00
Mariusz Pilarek
5794aebe47 Added hooks-guard-call-non-const checker doc, updated hooks-guard-in-for doc. 2023-01-05 16:09:36 +01:00
85 changed files with 2880 additions and 2502 deletions

View File

@@ -5,8 +5,7 @@ GITHUB_ID=""
NEXT_PUBLIC_COMPILE_API_ENDPOINT="http://localhost:9000/api/build" NEXT_PUBLIC_COMPILE_API_ENDPOINT="http://localhost:9000/api/build"
NEXT_PUBLIC_COMPILE_API_BASE_URL="http://localhost:9000" NEXT_PUBLIC_COMPILE_API_BASE_URL="http://localhost:9000"
NEXT_PUBLIC_LANGUAGE_SERVER_API_ENDPOINT="ws://localhost:9000/language-server/c" NEXT_PUBLIC_LANGUAGE_SERVER_API_ENDPOINT="ws://localhost:9000/language-server/c"
NEXT_PUBLIC_TESTNET_URL="xahau-test.net" NEXT_PUBLIC_TESTNET_URL="hooks-testnet-v2.xrpl-labs.com"
NEXT_PUBLIC_DEBUG_STREAM_URL="xahau-test.net/debugstream" NEXT_PUBLIC_DEBUG_STREAM_URL="hooks-testnet-v2-debugstream.xrpl-labs.com"
NEXT_PUBLIC_EXPLORER_URL="explorer.xahau-test.net" NEXT_PUBLIC_EXPLORER_URL="hooks-testnet-v2-explorer.xrpl-labs.com"
NEXT_PUBLIC_NETWORK_ID="21338" NEXT_PUBLIC_SITE_URL=http://localhost:3000
NEXT_PUBLIC_SITE_URL="http://localhost:3000"

4
.gitignore vendored
View File

@@ -33,7 +33,3 @@ yarn-error.log*
# vercel # vercel
.vercel .vercel
.vscode .vscode
# yarn
.yarnrc.yml
.yarn/

View File

@@ -1,8 +1,8 @@
# Xahau Hooks Builder # XRPL Hooks Builder
https://builder.xahau.network/ https://hooks-builder.xrpl.org/
This is the repository for Xahau Hooks Builder. This project is built with Next.JS This is the repository for XRPL Hooks Builder. This project is built with Next.JS
## General ## General

View File

@@ -33,7 +33,6 @@ import { addFunds } from '../state/actions/addFaucetAccount'
import { deleteHook } from '../state/actions/deployHook' import { deleteHook } from '../state/actions/deployHook'
import { capitalize } from '../utils/helpers' import { capitalize } from '../utils/helpers'
import { deleteAccount } from '../state/actions/deleteAccount' import { deleteAccount } from '../state/actions/deleteAccount'
import { xrplSend } from '../state/actions/xrpl-client'
export const AccountDialog = ({ export const AccountDialog = ({
activeAccountAddress, activeAccountAddress,
@@ -200,7 +199,7 @@ export const AccountDialog = ({
.toUnit() .toUnit()
.toLocaleString(undefined, { .toLocaleString(undefined, {
style: 'currency', style: 'currency',
currency: 'XAH', currency: 'XRP',
currencyDisplay: 'name' currencyDisplay: 'name'
})} })}
<Button <Button
@@ -302,7 +301,7 @@ const Accounts: FC<AccountProps> = props => {
const fetchAccInfo = async () => { const fetchAccInfo = async () => {
if (snap.clientStatus === 'online') { if (snap.clientStatus === 'online') {
const requests = snap.accounts.map(acc => const requests = snap.accounts.map(acc =>
xrplSend({ snap.client?.send({
id: `hooks-builder-req-info-${acc.address}`, id: `hooks-builder-req-info-${acc.address}`,
command: 'account_info', command: 'account_info',
account: acc.address account: acc.address
@@ -330,7 +329,7 @@ const Accounts: FC<AccountProps> = props => {
} }
}) })
const objectRequests = snap.accounts.map(acc => { const objectRequests = snap.accounts.map(acc => {
return xrplSend({ return snap.client?.send({
id: `hooks-builder-req-objects-${acc.address}`, id: `hooks-builder-req-objects-${acc.address}`,
command: 'account_objects', command: 'account_objects',
account: acc.address account: acc.address
@@ -459,7 +458,7 @@ const Accounts: FC<AccountProps> = props => {
.toUnit() .toUnit()
.toLocaleString(undefined, { .toLocaleString(undefined, {
style: 'currency', style: 'currency',
currency: 'XAH', currency: 'XRP',
currencyDisplay: 'name' currencyDisplay: 'name'
})})` })})`
) : ( ) : (

View File

@@ -15,7 +15,7 @@ const contentShow = keyframes({
'100%': { opacity: 1 } '100%': { opacity: 1 }
}) })
const StyledOverlay = styled(DialogPrimitive.Overlay, { const StyledOverlay = styled(DialogPrimitive.Overlay, {
zIndex: 3000, zIndex: 10000,
backgroundColor: blackA.blackA9, backgroundColor: blackA.blackA9,
position: 'fixed', position: 'fixed',
inset: 0, inset: 0,

View File

@@ -101,7 +101,7 @@ const Navigation = () => {
<Spinner /> <Spinner />
) : ( ) : (
<> <>
<Heading css={{ lineHeight: 1 }}>{snap.gistName || 'Xahau Hooks'}</Heading> <Heading css={{ lineHeight: 1 }}>{snap.gistName || 'XRPL Hooks'}</Heading>
<Text css={{ fontSize: '$xs', color: '$mauve10', lineHeight: 1 }}> <Text css={{ fontSize: '$xs', color: '$mauve10', lineHeight: 1 }}>
{snap.files.length > 0 ? 'Gist: ' : 'Builder'} {snap.files.length > 0 ? 'Gist: ' : 'Builder'}
{snap.files.length > 0 && ( {snap.files.length > 0 && (
@@ -180,7 +180,7 @@ const Navigation = () => {
fontWeight: '$bold' fontWeight: '$bold'
}} }}
> >
<Logo width="48px" height="48px" /> Xahau Hooks Builder <Logo width="48px" height="48px" /> XRPL Hooks Builder
</DialogTitle> </DialogTitle>
<DialogDescription as="div"> <DialogDescription as="div">
<Text <Text
@@ -191,7 +191,7 @@ const Navigation = () => {
mb: '$7' mb: '$7'
}} }}
> >
Hooks add smart contract functionality to the Xahau Network. Hooks add smart contract functionality to the XRP Ledger.
</Text> </Text>
<Flex css={{ flexDirection: 'column', gap: '$2', mt: '$2' }}> <Flex css={{ flexDirection: 'column', gap: '$2', mt: '$2' }}>
<Text <Text
@@ -210,9 +210,9 @@ const Navigation = () => {
as="a" as="a"
rel="noreferrer noopener" rel="noreferrer noopener"
target="_blank" target="_blank"
href="https://github.com/Xahau" href="https://github.com/XRPL-Labs/xrpld-hooks"
> >
<ArrowUpRight size="15px" /> Xahau Github <ArrowUpRight size="15px" /> Hooks Github
</Text> </Text>
<Text <Text
@@ -231,7 +231,7 @@ const Navigation = () => {
as="a" as="a"
rel="noreferrer noopener" rel="noreferrer noopener"
target="_blank" target="_blank"
href="https://docs.xahau.network/readme-1" href="https://xrpl-hooks.readme.io/v2.0/docs"
> >
<ArrowUpRight size="15px" /> Hooks documentation <ArrowUpRight size="15px" /> Hooks documentation
</Text> </Text>
@@ -352,7 +352,7 @@ const Navigation = () => {
</Button> </Button>
</Link> </Link>
</ButtonGroup> </ButtonGroup>
<Link href="https://xrpl-hooks.readme.io/" passHref> <Link href="https://xrpl-hooks.readme.io/v2.0" passHref>
<a target="_blank" rel="noreferrer noopener"> <a target="_blank" rel="noreferrer noopener">
<Button outline> <Button outline>
<BookOpen size="15px" /> <BookOpen size="15px" />

View File

@@ -21,7 +21,7 @@ import { saveFile } from '../../state/actions/saveFile'
import { getErrors, getTags } from '../../utils/comment-parser' import { getErrors, getTags } from '../../utils/comment-parser'
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
const generateHtmlTemplate = async (code: string, data?: Record<string, any>) => { const generateHtmlTemplate = (code: string, data?: Record<string, any>) => {
let processString: string | undefined let processString: string | undefined
const process = { env: { NODE_ENV: 'production' } } as any const process = { env: { NODE_ENV: 'production' } } as any
if (data) { if (data) {
@@ -29,10 +29,8 @@ const generateHtmlTemplate = async (code: string, data?: Record<string, any>) =>
process.env[key] = data[key] process.env[key] = data[key]
}) })
} }
processString = JSON.stringify(process) processString = JSON.stringify(process)
const libs = (await import("xrpl-accountlib/dist/browser.hook-bundle.js")).default;
return ` return `
<html> <html>
<head> <head>
@@ -74,9 +72,7 @@ const generateHtmlTemplate = async (code: string, data?: Record<string, any>) =>
window.addEventListener('error', windowErrorHandler); window.addEventListener('error', windowErrorHandler);
</script> </script>
<script>
${libs}
</script>
<script type="module"> <script type="module">
${code} ${code}
</script> </script>
@@ -104,7 +100,6 @@ const RunScript: React.FC<{ file: IFile }> = ({ file: { content, name } }) => {
const [fields, setFields] = useState<Fields>({}) const [fields, setFields] = useState<Fields>({})
const [iFrameCode, setIframeCode] = useState('') const [iFrameCode, setIframeCode] = useState('')
const [isDialogOpen, setIsDialogOpen] = useState(false) const [isDialogOpen, setIsDialogOpen] = useState(false)
const [isLoading, setIsLoading] = useState(false)
const getFields = useCallback(() => { const getFields = useCallback(() => {
const inputTags = ['input', 'param', 'arg', 'argument'] const inputTags = ['input', 'param', 'arg', 'argument']
@@ -132,31 +127,16 @@ const RunScript: React.FC<{ file: IFile }> = ({ file: { content, name } }) => {
return fields return fields
}, [content]) }, [content])
const runScript = useCallback(async () => { const runScript = useCallback(() => {
setIsLoading(true);
try { try {
// Show loading toast only after 1 second, otherwise skip it.
let loaded = false
let toastId: string | undefined;
setTimeout(() => {
if (!loaded) {
toastId = toast.loading('Loading packages, may take a few seconds...', {
position: 'bottom-center',
})
}
}, 1000)
let data: any = {} let data: any = {}
Object.keys(fields).forEach(key => { Object.keys(fields).forEach(key => {
data[key] = fields[key].value data[key] = fields[key].value
}) })
const template = await generateHtmlTemplate(content, data) const template = generateHtmlTemplate(content, data)
setIframeCode(template) setIframeCode(template)
loaded = true
if (toastId) {
toast.dismiss(toastId)
}
state.scriptLogs = [{ type: 'success', message: 'Started running...' }] state.scriptLogs = [{ type: 'success', message: 'Started running...' }]
} catch (err) { } catch (err) {
state.scriptLogs = [ state.scriptLogs = [
@@ -165,7 +145,6 @@ const RunScript: React.FC<{ file: IFile }> = ({ file: { content, name } }) => {
{ type: 'error', message: err?.message || 'Could not parse template' } { type: 'error', message: err?.message || 'Could not parse template' }
] ]
} }
setIsLoading(false);
}, [content, fields, snap.scriptLogs]) }, [content, fields, snap.scriptLogs])
useEffect(() => { useEffect(() => {
@@ -195,11 +174,11 @@ const RunScript: React.FC<{ file: IFile }> = ({ file: { content, name } }) => {
const isDisabled = Object.values(fields).some(field => field.required && !field.value) const isDisabled = Object.values(fields).some(field => field.required && !field.value)
const handleRun = useCallback(async () => { const handleRun = useCallback(() => {
if (isDisabled) return toast.error('Please fill in all the required fields.') if (isDisabled) return toast.error('Please fill in all the required fields.')
state.scriptLogs = [] state.scriptLogs = []
await runScript(); runScript()
setIsDialogOpen(false) setIsDialogOpen(false)
}, [isDisabled, runScript]) }, [isDisabled, runScript])
@@ -300,7 +279,7 @@ const RunScript: React.FC<{ file: IFile }> = ({ file: { content, name } }) => {
<DialogClose asChild> <DialogClose asChild>
<Button outline>Cancel</Button> <Button outline>Cancel</Button>
</DialogClose> </DialogClose>
<Button variant="primary" isDisabled={isDisabled || isLoading} isLoading={isLoading} onClick={handleRun}> <Button variant="primary" isDisabled={isDisabled} onClick={handleRun}>
Run script Run script
</Button> </Button>
</Flex> </Flex>

View File

@@ -3,11 +3,13 @@ import { mauve, mauveDark, purple, purpleDark } from '@radix-ui/colors'
import { useTheme } from 'next-themes' import { useTheme } from 'next-themes'
import { styled } from '../stitches.config' import { styled } from '../stitches.config'
import dynamic from 'next/dynamic' import dynamic from 'next/dynamic'
import type { Props, StylesConfig } from 'react-select' import type { Props } from 'react-select'
const SelectInput = dynamic(() => import('react-select'), { ssr: false }) const SelectInput = dynamic(() => import('react-select'), { ssr: false })
const CreatableSelectInput = dynamic(() => import('react-select/creatable'), { ssr: false })
const getColors = (isDark: boolean) => { // eslint-disable-next-line react/display-name
const Select = forwardRef<any, Props>((props, ref) => {
const { theme } = useTheme()
const isDark = theme === 'dark'
const colors: any = { const colors: any = {
// primary: pink.pink9, // primary: pink.pink9,
active: isDark ? purpleDark.purple9 : purple.purple9, active: isDark ? purpleDark.purple9 : purple.purple9,
@@ -28,136 +30,93 @@ const getColors = (isDark: boolean) => {
} }
colors.outline = colors.background colors.outline = colors.background
colors.selected = colors.secondary 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 ( return (
<SelectInput <SelectInput
ref={ref} ref={ref}
menuPosition={props.menuPosition || 'fixed'} menuPosition={props.menuPosition || 'fixed'}
styles={styles} styles={{
{...props} container: provided => {
/> return {
) ...provided,
}) position: 'relative'
}
// eslint-disable-next-line react/display-name },
const Creatable = forwardRef<any, Props>((props, ref) => { singleValue: provided => ({
const { theme } = useTheme() ...provided,
const isDark = theme === 'dark' color: colors.mauve12
const styles = getStyles(isDark) }),
return ( menu: provided => ({
<CreatableSelectInput ...provided,
ref={ref} backgroundColor: colors.dropDownBg
formatCreateLabel={label => `Enter "${label}"`} }),
menuPosition={props.menuPosition || 'fixed'} control: (provided, state) => {
styles={styles} 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,
color: state.isFocused ? colors.border : colors.secondary,
':hover': {
color: colors.border
}
}
}
}}
{...props} {...props}
/> />
) )
}) })
export default styled(Select, {}) export default styled(Select, {})
export const CreatableSelect = styled(Creatable, {})

View File

@@ -1,71 +0,0 @@
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

View File

@@ -21,7 +21,6 @@ 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 }) => {
@@ -191,10 +190,6 @@ 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

View File

@@ -20,7 +20,6 @@ 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
@@ -41,40 +40,20 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
const prepareOptions = useCallback( const prepareOptions = useCallback(
(state: Partial<TransactionState> = txState) => { (state: Partial<TransactionState> = txState) => {
const { const { selectedTransaction, selectedDestAccount, selectedAccount, txFields, selectedFlags } =
selectedTransaction, state
selectedAccount,
txFields,
selectedFlags,
hookParameters,
memos
} = state
const TransactionType = selectedTransaction?.value || null const TransactionType = selectedTransaction?.value || null
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: value }
})
}, [])
const Memos = memos
? Object.entries(memos).reduce<SetHookData['Memos']>((acc, [_, { format, data, type }]) => {
return acc?.concat({
Memo: { MemoData: data, MemoFormat: toHex(format), MemoType: toHex(type) }
})
}, [])
: undefined
return prepareTransaction({ return prepareTransaction({
...txFields, ...txFields,
HookParameters,
Flags, Flags,
TransactionType, TransactionType,
Account, Destination,
Memos Account
}) })
}, },
[txState] [txState]
@@ -90,29 +69,15 @@ 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') {
st = saveEditorState(editorValue, tt) // save the editor state first
if (!st) return const pst = prepareState(editorValue || '', tt)
if (!pst) return
st = setState(pst)
} }
const account = accounts.find(acc => acc.address === selectedAccount?.value) const account = accounts.find(acc => acc.address === selectedAccount?.value)
@@ -125,12 +90,11 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
throw Error('Account must be selected from imported accounts!') throw Error('Account must be selected from imported accounts!')
} }
const options = prepareOptions(st) const options = prepareOptions(st)
// delete unnecessary fields
Object.keys(options).forEach(field => { const fields = getTxFields(options.TransactionType)
if (!options[field]) { if (fields.Destination && !options.Destination) {
delete options[field] throw Error('Destination account is required!')
} }
})
await sendTransaction(account, options, { logPrefix }) await sendTransaction(account, options, { logPrefix })
} catch (error) { } catch (error) {
@@ -144,18 +108,23 @@ 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)
@@ -165,6 +134,13 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
selectedTransaction: transactionType selectedTransaction: transactionType
} }
if (fields.Destination !== undefined) {
nwState.selectedDestAccount = null
fields.Destination = ''
} else {
fields.Destination = undefined
}
if (transactionType?.value && transactionFlags[transactionType?.value] && fields.Flags) { if (transactionType?.value && transactionFlags[transactionType?.value] && fields.Flags) {
nwState.selectedFlags = extractFlags(transactionType.value, fields.Flags) nwState.selectedFlags = extractFlags(transactionType.value, fields.Flags)
fields.Flags = undefined fields.Flags = undefined
@@ -201,21 +177,11 @@ 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}
@@ -223,7 +189,6 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
/> />
) : ( ) : (
<TxUI <TxUI
switchToJson={switchToJson}
state={txState} state={txState}
resetState={resetState} resetState={resetState}
setState={setState} setState={setState}
@@ -244,8 +209,8 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
<Button <Button
onClick={() => { onClick={() => {
if (viewType === 'ui') { if (viewType === 'ui') {
switchToJson() setState({ viewType: 'json' })
} else switchToUI() } else setState({ viewType: 'ui' })
}} }}
outline outline
> >

View File

@@ -1,6 +1,6 @@
import { FC, useCallback, useEffect, useState } from 'react' import { FC, useCallback, useEffect, useMemo, useState } from 'react'
import { useSnapshot } from 'valtio' import { useSnapshot } from 'valtio'
import state, { transactionsData, TransactionState } from '../../state' import state, { prepareState, 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: (st?: Partial<TransactionState>) => string getJsonString?: (state?: 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> = ({ export const TxJson: FC<JsonProps> = ({ getJsonString, state: txState, header, setState }) => {
getJsonString,
state: txState,
header,
setState,
saveEditorState
}) => {
const { editorSettings, accounts } = useSnapshot(state) const { editorSettings, accounts } = useSnapshot(state)
const { editorValue, estimatedFee, editorIsSaved } = txState const { editorValue, estimatedFee } = 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,19 +44,29 @@ export const TxJson: FC<JsonProps> = ({
} }
}, [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) {
saveEditorState(value, currTxType) saveState(value, currTxType)
return return
} }
showAlert('Error!', { showAlert('Error!', {
@@ -153,6 +163,8 @@ export const TxJson: FC<JsonProps> = ({
}) })
}, [getSchemas, monacoInst]) }, [getSchemas, monacoInst])
const hasUnsaved = useMemo(() => editorValue !== getJsonString?.(), [editorValue, getJsonString])
return ( return (
<Monaco <Monaco
rootProps={{ rootProps={{
@@ -162,7 +174,7 @@ export const TxJson: FC<JsonProps> = ({
id={header} id={header}
height="100%" height="100%"
value={editorValue} value={editorValue}
onChange={val => setState({ editorValue: val, editorIsSaved: false })} onChange={val => setState({ editorValue: val })}
onMount={(editor, monaco) => { onMount={(editor, monaco) => {
editor.updateOptions({ editor.updateOptions({
minimap: { enabled: false }, minimap: { enabled: false },
@@ -178,12 +190,12 @@ export const TxJson: FC<JsonProps> = ({
model?.onWillDispose(() => onExit(model.getValue())) model?.onWillDispose(() => onExit(model.getValue()))
}} }}
overlay={ overlay={
!editorIsSaved ? ( hasUnsaved ? (
<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={() => saveEditorState(editorValue, currTxType)}> <Link css={{ ml: '$1' }} onClick={() => saveState(editorValue || '', currTxType)}>
save save
</Link> </Link>
<Link css={{ ml: '$1' }} onClick={discardChanges}> <Link css={{ ml: '$1' }} onClick={discardChanges}>

View File

@@ -1,43 +1,34 @@
import { FC, ReactNode, useCallback, useEffect, useState } from 'react' import { FC, 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'
import Select, { CreatableSelect } from '../Select' import Select from '../Select'
import Text from '../Text' import Text from '../Text'
import { import {
SelectOption, SelectOption,
TransactionState, TransactionState,
transactionsOptions, transactionsOptions,
TxFields, TxFields,
getTxFields,
defaultTransactionType defaultTransactionType
} from '../../state/transactions' } from '../../state/transactions'
import { useSnapshot } from 'valtio' import { useSnapshot } from 'valtio'
import state from '../../state' import state from '../../state'
import { streamState } from '../DebugStream' import { streamState } from '../DebugStream'
import { Box, 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'
import { capitalize, typeIs } from '../../utils/helpers'
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> = ({ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estimateFee }) => {
state: txState,
setState,
resetState,
estimateFee,
switchToJson
}) => {
const { accounts } = useSnapshot(state) const { accounts } = useSnapshot(state)
const { selectedAccount, selectedTransaction, txFields, selectedFlags, hookParameters, memos } = const { selectedAccount, selectedDestAccount, selectedTransaction, txFields, selectedFlags } =
txState txState
const accountOptions: SelectOption[] = accounts.map(acc => ({ const accountOptions: SelectOption[] = accounts.map(acc => ({
@@ -45,6 +36,13 @@ export const TxUI: FC<UIProps> = ({
value: acc.address 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( const flagsOptions: SelectOption[] = Object.entries(
getFlags(selectedTransaction?.value) || {} getFlags(selectedTransaction?.value) || {}
).map(([label, value]) => ({ ).map(([label, value]) => ({
@@ -73,22 +71,6 @@ export const TxUI: FC<UIProps> = ({
[setState, txFields] [setState, txFields]
) )
const setRawField = useCallback(
(field: keyof TxFields, type: string, value: any) => {
// TODO $type should be a narrowed type
setState({
txFields: {
...txFields,
[field]: {
$type: type,
$value: value
}
}
})
},
[setState, txFields]
)
const handleEstimateFee = useCallback( const handleEstimateFee = useCallback(
async (state?: TransactionState, silent?: boolean) => { async (state?: TransactionState, silent?: boolean) => {
setFeeLoading(true) setFeeLoading(true)
@@ -112,6 +94,8 @@ export const TxUI: FC<UIProps> = ({
[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
@@ -121,23 +105,22 @@ export const TxUI: FC<UIProps> = ({
} }
}, [handleChangeTxType, selectedTransaction?.value]) }, [handleChangeTxType, selectedTransaction?.value])
const richFields = ['TransactionType', 'Account', 'HookParameters', 'Memos'] const fields = useMemo(
() => getTxFields(selectedTransaction?.value),
[selectedTransaction?.value]
)
const richFields = ['TransactionType', 'Account']
if (fields.Destination !== undefined) {
richFields.push('Destination')
}
if (flagsOptions.length) { if (flagsOptions.length) {
richFields.push('Flags') richFields.push('Flags')
} }
const otherFields = Object.keys(txFields).filter(k => !richFields.includes(k)) as [keyof TxFields] const otherFields = Object.keys(txFields).filter(k => !richFields.includes(k)) as [keyof TxFields]
const amountOptions = [
{ label: 'XAH', value: 'xah' },
{ label: 'Token', value: 'token' }
] as const
const defaultTokenAmount = {
value: '0',
currency: '',
issuer: ''
}
return ( return (
<Container <Container
css={{ css={{
@@ -147,32 +130,94 @@ export const TxUI: FC<UIProps> = ({
}} }}
> >
<Flex column fluid css={{ height: '100%', overflowY: 'auto', pr: '$1' }}> <Flex column fluid css={{ height: '100%', overflowY: 'auto', pr: '$1' }}>
<TxField label="Transaction type"> <Flex
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)}
/> />
</TxField> </Flex>
<TxField label="Account"> <Flex
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
/> />
</TxField> </Flex>
<TxField label="Sequence"> {richFields.includes('Destination') && (
<AccountSequence address={selectedAccount?.value} /> <Flex
</TxField> row
fluid
css={{
justifyContent: 'flex-end',
alignItems: 'center',
mb: '$3',
pr: '1px'
}}
>
<Text muted css={{ mr: '$3', textAlign: 'end' }}>
Destination account:{' '}
</Text>
<Select
instanceId="to-account"
placeholder="Select the destination account"
css={{ width: '70%' }}
options={destAccountOptions}
value={selectedDestAccount}
isClearable
onChange={(acc: any) => setState({ selectedDestAccount: acc })}
/>
</Flex>
)}
{richFields.includes('Flags') && ( {richFields.includes('Flags') && (
<TxField label="Flags"> <Flex
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"
@@ -184,400 +229,115 @@ export const TxUI: FC<UIProps> = ({
selectedFlags ? selectedFlags.length >= flagsOptions.length - 1 : false selectedFlags ? selectedFlags.length >= flagsOptions.length - 1 : false
} }
/> />
</TxField> </Flex>
)} )}
{otherFields.map(field => { {otherFields.map(field => {
let _value = txFields[field] let _value = txFields[field]
let value: string | undefined let value: string | undefined
if (typeIs(_value, 'object')) { if (typeof _value === 'object') {
if (_value.$type === 'json' && typeIs(_value.$value, ['object', 'array'])) { if (_value.$type === 'json' && typeof _value.$value === 'object') {
value = JSON.stringify(_value.$value, null, 2) value = JSON.stringify(_value.$value, null, 2)
} else { } else {
value = _value.$value?.toString() value = _value.$value.toString()
} }
} else { } else {
value = _value?.toString() value = _value?.toString()
} }
const isAccount = typeIs(_value, 'object') && _value.$type === 'account' const isXrp = typeof _value === 'object' && _value.$type === 'xrp'
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' const isJson = typeof _value === 'object' && _value.$type === 'json'
const isFee = field === 'Fee' const isFee = field === 'Fee'
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
let tokenAmount = defaultTokenAmount
if (isTokenAmount && typeIs(_value, 'object') && typeIs(_value.$value, 'object')) {
tokenAmount = {
value: _value.$value.value,
currency: _value.$value.currency,
issuer: _value.$value.issuer
}
}
if (isXrpAmount || isTokenAmount) {
return (
<TxField key={field} label={field}>
<Flex fluid css={{ alignItems: 'center' }}>
{isTokenAmount ? (
<Flex
fluid
row
align="center"
justify="space-between"
css={{ position: 'relative' }}
>
{/* <Input
type="text"
placeholder="Issuer"
value={tokenAmount.issuer}
onChange={e =>
setRawField(field, 'amount.token', {
...tokenAmount,
issuer: e.target.value
})
}
/> */}
<Input
type="text"
value={tokenAmount.currency}
placeholder="Currency"
onChange={e => {
setRawField(field, 'amount.token', {
...tokenAmount,
currency: e.target.value
})
}}
/>
<Input
css={{ mx: '$1' }}
type="number"
value={tokenAmount.value}
placeholder="Value"
onChange={e => {
setRawField(field, 'amount.token', {
...tokenAmount,
value: e.target.value
})
}}
/>
<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
css={{ flex: 'inherit' }}
type="number"
value={value}
onChange={e => handleSetField(field, e.target.value)}
/>
)}
<Box
css={{
ml: '$2',
width: '150px'
}}
>
<Select
instanceId="currency-type"
options={amountOptions}
value={isXrpAmount ? amountOptions['0'] : amountOptions['1']}
onChange={(e: any) => {
const opt = e as typeof amountOptions[number]
if (opt.value === 'xah') {
setRawField(field, 'amount.xrp', '0')
} else {
setRawField(field, 'amount.token', defaultTokenAmount)
}
}}
/>
</Box>
</Flex>
</TxField>
)
}
if (isAccount) {
return (
<TxField key={field} label={field}>
<CreatableAccount value={value} field={field} setField={handleSetField} />
</TxField>
)
}
return ( return (
<TxField key={field} label={field}> <Flex column key={field} css={{ mb: '$2', pr: '1px' }}>
{isJson ? ( <Flex
<Textarea row
rows={rows} fluid
value={value} css={{
spellCheck={false} justifyContent: 'flex-end',
onChange={switchToJson} alignItems: 'center',
css={{ position: 'relative'
flex: 'inherit', }}
resize: 'vertical' >
}} <Text muted css={{ mr: '$3' }}>
/> {field + (isXrp ? ' (XRP)' : '')}:{' '}
) : ( </Text>
<Input {isJson ? (
type={isFee ? 'number' : 'text'} <Textarea
value={value} rows={rows}
onChange={e => { value={value}
if (isFee) { spellCheck={false}
const val = e.target.value.replaceAll('.', '').replaceAll(',', '') onChange={switchToJson}
handleSetField(field, val) css={{
} else { width: '70%',
handleSetField(field, e.target.value) flex: 'inherit',
} resize: 'vertical'
}}
onKeyPress={
isFee
? e => {
if (e.key === '.' || e.key === ',') {
e.preventDefault()
}
}
: undefined
}
css={{
flex: 'inherit',
'-moz-appearance': 'textfield',
'&::-webkit-outer-spin-button': {
'-webkit-appearance': 'none',
margin: 0
},
'&::-webkit-inner-spin-button ': {
'-webkit-appearance': 'none',
margin: 0
}
}}
/>
)}
{isFee && (
<Button
size="xs"
variant="primary"
outline
disabled={txState.txIsDisabled}
isDisabled={txState.txIsDisabled}
isLoading={feeLoading}
css={{
position: 'absolute',
right: '$2',
fontSize: '$xs',
cursor: 'pointer',
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 <Input
css={{ mx: '$2' }} type={isFee ? 'number' : 'text'}
placeholder="Value (hex-quoted)"
value={value} value={value}
onChange={e => { onChange={e => {
setState({ if (isFee) {
hookParameters: { const val = e.target.value.replaceAll('.', '').replaceAll(',', '')
...hookParameters, handleSetField(field, val)
[id]: { label, value: e.target.value } } else {
} handleSetField(field, e.target.value)
}) }
}}
onKeyPress={
isFee
? e => {
if (e.key === '.' || e.key === ',') {
e.preventDefault()
}
}
: undefined
}
css={{
width: '70%',
flex: 'inherit',
'-moz-appearance': 'textfield',
'&::-webkit-outer-spin-button': {
'-webkit-appearance': 'none',
margin: 0
},
'&::-webkit-inner-spin-button ': {
'-webkit-appearance': 'none',
margin: 0
}
}} }}
/> />
)}
{isFee && (
<Button <Button
onClick={() => { size="xs"
const { [id]: _, ...rest } = hookParameters variant="primary"
setState({ hookParameters: rest }) outline
disabled={txState.txIsDisabled}
isDisabled={txState.txIsDisabled}
isLoading={feeLoading}
css={{
position: 'absolute',
right: '$2',
fontSize: '$xs',
cursor: 'pointer',
alignContent: 'center',
display: 'flex'
}} }}
variant="destroy" onClick={() => handleEstimateFee()}
> >
<Trash weight="regular" size="16px" /> Suggest
</Button> </Button>
</Flex> )}
</Flex> </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 (hex-quoted)"
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 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,
multiLine = false
}) => {
return (
<Flex
row
fluid
css={{
justifyContent: 'flex-end',
alignItems: multiLine ? 'flex-start' : 'center',
position: 'relative',
mb: '$2',
mt: '1px',
pr: '1px'
}}
>
<Text muted css={{ mr: '$3', mt: multiLine ? '$2' : 0 }}>
{label}:{' '}
</Text>
<Flex css={{ width: '70%', alignItems: 'center' }}>{children}</Flex>
</Flex>
)
}

View File

@@ -1,7 +1,7 @@
{ {
"uri": "file:///amount-schema.json", "uri": "file:///amount-schema.json",
"title": "Amount", "title": "Amount",
"description": "Specify XAH in drops and tokens as objects.", "description": "Specify xrp in drops and tokens as objects.",
"schema": { "schema": {
"anyOf": [ "anyOf": [
{ {
@@ -13,7 +13,7 @@
"type": "object", "type": "object",
"properties": { "properties": {
"currency": { "currency": {
"description": "Arbitrary currency code for the token. Cannot be XAH." "description": "Arbitrary currency code for the token. Cannot be XRP."
}, },
"value": { "value": {
"type": ["string", "number"], "type": ["string", "number"],
@@ -28,7 +28,7 @@
], ],
"defaultSnippets": [ "defaultSnippets": [
{ {
"label": "XAH", "label": "Xrp",
"body": "1000000" "body": "1000000"
}, },
{ {

View File

@@ -2,10 +2,7 @@
{ {
"TransactionType": "AccountDelete", "TransactionType": "AccountDelete",
"Account": "rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm", "Account": "rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm",
"Destination": { "Destination": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
"$type": "account",
"$value": ""
},
"DestinationTag": 13, "DestinationTag": 13,
"Fee": "2000000", "Fee": "2000000",
"Sequence": 2470665, "Sequence": 2470665,
@@ -31,11 +28,7 @@
"TransactionType": "CheckCash", "TransactionType": "CheckCash",
"Amount": { "Amount": {
"$value": "100", "$value": "100",
"$type": "amount.xrp" "$type": "xrp"
},
"DeliverMin": {
"$value": "",
"$type": "amount.xrp"
}, },
"CheckID": "838766BA2B995C00744175F69A1B11E32C3DBC40E64801A4056FCBD657F57334", "CheckID": "838766BA2B995C00744175F69A1B11E32C3DBC40E64801A4056FCBD657F57334",
"Fee": "12" "Fee": "12"
@@ -43,10 +36,7 @@
{ {
"TransactionType": "CheckCreate", "TransactionType": "CheckCreate",
"Account": "rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo", "Account": "rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo",
"Destination": { "Destination": "rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy",
"$type": "account",
"$value": ""
},
"SendMax": "100000000", "SendMax": "100000000",
"Expiration": 570113521, "Expiration": 570113521,
"InvoiceID": "6F1DFD1D0FE8A32E40E1F2C05CF1C15545BAB56B617F9C6C2D63A6B704BEF59B", "InvoiceID": "6F1DFD1D0FE8A32E40E1F2C05CF1C15545BAB56B617F9C6C2D63A6B704BEF59B",
@@ -64,10 +54,7 @@
{ {
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"TransactionType": "EscrowCancel", "TransactionType": "EscrowCancel",
"Owner": { "Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"$type": "account",
"$value": ""
},
"OfferSequence": 7, "OfferSequence": 7,
"Fee": "10" "Fee": "10"
}, },
@@ -76,12 +63,9 @@
"TransactionType": "EscrowCreate", "TransactionType": "EscrowCreate",
"Amount": { "Amount": {
"$value": "100", "$value": "100",
"$type": "amount.xrp" "$type": "xrp"
},
"Destination": {
"$type": "account",
"$value": ""
}, },
"Destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
"CancelAfter": 533257958, "CancelAfter": 533257958,
"FinishAfter": 533171558, "FinishAfter": 533171558,
"Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100", "Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
@@ -92,15 +76,56 @@
{ {
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"TransactionType": "EscrowFinish", "TransactionType": "EscrowFinish",
"Owner": { "Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"$type": "account",
"$value": ""
},
"OfferSequence": 7, "OfferSequence": 7,
"Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100", "Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
"Fulfillment": "A0028000", "Fulfillment": "A0028000",
"Fee": "10" "Fee": "10"
}, },
{
"TransactionType": "NFTokenMint",
"Account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"Fee": "10",
"NFTokenTaxon": 0,
"URI": "697066733A2F2F516D614374444B5A4656767666756676626479346573745A626851483744586831364354707631686F776D424779"
},
{
"TransactionType": "NFTokenBurn",
"Account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"Fee": "10",
"NFTokenID": "000B013A95F14B0044F78A264E41713C64B5F89242540EE208C3098E00000D65"
},
{
"TransactionType": "NFTokenAcceptOffer",
"Account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"Fee": "10",
"NFTokenSellOffer": "A2FA1A9911FE2AEF83DAB05F437768E26A301EF899BD31EB85E704B3D528FF18",
"NFTokenBuyOffer": "4AAAEEA76E3C8148473CB3840CE637676E561FB02BD4CA22CA59729EA815B862",
"NFTokenBrokerFee": "10"
},
{
"TransactionType": "NFTokenCancelOffer",
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"Fee": "10",
"NFTokenOffers": {
"$type": "json",
"$value": [
"4AAAEEA76E3C8148473CB3840CE637676E561FB02BD4CA22CA59729EA815B862"
]
}
},
{
"TransactionType": "NFTokenCreateOffer",
"Account": "rs8jBmmfpwgmrSPgwMsh7CvKRmRt1JTVSX",
"NFTokenID": "000100001E962F495F07A990F4ED55ACCFEEF365DBAA76B6A048C0A200000007",
"Amount": {
"$value": "100",
"$type": "xrp"
},
"Flags": "1",
"Destination": "",
"Fee": "10"
},
{ {
"TransactionType": "OfferCancel", "TransactionType": "OfferCancel",
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX", "Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
@@ -117,29 +142,19 @@
"Flags": "0", "Flags": "0",
"LastLedgerSequence": 7108682, "LastLedgerSequence": 7108682,
"Sequence": 8, "Sequence": 8,
"TakerGets": { "TakerGets": "6000000",
"$type": "amount.xrp", "Amount": {
"$value": "6000000" "$value": "100",
}, "$type": "xrp"
"TakerPays": {
"$type": "amount.token",
"$value": {
"currency": "GKO",
"issuer": "ruazs5h1qEsqpke88pcqnaseXdm6od2xc",
"value": "2"
}
} }
}, },
{ {
"TransactionType": "Payment", "TransactionType": "Payment",
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Destination": { "Destination": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"$type": "account",
"$value": ""
},
"Amount": { "Amount": {
"$value": "100", "$value": "100",
"$type": "amount.xrp" "$type": "xrp"
}, },
"Fee": "12", "Fee": "12",
"Flags": "2147483648", "Flags": "2147483648",
@@ -150,12 +165,9 @@
"TransactionType": "PaymentChannelCreate", "TransactionType": "PaymentChannelCreate",
"Amount": { "Amount": {
"$value": "100", "$value": "100",
"$type": "amount.xrp" "$type": "xrp"
},
"Destination": {
"$type": "account",
"$value": ""
}, },
"Destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
"SettleDelay": 86400, "SettleDelay": 86400,
"PublicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A", "PublicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A",
"CancelAfter": 533171558, "CancelAfter": 533171558,
@@ -169,7 +181,7 @@
"Channel": "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198", "Channel": "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198",
"Amount": { "Amount": {
"$value": "200", "$value": "200",
"$type": "amount.xrp" "$type": "xrp"
}, },
"Expiration": 543171558, "Expiration": 543171558,
"Fee": "10" "Fee": "10"
@@ -225,7 +237,7 @@
"Flags": "262144", "Flags": "262144",
"LastLedgerSequence": 8007750, "LastLedgerSequence": 8007750,
"LimitAmount": { "LimitAmount": {
"$type": "amount.token", "$type": "json",
"$value": { "$value": {
"currency": "USD", "currency": "USD",
"issuer": "rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc", "issuer": "rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc",
@@ -233,57 +245,5 @@
} }
}, },
"Sequence": 12 "Sequence": 12
},
{
"TransactionType": "Invoke",
"Destination": {
"$type": "account",
"$value": ""
},
"Fee": "12"
},
{
"TransactionType": "URITokenMint",
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"URI": "697066733A2F2F434944",
"Fee": "10",
"Sequence": 1
},
{
"TransactionType": "URITokenBurn",
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"URITokenID": "B792B56B558C89C4E942E41B5DB66074E005CB198DB70C26C707AAC2FF5F74CB",
"Fee": "10",
"Sequence": 1
},
{
"TransactionType": "URITokenCreateSellOffer",
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"URITokenID": "B792B56B558C89C4E942E41B5DB66074E005CB198DB70C26C707AAC2FF5F74CB",
"Destination": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Amount": {
"$value": "100",
"$type": "amount.xrp"
},
"Fee": "10",
"Sequence": 1
},
{
"TransactionType": "URITokenCancelSellOffer",
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"URITokenID": "B792B56B558C89C4E942E41B5DB66074E005CB198DB70C26C707AAC2FF5F74CB",
"Fee": "10",
"Sequence": 1
},
{
"TransactionType": "URITokenBuy",
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"URITokenID": "B792B56B558C89C4E942E41B5DB66074E005CB198DB70C26C707AAC2FF5F74CB",
"Amount": {
"$value": "100",
"$type": "amount.xrp"
},
"Fee": "10",
"Sequence": 1
} }
] ]

View File

@@ -12,7 +12,7 @@ module.exports = {
config.resolve.fallback.fs = false config.resolve.fallback.fs = false
} }
config.module.rules.push({ config.module.rules.push({
test: [/\.md$/, /hook-bundle\.js$/], test: /\.md$/,
use: 'raw-loader' use: 'raw-loader'
}) })
return config return config

View File

@@ -8,8 +8,7 @@
"start": "next start", "start": "next start",
"lint": "next lint", "lint": "next lint",
"format": "prettier --write .", "format": "prettier --write .",
"postinstall": "patch-package && yarn run postinstall-postinstall", "postinstall": "patch-package"
"postinstall-postinstall": "./node_modules/.bin/browserify -r ripple-binary-codec -r ripple-keypairs -r ripple-address-codec -r ripple-secret-codec -r ./node_modules/xrpl-accountlib/dist/index.js:xrpl-accountlib -o node_modules/xrpl-accountlib/dist/browser.hook-bundle.js"
}, },
"dependencies": { "dependencies": {
"@codingame/monaco-jsonrpc": "^0.3.1", "@codingame/monaco-jsonrpc": "^0.3.1",
@@ -66,8 +65,8 @@
"vscode-languageserver": "^7.0.0", "vscode-languageserver": "^7.0.0",
"vscode-uri": "^3.0.2", "vscode-uri": "^3.0.2",
"wabt": "^1.0.30", "wabt": "^1.0.30",
"xrpl-accountlib": "^1.6.1", "xrpl-accountlib": "^1.5.2",
"xrpl-client": "^2.0.2" "xrpl-client": "^1.9.4"
}, },
"devDependencies": { "devDependencies": {
"@types/dinero.js": "^1.9.0", "@types/dinero.js": "^1.9.0",
@@ -76,13 +75,12 @@
"@types/lodash.xor": "^4.5.6", "@types/lodash.xor": "^4.5.6",
"@types/pako": "^1.0.2", "@types/pako": "^1.0.2",
"@types/react": "17.0.31", "@types/react": "17.0.31",
"browserify": "^17.0.0",
"eslint": "7.32.0", "eslint": "7.32.0",
"eslint-config-next": "11.1.2", "eslint-config-next": "11.1.2",
"raw-loader": "^4.0.2", "raw-loader": "^4.0.2",
"typescript": "4.4.4" "typescript": "4.4.4"
}, },
"resolutions": { "resolutions": {
"ripple-binary-codec": "=1.6.0" "ripple-binary-codec": "=1.4.2"
} }
} }

View File

@@ -58,22 +58,22 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
<meta name="format-detection" content="telephone=no" /> <meta name="format-detection" content="telephone=no" />
<meta property="og:url" content={`${origin}${router.asPath}`} /> <meta property="og:url" content={`${origin}${router.asPath}`} />
<title>Xahau Hooks Builder</title> <title>XRPL Hooks Builder</title>
<meta property="og:title" content="Xahau Hooks Builder" /> <meta property="og:title" content="XRPL Hooks Builder" />
<meta name="twitter:title" content="Xahau Hooks Builder" /> <meta name="twitter:title" content="XRPL Hooks Builder" />
<meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@XRPLF" /> <meta name="twitter:site" content="@XRPLF" />
<meta <meta
name="description" name="description"
content="Hooks Builder, add smart contract functionality to the Xahau Network." content="Hooks Builder, add smart contract functionality to the XRP Ledger."
/> />
<meta <meta
property="og:description" property="og:description"
content="Hooks Builder, add smart contract functionality to the Xahau Network." content="Hooks Builder, add smart contract functionality to the XRP Ledger."
/> />
<meta <meta
name="twitter:description" name="twitter:description"
content="Hooks Builder, add smart contract functionality to the Xahau Network." content="Hooks Builder, add smart contract functionality to the XRP Ledger."
/> />
<meta property="og:image" content={`${origin}${shareImg}`} /> <meta property="og:image" content={`${origin}${shareImg}`} />
<meta property="og:image:width" content="1200" /> <meta property="og:image:width" content="1200" />

File diff suppressed because it is too large Load Diff

View File

@@ -1,346 +0,0 @@
diff --git a/node_modules/ripple-binary-codec/dist/enums/definitions.json b/node_modules/ripple-binary-codec/dist/enums/definitions.json
index e623376..7e1e4d5 100644
--- a/node_modules/ripple-binary-codec/dist/enums/definitions.json
+++ b/node_modules/ripple-binary-codec/dist/enums/definitions.json
@@ -44,11 +44,16 @@
"NegativeUNL": 78,
"NFTokenPage": 80,
"NFTokenOffer": 55,
+ "URIToken": 85,
"Any": -3,
"Child": -2,
"Nickname": 110,
"Contract": 99,
- "GeneratorMap": 103
+ "GeneratorMap": 103,
+ "Hook": 72,
+ "HookState": 118,
+ "HookDefinition": 68,
+ "EmittedTxn": 69
},
"FIELDS": [
[
@@ -321,6 +326,16 @@
"type": "UInt16"
}
],
+ [
+ "NetworkID",
+ {
+ "nth": 1,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "UInt32"
+ }
+ ],
[
"Flags",
{
@@ -761,6 +776,36 @@
"type": "UInt32"
}
],
+ [
+ "LockCount",
+ {
+ "nth": 49,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "UInt32"
+ }
+ ],
+ [
+ "FirstNFTokenSequence",
+ {
+ "nth": 50,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "UInt32"
+ }
+ ],
+ [
+ "ImportSequence",
+ {
+ "nth": 97,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "UInt32"
+ }
+ ],
[
"IndexNext",
{
@@ -891,16 +936,6 @@
"type": "UInt64"
}
],
- [
- "HookOn",
- {
- "nth": 16,
- "isVLEncoded": false,
- "isSerialized": true,
- "isSigningField": true,
- "type": "UInt64"
- }
- ],
[
"HookInstructionCount",
{
@@ -1151,6 +1186,16 @@
"type": "Hash256"
}
],
+ [
+ "HookOn",
+ {
+ "nth": 20,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "Hash256"
+ }
+ ],
[
"Digest",
{
@@ -1281,6 +1326,36 @@
"type": "Hash256"
}
],
+ [
+ "OfferID",
+ {
+ "nth": 34,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "Hash256"
+ }
+ ],
+ [
+ "EscrowID",
+ {
+ "nth": 35,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "Hash256"
+ }
+ ],
+ [
+ "URITokenID",
+ {
+ "nth": 36,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "Hash256"
+ }
+ ],
[
"Amount",
{
@@ -1421,6 +1496,56 @@
"type": "Amount"
}
],
+ [
+ "HookCallbackFee",
+ {
+ "nth": 20,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "Amount"
+ }
+ ],
+ [
+ "LockedBalance",
+ {
+ "nth": 21,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "Amount"
+ }
+ ],
+ [
+ "BaseFeeDrops",
+ {
+ "nth": 22,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "Amount"
+ }
+ ],
+ [
+ "ReserveBaseDrops",
+ {
+ "nth": 23,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "Amount"
+ }
+ ],
+ [
+ "ReserveIncrementDrops",
+ {
+ "nth": 24,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "Amount"
+ }
+ ],
[
"PublicKey",
{
@@ -1661,6 +1786,16 @@
"type": "Blob"
}
],
+ [
+ "Blob",
+ {
+ "nth": 26,
+ "isVLEncoded": true,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "Blob"
+ }
+ ],
[
"Account",
{
@@ -1801,6 +1936,16 @@
"type": "Vector256"
}
],
+ [
+ "HookNamespaces",
+ {
+ "nth": 5,
+ "isVLEncoded": true,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "Vector256"
+ }
+ ],
[
"Paths",
{
@@ -2176,6 +2321,12 @@
"telCAN_NOT_QUEUE_BLOCKED": -389,
"telCAN_NOT_QUEUE_FEE": -388,
"telCAN_NOT_QUEUE_FULL": -387,
+ "telWRONG_NETWORK": -386,
+ "telREQUIRES_NETWORK_ID": -385,
+ "telNETWORK_ID_MAKES_TX_NON_CANONICAL": -384,
+ "telNON_LOCAL_EMITTED_TXN": -383,
+ "telIMPORT_VL_KEY_NOT_RECOGNISED": -382,
+ "telCAN_NOT_QUEUE_IMPORT": -381,
"temMALFORMED": -299,
"temBAD_AMOUNT": -298,
"temBAD_CURRENCY": -297,
@@ -2214,6 +2365,16 @@
"temUNKNOWN": -264,
"temSEQ_AND_TICKET": -263,
"temBAD_NFTOKEN_TRANSFER_FEE": -262,
+ "temAMM_BAD_TOKENS": -261,
+ "temXCHAIN_EQUAL_DOOR_ACCOUNTS": -260,
+ "temXCHAIN_BAD_PROOF": -259,
+ "temXCHAIN_BRIDGE_BAD_ISSUES": -258,
+ "temXCHAIN_BRIDGE_NONDOOR_OWNER": -257,
+ "temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT": -256,
+ "temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT": -255,
+ "temXCHAIN_TOO_MANY_ATTESTATIONS": -254,
+ "temHOOK_DATA_TOO_LARGE": -253,
+ "temHOOK_REJECTED": -252,
"tefFAILURE": -199,
"tefALREADY": -198,
"tefBAD_ADD_AUTH": -197,
@@ -2235,6 +2396,7 @@
"tefTOO_BIG": -181,
"tefNO_TICKET": -180,
"tefNFTOKEN_IS_NOT_TRANSFERABLE": -179,
+ "tefPAST_IMPORT_SEQ": -178,
"terRETRY": -99,
"terFUNDS_SPENT": -98,
"terINSUF_FEE_B": -97,
@@ -2247,6 +2409,8 @@
"terNO_RIPPLE": -90,
"terQUEUED": -89,
"terPRE_TICKET": -88,
+ "terNO_AMM": -87,
+ "terNO_HOOK": -86,
"tesSUCCESS": 0,
"tecCLAIM": 100,
"tecPATH_PARTIAL": 101,
@@ -2286,6 +2450,7 @@
"tecKILLED": 150,
"tecHAS_OBLIGATIONS": 151,
"tecTOO_SOON": 152,
+ "tecHOOK_REJECTED": 153,
"tecMAX_SEQUENCE_REACHED": 154,
"tecNO_SUITABLE_NFTOKEN_PAGE": 155,
"tecNFTOKEN_BUY_SELL_MISMATCH": 156,
@@ -2293,7 +2458,33 @@
"tecCANT_ACCEPT_OWN_NFTOKEN_OFFER": 158,
"tecINSUFFICIENT_FUNDS": 159,
"tecOBJECT_NOT_FOUND": 160,
- "tecINSUFFICIENT_PAYMENT": 161
+ "tecINSUFFICIENT_PAYMENT": 161,
+ "tecAMM_UNFUNDED": 162,
+ "tecAMM_BALANCE": 163,
+ "tecAMM_FAILED_DEPOSIT": 164,
+ "tecAMM_FAILED_WITHDRAW": 165,
+ "tecAMM_INVALID_TOKENS": 166,
+ "tecAMM_FAILED_BID": 167,
+ "tecAMM_FAILED_VOTE": 168,
+ "tecREQUIRES_FLAG": 169,
+ "tecPRECISION_LOSS": 170,
+ "tecBAD_XCHAIN_TRANSFER_ISSUE": 171,
+ "tecXCHAIN_NO_CLAIM_ID": 172,
+ "tecXCHAIN_BAD_CLAIM_ID": 173,
+ "tecXCHAIN_CLAIM_NO_QUORUM": 174,
+ "tecXCHAIN_PROOF_UNKNOWN_KEY": 175,
+ "tecXCHAIN_CREATE_ACCOUNT_NONXRP_ISSUE": 176,
+ "tecXCHAIN_WRONG_CHAIN": 177,
+ "tecXCHAIN_REWARD_MISMATCH": 178,
+ "tecXCHAIN_NO_SIGNERS_LIST": 179,
+ "tecXCHAIN_SENDING_ACCOUNT_MISMATCH": 180,
+ "tecXCHAIN_INSUFF_CREATE_AMOUNT": 181,
+ "tecXCHAIN_ACCOUNT_CREATE_PAST": 182,
+ "tecXCHAIN_ACCOUNT_CREATE_TOO_MANY": 183,
+ "tecXCHAIN_PAYMENT_FAILED": 184,
+ "tecXCHAIN_SELF_COMMIT": 185,
+ "tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR": 186,
+ "tecLAST_POSSIBLE_ENTRY": 255
},
"TRANSACTION_TYPES": {
"Invalid": -1,
@@ -2325,8 +2516,16 @@
"NFTokenCreateOffer": 27,
"NFTokenCancelOffer": 28,
"NFTokenAcceptOffer": 29,
+ "URITokenMint": 45,
+ "URITokenBurn": 46,
+ "URITokenBuy": 47,
+ "URITokenCreateSellOffer": 48,
+ "URITokenCancelSellOffer": 49,
+ "Import": 97,
+ "Invoke": 99,
"EnableAmendment": 100,
"SetFee": 101,
- "UNLModify": 102
+ "UNLModify": 102,
+ "EmitFailure": 103
}
}

5
raw-loader.d.ts vendored
View File

@@ -2,8 +2,3 @@ declare module '*.md' {
const content: string const content: string
export default content export default content
} }
declare module '*.hook-bundle.js' {
const content: string
export default content
}

View File

@@ -1,6 +1,5 @@
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',
@@ -36,37 +35,40 @@ 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) return; if (showToast) {
return toast.error(json.error, { id: toastId }) return toast.error(json.error, { id: toastId })
} } else {
const currNames = state.accounts.map(acc => acc.name) return
const info = await fetchAccountInfo(json.address, { silent: true }) }
state.accounts.push({ } else {
name: name || names.filter(name => !currNames.includes(name))[0], if (showToast) {
xrp: (json.xrp || 0 * 1000000).toString(), toast.success('New account created', { id: toastId })
address: json.address, }
secret: json.secret, const currNames = state.accounts.map(acc => acc.name)
sequence: info?.Sequence || 1, state.accounts.push({
hooks: [], name: name || names.filter(name => !currNames.includes(name))[0],
isLoading: false, xrp: (json.xrp || 0 * 1000000).toString(),
version: '2' address: json.address,
}) secret: json.secret,
if (showToast) { sequence: 1,
toast.success('New account created', { id: toastId }) hooks: [],
isLoading: false,
version: '2'
})
} }
} }
// 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()
// setTimeout(() => { // setTimeout(() => {
// addFaucetAccount(); // addFaucetAccount();
// }, 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')
@@ -77,7 +79,7 @@ export const addFunds = async (address: string) => {
if ('error' in json) { if ('error' in json) {
return toast.error(json.error, { id: toastId }) return toast.error(json.error, { id: toastId })
} else { } else {
toast.success(`Funds added (${json.xrp} XAH)`, { id: toastId }) toast.success(`Funds added (${json.xrp} XRP)`, { id: toastId })
const currAccount = state.accounts.find(acc => acc.address === address) const currAccount = state.accounts.find(acc => acc.address === address)
if (currAccount) { if (currAccount) {
currAccount.xrp = (Number(currAccount.xrp) + json.xrp * 1000000).toString() currAccount.xrp = (Number(currAccount.xrp) + json.xrp * 1000000).toString()

View File

@@ -44,7 +44,6 @@ export const compileCode = async (activeId: number) => {
output: 'wasm', output: 'wasm',
compress: true, compress: true,
strip: state.compileOptions.strip, strip: state.compileOptions.strip,
optimize: false,
files: [ files: [
{ {
type: 'c', type: 'c',

View File

@@ -14,4 +14,11 @@ export const deleteAccount = (addr?: string) => {
if (!acc) return if (!acc) return
acc.label = acc.value 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

@@ -6,9 +6,8 @@ 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, toHex } from '../../utils/setHook' import { SetHookData } from '../../utils/setHook'
import ResultLink from '../../components/ResultLink' import ResultLink from '../../components/ResultLink'
import { xrplSend } from './xrpl-client'
export const sha256 = async (string: string) => { export const sha256 = async (string: string) => {
const utf8 = new TextEncoder().encode(string) const utf8 = new TextEncoder().encode(string)
@@ -18,6 +17,13 @@ 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) {
@@ -58,6 +64,9 @@ export const prepareDeployHookTx = async (
if (!activeFile?.compiledContent) { if (!activeFile?.compiledContent) {
return return
} }
if (!state.client) {
return
}
const HookNamespace = (await sha256(data.HookNamespace)).toUpperCase() const HookNamespace = (await sha256(data.HookNamespace)).toUpperCase()
const hookOnValues: (keyof TTS)[] = data.Invoke.map(tt => tt.value) const hookOnValues: (keyof TTS)[] = data.Invoke.map(tt => tt.value)
const { HookParameters } = data const { HookParameters } = data
@@ -78,184 +87,196 @@ export const prepareDeployHookTx = async (
// } // }
// } // }
// }); // });
if (typeof window === 'undefined') return if (typeof window !== 'undefined') {
const tx = { const tx = {
Account: account.address, Account: account.address,
TransactionType: 'SetHook', TransactionType: 'SetHook',
Sequence: account.sequence, Sequence: account.sequence,
Fee: data.Fee, Fee: data.Fee,
NetworkID: process.env.NEXT_PUBLIC_NETWORK_ID, Hooks: [
Hooks: [ {
{ Hook: {
Hook: { CreateCode: arrayBufferToHex(activeFile?.compiledContent).toUpperCase(),
CreateCode: arrayBufferToHex(activeFile?.compiledContent).toUpperCase(), HookOn: calculateHookOn(hookOnValues),
HookOn: calculateHookOn(hookOnValues), HookNamespace,
HookNamespace, HookApiVersion: 0,
HookApiVersion: 0, Flags: 1,
Flags: 1, // ...(filteredHookGrants.length > 0 && { HookGrants: filteredHookGrants }),
// ...(filteredHookGrants.length > 0 && { HookGrants: filteredHookGrants }), ...(filteredHookParameters.length > 0 && {
...(filteredHookParameters.length > 0 && { HookParameters: filteredHookParameters
HookParameters: filteredHookParameters })
}) }
} }
} ]
] }
return tx
} }
return tx
} }
/* /* deployHook function turns the wasm binary into
* Turns the wasm binary into hex string, signs the transaction and deploys it to Hooks testnet. * hex string, signs the transaction and deploys it to
* Hooks testnet.
*/ */
export const deployHook = async (account: IAccount & { name?: string }, data: SetHookData) => { export const deployHook = async (account: IAccount & { name?: string }, data: SetHookData) => {
const activeFile = state.files[state.active]?.compiledContent if (typeof window !== 'undefined') {
? state.files[state.active] const activeFile = state.files[state.active]?.compiledContent
: state.files.filter(file => file.compiledContent)[0] ? state.files[state.active]
state.deployValues[activeFile.name] = data : state.files.filter(file => file.compiledContent)[0]
state.deployValues[activeFile.name] = data
const tx = await prepareDeployHookTx(account, data)
if (!tx) {
return
}
if (!state.client) {
return
}
const keypair = derive.familySeed(account.secret)
const tx = await prepareDeployHookTx(account, data) const { signedTransaction } = sign(tx, keypair)
if (!tx) { const currentAccount = state.accounts.find(acc => acc.address === account.address)
return if (currentAccount) {
} currentAccount.isLoading = true
const keypair = derive.familySeed(account.secret) }
const { signedTransaction } = sign(tx, keypair) let submitRes
const currentAccount = state.accounts.find(acc => acc.address === account.address) try {
if (currentAccount) { submitRes = await state.client?.send({
currentAccount.isLoading = true command: 'submit',
} tx_blob: signedTransaction
let submitRes
try {
submitRes = await xrplSend({
command: 'submit',
tx_blob: signedTransaction
})
const txHash = submitRes.tx_json?.hash
const resultMsg = ref(
<>
[<ResultLink result={submitRes.engine_result} />] {submitRes.engine_result_message}{' '}
{txHash && (
<>
Transaction hash:{' '}
<Link
as="a"
href={`https://${process.env.NEXT_PUBLIC_EXPLORER_URL}/${txHash}`}
target="_blank"
rel="noopener noreferrer"
>
{txHash}
</Link>
</>
)}
</>
)
if (submitRes.engine_result === 'tesSUCCESS') {
state.deployLogs.push({
type: 'success',
message: 'Hook deployed successfully ✅'
}) })
state.deployLogs.push({
type: 'success', const txHash = submitRes.tx_json?.hash
message: resultMsg const resultMsg = ref(
}) <>
} else if (submitRes.engine_result) { [<ResultLink result={submitRes.engine_result} />] {submitRes.engine_result_message}{' '}
{txHash && (
<>
Transaction hash:{' '}
<Link
as="a"
href={`https://${process.env.NEXT_PUBLIC_EXPLORER_URL}/${txHash}`}
target="_blank"
rel="noopener noreferrer"
>
{txHash}
</Link>
</>
)}
</>
)
if (submitRes.engine_result === 'tesSUCCESS') {
state.deployLogs.push({
type: 'success',
message: 'Hook deployed successfully ✅'
})
state.deployLogs.push({
type: 'success',
message: resultMsg
})
} else if (submitRes.engine_result) {
state.deployLogs.push({
type: 'error',
message: resultMsg
})
} else {
state.deployLogs.push({
type: 'error',
message: `[${submitRes.error}] ${submitRes.error_exception}`
})
}
} catch (err) {
console.log(err)
state.deployLogs.push({ state.deployLogs.push({
type: 'error', type: 'error',
message: resultMsg message: 'Error occurred while deploying'
})
} else {
state.deployLogs.push({
type: 'error',
message: `[${submitRes.error}] ${submitRes.error_exception}`
}) })
} }
} catch (err) { if (currentAccount) {
console.error(err) currentAccount.isLoading = false
state.deployLogs.push({ }
type: 'error', return submitRes
message: 'Error occurred while deploying'
})
} }
if (currentAccount) {
currentAccount.isLoading = false
}
return submitRes
} }
export const deleteHook = async (account: IAccount & { name?: string }) => { export const deleteHook = async (account: IAccount & { name?: string }) => {
if (!state.client) {
return
}
const currentAccount = state.accounts.find(acc => acc.address === account.address) const currentAccount = state.accounts.find(acc => acc.address === account.address)
if (currentAccount?.isLoading || !currentAccount?.hooks.length) { if (currentAccount?.isLoading || !currentAccount?.hooks.length) {
return return
} }
const tx = { if (typeof window !== 'undefined') {
Account: account.address, const tx = {
TransactionType: 'SetHook', Account: account.address,
Sequence: account.sequence, TransactionType: 'SetHook',
Fee: '100000', Sequence: account.sequence,
NetworkID: process.env.NEXT_PUBLIC_NETWORK_ID, Fee: '100000',
Hooks: [ Hooks: [
{ {
Hook: { Hook: {
CreateCode: '', CreateCode: '',
Flags: 1 Flags: 1
}
} }
} ]
] }
}
const keypair = derive.familySeed(account.secret)
try {
// Update tx Fee value with network estimation
const res = await estimateFee(tx, account)
tx['Fee'] = res?.base_fee || '1000'
} catch (err) {
console.error(err)
}
const { signedTransaction } = sign(tx, keypair)
if (currentAccount) {
currentAccount.isLoading = true
}
let submitRes
const toastId = toast.loading('Deleting hook...')
try {
submitRes = await xrplSend({
command: 'submit',
tx_blob: signedTransaction
})
if (submitRes.engine_result === 'tesSUCCESS') { const keypair = derive.familySeed(account.secret)
toast.success('Hook deleted successfully ✅', { id: toastId }) try {
state.deployLogs.push({ // Update tx Fee value with network estimation
type: 'success', const res = await estimateFee(tx, account)
message: 'Hook deleted successfully ✅' tx['Fee'] = res?.base_fee ? res?.base_fee : '1000'
}) } catch (err) {
state.deployLogs.push({ // use default value what you defined earlier
type: 'success', console.log(err)
message: `[${submitRes.engine_result}] ${submitRes.engine_result_message} Validated ledger index: ${submitRes.validated_ledger_index}` }
}) const { signedTransaction } = sign(tx, keypair)
currentAccount.hooks = []
} else { if (currentAccount) {
toast.error(`${submitRes.engine_result_message || submitRes.error_exception}`, { currentAccount.isLoading = true
id: toastId }
let submitRes
const toastId = toast.loading('Deleting hook...')
try {
submitRes = await state.client.send({
command: 'submit',
tx_blob: signedTransaction
}) })
if (submitRes.engine_result === 'tesSUCCESS') {
toast.success('Hook deleted successfully ✅', { id: toastId })
state.deployLogs.push({
type: 'success',
message: 'Hook deleted successfully ✅'
})
state.deployLogs.push({
type: 'success',
message: `[${submitRes.engine_result}] ${submitRes.engine_result_message} Validated ledger index: ${submitRes.validated_ledger_index}`
})
currentAccount.hooks = []
} else {
toast.error(`${submitRes.engine_result_message || submitRes.error_exception}`, {
id: toastId
})
state.deployLogs.push({
type: 'error',
message: `[${submitRes.engine_result || submitRes.error}] ${
submitRes.engine_result_message || submitRes.error_exception
}`
})
}
} catch (err) {
console.log(err)
toast.error('Error occurred while deleting hook', { id: toastId })
state.deployLogs.push({ state.deployLogs.push({
type: 'error', type: 'error',
message: `[${submitRes.engine_result || submitRes.error}] ${ message: 'Error occurred while deleting hook'
submitRes.engine_result_message || submitRes.error_exception
}`
}) })
} }
} catch (err) { if (currentAccount) {
console.log(err) currentAccount.isLoading = false
toast.error('Error occurred while deleting hook', { id: toastId }) }
state.deployLogs.push({ return submitRes
type: 'error',
message: 'Error occurred while deleting hook'
})
} }
if (currentAccount) {
currentAccount.isLoading = false
}
return submitRes
} }

View File

@@ -0,0 +1,66 @@
import { derive, sign } from 'xrpl-accountlib'
import state from '..'
import type { IAccount } from '..'
interface TransactionOptions {
TransactionType: string
Account?: string
Fee?: string
Destination?: string
[index: string]: any
}
interface OtherOptions {
logPrefix?: string
}
export const sendTransaction = async (
account: IAccount,
txOptions: TransactionOptions,
options?: OtherOptions
) => {
if (!state.client) throw Error('XRPL client not initalized')
const { Fee = '1000', ...opts } = txOptions
const tx: TransactionOptions = {
Account: account.address,
Sequence: account.sequence,
Fee, // TODO auto-fillable default
...opts
}
const { logPrefix = '' } = options || {}
try {
const signedAccount = derive.familySeed(account.secret)
const { signedTransaction } = sign(tx, signedAccount)
const response = await state.client.send({
command: 'submit',
tx_blob: signedTransaction
})
if (response.engine_result === 'tesSUCCESS') {
state.transactionLogs.push({
type: 'success',
message: `${logPrefix}[${response.engine_result}] ${response.engine_result_message}`
})
} else {
state.transactionLogs.push({
type: 'error',
message: `${logPrefix}[${response.error || response.engine_result}] ${
response.error_exception || response.engine_result_message
}`
})
}
const currAcc = state.accounts.find(acc => acc.address === account.address)
if (currAcc && response.account_sequence_next) {
currAcc.sequence = response.account_sequence_next
}
} catch (err) {
console.error(err)
state.transactionLogs.push({
type: 'error',
message:
err instanceof Error
? `${logPrefix}Error: ${err.message}`
: `${logPrefix}Something went wrong, try again later`
})
}
}

View File

@@ -4,12 +4,12 @@ import state from '..'
import type { IAccount } from '..' import type { IAccount } from '..'
import ResultLink from '../../components/ResultLink' import ResultLink from '../../components/ResultLink'
import { ref } from 'valtio' import { ref } from 'valtio'
import { xrplSend } from './xrpl-client'
interface TransactionOptions { interface TransactionOptions {
TransactionType: string TransactionType: string
Account?: string Account?: string
Fee?: string Fee?: string
Destination?: string
[index: string]: any [index: string]: any
} }
interface OtherOptions { interface OtherOptions {
@@ -21,23 +21,20 @@ export const sendTransaction = async (
txOptions: TransactionOptions, txOptions: TransactionOptions,
options?: OtherOptions options?: OtherOptions
) => { ) => {
if (!state.client) throw Error('XRPL client not initalized')
const { Fee = '1000', ...opts } = txOptions const { Fee = '1000', ...opts } = txOptions
const tx: TransactionOptions = { const tx: TransactionOptions = {
Account: account.address, Account: account.address,
Sequence: account.sequence, Sequence: account.sequence,
Fee, Fee, // TODO auto-fillable default
NetworkID: process.env.NEXT_PUBLIC_NETWORK_ID,
...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)
const response = await xrplSend({ const response = await state.client.send({
command: 'submit', command: 'submit',
tx_blob: signedTransaction tx_blob: signedTransaction
}) })

View File

@@ -1,7 +0,0 @@
import { XrplClient } from 'xrpl-client';
import state from '..';
export const xrplSend = async(...params: Parameters<XrplClient['send']>) => {
const client = await state.client.ready()
return client.send(...params);
}

View File

@@ -47,9 +47,6 @@ export const transactionFlags: { [key: /* TransactionType */ string]: Flags } =
tfSetFreeze: '0x00100000', tfSetFreeze: '0x00100000',
tfClearFreeze: '0x00200000', tfClearFreeze: '0x00200000',
}, },
URITokenMint: {
tfBurnable: '0x00000001',
},
} }
export const getFlags = (tt?: string) => { export const getFlags = (tt?: string) => {

View File

@@ -78,7 +78,7 @@ export interface IState {
splits: { splits: {
[id: string]: SplitSize [id: string]: SplitSize
} }
client: XrplClient client: XrplClient | null
clientStatus: 'offline' | 'online' clientStatus: 'offline' | 'online'
mainModalOpen: boolean mainModalOpen: boolean
mainModalShowed: boolean mainModalShowed: boolean
@@ -113,7 +113,7 @@ let initialState: IState = {
tabSize: 2 tabSize: 2
}, },
splits: {}, splits: {},
client: undefined!, // set below only. client: null,
clientStatus: 'offline' as 'offline', clientStatus: 'offline' as 'offline',
mainModalOpen: false, mainModalOpen: false,
mainModalShowed: false, mainModalShowed: false,
@@ -153,9 +153,9 @@ const state = proxy<IState>({
}) })
// Initialize socket connection // Initialize socket connection
const client = new XrplClient(`wss://${process.env.NEXT_PUBLIC_TESTNET_URL}`) const client = new XrplClient(`wss://${process.env.NEXT_PUBLIC_TESTNET_URL}`)
state.client = ref(client);
client.on('online', () => { client.on('online', () => {
state.client = ref(client)
state.clientStatus = 'online' state.clientStatus = 'online'
}) })

View File

@@ -5,55 +5,35 @@ 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'
import { typeIs } from '../utils/helpers'
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
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]>,
typeof commonFields[number] 'Account' | 'Sequence' | 'TransactionType'
> >
export const defaultTransaction: TransactionState = { export const defaultTransaction: TransactionState = {
selectedTransaction: null, selectedTransaction: null,
selectedAccount: null, selectedAccount: null,
selectedDestAccount: null,
selectedFlags: null, selectedFlags: null,
hookParameters: {},
memos: {},
editorIsSaved: true,
txIsLoading: false, txIsLoading: false,
txIsDisabled: false, txIsDisabled: false,
txFields: {}, txFields: {},
@@ -129,51 +109,46 @@ export const modifyTxState = (
return tx.state return tx.state
} }
// state to tx options
export const prepareTransaction = (data: any) => { export const prepareTransaction = (data: any) => {
let options = { ...data } let options = { ...data }
Object.keys(options).forEach(field => { Object.keys(options).forEach(field => {
let _value = options[field] let _value = options[field]
if (!typeIs(_value, 'object')) return // convert xrp
// amount.xrp if (_value && typeof _value === 'object' && _value.$type === 'xrp') {
if (_value.$type === 'amount.xrp') { if (+_value.$value) {
if (_value.$value) { options[field] = (+_value.$value * 1000000 + '') as any
options[field] = (+(_value as any).$value * 1000000 + '')
} else { } else {
options[field] = "" options[field] = undefined // 👇 💀
} }
} }
// amount.token // handle type: `json`
if (_value.$type === 'amount.token') { if (_value && typeof _value === 'object' && _value.$type === 'json') {
if (typeIs(_value.$value, 'string')) { if (typeof _value.$value === 'object') {
options[field] = parseJSON(_value.$value)
} else if (typeIs(_value.$value, 'object')) {
options[field] = _value.$value options[field] = _value.$value
} else { } else {
options[field] = undefined try {
options[field] = JSON.parse(_value.$value)
} catch (error) {
const message = `Input error for json field '${field}': ${error instanceof Error ? error.message : ''
}`
console.error(message)
options[field] = _value.$value
}
} }
} }
// account
if (_value.$type === 'account') { // delete unnecessary fields
options[field] = (_value.$value as any)?.toString() || "" if (!options[field]) {
} delete options[field]
// json
if (_value.$type === 'json') {
const val = _value.$value;
let res: any = val;
if (typeIs(val, ["object", "array"])) {
options[field] = res
} else if (typeIs(val, "string") && (res = parseJSON(val))) {
options[field] = res;
} else {
options[field] = res;
}
} }
}) })
return options return options
} }
// editor value to state
export const prepareState = (value: string, transactionType?: string) => { export const prepareState = (value: string, transactionType?: string) => {
const options = parseJSON(value) const options = parseJSON(value)
if (!options) { if (!options) {
@@ -183,7 +158,7 @@ export const prepareState = (value: string, transactionType?: string) => {
return return
} }
const { Account, TransactionType, HookParameters, Memos, ...rest } = options const { Account, TransactionType, Destination, ...rest } = options
let tx: Partial<TransactionState> = {} let tx: Partial<TransactionState> = {}
const schema = getTxFields(transactionType) const schema = getTxFields(transactionType)
@@ -213,23 +188,25 @@ export const prepareState = (value: string, transactionType?: string) => {
tx.selectedTransaction = null tx.selectedTransaction = null
} }
if (HookParameters && HookParameters instanceof Array) { if (schema.Destination !== undefined) {
tx.hookParameters = HookParameters.reduce<TransactionState["hookParameters"]>((acc, cur, idx) => { const dest = state.accounts.find(acc => acc.address === Destination)
const param = { label: fromHex(cur.HookParameter?.HookParameterName || ""), value: cur.HookParameter?.HookParameterValue || "" } if (dest) {
acc[idx] = param; tx.selectedDestAccount = {
return acc; 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 (Memos && Memos instanceof Array) {
tx.memos = Memos.reduce<TransactionState["memos"]>((acc, cur, idx) => {
const memo = { data: cur.Memo?.MemoData || "", type: fromHex(cur.Memo?.MemoType || ""), format: fromHex(cur.Memo?.MemoFormat || "") }
acc[idx] = memo;
return acc;
}, {})
}
if (getFlags(TransactionType) && rest.Flags) { if (getFlags(TransactionType) && rest.Flags) {
const flags = extractFlags(TransactionType, rest.Flags) const flags = extractFlags(TransactionType, rest.Flags)
@@ -240,31 +217,17 @@ export const prepareState = (value: string, transactionType?: string) => {
Object.keys(rest).forEach(field => { Object.keys(rest).forEach(field => {
const value = rest[field] const value = rest[field]
const schemaVal = schema[field as keyof TxFields] const schemaVal = schema[field as keyof TxFields]
const isXrp =
const isAmount = schemaVal && typeof value !== 'object' &&
typeIs(schemaVal, "object") && schemaVal &&
schemaVal.$type.startsWith('amount.'); typeof schemaVal === 'object' &&
const isAccount = schemaVal && schemaVal.$type === 'xrp'
typeIs(schemaVal, "object") && if (isXrp) {
schemaVal.$type.startsWith("account");
if (isAmount && ["number", "string"].includes(typeof value)) {
rest[field] = { rest[field] = {
$type: 'amount.xrp', // TODO narrow typed $type. $type: 'xrp',
$value: +value / 1000000 // ! maybe use bigint? $value: +value / 1000000 // ! maybe use bigint?
} }
} else if (isAmount && typeof value === 'object') { } else if (typeof value === 'object') {
rest[field] = {
$type: 'amount.token',
$value: value
}
} else if (isAccount) {
rest[field] = {
$type: "account",
$value: value?.toString() || ""
}
}
else if (typeof value === 'object') {
rest[field] = { rest[field] = {
$type: 'json', $type: 'json',
$value: value $value: value
@@ -273,7 +236,6 @@ export const prepareState = (value: string, transactionType?: string) => {
}) })
tx.txFields = rest tx.txFields = rest
tx.editorIsSaved = true;
return tx return tx
} }
@@ -284,12 +246,12 @@ export const getTxFields = (tt?: string) => {
if (!txFields) return {} if (!txFields) return {}
let _txFields = Object.keys(txFields) let _txFields = Object.keys(txFields)
.filter(key => !commonFields.includes(key as any)) .filter(key => !['TransactionType', 'Account', 'Sequence'].includes(key))
.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, commonFields } export { transactionsData }
export const transactionsOptions = transactionsData.map(tx => ({ export const transactionsOptions = transactionsData.map(tx => ({
value: tx.TransactionType, value: tx.TransactionType,

View File

@@ -1,31 +0,0 @@
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

View File

@@ -1,7 +1,6 @@
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
import { derive, sign } from 'xrpl-accountlib' import { derive, sign } from 'xrpl-accountlib'
import { IAccount } from '../state' import state, { IAccount } from '../state'
import { xrplSend } from '../state/actions/xrpl-client'
const estimateFee = async ( const estimateFee = async (
tx: Record<string, unknown>, tx: Record<string, unknown>,
@@ -23,10 +22,7 @@ const estimateFee = async (
const keypair = derive.familySeed(account.secret) const keypair = derive.familySeed(account.secret)
const { signedTransaction } = sign(copyTx, keypair) const { signedTransaction } = sign(copyTx, keypair)
const res = await xrplSend({ command: 'fee', tx_blob: signedTransaction }) const res = await state.client?.send({ command: 'fee', tx_blob: signedTransaction })
if (res.error) {
throw new Error(`[${res.error}] ${res.error_exception}.`);
}
if (res && res.drops) { if (res && res.drops) {
return res.drops return res.drops
} }
@@ -34,8 +30,7 @@ const estimateFee = async (
} catch (err) { } catch (err) {
if (!opts.silent) { if (!opts.silent) {
console.error(err) console.error(err)
const msg = err instanceof Error ? err.message : 'Error estimating fee!'; toast.error('Cannot estimate fee.') // ? Some better msg
toast.error(msg);
} }
return null return null
} }

View File

@@ -19,17 +19,3 @@ export const getFileExtention = (filename?: string): string | undefined => {
const ext = (filename.includes('.') && filename.split('.').pop()) || undefined const ext = (filename.includes('.') && filename.split('.').pop()) || undefined
return ext return ext
} }
type Type = "array" | "undefined" | "object" | "string" | "number" | "bigint" | "boolean" | "symbol" | "function"
type obj = Record<string | number | symbol, unknown>
type arr = unknown[]
export const typeIs = <T extends Type>(arg: any, t: T | T[]): arg is unknown & (T extends "array" ? arr : T extends "undefined" ? undefined | null : T extends "object" ? obj : T extends "string" ? string : T extends "number" ? number : T extends "bigint" ? bigint : T extends "boolean" ? boolean : T extends "symbol" ? symbol : T extends "function" ? Function : never) => {
const types = Array.isArray(t) ? t : [t]
return types.includes(typeOf(arg) as T);
}
export const typeOf = (arg: any): Type => {
const type = arg instanceof Array ? 'array' : arg === null ? 'undefined' : typeof arg
return type;
}

View File

@@ -8,7 +8,6 @@ export const tts = {
ttOFFER_CREATE: 7, ttOFFER_CREATE: 7,
ttOFFER_CANCEL: 8, ttOFFER_CANCEL: 8,
ttTICKET_CREATE: 10, ttTICKET_CREATE: 10,
ttTICKET_CANCEL: 11,
ttSIGNER_LIST_SET: 12, ttSIGNER_LIST_SET: 12,
ttPAYCHAN_CREATE: 13, ttPAYCHAN_CREATE: 13,
ttPAYCHAN_FUND: 14, ttPAYCHAN_FUND: 14,
@@ -19,28 +18,28 @@ export const tts = {
ttDEPOSIT_PREAUTH: 19, ttDEPOSIT_PREAUTH: 19,
ttTRUST_SET: 20, ttTRUST_SET: 20,
ttACCOUNT_DELETE: 21, ttACCOUNT_DELETE: 21,
ttSET_HOOK: 22, ttHOOK_SET: 22,
ttURI_TOKEN_MINT: 45, ttNFTOKEN_MINT: 25,
ttURI_TOKEN_BURN: 46, ttNFTOKEN_BURN: 26,
ttURI_TOKEN_BUY: 47, ttNFTOKEN_CREATE_OFFER: 27,
ttURI_TOKEN_CREATE_SELL_OFFER: 48, ttNFTOKEN_CANCEL_OFFER: 28,
ttURI_TOKEN_CANCEL_SELL_OFFER: 49, ttNFTOKEN_ACCEPT_OFFER: 29
ttIMPORT: 97,
ttINVOKE: 99
} }
export type TTS = typeof tts export type TTS = typeof tts
const calculateHookOn = (arr: (keyof TTS)[]) => { const calculateHookOn = (arr: (keyof TTS)[]) => {
let s = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff' let start = '0x000000003e3ff5bf'
arr.forEach(n => { arr.forEach(n => {
let v = BigInt(s) let v = BigInt(start)
v ^= BigInt(1) << BigInt(tts[n]) v ^= BigInt(1) << BigInt(tts[n as keyof TTS])
s = "0x" + v.toString(16) let s = v.toString(16)
let l = s.length
if (l < 16) s = '0'.repeat(16 - l) + s
s = '0x' + s
start = s
}) })
s = s.replace('0x', '') return start.substring(2)
s = s.padStart(64, '0')
return s
} }
export default calculateHookOn export default calculateHookOn

59
utils/libwabt.js Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,9 @@
import { typeIs, typeOf } from './helpers'
export const extractSchemaProps = <O extends object>(obj: O) => export const extractSchemaProps = <O extends object>(obj: O) =>
Object.entries(obj).reduce((prev, [key, val]) => { Object.entries(obj).reduce((prev, [key, val]) => {
const value = typeIs(val, "object") && '$type' in val && '$value' in val ? val?.$value : val const typeOf = <T>(arg: T) =>
arg instanceof Array ? 'array' : arg === null ? 'undefined' : typeof arg
const value = typeOf(val) === 'object' && '$type' in val && '$value' in val ? val?.$value : val
const type = typeOf(value) const type = typeOf(value)
let schema: any = { let schema: any = {
@@ -11,19 +12,19 @@ export const extractSchemaProps = <O extends object>(obj: O) =>
default: value default: value
} }
if (typeIs(value, "array")) { if (typeOf(value) === 'array') {
const item = value[0] // TODO merge other item schema's into one const item = value[0] // TODO merge other item schema's into one
if (typeIs(item, "object")) { if (typeOf(item) !== 'object') {
schema.items = { schema.items = {
type: 'object', type: 'object',
properties: extractSchemaProps(item), properties: extractSchemaProps(item),
default: item default: item
} }
} }
// TODO primitive-value arrays // TODO support primitive-value arrays
} }
if (typeIs(value, "object")) { if (typeOf(value) === 'object') {
schema.properties = extractSchemaProps(value) schema.properties = extractSchemaProps(value)
} }
return { return {

View File

@@ -20,13 +20,6 @@ export type SetHookData = {
} }
$metaData?: any $metaData?: any
}[] }[]
Memos?: {
Memo: {
MemoType: string,
MemoData: string
MemoFormat: string
}
}[]
// HookGrants: { // HookGrants: {
// HookGrant: { // HookGrant: {
// Authorize: string; // Authorize: string;
@@ -72,22 +65,12 @@ export const getInvokeOptions = (content?: string) => {
label: opt, label: opt,
value: opt value: opt
})) }))
// default
if (!invokeOptions.length) {
const payment = transactionOptions.find(tx => tx.value === 'ttPAYMENT')
if (payment) return [payment]
}
return invokeOptions return invokeOptions
} }
export function toHex(str: string) {
var result = ''
for (var i = 0; i < str.length; i++) {
const hex = str.charCodeAt(i).toString(16)
result += hex.padStart(2, '0')
}
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
}

View File

@@ -1,5 +1,5 @@
# hooks-account-buf-len # hooks-account-buf-len
Function [hook_account](https://xrpl-hooks.readme.io/reference/hook_account) has fixed-size account ID output. Function [hook_account](https://xrpl-hooks.readme.io/v2.0/reference/hook_account) has fixed-size account ID output.
This check warns about too-small size of its output buffer (if it's specified by a constant - variable parameter is ignored). This check warns about too-small size of its output buffer (if it's specified by a constant - variable parameter is ignored).

View File

@@ -1,5 +1,5 @@
# hooks-account-conv-buf-len # hooks-account-conv-buf-len
Function [util_raddr](https://xrpl-hooks.readme.io/reference/util_raddr) has fixed-size account ID input. Function [util_raddr](https://xrpl-hooks.readme.io/v2.0/reference/util_raddr) has fixed-size account ID input.
This check warns unless the correct size is passed in the input size parameter (if it's specified by a constant - variable parameter is ignored). This check warns unless the correct size is passed in the input size parameter (if it's specified by a constant - variable parameter is ignored).

View File

@@ -1,5 +1,5 @@
# hooks-account-conv-pure # hooks-account-conv-pure
Hooks identify accounts by the 20 byte account ID, which can be converted to an raddr using the [util_raddr](https://xrpl-hooks.readme.io/reference/util_raddr) function. If the account ID never changes, a more efficient way to do this is precompute the raddr from the account ID. Hooks identify accounts by the 20 byte account ID, which can be converted to an raddr using the [util_raddr](https://xrpl-hooks.readme.io/v2.0/reference/util_raddr) function. If the account ID never changes, a more efficient way to do this is precompute the raddr from the account ID.
This check warns about calls of `util_raddr` with constant input and proposes to add a tracing statement showing the computed value (so that the user can use it to replace the call). This check warns about calls of `util_raddr` with constant input and proposes to add a tracing statement showing the computed value (so that the user can use it to replace the call).

View File

@@ -1,7 +1,7 @@
# hooks-array-buf-len # hooks-array-buf-len
Hook API [sto_subarray](https://xrpl-hooks.readme.io/reference/sto_subarray) requires non-empty input buffer and takes a parameter specifying the array index, whose value is limited - the sought object cannot be found if the limit is exceeded. Hook API [sto_subarray](https://xrpl-hooks.readme.io/v2.0/reference/sto_subarray) requires non-empty input buffer and takes a parameter specifying the array index, whose value is limited - the sought object cannot be found if the limit is exceeded.
This check warns about empty input as well as too-large values of the index specified in calls to `sto_subarray` (if they're specified by constants - variable parameters are ignored). This check warns about empty input as well as too-large values of the index specified in calls to `sto_subarray` (if they're specified by constants - variable parameters are ignored).
[Read more](https://xrpl-hooks.readme.io/docs/serialized-objects) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/serialized-objects)

View File

@@ -1,3 +1,3 @@
# hooks-burden-prereq # hooks-burden-prereq
Hook API [etxn_burden](https://xrpl-hooks.readme.io/reference/etxn_burden) computes transaction burden, based on (i.a.) the number of reserved transactions, so a call to it must be preceded by a call to [etxn_reserve](https://xrpl-hooks.readme.io/reference/etxn_reserve). Hook API [etxn_burden](https://xrpl-hooks.readme.io/v2.0/reference/etxn_burden) computes transaction burden, based on (i.a.) the number of reserved transactions, so a call to it must be preceded by a call to [etxn_reserve](https://xrpl-hooks.readme.io/v2.0/reference/etxn_reserve).

View File

@@ -1,5 +1,5 @@
# hooks-control-string-arg # hooks-control-string-arg
Functions [accept](https://xrpl-hooks.readme.io/reference/accept) and [rollback](https://xrpl-hooks.readme.io/reference/rollback) take an optional string buffer stored outside the hook as its result message. This is useful for debugging but takes up space. Functions [accept](https://xrpl-hooks.readme.io/v2.0/reference/accept) and [rollback](https://xrpl-hooks.readme.io/v2.0/reference/rollback) take an optional string buffer stored outside the hook as its result message. This is useful for debugging but takes up space.
For a release version, this check warns about constant strings passed to `accept` and `rollback`. For a release version, this check warns about constant strings passed to `accept` and `rollback`.

View File

@@ -1,7 +1,7 @@
# hooks-detail-buf-len # hooks-detail-buf-len
Function [etxn_details](https://xrpl-hooks.readme.io/reference/etxn_details) has fixed-size sfEmitDetails output. Function [etxn_details](https://xrpl-hooks.readme.io/v2.0/reference/etxn_details) has fixed-size sfEmitDetails output.
This check warns about too-small size of its output buffer (if it's specified by a constant - variable parameter is ignored). This check warns about too-small size of its output buffer (if it's specified by a constant - variable parameter is ignored).
[Read more](https://xrpl-hooks.readme.io/docs/emitted-transactions) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/emitted-transactions)

View File

@@ -1,5 +1,5 @@
# hooks-detail-prereq # hooks-detail-prereq
Hook API [etxn_details](https://xrpl-hooks.readme.io/reference/etxn_details) serializes emit details, based on (i.a.) the number of reserved transactions, so a call to it must be preceded by a call to [etxn_reserve](https://xrpl-hooks.readme.io/reference/etxn_reserve). Hook API [etxn_details](https://xrpl-hooks.readme.io/v2.0/reference/etxn_details) serializes emit details, based on (i.a.) the number of reserved transactions, so a call to it must be preceded by a call to [etxn_reserve](https://xrpl-hooks.readme.io/v2.0/reference/etxn_reserve).
[Read more](https://xrpl-hooks.readme.io/docs/emitted-transactions) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/emitted-transactions)

View File

@@ -1,7 +1,7 @@
# hooks-emit-buf-len # hooks-emit-buf-len
Function [emit](https://xrpl-hooks.readme.io/reference/emit) has fixed-size transaction hash output. Function [emit](https://xrpl-hooks.readme.io/v2.0/reference/emit) has fixed-size transaction hash output.
This check warns about too-small size of its output buffer (if it's specified by a constant - variable parameter is ignored). This check warns about too-small size of its output buffer (if it's specified by a constant - variable parameter is ignored).
[Read more](https://xrpl-hooks.readme.io/docs/emitted-transactions) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/emitted-transactions)

View File

@@ -1,5 +1,5 @@
# hooks-emit-prereq # hooks-emit-prereq
Before emitting a transaction using [emit](https://xrpl-hooks.readme.io/reference/emit) Hook API, a hook must set a maximal count of transactions it plans to emit, by calling [etxn_reserve](https://xrpl-hooks.readme.io/reference/etxn_reserve). Before emitting a transaction using [emit](https://xrpl-hooks.readme.io/v2.0/reference/emit) Hook API, a hook must set a maximal count of transactions it plans to emit, by calling [etxn_reserve](https://xrpl-hooks.readme.io/v2.0/reference/etxn_reserve).
[Read more](https://xrpl-hooks.readme.io/docs/emitted-transactions) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/emitted-transactions)

View File

@@ -2,4 +2,4 @@
Recursive calls are disallowed in the implementation of hook entry points. Recursive calls are disallowed in the implementation of hook entry points.
[Read more](https://xrpl-hooks.readme.io/docs/loops-and-guarding#no-recursion) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/loops-and-guarding#no-recursion)

View File

@@ -2,4 +2,4 @@
Shows error on function definitions with unexpected (that is, neither `hook` nor `cbak`) names. Shows error on function definitions with unexpected (that is, neither `hook` nor `cbak`) names.
[Read more](https://xrpl-hooks.readme.io/docs/compiling-hooks#constraints) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/compiling-hooks#constraints)

View File

@@ -1,7 +1,7 @@
# hooks-entry-points # hooks-entry-points
A Hook always implements and exports a [hook](https://xrpl-hooks.readme.io/reference/hook) function. A Hook always implements and exports a [hook](https://xrpl-hooks.readme.io/v2.0/reference/hook) function.
This check shows error on translation units that do not have it. This check shows error on translation units that do not have it.
[Read more](https://xrpl-hooks.readme.io/docs/compiling-hooks) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/compiling-hooks)

View File

@@ -1,5 +1,5 @@
# hooks-fee-prereq # hooks-fee-prereq
Hook API [etxn_fee_base](https://xrpl-hooks.readme.io/reference/etxn_fee_base) estimates a transaction fee, based on (i.a.) the number of reserved transactions, so a call to it must be preceded by a call to [etxn_reserve](https://xrpl-hooks.readme.io/reference/etxn_reserve). Hook API [etxn_fee_base](https://xrpl-hooks.readme.io/v2.0/reference/etxn_fee_base) estimates a transaction fee, based on (i.a.) the number of reserved transactions, so a call to it must be preceded by a call to [etxn_reserve](https://xrpl-hooks.readme.io/v2.0/reference/etxn_reserve).
[Read more](https://xrpl-hooks.readme.io/docs/hook-fees) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/hook-fees)

View File

@@ -1,7 +1,7 @@
# hooks-field-add-buf-len # hooks-field-add-buf-len
Emplacing a new field into STObject by calling [sto_emplace](https://xrpl-hooks.readme.io/reference/sto_emplace) requires enough space to serialize the new STObject into; the API also limits sizes of the old object and field. Emplacing a new field into STObject by calling [sto_emplace](https://xrpl-hooks.readme.io/v2.0/reference/sto_emplace) requires enough space to serialize the new STObject into; the API also limits sizes of the old object and field.
This check warns about insufficient output buffer space as well as too-large values of the inputs in calls to `sto_emplace` (if they're specified by constants - variable parameters are ignored). This check warns about insufficient output buffer space as well as too-large values of the inputs in calls to `sto_emplace` (if they're specified by constants - variable parameters are ignored).
[Read more](https://xrpl-hooks.readme.io/docs/serialized-objects) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/serialized-objects)

View File

@@ -1,7 +1,7 @@
# hooks-field-buf-len # hooks-field-buf-len
Hook API [sto_subfield](https://xrpl-hooks.readme.io/reference/sto_subfield) requires non-empty input buffer. Hook API [sto_subfield](https://xrpl-hooks.readme.io/v2.0/reference/sto_subfield) requires non-empty input buffer.
This check warns about empty input in calls to `sto_subfield` (if it's specified by a constant - variable parameter is ignored). This check warns about empty input in calls to `sto_subfield` (if it's specified by a constant - variable parameter is ignored).
[Read more](https://xrpl-hooks.readme.io/docs/serialized-objects) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/serialized-objects)

View File

@@ -1,7 +1,7 @@
# hooks-field-del-buf-len # hooks-field-del-buf-len
Erasing a field from STObject by calling [sto_erase](https://xrpl-hooks.readme.io/reference/sto_erase) requires enough space to serialize the new STObject into; the API also limits size of the old object. Erasing a field from STObject by calling [sto_erase](https://xrpl-hooks.readme.io/v2.0/reference/sto_erase) requires enough space to serialize the new STObject into; the API also limits size of the old object.
This check warns about insufficient output buffer space as well as too-large value of the input STObject in calls to `sto_erase` (if they're specified by constants - variable parameters are ignored). This check warns about insufficient output buffer space as well as too-large value of the input STObject in calls to `sto_erase` (if they're specified by constants - variable parameters are ignored).
[Read more](https://xrpl-hooks.readme.io/docs/serialized-objects) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/serialized-objects)

View File

@@ -1,8 +1,8 @@
# hooks-float-arith-pure # hooks-float-arith-pure
Hooks can compute floating-point values in XFL format by calling functions [float_multiply](https://xrpl-hooks.readme.io/reference/float_multiply), [float_mulratio](https://xrpl-hooks.readme.io/reference/float_mulratio), [float_negate](https://xrpl-hooks.readme.io/reference/float_negate), [float_sum](https://xrpl-hooks.readme.io/reference/float_sum), [float_invert](https://xrpl-hooks.readme.io/reference/float_invert) and [float_divide](https://xrpl-hooks.readme.io/reference/float_divide) and access their constituent parts by calling [float_exponent](https://xrpl-hooks.readme.io/reference/float_exponent), [float_mantissa](https://xrpl-hooks.readme.io/reference/float_mantissa) and [float_sign](https://xrpl-hooks.readme.io/reference/float_sign). If the inputs of the computation never change, a more efficient way to do this is to precompute it. Hooks can compute floating-point values in XFL format by calling functions [float_multiply](https://xrpl-hooks.readme.io/v2.0/reference/float_multiply), [float_mulratio](https://xrpl-hooks.readme.io/v2.0/reference/float_mulratio), [float_negate](https://xrpl-hooks.readme.io/v2.0/reference/float_negate), [float_sum](https://xrpl-hooks.readme.io/v2.0/reference/float_sum), [float_invert](https://xrpl-hooks.readme.io/v2.0/reference/float_invert) and [float_divide](https://xrpl-hooks.readme.io/v2.0/reference/float_divide) and access their constituent parts by calling [float_exponent](https://xrpl-hooks.readme.io/v2.0/reference/float_exponent), [float_mantissa](https://xrpl-hooks.readme.io/v2.0/reference/float_mantissa) and [float_sign](https://xrpl-hooks.readme.io/v2.0/reference/float_sign). If the inputs of the computation never change, a more efficient way to do this is to precompute it.
This check warns about calls of the aforementioned functions with constant inputs and in simple cases proposes to add a tracing statement showing the computed value (so that the user can use it to replace the call). It also checks that the divisor passed to `float_divide`, `float_mulratio` and `float_invert` is not 0 (if it's specified by a constant - variable parameters are ignored). This check warns about calls of the aforementioned functions with constant inputs and in simple cases proposes to add a tracing statement showing the computed value (so that the user can use it to replace the call). It also checks that the divisor passed to `float_divide`, `float_mulratio` and `float_invert` is not 0 (if it's specified by a constant - variable parameters are ignored).
[Read more](https://xrpl-hooks.readme.io/docs/floating-point-numbers-xfl) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/floating-point-numbers-xfl)

View File

@@ -1,7 +1,7 @@
# hooks-float-compare-pure # hooks-float-compare-pure
Hooks can compare floating-point values in XFL format by calling the [float_compare](https://xrpl-hooks.readme.io/reference/float_compare) function. If the inputs of the comparison never change, its result is fixed and the function need not be called. Hooks can compare floating-point values in XFL format by calling the [float_compare](https://xrpl-hooks.readme.io/v2.0/reference/float_compare) function. If the inputs of the comparison never change, its result is fixed and the function need not be called.
This check warns about calls of `float_compare` with constant inputs as well as invalid values of the comparison mode parameter (if it's specified by a constant - variable parameter is ignored). This check warns about calls of `float_compare` with constant inputs as well as invalid values of the comparison mode parameter (if it's specified by a constant - variable parameter is ignored).
[Read more](https://xrpl-hooks.readme.io/docs/floating-point-numbers-xfl) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/floating-point-numbers-xfl)

View File

@@ -1,7 +1,7 @@
# hooks-float-int-pure # hooks-float-int-pure
Hooks can convert floating-point values in XFL format to integers by calling the [float_int](https://xrpl-hooks.readme.io/reference/float_int) function. If the inputs of this function never change, a more efficient way to do this is to precompute the integer value. Hooks can convert floating-point values in XFL format to integers by calling the [float_int](https://xrpl-hooks.readme.io/v2.0/reference/float_int) function. If the inputs of this function never change, a more efficient way to do this is to precompute the integer value.
This check warns about calls of `float_int` with constant inputs as well as invalid values of the decimal places parameter (if it's specified by a constant - variable parameter is ignored). This check warns about calls of `float_int` with constant inputs as well as invalid values of the decimal places parameter (if it's specified by a constant - variable parameter is ignored).
[Read more](https://xrpl-hooks.readme.io/docs/floating-point-numbers-xfl) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/floating-point-numbers-xfl)

View File

@@ -1,7 +1,7 @@
# hooks-float-manip-pure # hooks-float-manip-pure
Hooks can directly manipulate floating-point values in XFL format by calling functions [float_exponent_set](https://xrpl-hooks.readme.io/reference/float_exponent_set), [float_mantissa_set](https://xrpl-hooks.readme.io/reference/float_mantissa_set) and [float_sign_set](https://xrpl-hooks.readme.io/reference/float_sign_set). If the inputs of the update never change, a more efficient way to do this is to precompute it. Hooks can directly manipulate floating-point values in XFL format by calling functions [float_exponent_set](https://xrpl-hooks.readme.io/v2.0/reference/float_exponent_set), [float_mantissa_set](https://xrpl-hooks.readme.io/v2.0/reference/float_mantissa_set) and [float_sign_set](https://xrpl-hooks.readme.io/v2.0/reference/float_sign_set). If the inputs of the update never change, a more efficient way to do this is to precompute it.
This check warns about calls of the aforementioned functions with constant inputs and in simple cases proposes to add a tracing statement showing the computed value (so that the user can use it to replace the call). It also checks documented bounds of the second parameter of these functions (if it's specified by a constant - variable parameter is ignored). This check warns about calls of the aforementioned functions with constant inputs and in simple cases proposes to add a tracing statement showing the computed value (so that the user can use it to replace the call). It also checks documented bounds of the second parameter of these functions (if it's specified by a constant - variable parameter is ignored).
[Read more](https://xrpl-hooks.readme.io/docs/floating-point-numbers-xfl) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/floating-point-numbers-xfl)

View File

@@ -1,5 +1,5 @@
# hooks-float-one-pure # hooks-float-one-pure
Hooks can obtain XFL enclosing number 1 by calling the [float_one](https://xrpl-hooks.readme.io/reference/float_one) function. Since the number never changes, a more efficient way is to use its precomputed value. Hooks can obtain XFL enclosing number 1 by calling the [float_one](https://xrpl-hooks.readme.io/v2.0/reference/float_one) function. Since the number never changes, a more efficient way is to use its precomputed value.
[Read more](https://xrpl-hooks.readme.io/docs/floating-point-numbers-xfl) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/floating-point-numbers-xfl)

View File

@@ -1,7 +1,7 @@
# hooks-float-pure # hooks-float-pure
Hooks can use floating-point values in XFL format, creating them from mantissa and exponent by calling the [float_set](https://xrpl-hooks.readme.io/reference/float_set) function. If the mantissa and exponent never change, a more efficient way to do this is to precompute the floating-point value. Hooks can use floating-point values in XFL format, creating them from mantissa and exponent by calling the [float_set](https://xrpl-hooks.readme.io/v2.0/reference/float_set) function. If the mantissa and exponent never change, a more efficient way to do this is to precompute the floating-point value.
This check warns about calls of `float_set` with constant inputs and proposes to add a tracing statement showing the computed value (so that the user can use it to replace the call). In the special case of 0 mantissa and 0 exponent ("canonical 0"), a replacement value of 0 is proposed directly, with no need to trace it. This check warns about calls of `float_set` with constant inputs and proposes to add a tracing statement showing the computed value (so that the user can use it to replace the call). In the special case of 0 mantissa and 0 exponent ("canonical 0"), a replacement value of 0 is proposed directly, with no need to trace it.
[Read more](https://xrpl-hooks.readme.io/docs/floating-point-numbers-xfl) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/floating-point-numbers-xfl)

View File

@@ -3,4 +3,4 @@
Only compile-time constants can be used as an argument in loop GUARD call. This check warns if a non compile-time constant is used. Only compile-time constants can be used as an argument in loop GUARD call. This check warns if a non compile-time constant is used.
It also checks whether a compile-time constant is used as a first argument of `_g()` call and whether it is a unique value. If not - it warns. It also checks whether a compile-time constant is used as a first argument of `_g()` call and whether it is a unique value. If not - it warns.
[Read more](https://xrpl-hooks.readme.io/docs/loops-and-guarding) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/loops-and-guarding)

View File

@@ -1,5 +1,5 @@
# hooks-guard-called # hooks-guard-called
Every hook needs to import the guard function [_g](https://xrpl-hooks.readme.io/docs/loops-and-guarding#the-guard-function) and use it at least once. Every hook needs to import the guard function [_g](https://xrpl-hooks.readme.io/v2.0/docs/loops-and-guarding#the-guard-function) and use it at least once.
[Read more](https://xrpl-hooks.readme.io/docs/loops-and-guarding) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/loops-and-guarding)

View File

@@ -32,4 +32,4 @@ for(int i = 0; GUARD(3), i < 3; ++i) {
This check will warn if the GUARD call is missing and also it will propose a GUARD value based on the for loop initial value, This check will warn if the GUARD call is missing and also it will propose a GUARD value based on the for loop initial value,
the increment and loop condition. the increment and loop condition.
[Read more](https://xrpl-hooks.readme.io/docs/loops-and-guarding) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/loops-and-guarding)

View File

@@ -11,4 +11,4 @@ Like for loops, while loops must have a guard in their condition:
``` ```
<BR/> <BR/>
[Read more](https://xrpl-hooks.readme.io/docs/loops-and-guarding) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/loops-and-guarding)

View File

@@ -1,5 +1,5 @@
# hooks-hash-buf-len # hooks-hash-buf-len
Functions [util_sha512h](https://xrpl-hooks.readme.io/reference/util_sha512h), [hook_hash](https://xrpl-hooks.readme.io/reference/hook_hash), [ledger_last_hash](https://xrpl-hooks.readme.io/reference/ledger_last_hash), [etxn_nonce](https://xrpl-hooks.readme.io/reference/etxn_nonce) and [ledger_nonce](https://xrpl-hooks.readme.io/reference/ledger_nonce) have fixed-size hash output. Functions [util_sha512h](https://xrpl-hooks.readme.io/v2.0/reference/util_sha512h), [hook_hash](https://xrpl-hooks.readme.io/v2.0/reference/hook_hash), [ledger_last_hash](https://xrpl-hooks.readme.io/v2.0/reference/ledger_last_hash), [etxn_nonce](https://xrpl-hooks.readme.io/v2.0/reference/etxn_nonce) and [ledger_nonce](https://xrpl-hooks.readme.io/v2.0/reference/ledger_nonce) have fixed-size hash output.
This check warns about too-small size of their output buffer (if it's specified by a constant - variable parameter is ignored). This check warns about too-small size of their output buffer (if it's specified by a constant - variable parameter is ignored).

View File

@@ -1,7 +1,7 @@
# hooks-keylet-buf-len # hooks-keylet-buf-len
Computing a ripple keylet by calling [util_keylet](https://xrpl-hooks.readme.io/reference/util_keylet) requires valid parameters dependent on the keylet type. Computing a ripple keylet by calling [util_keylet](https://xrpl-hooks.readme.io/v2.0/reference/util_keylet) requires valid parameters dependent on the keylet type.
This check does not fully parse these parameters, but warns about invalid keylet type as well as buffer sizes that cannot be valid (if they're specified by constants - variable parameters are ignored). This check does not fully parse these parameters, but warns about invalid keylet type as well as buffer sizes that cannot be valid (if they're specified by constants - variable parameters are ignored).
[Read more](https://xrpl-hooks.readme.io/docs/slots-and-keylets) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/slots-and-keylets)

View File

@@ -1,7 +1,7 @@
# hooks-param-buf-len # hooks-param-buf-len
Function [hook_param](https://xrpl-hooks.readme.io/reference/hook_param) expects a limited-length name input and produces fixed-size value output. Function [hook_param](https://xrpl-hooks.readme.io/v2.0/reference/hook_param) expects a limited-length name input and produces fixed-size value output.
This check warns about invalid sizes of input and output buffers (if they're specified by constants - variable parameters are ignored). This check warns about invalid sizes of input and output buffers (if they're specified by constants - variable parameters are ignored).
[Read more](https://xrpl-hooks.readme.io/docs/parameters) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/parameters)

View File

@@ -1,7 +1,7 @@
# hooks-param-set-buf-len # hooks-param-set-buf-len
Function [hook_param_set](https://xrpl-hooks.readme.io/reference/hook_param_set) expects limited-length name, fixed-length hash and limited-length value inputs. Function [hook_param_set](https://xrpl-hooks.readme.io/v2.0/reference/hook_param_set) expects limited-length name, fixed-length hash and limited-length value inputs.
This check warns about invalid sizes of input buffers (if they're specified by constants - variable parameters are ignored). This check warns about invalid sizes of input buffers (if they're specified by constants - variable parameters are ignored).
[Read more](https://xrpl-hooks.readme.io/docs/parameters) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/parameters)

View File

@@ -1,5 +1,5 @@
# hooks-raddr-conv-buf-len # hooks-raddr-conv-buf-len
Hook API [util_accid](https://xrpl-hooks.readme.io/reference/util_accid) has upper limit on the length of its input (because it expects it to be a raddr) and fixed-size account ID output. Hook API [util_accid](https://xrpl-hooks.readme.io/v2.0/reference/util_accid) has upper limit on the length of its input (because it expects it to be a raddr) and fixed-size account ID output.
This check warns about invalid sizes of input and output parameters (if they're specified by constants - variable parameters are ignored). This check warns about invalid sizes of input and output parameters (if they're specified by constants - variable parameters are ignored).

View File

@@ -1,5 +1,5 @@
# hooks-raddr-conv-pure # hooks-raddr-conv-pure
Hooks identify accounts by the 20 byte account ID, which can be converted from a raddr using the [util_accid](https://xrpl-hooks.readme.io/reference/util_accid) function. If the raddr never changes, a more efficient way to do this is precompute the account-id from the raddr. Hooks identify accounts by the 20 byte account ID, which can be converted from a raddr using the [util_accid](https://xrpl-hooks.readme.io/v2.0/reference/util_accid) function. If the raddr never changes, a more efficient way to do this is precompute the account-id from the raddr.
This check warns about calls of `util_accid` with constant input and proposes to add a tracing statement showing the computed value (so that the user can use it to replace the call). This check warns about calls of `util_accid` with constant input and proposes to add a tracing statement showing the computed value (so that the user can use it to replace the call).

View File

@@ -1,7 +1,7 @@
# hooks-reserve-limit # hooks-reserve-limit
Hook API [etxn_reserve](https://xrpl-hooks.readme.io/reference/etxn_reserve) takes a parameter specifying the number of transactions intended to emit from the calling hook. Value of this parameter is limited, and the function fails if the limit is exceeded. Hook API [etxn_reserve](https://xrpl-hooks.readme.io/v2.0/reference/etxn_reserve) takes a parameter specifying the number of transactions intended to emit from the calling hook. Value of this parameter is limited, and the function fails if the limit is exceeded.
This check warns about too-large values of the number of reserved transactions (if they're specified by a constant - variable parameter is ignored). This check warns about too-large values of the number of reserved transactions (if they're specified by a constant - variable parameter is ignored).
[Read more](https://xrpl-hooks.readme.io/docs/emitted-transactions) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/emitted-transactions)

View File

@@ -1,5 +1,5 @@
# hooks-skip-hash-buf-len # hooks-skip-hash-buf-len
Function [hook_skip](https://xrpl-hooks.readme.io/reference/hook_skip) has fixed-size canonical hash input. Function [hook_skip](https://xrpl-hooks.readme.io/v2.0/reference/hook_skip) has fixed-size canonical hash input.
This check warns about invalid size of its input buffer (if it's specified by a constant - variable parameter is ignored). This check warns about invalid size of its input buffer (if it's specified by a constant - variable parameter is ignored).

View File

@@ -1,7 +1,7 @@
# hooks-slot-hash-buf-len # hooks-slot-hash-buf-len
Function [slot_id](https://xrpl-hooks.readme.io/reference/slot_id) has fixed-size canonical hash output. Function [slot_id](https://xrpl-hooks.readme.io/v2.0/reference/slot_id) has fixed-size canonical hash output.
This check warns about too-small size of its output buffer as well as invalid values of the slot number parameter (if they're specified by constants - variable parameters are ignored). This check warns about too-small size of its output buffer as well as invalid values of the slot number parameter (if they're specified by constants - variable parameters are ignored).
[Read more](https://xrpl-hooks.readme.io/docs/slots-and-keylets) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/slots-and-keylets)

View File

@@ -1,7 +1,7 @@
# hooks-slot-keylet-buf-len # hooks-slot-keylet-buf-len
Function [slot_set](https://xrpl-hooks.readme.io/reference/slot_set) has structured keylet input. Function [slot_set](https://xrpl-hooks.readme.io/v2.0/reference/slot_set) has structured keylet input.
This check does not parse the input, but warns about its sizes that cannot be valid as well as invalid values of the slot number parameter (if they're specified by constants - variable parameters are ignored). This check does not parse the input, but warns about its sizes that cannot be valid as well as invalid values of the slot number parameter (if they're specified by constants - variable parameters are ignored).
[Read more](https://xrpl-hooks.readme.io/docs/slots-and-keylets) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/slots-and-keylets)

View File

@@ -1,7 +1,7 @@
# hooks-slot-limit # hooks-slot-limit
Hook APIs [slot](https://xrpl-hooks.readme.io/reference/slot), [slot_count](https://xrpl-hooks.readme.io/reference/slot_count), [slot_clear](https://xrpl-hooks.readme.io/reference/slot_clear), [slot_size](https://xrpl-hooks.readme.io/reference/slot_size), [slot_float](https://xrpl-hooks.readme.io/reference/slot_float) and [trace_slot](https://xrpl-hooks.readme.io/reference/trace_slot) take a parameter specifying the accessed slot number. Value of this parameter is limited, and the functions fail if the limit is exceeded. Hook APIs [slot](https://xrpl-hooks.readme.io/v2.0/reference/slot), [slot_count](https://xrpl-hooks.readme.io/v2.0/reference/slot_count), [slot_clear](https://xrpl-hooks.readme.io/v2.0/reference/slot_clear), [slot_size](https://xrpl-hooks.readme.io/v2.0/reference/slot_size), [slot_float](https://xrpl-hooks.readme.io/v2.0/reference/slot_float) and [trace_slot](https://xrpl-hooks.readme.io/v2.0/reference/trace_slot) take a parameter specifying the accessed slot number. Value of this parameter is limited, and the functions fail if the limit is exceeded.
This check warns about too-large values of the slot number (if it's specified by a constant - variable parameter is ignored). This check warns about too-large values of the slot number (if it's specified by a constant - variable parameter is ignored).
[Read more](https://xrpl-hooks.readme.io/docs/slots-and-keylets) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/slots-and-keylets)

View File

@@ -1,7 +1,7 @@
# hooks-slot-sub-limit # hooks-slot-sub-limit
Hook APIs [slot_subarray](https://xrpl-hooks.readme.io/reference/slot_subarray) and [slot_subfield](https://xrpl-hooks.readme.io/reference/slot_subfield) take parameters specifying parent and child slot numbers. Values of these parameters are limited, and the functions fail if the limit is exceeded. Hook APIs [slot_subarray](https://xrpl-hooks.readme.io/v2.0/reference/slot_subarray) and [slot_subfield](https://xrpl-hooks.readme.io/v2.0/reference/slot_subfield) take parameters specifying parent and child slot numbers. Values of these parameters are limited, and the functions fail if the limit is exceeded.
This check warns about too-large values of the slot numbers (if they're specified by a constant - variable parameters are ignored). This check warns about too-large values of the slot numbers (if they're specified by a constant - variable parameters are ignored).
[Read more](https://xrpl-hooks.readme.io/docs/slots-and-keylets) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/slots-and-keylets)

View File

@@ -1,7 +1,7 @@
# hooks-slot-type-limit # hooks-slot-type-limit
Hook API [slot_type](https://xrpl-hooks.readme.io/reference/slot_type) takes a parameter specifying the accessed slot number. Value of this parameter is limited, and the function fails if the limit is exceeded. Hook API [slot_type](https://xrpl-hooks.readme.io/v2.0/reference/slot_type) takes a parameter specifying the accessed slot number. Value of this parameter is limited, and the function fails if the limit is exceeded.
This check warns about too-large values of the slot number as well as invalid values of the flags parameter (if they're specified by constants - variable parameters are ignored). This check warns about too-large values of the slot number as well as invalid values of the flags parameter (if they're specified by constants - variable parameters are ignored).
[Read more](https://xrpl-hooks.readme.io/docs/slots-and-keylets) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/slots-and-keylets)

View File

@@ -1,7 +1,7 @@
# hooks-state-buf-len # hooks-state-buf-len
Functions [state](https://xrpl-hooks.readme.io/reference/state) and [state_set](https://xrpl-hooks.readme.io/reference/state_set) accept fixed-size Hook State key. Functions [state](https://xrpl-hooks.readme.io/v2.0/reference/state) and [state_set](https://xrpl-hooks.readme.io/v2.0/reference/state_set) accept fixed-size Hook State key.
This check warns about invalid size of its input buffer (if it's specified by a constant - variable parameter is ignored). This check warns about invalid size of its input buffer (if it's specified by a constant - variable parameter is ignored).
[Read more](https://xrpl-hooks.readme.io/docs/state-management) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/state-management)

View File

@@ -1,5 +1,5 @@
# hooks-transaction-hash-buf-len # hooks-transaction-hash-buf-len
Function [otxn_id](https://xrpl-hooks.readme.io/reference/otxn_id) has fixed-size canonical hash output. Function [otxn_id](https://xrpl-hooks.readme.io/v2.0/reference/otxn_id) has fixed-size canonical hash output.
This check warns about too-small size of its output buffer (if it's specified by a constant - variable parameter is ignored). This check warns about too-small size of its output buffer (if it's specified by a constant - variable parameter is ignored).

View File

@@ -1,7 +1,7 @@
# hooks-transaction-slot-limit # hooks-transaction-slot-limit
Function [otxn_slot](https://xrpl-hooks.readme.io/reference/otxn_slot) takes a parameter specifying the accessed slot number. Value of this parameter is limited, and the function fails if the limit is exceeded. Function [otxn_slot](https://xrpl-hooks.readme.io/v2.0/reference/otxn_slot) takes a parameter specifying the accessed slot number. Value of this parameter is limited, and the function fails if the limit is exceeded.
This check warns about too-large values of the slot number (if it's specified by a constant - variable parameter is ignored). This check warns about too-large values of the slot number (if it's specified by a constant - variable parameter is ignored).
[Read more](https://xrpl-hooks.readme.io/docs/slots-and-keylets) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/slots-and-keylets)

View File

@@ -1,7 +1,7 @@
# hooks-trivial-cbak # hooks-trivial-cbak
A Hook may implement and export a [cbak](https://xrpl-hooks.readme.io/reference/cbak) function. A Hook may implement and export a [cbak](https://xrpl-hooks.readme.io/v2.0/reference/cbak) function.
But the function is optional, and defining it so that it doesn't do anything besides returning a constant value is unnecessary (except for some debugging scenarios) and just increases the hook size. This check warns about such implementations. But the function is optional, and defining it so that it doesn't do anything besides returning a constant value is unnecessary (except for some debugging scenarios) and just increases the hook size. This check warns about such implementations.
[Read more](https://xrpl-hooks.readme.io/docs/compiling-hooks) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/compiling-hooks)

View File

@@ -1,7 +1,7 @@
# hooks-validate-buf-len # hooks-validate-buf-len
Hook API [sto_validate](https://xrpl-hooks.readme.io/reference/sto_validate) requires non-empty input buffer. Hook API [sto_validate](https://xrpl-hooks.readme.io/v2.0/reference/sto_validate) requires non-empty input buffer.
This check warns about empty input in calls to `sto_validate` (if it's specified by a constant - variable parameter is ignored). This check warns about empty input in calls to `sto_validate` (if it's specified by a constant - variable parameter is ignored).
[Read more](https://xrpl-hooks.readme.io/docs/serialized-objects) [Read more](https://xrpl-hooks.readme.io/v2.0/docs/serialized-objects)

View File

@@ -1,5 +1,5 @@
# hooks-verify-buf-len # hooks-verify-buf-len
Verifying a cryptographic signature by calling [util_verify](https://xrpl-hooks.readme.io/reference/util_verify) requires valid public key & data signature. Verifying a cryptographic signature by calling [util_verify](https://xrpl-hooks.readme.io/v2.0/reference/util_verify) requires valid public key & data signature.
This check does not fully parse these parameters, but warns about their sizes that cannot be valid (if they're specified by constants - variable parameters are ignored). This check does not fully parse these parameters, but warns about their sizes that cannot be valid (if they're specified by constants - variable parameters are ignored).

937
yarn.lock

File diff suppressed because it is too large Load Diff