Compare commits

...

10 Commits

Author SHA1 Message Date
Joni Juup
d3b0fef406 optimize editor navigation by removing shortcuts on mobile, more space to tabs 2022-08-26 16:22:44 +03:00
Joni Juup
4cbc316c62 small mobile style fixes 2022-08-26 16:03:46 +03:00
muzamil
2d9ca2674e Merge pull request #266 from XRPLF/feat/engine-code-links
Linkify engine error codes and some refactor.
2022-08-19 20:00:22 +05:30
muzam1l
221c727af6 Fix mistake in set-codes json. 2022-08-19 18:33:31 +05:30
muzam1l
3b0a8c44c9 Remove link title from log. 2022-08-19 17:46:02 +05:30
muzam1l
094f739b80 Merge branch 'main' into feat/engine-code-links 2022-08-19 15:07:44 +05:30
muzam1l
2d82966b3b Tooltip for SetHook codes in debug stream. 2022-08-16 18:16:49 +05:30
muzam1l
93c5ef231e Fix display of nullish values in script logs. 2022-08-09 17:18:22 +05:30
muzam1l
53c2104b94 Point groups of error codes to their individual pages. 2022-08-09 17:00:30 +05:30
muzam1l
c336ff8334 Linkify engine error codes and some refactor. 2022-08-09 02:07:15 +05:30
17 changed files with 794 additions and 123 deletions

View File

