Compare commits
102 Commits
fix/renami
...
fix-transa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41d9a399d7 | ||
|
|
4c6a0b5a40 | ||
|
|
0616149b6c | ||
|
|
ee86d657c9 | ||
|
|
12ebd4c1bc | ||
|
|
6ed0b54fc5 | ||
|
|
b7e1343055 | ||
|
|
a13dbaeb53 | ||
|
|
98a7a1ee72 | ||
|
|
46c0ec9682 | ||
|
|
fbc27b58c3 | ||
|
|
635e1e22aa | ||
|
|
62ca0603fc | ||
|
|
3234ba851a | ||
|
|
26aeea5a0d | ||
|
|
4a1bd98bd4 | ||
|
|
4171e618a2 | ||
|
|
1860b2d9ce | ||
|
|
488828f9d6 | ||
|
|
9f2105f6d3 | ||
|
|
6beccaef68 | ||
|
|
215af7258c | ||
|
|
ac3137088b | ||
|
|
1b8debda87 | ||
|
|
c50c7a5860 | ||
|
|
d21cda21d0 | ||
|
|
1cae0f161e | ||
|
|
412e3f2bbf | ||
|
|
dc37b1911a | ||
|
|
c348868c89 | ||
|
|
2c3cfebe3a | ||
|
|
6265a9cdbf | ||
|
|
1321b498cf | ||
|
|
801d9778cb | ||
|
|
2cf18ef61c | ||
|
|
4d2dc16ce5 | ||
|
|
9ecf5478e6 | ||
|
|
6d1ef110b7 | ||
|
|
b9edfcd63b | ||
|
|
b653d9a9cb | ||
|
|
da28e0a7d1 | ||
|
|
8a5b83d57f | ||
|
|
025eff6cf2 | ||
|
|
62d521b2cc | ||
|
|
7aafca21df | ||
|
|
80f58e903c | ||
|
|
c4af3df017 | ||
|
|
5d8d142bc4 | ||
|
|
e27a71d713 | ||
|
|
e08b07cbeb | ||
|
|
e4936c03ef | ||
|
|
21a69ac8ea | ||
|
|
52e4f219f7 | ||
|
|
e1f34c4beb | ||
|
|
54a89c969e | ||
|
|
ded867d997 | ||
|
|
0fce9af77c | ||
|
|
55c68c580a | ||
|
|
832a7997d1 | ||
|
|
4528e5a16e | ||
|
|
38f064c6d8 | ||
|
|
fbf4565dbc | ||
|
|
9001c64fed | ||
|
|
03b768db4e | ||
|
|
825af0db89 | ||
|
|
31043f33ab | ||
|
|
39699a1cb9 | ||
|
|
b50b300307 | ||
|
|
82c06cbb12 | ||
|
|
423ee18e6a | ||
|
|
3bb26d0c9b | ||
|
|
43c83d0de6 | ||
|
|
6bb407cb0f | ||
|
|
d7b29ba809 | ||
|
|
ca81d8ad41 | ||
|
|
a7e59d7b73 | ||
|
|
8f6b28cef5 | ||
|
|
c1815f272b | ||
|
|
76871a8041 | ||
|
|
d2033c8035 | ||
|
|
48daf1c5c8 | ||
|
|
a3365e4beb | ||
|
|
45d813cdad | ||
|
|
911416aa2f | ||
|
|
2d836af9ed | ||
|
|
4f0fc838be | ||
|
|
fa93912c38 | ||
|
|
f5cb76c302 | ||
|
|
df1d65dcab | ||
|
|
1513f78991 | ||
|
|
3a064f307b | ||
|
|
6fca05f310 | ||
|
|
31e67d382f | ||
|
|
27475301e4 | ||
|
|
2d9ca2674e | ||
|
|
221c727af6 | ||
|
|
3b0a8c44c9 | ||
|
|
094f739b80 | ||
|
|
2d82966b3b | ||
|
|
93c5ef231e | ||
|
|
53c2104b94 | ||
|
|
c336ff8334 |
@@ -5,7 +5,8 @@ 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="hooks-testnet-v2.xrpl-labs.com"
|
NEXT_PUBLIC_TESTNET_URL="xahau-test.net"
|
||||||
NEXT_PUBLIC_DEBUG_STREAM_URL="hooks-testnet-v2-debugstream.xrpl-labs.com"
|
NEXT_PUBLIC_DEBUG_STREAM_URL="xahau-test.net/debugstream"
|
||||||
NEXT_PUBLIC_EXPLORER_URL="hooks-testnet-v2-explorer.xrpl-labs.com"
|
NEXT_PUBLIC_EXPLORER_URL="explorer.xahau-test.net"
|
||||||
NEXT_PUBLIC_SITE_URL=http://localhost:3000
|
NEXT_PUBLIC_NETWORK_ID="21338"
|
||||||
|
NEXT_PUBLIC_SITE_URL="http://localhost:3000"
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -33,3 +33,7 @@ yarn-error.log*
|
|||||||
# vercel
|
# vercel
|
||||||
.vercel
|
.vercel
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
|
# yarn
|
||||||
|
.yarnrc.yml
|
||||||
|
.yarn/
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# XRPL Hooks Builder
|
# Xahau Hooks Builder
|
||||||
|
|
||||||
https://hooks-builder.xrpl.org/
|
https://builder.xahau.network/
|
||||||
|
|
||||||
This is the repository for XRPL Hooks Builder. This project is built with Next.JS
|
This is the repository for Xahau Hooks Builder. This project is built with Next.JS
|
||||||
|
|
||||||
## General
|
## General
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ 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,
|
||||||
@@ -199,7 +200,7 @@ export const AccountDialog = ({
|
|||||||
.toUnit()
|
.toUnit()
|
||||||
.toLocaleString(undefined, {
|
.toLocaleString(undefined, {
|
||||||
style: 'currency',
|
style: 'currency',
|
||||||
currency: 'XRP',
|
currency: 'XAH',
|
||||||
currencyDisplay: 'name'
|
currencyDisplay: 'name'
|
||||||
})}
|
})}
|
||||||
<Button
|
<Button
|
||||||
@@ -301,7 +302,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 =>
|
||||||
snap.client?.send({
|
xrplSend({
|
||||||
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
|
||||||
@@ -329,7 +330,7 @@ const Accounts: FC<AccountProps> = props => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
const objectRequests = snap.accounts.map(acc => {
|
const objectRequests = snap.accounts.map(acc => {
|
||||||
return snap.client?.send({
|
return xrplSend({
|
||||||
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
|
||||||
@@ -458,7 +459,7 @@ const Accounts: FC<AccountProps> = props => {
|
|||||||
.toUnit()
|
.toUnit()
|
||||||
.toLocaleString(undefined, {
|
.toLocaleString(undefined, {
|
||||||
style: 'currency',
|
style: 'currency',
|
||||||
currency: 'XRP',
|
currency: 'XAH',
|
||||||
currencyDisplay: 'name'
|
currencyDisplay: 'name'
|
||||||
})})`
|
})})`
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { subscribeKey } from 'valtio/utils'
|
|||||||
import { Select } from '.'
|
import { Select } from '.'
|
||||||
import state, { ILog, transactionsState } from '../state'
|
import state, { ILog, transactionsState } from '../state'
|
||||||
import { extractJSON } from '../utils/json'
|
import { extractJSON } from '../utils/json'
|
||||||
|
import EnrichLog from './EnrichLog'
|
||||||
import LogBox from './LogBox'
|
import LogBox from './LogBox'
|
||||||
|
|
||||||
interface ISelect<T = string> {
|
interface ISelect<T = string> {
|
||||||
@@ -99,6 +100,11 @@ const addListeners = (account: ISelect | null) => {
|
|||||||
|
|
||||||
subscribeKey(streamState, 'selectedAccount', addListeners)
|
subscribeKey(streamState, 'selectedAccount', addListeners)
|
||||||
|
|
||||||
|
const clearLog = () => {
|
||||||
|
streamState.logs = []
|
||||||
|
streamState.statusChangeTimestamp = Date.now()
|
||||||
|
}
|
||||||
|
|
||||||
const DebugStream = () => {
|
const DebugStream = () => {
|
||||||
const { selectedAccount, logs } = useSnapshot(streamState)
|
const { selectedAccount, logs } = useSnapshot(streamState)
|
||||||
const { activeHeader: activeTxTab } = useSnapshot(transactionsState)
|
const { activeHeader: activeTxTab } = useSnapshot(transactionsState)
|
||||||
@@ -134,11 +140,6 @@ const DebugStream = () => {
|
|||||||
streamState.selectedAccount = account
|
streamState.selectedAccount = account
|
||||||
}, [activeTxTab])
|
}, [activeTxTab])
|
||||||
|
|
||||||
const clearLog = () => {
|
|
||||||
streamState.logs = []
|
|
||||||
streamState.statusChangeTimestamp = Date.now()
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LogBox enhanced renderNav={renderNav} title="Debug stream" logs={logs} clearLog={clearLog} />
|
<LogBox enhanced renderNav={renderNav} title="Debug stream" logs={logs} clearLog={clearLog} />
|
||||||
)
|
)
|
||||||
@@ -157,9 +158,11 @@ export const pushLog = (str: any, opts: Partial<Pick<ILog, 'type'>> = {}): ILog
|
|||||||
const timestring = !timestamp ? tm : new Date(timestamp).toLocaleTimeString()
|
const timestring = !timestamp ? tm : new Date(timestamp).toLocaleTimeString()
|
||||||
|
|
||||||
const extracted = extractJSON(msg)
|
const extracted = extractJSON(msg)
|
||||||
const message = !extracted ? msg : msg.slice(0, extracted.start) + msg.slice(extracted.end + 1)
|
const _message = !extracted ? msg : msg.slice(0, extracted.start) + msg.slice(extracted.end + 1)
|
||||||
|
const message = ref(<EnrichLog str={_message} />)
|
||||||
|
|
||||||
const jsonData = extracted ? JSON.stringify(extracted.result, null, 2) : undefined
|
const _jsonData = extracted ? JSON.stringify(extracted.result, null, 2) : undefined
|
||||||
|
const jsonData = _jsonData ? ref(<EnrichLog str={_jsonData} />) : undefined
|
||||||
|
|
||||||
if (extracted?.result?.id?._Request?.includes('hooks-builder-req')) {
|
if (extracted?.result?.id?._Request?.includes('hooks-builder-req')) {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const contentShow = keyframes({
|
|||||||
'100%': { opacity: 1 }
|
'100%': { opacity: 1 }
|
||||||
})
|
})
|
||||||
const StyledOverlay = styled(DialogPrimitive.Overlay, {
|
const StyledOverlay = styled(DialogPrimitive.Overlay, {
|
||||||
zIndex: 10000,
|
zIndex: 3000,
|
||||||
backgroundColor: blackA.blackA9,
|
backgroundColor: blackA.blackA9,
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
inset: 0,
|
inset: 0,
|
||||||
|
|||||||
73
components/EnrichLog.tsx
Normal file
73
components/EnrichLog.tsx
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { FC, useState } from 'react'
|
||||||
|
import regexifyString from 'regexify-string'
|
||||||
|
import { useSnapshot } from 'valtio'
|
||||||
|
import { Link } from '.'
|
||||||
|
import state from '../state'
|
||||||
|
import { AccountDialog } from './Accounts'
|
||||||
|
import Tooltip from './Tooltip'
|
||||||
|
import hookSetCodes from '../content/hook-set-codes.json'
|
||||||
|
import { capitalize } from '../utils/helpers'
|
||||||
|
|
||||||
|
interface EnrichLogProps {
|
||||||
|
str?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const EnrichLog: FC<EnrichLogProps> = ({ str }) => {
|
||||||
|
const { accounts } = useSnapshot(state)
|
||||||
|
const [dialogAccount, setDialogAccount] = useState<string | null>(null)
|
||||||
|
if (!str || !accounts.length) return <>{str}</>
|
||||||
|
|
||||||
|
const addrs = accounts.map(acc => acc.address)
|
||||||
|
const regex = `(${addrs.join('|')}|HookSet\\(\\d+\\))`
|
||||||
|
const res = regexifyString({
|
||||||
|
pattern: new RegExp(regex, 'gim'),
|
||||||
|
decorator: (match, idx) => {
|
||||||
|
if (match.startsWith('r')) {
|
||||||
|
// Account
|
||||||
|
const name = accounts.find(acc => acc.address === match)?.name
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={match + idx}
|
||||||
|
as="a"
|
||||||
|
onClick={() => setDialogAccount(match)}
|
||||||
|
title={match}
|
||||||
|
highlighted
|
||||||
|
>
|
||||||
|
{name || match}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (match.startsWith('HookSet')) {
|
||||||
|
const code = match.match(/^HookSet\((\d+)\)/)?.[1]
|
||||||
|
const val = hookSetCodes.find(v => code && v.code === +code)
|
||||||
|
console.log({ code, val })
|
||||||
|
if (!val) return match
|
||||||
|
|
||||||
|
const content = capitalize(val.description) || 'No hint available!'
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
HookSet(
|
||||||
|
<Tooltip content={content}>
|
||||||
|
<Link>{val.identifier}</Link>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return match
|
||||||
|
},
|
||||||
|
input: str
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{res}
|
||||||
|
<AccountDialog
|
||||||
|
setActiveAccountAddress={setDialogAccount}
|
||||||
|
activeAccountAddress={dialogAccount}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EnrichLog
|
||||||
@@ -1,15 +1,12 @@
|
|||||||
import { useRef, useLayoutEffect, ReactNode, FC, useState, useCallback } from 'react'
|
import { useRef, useLayoutEffect, ReactNode, FC, useState } from 'react'
|
||||||
import { IconProps, Notepad, Prohibit } from 'phosphor-react'
|
import { IconProps, Notepad, Prohibit } from 'phosphor-react'
|
||||||
import useStayScrolled from 'react-stay-scrolled'
|
import useStayScrolled from 'react-stay-scrolled'
|
||||||
import NextLink from 'next/link'
|
import NextLink from 'next/link'
|
||||||
|
|
||||||
import Container from './Container'
|
import Container from './Container'
|
||||||
import LogText from './LogText'
|
import LogText from './LogText'
|
||||||
import state, { ILog } from '../state'
|
import { ILog } from '../state'
|
||||||
import { Pre, Link, Heading, Button, Text, Flex, Box } from '.'
|
import { Pre, Link, Heading, Button, Text, Flex, Box } from '.'
|
||||||
import regexifyString from 'regexify-string'
|
|
||||||
import { useSnapshot } from 'valtio'
|
|
||||||
import { AccountDialog } from './Accounts'
|
|
||||||
|
|
||||||
interface ILogBox {
|
interface ILogBox {
|
||||||
title: string
|
title: string
|
||||||
@@ -143,70 +140,25 @@ const LogBox: FC<ILogBox> = ({
|
|||||||
export const Log: FC<ILog> = ({
|
export const Log: FC<ILog> = ({
|
||||||
type,
|
type,
|
||||||
timestring,
|
timestring,
|
||||||
message: _message,
|
message,
|
||||||
link,
|
link,
|
||||||
linkText,
|
linkText,
|
||||||
defaultCollapsed,
|
defaultCollapsed,
|
||||||
jsonData: _jsonData
|
jsonData
|
||||||
}) => {
|
}) => {
|
||||||
const [expanded, setExpanded] = useState(!defaultCollapsed)
|
const [expanded, setExpanded] = useState(!defaultCollapsed)
|
||||||
const { accounts } = useSnapshot(state)
|
|
||||||
const [dialogAccount, setDialogAccount] = useState<string | null>(null)
|
|
||||||
|
|
||||||
const enrichAccounts = useCallback(
|
|
||||||
(str?: string): ReactNode => {
|
|
||||||
if (!str || !accounts.length) return str
|
|
||||||
|
|
||||||
const pattern = `(${accounts.map(acc => acc.address).join('|')})`
|
|
||||||
const res = regexifyString({
|
|
||||||
pattern: new RegExp(pattern, 'gim'),
|
|
||||||
decorator: (match, idx) => {
|
|
||||||
const name = accounts.find(acc => acc.address === match)?.name
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
key={match + idx}
|
|
||||||
as="a"
|
|
||||||
onClick={() => setDialogAccount(match)}
|
|
||||||
title={match}
|
|
||||||
highlighted
|
|
||||||
>
|
|
||||||
{name || match}
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
input: str
|
|
||||||
})
|
|
||||||
|
|
||||||
return <>{res}</>
|
|
||||||
},
|
|
||||||
[accounts]
|
|
||||||
)
|
|
||||||
|
|
||||||
let message: ReactNode
|
|
||||||
|
|
||||||
if (typeof _message === 'string') {
|
|
||||||
_message = _message.trim().replace(/\n /gi, '\n')
|
|
||||||
if (_message) message = enrichAccounts(_message)
|
|
||||||
else message = <Text muted>{'""'}</Text>
|
|
||||||
} else {
|
|
||||||
message = _message
|
|
||||||
}
|
|
||||||
|
|
||||||
const jsonData = enrichAccounts(_jsonData)
|
|
||||||
|
|
||||||
|
if (message === undefined) message = <Text muted>{'undefined'}</Text>
|
||||||
|
else if (message === '') message = <Text muted>{'""'}</Text>
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AccountDialog
|
|
||||||
setActiveAccountAddress={setDialogAccount}
|
|
||||||
activeAccountAddress={dialogAccount}
|
|
||||||
/>
|
|
||||||
<LogText variant={type}>
|
<LogText variant={type}>
|
||||||
{timestring && (
|
{timestring && (
|
||||||
<Text muted monospace>
|
<Text muted monospace>
|
||||||
{timestring}{' '}
|
{timestring}{' '}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
<Pre>{message} </Pre>
|
<Pre>{message}</Pre>
|
||||||
{link && (
|
{link && (
|
||||||
<NextLink href={link} shallow passHref>
|
<NextLink href={link} shallow passHref>
|
||||||
<Link as="a">{linkText}</Link>
|
<Link as="a">{linkText}</Link>
|
||||||
@@ -219,7 +171,6 @@ export const Log: FC<ILog> = ({
|
|||||||
)}
|
)}
|
||||||
{expanded && jsonData && <Pre block>{jsonData}</Pre>}
|
{expanded && jsonData && <Pre block>{jsonData}</Pre>}
|
||||||
</LogText>
|
</LogText>
|
||||||
<br />
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ const Navigation = () => {
|
|||||||
<Spinner />
|
<Spinner />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Heading css={{ lineHeight: 1 }}>{snap.gistName || 'XRPL Hooks'}</Heading>
|
<Heading css={{ lineHeight: 1 }}>{snap.gistName || 'Xahau 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" /> XRPL Hooks Builder
|
<Logo width="48px" height="48px" /> Xahau 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 XRP Ledger.
|
Hooks add smart contract functionality to the Xahau Network.
|
||||||
</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/XRPL-Labs/xrpld-hooks"
|
href="https://github.com/Xahau"
|
||||||
>
|
>
|
||||||
<ArrowUpRight size="15px" /> Hooks Github
|
<ArrowUpRight size="15px" /> Xahau 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://xrpl-hooks.readme.io/v2.0/docs"
|
href="https://docs.xahau.network/readme-1"
|
||||||
>
|
>
|
||||||
<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/v2.0" passHref>
|
<Link href="https://xrpl-hooks.readme.io/" passHref>
|
||||||
<a target="_blank" rel="noreferrer noopener">
|
<a target="_blank" rel="noreferrer noopener">
|
||||||
<Button outline>
|
<Button outline>
|
||||||
<BookOpen size="15px" />
|
<BookOpen size="15px" />
|
||||||
|
|||||||
24
components/ResultLink.tsx
Normal file
24
components/ResultLink.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { FC } from 'react'
|
||||||
|
import { Link } from '.'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
result?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResultLink: FC<Props> = ({ result }) => {
|
||||||
|
if (!result) return null
|
||||||
|
let href: string
|
||||||
|
if (result === 'tesSUCCESS') {
|
||||||
|
href = 'https://xrpl.org/tes-success.html'
|
||||||
|
} else {
|
||||||
|
// Going shortcut here because of url structure, if that changes we will do it manually
|
||||||
|
href = `https://xrpl.org/${result.slice(0, 3)}-codes.html`
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Link as="a" href={href} target="_blank" rel="noopener noreferrer">
|
||||||
|
{result}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ResultLink
|
||||||
@@ -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 = (code: string, data?: Record<string, any>) => {
|
const generateHtmlTemplate = async (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,8 +29,10 @@ const generateHtmlTemplate = (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>
|
||||||
@@ -72,7 +74,9 @@ const generateHtmlTemplate = (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>
|
||||||
@@ -100,6 +104,7 @@ 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']
|
||||||
@@ -127,16 +132,31 @@ const RunScript: React.FC<{ file: IFile }> = ({ file: { content, name } }) => {
|
|||||||
return fields
|
return fields
|
||||||
}, [content])
|
}, [content])
|
||||||
|
|
||||||
const runScript = useCallback(() => {
|
const runScript = useCallback(async () => {
|
||||||
|
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 = generateHtmlTemplate(content, data)
|
const template = await 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 = [
|
||||||
@@ -145,6 +165,7 @@ 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(() => {
|
||||||
@@ -174,11 +195,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(() => {
|
const handleRun = useCallback(async () => {
|
||||||
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 = []
|
||||||
runScript()
|
await runScript();
|
||||||
setIsDialogOpen(false)
|
setIsDialogOpen(false)
|
||||||
}, [isDisabled, runScript])
|
}, [isDisabled, runScript])
|
||||||
|
|
||||||
@@ -279,7 +300,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} onClick={handleRun}>
|
<Button variant="primary" isDisabled={isDisabled || isLoading} isLoading={isLoading} onClick={handleRun}>
|
||||||
Run script
|
Run script
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -3,13 +3,11 @@ 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 } from 'react-select'
|
import type { Props, StylesConfig } 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 })
|
||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
const getColors = (isDark: boolean) => {
|
||||||
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,
|
||||||
@@ -30,93 +28,136 @@ const Select = forwardRef<any, Props>((props, ref) => {
|
|||||||
}
|
}
|
||||||
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}
|
||||||
container: provided => {
|
{...props}
|
||||||
return {
|
/>
|
||||||
...provided,
|
)
|
||||||
position: 'relative'
|
})
|
||||||
}
|
|
||||||
},
|
// eslint-disable-next-line react/display-name
|
||||||
singleValue: provided => ({
|
const Creatable = forwardRef<any, Props>((props, ref) => {
|
||||||
...provided,
|
const { theme } = useTheme()
|
||||||
color: colors.mauve12
|
const isDark = theme === 'dark'
|
||||||
}),
|
const styles = getStyles(isDark)
|
||||||
menu: provided => ({
|
return (
|
||||||
...provided,
|
<CreatableSelectInput
|
||||||
backgroundColor: colors.dropDownBg
|
ref={ref}
|
||||||
}),
|
formatCreateLabel={label => `Enter "${label}"`}
|
||||||
control: (provided, state) => {
|
menuPosition={props.menuPosition || 'fixed'}
|
||||||
return {
|
styles={styles}
|
||||||
...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, {})
|
||||||
|
|||||||
71
components/Sequence.tsx
Normal file
71
components/Sequence.tsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { FC, useCallback, useState } from 'react'
|
||||||
|
import state from '../state'
|
||||||
|
import { Flex, Input, Button } from '.'
|
||||||
|
import fetchAccountInfo from '../utils/accountInfo'
|
||||||
|
import { useSnapshot } from 'valtio'
|
||||||
|
|
||||||
|
interface AccountSequenceProps {
|
||||||
|
address?: string
|
||||||
|
}
|
||||||
|
const AccountSequence: FC<AccountSequenceProps> = ({ address }) => {
|
||||||
|
const { accounts } = useSnapshot(state)
|
||||||
|
const account = accounts.find(acc => acc.address === address)
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
const setSequence = useCallback(
|
||||||
|
(sequence: number) => {
|
||||||
|
const acc = state.accounts.find(acc => acc.address == address)
|
||||||
|
if (!acc) return
|
||||||
|
acc.sequence = sequence
|
||||||
|
},
|
||||||
|
[address]
|
||||||
|
)
|
||||||
|
const handleUpdateSequence = useCallback(
|
||||||
|
async (silent?: boolean) => {
|
||||||
|
if (!account) return
|
||||||
|
setIsLoading(true)
|
||||||
|
|
||||||
|
const info = await fetchAccountInfo(account.address, { silent })
|
||||||
|
if (info) {
|
||||||
|
setSequence(info.Sequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsLoading(false)
|
||||||
|
},
|
||||||
|
[account, setSequence]
|
||||||
|
)
|
||||||
|
const disabled = !account
|
||||||
|
return (
|
||||||
|
<Flex row align="center" fluid>
|
||||||
|
<Input
|
||||||
|
placeholder="Account sequence"
|
||||||
|
value={account?.sequence || ""}
|
||||||
|
disabled={!account}
|
||||||
|
type="number"
|
||||||
|
readOnly={true}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
size="xs"
|
||||||
|
variant="primary"
|
||||||
|
type="button"
|
||||||
|
outline
|
||||||
|
disabled={disabled}
|
||||||
|
isDisabled={disabled}
|
||||||
|
isLoading={isLoading}
|
||||||
|
css={{
|
||||||
|
background: '$backgroundAlt',
|
||||||
|
position: 'absolute',
|
||||||
|
right: '$2',
|
||||||
|
fontSize: '$xs',
|
||||||
|
cursor: 'pointer',
|
||||||
|
alignContent: 'center',
|
||||||
|
display: 'flex'
|
||||||
|
}}
|
||||||
|
onClick={() => handleUpdateSequence()}
|
||||||
|
>
|
||||||
|
Update
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AccountSequence
|
||||||
@@ -21,6 +21,7 @@ import { prepareDeployHookTx, sha256 } from '../state/actions/deployHook'
|
|||||||
import estimateFee from '../utils/estimateFee'
|
import estimateFee from '../utils/estimateFee'
|
||||||
import { getParameters, getInvokeOptions, transactionOptions, SetHookData } from '../utils/setHook'
|
import { getParameters, getInvokeOptions, transactionOptions, SetHookData } from '../utils/setHook'
|
||||||
import { capitalize } from '../utils/helpers'
|
import { capitalize } from '../utils/helpers'
|
||||||
|
import AccountSequence from './Sequence'
|
||||||
|
|
||||||
export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
||||||
({ accountAddress }) => {
|
({ accountAddress }) => {
|
||||||
@@ -190,6 +191,10 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
|||||||
onChange={(acc: any) => setSelectedAccount(acc)}
|
onChange={(acc: any) => setSelectedAccount(acc)}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
<Box css={{ width: '100%', position: 'relative' }}>
|
||||||
|
<Label>Sequence</Label>
|
||||||
|
<AccountSequence address={selectedAccount?.value} />
|
||||||
|
</Box>
|
||||||
<Box css={{ width: '100%' }}>
|
<Box css={{ width: '100%' }}>
|
||||||
<Label>Invoke on transactions</Label>
|
<Label>Invoke on transactions</Label>
|
||||||
<Controller
|
<Controller
|
||||||
|
|||||||
@@ -72,7 +72,12 @@ const Tooltip: React.FC<React.ComponentProps<typeof StyledContent> & ITooltip> =
|
|||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<TooltipPrimitive.Root open={open} defaultOpen={defaultOpen} onOpenChange={onOpenChange}>
|
<TooltipPrimitive.Root
|
||||||
|
open={open}
|
||||||
|
defaultOpen={defaultOpen}
|
||||||
|
onOpenChange={onOpenChange}
|
||||||
|
delayDuration={100}
|
||||||
|
>
|
||||||
<TooltipPrimitive.Trigger asChild>{children}</TooltipPrimitive.Trigger>
|
<TooltipPrimitive.Trigger asChild>{children}</TooltipPrimitive.Trigger>
|
||||||
<StyledContent side="bottom" align="center" {...rest}>
|
<StyledContent side="bottom" align="center" {...rest}>
|
||||||
<div dangerouslySetInnerHTML={{ __html: content }} />
|
<div dangerouslySetInnerHTML={{ __html: content }} />
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import { TxJson } from './json'
|
|||||||
import { TxUI } from './ui'
|
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 { SetHookData, toHex } from '../../utils/setHook'
|
||||||
|
|
||||||
export interface TransactionProps {
|
export interface TransactionProps {
|
||||||
header: string
|
header: string
|
||||||
@@ -39,17 +41,40 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
|
|||||||
|
|
||||||
const prepareOptions = useCallback(
|
const prepareOptions = useCallback(
|
||||||
(state: Partial<TransactionState> = txState) => {
|
(state: Partial<TransactionState> = txState) => {
|
||||||
const { selectedTransaction, selectedDestAccount, selectedAccount, txFields } = state
|
const {
|
||||||
|
selectedTransaction,
|
||||||
|
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 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,
|
||||||
TransactionType,
|
TransactionType,
|
||||||
Destination,
|
Account,
|
||||||
Account
|
Memos
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[txState]
|
[txState]
|
||||||
@@ -65,15 +90,29 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
|
|||||||
}
|
}
|
||||||
}, [selectedAccount?.value, selectedTransaction?.value, setState, txIsLoading])
|
}, [selectedAccount?.value, selectedTransaction?.value, setState, txIsLoading])
|
||||||
|
|
||||||
|
const getJsonString = useCallback(
|
||||||
|
(state?: Partial<TransactionState>) =>
|
||||||
|
JSON.stringify(prepareOptions?.(state) || {}, null, editorSettings.tabSize),
|
||||||
|
[editorSettings.tabSize, prepareOptions]
|
||||||
|
)
|
||||||
|
|
||||||
|
const saveEditorState = useCallback(
|
||||||
|
(value: string = '', transactionType?: string) => {
|
||||||
|
const pTx = prepareState(value, transactionType)
|
||||||
|
if (pTx) {
|
||||||
|
pTx.editorValue = getJsonString(pTx)
|
||||||
|
return setState(pTx)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[getJsonString, setState]
|
||||||
|
)
|
||||||
|
|
||||||
const submitTest = useCallback(async () => {
|
const submitTest = useCallback(async () => {
|
||||||
let st: TransactionState | undefined
|
let st: TransactionState | undefined
|
||||||
const tt = txState.selectedTransaction?.value
|
const tt = txState.selectedTransaction?.value
|
||||||
if (viewType === 'json') {
|
if (viewType === 'json') {
|
||||||
// save the editor state first
|
st = saveEditorState(editorValue, tt)
|
||||||
const pst = prepareState(editorValue || '', tt)
|
if (!st) return
|
||||||
if (!pst) return
|
|
||||||
|
|
||||||
st = setState(pst)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const account = accounts.find(acc => acc.address === selectedAccount?.value)
|
const account = accounts.find(acc => acc.address === selectedAccount?.value)
|
||||||
@@ -86,11 +125,12 @@ 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
|
||||||
const fields = getTxFields(options.TransactionType)
|
Object.keys(options).forEach(field => {
|
||||||
if (fields.Destination && !options.Destination) {
|
if (!options[field]) {
|
||||||
throw Error('Destination account is required!')
|
delete options[field]
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
await sendTransaction(account, options, { logPrefix })
|
await sendTransaction(account, options, { logPrefix })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -104,23 +144,18 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
|
|||||||
}
|
}
|
||||||
setState({ txIsLoading: false })
|
setState({ txIsLoading: false })
|
||||||
}, [
|
}, [
|
||||||
|
txState.selectedTransaction?.value,
|
||||||
viewType,
|
viewType,
|
||||||
accounts,
|
accounts,
|
||||||
txIsDisabled,
|
txIsDisabled,
|
||||||
setState,
|
setState,
|
||||||
header,
|
header,
|
||||||
|
saveEditorState,
|
||||||
editorValue,
|
editorValue,
|
||||||
txState,
|
|
||||||
selectedAccount?.value,
|
selectedAccount?.value,
|
||||||
prepareOptions
|
prepareOptions
|
||||||
])
|
])
|
||||||
|
|
||||||
const getJsonString = useCallback(
|
|
||||||
(state?: Partial<TransactionState>) =>
|
|
||||||
JSON.stringify(prepareOptions?.(state) || {}, null, editorSettings.tabSize),
|
|
||||||
[editorSettings.tabSize, prepareOptions]
|
|
||||||
)
|
|
||||||
|
|
||||||
const resetState = useCallback(
|
const resetState = useCallback(
|
||||||
(transactionType: SelectOption | undefined = defaultTransactionType) => {
|
(transactionType: SelectOption | undefined = defaultTransactionType) => {
|
||||||
const fields = getTxFields(transactionType?.value)
|
const fields = getTxFields(transactionType?.value)
|
||||||
@@ -130,14 +165,12 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
|
|||||||
selectedTransaction: transactionType
|
selectedTransaction: transactionType
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fields.Destination !== undefined) {
|
if (transactionType?.value && transactionFlags[transactionType?.value] && fields.Flags) {
|
||||||
nwState.selectedDestAccount = null
|
nwState.selectedFlags = extractFlags(transactionType.value, fields.Flags)
|
||||||
fields.Destination = ''
|
fields.Flags = undefined
|
||||||
} else {
|
|
||||||
fields.Destination = undefined
|
|
||||||
}
|
}
|
||||||
nwState.txFields = fields
|
|
||||||
|
|
||||||
|
nwState.txFields = fields
|
||||||
const state = modifyTxState(header, nwState, { replaceState: true })
|
const state = modifyTxState(header, nwState, { replaceState: true })
|
||||||
const editorValue = getJsonString(state)
|
const editorValue = getJsonString(state)
|
||||||
return setState({ editorValue })
|
return setState({ editorValue })
|
||||||
@@ -168,18 +201,34 @@ 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}
|
||||||
estimateFee={estimateFee}
|
estimateFee={estimateFee}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<TxUI state={txState} setState={setState} estimateFee={estimateFee} />
|
<TxUI
|
||||||
|
switchToJson={switchToJson}
|
||||||
|
state={txState}
|
||||||
|
resetState={resetState}
|
||||||
|
setState={setState}
|
||||||
|
estimateFee={estimateFee}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
<Flex
|
<Flex
|
||||||
row
|
row
|
||||||
@@ -195,8 +244,8 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
|
|||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (viewType === 'ui') {
|
if (viewType === 'ui') {
|
||||||
setState({ viewType: 'json' })
|
switchToJson()
|
||||||
} else setState({ viewType: 'ui' })
|
} else switchToUI()
|
||||||
}}
|
}}
|
||||||
outline
|
outline
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react'
|
import { FC, useCallback, useEffect, useState } from 'react'
|
||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
import state, { prepareState, transactionsData, TransactionState } from '../../state'
|
import state, { transactionsData, TransactionState } from '../../state'
|
||||||
import Text from '../Text'
|
import Text from '../Text'
|
||||||
import { Flex, Link } from '..'
|
import { Flex, Link } from '..'
|
||||||
import { showAlert } from '../../state/actions/showAlert'
|
import { showAlert } from '../../state/actions/showAlert'
|
||||||
@@ -11,27 +11,27 @@ import Monaco from '../Monaco'
|
|||||||
import type monaco from 'monaco-editor'
|
import type monaco from 'monaco-editor'
|
||||||
|
|
||||||
interface JsonProps {
|
interface JsonProps {
|
||||||
getJsonString?: (state?: Partial<TransactionState>) => string
|
getJsonString: (st?: Partial<TransactionState>) => string
|
||||||
|
saveEditorState: (val?: string, tt?: string) => TransactionState | undefined
|
||||||
header?: string
|
header?: string
|
||||||
setState: (pTx?: Partial<TransactionState> | undefined) => void
|
setState: (pTx?: Partial<TransactionState> | undefined) => void
|
||||||
state: TransactionState
|
state: TransactionState
|
||||||
estimateFee?: () => Promise<string | undefined>
|
estimateFee?: () => Promise<string | undefined>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TxJson: FC<JsonProps> = ({ getJsonString, state: txState, header, setState }) => {
|
export const TxJson: FC<JsonProps> = ({
|
||||||
|
getJsonString,
|
||||||
|
state: txState,
|
||||||
|
header,
|
||||||
|
setState,
|
||||||
|
saveEditorState
|
||||||
|
}) => {
|
||||||
const { editorSettings, accounts } = useSnapshot(state)
|
const { editorSettings, accounts } = useSnapshot(state)
|
||||||
const { editorValue, estimatedFee } = txState
|
const { editorValue, estimatedFee, editorIsSaved } = txState
|
||||||
const [currTxType, setCurrTxType] = useState<string | undefined>(
|
const [currTxType, setCurrTxType] = useState<string | undefined>(
|
||||||
txState.selectedTransaction?.value
|
txState.selectedTransaction?.value
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setState({
|
|
||||||
editorValue: getJsonString?.()
|
|
||||||
})
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const parsed = parseJSON(editorValue)
|
const parsed = parseJSON(editorValue)
|
||||||
if (!parsed) return
|
if (!parsed) return
|
||||||
@@ -44,29 +44,19 @@ export const TxJson: FC<JsonProps> = ({ getJsonString, state: txState, header, s
|
|||||||
}
|
}
|
||||||
}, [editorValue])
|
}, [editorValue])
|
||||||
|
|
||||||
const saveState = (value: string, transactionType?: string) => {
|
|
||||||
const tx = prepareState(value, transactionType)
|
|
||||||
if (tx) {
|
|
||||||
setState(tx)
|
|
||||||
setState({
|
|
||||||
editorValue: getJsonString?.(tx)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const discardChanges = () => {
|
const discardChanges = () => {
|
||||||
showAlert('Confirm', {
|
showAlert('Confirm', {
|
||||||
body: 'Are you sure to discard these changes?',
|
body: 'Are you sure to discard these changes?',
|
||||||
confirmText: 'Yes',
|
confirmText: 'Yes',
|
||||||
onCancel: () => {},
|
onCancel: () => {},
|
||||||
onConfirm: () => setState({ editorValue: getJsonString?.() })
|
onConfirm: () => setState({ editorValue: getJsonString() })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const onExit = (value: string) => {
|
const onExit = (value: string) => {
|
||||||
const options = parseJSON(value)
|
const options = parseJSON(value)
|
||||||
if (options) {
|
if (options) {
|
||||||
saveState(value, currTxType)
|
saveEditorState(value, currTxType)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
showAlert('Error!', {
|
showAlert('Error!', {
|
||||||
@@ -163,8 +153,6 @@ export const TxJson: FC<JsonProps> = ({ getJsonString, state: txState, header, s
|
|||||||
})
|
})
|
||||||
}, [getSchemas, monacoInst])
|
}, [getSchemas, monacoInst])
|
||||||
|
|
||||||
const hasUnsaved = useMemo(() => editorValue !== getJsonString?.(), [editorValue, getJsonString])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Monaco
|
<Monaco
|
||||||
rootProps={{
|
rootProps={{
|
||||||
@@ -174,7 +162,7 @@ export const TxJson: FC<JsonProps> = ({ getJsonString, state: txState, header, s
|
|||||||
id={header}
|
id={header}
|
||||||
height="100%"
|
height="100%"
|
||||||
value={editorValue}
|
value={editorValue}
|
||||||
onChange={val => setState({ editorValue: val })}
|
onChange={val => setState({ editorValue: val, editorIsSaved: false })}
|
||||||
onMount={(editor, monaco) => {
|
onMount={(editor, monaco) => {
|
||||||
editor.updateOptions({
|
editor.updateOptions({
|
||||||
minimap: { enabled: false },
|
minimap: { enabled: false },
|
||||||
@@ -190,12 +178,12 @@ export const TxJson: FC<JsonProps> = ({ getJsonString, state: txState, header, s
|
|||||||
model?.onWillDispose(() => onExit(model.getValue()))
|
model?.onWillDispose(() => onExit(model.getValue()))
|
||||||
}}
|
}}
|
||||||
overlay={
|
overlay={
|
||||||
hasUnsaved ? (
|
!editorIsSaved ? (
|
||||||
<Flex row align="center" css={{ fontSize: '$xs', color: '$textMuted', ml: 'auto' }}>
|
<Flex row align="center" css={{ fontSize: '$xs', color: '$textMuted', ml: 'auto' }}>
|
||||||
<Text muted small>
|
<Text muted small>
|
||||||
This file has unsaved changes.
|
This file has unsaved changes.
|
||||||
</Text>
|
</Text>
|
||||||
<Link css={{ ml: '$1' }} onClick={() => saveState(editorValue || '', currTxType)}>
|
<Link css={{ ml: '$1' }} onClick={() => saveEditorState(editorValue, currTxType)}>
|
||||||
save
|
save
|
||||||
</Link>
|
</Link>
|
||||||
<Link css={{ ml: '$1' }} onClick={discardChanges}>
|
<Link css={{ ml: '$1' }} onClick={discardChanges}>
|
||||||
|
|||||||
@@ -1,62 +1,59 @@
|
|||||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react'
|
import { FC, ReactNode, useCallback, useEffect, 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 from '../Select'
|
import Select, { CreatableSelect } 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 { Button } from '..'
|
import { Box, Button } from '..'
|
||||||
import Textarea from '../Textarea'
|
import Textarea from '../Textarea'
|
||||||
|
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
|
||||||
state: TransactionState
|
state: TransactionState
|
||||||
estimateFee?: (...arg: any) => Promise<string | undefined>
|
estimateFee?: (...arg: any) => Promise<string | undefined>
|
||||||
|
switchToJson: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) => {
|
export const TxUI: FC<UIProps> = ({
|
||||||
|
state: txState,
|
||||||
|
setState,
|
||||||
|
resetState,
|
||||||
|
estimateFee,
|
||||||
|
switchToJson
|
||||||
|
}) => {
|
||||||
const { accounts } = useSnapshot(state)
|
const { accounts } = useSnapshot(state)
|
||||||
const { selectedAccount, selectedDestAccount, selectedTransaction, txFields } = txState
|
const { selectedAccount, selectedTransaction, txFields, selectedFlags, hookParameters, memos } =
|
||||||
|
txState
|
||||||
|
|
||||||
const accountOptions: SelectOption[] = accounts.map(acc => ({
|
const accountOptions: SelectOption[] = accounts.map(acc => ({
|
||||||
label: acc.name,
|
label: acc.name,
|
||||||
value: acc.address
|
value: acc.address
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const destAccountOptions: SelectOption[] = accounts
|
const flagsOptions: SelectOption[] = Object.entries(
|
||||||
.map(acc => ({
|
getFlags(selectedTransaction?.value) || {}
|
||||||
label: acc.name,
|
).map(([label, value]) => ({
|
||||||
value: acc.address
|
label,
|
||||||
}))
|
value
|
||||||
.filter(acc => acc.value !== selectedAccount?.value)
|
}))
|
||||||
|
|
||||||
const [feeLoading, setFeeLoading] = useState(false)
|
const [feeLoading, setFeeLoading] = useState(false)
|
||||||
|
|
||||||
const resetFields = useCallback(
|
|
||||||
(tt: string) => {
|
|
||||||
const fields = getTxFields(tt)
|
|
||||||
|
|
||||||
if (fields.Destination !== undefined) {
|
|
||||||
setState({ selectedDestAccount: null })
|
|
||||||
fields.Destination = ''
|
|
||||||
} else {
|
|
||||||
fields.Destination = undefined
|
|
||||||
}
|
|
||||||
return setState({ txFields: fields })
|
|
||||||
},
|
|
||||||
[setState]
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleSetAccount = (acc: SelectOption) => {
|
const handleSetAccount = (acc: SelectOption) => {
|
||||||
setState({ selectedAccount: acc })
|
setState({ selectedAccount: acc })
|
||||||
streamState.selectedAccount = acc
|
streamState.selectedAccount = acc
|
||||||
@@ -76,6 +73,22 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) =>
|
|||||||
[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)
|
||||||
@@ -92,15 +105,13 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) =>
|
|||||||
(tt: SelectOption) => {
|
(tt: SelectOption) => {
|
||||||
setState({ selectedTransaction: tt })
|
setState({ selectedTransaction: tt })
|
||||||
|
|
||||||
const newState = resetFields(tt.value)
|
const newState = resetState(tt)
|
||||||
|
|
||||||
handleEstimateFee(newState, true)
|
handleEstimateFee(newState, true)
|
||||||
},
|
},
|
||||||
[handleEstimateFee, resetFields, setState]
|
[handleEstimateFee, resetState, setState]
|
||||||
)
|
)
|
||||||
|
|
||||||
const switchToJson = () => setState({ viewType: 'json' })
|
|
||||||
|
|
||||||
// default tx
|
// default tx
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedTransaction?.value) return
|
if (selectedTransaction?.value) return
|
||||||
@@ -110,20 +121,23 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) =>
|
|||||||
}
|
}
|
||||||
}, [handleChangeTxType, selectedTransaction?.value])
|
}, [handleChangeTxType, selectedTransaction?.value])
|
||||||
|
|
||||||
const fields = useMemo(
|
const richFields = ['TransactionType', 'Account', 'HookParameters', 'Memos']
|
||||||
() => getTxFields(selectedTransaction?.value),
|
|
||||||
[selectedTransaction?.value]
|
|
||||||
)
|
|
||||||
|
|
||||||
const specialFields = ['TransactionType', 'Account']
|
if (flagsOptions.length) {
|
||||||
if (fields.Destination !== undefined) {
|
richFields.push('Flags')
|
||||||
specialFields.push('Destination')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const otherFields = Object.keys(txFields).filter(k => !specialFields.includes(k)) as [
|
const otherFields = Object.keys(txFields).filter(k => !richFields.includes(k)) as [keyof TxFields]
|
||||||
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={{
|
||||||
@@ -133,184 +147,437 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) =>
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex column fluid css={{ height: '100%', overflowY: 'auto', pr: '$1' }}>
|
<Flex column fluid css={{ height: '100%', overflowY: 'auto', pr: '$1' }}>
|
||||||
<Flex
|
<TxField label="Transaction type">
|
||||||
row
|
|
||||||
fluid
|
|
||||||
css={{
|
|
||||||
justifyContent: 'flex-end',
|
|
||||||
alignItems: 'center',
|
|
||||||
mb: '$3',
|
|
||||||
mt: '1px',
|
|
||||||
pr: '1px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text muted css={{ mr: '$3' }}>
|
|
||||||
Transaction type:{' '}
|
|
||||||
</Text>
|
|
||||||
<Select
|
<Select
|
||||||
instanceId="transactionsType"
|
instanceId="transactionsType"
|
||||||
placeholder="Select transaction type"
|
placeholder="Select transaction type"
|
||||||
options={transactionsOptions}
|
options={transactionsOptions}
|
||||||
hideSelectedOptions
|
hideSelectedOptions
|
||||||
css={{ width: '70%' }}
|
|
||||||
value={selectedTransaction}
|
value={selectedTransaction}
|
||||||
onChange={(tt: any) => handleChangeTxType(tt)}
|
onChange={(tt: any) => handleChangeTxType(tt)}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</TxField>
|
||||||
<Flex
|
<TxField label="Account">
|
||||||
row
|
|
||||||
fluid
|
|
||||||
css={{
|
|
||||||
justifyContent: 'flex-end',
|
|
||||||
alignItems: 'center',
|
|
||||||
mb: '$3',
|
|
||||||
pr: '1px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text muted css={{ mr: '$3' }}>
|
|
||||||
Account:{' '}
|
|
||||||
</Text>
|
|
||||||
<Select
|
<Select
|
||||||
instanceId="from-account"
|
instanceId="from-account"
|
||||||
placeholder="Select your account"
|
placeholder="Select your account"
|
||||||
css={{ width: '70%' }}
|
|
||||||
options={accountOptions}
|
options={accountOptions}
|
||||||
value={selectedAccount}
|
value={selectedAccount}
|
||||||
onChange={(acc: any) => handleSetAccount(acc)} // TODO make react-select have correct types for acc
|
onChange={(acc: any) => handleSetAccount(acc)} // TODO make react-select have correct types for acc
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</TxField>
|
||||||
{fields.Destination !== undefined && (
|
<TxField label="Sequence">
|
||||||
<Flex
|
<AccountSequence address={selectedAccount?.value} />
|
||||||
row
|
</TxField>
|
||||||
fluid
|
{richFields.includes('Flags') && (
|
||||||
css={{
|
<TxField label="Flags">
|
||||||
justifyContent: 'flex-end',
|
|
||||||
alignItems: 'center',
|
|
||||||
mb: '$3',
|
|
||||||
pr: '1px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text muted css={{ mr: '$3' }}>
|
|
||||||
Destination account:{' '}
|
|
||||||
</Text>
|
|
||||||
<Select
|
<Select
|
||||||
instanceId="to-account"
|
|
||||||
placeholder="Select the destination account"
|
|
||||||
css={{ width: '70%' }}
|
|
||||||
options={destAccountOptions}
|
|
||||||
value={selectedDestAccount}
|
|
||||||
isClearable
|
isClearable
|
||||||
onChange={(acc: any) => setState({ selectedDestAccount: acc })}
|
instanceId="flags"
|
||||||
|
placeholder="Select flags to apply"
|
||||||
|
menuPosition="fixed"
|
||||||
|
value={selectedFlags}
|
||||||
|
isMulti
|
||||||
|
options={flagsOptions}
|
||||||
|
onChange={flags => setState({ selectedFlags: flags as any })}
|
||||||
|
closeMenuOnSelect={
|
||||||
|
selectedFlags ? selectedFlags.length >= flagsOptions.length - 1 : false
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</TxField>
|
||||||
)}
|
)}
|
||||||
{otherFields.map(field => {
|
{otherFields.map(field => {
|
||||||
let _value = txFields[field]
|
let _value = txFields[field]
|
||||||
|
|
||||||
let value: string | undefined
|
let value: string | undefined
|
||||||
if (typeof _value === 'object') {
|
if (typeIs(_value, 'object')) {
|
||||||
if (_value.$type === 'json' && typeof _value.$value === 'object') {
|
if (_value.$type === 'json' && typeIs(_value.$value, ['object', 'array'])) {
|
||||||
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 isXrp = typeof _value === 'object' && _value.$type === 'xrp'
|
const isAccount = typeIs(_value, 'object') && _value.$type === 'account'
|
||||||
|
const isXrpAmount = typeIs(_value, 'object') && _value.$type === 'amount.xrp'
|
||||||
|
const isTokenAmount = typeIs(_value, 'object') && _value.$type === 'amount.token'
|
||||||
const isJson = typeof _value === 'object' && _value.$type === 'json'
|
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
|
||||||
return (
|
let tokenAmount = defaultTokenAmount
|
||||||
<Flex column key={field} css={{ mb: '$2', pr: '1px' }}>
|
if (isTokenAmount && typeIs(_value, 'object') && typeIs(_value.$value, 'object')) {
|
||||||
<Flex
|
tokenAmount = {
|
||||||
row
|
value: _value.$value.value,
|
||||||
fluid
|
currency: _value.$value.currency,
|
||||||
css={{
|
issuer: _value.$value.issuer
|
||||||
justifyContent: 'flex-end',
|
}
|
||||||
alignItems: 'center',
|
}
|
||||||
position: 'relative'
|
|
||||||
}}
|
if (isXrpAmount || isTokenAmount) {
|
||||||
>
|
return (
|
||||||
<Text muted css={{ mr: '$3' }}>
|
<TxField key={field} label={field}>
|
||||||
{field + (isXrp ? ' (XRP)' : '')}:{' '}
|
<Flex fluid css={{ alignItems: 'center' }}>
|
||||||
</Text>
|
{isTokenAmount ? (
|
||||||
{isJson ? (
|
<Flex
|
||||||
<Textarea
|
fluid
|
||||||
rows={rows}
|
row
|
||||||
value={value}
|
align="center"
|
||||||
spellCheck={false}
|
justify="space-between"
|
||||||
onChange={switchToJson}
|
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={{
|
css={{
|
||||||
width: '70%',
|
ml: '$2',
|
||||||
flex: 'inherit',
|
width: '150px'
|
||||||
resize: 'vertical'
|
|
||||||
}}
|
}}
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Input
|
|
||||||
type={isFee ? 'number' : 'text'}
|
|
||||||
value={value}
|
|
||||||
onChange={e => {
|
|
||||||
if (isFee) {
|
|
||||||
const val = e.target.value.replaceAll('.', '').replaceAll(',', '')
|
|
||||||
handleSetField(field, val)
|
|
||||||
} else {
|
|
||||||
handleSetField(field, e.target.value)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onKeyPress={
|
|
||||||
isFee
|
|
||||||
? e => {
|
|
||||||
if (e.key === '.' || e.key === ',') {
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
css={{
|
|
||||||
width: '70%',
|
|
||||||
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
|
<Select
|
||||||
</Button>
|
instanceId="currency-type"
|
||||||
)}
|
options={amountOptions}
|
||||||
</Flex>
|
value={isXrpAmount ? amountOptions['0'] : amountOptions['1']}
|
||||||
</Flex>
|
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 (
|
||||||
|
<TxField key={field} label={field}>
|
||||||
|
{isJson ? (
|
||||||
|
<Textarea
|
||||||
|
rows={rows}
|
||||||
|
value={value}
|
||||||
|
spellCheck={false}
|
||||||
|
onChange={switchToJson}
|
||||||
|
css={{
|
||||||
|
flex: 'inherit',
|
||||||
|
resize: 'vertical'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Input
|
||||||
|
type={isFee ? 'number' : 'text'}
|
||||||
|
value={value}
|
||||||
|
onChange={e => {
|
||||||
|
if (isFee) {
|
||||||
|
const val = e.target.value.replaceAll('.', '').replaceAll(',', '')
|
||||||
|
handleSetField(field, val)
|
||||||
|
} else {
|
||||||
|
handleSetField(field, e.target.value)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onKeyPress={
|
||||||
|
isFee
|
||||||
|
? e => {
|
||||||
|
if (e.key === '.' || e.key === ',') {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
css={{
|
||||||
|
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
|
||||||
|
css={{ mx: '$2' }}
|
||||||
|
placeholder="Value (hex-quoted)"
|
||||||
|
value={value}
|
||||||
|
onChange={e => {
|
||||||
|
setState({
|
||||||
|
hookParameters: {
|
||||||
|
...hookParameters,
|
||||||
|
[id]: { label, value: e.target.value }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
const { [id]: _, ...rest } = hookParameters
|
||||||
|
setState({ hookParameters: rest })
|
||||||
|
}}
|
||||||
|
variant="destroy"
|
||||||
|
>
|
||||||
|
<Trash weight="regular" size="16px" />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
))}
|
||||||
|
<Button
|
||||||
|
outline
|
||||||
|
fullWidth
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
const id = Object.keys(hookParameters).length
|
||||||
|
setState({
|
||||||
|
hookParameters: { ...hookParameters, [id]: { label: '', value: '' } }
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Plus size="16px" />
|
||||||
|
Add Hook Parameter
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</TxField>
|
||||||
|
<TxField multiLine label="Memos">
|
||||||
|
<Flex column fluid>
|
||||||
|
{Object.entries(memos).map(([id, memo]) => (
|
||||||
|
<Flex column key={id} css={{ mb: '$2' }}>
|
||||||
|
<Flex
|
||||||
|
row
|
||||||
|
css={{
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
width: '100%'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
placeholder="Memo type"
|
||||||
|
value={memo.type}
|
||||||
|
onChange={e => {
|
||||||
|
setState({
|
||||||
|
memos: {
|
||||||
|
...memos,
|
||||||
|
[id]: { ...memo, type: e.target.value }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
placeholder="Data (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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"uri": "file:///amount-schema.json",
|
"uri": "file:///amount-schema.json",
|
||||||
"title": "Amount",
|
"title": "Amount",
|
||||||
"description": "Specify xrp in drops and tokens as objects.",
|
"description": "Specify XAH 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 XRP."
|
"description": "Arbitrary currency code for the token. Cannot be XAH."
|
||||||
},
|
},
|
||||||
"value": {
|
"value": {
|
||||||
"type": ["string", "number"],
|
"type": ["string", "number"],
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
],
|
],
|
||||||
"defaultSnippets": [
|
"defaultSnippets": [
|
||||||
{
|
{
|
||||||
"label": "Xrp",
|
"label": "XAH",
|
||||||
"body": "1000000"
|
"body": "1000000"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
409
content/hook-set-codes.json
Normal file
409
content/hook-set-codes.json
Normal file
@@ -0,0 +1,409 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"code": 1,
|
||||||
|
"identifier": "AMENDMENT_DISABLED",
|
||||||
|
"description": "attempt to HookSet when amendment is not yet enabled."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": 2,
|
||||||
|
"identifier": "API_ILLEGAL",
|
||||||
|
"description": "HookSet object contained HookApiVersion for existing HookDefinition"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": 3,
|
||||||
|
"identifier": "API_INVALID",
|
||||||
|
"description": "HookSet object contained HookApiVersion for unrecognised hook API "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": 4,
|
||||||
|
"identifier": "API_MISSING",
|
||||||
|
"description": "HookSet object lacked HookApiVersion"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": 5,
|
||||||
|
"identifier": "BLOCK_ILLEGAL",
|
||||||
|
"description": " a block end instruction moves execution below depth 0 {{}}`}` <= like this"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": 6,
|
||||||
|
"identifier": "CALL_ILLEGAL",
|
||||||
|
"description": "wasm tries to call a non-whitelisted function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": 7,
|
||||||
|
"identifier": "CALL_INDIRECT",
|
||||||
|
"description": "wasm used call indirect instruction which is disallowed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": 8,
|
||||||
|
"identifier": "CREATE_FLAG",
|
||||||
|
"description": "create operation requires hsoOVERRIDE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": 9,
|
||||||
|
"identifier": "DELETE_FIELD",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": 10,
|
||||||
|
"identifier": "DELETE_FLAG",
|
||||||
|
"description": "delete operation requires hsoOVERRIDE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": 11,
|
||||||
|
"identifier": "DELETE_NOTHING",
|
||||||
|
"description": "delete operation would delete nothing"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": 12,
|
||||||
|
"identifier": "EXPORTS_MISSING",
|
||||||
|
"description": "hook did not export *any* functions (should be cbak, hook)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": 13,
|
||||||
|
"identifier": "EXPORT_CBAK_FUNC",
|
||||||
|
"description": "hook did not export correct func def int64_t cbak(uint32_t)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": 14,
|
||||||
|
"identifier": "EXPORT_HOOK_FUNC",
|
||||||
|
"description": "hook did not export correct func def int64_t hook(uint32_t)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": 15,
|
||||||
|
"identifier": "EXPORT_MISSING",
|
||||||
|
"description": "distinct from export*S*_missing, either hook or cbak is missing"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "FLAGS_INVALID",
|
||||||
|
"code": 16,
|
||||||
|
"description": "HookSet flags were invalid for specified operation "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "FUNCS_MISSING",
|
||||||
|
"code": 17,
|
||||||
|
"description": "hook did not include function code for any functions "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "FUNC_PARAM_INVALID",
|
||||||
|
"code": 18,
|
||||||
|
"description": "parameter types may only be i32 i64 u32 u64 "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "FUNC_RETURN_COUNT",
|
||||||
|
"code": 19,
|
||||||
|
"description": "a function type is defined in the wasm which returns > 1 return value "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "FUNC_RETURN_INVALID",
|
||||||
|
"code": 20,
|
||||||
|
"description": "a function type does not return i32 i64 u32 or u64 "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "FUNC_TYPELESS",
|
||||||
|
"code": 21,
|
||||||
|
"description": "hook defined hook/cbak but their type is not defined in wasm "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "FUNC_TYPE_INVALID",
|
||||||
|
"code": 22,
|
||||||
|
"description": "malformed and illegal wasm in the func type section "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "GRANTS_EMPTY",
|
||||||
|
"code": 23,
|
||||||
|
"description": "HookSet object contained an empty grants array (you should remove it) "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "GRANTS_EXCESS",
|
||||||
|
"code": 24,
|
||||||
|
"description": "HookSet object cotnained a grants array with too many grants "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "GRANTS_FIELD",
|
||||||
|
"code": 25,
|
||||||
|
"description": "HookSet object contained a grant without Authorize or HookHash "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "GRANTS_ILLEGAL",
|
||||||
|
"code": 26,
|
||||||
|
"description": "Hookset object contained grants array which contained a non Grant object "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "GUARD_IMPORT",
|
||||||
|
"code": 27,
|
||||||
|
"description": "guard import is missing "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "GUARD_MISSING",
|
||||||
|
"code": 28,
|
||||||
|
"description": "guard call missing at top of loop "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "GUARD_PARAMETERS",
|
||||||
|
"code": 29,
|
||||||
|
"description": "guard called but did not use constant expressions for params "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "HASH_OR_CODE",
|
||||||
|
"code": 30,
|
||||||
|
"description": "HookSet object can contain only one of CreateCode and HookHash "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "HOOKON_MISSING",
|
||||||
|
"code": 31,
|
||||||
|
"description": "HookSet object did not contain HookOn but should have "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "HOOKS_ARRAY_BAD",
|
||||||
|
"code": 32,
|
||||||
|
"description": "attempt to HookSet with a Hooks array containing a non-Hook obj "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "HOOKS_ARRAY_BLANK",
|
||||||
|
"code": 33,
|
||||||
|
"description": "all hook set objs were blank "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "HOOKS_ARRAY_EMPTY",
|
||||||
|
"code": 34,
|
||||||
|
"description": "attempt to HookSet with an empty Hooks array "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "HOOKS_ARRAY_MISSING",
|
||||||
|
"code": 35,
|
||||||
|
"description": "attempt to HookSet without a Hooks array "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "HOOKS_ARRAY_TOO_BIG",
|
||||||
|
"code": 36,
|
||||||
|
"description": "attempt to HookSet with a Hooks array beyond the chain size limit "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "HOOK_ADD",
|
||||||
|
"code": 37,
|
||||||
|
"description": "Informational: adding ltHook to directory "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "HOOK_DEF_MISSING",
|
||||||
|
"code": 38,
|
||||||
|
"description": "attempt to reference a hook definition (by hash) that is not on ledger "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "HOOK_DELETE",
|
||||||
|
"code": 39,
|
||||||
|
"description": "unable to delete ltHook from owner "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "HOOK_INVALID_FIELD",
|
||||||
|
"code": 40,
|
||||||
|
"description": "HookSetObj contained an illegal/unexpected field "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "HOOK_PARAMS_COUNT",
|
||||||
|
"code": 41,
|
||||||
|
"description": "hookset obj would create too many hook parameters "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "HOOK_PARAM_SIZE",
|
||||||
|
"code": 42,
|
||||||
|
"description": "hookset obj sets a parameter or value that exceeds max allowable size "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "IMPORTS_MISSING",
|
||||||
|
"code": 43,
|
||||||
|
"description": "hook must import guard, and accept/rollback "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "IMPORT_ILLEGAL",
|
||||||
|
"code": 44,
|
||||||
|
"description": "attempted import of a non-whitelisted function "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "IMPORT_MODULE_BAD",
|
||||||
|
"code": 45,
|
||||||
|
"description": "hook attempted to specify no or a bad import module "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "IMPORT_MODULE_ENV",
|
||||||
|
"code": 46,
|
||||||
|
"description": "hook attempted to specify import module not named env "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "IMPORT_NAME_BAD",
|
||||||
|
"code": 47,
|
||||||
|
"description": "import name was too short or too long "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "INSTALL_FLAG",
|
||||||
|
"code": 48,
|
||||||
|
"description": "install operation requires hsoOVERRIDE "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "INSTALL_MISSING",
|
||||||
|
"code": 49,
|
||||||
|
"description": "install operation specifies hookhash which doesn't exist on the ledger "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "INSTRUCTION_COUNT",
|
||||||
|
"code": 50,
|
||||||
|
"description": "worst case execution instruction count as computed by HookSet "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "INSTRUCTION_EXCESS",
|
||||||
|
"code": 51,
|
||||||
|
"description": "worst case execution instruction count was too large "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "MEMORY_GROW",
|
||||||
|
"code": 52,
|
||||||
|
"description": "memory.grow instruction is present but disallowed "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "NAMESPACE_MISSING",
|
||||||
|
"code": 53,
|
||||||
|
"description": "HookSet object lacked HookNamespace "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "NSDELETE",
|
||||||
|
"code": 54,
|
||||||
|
"description": "Informational: a namespace is being deleted "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "NSDELETE_ACCOUNT",
|
||||||
|
"code": 55,
|
||||||
|
"description": "nsdelete tried to delete ns from a non-existing account "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "NSDELETE_COUNT",
|
||||||
|
"code": 56,
|
||||||
|
"description": "namespace state count less than 0 / overflow "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "NSDELETE_DIR",
|
||||||
|
"code": 57,
|
||||||
|
"description": "could not delete directory node in ledger "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "NSDELETE_DIRECTORY",
|
||||||
|
"code": 58,
|
||||||
|
"description": "nsdelete operation failed to delete ns directory "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "NSDELETE_DIR_ENTRY",
|
||||||
|
"code": 59,
|
||||||
|
"description": "nsdelete operation failed due to bad entry in ns directory "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "NSDELETE_ENTRY",
|
||||||
|
"code": 60,
|
||||||
|
"description": "nsdelete operation failed due to missing hook state entry "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "NSDELETE_FIELD",
|
||||||
|
"code": 61
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "NSDELETE_FLAGS",
|
||||||
|
"code": 62
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "NSDELETE_NONSTATE",
|
||||||
|
"code": 63,
|
||||||
|
"description": "nsdelete operation failed due to the presence of a non-hookstate obj "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "NSDELETE_NOTHING",
|
||||||
|
"code": 64,
|
||||||
|
"description": "hsfNSDELETE provided but nothing to delete "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "OPERATION_INVALID",
|
||||||
|
"code": 65,
|
||||||
|
"description": "could not deduce an operation from the provided hookset obj "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "OVERRIDE_MISSING",
|
||||||
|
"code": 66,
|
||||||
|
"description": "HookSet object was trying to update or delete a hook but lacked hsfOVERRIDE "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "PARAMETERS_FIELD",
|
||||||
|
"code": 67,
|
||||||
|
"description": "HookParameters contained a HookParameter with an invalid key in it "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "PARAMETERS_ILLEGAL",
|
||||||
|
"code": 68,
|
||||||
|
"description": "HookParameters contained something other than a HookParameter "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "PARAMETERS_NAME",
|
||||||
|
"code": 69,
|
||||||
|
"description": "HookParameters contained a HookParameter which lacked ParameterName field "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "PARAM_HOOK_CBAK",
|
||||||
|
"code": 70,
|
||||||
|
"description": "hook and cbak must take exactly one u32 parameter "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "RETURN_HOOK_CBAK",
|
||||||
|
"code": 71,
|
||||||
|
"description": "hook and cbak must retunr i64 "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "SHORT_HOOK",
|
||||||
|
"code": 72,
|
||||||
|
"description": "web assembly byte code ended abruptly "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "TYPE_INVALID",
|
||||||
|
"code": 73,
|
||||||
|
"description": "malformed and illegal wasm specifying an illegal local var type "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "WASM_BAD_MAGIC",
|
||||||
|
"code": 74,
|
||||||
|
"description": "wasm magic number missing or not wasm "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "WASM_INVALID",
|
||||||
|
"code": 75,
|
||||||
|
"description": "set hook operation would set invalid wasm "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "WASM_PARSE_LOOP",
|
||||||
|
"code": 76,
|
||||||
|
"description": "wasm section parsing resulted in an infinite loop "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "WASM_SMOKE_TEST",
|
||||||
|
"code": 77,
|
||||||
|
"description": "Informational: first attempt to load wasm into wasm runtime "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "WASM_TEST_FAILURE",
|
||||||
|
"code": 78,
|
||||||
|
"description": "the smoke test failed "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "WASM_TOO_BIG",
|
||||||
|
"code": 79,
|
||||||
|
"description": "set hook would exceed maximum hook size "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "WASM_TOO_SMALL",
|
||||||
|
"code": 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "WASM_VALIDATION",
|
||||||
|
"code": 81,
|
||||||
|
"description": "a generic error while parsing wasm, usually leb128 overflow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "HOOK_CBAK_DIFF_TYPES",
|
||||||
|
"code": 82,
|
||||||
|
"description": "hook and cbak function definitions were different"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -2,11 +2,14 @@
|
|||||||
{
|
{
|
||||||
"TransactionType": "AccountDelete",
|
"TransactionType": "AccountDelete",
|
||||||
"Account": "rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm",
|
"Account": "rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm",
|
||||||
"Destination": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
|
"Destination": {
|
||||||
|
"$type": "account",
|
||||||
|
"$value": ""
|
||||||
|
},
|
||||||
"DestinationTag": 13,
|
"DestinationTag": 13,
|
||||||
"Fee": "2000000",
|
"Fee": "2000000",
|
||||||
"Sequence": 2470665,
|
"Sequence": 2470665,
|
||||||
"Flags": 2147483648
|
"Flags": "2147483648"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"TransactionType": "AccountSet",
|
"TransactionType": "AccountSet",
|
||||||
@@ -28,7 +31,11 @@
|
|||||||
"TransactionType": "CheckCash",
|
"TransactionType": "CheckCash",
|
||||||
"Amount": {
|
"Amount": {
|
||||||
"$value": "100",
|
"$value": "100",
|
||||||
"$type": "xrp"
|
"$type": "amount.xrp"
|
||||||
|
},
|
||||||
|
"DeliverMin": {
|
||||||
|
"$value": "",
|
||||||
|
"$type": "amount.xrp"
|
||||||
},
|
},
|
||||||
"CheckID": "838766BA2B995C00744175F69A1B11E32C3DBC40E64801A4056FCBD657F57334",
|
"CheckID": "838766BA2B995C00744175F69A1B11E32C3DBC40E64801A4056FCBD657F57334",
|
||||||
"Fee": "12"
|
"Fee": "12"
|
||||||
@@ -36,7 +43,10 @@
|
|||||||
{
|
{
|
||||||
"TransactionType": "CheckCreate",
|
"TransactionType": "CheckCreate",
|
||||||
"Account": "rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo",
|
"Account": "rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo",
|
||||||
"Destination": "rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy",
|
"Destination": {
|
||||||
|
"$type": "account",
|
||||||
|
"$value": ""
|
||||||
|
},
|
||||||
"SendMax": "100000000",
|
"SendMax": "100000000",
|
||||||
"Expiration": 570113521,
|
"Expiration": 570113521,
|
||||||
"InvoiceID": "6F1DFD1D0FE8A32E40E1F2C05CF1C15545BAB56B617F9C6C2D63A6B704BEF59B",
|
"InvoiceID": "6F1DFD1D0FE8A32E40E1F2C05CF1C15545BAB56B617F9C6C2D63A6B704BEF59B",
|
||||||
@@ -48,13 +58,16 @@
|
|||||||
"Account": "rsUiUMpnrgxQp24dJYZDhmV4bE3aBtQyt8",
|
"Account": "rsUiUMpnrgxQp24dJYZDhmV4bE3aBtQyt8",
|
||||||
"Authorize": "rEhxGqkqPPSxQ3P25J66ft5TwpzV14k2de",
|
"Authorize": "rEhxGqkqPPSxQ3P25J66ft5TwpzV14k2de",
|
||||||
"Fee": "10",
|
"Fee": "10",
|
||||||
"Flags": 2147483648,
|
"Flags": "2147483648",
|
||||||
"Sequence": 2
|
"Sequence": 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"TransactionType": "EscrowCancel",
|
"TransactionType": "EscrowCancel",
|
||||||
"Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"Owner": {
|
||||||
|
"$type": "account",
|
||||||
|
"$value": ""
|
||||||
|
},
|
||||||
"OfferSequence": 7,
|
"OfferSequence": 7,
|
||||||
"Fee": "10"
|
"Fee": "10"
|
||||||
},
|
},
|
||||||
@@ -63,9 +76,12 @@
|
|||||||
"TransactionType": "EscrowCreate",
|
"TransactionType": "EscrowCreate",
|
||||||
"Amount": {
|
"Amount": {
|
||||||
"$value": "100",
|
"$value": "100",
|
||||||
"$type": "xrp"
|
"$type": "amount.xrp"
|
||||||
|
},
|
||||||
|
"Destination": {
|
||||||
|
"$type": "account",
|
||||||
|
"$value": ""
|
||||||
},
|
},
|
||||||
"Destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
|
|
||||||
"CancelAfter": 533257958,
|
"CancelAfter": 533257958,
|
||||||
"FinishAfter": 533171558,
|
"FinishAfter": 533171558,
|
||||||
"Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
|
"Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
|
||||||
@@ -76,60 +92,20 @@
|
|||||||
{
|
{
|
||||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"TransactionType": "EscrowFinish",
|
"TransactionType": "EscrowFinish",
|
||||||
"Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"Owner": {
|
||||||
|
"$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",
|
||||||
"Fee": "12",
|
"Fee": "12",
|
||||||
"Flags": 0,
|
"Flags": "0",
|
||||||
"LastLedgerSequence": 7108629,
|
|
||||||
"OfferSequence": 6,
|
"OfferSequence": 6,
|
||||||
"Sequence": 7
|
"Sequence": 7
|
||||||
},
|
},
|
||||||
@@ -137,25 +113,34 @@
|
|||||||
"TransactionType": "OfferCreate",
|
"TransactionType": "OfferCreate",
|
||||||
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||||
"Fee": "12",
|
"Fee": "12",
|
||||||
"Flags": 0,
|
"Flags": "0",
|
||||||
"LastLedgerSequence": 7108682,
|
|
||||||
"Sequence": 8,
|
"Sequence": 8,
|
||||||
"TakerGets": "6000000",
|
"TakerGets": {
|
||||||
"Amount": {
|
"$type": "amount.xrp",
|
||||||
"$value": "100",
|
"$value": "6000000"
|
||||||
"$type": "xrp"
|
},
|
||||||
|
"TakerPays": {
|
||||||
|
"$type": "amount.token",
|
||||||
|
"$value": {
|
||||||
|
"currency": "USD",
|
||||||
|
"issuer": "rhQEswwTsjMXk75QL9Dd9RWZAokNHTzJpr",
|
||||||
|
"value": "2"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"TransactionType": "Payment",
|
"TransactionType": "Payment",
|
||||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"Destination": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
"Destination": {
|
||||||
|
"$type": "account",
|
||||||
|
"$value": ""
|
||||||
|
},
|
||||||
"Amount": {
|
"Amount": {
|
||||||
"$value": "100",
|
"$value": "100",
|
||||||
"$type": "xrp"
|
"$type": "amount.xrp"
|
||||||
},
|
},
|
||||||
"Fee": "12",
|
"Fee": "12",
|
||||||
"Flags": 2147483648,
|
"Flags": "2147483648",
|
||||||
"Sequence": 2
|
"Sequence": 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -163,9 +148,12 @@
|
|||||||
"TransactionType": "PaymentChannelCreate",
|
"TransactionType": "PaymentChannelCreate",
|
||||||
"Amount": {
|
"Amount": {
|
||||||
"$value": "100",
|
"$value": "100",
|
||||||
"$type": "xrp"
|
"$type": "amount.xrp"
|
||||||
|
},
|
||||||
|
"Destination": {
|
||||||
|
"$type": "account",
|
||||||
|
"$value": ""
|
||||||
},
|
},
|
||||||
"Destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
|
|
||||||
"SettleDelay": 86400,
|
"SettleDelay": 86400,
|
||||||
"PublicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A",
|
"PublicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A",
|
||||||
"CancelAfter": 533171558,
|
"CancelAfter": 533171558,
|
||||||
@@ -179,20 +167,20 @@
|
|||||||
"Channel": "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198",
|
"Channel": "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198",
|
||||||
"Amount": {
|
"Amount": {
|
||||||
"$value": "200",
|
"$value": "200",
|
||||||
"$type": "xrp"
|
"$type": "amount.xrp"
|
||||||
},
|
},
|
||||||
"Expiration": 543171558,
|
"Expiration": 543171558,
|
||||||
"Fee": "10"
|
"Fee": "10"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Flags": 0,
|
"Flags": "0",
|
||||||
"TransactionType": "SetRegularKey",
|
"TransactionType": "SetRegularKey",
|
||||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"Fee": "12",
|
"Fee": "12",
|
||||||
"RegularKey": "rAR8rR8sUkBoCZFawhkWzY4Y5YoyuznwD"
|
"RegularKey": "rAR8rR8sUkBoCZFawhkWzY4Y5YoyuznwD"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Flags": 0,
|
"Flags": "0",
|
||||||
"TransactionType": "SignerListSet",
|
"TransactionType": "SignerListSet",
|
||||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"Fee": "12",
|
"Fee": "12",
|
||||||
@@ -232,16 +220,67 @@
|
|||||||
"TransactionType": "TrustSet",
|
"TransactionType": "TrustSet",
|
||||||
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||||
"Fee": "12",
|
"Fee": "12",
|
||||||
"Flags": 262144,
|
"Flags": "262144",
|
||||||
"LastLedgerSequence": 8007750,
|
|
||||||
"LimitAmount": {
|
"LimitAmount": {
|
||||||
"$type": "json",
|
"$type": "amount.token",
|
||||||
"$value": {
|
"$value": {
|
||||||
"currency": "USD",
|
"currency": "USD",
|
||||||
"issuer": "rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc",
|
"issuer": "rhQEswwTsjMXk75QL9Dd9RWZAokNHTzJpr",
|
||||||
"value": "100"
|
"value": "100"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -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$/,
|
test: [/\.md$/, /hook-bundle\.js$/],
|
||||||
use: 'raw-loader'
|
use: 'raw-loader'
|
||||||
})
|
})
|
||||||
return config
|
return config
|
||||||
|
|||||||
19
package.json
19
package.json
@@ -8,7 +8,8 @@
|
|||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"postinstall": "patch-package"
|
"postinstall": "patch-package && yarn run postinstall-postinstall",
|
||||||
|
"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",
|
||||||
@@ -37,7 +38,7 @@
|
|||||||
"lodash.xor": "^4.5.0",
|
"lodash.xor": "^4.5.0",
|
||||||
"monaco-editor": "^0.33.0",
|
"monaco-editor": "^0.33.0",
|
||||||
"next": "^12.0.4",
|
"next": "^12.0.4",
|
||||||
"next-auth": "^4.10.3",
|
"next-auth": "^4.24.11",
|
||||||
"next-plausible": "^3.2.0",
|
"next-plausible": "^3.2.0",
|
||||||
"next-themes": "^0.1.1",
|
"next-themes": "^0.1.1",
|
||||||
"normalize-url": "^7.0.2",
|
"normalize-url": "^7.0.2",
|
||||||
@@ -64,9 +65,9 @@
|
|||||||
"valtio": "^1.2.5",
|
"valtio": "^1.2.5",
|
||||||
"vscode-languageserver": "^7.0.0",
|
"vscode-languageserver": "^7.0.0",
|
||||||
"vscode-uri": "^3.0.2",
|
"vscode-uri": "^3.0.2",
|
||||||
"wabt": "1.0.16",
|
"wabt": "^1.0.30",
|
||||||
"xrpl-accountlib": "^1.5.2",
|
"xrpl-accountlib": "^1.6.1",
|
||||||
"xrpl-client": "^1.9.4"
|
"xrpl-client": "^2.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/dinero.js": "^1.9.0",
|
"@types/dinero.js": "^1.9.0",
|
||||||
@@ -75,12 +76,16 @@
|
|||||||
"@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.9.5"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"ripple-binary-codec": "=1.4.2"
|
"ripple-binary-codec": "=1.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>XRPL Hooks Builder</title>
|
<title>Xahau Hooks Builder</title>
|
||||||
<meta property="og:title" content="XRPL Hooks Builder" />
|
<meta property="og:title" content="Xahau Hooks Builder" />
|
||||||
<meta name="twitter:title" content="XRPL Hooks Builder" />
|
<meta name="twitter:title" content="Xahau 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 XRP Ledger."
|
content="Hooks Builder, add smart contract functionality to the Xahau Network."
|
||||||
/>
|
/>
|
||||||
<meta
|
<meta
|
||||||
property="og:description"
|
property="og:description"
|
||||||
content="Hooks Builder, add smart contract functionality to the XRP Ledger."
|
content="Hooks Builder, add smart contract functionality to the Xahau Network."
|
||||||
/>
|
/>
|
||||||
<meta
|
<meta
|
||||||
name="twitter:description"
|
name="twitter:description"
|
||||||
content="Hooks Builder, add smart contract functionality to the XRP Ledger."
|
content="Hooks Builder, add smart contract functionality to the Xahau Network."
|
||||||
/>
|
/>
|
||||||
<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" />
|
||||||
@@ -121,7 +121,7 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
|
|||||||
<Alert />
|
<Alert />
|
||||||
<Flex
|
<Flex
|
||||||
as="a"
|
as="a"
|
||||||
href="https://github.com/XRPLF/Hooks/discussions"
|
href="https://github.com/Xahau/xrpl-hooks-ide/issues"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
css={{ position: 'fixed', right: '$4', bottom: '$4' }}
|
css={{ position: 'fixed', right: '$4', bottom: '$4' }}
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import NextAuth from 'next-auth'
|
import NextAuth from 'next-auth'
|
||||||
|
|
||||||
|
declare module "next-auth" {
|
||||||
|
interface User {
|
||||||
|
username: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default NextAuth({
|
export default NextAuth({
|
||||||
// Configure one or more authentication providers
|
// Configure one or more authentication providers
|
||||||
providers: [
|
providers: [
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ const Home: NextPage = () => {
|
|||||||
|
|
||||||
const activeFile = snap.files[snap.active] as IFile | undefined
|
const activeFile = snap.files[snap.active] as IFile | undefined
|
||||||
const activeFileExt = getFileExtention(activeFile?.name)
|
const activeFileExt = getFileExtention(activeFile?.name)
|
||||||
|
const canCompile = activeFileExt === 'c' || activeFileExt === 'wat'
|
||||||
return (
|
return (
|
||||||
<Split
|
<Split
|
||||||
direction="vertical"
|
direction="vertical"
|
||||||
@@ -162,7 +163,7 @@ const Home: NextPage = () => {
|
|||||||
>
|
>
|
||||||
<main style={{ display: 'flex', flex: 1, position: 'relative' }}>
|
<main style={{ display: 'flex', flex: 1, position: 'relative' }}>
|
||||||
<HooksEditor />
|
<HooksEditor />
|
||||||
{activeFileExt === 'c' && (
|
{canCompile && (
|
||||||
<Hotkeys
|
<Hotkeys
|
||||||
keyName="command+b,ctrl+b"
|
keyName="command+b,ctrl+b"
|
||||||
onKeyDown={() => !snap.compiling && snap.files.length && compileCode(snap.active)}
|
onKeyDown={() => !snap.compiling && snap.files.length && compileCode(snap.active)}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
346
patches/ripple-binary-codec+1.6.0.patch
Normal file
346
patches/ripple-binary-codec+1.6.0.patch
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 710 KiB After Width: | Height: | Size: 352 KiB |
5
raw-loader.d.ts
vendored
5
raw-loader.d.ts
vendored
@@ -2,3 +2,8 @@ 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
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import state, { FaucetAccountRes } from '../index'
|
import state, { FaucetAccountRes } from '../index'
|
||||||
|
import fetchAccountInfo from '../../utils/accountInfo';
|
||||||
|
|
||||||
export const names = [
|
export const names = [
|
||||||
'Alice',
|
'Alice',
|
||||||
@@ -35,40 +36,37 @@ export const addFaucetAccount = async (name?: string, showToast: boolean = false
|
|||||||
})
|
})
|
||||||
const json: FaucetAccountRes | { error: string } = await res.json()
|
const json: FaucetAccountRes | { error: string } = await res.json()
|
||||||
if ('error' in json) {
|
if ('error' in json) {
|
||||||
if (showToast) {
|
if (!showToast) return;
|
||||||
return toast.error(json.error, { id: toastId })
|
return toast.error(json.error, { id: toastId })
|
||||||
} else {
|
}
|
||||||
return
|
const currNames = state.accounts.map(acc => acc.name)
|
||||||
}
|
const info = await fetchAccountInfo(json.address, { silent: true })
|
||||||
} else {
|
state.accounts.push({
|
||||||
if (showToast) {
|
name: name || names.filter(name => !currNames.includes(name))[0],
|
||||||
toast.success('New account created', { id: toastId })
|
xrp: (json.xrp || 0 * 1000000).toString(),
|
||||||
}
|
address: json.address,
|
||||||
const currNames = state.accounts.map(acc => acc.name)
|
secret: json.secret,
|
||||||
state.accounts.push({
|
sequence: info?.Sequence || 1,
|
||||||
name: name || names.filter(name => !currNames.includes(name))[0],
|
hooks: [],
|
||||||
xrp: (json.xrp || 0 * 1000000).toString(),
|
isLoading: false,
|
||||||
address: json.address,
|
version: '2'
|
||||||
secret: json.secret,
|
})
|
||||||
sequence: 1,
|
if (showToast) {
|
||||||
hooks: [],
|
toast.success('New account created', { id: toastId })
|
||||||
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')
|
||||||
@@ -79,7 +77,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} XRP)`, { id: toastId })
|
toast.success(`Funds added (${json.xrp} XAH)`, { 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()
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ import { ref } from 'valtio'
|
|||||||
export const compileCode = async (activeId: number) => {
|
export const compileCode = async (activeId: number) => {
|
||||||
// Save the file to global state
|
// Save the file to global state
|
||||||
saveFile(false, activeId)
|
saveFile(false, activeId)
|
||||||
|
const file = state.files[activeId]
|
||||||
|
if (file.name.endsWith('.wat')) {
|
||||||
|
return compileWat(activeId)
|
||||||
|
}
|
||||||
|
|
||||||
if (!process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT) {
|
if (!process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT) {
|
||||||
throw Error('Missing env!')
|
throw Error('Missing env!')
|
||||||
}
|
}
|
||||||
@@ -26,7 +31,6 @@ export const compileCode = async (activeId: number) => {
|
|||||||
// Set loading state to true
|
// Set loading state to true
|
||||||
state.compiling = true
|
state.compiling = true
|
||||||
state.logs = []
|
state.logs = []
|
||||||
const file = state.files[activeId]
|
|
||||||
try {
|
try {
|
||||||
file.containsErrors = false
|
file.containsErrors = false
|
||||||
let res: Response
|
let res: Response
|
||||||
@@ -72,7 +76,7 @@ export const compileCode = async (activeId: number) => {
|
|||||||
|
|
||||||
// Import wabt from and create human readable version of wasm file and
|
// Import wabt from and create human readable version of wasm file and
|
||||||
// put it into state
|
// put it into state
|
||||||
const ww = (await import('wabt')).default()
|
const ww = await (await import('wabt')).default()
|
||||||
const myModule = ww.readWasm(new Uint8Array(bufferData), {
|
const myModule = ww.readWasm(new Uint8Array(bufferData), {
|
||||||
readDebugNames: true
|
readDebugNames: true
|
||||||
})
|
})
|
||||||
@@ -122,3 +126,46 @@ export const compileCode = async (activeId: number) => {
|
|||||||
file.containsErrors = true
|
file.containsErrors = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const compileWat = async (activeId: number) => {
|
||||||
|
if (state.compiling) return;
|
||||||
|
const file = state.files[activeId]
|
||||||
|
state.compiling = true
|
||||||
|
state.logs = []
|
||||||
|
try {
|
||||||
|
const wabt = await (await import('wabt')).default()
|
||||||
|
const module = wabt.parseWat(file.name, file.content);
|
||||||
|
module.resolveNames();
|
||||||
|
module.validate();
|
||||||
|
const { buffer } = module.toBinary({
|
||||||
|
log: false,
|
||||||
|
write_debug_names: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
file.compiledContent = ref(buffer)
|
||||||
|
file.lastCompiled = new Date()
|
||||||
|
file.compiledValueSnapshot = file.content
|
||||||
|
file.compiledWatContent = file.content
|
||||||
|
|
||||||
|
toast.success('Compiled successfully!', { position: 'bottom-center' })
|
||||||
|
state.logs.push({
|
||||||
|
type: 'success',
|
||||||
|
message: `File ${state.files?.[activeId]?.name} compiled successfully. Ready to deploy.`,
|
||||||
|
link: Router.asPath.replace('develop', 'deploy'),
|
||||||
|
linkText: 'Go to deploy'
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
let message = "Error compiling WAT file!"
|
||||||
|
if (err instanceof Error) {
|
||||||
|
message = err.message
|
||||||
|
}
|
||||||
|
state.logs.push({
|
||||||
|
type: 'error',
|
||||||
|
message
|
||||||
|
})
|
||||||
|
toast.error(`Error occurred while compiling!`, { position: 'bottom-center' })
|
||||||
|
file.containsErrors = true
|
||||||
|
}
|
||||||
|
state.compiling = false
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,11 +14,4 @@ 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
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import calculateHookOn, { TTS } from '../../utils/hookOnCalculator'
|
|||||||
import { Link } from '../../components'
|
import { Link } from '../../components'
|
||||||
import { ref } from 'valtio'
|
import { ref } from 'valtio'
|
||||||
import estimateFee from '../../utils/estimateFee'
|
import estimateFee from '../../utils/estimateFee'
|
||||||
import { SetHookData } from '../../utils/setHook'
|
import { SetHookData, toHex } from '../../utils/setHook'
|
||||||
|
import ResultLink from '../../components/ResultLink'
|
||||||
|
import { 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)
|
||||||
@@ -16,13 +18,6 @@ export const sha256 = async (string: string) => {
|
|||||||
return hashHex
|
return hashHex
|
||||||
}
|
}
|
||||||
|
|
||||||
function toHex(str: string) {
|
|
||||||
var result = ''
|
|
||||||
for (var i = 0; i < str.length; i++) {
|
|
||||||
result += str.charCodeAt(i).toString(16)
|
|
||||||
}
|
|
||||||
return result.toUpperCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
function arrayBufferToHex(arrayBuffer?: ArrayBuffer | null) {
|
function arrayBufferToHex(arrayBuffer?: ArrayBuffer | null) {
|
||||||
if (!arrayBuffer) {
|
if (!arrayBuffer) {
|
||||||
@@ -63,9 +58,6 @@ 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
|
||||||
@@ -86,187 +78,184 @@ export const prepareDeployHookTx = async (
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// });
|
// });
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window === 'undefined') return
|
||||||
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,
|
||||||
Hooks: [
|
NetworkID: process.env.NEXT_PUBLIC_NETWORK_ID,
|
||||||
{
|
Hooks: [
|
||||||
Hook: {
|
{
|
||||||
CreateCode: arrayBufferToHex(activeFile?.compiledContent).toUpperCase(),
|
Hook: {
|
||||||
HookOn: calculateHookOn(hookOnValues),
|
CreateCode: arrayBufferToHex(activeFile?.compiledContent).toUpperCase(),
|
||||||
HookNamespace,
|
HookOn: calculateHookOn(hookOnValues),
|
||||||
HookApiVersion: 0,
|
HookNamespace,
|
||||||
Flags: 1,
|
HookApiVersion: 0,
|
||||||
// ...(filteredHookGrants.length > 0 && { HookGrants: filteredHookGrants }),
|
Flags: 1,
|
||||||
...(filteredHookParameters.length > 0 && {
|
// ...(filteredHookGrants.length > 0 && { HookGrants: filteredHookGrants }),
|
||||||
HookParameters: filteredHookParameters
|
...(filteredHookParameters.length > 0 && {
|
||||||
})
|
HookParameters: filteredHookParameters
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
]
|
||||||
return tx
|
|
||||||
}
|
}
|
||||||
|
return tx
|
||||||
}
|
}
|
||||||
|
|
||||||
/* deployHook function turns the wasm binary into
|
/*
|
||||||
* hex string, signs the transaction and deploys it to
|
* Turns the wasm binary into hex string, signs the transaction and deploys it to Hooks testnet.
|
||||||
* Hooks testnet.
|
|
||||||
*/
|
*/
|
||||||
export const deployHook = async (account: IAccount & { name?: string }, data: SetHookData) => {
|
export const deployHook = async (account: IAccount & { name?: string }, data: SetHookData) => {
|
||||||
if (typeof window !== 'undefined') {
|
const activeFile = state.files[state.active]?.compiledContent
|
||||||
const activeFile = state.files[state.active]?.compiledContent
|
? state.files[state.active]
|
||||||
? state.files[state.active]
|
: state.files.filter(file => file.compiledContent)[0]
|
||||||
: state.files.filter(file => file.compiledContent)[0]
|
state.deployValues[activeFile.name] = data
|
||||||
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 { signedTransaction } = sign(tx, keypair)
|
const tx = await prepareDeployHookTx(account, data)
|
||||||
const currentAccount = state.accounts.find(acc => acc.address === account.address)
|
if (!tx) {
|
||||||
if (currentAccount) {
|
return
|
||||||
currentAccount.isLoading = true
|
}
|
||||||
}
|
const keypair = derive.familySeed(account.secret)
|
||||||
let submitRes
|
const { signedTransaction } = sign(tx, keypair)
|
||||||
|
|
||||||
try {
|
const currentAccount = state.accounts.find(acc => acc.address === account.address)
|
||||||
submitRes = await state.client?.send({
|
if (currentAccount) {
|
||||||
command: 'submit',
|
currentAccount.isLoading = true
|
||||||
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({
|
||||||
if (submitRes.engine_result === 'tesSUCCESS') {
|
type: 'success',
|
||||||
state.deployLogs.push({
|
message: resultMsg
|
||||||
type: 'success',
|
})
|
||||||
message: 'Hook deployed successfully ✅'
|
} else if (submitRes.engine_result) {
|
||||||
})
|
|
||||||
state.deployLogs.push({
|
|
||||||
type: 'success',
|
|
||||||
message: ref(
|
|
||||||
<>
|
|
||||||
[{submitRes.engine_result}] {submitRes.engine_result_message} Transaction hash:{' '}
|
|
||||||
<Link
|
|
||||||
as="a"
|
|
||||||
href={`https://${process.env.NEXT_PUBLIC_EXPLORER_URL}/${submitRes.tx_json?.hash}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
{submitRes.tx_json?.hash}
|
|
||||||
</Link>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
// message: `[${submitRes.engine_result}] ${submitRes.engine_result_message} Validated ledger index: ${submitRes.validated_ledger_index}`,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
state.deployLogs.push({
|
|
||||||
type: 'error',
|
|
||||||
message: `[${submitRes.engine_result || submitRes.error}] ${
|
|
||||||
submitRes.engine_result_message || submitRes.error_exception
|
|
||||||
}`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err)
|
|
||||||
state.deployLogs.push({
|
state.deployLogs.push({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: 'Error occurred while deploying'
|
message: resultMsg
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
state.deployLogs.push({
|
||||||
|
type: 'error',
|
||||||
|
message: `[${submitRes.error}] ${submitRes.error_exception}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (currentAccount) {
|
} catch (err) {
|
||||||
currentAccount.isLoading = false
|
console.error(err)
|
||||||
}
|
state.deployLogs.push({
|
||||||
return submitRes
|
type: 'error',
|
||||||
|
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
|
||||||
}
|
}
|
||||||
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: '100000',
|
||||||
Fee: '100000',
|
NetworkID: process.env.NEXT_PUBLIC_NETWORK_ID,
|
||||||
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 ? res?.base_fee : '1000'
|
|
||||||
} catch (err) {
|
|
||||||
// use default value what you defined earlier
|
|
||||||
console.log(err)
|
|
||||||
}
|
|
||||||
const { signedTransaction } = sign(tx, keypair)
|
|
||||||
|
|
||||||
if (currentAccount) {
|
|
||||||
currentAccount.isLoading = true
|
|
||||||
}
|
|
||||||
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 })
|
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') {
|
||||||
|
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({
|
state.deployLogs.push({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: 'Error occurred while deleting hook'
|
message: `[${submitRes.engine_result || submitRes.error}] ${
|
||||||
|
submitRes.engine_result_message || submitRes.error_exception
|
||||||
|
}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (currentAccount) {
|
} catch (err) {
|
||||||
currentAccount.isLoading = false
|
console.log(err)
|
||||||
}
|
toast.error('Error occurred while deleting hook', { id: toastId })
|
||||||
return submitRes
|
state.deployLogs.push({
|
||||||
|
type: 'error',
|
||||||
|
message: 'Error occurred while deleting hook'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
if (currentAccount) {
|
||||||
|
currentAccount.isLoading = false
|
||||||
|
}
|
||||||
|
return submitRes
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,19 +24,28 @@ export const fetchFiles = async (gistId: string) => {
|
|||||||
.includes(id)
|
.includes(id)
|
||||||
|
|
||||||
if (isTemplate(gistId)) {
|
if (isTemplate(gistId)) {
|
||||||
// fetch headers
|
const template = Object.values(templateFileIds).find(tmp => tmp.id === gistId)
|
||||||
const headerRes = await fetch(
|
let headerFiles: Record<string, { filename: string; content: string; language: string }> =
|
||||||
`${process.env.NEXT_PUBLIC_COMPILE_API_BASE_URL}/api/header-files`
|
|
||||||
)
|
|
||||||
if (!headerRes.ok) throw Error('Failed to fetch headers')
|
|
||||||
|
|
||||||
const headerJson = await headerRes.json()
|
|
||||||
const headerFiles: Record<string, { filename: string; content: string; language: string }> =
|
|
||||||
{}
|
{}
|
||||||
Object.entries(headerJson).forEach(([key, value]) => {
|
if (template?.headerId) {
|
||||||
const fname = `${key}.h`
|
const resHeader = await octokit.request('GET /gists/{gist_id}', { gist_id: template.headerId })
|
||||||
headerFiles[fname] = { filename: fname, content: value as string, language: 'C' }
|
if (!resHeader.data.files) throw new Error('No header files could be fetched from given gist id!')
|
||||||
})
|
headerFiles = resHeader.data.files as any
|
||||||
|
} else {
|
||||||
|
// fetch headers
|
||||||
|
const headerRes = await fetch(
|
||||||
|
`${process.env.NEXT_PUBLIC_COMPILE_API_BASE_URL}/api/header-files`
|
||||||
|
)
|
||||||
|
if (!headerRes.ok) throw Error('Failed to fetch headers')
|
||||||
|
|
||||||
|
const headerJson = await headerRes.json()
|
||||||
|
const headerFiles: Record<string, { filename: string; content: string; language: string }> =
|
||||||
|
{}
|
||||||
|
Object.entries(headerJson).forEach(([key, value]) => {
|
||||||
|
const fname = `${key}.h`
|
||||||
|
headerFiles[fname] = { filename: fname, content: value as string, language: 'C' }
|
||||||
|
})
|
||||||
|
}
|
||||||
const files = {
|
const files = {
|
||||||
...res.data.files,
|
...res.data.files,
|
||||||
...headerFiles
|
...headerFiles
|
||||||
@@ -61,6 +70,7 @@ export const fetchFiles = async (gistId: string) => {
|
|||||||
// default priority is undefined == 0
|
// default priority is undefined == 0
|
||||||
const extPriority: Record<string, number> = {
|
const extPriority: Record<string, number> = {
|
||||||
c: 3,
|
c: 3,
|
||||||
|
wat: 3,
|
||||||
md: 2,
|
md: 2,
|
||||||
h: -1
|
h: -1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ import { derive, sign } from 'xrpl-accountlib'
|
|||||||
|
|
||||||
import state from '..'
|
import state from '..'
|
||||||
import type { IAccount } from '..'
|
import type { IAccount } from '..'
|
||||||
|
import ResultLink from '../../components/ResultLink'
|
||||||
|
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 {
|
||||||
@@ -19,34 +21,46 @@ 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, // TODO auto-fillable default
|
Fee,
|
||||||
|
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 state.client.send({
|
const response = await xrplSend({
|
||||||
command: 'submit',
|
command: 'submit',
|
||||||
tx_blob: signedTransaction
|
tx_blob: signedTransaction
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const resultMsg = ref(
|
||||||
|
<>
|
||||||
|
{logPrefix}[<ResultLink result={response.engine_result} />] {response.engine_result_message}
|
||||||
|
</>
|
||||||
|
)
|
||||||
if (response.engine_result === 'tesSUCCESS') {
|
if (response.engine_result === 'tesSUCCESS') {
|
||||||
state.transactionLogs.push({
|
state.transactionLogs.push({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message: `${logPrefix}[${response.engine_result}] ${response.engine_result_message}`
|
message: resultMsg
|
||||||
|
})
|
||||||
|
} else if (response.engine_result) {
|
||||||
|
state.transactionLogs.push({
|
||||||
|
type: 'error',
|
||||||
|
message: resultMsg
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
state.transactionLogs.push({
|
state.transactionLogs.push({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: `${logPrefix}[${response.error || response.engine_result}] ${
|
message: `${logPrefix}[${response.error}] ${response.error_exception}`
|
||||||
response.error_exception || response.engine_result_message
|
|
||||||
}`
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const currAcc = state.accounts.find(acc => acc.address === account.address)
|
const currAcc = state.accounts.find(acc => acc.address === account.address)
|
||||||
7
state/actions/xrpl-client.ts
Normal file
7
state/actions/xrpl-client.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
82
state/constants/flags.ts
Normal file
82
state/constants/flags.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import { SelectOption } from '../transactions';
|
||||||
|
|
||||||
|
interface Flags {
|
||||||
|
[key: string]: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const transactionFlags: { [key: /* TransactionType */ string]: Flags } = {
|
||||||
|
"*": {
|
||||||
|
tfFullyCanonicalSig: '0x80000000'
|
||||||
|
},
|
||||||
|
Payment: {
|
||||||
|
tfNoDirectRipple: '0x00010000',
|
||||||
|
tfPartialPayment: '0x00020000',
|
||||||
|
tfLimitQuality: '0x00040000',
|
||||||
|
},
|
||||||
|
AccountSet: {
|
||||||
|
tfRequireDestTag: '0x00010000',
|
||||||
|
tfOptionalDestTag: '0x00020000',
|
||||||
|
tfRequireAuth: '0x00040000',
|
||||||
|
tfOptionalAuth: '0x00080000',
|
||||||
|
tfDisallowXRP: '0x00100000',
|
||||||
|
tfAllowXRP: '0x00200000',
|
||||||
|
},
|
||||||
|
NFTokenCreateOffer: {
|
||||||
|
tfSellNFToken: '0x00000001',
|
||||||
|
},
|
||||||
|
NFTokenMint: {
|
||||||
|
tfBurnable: '0x00000001',
|
||||||
|
tfOnlyXRP: '0x00000002',
|
||||||
|
tfTrustLine: '0x00000004',
|
||||||
|
tfTransferable: '0x00000008',
|
||||||
|
},
|
||||||
|
OfferCreate: {
|
||||||
|
tfPassive: '0x00010000',
|
||||||
|
tfImmediateOrCancel: '0x00020000',
|
||||||
|
tfFillOrKill: '0x00040000',
|
||||||
|
tfSell: '0x00080000',
|
||||||
|
},
|
||||||
|
PaymentChannelClaim: {
|
||||||
|
tfRenew: '0x00010000',
|
||||||
|
tfClose: '0x00020000',
|
||||||
|
},
|
||||||
|
TrustSet: {
|
||||||
|
tfSetfAuth: '0x00010000',
|
||||||
|
tfSetNoRipple: '0x00020000',
|
||||||
|
tfClearNoRipple: '0x00040000',
|
||||||
|
tfSetFreeze: '0x00100000',
|
||||||
|
tfClearFreeze: '0x00200000',
|
||||||
|
},
|
||||||
|
URITokenMint: {
|
||||||
|
tfBurnable: '0x00000001',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getFlags = (tt?: string) => {
|
||||||
|
if (!tt) return
|
||||||
|
const flags = {
|
||||||
|
...transactionFlags['*'],
|
||||||
|
...transactionFlags[tt]
|
||||||
|
}
|
||||||
|
|
||||||
|
return flags
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function combineFlags(flags?: string[]): string | undefined {
|
||||||
|
if (!flags) return
|
||||||
|
|
||||||
|
const num = flags.reduce((cumm, curr) => cumm | BigInt(curr), BigInt(0))
|
||||||
|
return num.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extractFlags(transactionType: string, flags?: string | number,): SelectOption[] {
|
||||||
|
const flagsObj = getFlags(transactionType)
|
||||||
|
if (!flags || !flagsObj) return []
|
||||||
|
|
||||||
|
const extracted = Object.entries(flagsObj).reduce((cumm, [label, value]) => {
|
||||||
|
return (BigInt(flags) & BigInt(value)) ? cumm.concat({ label, value }) : cumm
|
||||||
|
}, [] as SelectOption[])
|
||||||
|
|
||||||
|
return extracted
|
||||||
|
}
|
||||||
@@ -4,36 +4,49 @@ import Notary from '../../components/icons/Notary'
|
|||||||
import Peggy from '../../components/icons/Peggy'
|
import Peggy from '../../components/icons/Peggy'
|
||||||
import Starter from '../../components/icons/Starter'
|
import Starter from '../../components/icons/Starter'
|
||||||
|
|
||||||
export const templateFileIds = {
|
type Template = {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
headerId?: string
|
||||||
|
icon: () => JSX.Element
|
||||||
|
}
|
||||||
|
|
||||||
|
export const templateFileIds: Record<string, Template> = {
|
||||||
starter: {
|
starter: {
|
||||||
id: '1f8109c80f504e6326db2735df2f0ad6', // Forked
|
id: '1f8109c80f504e6326db2735df2f0ad6', // Forked
|
||||||
name: 'Starter',
|
name: 'Starter',
|
||||||
description:
|
description:
|
||||||
'Just a basic starter with essential imports, just accepts any transaction coming through',
|
'Just a basic starter with essential imports, just accepts any transaction coming through',
|
||||||
|
headerId: '028e8ce6d6d674776970caf8acc77ecc',
|
||||||
icon: Starter
|
icon: Starter
|
||||||
},
|
},
|
||||||
firewall: {
|
firewall: {
|
||||||
id: '1cc30f39c8a0b9c55b88c312669ca45e', // Forked
|
id: '1cc30f39c8a0b9c55b88c312669ca45e', // Forked
|
||||||
name: 'Firewall',
|
name: 'Firewall',
|
||||||
description: 'This Hook essentially checks a blacklist of accounts',
|
description: 'This Hook essentially checks a blacklist of accounts',
|
||||||
|
headerId: '028e8ce6d6d674776970caf8acc77ecc',
|
||||||
icon: Firewall
|
icon: Firewall
|
||||||
},
|
},
|
||||||
notary: {
|
notary: {
|
||||||
id: '87b6f5a8c2f5038fb0f20b8b510efa10', // Forked
|
id: '87b6f5a8c2f5038fb0f20b8b510efa10', // Forked
|
||||||
name: 'Notary',
|
name: 'Notary',
|
||||||
description: 'Collecting signatures for multi-sign transactions',
|
description: 'Collecting signatures for multi-sign transactions',
|
||||||
|
headerId: '028e8ce6d6d674776970caf8acc77ecc',
|
||||||
icon: Notary
|
icon: Notary
|
||||||
},
|
},
|
||||||
carbon: {
|
carbon: {
|
||||||
id: '953662b22d065449f8ab6f69bc2afe41', // Forked
|
id: '953662b22d065449f8ab6f69bc2afe41', // Forked
|
||||||
name: 'Carbon',
|
name: 'Carbon',
|
||||||
description: 'Send a percentage of sum to an address',
|
description: 'Send a percentage of sum to an address',
|
||||||
|
headerId: '028e8ce6d6d674776970caf8acc77ecc',
|
||||||
icon: Carbon
|
icon: Carbon
|
||||||
},
|
},
|
||||||
peggy: {
|
peggy: {
|
||||||
id: '049784a83fa068faf7912f663f7b6471', // Forked
|
id: '049784a83fa068faf7912f663f7b6471', // Forked
|
||||||
name: 'Peggy',
|
name: 'Peggy',
|
||||||
description: 'An oracle based stable coin hook',
|
description: 'An oracle based stable coin hook',
|
||||||
|
headerId: '028e8ce6d6d674776970caf8acc77ecc',
|
||||||
icon: Peggy
|
icon: Peggy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export interface IState {
|
|||||||
splits: {
|
splits: {
|
||||||
[id: string]: SplitSize
|
[id: string]: SplitSize
|
||||||
}
|
}
|
||||||
client: XrplClient | null
|
client: XrplClient
|
||||||
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: null,
|
client: undefined!, // set below only.
|
||||||
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'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -4,33 +4,56 @@ import transactionsData from '../content/transactions.json'
|
|||||||
import state from '.'
|
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 { 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
|
||||||
|
hookParameters: HookParameters
|
||||||
|
memos: Memos
|
||||||
txIsLoading: boolean
|
txIsLoading: boolean
|
||||||
txIsDisabled: boolean
|
txIsDisabled: boolean
|
||||||
txFields: TxFields
|
txFields: TxFields
|
||||||
viewType: 'json' | 'ui'
|
viewType: 'json' | 'ui'
|
||||||
editorValue?: string
|
editorValue?: string
|
||||||
|
editorIsSaved: boolean
|
||||||
estimatedFee?: string
|
estimatedFee?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const commonFields = ['TransactionType', 'Account', 'Sequence', "HookParameters"] as const;
|
||||||
|
|
||||||
export type TxFields = Omit<
|
export type TxFields = Omit<
|
||||||
Partial<typeof transactionsData[0]>,
|
Partial<typeof transactionsData[0]>,
|
||||||
'Account' | 'Sequence' | 'TransactionType'
|
typeof commonFields[number]
|
||||||
>
|
>
|
||||||
|
|
||||||
export const defaultTransaction: TransactionState = {
|
export const defaultTransaction: TransactionState = {
|
||||||
selectedTransaction: null,
|
selectedTransaction: null,
|
||||||
selectedAccount: null,
|
selectedAccount: null,
|
||||||
selectedDestAccount: null,
|
selectedFlags: null,
|
||||||
|
hookParameters: {},
|
||||||
|
memos: {},
|
||||||
|
editorIsSaved: true,
|
||||||
txIsLoading: false,
|
txIsLoading: false,
|
||||||
txIsDisabled: false,
|
txIsDisabled: false,
|
||||||
txFields: {},
|
txFields: {},
|
||||||
@@ -106,47 +129,51 @@ 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]
|
||||||
// convert xrp
|
if (!typeIs(_value, 'object')) return
|
||||||
if (_value && typeof _value === 'object' && _value.$type === 'xrp') {
|
// amount.xrp
|
||||||
if (+_value.$value) {
|
if (_value.$type === 'amount.xrp') {
|
||||||
options[field] = (+_value.$value * 1000000 + '') as any
|
if (_value.$value) {
|
||||||
|
options[field] = (+(_value as any).$value * 1000000 + '')
|
||||||
} else {
|
} else {
|
||||||
options[field] = undefined // 👇 💀
|
options[field] = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// handle type: `json`
|
// amount.token
|
||||||
if (_value && typeof _value === 'object' && _value.$type === 'json') {
|
if (_value.$type === 'amount.token') {
|
||||||
if (typeof _value.$value === 'object') {
|
if (typeIs(_value.$value, 'string')) {
|
||||||
|
options[field] = parseJSON(_value.$value)
|
||||||
|
} else if (typeIs(_value.$value, 'object')) {
|
||||||
options[field] = _value.$value
|
options[field] = _value.$value
|
||||||
} else {
|
} else {
|
||||||
try {
|
options[field] = undefined
|
||||||
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
|
||||||
// delete unnecessary fields
|
if (_value.$type === 'account') {
|
||||||
if (!options[field]) {
|
options[field] = (_value.$value as any)?.toString() || ""
|
||||||
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) {
|
||||||
@@ -156,7 +183,7 @@ export const prepareState = (value: string, transactionType?: string) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const { Account, TransactionType, Destination, ...rest } = options
|
const { Account, TransactionType, HookParameters, Memos, ...rest } = options
|
||||||
let tx: Partial<TransactionState> = {}
|
let tx: Partial<TransactionState> = {}
|
||||||
const schema = getTxFields(transactionType)
|
const schema = getTxFields(transactionType)
|
||||||
|
|
||||||
@@ -186,39 +213,58 @@ export const prepareState = (value: string, transactionType?: string) => {
|
|||||||
tx.selectedTransaction = null
|
tx.selectedTransaction = null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (schema.Destination !== undefined) {
|
if (HookParameters && HookParameters instanceof Array) {
|
||||||
const dest = state.accounts.find(acc => acc.address === Destination)
|
tx.hookParameters = HookParameters.reduce<TransactionState["hookParameters"]>((acc, cur, idx) => {
|
||||||
if (dest) {
|
const param = { label: fromHex(cur.HookParameter?.HookParameterName || ""), value: cur.HookParameter?.HookParameterValue || "" }
|
||||||
tx.selectedDestAccount = {
|
acc[idx] = param;
|
||||||
label: dest.name,
|
return acc;
|
||||||
value: dest.address
|
}, {})
|
||||||
}
|
}
|
||||||
} else if (Destination) {
|
|
||||||
tx.selectedDestAccount = {
|
if (Memos && Memos instanceof Array) {
|
||||||
label: Destination,
|
tx.memos = Memos.reduce<TransactionState["memos"]>((acc, cur, idx) => {
|
||||||
value: Destination
|
const memo = { data: cur.Memo?.MemoData || "", type: fromHex(cur.Memo?.MemoType || ""), format: fromHex(cur.Memo?.MemoFormat || "") }
|
||||||
}
|
acc[idx] = memo;
|
||||||
} else {
|
return acc;
|
||||||
tx.selectedDestAccount = null
|
}, {})
|
||||||
}
|
}
|
||||||
} else if (Destination) {
|
|
||||||
rest.Destination = Destination
|
|
||||||
|
if (getFlags(TransactionType) && rest.Flags) {
|
||||||
|
const flags = extractFlags(TransactionType, rest.Flags)
|
||||||
|
|
||||||
|
rest.Flags = undefined
|
||||||
|
tx.selectedFlags = flags
|
||||||
}
|
}
|
||||||
|
|
||||||
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 =
|
|
||||||
typeof value !== 'object' &&
|
const isAmount = schemaVal &&
|
||||||
schemaVal &&
|
typeIs(schemaVal, "object") &&
|
||||||
typeof schemaVal === 'object' &&
|
schemaVal.$type.startsWith('amount.');
|
||||||
schemaVal.$type === 'xrp'
|
const isAccount = schemaVal &&
|
||||||
if (isXrp) {
|
typeIs(schemaVal, "object") &&
|
||||||
|
schemaVal.$type.startsWith("account");
|
||||||
|
|
||||||
|
if (isAmount && ["number", "string"].includes(typeof value)) {
|
||||||
rest[field] = {
|
rest[field] = {
|
||||||
$type: 'xrp',
|
$type: 'amount.xrp', // TODO narrow typed $type.
|
||||||
$value: +value / 1000000 // ! maybe use bigint?
|
$value: +value / 1000000 // ! maybe use bigint?
|
||||||
}
|
}
|
||||||
} else if (typeof value === 'object') {
|
} else if (isAmount && 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
|
||||||
@@ -227,6 +273,7 @@ export const prepareState = (value: string, transactionType?: string) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
tx.txFields = rest
|
tx.txFields = rest
|
||||||
|
tx.editorIsSaved = true;
|
||||||
|
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
@@ -237,12 +284,12 @@ export const getTxFields = (tt?: string) => {
|
|||||||
if (!txFields) return {}
|
if (!txFields) return {}
|
||||||
|
|
||||||
let _txFields = Object.keys(txFields)
|
let _txFields = Object.keys(txFields)
|
||||||
.filter(key => !['TransactionType', 'Account', 'Sequence'].includes(key))
|
.filter(key => !commonFields.includes(key as any))
|
||||||
.reduce<TxFields>((tf, key) => ((tf[key as keyof TxFields] = (txFields as any)[key]), tf), {})
|
.reduce<TxFields>((tf, key) => ((tf[key as keyof TxFields] = (txFields as any)[key]), tf), {})
|
||||||
return _txFields
|
return _txFields
|
||||||
}
|
}
|
||||||
|
|
||||||
export { transactionsData }
|
export { transactionsData, commonFields }
|
||||||
|
|
||||||
export const transactionsOptions = transactionsData.map(tx => ({
|
export const transactionsOptions = transactionsData.map(tx => ({
|
||||||
value: tx.TransactionType,
|
value: tx.TransactionType,
|
||||||
|
|||||||
31
utils/accountInfo.ts
Normal file
31
utils/accountInfo.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import toast from 'react-hot-toast'
|
||||||
|
|
||||||
|
import { xrplSend } from '../state/actions/xrpl-client'
|
||||||
|
|
||||||
|
interface AccountInfo {
|
||||||
|
Account: string,
|
||||||
|
Sequence: number,
|
||||||
|
Flags: number,
|
||||||
|
Balance?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchAccountInfo = async (
|
||||||
|
address: string,
|
||||||
|
opts: { silent?: boolean } = {}
|
||||||
|
): Promise<AccountInfo | undefined> => {
|
||||||
|
try {
|
||||||
|
const res = await xrplSend({
|
||||||
|
id: `hooks-builder-req-info-${address}`,
|
||||||
|
command: 'account_info',
|
||||||
|
account: address
|
||||||
|
})
|
||||||
|
return res.account_data;
|
||||||
|
} catch (err) {
|
||||||
|
if (!opts.silent) {
|
||||||
|
console.error(err)
|
||||||
|
toast.error('Could not fetch account info!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default fetchAccountInfo
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
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 state, { IAccount } from '../state'
|
import { IAccount } from '../state'
|
||||||
|
import { xrplSend } from '../state/actions/xrpl-client'
|
||||||
|
|
||||||
const estimateFee = async (
|
const estimateFee = async (
|
||||||
tx: Record<string, unknown>,
|
tx: Record<string, unknown>,
|
||||||
@@ -22,7 +23,10 @@ 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 state.client?.send({ command: 'fee', tx_blob: signedTransaction })
|
const res = await xrplSend({ 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
|
||||||
}
|
}
|
||||||
@@ -30,7 +34,8 @@ const estimateFee = async (
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!opts.silent) {
|
if (!opts.silent) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
toast.error('Cannot estimate fee.') // ? Some better msg
|
const msg = err instanceof Error ? err.message : 'Error estimating fee!';
|
||||||
|
toast.error(msg);
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,3 +19,17 @@ 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;
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ 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,
|
||||||
@@ -18,28 +19,28 @@ export const tts = {
|
|||||||
ttDEPOSIT_PREAUTH: 19,
|
ttDEPOSIT_PREAUTH: 19,
|
||||||
ttTRUST_SET: 20,
|
ttTRUST_SET: 20,
|
||||||
ttACCOUNT_DELETE: 21,
|
ttACCOUNT_DELETE: 21,
|
||||||
ttHOOK_SET: 22,
|
ttSET_HOOK: 22,
|
||||||
ttNFTOKEN_MINT: 25,
|
ttURI_TOKEN_MINT: 45,
|
||||||
ttNFTOKEN_BURN: 26,
|
ttURI_TOKEN_BURN: 46,
|
||||||
ttNFTOKEN_CREATE_OFFER: 27,
|
ttURI_TOKEN_BUY: 47,
|
||||||
ttNFTOKEN_CANCEL_OFFER: 28,
|
ttURI_TOKEN_CREATE_SELL_OFFER: 48,
|
||||||
ttNFTOKEN_ACCEPT_OFFER: 29
|
ttURI_TOKEN_CANCEL_SELL_OFFER: 49,
|
||||||
|
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 start = '0x000000003e3ff5bf'
|
let s = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff'
|
||||||
arr.forEach(n => {
|
arr.forEach(n => {
|
||||||
let v = BigInt(start)
|
let v = BigInt(s)
|
||||||
v ^= BigInt(1) << BigInt(tts[n as keyof TTS])
|
v ^= BigInt(1) << BigInt(tts[n])
|
||||||
let s = v.toString(16)
|
s = "0x" + v.toString(16)
|
||||||
let l = s.length
|
|
||||||
if (l < 16) s = '0'.repeat(16 - l) + s
|
|
||||||
s = '0x' + s
|
|
||||||
start = s
|
|
||||||
})
|
})
|
||||||
return start.substring(2)
|
s = s.replace('0x', '')
|
||||||
|
s = s.padStart(64, '0')
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
export default calculateHookOn
|
export default calculateHookOn
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,9 +1,8 @@
|
|||||||
|
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 typeOf = <T>(arg: T) =>
|
const value = typeIs(val, "object") && '$type' in val && '$value' in val ? val?.$value : val
|
||||||
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 = {
|
||||||
@@ -12,19 +11,19 @@ export const extractSchemaProps = <O extends object>(obj: O) =>
|
|||||||
default: value
|
default: value
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeOf(value) === 'array') {
|
if (typeIs(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 (typeOf(item) !== 'object') {
|
if (typeIs(item, "object")) {
|
||||||
schema.items = {
|
schema.items = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: extractSchemaProps(item),
|
properties: extractSchemaProps(item),
|
||||||
default: item
|
default: item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO support primitive-value arrays
|
// TODO primitive-value arrays
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeOf(value) === 'object') {
|
if (typeIs(value, "object")) {
|
||||||
schema.properties = extractSchemaProps(value)
|
schema.properties = extractSchemaProps(value)
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -20,6 +20,13 @@ export type SetHookData = {
|
|||||||
}
|
}
|
||||||
$metaData?: any
|
$metaData?: any
|
||||||
}[]
|
}[]
|
||||||
|
Memos?: {
|
||||||
|
Memo: {
|
||||||
|
MemoType: string,
|
||||||
|
MemoData: string
|
||||||
|
MemoFormat: string
|
||||||
|
}
|
||||||
|
}[]
|
||||||
// HookGrants: {
|
// HookGrants: {
|
||||||
// HookGrant: {
|
// HookGrant: {
|
||||||
// Authorize: string;
|
// Authorize: string;
|
||||||
@@ -65,12 +72,22 @@ 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
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ import hooksFloatManipPure from './md/hooks-float-manip-pure.md'
|
|||||||
import hooksFloatOnePure from './md/hooks-float-one-pure.md'
|
import hooksFloatOnePure from './md/hooks-float-one-pure.md'
|
||||||
import hooksFloatPure from './md/hooks-float-pure.md'
|
import hooksFloatPure from './md/hooks-float-pure.md'
|
||||||
import hooksGuardCalled from './md/hooks-guard-called.md'
|
import hooksGuardCalled from './md/hooks-guard-called.md'
|
||||||
|
import hooksGuardCallNonConst from './md/hooks-guard-call-non-const.md'
|
||||||
import hooksGuardInFor from './md/hooks-guard-in-for.md'
|
import hooksGuardInFor from './md/hooks-guard-in-for.md'
|
||||||
import hooksGuardInWhile from './md/hooks-guard-in-while.md'
|
import hooksGuardInWhile from './md/hooks-guard-in-while.md'
|
||||||
import hooksHashBufLen from './md/hooks-hash-buf-len.md'
|
import hooksHashBufLen from './md/hooks-hash-buf-len.md'
|
||||||
@@ -70,6 +71,7 @@ const docs: { [key: string]: string } = {
|
|||||||
'hooks-float-one-pure': hooksFloatOnePure,
|
'hooks-float-one-pure': hooksFloatOnePure,
|
||||||
'hooks-float-pure': hooksFloatPure,
|
'hooks-float-pure': hooksFloatPure,
|
||||||
'hooks-guard-called': hooksGuardCalled,
|
'hooks-guard-called': hooksGuardCalled,
|
||||||
|
'hooks-guard-call-non-const': hooksGuardCallNonConst,
|
||||||
'hooks-guard-in-for': hooksGuardInFor,
|
'hooks-guard-in-for': hooksGuardInFor,
|
||||||
'hooks-guard-in-while': hooksGuardInWhile,
|
'hooks-guard-in-while': hooksGuardInWhile,
|
||||||
'hooks-hash-buf-len': hooksHashBufLen,
|
'hooks-hash-buf-len': hooksHashBufLen,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# hooks-account-buf-len
|
# hooks-account-buf-len
|
||||||
|
|
||||||
Function [hook_account](https://xrpl-hooks.readme.io/v2.0/reference/hook_account) has fixed-size account ID output.
|
Function [hook_account](https://xrpl-hooks.readme.io/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).
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# hooks-account-conv-buf-len
|
# hooks-account-conv-buf-len
|
||||||
|
|
||||||
Function [util_raddr](https://xrpl-hooks.readme.io/v2.0/reference/util_raddr) has fixed-size account ID input.
|
Function [util_raddr](https://xrpl-hooks.readme.io/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).
|
||||||
|
|||||||
@@ -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/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.
|
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.
|
||||||
|
|
||||||
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).
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# hooks-array-buf-len
|
# hooks-array-buf-len
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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/v2.0/docs/serialized-objects)
|
[Read more](https://xrpl-hooks.readme.io/docs/serialized-objects)
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
# hooks-burden-prereq
|
# hooks-burden-prereq
|
||||||
|
|
||||||
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).
|
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).
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# hooks-control-string-arg
|
# hooks-control-string-arg
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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`.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# hooks-detail-buf-len
|
# hooks-detail-buf-len
|
||||||
|
|
||||||
Function [etxn_details](https://xrpl-hooks.readme.io/v2.0/reference/etxn_details) has fixed-size sfEmitDetails output.
|
Function [etxn_details](https://xrpl-hooks.readme.io/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/v2.0/docs/emitted-transactions)
|
[Read more](https://xrpl-hooks.readme.io/docs/emitted-transactions)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# hooks-detail-prereq
|
# hooks-detail-prereq
|
||||||
|
|
||||||
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).
|
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).
|
||||||
|
|
||||||
[Read more](https://xrpl-hooks.readme.io/v2.0/docs/emitted-transactions)
|
[Read more](https://xrpl-hooks.readme.io/docs/emitted-transactions)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# hooks-emit-buf-len
|
# hooks-emit-buf-len
|
||||||
|
|
||||||
Function [emit](https://xrpl-hooks.readme.io/v2.0/reference/emit) has fixed-size transaction hash output.
|
Function [emit](https://xrpl-hooks.readme.io/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/v2.0/docs/emitted-transactions)
|
[Read more](https://xrpl-hooks.readme.io/docs/emitted-transactions)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# hooks-emit-prereq
|
# hooks-emit-prereq
|
||||||
|
|
||||||
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).
|
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).
|
||||||
|
|
||||||
[Read more](https://xrpl-hooks.readme.io/v2.0/docs/emitted-transactions)
|
[Read more](https://xrpl-hooks.readme.io/docs/emitted-transactions)
|
||||||
|
|||||||
@@ -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/v2.0/docs/loops-and-guarding#no-recursion)
|
[Read more](https://xrpl-hooks.readme.io/docs/loops-and-guarding#no-recursion)
|
||||||
|
|||||||
@@ -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/v2.0/docs/compiling-hooks#constraints)
|
[Read more](https://xrpl-hooks.readme.io/docs/compiling-hooks#constraints)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# hooks-entry-points
|
# hooks-entry-points
|
||||||
|
|
||||||
A Hook always implements and exports a [hook](https://xrpl-hooks.readme.io/v2.0/reference/hook) function.
|
A Hook always implements and exports a [hook](https://xrpl-hooks.readme.io/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/v2.0/docs/compiling-hooks)
|
[Read more](https://xrpl-hooks.readme.io/docs/compiling-hooks)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# hooks-fee-prereq
|
# hooks-fee-prereq
|
||||||
|
|
||||||
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).
|
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).
|
||||||
|
|
||||||
[Read more](https://xrpl-hooks.readme.io/v2.0/docs/hook-fees)
|
[Read more](https://xrpl-hooks.readme.io/docs/hook-fees)
|
||||||
|
|||||||
@@ -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/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.
|
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.
|
||||||
|
|
||||||
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/v2.0/docs/serialized-objects)
|
[Read more](https://xrpl-hooks.readme.io/docs/serialized-objects)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# hooks-field-buf-len
|
# hooks-field-buf-len
|
||||||
|
|
||||||
Hook API [sto_subfield](https://xrpl-hooks.readme.io/v2.0/reference/sto_subfield) requires non-empty input buffer.
|
Hook API [sto_subfield](https://xrpl-hooks.readme.io/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/v2.0/docs/serialized-objects)
|
[Read more](https://xrpl-hooks.readme.io/docs/serialized-objects)
|
||||||
|
|||||||
@@ -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/v2.0/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/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/v2.0/docs/serialized-objects)
|
[Read more](https://xrpl-hooks.readme.io/docs/serialized-objects)
|
||||||
|
|||||||
@@ -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/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.
|
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.
|
||||||
|
|
||||||
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/v2.0/docs/floating-point-numbers-xfl)
|
[Read more](https://xrpl-hooks.readme.io/docs/floating-point-numbers-xfl)
|
||||||
|
|
||||||
|
|||||||
@@ -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/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.
|
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.
|
||||||
|
|
||||||
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/v2.0/docs/floating-point-numbers-xfl)
|
[Read more](https://xrpl-hooks.readme.io/docs/floating-point-numbers-xfl)
|
||||||
|
|||||||
@@ -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/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.
|
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.
|
||||||
|
|
||||||
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/v2.0/docs/floating-point-numbers-xfl)
|
[Read more](https://xrpl-hooks.readme.io/docs/floating-point-numbers-xfl)
|
||||||
|
|||||||
@@ -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/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.
|
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.
|
||||||
|
|
||||||
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/v2.0/docs/floating-point-numbers-xfl)
|
[Read more](https://xrpl-hooks.readme.io/docs/floating-point-numbers-xfl)
|
||||||
|
|||||||
@@ -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/v2.0/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/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/v2.0/docs/floating-point-numbers-xfl)
|
[Read more](https://xrpl-hooks.readme.io/docs/floating-point-numbers-xfl)
|
||||||
|
|||||||
@@ -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/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.
|
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.
|
||||||
|
|
||||||
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/v2.0/docs/floating-point-numbers-xfl)
|
[Read more](https://xrpl-hooks.readme.io/docs/floating-point-numbers-xfl)
|
||||||
|
|||||||
6
xrpl-hooks-docs/md/hooks-guard-call-non-const.md
Normal file
6
xrpl-hooks-docs/md/hooks-guard-call-non-const.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# hooks-guard-call-non-const
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
[Read more](https://xrpl-hooks.readme.io/docs/loops-and-guarding)
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# hooks-guard-called
|
# hooks-guard-called
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
[Read more](https://xrpl-hooks.readme.io/v2.0/docs/loops-and-guarding)
|
[Read more](https://xrpl-hooks.readme.io/docs/loops-and-guarding)
|
||||||
|
|||||||
@@ -1,14 +1,35 @@
|
|||||||
# hooks-guard-in-for
|
# hooks-guard-in-for
|
||||||
|
|
||||||
A guard is a marker that must be placed in your code at the top of each loop. Consider the following for-loop in C:
|
Consider the following for-loop in C:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
#define GUARD(maxiter) _g(__LINE__, (maxiter)+1)
|
#define GUARD(maxiter) _g(__LINE__, (maxiter)+1)
|
||||||
|
for (int i = 0; GUARD(3), i < 3; ++i)
|
||||||
for (int i = 0; GUARD(3), i < 3; ++i)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<BR/>
|
To satisfy the guard rule when using a for-loop in C guard should be
|
||||||
This is the only way to satisfy the guard rule when using a for-loop in C.
|
placed either in the condition part of the loop, or as a first call in loop body, e.g.
|
||||||
|
|
||||||
[Read more](https://xrpl-hooks.readme.io/v2.0/docs/loops-and-guarding)
|
```c
|
||||||
|
for(int i = 0; i < 3; ++i) {
|
||||||
|
GUARD(3);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In case of nested loops, the guard limit value should be
|
||||||
|
multiplied by a number of iterations in each loop, e.g.
|
||||||
|
|
||||||
|
```c
|
||||||
|
for(int i = 0; GUARD(3), i < 3; ++i) {
|
||||||
|
for (int j = 0; GUARD(17), j < 5; ++j)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
(most descendant loop iterations + 1) * (each parent loops iterations) - 1
|
||||||
|
```
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
[Read more](https://xrpl-hooks.readme.io/docs/loops-and-guarding)
|
||||||
|
|||||||
@@ -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/v2.0/docs/loops-and-guarding)
|
[Read more](https://xrpl-hooks.readme.io/docs/loops-and-guarding)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# hooks-hash-buf-len
|
# hooks-hash-buf-len
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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).
|
||||||
|
|||||||
@@ -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/v2.0/reference/util_keylet) requires valid parameters dependent on the keylet type.
|
Computing a ripple keylet by calling [util_keylet](https://xrpl-hooks.readme.io/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/v2.0/docs/slots-and-keylets)
|
[Read more](https://xrpl-hooks.readme.io/docs/slots-and-keylets)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# hooks-param-buf-len
|
# hooks-param-buf-len
|
||||||
|
|
||||||
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.
|
Function [hook_param](https://xrpl-hooks.readme.io/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/v2.0/docs/parameters)
|
[Read more](https://xrpl-hooks.readme.io/docs/parameters)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# hooks-param-set-buf-len
|
# hooks-param-set-buf-len
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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/v2.0/docs/parameters)
|
[Read more](https://xrpl-hooks.readme.io/docs/parameters)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# hooks-raddr-conv-buf-len
|
# hooks-raddr-conv-buf-len
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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).
|
||||||
|
|||||||
@@ -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/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.
|
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.
|
||||||
|
|
||||||
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).
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# hooks-reserve-limit
|
# hooks-reserve-limit
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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/v2.0/docs/emitted-transactions)
|
[Read more](https://xrpl-hooks.readme.io/docs/emitted-transactions)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# hooks-skip-hash-buf-len
|
# hooks-skip-hash-buf-len
|
||||||
|
|
||||||
Function [hook_skip](https://xrpl-hooks.readme.io/v2.0/reference/hook_skip) has fixed-size canonical hash input.
|
Function [hook_skip](https://xrpl-hooks.readme.io/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).
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# hooks-slot-hash-buf-len
|
# hooks-slot-hash-buf-len
|
||||||
|
|
||||||
Function [slot_id](https://xrpl-hooks.readme.io/v2.0/reference/slot_id) has fixed-size canonical hash output.
|
Function [slot_id](https://xrpl-hooks.readme.io/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/v2.0/docs/slots-and-keylets)
|
[Read more](https://xrpl-hooks.readme.io/docs/slots-and-keylets)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# hooks-slot-keylet-buf-len
|
# hooks-slot-keylet-buf-len
|
||||||
|
|
||||||
Function [slot_set](https://xrpl-hooks.readme.io/v2.0/reference/slot_set) has structured keylet input.
|
Function [slot_set](https://xrpl-hooks.readme.io/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/v2.0/docs/slots-and-keylets)
|
[Read more](https://xrpl-hooks.readme.io/docs/slots-and-keylets)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# hooks-slot-limit
|
# hooks-slot-limit
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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/v2.0/docs/slots-and-keylets)
|
[Read more](https://xrpl-hooks.readme.io/docs/slots-and-keylets)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# hooks-slot-sub-limit
|
# hooks-slot-sub-limit
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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/v2.0/docs/slots-and-keylets)
|
[Read more](https://xrpl-hooks.readme.io/docs/slots-and-keylets)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# hooks-slot-type-limit
|
# hooks-slot-type-limit
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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/v2.0/docs/slots-and-keylets)
|
[Read more](https://xrpl-hooks.readme.io/docs/slots-and-keylets)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# hooks-state-buf-len
|
# hooks-state-buf-len
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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/v2.0/docs/state-management)
|
[Read more](https://xrpl-hooks.readme.io/docs/state-management)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# hooks-transaction-hash-buf-len
|
# hooks-transaction-hash-buf-len
|
||||||
|
|
||||||
Function [otxn_id](https://xrpl-hooks.readme.io/v2.0/reference/otxn_id) has fixed-size canonical hash output.
|
Function [otxn_id](https://xrpl-hooks.readme.io/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).
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# hooks-transaction-slot-limit
|
# hooks-transaction-slot-limit
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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/v2.0/docs/slots-and-keylets)
|
[Read more](https://xrpl-hooks.readme.io/docs/slots-and-keylets)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# hooks-trivial-cbak
|
# hooks-trivial-cbak
|
||||||
|
|
||||||
A Hook may implement and export a [cbak](https://xrpl-hooks.readme.io/v2.0/reference/cbak) function.
|
A Hook may implement and export a [cbak](https://xrpl-hooks.readme.io/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/v2.0/docs/compiling-hooks)
|
[Read more](https://xrpl-hooks.readme.io/docs/compiling-hooks)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# hooks-validate-buf-len
|
# hooks-validate-buf-len
|
||||||
|
|
||||||
Hook API [sto_validate](https://xrpl-hooks.readme.io/v2.0/reference/sto_validate) requires non-empty input buffer.
|
Hook API [sto_validate](https://xrpl-hooks.readme.io/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/v2.0/docs/serialized-objects)
|
[Read more](https://xrpl-hooks.readme.io/docs/serialized-objects)
|
||||||
|
|||||||
@@ -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/v2.0/reference/util_verify) requires valid public key & data signature.
|
Verifying a cryptographic signature by calling [util_verify](https://xrpl-hooks.readme.io/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).
|
||||||
|
|||||||
Reference in New Issue
Block a user