Compare commits

..

6 Commits

Author SHA1 Message Date
Valtteri Karesto
775ae3de9b Add new asc compile logic 2022-08-17 21:06:24 +03:00
Valtteri Karesto
6fa68b9d6b Update state type 2022-08-17 21:06:07 +03:00
Valtteri Karesto
b7c48c81a4 conditional rendering based on file type 2022-08-17 21:06:01 +03:00
Valtteri Karesto
33019835ea Add some extra definitions to monaco typescript 2022-08-17 21:05:46 +03:00
Valtteri Karesto
fbc9a038ef Add asc dependency 2022-08-17 21:05:24 +03:00
Valtteri Karesto
318633c5e1 Add support for asc on project level 2022-08-17 21:05:13 +03:00
27 changed files with 298 additions and 958 deletions

View File

@@ -5,7 +5,6 @@ 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> {
@@ -100,11 +99,6 @@ 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)
@@ -140,6 +134,11 @@ 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} />
)
@@ -158,11 +157,9 @@ 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 = ref(<EnrichLog str={_message} />)
const message = !extracted ? msg : msg.slice(0, extracted.start) + msg.slice(extracted.end + 1)
const _jsonData = extracted ? JSON.stringify(extracted.result, null, 2) : undefined
const jsonData = _jsonData ? ref(<EnrichLog str={_jsonData} />) : undefined
const jsonData = extracted ? JSON.stringify(extracted.result, null, 2) : undefined
if (extracted?.result?.id?._Request?.includes('hooks-builder-req')) {
return

View File

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

@@ -7,6 +7,7 @@ import { useRouter } from 'next/router'
import Box from './Box'
import Container from './Container'
import asc from 'assemblyscript/dist/asc'
import { createNewFile, saveFile } from '../state/actions'
import { apiHeaderFiles } from '../state/constants'
import state from '../state'
@@ -200,15 +201,20 @@ const HooksEditor = () => {
defaultValue={file?.content}
// onChange={val => (state.files[snap.active].content = val)} // Auto save?
beforeMount={monaco => {
// if (!snap.editorCtx) {
// snap.files.forEach(file =>
// monaco.editor.createModel(
// file.content,
// file.language,
// monaco.Uri.parse(`file:///work/c/${file.name}`)
// )
// )
// }
if (!snap.editorCtx) {
snap.files.forEach(file =>
monaco.editor.createModel(
file.content,
file.language,
monaco.Uri.parse(`file:///work/c/${file.name}`)
)
)
}
monaco.languages.typescript.typescriptDefaults.addExtraLib(
asc.definitionFiles.assembly,
'assemblyscript/std/assembly/index.d.ts'
)
// create the web socket
if (!subscriptionRef.current) {
@@ -218,11 +224,6 @@ const HooksEditor = () => {
aliases: ['C', 'c', 'H', 'h'],
mimetypes: ['text/plain']
})
monaco.languages.register({
id: 'text',
extensions: ['.txt'],
mimetypes: ['text/plain'],
})
MonacoServices.install(monaco)
const webSocket = createWebSocket(
process.env.NEXT_PUBLIC_LANGUAGE_SERVER_API_ENDPOINT || ''

View File

@@ -1,12 +1,15 @@
import { useRef, useLayoutEffect, ReactNode, FC, useState } from 'react'
import { useRef, useLayoutEffect, ReactNode, FC, useState, useCallback } 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 { ILog } from '../state'
import state, { 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
@@ -140,25 +143,70 @@ 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>
@@ -171,6 +219,7 @@ export const Log: FC<ILog> = ({
)}
{expanded && jsonData && <Pre block>{jsonData}</Pre>}
</LogText>
<br />
</>
)
}

View File

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

@@ -11,7 +11,7 @@ import {
} from './Dialog'
import { Plus, X } from 'phosphor-react'
import { styled } from '../stitches.config'
import { capitalize, getFileExtention } from '../utils/helpers'
import { capitalize } from '../utils/helpers'
import ContextMenu, { ContentMenuOption } from './ContextMenu'
const ErrorText = styled(Text, {
@@ -97,7 +97,7 @@ export const Tabs = ({
if (!tabname) {
return { error: `Please enter ${label.toLocaleLowerCase()} name.` }
}
let ext = getFileExtention(tabname)
let ext = (tabname.includes('.') && tabname.split('.').pop()) || ''
if (!ext && defaultExtension) {
ext = defaultExtension
@@ -109,7 +109,7 @@ export const Tabs = ({
if (extensionRequired && !ext) {
return { error: 'File extension is required!' }
}
if (allowedExtensions && ext && !allowedExtensions.includes(ext)) {
if (allowedExtensions && !allowedExtensions.includes(ext)) {
return { error: 'This file extension is not allowed!' }
}
if (headerExtraValidation && !tabname.match(headerExtraValidation.regex)) {

View File

@@ -72,12 +72,7 @@ const Tooltip: React.FC<React.ComponentProps<typeof StyledContent> & ITooltip> =
...rest
}) => {
return (
<TooltipPrimitive.Root
open={open}
defaultOpen={defaultOpen}
onOpenChange={onOpenChange}
delayDuration={100}
>
<TooltipPrimitive.Root open={open} defaultOpen={defaultOpen} onOpenChange={onOpenChange}>
<TooltipPrimitive.Trigger asChild>{children}</TooltipPrimitive.Trigger>
<StyledContent side="bottom" align="center" {...rest}>
<div dangerouslySetInnerHTML={{ __html: content }} />

View File

@@ -19,7 +19,6 @@ import { TxJson } from './json'
import { TxUI } from './ui'
import { default as _estimateFee } from '../../utils/estimateFee'
import toast from 'react-hot-toast'
import { combineFlags, extractFlags, transactionFlags } from '../../state/constants/flags'
export interface TransactionProps {
header: string
@@ -40,17 +39,14 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
const prepareOptions = useCallback(
(state: Partial<TransactionState> = txState) => {
const { selectedTransaction, selectedDestAccount, selectedAccount, txFields, selectedFlags } =
state
const { selectedTransaction, selectedDestAccount, selectedAccount, txFields } = state
const TransactionType = selectedTransaction?.value || null
const Destination = selectedDestAccount?.value || txFields?.Destination
const Account = selectedAccount?.value || null
const Flags = combineFlags(selectedFlags?.map(flag => flag.value)) || txFields?.Flags
return prepareTransaction({
...txFields,
Flags,
TransactionType,
Destination,
Account
@@ -140,13 +136,8 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
} else {
fields.Destination = undefined
}
if (transactionType?.value && transactionFlags[transactionType?.value] && fields.Flags) {
nwState.selectedFlags = extractFlags(transactionType.value, fields.Flags)
fields.Flags = undefined
}
nwState.txFields = fields
const state = modifyTxState(header, nwState, { replaceState: true })
const editorValue = getJsonString(state)
return setState({ editorValue })
@@ -188,12 +179,7 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
estimateFee={estimateFee}
/>
) : (
<TxUI
state={txState}
resetState={resetState}
setState={setState}
estimateFee={estimateFee}
/>
<TxUI state={txState} setState={setState} estimateFee={estimateFee} />
)}
<Flex
row

View File

@@ -17,19 +17,16 @@ import state from '../../state'
import { streamState } from '../DebugStream'
import { Button } from '..'
import Textarea from '../Textarea'
import { getFlags } from '../../state/constants/flags'
interface UIProps {
setState: (pTx?: Partial<TransactionState> | undefined) => TransactionState | undefined
resetState: (tt?: SelectOption) => TransactionState | undefined
state: TransactionState
estimateFee?: (...arg: any) => Promise<string | undefined>
}
export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estimateFee }) => {
export const TxUI: FC<UIProps> = ({ state: txState, setState, estimateFee }) => {
const { accounts } = useSnapshot(state)
const { selectedAccount, selectedDestAccount, selectedTransaction, txFields, selectedFlags } =
txState
const { selectedAccount, selectedDestAccount, selectedTransaction, txFields } = txState
const accountOptions: SelectOption[] = accounts.map(acc => ({
label: acc.name,
@@ -43,15 +40,23 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estima
}))
.filter(acc => acc.value !== selectedAccount?.value)
const flagsOptions: SelectOption[] = Object.entries(
getFlags(selectedTransaction?.value) || {}
).map(([label, value]) => ({
label,
value
}))
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) => {
setState({ selectedAccount: acc })
streamState.selectedAccount = acc
@@ -87,11 +92,11 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estima
(tt: SelectOption) => {
setState({ selectedTransaction: tt })
const newState = resetState(tt)
const newState = resetFields(tt.value)
handleEstimateFee(newState, true)
},
[handleEstimateFee, resetState, setState]
[handleEstimateFee, resetFields, setState]
)
const switchToJson = () => setState({ viewType: 'json' })
@@ -110,16 +115,14 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estima
[selectedTransaction?.value]
)
const richFields = ['TransactionType', 'Account']
const specialFields = ['TransactionType', 'Account']
if (fields.Destination !== undefined) {
richFields.push('Destination')
specialFields.push('Destination')
}
if (flagsOptions.length) {
richFields.push('Flags')
}
const otherFields = Object.keys(txFields).filter(k => !richFields.includes(k)) as [keyof TxFields]
const otherFields = Object.keys(txFields).filter(k => !specialFields.includes(k)) as [
keyof TxFields
]
return (
<Container
@@ -176,32 +179,7 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estima
onChange={(acc: any) => handleSetAccount(acc)} // TODO make react-select have correct types for acc
/>
</Flex>
{richFields.includes('Destination') && (
<Flex
row
fluid
css={{
justifyContent: 'flex-end',
alignItems: 'center',
mb: '$3',
pr: '1px'
}}
>
<Text muted css={{ mr: '$3', textAlign: 'end' }}>
Destination account:{' '}
</Text>
<Select
instanceId="to-account"
placeholder="Select the destination account"
css={{ width: '70%' }}
options={destAccountOptions}
value={selectedDestAccount}
isClearable
onChange={(acc: any) => setState({ selectedDestAccount: acc })}
/>
</Flex>
)}
{richFields.includes('Flags') && (
{fields.Destination !== undefined && (
<Flex
row
fluid
@@ -213,21 +191,16 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState, resetState, estima
}}
>
<Text muted css={{ mr: '$3' }}>
Flags:{' '}
Destination account:{' '}
</Text>
<Select
isClearable
instanceId="to-account"
placeholder="Select the destination account"
css={{ width: '70%' }}
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
}
options={destAccountOptions}
value={selectedDestAccount}
isClearable
onChange={(acc: any) => setState({ selectedDestAccount: acc })}
/>
</Flex>
)}

View File

@@ -1,409 +0,0 @@
[
{
"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

@@ -6,7 +6,7 @@
"DestinationTag": 13,
"Fee": "2000000",
"Sequence": 2470665,
"Flags": "2147483648"
"Flags": 2147483648
},
{
"TransactionType": "AccountSet",
@@ -48,7 +48,7 @@
"Account": "rsUiUMpnrgxQp24dJYZDhmV4bE3aBtQyt8",
"Authorize": "rEhxGqkqPPSxQ3P25J66ft5TwpzV14k2de",
"Fee": "10",
"Flags": "2147483648",
"Flags": 2147483648,
"Sequence": 2
},
{
@@ -109,9 +109,7 @@
"Fee": "10",
"NFTokenOffers": {
"$type": "json",
"$value": [
"4AAAEEA76E3C8148473CB3840CE637676E561FB02BD4CA22CA59729EA815B862"
]
"$value": ["4AAAEEA76E3C8148473CB3840CE637676E561FB02BD4CA22CA59729EA815B862"]
}
},
{
@@ -122,7 +120,7 @@
"$value": "100",
"$type": "xrp"
},
"Flags": "1",
"Flags": 1,
"Destination": "",
"Fee": "10"
},
@@ -130,7 +128,7 @@
"TransactionType": "OfferCancel",
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"Fee": "12",
"Flags": "0",
"Flags": 0,
"LastLedgerSequence": 7108629,
"OfferSequence": 6,
"Sequence": 7
@@ -139,7 +137,7 @@
"TransactionType": "OfferCreate",
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"Fee": "12",
"Flags": "0",
"Flags": 0,
"LastLedgerSequence": 7108682,
"Sequence": 8,
"TakerGets": "6000000",
@@ -157,7 +155,7 @@
"$type": "xrp"
},
"Fee": "12",
"Flags": "2147483648",
"Flags": 2147483648,
"Sequence": 2
},
{
@@ -187,14 +185,14 @@
"Fee": "10"
},
{
"Flags": "0",
"Flags": 0,
"TransactionType": "SetRegularKey",
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Fee": "12",
"RegularKey": "rAR8rR8sUkBoCZFawhkWzY4Y5YoyuznwD"
},
{
"Flags": "0",
"Flags": 0,
"TransactionType": "SignerListSet",
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Fee": "12",
@@ -234,7 +232,7 @@
"TransactionType": "TrustSet",
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"Fee": "12",
"Flags": "262144",
"Flags": 262144,
"LastLedgerSequence": 8007750,
"LimitAmount": {
"$type": "json",
@@ -246,4 +244,4 @@
},
"Sequence": 12
}
]
]

View File

@@ -8,8 +8,16 @@ module.exports = {
config.resolve.alias['vscode'] = require.resolve(
'@codingame/monaco-languageclient/lib/vscode-compatibility'
)
config.experiments = {
topLevelAwait: true,
layers: true
}
if (!isServer) {
config.resolve.fallback.fs = false
config.resolve.fallback = {
...config.resolve.fallback,
fs: false,
module: false
}
}
config.module.rules.push({
test: /\.md$/,

View File

@@ -26,6 +26,7 @@
"@radix-ui/react-switch": "^0.1.5",
"@radix-ui/react-tooltip": "^0.1.7",
"@stitches/react": "^1.2.8",
"assemblyscript": "^0.20.19",
"base64-js": "^1.5.1",
"comment-parser": "^1.3.1",
"dinero.js": "^1.9.1",
@@ -64,7 +65,7 @@
"valtio": "^1.2.5",
"vscode-languageserver": "^7.0.0",
"vscode-uri": "^3.0.2",
"wabt": "^1.0.30",
"wabt": "1.0.16",
"xrpl-accountlib": "^1.5.2",
"xrpl-client": "^1.9.4"
},

View File

@@ -10,11 +10,10 @@ import Box from '../../components/Box'
import Button from '../../components/Button'
import Popover from '../../components/Popover'
import RunScript from '../../components/RunScript'
import state, { IFile } from '../../state'
import state from '../../state'
import { compileCode } from '../../state/actions'
import { getSplit, saveSplit } from '../../state/actions/persistSplits'
import { styled } from '../../stitches.config'
import { getFileExtention } from '../../utils/helpers'
const HooksEditor = dynamic(() => import('../../components/HooksEditor'), {
ssr: false
@@ -148,9 +147,6 @@ const CompilerSettings = () => {
const Home: NextPage = () => {
const snap = useSnapshot(state)
const activeFile = snap.files[snap.active] as IFile | undefined
const activeFileExt = getFileExtention(activeFile?.name)
const canCompile = activeFileExt === 'c' || activeFileExt === 'wat'
return (
<Split
direction="vertical"
@@ -163,7 +159,8 @@ const Home: NextPage = () => {
>
<main style={{ display: 'flex', flex: 1, position: 'relative' }}>
<HooksEditor />
{canCompile && (
{(snap.files[snap.active]?.name?.split('.')?.[1]?.toLowerCase() === 'c' ||
snap.files[snap.active]?.name?.split('.')?.[1]?.toLowerCase() === 'ts') && (
<Hotkeys
keyName="command+b,ctrl+b"
onKeyDown={() => !snap.compiling && snap.files.length && compileCode(snap.active)}
@@ -189,15 +186,17 @@ const Home: NextPage = () => {
<Play weight="bold" size="16px" />
Compile to Wasm
</Button>
<Popover content={<CompilerSettings />}>
<Button variant="primary" css={{ px: '10px' }}>
<Gear size="16px" />
</Button>
</Popover>
{snap.files[snap.active].language === 'c' && (
<Popover content={<CompilerSettings />}>
<Button variant="primary" css={{ px: '10px' }}>
<Gear size="16px" />
</Button>
</Popover>
)}
</Flex>
</Hotkeys>
)}
{activeFileExt === 'js' && (
{snap.files[snap.active]?.name?.split('.')?.[1]?.toLowerCase() === 'js' && (
<Hotkeys
keyName="command+b,ctrl+b"
onKeyDown={() => !snap.compiling && snap.files.length && compileCode(snap.active)}
@@ -213,7 +212,7 @@ const Home: NextPage = () => {
gap: '$2'
}}
>
<RunScript file={activeFile as IFile} />
<RunScript file={snap.files[snap.active]} />
</Flex>
</Hotkeys>
)}
@@ -229,7 +228,7 @@ const Home: NextPage = () => {
>
<LogBox title="Development Log" clearLog={() => (state.logs = [])} logs={snap.logs} />
</Flex>
{activeFileExt === 'js' && (
{snap.files[snap.active]?.name?.split('.')?.[1]?.toLowerCase() === 'js' && (
<Flex
css={{
flex: 1

View File

@@ -13,13 +13,9 @@ import { ref } from 'valtio'
* out of it and store both in global state.
*/
export const compileCode = async (activeId: number) => {
let asc: typeof import('assemblyscript/dist/asc') | undefined
// Save the file to global state
saveFile(false, activeId)
const file = state.files[activeId]
if (file.name.endsWith('.wat')) {
return compileWat(activeId)
}
if (!process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT) {
throw Error('Missing env!')
}
@@ -30,7 +26,87 @@ export const compileCode = async (activeId: number) => {
}
// Set loading state to true
state.compiling = true
if (typeof window !== 'undefined') {
// IF AssemblyScript
if (
state.files[activeId].language.toLowerCase() === 'ts' ||
state.files[activeId].language.toLowerCase() === 'typescript'
) {
if (!asc) {
asc = await import('assemblyscript/dist/asc')
}
const files: { [key: string]: string } = {}
state.files.forEach(file => {
files[file.name] = file.content
})
const res = await asc.main(
[
state.files[activeId].name,
'--textFile',
'-o',
state.files[activeId].name,
'--runtime',
'minimal',
'-O3'
],
{
readFile: (name, baseDir) => {
console.log('--> ', name)
const currentFile = state.files.find(file => file.name === name)
if (currentFile) {
return currentFile.content
}
return null
},
writeFile: (name, data, baseDir) => {
console.log(name)
const curr = state.files.find(file => file.name === name)
if (curr) {
curr.compiledContent = ref(data)
}
},
listFiles: (dirname, baseDir) => {
console.log('listFiles: ' + dirname + ', baseDir=' + baseDir)
return []
}
}
)
// In case you want to compile just single file
// const res = await asc.compileString(state.files[activeId].content, {
// optimizeLevel: 3,
// runtime: 'stub',
// })
if (res.error?.message) {
state.compiling = false
state.logs.push({
type: 'error',
message: res.error.message
})
state.logs.push({
type: 'error',
message: res.stderr.toString()
})
return
}
if (res.stdout) {
const wat = res.stdout.toString()
state.files[activeId].lastCompiled = new Date()
state.files[activeId].compiledWatContent = wat
state.files[activeId].compiledValueSnapshot = state.files[activeId].content
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'
})
}
state.compiling = false
return
}
}
state.logs = []
const file = state.files[activeId]
try {
file.containsErrors = false
let res: Response
@@ -76,7 +152,7 @@ export const compileCode = async (activeId: number) => {
// Import wabt from and create human readable version of wasm file and
// put it into state
const ww = await (await import('wabt')).default()
const ww = (await import('wabt')).default()
const myModule = ww.readWasm(new Uint8Array(bufferData), {
readDebugNames: true
})
@@ -126,46 +202,3 @@ export const compileCode = async (activeId: number) => {
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
}

View File

@@ -1,19 +1,21 @@
import { getFileExtention } from '../../utils/helpers'
import state, { IFile } from '../index'
const languageMapping: Record<string, string | undefined> = {
const languageMapping = {
ts: 'typescript',
js: 'javascript',
md: 'markdown',
c: 'c',
h: 'c',
txt: 'text'
}
other: ''
} /* Initializes empty file to global state */
export const createNewFile = (name: string) => {
const ext = getFileExtention(name) || ''
const emptyFile: IFile = { name, language: languageMapping[ext] || 'text', content: '' }
const tempName = name.split('.')
const fileExt = tempName[tempName.length - 1] || 'other'
const emptyFile: IFile = {
name,
language: languageMapping[fileExt as 'ts' | 'js' | 'md' | 'c' | 'h' | 'other'],
content: ''
}
state.files.push(emptyFile)
state.active = state.files.length - 1
}
@@ -22,8 +24,5 @@ export const renameFile = (oldName: string, nwName: string) => {
const file = state.files.find(file => file.name === oldName)
if (!file) throw Error(`No file exists with name ${oldName}`)
const ext = getFileExtention(nwName) || ''
const language = languageMapping[ext] || 'text'
file.name = nwName
file.language = language
}

View File

@@ -7,7 +7,6 @@ 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)
@@ -25,7 +24,7 @@ function toHex(str: string) {
return result.toUpperCase()
}
function arrayBufferToHex(arrayBuffer?: ArrayBuffer | null) {
function arrayBufferToHex(arrayBuffer?: ArrayBuffer | Uint8Array | null) {
if (!arrayBuffer) {
return ''
}
@@ -37,7 +36,7 @@ function arrayBufferToHex(arrayBuffer?: ArrayBuffer | null) {
throw new TypeError('Expected input to be an ArrayBuffer')
}
var view = new Uint8Array(arrayBuffer)
var view = arrayBuffer instanceof Uint8Array ? arrayBuffer : new Uint8Array(arrayBuffer)
var result = ''
var value
@@ -145,25 +144,6 @@ 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',
@@ -171,17 +151,27 @@ export const deployHook = async (account: IAccount & { name?: string }, data: Se
})
state.deployLogs.push({
type: 'success',
message: resultMsg
})
} else if (submitRes.engine_result) {
state.deployLogs.push({
type: 'error',
message: resultMsg
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.error}] ${submitRes.error_exception}`
message: `[${submitRes.engine_result || submitRes.error}] ${
submitRes.engine_result_message || submitRes.error_exception
}`
})
}
} catch (err) {

View File

@@ -61,7 +61,6 @@ export const fetchFiles = async (gistId: string) => {
// default priority is undefined == 0
const extPriority: Record<string, number> = {
c: 3,
wat: 3,
md: 2,
h: -1
}

View File

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

@@ -1,79 +0,0 @@
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',
},
}
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
}

View File

@@ -14,7 +14,7 @@ export interface IFile {
language: string
content: string
compiledValueSnapshot?: string
compiledContent?: ArrayBuffer | null
compiledContent?: ArrayBuffer | Uint8Array | null
compiledWatContent?: string | null
lastCompiled?: Date
containsErrors?: boolean

View File

@@ -4,7 +4,6 @@ import transactionsData from '../content/transactions.json'
import state from '.'
import { showAlert } from '../state/actions/showAlert'
import { parseJSON } from '../utils/json'
import { extractFlags, getFlags } from './constants/flags'
export type SelectOption = {
value: string
@@ -15,7 +14,6 @@ export interface TransactionState {
selectedTransaction: SelectOption | null
selectedAccount: SelectOption | null
selectedDestAccount: SelectOption | null
selectedFlags: SelectOption[] | null
txIsLoading: boolean
txIsDisabled: boolean
txFields: TxFields
@@ -33,7 +31,6 @@ export const defaultTransaction: TransactionState = {
selectedTransaction: null,
selectedAccount: null,
selectedDestAccount: null,
selectedFlags: null,
txIsLoading: false,
txIsDisabled: false,
txFields: {},
@@ -131,8 +128,9 @@ export const prepareTransaction = (data: any) => {
try {
options[field] = JSON.parse(_value.$value)
} catch (error) {
const message = `Input error for json field '${field}': ${error instanceof Error ? error.message : ''
}`
const message = `Input error for json field '${field}': ${
error instanceof Error ? error.message : ''
}`
console.error(message)
options[field] = _value.$value
}
@@ -207,13 +205,6 @@ export const prepareState = (value: string, transactionType?: string) => {
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 => {
const value = rest[field]
const schemaVal = schema[field as keyof TxFields]

View File

@@ -13,9 +13,3 @@ export const capitalize = (value?: string) => {
return value[0].toLocaleUpperCase() + value.slice(1)
}
export const getFileExtention = (filename?: string): string | undefined => {
if (!filename) return
const ext = (filename.includes('.') && filename.split('.').pop()) || undefined
return ext
}

View File

@@ -22,7 +22,6 @@ import hooksFloatManipPure from './md/hooks-float-manip-pure.md'
import hooksFloatOnePure from './md/hooks-float-one-pure.md'
import hooksFloatPure from './md/hooks-float-pure.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 hooksGuardInWhile from './md/hooks-guard-in-while.md'
import hooksHashBufLen from './md/hooks-hash-buf-len.md'
@@ -71,7 +70,6 @@ const docs: { [key: string]: string } = {
'hooks-float-one-pure': hooksFloatOnePure,
'hooks-float-pure': hooksFloatPure,
'hooks-guard-called': hooksGuardCalled,
'hooks-guard-call-non-const': hooksGuardCallNonConst,
'hooks-guard-in-for': hooksGuardInFor,
'hooks-guard-in-while': hooksGuardInWhile,
'hooks-hash-buf-len': hooksHashBufLen,

View File

@@ -1,6 +0,0 @@
# 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/v2.0/docs/loops-and-guarding)

View File

@@ -1,35 +1,14 @@
# hooks-guard-in-for
Consider the following for-loop in C:
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:
```c
#define GUARD(maxiter) _g(__LINE__, (maxiter)+1)
for (int i = 0; GUARD(3), i < 3; ++i)
#define GUARD(maxiter) _g(__LINE__, (maxiter)+1)
for (int i = 0; GUARD(3), i < 3; ++i)
```
To satisfy the guard rule when using a for-loop in C guard should be
placed either in the condition part of the loop, or as a first call in loop body, e.g.
```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.
<BR/>
This is the only way to satisfy the guard rule when using a for-loop in C.
[Read more](https://xrpl-hooks.readme.io/v2.0/docs/loops-and-guarding)

View File

@@ -1289,6 +1289,14 @@ array.prototype.flatmap@^1.2.5:
define-properties "^1.1.3"
es-abstract "^1.19.0"
assemblyscript@^0.20.19:
version "0.20.19"
resolved "https://registry.yarnpkg.com/assemblyscript/-/assemblyscript-0.20.19.tgz#7c29f506a31d526f7d7e3b22908cc8774b378681"
integrity sha512-IJN2CaAwCdRgTSZz3GiuYyXtXIjETBrlzky9ww9jFlEgH8i0pNFt6MAS3viogkofem+rcY6Fhr/clk+b6LWLBw==
dependencies:
binaryen "109.0.0-nightly.20220813"
long "^5.2.0"
assert@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz"
@@ -1392,6 +1400,11 @@ bignumber.js@^9.0.0:
resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz"
integrity sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw==
binaryen@109.0.0-nightly.20220813:
version "109.0.0-nightly.20220813"
resolved "https://registry.yarnpkg.com/binaryen/-/binaryen-109.0.0-nightly.20220813.tgz#6928acf8820fc1270d41dbb157c0c2337a067d20"
integrity sha512-u85Ti3LiGRrV0HqdNDRknalkx7QiCSL0jNsEsT522nnZacWxUaSJEILphxc2OnzmHVtdiWBE3c4lEzlU3ZEyDw==
bindings@^1.3.0:
version "1.5.0"
resolved "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz"
@@ -2725,9 +2738,9 @@ javascript-time-ago@^2.3.11:
relative-time-format "^1.0.7"
jose@^4.1.4, jose@^4.3.7:
version "4.10.0"
resolved "https://registry.yarnpkg.com/jose/-/jose-4.10.0.tgz#2e0b7bcc80dd0775f8a4588e55beb9460c37d60a"
integrity sha512-KEhB/eLGLomWGPTb+/RNbYsTjIyx03JmbqAyIyiXBuNSa7CmNrJd5ysFhblayzs/e/vbOPMUaLnjHUMhGp4yLw==
version "4.6.0"
resolved "https://registry.npmjs.org/jose/-/jose-4.6.0.tgz"
integrity sha512-0hNAkhMBNi4soKSAX4zYOFV+aqJlEz/4j4fregvasJzEVtjDChvWqRjPvHwLqr5hx28Ayr6bsOs1Kuj87V0O8w==
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
@@ -2960,6 +2973,11 @@ lodash@^4.17.15, lodash@^4.17.4:
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
long@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/long/-/long-5.2.0.tgz#2696dadf4b4da2ce3f6f6b89186085d94d52fd61"
integrity sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"
@@ -4765,10 +4783,10 @@ vscode-uri@^3.0.2:
resolved "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.3.tgz"
integrity sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA==
wabt@^1.0.30:
version "1.0.30"
resolved "https://registry.yarnpkg.com/wabt/-/wabt-1.0.30.tgz#23cff6d38a05d0718e298fda371a70f0dff38ad7"
integrity sha512-qM1QnttJhjZ4vTSuXvder43yxgGhVffT/0wMc0SwYpboEW0/ENISpei/2kIDEMPrnNfTQ4GdvD7JIFV0IJPYog==
wabt@1.0.16:
version "1.0.16"
resolved "https://registry.npmjs.org/wabt/-/wabt-1.0.16.tgz"
integrity sha512-aSQEAJfYkoZRY9Qt2/6hTM8yRX/Nc2R1bScET/4lppRqfUyzynB5HI+lK0u/hp8NbCVTAXg0iETviSS3zoufJw==
webidl-conversions@^3.0.0:
version "3.0.1"