@@ -6,11 +6,14 @@ const ButtonGroup = styled('div', {
marginLeft: '1px',
[`& ${StyledButton}`]: {
marginLeft: '-1px',
px: '$4',
px: '0.65rem',
zIndex: 2,
position: 'relative',
'&:hover, &:focus': {
zIndex: 200
},
'@sm': {
px: '$4'
}
},
[`& ${StyledButton}:not(:only-of-type):not(:first-child):not(:last-child)`]: {

View File

@@ -5,6 +5,7 @@ import { subscribeKey } from 'valtio/utils'
import { Select } from '.'
import state, { ILog, transactionsState } from '../state'
import { extractJSON } from '../utils/json'
import EnrichLog from './EnrichLog'
import LogBox from './LogBox'
interface ISelect<T = string> {
@@ -99,6 +100,11 @@ const addListeners = (account: ISelect | null) => {
subscribeKey(streamState, 'selectedAccount', addListeners)
const clearLog = () => {
streamState.logs = []
streamState.statusChangeTimestamp = Date.now()
}
const DebugStream = () => {
const { selectedAccount, logs } = useSnapshot(streamState)
const { activeHeader: activeTxTab } = useSnapshot(transactionsState)
@@ -134,11 +140,6 @@ const DebugStream = () => {
streamState.selectedAccount = account
}, [activeTxTab])
const clearLog = () => {
streamState.logs = []
streamState.statusChangeTimestamp = Date.now()
}
return (
<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 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')) {
return

View File

@@ -40,11 +40,10 @@ const StyledContent = styled(DialogPrimitive.Content, {
color: '$mauve12',
borderRadius: '$md',
position: 'relative',
mb: '15%',
mb: '0%',
boxShadow: '0px 10px 38px -5px rgba(22, 23, 24, 0.25), 0px 10px 20px -5px rgba(22, 23, 24, 0.2)',
width: '90vw',
maxWidth: '450px',
// maxHeight: "85vh",
padding: 25,
'@media (prefers-reduced-motion: no-preference)': {
animation: `${contentShow} 150ms cubic-bezier(0.16, 1, 0.3, 1)`
@@ -53,6 +52,9 @@ const StyledContent = styled(DialogPrimitive.Content, {
'.dark &': {
backgroundColor: '$mauve5',
boxShadow: '0px 10px 38px 0px rgba(0, 0, 0, 0.85), 0px 10px 20px 0px rgba(0, 0, 0, 0.6)'
},
'@md': {
mb: '8%'
}
})

View File

@@ -78,7 +78,6 @@ const EditorNavigation = ({ renderNav }: { renderNav?: () => ReactNode }) => {
return (
<Flex css={{ flexShrink: 0, gap: '$0' }}>
<Flex
id="kissa"
ref={scrollRef}
css={{
overflowX: 'scroll',
@@ -181,7 +180,21 @@ const EditorNavigation = ({ renderNav }: { renderNav?: () => ReactNode }) => {
</DropdownMenuContent>
</DropdownMenu>
) : (
<Button outline size="sm" css={{ mr: '$3' }} onClick={() => setPopUp(true)}>
<Button
outline
size="sm"
css={{
mr: '-1px',
borderTopRightRadius: 0,
borderBottomRightRadius: 0,
'@sm': {
borderTopRightRadius: '$sm',
borderBottomRightRadius: '$sm',
mr: '$3'
}
}}
onClick={() => setPopUp(true)}
>
<GithubLogo size="16px" /> Login
</Button>
)}
@@ -202,6 +215,9 @@ const EditorNavigation = ({ renderNav }: { renderNav?: () => ReactNode }) => {
alignSelf: 'flex-start',
boxShadow: 'none'
},
'button:not(:last-child)': {
display: 'none'
},
'button:not(:first-child):not(:last-child)': {
borderRight: 0,
borderLeft: 0
@@ -217,6 +233,11 @@ const EditorNavigation = ({ renderNav }: { renderNav?: () => ReactNode }) => {
'&:hover': {
boxShadow: 'inset 0px 0px 0px 1px $colors$mauve12'
}
},
'@sm': {
'button:not(:last-child)': {
display: 'flex'
}
}
}}
>

73
components/EnrichLog.tsx Normal file
View 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

View File

@@ -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 useStayScrolled from 'react-stay-scrolled'
import NextLink from 'next/link'
import Container from './Container'
import LogText from './LogText'
import state, { ILog } from '../state'
import { ILog } from '../state'
import { Pre, Link, Heading, Button, Text, Flex, Box } from '.'
import regexifyString from 'regexify-string'
import { useSnapshot } from 'valtio'
import { AccountDialog } from './Accounts'
interface ILogBox {
title: string
@@ -143,70 +140,25 @@ const LogBox: FC<ILogBox> = ({
export const Log: FC<ILog> = ({
type,
timestring,
message: _message,
message,
link,
linkText,
defaultCollapsed,
jsonData: _jsonData
jsonData
}) => {
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 (
<>
<AccountDialog
setActiveAccountAddress={setDialogAccount}
activeAccountAddress={dialogAccount}
/>
<LogText variant={type}>
{timestring && (
<Text muted monospace>
{timestring}{' '}
</Text>
)}
<Pre>{message} </Pre>
<Pre>{message}</Pre>
{link && (
<NextLink href={link} shallow passHref>
<Link as="a">{linkText}</Link>
@@ -219,7 +171,6 @@ export const Log: FC<ILog> = ({
)}
{expanded && jsonData && <Pre block>{jsonData}</Pre>}
</LogText>
<br />
</>
)
}

View File

@@ -33,7 +33,7 @@ import { styled } from '../stitches.config'
const ImageWrapper = styled(Flex, {
position: 'relative',
mt: '$2',
mb: '$10',
mb: '$6',
svg: {
// fill: "red",
'.angle': {
@@ -66,16 +66,23 @@ const Navigation = () => {
<Container
css={{
display: 'flex',
alignItems: 'center'
alignItems: 'center',
px: '$3',
'@sm': {
px: '$4'
}
}}
>
<Flex
css={{
flex: 1,
alignItems: 'center',
borderRight: '1px solid $colors$mauve6',
py: '$3',
pr: '$4'
pr: '$0',
'@sm': {
borderRight: '1px solid $colors$mauve6',
pr: '$4'
}
}}
>
<Link href={gistId ? `/develop/${gistId}` : '/develop'} passHref>
@@ -84,7 +91,11 @@ const Navigation = () => {
css={{
display: 'flex',
alignItems: 'center',
color: '$textColor'
color: '$textColor',
mr: '$2',
'@sm': {
mr: '$4'
}
}}
>
<Logo width="32px" height="32px" />
@@ -92,9 +103,12 @@ const Navigation = () => {
</Link>
<Flex
css={{
ml: '$5',
flexDirection: 'column',
gap: '1px'
gap: '1px',
display: 'none',
'@md': {
display: 'flex'
}
}}
>
{snap.loading ? (
@@ -135,8 +149,8 @@ const Navigation = () => {
css={{
display: 'flex',
maxWidth: '1080px',
width: '80vw',
maxHeight: '80%',
width: '90vw',
maxHeight: '90%',
backgroundColor: '$mauve1 !important',
overflowY: 'auto',
background: 'black',
@@ -265,15 +279,18 @@ const Navigation = () => {
gridTemplateColumns: '1fr',
gridTemplateRows: 'max-content',
flex: 1,
p: '$7',
pb: '$16',
p: '$4',
pb: '$8',
gap: '$3',
alignItems: 'normal',
flexWrap: 'wrap',
backgroundColor: '$mauve1',
'@md': {
gridTemplateColumns: '1fr 1fr',
gridTemplateRows: 'max-content'
gridTemplateRows: 'max-content',
p: '$7',
pb: '$10',
paddingRight: '$12'
},
'@lg': {
gridTemplateColumns: '1fr 1fr 1fr',
@@ -316,23 +333,30 @@ const Navigation = () => {
<Flex
css={{
flexWrap: 'nowrap',
marginLeft: '$4',
marginLeft: '$2',
overflowX: 'scroll',
'&::-webkit-scrollbar': {
height: 0,
background: 'transparent'
},
scrollbarColor: 'transparent',
scrollbarWidth: 'none'
scrollbarWidth: 'none',
'@sm': {
marginLeft: '$4'
}
}}
>
<Stack
css={{
ml: '$4',
gap: '$3',
ml: '$0',
gap: '$2',
flexWrap: 'nowrap',
alignItems: 'center',
marginLeft: 'auto'
marginLeft: 'auto',
'@sm': {
marginLeft: '$4',
gap: '$3'
}
}}
>
<ButtonGroup>

View File

@@ -7,7 +7,7 @@ const PanelBox = styled('div', {
flexDirection: 'column',
border: '1px solid $colors$mauve6',
backgroundColor: '$mauve2',
padding: '$3',
padding: '$5',
borderRadius: '$sm',
fontWeight: 'lighter',
height: 'auto',

24
components/ResultLink.tsx Normal file
View 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

View File

@@ -72,7 +72,12 @@ const Tooltip: React.FC<React.ComponentProps<typeof StyledContent> & ITooltip> =
...rest
}) => {
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>
<StyledContent side="bottom" align="center" {...rest}>
<div dangerouslySetInnerHTML={{ __html: content }} />

View File

@@ -185,11 +185,16 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
row
css={{
justifyContent: 'space-between',
position: 'absolute',
left: 0,
bottom: 0,
width: '100%',
mb: '$1'
mb: '$2',
mt: '$1',
'@md': {
position: 'absolute',
left: 0,
bottom: 0,
mt: '$0',
mb: '$2'
}
}}
>
<Button

View File

@@ -137,11 +137,14 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) =>
row
fluid
css={{
justifyContent: 'flex-end',
justifyContent: 'space-between',
alignItems: 'center',
mb: '$3',
mt: '1px',
pr: '1px'
pr: '1px',
'@xl': {
justifyContent: 'flex-end'
}
}}
>
<Text muted css={{ mr: '$3' }}>
@@ -152,7 +155,13 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) =>
placeholder="Select transaction type"
options={transactionsOptions}
hideSelectedOptions
css={{ width: '70%' }}
css={{
width: '60%',
minWidth: '200px',
'@lg': {
width: '70%'
}
}}
value={selectedTransaction}
onChange={(tt: any) => handleChangeTxType(tt)}
/>
@@ -161,10 +170,13 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) =>
row
fluid
css={{
justifyContent: 'flex-end',
justifyContent: 'space-between',
alignItems: 'center',
mb: '$3',
pr: '1px'
pr: '1px',
'@xl': {
justifyContent: 'flex-end'
}
}}
>
<Text muted css={{ mr: '$3' }}>
@@ -173,7 +185,13 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) =>
<Select
instanceId="from-account"
placeholder="Select your account"
css={{ width: '70%' }}
css={{
width: '60%',
minWidth: '200px',
'@lg': {
width: '70%'
}
}}
options={accountOptions}
value={selectedAccount}
onChange={(acc: any) => handleSetAccount(acc)} // TODO make react-select have correct types for acc
@@ -184,10 +202,13 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) =>
row
fluid
css={{
justifyContent: 'flex-end',
justifyContent: 'space-between',
alignItems: 'center',
mb: '$3',
pr: '1px'
pr: '1px',
'@xl': {
justifyContent: 'flex-end'
}
}}
>
<Text muted css={{ mr: '$3' }}>
@@ -196,7 +217,13 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) =>
<Select
instanceId="to-account"
placeholder="Select the destination account"
css={{ width: '70%' }}
css={{
width: '60%',
minWidth: '200px',
'@lg': {
width: '70%'
}
}}
options={destAccountOptions}
value={selectedDestAccount}
isClearable
@@ -229,9 +256,12 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) =>
row
fluid
css={{
justifyContent: 'flex-end',
justifyContent: 'space-between',
alignItems: 'center',
position: 'relative'
position: 'relative',
'@xl': {
justifyContent: 'flex-end'
}
}}
>
<Text muted css={{ mr: '$3' }}>
@@ -244,9 +274,13 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) =>
spellCheck={false}
onChange={switchToJson}
css={{
width: '70%',
width: '60%',
minWidth: '200px',
flex: 'inherit',
resize: 'vertical'
resize: 'vertical',
'@lg': {
width: '70%'
}
}}
/>
) : (
@@ -271,7 +305,8 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) =>
: undefined
}
css={{
width: '70%',
width: '60%',
minWidth: '200px',
flex: 'inherit',
'-moz-appearance': 'textfield',
'&::-webkit-outer-spin-button': {
@@ -281,6 +316,9 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) =>
'&::-webkit-inner-spin-button ': {
'-webkit-appearance': 'none',
margin: 0
},
'@lg': {
width: '70%'
}
}}
/>

409
content/hook-set-codes.json Normal file
View 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"
}
]

View File

@@ -68,6 +68,7 @@ const Test = () => {
justifyContent: 'center',
p: '$3 $2'
}}
className="split-mobile-forceAutoHeight"
>
<Split
direction="horizontal"
@@ -82,6 +83,7 @@ const Test = () => {
height: '100%'
}}
onDragEnd={e => saveSplit('testHorizontal', e)}
className="split-mobile-forceVertical"
>
<Box css={{ width: '55%', px: '$2' }}>
<Tabs
@@ -105,7 +107,18 @@ const Test = () => {
))}
</Tabs>
</Box>
<Box css={{ width: '45%', mx: '$2', height: '100%' }}>
<Box
css={{
width: '45%',
mx: '$0',
mt: '$1',
height: '100%',
'@md': {
mx: '$2',
mt: '$0'
}
}}
>
<Accounts card hideDeployBtn showHookStats />
</Box>
</Split>
@@ -131,6 +144,7 @@ const Test = () => {
<Flex>
<Split
direction="horizontal"
className="split-mobile-forceVertical"
sizes={[50, 50]}
minSize={[320, 160]}
gutterSize={4}

View File

@@ -7,6 +7,7 @@ import { Link } from '../../components'
import { ref } from 'valtio'
import estimateFee from '../../utils/estimateFee'
import { SetHookData } from '../../utils/setHook'
import ResultLink from '../../components/ResultLink'
export const sha256 = async (string: string) => {
const utf8 = new TextEncoder().encode(string)
@@ -144,6 +145,25 @@ export const deployHook = async (account: IAccount & { name?: string }, data: Se
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',
@@ -151,27 +171,17 @@ export const deployHook = async (account: IAccount & { name?: string }, data: Se
})
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}`,
message: resultMsg
})
} else if (submitRes.engine_result) {
state.deployLogs.push({
type: 'error',
message: resultMsg
})
} else {
state.deployLogs.push({
type: 'error',
message: `[${submitRes.engine_result || submitRes.error}] ${
submitRes.engine_result_message || submitRes.error_exception
}`
message: `[${submitRes.error}] ${submitRes.error_exception}`
})
}
} catch (err) {

View File

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

View File

@@ -54,3 +54,15 @@ html.light .gutter-horizontal:hover {
.monaco-editor .monaco-hover {
z-index: 9999;
}
@media screen and (max-width: 48rem) {
.split-mobile-forceAutoHeight {
height: auto !important;
}
.split-mobile-forceVertical {
flex-direction: column !important;
}
.split-mobile-forceVertical > div {
width: 100% !important;
}
}