Compare commits
1 Commits
feat/ts-su
...
fix/nft-cr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3340857575 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -23,7 +23,6 @@
|
|||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
.yarn
|
|
||||||
|
|
||||||
# local env files
|
# local env files
|
||||||
.env.local
|
.env.local
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ const DeployEditor = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{`You haven't compiled any files yet, compile files on `}
|
{`You haven't compiled any files yet, compile files on `}
|
||||||
<NextLink legacyBehavior shallow href={`/develop/${router.query.slug}`} passHref>
|
<NextLink shallow href={`/develop/${router.query.slug}`} passHref>
|
||||||
<Link as="a">develop view</Link>
|
<Link as="a">develop view</Link>
|
||||||
</NextLink>
|
</NextLink>
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -155,11 +155,9 @@ const EditorNavigation = ({ renderNav }: { renderNav?: () => ReactNode }) => {
|
|||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src={session?.user?.image || ''}
|
src={session?.user?.image || ''}
|
||||||
width="30"
|
width="30px"
|
||||||
height="30"
|
height="30px"
|
||||||
style={{
|
objectFit="cover"
|
||||||
objectFit: 'cover'
|
|
||||||
}}
|
|
||||||
alt="User avatar"
|
alt="User avatar"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ const EnrichLog: FC<EnrichLogProps> = ({ str }) => {
|
|||||||
if (match.startsWith('HookSet')) {
|
if (match.startsWith('HookSet')) {
|
||||||
const code = match.match(/^HookSet\((\d+)\)/)?.[1]
|
const code = match.match(/^HookSet\((\d+)\)/)?.[1]
|
||||||
const val = hookSetCodes.find(v => code && v.code === +code)
|
const val = hookSetCodes.find(v => code && v.code === +code)
|
||||||
|
console.log({ code, val })
|
||||||
if (!val) return match
|
if (!val) return match
|
||||||
|
|
||||||
const content = capitalize(val.description) || 'No hint available!'
|
const content = capitalize(val.description) || 'No hint available!'
|
||||||
|
|||||||
@@ -94,14 +94,6 @@ const setMarkers = (monacoE: typeof monaco) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const langWarnings: Record<string, { shown: boolean; message: string }> = {
|
|
||||||
ts: {
|
|
||||||
shown: false,
|
|
||||||
message:
|
|
||||||
'Typescript suppport for hooks is still in early planning stage, write actual hooks in C only for now!'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const HooksEditor = () => {
|
const HooksEditor = () => {
|
||||||
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>()
|
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>()
|
||||||
const monacoRef = useRef<typeof monaco>()
|
const monacoRef = useRef<typeof monaco>()
|
||||||
@@ -133,14 +125,6 @@ const HooksEditor = () => {
|
|||||||
|
|
||||||
const file = snap.files[snap.active]
|
const file = snap.files[snap.active]
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let warning = langWarnings[file?.language || '']
|
|
||||||
if (warning && !warning.shown) {
|
|
||||||
alert(warning.message) // TODO Custom dialog.
|
|
||||||
warning.shown = true
|
|
||||||
}
|
|
||||||
}, [file])
|
|
||||||
|
|
||||||
const renderNav = () => (
|
const renderNav = () => (
|
||||||
<Tabs
|
<Tabs
|
||||||
label="File"
|
label="File"
|
||||||
@@ -237,7 +221,7 @@ const HooksEditor = () => {
|
|||||||
monaco.languages.register({
|
monaco.languages.register({
|
||||||
id: 'text',
|
id: 'text',
|
||||||
extensions: ['.txt'],
|
extensions: ['.txt'],
|
||||||
mimetypes: ['text/plain']
|
mimetypes: ['text/plain'],
|
||||||
})
|
})
|
||||||
MonacoServices.install(monaco)
|
MonacoServices.install(monaco)
|
||||||
const webSocket = createWebSocket(
|
const webSocket = createWebSocket(
|
||||||
@@ -256,7 +240,7 @@ const HooksEditor = () => {
|
|||||||
try {
|
try {
|
||||||
disposable.dispose()
|
disposable.dispose()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('err', err)
|
console.log('err', err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ export const Log: FC<ILog> = ({
|
|||||||
)}
|
)}
|
||||||
<Pre>{message}</Pre>
|
<Pre>{message}</Pre>
|
||||||
{link && (
|
{link && (
|
||||||
<NextLink legacyBehavior href={link} shallow passHref>
|
<NextLink href={link} shallow passHref>
|
||||||
<Link as="a">{linkText}</Link>
|
<Link as="a">{linkText}</Link>
|
||||||
</NextLink>
|
</NextLink>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import NextLink from 'next/link'
|
import Link from 'next/link'
|
||||||
|
|
||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
@@ -78,7 +78,7 @@ const Navigation = () => {
|
|||||||
pr: '$4'
|
pr: '$4'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<NextLink legacyBehavior href={gistId ? `/develop/${gistId}` : '/develop'} passHref>
|
<Link href={gistId ? `/develop/${gistId}` : '/develop'} passHref>
|
||||||
<Box
|
<Box
|
||||||
as="a"
|
as="a"
|
||||||
css={{
|
css={{
|
||||||
@@ -89,7 +89,7 @@ const Navigation = () => {
|
|||||||
>
|
>
|
||||||
<Logo width="32px" height="32px" />
|
<Logo width="32px" height="32px" />
|
||||||
</Box>
|
</Box>
|
||||||
</NextLink>
|
</Link>
|
||||||
<Flex
|
<Flex
|
||||||
css={{
|
css={{
|
||||||
ml: '$5',
|
ml: '$5',
|
||||||
@@ -105,8 +105,7 @@ const Navigation = () => {
|
|||||||
<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 && (
|
||||||
<NextLink
|
<Link
|
||||||
legacyBehavior
|
|
||||||
href={`https://gist.github.com/${snap.gistOwner || ''}/${snap.gistId || ''}`}
|
href={`https://gist.github.com/${snap.gistOwner || ''}/${snap.gistId || ''}`}
|
||||||
passHref
|
passHref
|
||||||
>
|
>
|
||||||
@@ -118,7 +117,7 @@ const Navigation = () => {
|
|||||||
>
|
>
|
||||||
{`${snap.gistOwner || '-'}/${truncate(snap.gistId || '')}`}
|
{`${snap.gistOwner || '-'}/${truncate(snap.gistId || '')}`}
|
||||||
</Text>
|
</Text>
|
||||||
</NextLink>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
</>
|
</>
|
||||||
@@ -337,39 +336,29 @@ const Navigation = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<NextLink
|
<Link href={gistId ? `/develop/${gistId}` : '/develop'} passHref shallow>
|
||||||
legacyBehavior
|
|
||||||
href={gistId ? `/develop/${gistId}` : '/develop'}
|
|
||||||
passHref
|
|
||||||
shallow
|
|
||||||
>
|
|
||||||
<Button as="a" outline={!router.pathname.includes('/develop')} uppercase>
|
<Button as="a" outline={!router.pathname.includes('/develop')} uppercase>
|
||||||
Develop
|
Develop
|
||||||
</Button>
|
</Button>
|
||||||
</NextLink>
|
</Link>
|
||||||
<NextLink
|
<Link href={gistId ? `/deploy/${gistId}` : '/deploy'} passHref shallow>
|
||||||
legacyBehavior
|
|
||||||
href={gistId ? `/deploy/${gistId}` : '/deploy'}
|
|
||||||
passHref
|
|
||||||
shallow
|
|
||||||
>
|
|
||||||
<Button as="a" outline={!router.pathname.includes('/deploy')} uppercase>
|
<Button as="a" outline={!router.pathname.includes('/deploy')} uppercase>
|
||||||
Deploy
|
Deploy
|
||||||
</Button>
|
</Button>
|
||||||
</NextLink>
|
</Link>
|
||||||
<NextLink legacyBehavior href={gistId ? `/test/${gistId}` : '/test'} passHref shallow>
|
<Link href={gistId ? `/test/${gistId}` : '/test'} passHref shallow>
|
||||||
<Button as="a" outline={!router.pathname.includes('/test')} uppercase>
|
<Button as="a" outline={!router.pathname.includes('/test')} uppercase>
|
||||||
Test
|
Test
|
||||||
</Button>
|
</Button>
|
||||||
</NextLink>
|
</Link>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
<NextLink legacyBehavior href="https://xrpl-hooks.readme.io/v2.0" passHref>
|
<Link href="https://xrpl-hooks.readme.io/v2.0" passHref>
|
||||||
<a target="_blank" rel="noreferrer noopener">
|
<a target="_blank" rel="noreferrer noopener">
|
||||||
<Button outline>
|
<Button outline>
|
||||||
<BookOpen size="15px" />
|
<BookOpen size="15px" />
|
||||||
</Button>
|
</Button>
|
||||||
</a>
|
</a>
|
||||||
</NextLink>
|
</Link>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ import { mauve, mauveDark, purple, purpleDark } from '@radix-ui/colors'
|
|||||||
import { useTheme } from 'next-themes'
|
import { useTheme } from 'next-themes'
|
||||||
import { styled } from '../stitches.config'
|
import { styled } from '../stitches.config'
|
||||||
import dynamic from 'next/dynamic'
|
import dynamic from 'next/dynamic'
|
||||||
import type { Props, StylesConfig } from 'react-select'
|
import type { Props } from 'react-select'
|
||||||
const SelectInput = dynamic(() => import('react-select'), { ssr: false })
|
const SelectInput = dynamic(() => import('react-select'), { ssr: false })
|
||||||
const CreatableSelectInput = dynamic(() => import('react-select/creatable'), { ssr: false })
|
|
||||||
|
|
||||||
const getColors = (isDark: boolean) => {
|
// eslint-disable-next-line react/display-name
|
||||||
|
const Select = forwardRef<any, Props>((props, ref) => {
|
||||||
|
const { theme } = useTheme()
|
||||||
|
const isDark = theme === 'dark'
|
||||||
const colors: any = {
|
const colors: any = {
|
||||||
// primary: pink.pink9,
|
// primary: pink.pink9,
|
||||||
active: isDark ? purpleDark.purple9 : purple.purple9,
|
active: isDark ? purpleDark.purple9 : purple.purple9,
|
||||||
@@ -28,136 +30,94 @@ const getColors = (isDark: boolean) => {
|
|||||||
}
|
}
|
||||||
colors.outline = colors.background
|
colors.outline = colors.background
|
||||||
colors.selected = colors.secondary
|
colors.selected = colors.secondary
|
||||||
return colors
|
|
||||||
}
|
|
||||||
|
|
||||||
const getStyles = (isDark: boolean) => {
|
|
||||||
const colors = getColors(isDark)
|
|
||||||
const styles: StylesConfig = {
|
|
||||||
container: provided => {
|
|
||||||
return {
|
|
||||||
...provided,
|
|
||||||
position: 'relative',
|
|
||||||
width: '100%'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
singleValue: provided => ({
|
|
||||||
...provided,
|
|
||||||
color: colors.mauve12
|
|
||||||
}),
|
|
||||||
menu: provided => ({
|
|
||||||
...provided,
|
|
||||||
backgroundColor: colors.dropDownBg
|
|
||||||
}),
|
|
||||||
control: (provided, state) => {
|
|
||||||
return {
|
|
||||||
...provided,
|
|
||||||
minHeight: 0,
|
|
||||||
border: '0px',
|
|
||||||
backgroundColor: colors.mauve4,
|
|
||||||
boxShadow: `0 0 0 1px ${state.isFocused ? colors.border : colors.secondary}`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
input: provided => {
|
|
||||||
return {
|
|
||||||
...provided,
|
|
||||||
color: '$text'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
multiValue: provided => {
|
|
||||||
return {
|
|
||||||
...provided,
|
|
||||||
backgroundColor: colors.mauve8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
multiValueLabel: provided => {
|
|
||||||
return {
|
|
||||||
...provided,
|
|
||||||
color: colors.mauve12
|
|
||||||
}
|
|
||||||
},
|
|
||||||
multiValueRemove: provided => {
|
|
||||||
return {
|
|
||||||
...provided,
|
|
||||||
':hover': {
|
|
||||||
background: colors.mauve9
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
option: (provided, state) => {
|
|
||||||
return {
|
|
||||||
...provided,
|
|
||||||
color: colors.searchText,
|
|
||||||
backgroundColor: state.isFocused ? colors.activeLight : colors.dropDownBg,
|
|
||||||
':hover': {
|
|
||||||
backgroundColor: colors.active,
|
|
||||||
color: '#ffffff'
|
|
||||||
},
|
|
||||||
':selected': {
|
|
||||||
backgroundColor: 'red'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
indicatorSeparator: provided => {
|
|
||||||
return {
|
|
||||||
...provided,
|
|
||||||
backgroundColor: colors.secondary
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dropdownIndicator: (provided, state) => {
|
|
||||||
return {
|
|
||||||
...provided,
|
|
||||||
padding: 6,
|
|
||||||
color: state.isFocused ? colors.border : colors.secondary,
|
|
||||||
':hover': {
|
|
||||||
color: colors.border
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
clearIndicator: provided => {
|
|
||||||
return {
|
|
||||||
...provided,
|
|
||||||
padding: 6,
|
|
||||||
color: colors.secondary,
|
|
||||||
':hover': {
|
|
||||||
color: colors.border
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return styles
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
|
||||||
const Select = forwardRef<any, Props>((props, ref) => {
|
|
||||||
const { theme } = useTheme()
|
|
||||||
const isDark = theme === 'dark'
|
|
||||||
const styles = getStyles(isDark)
|
|
||||||
return (
|
return (
|
||||||
<SelectInput
|
<SelectInput
|
||||||
ref={ref}
|
ref={ref}
|
||||||
menuPosition={props.menuPosition || 'fixed'}
|
menuPosition={props.menuPosition || 'fixed'}
|
||||||
styles={styles}
|
styles={{
|
||||||
{...props}
|
container: provided => {
|
||||||
/>
|
return {
|
||||||
)
|
...provided,
|
||||||
})
|
position: 'relative',
|
||||||
|
width: '100%'
|
||||||
// eslint-disable-next-line react/display-name
|
}
|
||||||
const Creatable = forwardRef<any, Props>((props, ref) => {
|
},
|
||||||
const { theme } = useTheme()
|
singleValue: provided => ({
|
||||||
const isDark = theme === 'dark'
|
...provided,
|
||||||
const styles = getStyles(isDark)
|
color: colors.mauve12
|
||||||
return (
|
}),
|
||||||
<CreatableSelectInput
|
menu: provided => ({
|
||||||
ref={ref}
|
...provided,
|
||||||
formatCreateLabel={label => `Enter "${label}"`}
|
backgroundColor: colors.dropDownBg
|
||||||
menuPosition={props.menuPosition || 'fixed'}
|
}),
|
||||||
styles={styles}
|
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,
|
||||||
|
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, {})
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
|
|||||||
(state: Partial<TransactionState> = txState) => {
|
(state: Partial<TransactionState> = txState) => {
|
||||||
const {
|
const {
|
||||||
selectedTransaction,
|
selectedTransaction,
|
||||||
|
selectedDestAccount,
|
||||||
selectedAccount,
|
selectedAccount,
|
||||||
txFields,
|
txFields,
|
||||||
selectedFlags,
|
selectedFlags,
|
||||||
@@ -51,19 +52,20 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
|
|||||||
} = state
|
} = state
|
||||||
|
|
||||||
const TransactionType = selectedTransaction?.value || null
|
const TransactionType = selectedTransaction?.value || null
|
||||||
|
const Destination = selectedDestAccount?.value || txFields?.Destination
|
||||||
const Account = selectedAccount?.value || null
|
const Account = selectedAccount?.value || null
|
||||||
const Flags = combineFlags(selectedFlags?.map(flag => flag.value)) || txFields?.Flags
|
const Flags = combineFlags(selectedFlags?.map(flag => flag.value)) || txFields?.Flags
|
||||||
const HookParameters = Object.entries(hookParameters || {}).reduce<
|
const HookParameters = Object.entries(hookParameters || {}).reduce<
|
||||||
SetHookData['HookParameters']
|
SetHookData['HookParameters']
|
||||||
>((acc, [_, { label, value }]) => {
|
>((acc, [_, { label, value }]) => {
|
||||||
return acc.concat({
|
return acc.concat({
|
||||||
HookParameter: { HookParameterName: toHex(label), HookParameterValue: value }
|
HookParameter: { HookParameterName: toHex(label), HookParameterValue: toHex(value) }
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
const Memos = memos
|
const Memos = memos
|
||||||
? Object.entries(memos).reduce<SetHookData['Memos']>((acc, [_, { format, data, type }]) => {
|
? Object.entries(memos).reduce<SetHookData['Memos']>((acc, [_, { format, data, type }]) => {
|
||||||
return acc?.concat({
|
return acc?.concat({
|
||||||
Memo: { MemoData: data, MemoFormat: toHex(format), MemoType: toHex(type) }
|
Memo: { MemoData: toHex(data), MemoFormat: toHex(format), MemoType: toHex(type) }
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
: undefined
|
: undefined
|
||||||
@@ -73,6 +75,7 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
|
|||||||
HookParameters,
|
HookParameters,
|
||||||
Flags,
|
Flags,
|
||||||
TransactionType,
|
TransactionType,
|
||||||
|
Destination,
|
||||||
Account,
|
Account,
|
||||||
Memos
|
Memos
|
||||||
})
|
})
|
||||||
@@ -125,12 +128,11 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
|
|||||||
throw Error('Account must be selected from imported accounts!')
|
throw Error('Account must be selected from imported accounts!')
|
||||||
}
|
}
|
||||||
const options = prepareOptions(st)
|
const options = prepareOptions(st)
|
||||||
// delete unnecessary fields
|
|
||||||
Object.keys(options).forEach(field => {
|
const fields = getTxFields(options.TransactionType)
|
||||||
if (!options[field]) {
|
if (fields.Destination && !options.Destination) {
|
||||||
delete options[field]
|
throw Error('Destination account is required!')
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
await sendTransaction(account, options, { logPrefix })
|
await sendTransaction(account, options, { logPrefix })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -165,6 +167,13 @@ const Transaction: FC<TransactionProps> = ({ header, state: txState, ...props })
|
|||||||
selectedTransaction: transactionType
|
selectedTransaction: transactionType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fields.Destination !== undefined) {
|
||||||
|
nwState.selectedDestAccount = null
|
||||||
|
fields.Destination = ''
|
||||||
|
} else {
|
||||||
|
fields.Destination = undefined
|
||||||
|
}
|
||||||
|
|
||||||
if (transactionType?.value && transactionFlags[transactionType?.value] && fields.Flags) {
|
if (transactionType?.value && transactionFlags[transactionType?.value] && fields.Flags) {
|
||||||
nwState.selectedFlags = extractFlags(transactionType.value, fields.Flags)
|
nwState.selectedFlags = extractFlags(transactionType.value, fields.Flags)
|
||||||
fields.Flags = undefined
|
fields.Flags = undefined
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import { FC, ReactNode, useCallback, useEffect, useState } from 'react'
|
import { FC, ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
import Container from '../Container'
|
import Container from '../Container'
|
||||||
import Flex from '../Flex'
|
import Flex from '../Flex'
|
||||||
import Input from '../Input'
|
import Input from '../Input'
|
||||||
import Select, { CreatableSelect } from '../Select'
|
import Select from '../Select'
|
||||||
import Text from '../Text'
|
import Text from '../Text'
|
||||||
import {
|
import {
|
||||||
SelectOption,
|
SelectOption,
|
||||||
TransactionState,
|
TransactionState,
|
||||||
transactionsOptions,
|
transactionsOptions,
|
||||||
TxFields,
|
TxFields,
|
||||||
|
getTxFields,
|
||||||
defaultTransactionType
|
defaultTransactionType
|
||||||
} from '../../state/transactions'
|
} from '../../state/transactions'
|
||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
import state from '../../state'
|
import state from '../../state'
|
||||||
import { streamState } from '../DebugStream'
|
import { streamState } from '../DebugStream'
|
||||||
import { Box, Button } from '..'
|
import { Button } from '..'
|
||||||
import Textarea from '../Textarea'
|
import Textarea from '../Textarea'
|
||||||
import { getFlags } from '../../state/constants/flags'
|
import { getFlags } from '../../state/constants/flags'
|
||||||
import { Plus, Trash } from 'phosphor-react'
|
import { Plus, Trash } from 'phosphor-react'
|
||||||
import AccountSequence from '../Sequence'
|
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
|
||||||
@@ -37,14 +37,28 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
switchToJson
|
switchToJson
|
||||||
}) => {
|
}) => {
|
||||||
const { accounts } = useSnapshot(state)
|
const { accounts } = useSnapshot(state)
|
||||||
const { selectedAccount, selectedTransaction, txFields, selectedFlags, hookParameters, memos } =
|
const {
|
||||||
txState
|
selectedAccount,
|
||||||
|
selectedDestAccount,
|
||||||
|
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
|
||||||
|
.map(acc => ({
|
||||||
|
label: acc.name,
|
||||||
|
value: acc.address
|
||||||
|
}))
|
||||||
|
.filter(acc => acc.value !== selectedAccount?.value)
|
||||||
|
|
||||||
const flagsOptions: SelectOption[] = Object.entries(
|
const flagsOptions: SelectOption[] = Object.entries(
|
||||||
getFlags(selectedTransaction?.value) || {}
|
getFlags(selectedTransaction?.value) || {}
|
||||||
).map(([label, value]) => ({
|
).map(([label, value]) => ({
|
||||||
@@ -73,22 +87,6 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
[setState, txFields]
|
[setState, txFields]
|
||||||
)
|
)
|
||||||
|
|
||||||
const setRawField = useCallback(
|
|
||||||
(field: keyof TxFields, type: string, value: any) => {
|
|
||||||
// TODO $type should be a narrowed type
|
|
||||||
setState({
|
|
||||||
txFields: {
|
|
||||||
...txFields,
|
|
||||||
[field]: {
|
|
||||||
$type: type,
|
|
||||||
$value: value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[setState, txFields]
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleEstimateFee = useCallback(
|
const handleEstimateFee = useCallback(
|
||||||
async (state?: TransactionState, silent?: boolean) => {
|
async (state?: TransactionState, silent?: boolean) => {
|
||||||
setFeeLoading(true)
|
setFeeLoading(true)
|
||||||
@@ -121,23 +119,21 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
}
|
}
|
||||||
}, [handleChangeTxType, selectedTransaction?.value])
|
}, [handleChangeTxType, selectedTransaction?.value])
|
||||||
|
|
||||||
|
const fields = useMemo(
|
||||||
|
() => getTxFields(selectedTransaction?.value),
|
||||||
|
[selectedTransaction?.value]
|
||||||
|
)
|
||||||
|
|
||||||
const richFields = ['TransactionType', 'Account', 'HookParameters', 'Memos']
|
const richFields = ['TransactionType', 'Account', 'HookParameters', 'Memos']
|
||||||
|
if (fields.Destination !== undefined) {
|
||||||
|
richFields.push('Destination')
|
||||||
|
}
|
||||||
|
|
||||||
if (flagsOptions.length) {
|
if (flagsOptions.length) {
|
||||||
richFields.push('Flags')
|
richFields.push('Flags')
|
||||||
}
|
}
|
||||||
|
|
||||||
const otherFields = Object.keys(txFields).filter(k => !richFields.includes(k)) as [keyof TxFields]
|
const otherFields = Object.keys(txFields).filter(k => !richFields.includes(k)) as [keyof TxFields]
|
||||||
const amountOptions = [
|
|
||||||
{ label: 'XRP', value: 'xrp' },
|
|
||||||
{ label: 'Token', value: 'token' }
|
|
||||||
] as const
|
|
||||||
|
|
||||||
const defaultTokenAmount = {
|
|
||||||
value: '0',
|
|
||||||
currency: '',
|
|
||||||
issuer: ''
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
css={{
|
css={{
|
||||||
@@ -169,6 +165,18 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
<TxField label="Sequence">
|
<TxField label="Sequence">
|
||||||
<AccountSequence address={selectedAccount?.value} />
|
<AccountSequence address={selectedAccount?.value} />
|
||||||
</TxField>
|
</TxField>
|
||||||
|
{richFields.includes('Destination') && (
|
||||||
|
<TxField label="Destination account">
|
||||||
|
<Select
|
||||||
|
instanceId="to-account"
|
||||||
|
placeholder="Select the destination account"
|
||||||
|
options={destAccountOptions}
|
||||||
|
value={selectedDestAccount}
|
||||||
|
isClearable
|
||||||
|
onChange={(acc: any) => setState({ selectedDestAccount: acc })}
|
||||||
|
/>
|
||||||
|
</TxField>
|
||||||
|
)}
|
||||||
{richFields.includes('Flags') && (
|
{richFields.includes('Flags') && (
|
||||||
<TxField label="Flags">
|
<TxField label="Flags">
|
||||||
<Select
|
<Select
|
||||||
@@ -190,133 +198,23 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
let _value = txFields[field]
|
let _value = txFields[field]
|
||||||
|
|
||||||
let value: string | undefined
|
let value: string | undefined
|
||||||
if (typeIs(_value, 'object')) {
|
if (typeof _value === 'object') {
|
||||||
if (_value.$type === 'json' && typeIs(_value.$value, ['object', 'array'])) {
|
if (_value.$type === 'json' && typeof _value.$value === 'object') {
|
||||||
value = JSON.stringify(_value.$value, null, 2)
|
value = JSON.stringify(_value.$value, null, 2)
|
||||||
} else {
|
} else {
|
||||||
value = _value.$value?.toString()
|
value = _value.$value.toString()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
value = _value?.toString()
|
value = _value?.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
const isAccount = typeIs(_value, 'object') && _value.$type === 'account'
|
const isXrp = typeof _value === 'object' && _value.$type === 'xrp'
|
||||||
const isXrpAmount = typeIs(_value, 'object') && _value.$type === 'amount.xrp'
|
|
||||||
const isTokenAmount = typeIs(_value, 'object') && _value.$type === 'amount.token'
|
|
||||||
const isJson = typeof _value === 'object' && _value.$type === 'json'
|
const isJson = typeof _value === 'object' && _value.$type === 'json'
|
||||||
const isFee = field === 'Fee'
|
const isFee = field === 'Fee'
|
||||||
let rows = isJson ? (value?.match(/\n/gm)?.length || 0) + 1 : undefined
|
let rows = isJson ? (value?.match(/\n/gm)?.length || 0) + 1 : undefined
|
||||||
if (rows && rows > 5) rows = 5
|
if (rows && rows > 5) rows = 5
|
||||||
let tokenAmount = defaultTokenAmount
|
|
||||||
if (isTokenAmount && typeIs(_value, 'object') && typeIs(_value.$value, 'object')) {
|
|
||||||
tokenAmount = {
|
|
||||||
value: _value.$value.value,
|
|
||||||
currency: _value.$value.currency,
|
|
||||||
issuer: _value.$value.issuer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isXrpAmount || isTokenAmount) {
|
|
||||||
return (
|
|
||||||
<TxField key={field} label={field}>
|
|
||||||
<Flex fluid css={{ alignItems: 'center' }}>
|
|
||||||
{isTokenAmount ? (
|
|
||||||
<Flex
|
|
||||||
fluid
|
|
||||||
row
|
|
||||||
align="center"
|
|
||||||
justify="space-between"
|
|
||||||
css={{ position: 'relative' }}
|
|
||||||
>
|
|
||||||
{/* <Input
|
|
||||||
type="text"
|
|
||||||
placeholder="Issuer"
|
|
||||||
value={tokenAmount.issuer}
|
|
||||||
onChange={e =>
|
|
||||||
setRawField(field, 'amount.token', {
|
|
||||||
...tokenAmount,
|
|
||||||
issuer: e.target.value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/> */}
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
value={tokenAmount.currency}
|
|
||||||
placeholder="Currency"
|
|
||||||
onChange={e => {
|
|
||||||
setRawField(field, 'amount.token', {
|
|
||||||
...tokenAmount,
|
|
||||||
currency: e.target.value
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
css={{ mx: '$1' }}
|
|
||||||
type="number"
|
|
||||||
value={tokenAmount.value}
|
|
||||||
placeholder="Value"
|
|
||||||
onChange={e => {
|
|
||||||
setRawField(field, 'amount.token', {
|
|
||||||
...tokenAmount,
|
|
||||||
value: e.target.value
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Box css={{ width: '50%' }}>
|
|
||||||
<CreatableAccount
|
|
||||||
value={tokenAmount.issuer}
|
|
||||||
field={'Issuer' as any}
|
|
||||||
placeholder="Issuer"
|
|
||||||
setField={(_, value = '') => {
|
|
||||||
setRawField(field, 'amount.token', {
|
|
||||||
...tokenAmount,
|
|
||||||
issuer: value
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
) : (
|
|
||||||
<Input
|
|
||||||
css={{ flex: 'inherit' }}
|
|
||||||
type="number"
|
|
||||||
value={value}
|
|
||||||
onChange={e => handleSetField(field, e.target.value)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Box
|
|
||||||
css={{
|
|
||||||
ml: '$2',
|
|
||||||
width: '150px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
instanceId="currency-type"
|
|
||||||
options={amountOptions}
|
|
||||||
value={isXrpAmount ? amountOptions['0'] : amountOptions['1']}
|
|
||||||
onChange={(e: any) => {
|
|
||||||
const opt = e as typeof amountOptions[number]
|
|
||||||
if (opt.value === 'xrp') {
|
|
||||||
setRawField(field, 'amount.xrp', '0')
|
|
||||||
} else {
|
|
||||||
setRawField(field, 'amount.token', defaultTokenAmount)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
</TxField>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (isAccount) {
|
|
||||||
return (
|
|
||||||
<TxField key={field} label={field}>
|
|
||||||
<CreatableAccount value={value} field={field} setField={handleSetField} />
|
|
||||||
</TxField>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<TxField key={field} label={field}>
|
<TxField key={field} label={field + (isXrp ? ' (XRP)' : '')}>
|
||||||
{isJson ? (
|
{isJson ? (
|
||||||
<Textarea
|
<Textarea
|
||||||
rows={rows}
|
rows={rows}
|
||||||
@@ -406,7 +304,7 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
css={{ mx: '$2' }}
|
css={{ mx: '$2' }}
|
||||||
placeholder="Value (hex-quoted)"
|
placeholder="Value"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
setState({
|
setState({
|
||||||
@@ -469,7 +367,7 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
placeholder="Data (hex-quoted)"
|
placeholder="Data"
|
||||||
css={{ mx: '$2' }}
|
css={{ mx: '$2' }}
|
||||||
value={memo.data}
|
value={memo.data}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
@@ -527,35 +425,6 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CreatableAccount: FC<{
|
|
||||||
value: string | undefined
|
|
||||||
field: keyof TxFields
|
|
||||||
placeholder?: string
|
|
||||||
setField: (field: keyof TxFields, value: string, opFields?: TxFields) => void
|
|
||||||
}> = ({ value, field, setField, placeholder }) => {
|
|
||||||
const { accounts } = useSnapshot(state)
|
|
||||||
const accountOptions: SelectOption[] = accounts.map(acc => ({
|
|
||||||
label: acc.name,
|
|
||||||
value: acc.address
|
|
||||||
}))
|
|
||||||
const label = accountOptions.find(a => a.value === value)?.label || value
|
|
||||||
const val = {
|
|
||||||
value,
|
|
||||||
label
|
|
||||||
}
|
|
||||||
placeholder = placeholder || `${capitalize(field)} account`
|
|
||||||
return (
|
|
||||||
<CreatableSelect
|
|
||||||
isClearable
|
|
||||||
instanceId={field}
|
|
||||||
placeholder={placeholder}
|
|
||||||
options={accountOptions}
|
|
||||||
value={value ? val : undefined}
|
|
||||||
onChange={(acc: any) => setField(field, acc?.value)}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const TxField: FC<{ label: string; children: ReactNode; multiLine?: boolean }> = ({
|
export const TxField: FC<{ label: string; children: ReactNode; multiLine?: boolean }> = ({
|
||||||
label,
|
label,
|
||||||
children,
|
children,
|
||||||
@@ -569,7 +438,7 @@ export const TxField: FC<{ label: string; children: ReactNode; multiLine?: boole
|
|||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
alignItems: multiLine ? 'flex-start' : 'center',
|
alignItems: multiLine ? 'flex-start' : 'center',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
mb: '$2',
|
mb: '$3',
|
||||||
mt: '1px',
|
mt: '1px',
|
||||||
pr: '1px'
|
pr: '1px'
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -2,10 +2,7 @@
|
|||||||
{
|
{
|
||||||
"TransactionType": "AccountDelete",
|
"TransactionType": "AccountDelete",
|
||||||
"Account": "rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm",
|
"Account": "rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm",
|
||||||
"Destination": {
|
"Destination": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
|
||||||
"$type": "account",
|
|
||||||
"$value": ""
|
|
||||||
},
|
|
||||||
"DestinationTag": 13,
|
"DestinationTag": 13,
|
||||||
"Fee": "2000000",
|
"Fee": "2000000",
|
||||||
"Sequence": 2470665,
|
"Sequence": 2470665,
|
||||||
@@ -31,11 +28,7 @@
|
|||||||
"TransactionType": "CheckCash",
|
"TransactionType": "CheckCash",
|
||||||
"Amount": {
|
"Amount": {
|
||||||
"$value": "100",
|
"$value": "100",
|
||||||
"$type": "amount.xrp"
|
"$type": "xrp"
|
||||||
},
|
|
||||||
"DeliverMin": {
|
|
||||||
"$value": "",
|
|
||||||
"$type": "amount.xrp"
|
|
||||||
},
|
},
|
||||||
"CheckID": "838766BA2B995C00744175F69A1B11E32C3DBC40E64801A4056FCBD657F57334",
|
"CheckID": "838766BA2B995C00744175F69A1B11E32C3DBC40E64801A4056FCBD657F57334",
|
||||||
"Fee": "12"
|
"Fee": "12"
|
||||||
@@ -43,10 +36,7 @@
|
|||||||
{
|
{
|
||||||
"TransactionType": "CheckCreate",
|
"TransactionType": "CheckCreate",
|
||||||
"Account": "rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo",
|
"Account": "rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo",
|
||||||
"Destination": {
|
"Destination": "rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy",
|
||||||
"$type": "account",
|
|
||||||
"$value": ""
|
|
||||||
},
|
|
||||||
"SendMax": "100000000",
|
"SendMax": "100000000",
|
||||||
"Expiration": 570113521,
|
"Expiration": 570113521,
|
||||||
"InvoiceID": "6F1DFD1D0FE8A32E40E1F2C05CF1C15545BAB56B617F9C6C2D63A6B704BEF59B",
|
"InvoiceID": "6F1DFD1D0FE8A32E40E1F2C05CF1C15545BAB56B617F9C6C2D63A6B704BEF59B",
|
||||||
@@ -64,10 +54,7 @@
|
|||||||
{
|
{
|
||||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"TransactionType": "EscrowCancel",
|
"TransactionType": "EscrowCancel",
|
||||||
"Owner": {
|
"Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"$type": "account",
|
|
||||||
"$value": ""
|
|
||||||
},
|
|
||||||
"OfferSequence": 7,
|
"OfferSequence": 7,
|
||||||
"Fee": "10"
|
"Fee": "10"
|
||||||
},
|
},
|
||||||
@@ -76,12 +63,9 @@
|
|||||||
"TransactionType": "EscrowCreate",
|
"TransactionType": "EscrowCreate",
|
||||||
"Amount": {
|
"Amount": {
|
||||||
"$value": "100",
|
"$value": "100",
|
||||||
"$type": "amount.xrp"
|
"$type": "xrp"
|
||||||
},
|
|
||||||
"Destination": {
|
|
||||||
"$type": "account",
|
|
||||||
"$value": ""
|
|
||||||
},
|
},
|
||||||
|
"Destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
|
||||||
"CancelAfter": 533257958,
|
"CancelAfter": 533257958,
|
||||||
"FinishAfter": 533171558,
|
"FinishAfter": 533171558,
|
||||||
"Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
|
"Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
|
||||||
@@ -92,10 +76,7 @@
|
|||||||
{
|
{
|
||||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"TransactionType": "EscrowFinish",
|
"TransactionType": "EscrowFinish",
|
||||||
"Owner": {
|
"Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"$type": "account",
|
|
||||||
"$value": ""
|
|
||||||
},
|
|
||||||
"OfferSequence": 7,
|
"OfferSequence": 7,
|
||||||
"Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
|
"Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
|
||||||
"Fulfillment": "A0028000",
|
"Fulfillment": "A0028000",
|
||||||
@@ -139,18 +120,12 @@
|
|||||||
"NFTokenID": "000100001E962F495F07A990F4ED55ACCFEEF365DBAA76B6A048C0A200000007",
|
"NFTokenID": "000100001E962F495F07A990F4ED55ACCFEEF365DBAA76B6A048C0A200000007",
|
||||||
"Amount": {
|
"Amount": {
|
||||||
"$value": "100",
|
"$value": "100",
|
||||||
"$type": "amount.xrp"
|
"$type": "xrp"
|
||||||
},
|
},
|
||||||
"Flags": "1",
|
"Flags": "1",
|
||||||
"Destination": {
|
"Destination": "",
|
||||||
"$type": "account",
|
"Fee": "10",
|
||||||
"$value": ""
|
"Owner": ""
|
||||||
},
|
|
||||||
"Owner": {
|
|
||||||
"$type": "account",
|
|
||||||
"$value": ""
|
|
||||||
},
|
|
||||||
"Fee": "10"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"TransactionType": "OfferCancel",
|
"TransactionType": "OfferCancel",
|
||||||
@@ -168,29 +143,19 @@
|
|||||||
"Flags": "0",
|
"Flags": "0",
|
||||||
"LastLedgerSequence": 7108682,
|
"LastLedgerSequence": 7108682,
|
||||||
"Sequence": 8,
|
"Sequence": 8,
|
||||||
"TakerGets": {
|
"TakerGets": "6000000",
|
||||||
"$type": "amount.xrp",
|
"Amount": {
|
||||||
"$value": "6000000"
|
"$value": "100",
|
||||||
},
|
"$type": "xrp"
|
||||||
"TakerPays": {
|
|
||||||
"$type": "amount.token",
|
|
||||||
"$value": {
|
|
||||||
"currency": "GKO",
|
|
||||||
"issuer": "ruazs5h1qEsqpke88pcqnaseXdm6od2xc",
|
|
||||||
"value": "2"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"TransactionType": "Payment",
|
"TransactionType": "Payment",
|
||||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"Destination": {
|
"Destination": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||||
"$type": "account",
|
|
||||||
"$value": ""
|
|
||||||
},
|
|
||||||
"Amount": {
|
"Amount": {
|
||||||
"$value": "100",
|
"$value": "100",
|
||||||
"$type": "amount.xrp"
|
"$type": "xrp"
|
||||||
},
|
},
|
||||||
"Fee": "12",
|
"Fee": "12",
|
||||||
"Flags": "2147483648",
|
"Flags": "2147483648",
|
||||||
@@ -201,12 +166,9 @@
|
|||||||
"TransactionType": "PaymentChannelCreate",
|
"TransactionType": "PaymentChannelCreate",
|
||||||
"Amount": {
|
"Amount": {
|
||||||
"$value": "100",
|
"$value": "100",
|
||||||
"$type": "amount.xrp"
|
"$type": "xrp"
|
||||||
},
|
|
||||||
"Destination": {
|
|
||||||
"$type": "account",
|
|
||||||
"$value": ""
|
|
||||||
},
|
},
|
||||||
|
"Destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
|
||||||
"SettleDelay": 86400,
|
"SettleDelay": 86400,
|
||||||
"PublicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A",
|
"PublicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A",
|
||||||
"CancelAfter": 533171558,
|
"CancelAfter": 533171558,
|
||||||
@@ -220,7 +182,7 @@
|
|||||||
"Channel": "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198",
|
"Channel": "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198",
|
||||||
"Amount": {
|
"Amount": {
|
||||||
"$value": "200",
|
"$value": "200",
|
||||||
"$type": "amount.xrp"
|
"$type": "xrp"
|
||||||
},
|
},
|
||||||
"Expiration": 543171558,
|
"Expiration": 543171558,
|
||||||
"Fee": "10"
|
"Fee": "10"
|
||||||
@@ -276,7 +238,7 @@
|
|||||||
"Flags": "262144",
|
"Flags": "262144",
|
||||||
"LastLedgerSequence": 8007750,
|
"LastLedgerSequence": 8007750,
|
||||||
"LimitAmount": {
|
"LimitAmount": {
|
||||||
"$type": "amount.token",
|
"$type": "json",
|
||||||
"$value": {
|
"$value": {
|
||||||
"currency": "USD",
|
"currency": "USD",
|
||||||
"issuer": "rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc",
|
"issuer": "rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc",
|
||||||
@@ -287,10 +249,6 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"TransactionType": "Invoke",
|
"TransactionType": "Invoke",
|
||||||
"Destination": {
|
|
||||||
"$type": "account",
|
|
||||||
"$value": ""
|
|
||||||
},
|
|
||||||
"Fee": "12"
|
"Fee": "12"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,16 +8,8 @@ module.exports = {
|
|||||||
config.resolve.alias['vscode'] = require.resolve(
|
config.resolve.alias['vscode'] = require.resolve(
|
||||||
'@codingame/monaco-languageclient/lib/vscode-compatibility'
|
'@codingame/monaco-languageclient/lib/vscode-compatibility'
|
||||||
)
|
)
|
||||||
config.experiments = {
|
|
||||||
topLevelAwait: true,
|
|
||||||
layers: true
|
|
||||||
}
|
|
||||||
if (!isServer) {
|
if (!isServer) {
|
||||||
config.resolve.fallback = {
|
config.resolve.fallback.fs = false
|
||||||
...config.resolve.fallback,
|
|
||||||
fs: false,
|
|
||||||
module: false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
config.module.rules.push({
|
config.module.rules.push({
|
||||||
test: [/\.md$/, /hook-bundle\.js$/],
|
test: [/\.md$/, /hook-bundle\.js$/],
|
||||||
|
|||||||
11
package.json
11
package.json
@@ -14,7 +14,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codingame/monaco-jsonrpc": "^0.3.1",
|
"@codingame/monaco-jsonrpc": "^0.3.1",
|
||||||
"@codingame/monaco-languageclient": "^0.17.0",
|
"@codingame/monaco-languageclient": "^0.17.0",
|
||||||
"@eqlabs/assemblyscript": "^0.0.0-alpha.1680097351",
|
|
||||||
"@monaco-editor/react": "^4.4.5",
|
"@monaco-editor/react": "^4.4.5",
|
||||||
"@octokit/core": "^3.5.1",
|
"@octokit/core": "^3.5.1",
|
||||||
"@radix-ui/colors": "^0.1.7",
|
"@radix-ui/colors": "^0.1.7",
|
||||||
@@ -38,7 +37,7 @@
|
|||||||
"lodash.uniqby": "^4.7.0",
|
"lodash.uniqby": "^4.7.0",
|
||||||
"lodash.xor": "^4.5.0",
|
"lodash.xor": "^4.5.0",
|
||||||
"monaco-editor": "^0.33.0",
|
"monaco-editor": "^0.33.0",
|
||||||
"next": "^13.1.1",
|
"next": "^12.0.4",
|
||||||
"next-auth": "^4.10.3",
|
"next-auth": "^4.10.3",
|
||||||
"next-plausible": "^3.2.0",
|
"next-plausible": "^3.2.0",
|
||||||
"next-themes": "^0.1.1",
|
"next-themes": "^0.1.1",
|
||||||
@@ -50,8 +49,8 @@
|
|||||||
"postinstall-postinstall": "^2.1.0",
|
"postinstall-postinstall": "^2.1.0",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
"re-resizable": "^6.9.1",
|
"re-resizable": "^6.9.1",
|
||||||
"react": "^18.2.0",
|
"react": "17.0.2",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "17.0.2",
|
||||||
"react-hook-form": "^7.28.0",
|
"react-hook-form": "^7.28.0",
|
||||||
"react-hot-keys": "^2.7.1",
|
"react-hot-keys": "^2.7.1",
|
||||||
"react-hot-toast": "^2.1.1",
|
"react-hot-toast": "^2.1.1",
|
||||||
@@ -66,7 +65,7 @@
|
|||||||
"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.32",
|
"wabt": "^1.0.30",
|
||||||
"xrpl-accountlib": "^1.6.1",
|
"xrpl-accountlib": "^1.6.1",
|
||||||
"xrpl-client": "^2.0.2"
|
"xrpl-client": "^2.0.2"
|
||||||
},
|
},
|
||||||
@@ -79,7 +78,7 @@
|
|||||||
"@types/react": "17.0.31",
|
"@types/react": "17.0.31",
|
||||||
"browserify": "^17.0.0",
|
"browserify": "^17.0.0",
|
||||||
"eslint": "7.32.0",
|
"eslint": "7.32.0",
|
||||||
"eslint-config-next": "^13.1.1",
|
"eslint-config-next": "11.1.2",
|
||||||
"raw-loader": "^4.0.2",
|
"raw-loader": "^4.0.2",
|
||||||
"typescript": "4.4.4"
|
"typescript": "4.4.4"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import { ChatCircleText } from 'phosphor-react'
|
|||||||
TimeAgo.setDefaultLocale(en.locale)
|
TimeAgo.setDefaultLocale(en.locale)
|
||||||
TimeAgo.addLocale(en)
|
TimeAgo.addLocale(en)
|
||||||
|
|
||||||
function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps<{ session?: any }>) {
|
function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const slug = router.query?.slug
|
const slug = router.query?.slug
|
||||||
const gistId = (Array.isArray(slug) && slug[0]) ?? null
|
const gistId = (Array.isArray(slug) && slug[0]) ?? null
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { NextRequest } from 'next/server'
|
import type { NextRequest, NextFetchEvent } from 'next/server'
|
||||||
import { NextResponse as Response } from 'next/server'
|
import { NextResponse as Response } from 'next/server'
|
||||||
|
|
||||||
export default function middleware(req: NextRequest) {
|
export default function middleware(req: NextRequest, ev: NextFetchEvent) {
|
||||||
if (req.nextUrl.pathname === '/') {
|
if (req.nextUrl.pathname === '/') {
|
||||||
const url = req.nextUrl.clone()
|
const url = req.nextUrl.clone()
|
||||||
url.pathname = '/develop'
|
url.pathname = '/develop'
|
||||||
@@ -40,7 +40,7 @@ export default async function handler(
|
|||||||
}
|
}
|
||||||
return res.status(200).json(json)
|
return res.status(200).json(json)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.log(err)
|
||||||
return res.status(500).json({ error: 'Server error' })
|
return res.status(500).json({ error: 'Server error' })
|
||||||
}
|
}
|
||||||
return res.status(500).json({ error: 'Not able to create faucet, try again' })
|
return res.status(500).json({ error: 'Not able to create faucet, try again' })
|
||||||
|
|||||||
@@ -150,9 +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' || activeFileExt === 'ts'
|
const canCompile = activeFileExt === 'c' || activeFileExt === 'wat'
|
||||||
|
|
||||||
const isCompiling = snap.compiling.includes(snap.active);
|
|
||||||
return (
|
return (
|
||||||
<Split
|
<Split
|
||||||
direction="vertical"
|
direction="vertical"
|
||||||
@@ -168,9 +166,7 @@ const Home: NextPage = () => {
|
|||||||
{canCompile && (
|
{canCompile && (
|
||||||
<Hotkeys
|
<Hotkeys
|
||||||
keyName="command+b,ctrl+b"
|
keyName="command+b,ctrl+b"
|
||||||
onKeyDown={() =>
|
onKeyDown={() => !snap.compiling && snap.files.length && compileCode(snap.active)}
|
||||||
snap.compiling === undefined && snap.files.length && compileCode(snap.active)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
css={{
|
css={{
|
||||||
@@ -187,7 +183,7 @@ const Home: NextPage = () => {
|
|||||||
variant="primary"
|
variant="primary"
|
||||||
uppercase
|
uppercase
|
||||||
disabled={!snap.files.length}
|
disabled={!snap.files.length}
|
||||||
isLoading={isCompiling}
|
isLoading={snap.compiling}
|
||||||
onClick={() => compileCode(snap.active)}
|
onClick={() => compileCode(snap.active)}
|
||||||
>
|
>
|
||||||
<Play weight="bold" size="16px" />
|
<Play weight="bold" size="16px" />
|
||||||
@@ -204,9 +200,7 @@ const Home: NextPage = () => {
|
|||||||
{activeFileExt === 'js' && (
|
{activeFileExt === 'js' && (
|
||||||
<Hotkeys
|
<Hotkeys
|
||||||
keyName="command+b,ctrl+b"
|
keyName="command+b,ctrl+b"
|
||||||
onKeyDown={() =>
|
onKeyDown={() => !snap.compiling && snap.files.length && compileCode(snap.active)}
|
||||||
!isCompiling && snap.files.length && compileCode(snap.active)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
css={{
|
css={{
|
||||||
|
|||||||
@@ -1,49 +1,97 @@
|
|||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import Router from 'next/router'
|
import Router from 'next/router'
|
||||||
|
|
||||||
import state, { IFile } from '../index'
|
import state from '../index'
|
||||||
import { saveFile } from './saveFile'
|
import { saveFile } from './saveFile'
|
||||||
import { decodeBinary } from '../../utils/decodeBinary'
|
import { decodeBinary } from '../../utils/decodeBinary'
|
||||||
import { ref } from 'valtio'
|
import { ref } from 'valtio'
|
||||||
import asc from "@eqlabs/assemblyscript/dist/asc"
|
|
||||||
import { getFileExtention } from '../../utils/helpers'
|
|
||||||
|
|
||||||
type CompilationResult = Pick<IFile, "compiledContent" | "compiledWatContent">
|
|
||||||
|
|
||||||
|
/* compileCode sends the code of the active file to compile endpoint
|
||||||
|
* If all goes well you will get base64 encoded wasm file back with
|
||||||
|
* some extra logging information if we can provide it. This function
|
||||||
|
* also decodes the returned wasm and creates human readable WAT file
|
||||||
|
* out of it and store both in global state.
|
||||||
|
*/
|
||||||
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]
|
const file = state.files[activeId]
|
||||||
|
if (file.name.endsWith('.wat')) {
|
||||||
// Bail out if we're already compiling the file.
|
return compileWat(activeId)
|
||||||
if (!file || state.compiling.includes(activeId)) {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT) {
|
||||||
|
throw Error('Missing env!')
|
||||||
|
}
|
||||||
|
// Bail out if we're already compiling
|
||||||
|
if (state.compiling) {
|
||||||
|
// if compiling is ongoing return // TODO Inform user about it.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Set loading state to true
|
||||||
|
state.compiling = true
|
||||||
|
state.logs = []
|
||||||
try {
|
try {
|
||||||
state.compiling.push(activeId)
|
|
||||||
state.logs = []
|
|
||||||
file.containsErrors = false
|
file.containsErrors = false
|
||||||
|
let res: Response
|
||||||
|
try {
|
||||||
|
res = await fetch(process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
output: 'wasm',
|
||||||
|
compress: true,
|
||||||
|
strip: state.compileOptions.strip,
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
type: 'c',
|
||||||
|
options: state.compileOptions.optimizationLevel || '-O2',
|
||||||
|
name: file.name,
|
||||||
|
src: file.content
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
throw Error('Something went wrong, check your network connection and try again!')
|
||||||
|
}
|
||||||
|
const json = await res.json()
|
||||||
|
state.compiling = false
|
||||||
|
if (!json.success) {
|
||||||
|
const errors = [json.message]
|
||||||
|
if (json.tasks && json.tasks.length > 0) {
|
||||||
|
json.tasks.forEach((task: any) => {
|
||||||
|
if (!task.success) {
|
||||||
|
errors.push(task?.console)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
throw errors
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// Decode base64 encoded wasm that is coming back from the endpoint
|
||||||
|
const bufferData = await decodeBinary(json.output)
|
||||||
|
|
||||||
let result: CompilationResult;
|
// Import wabt from and create human readable version of wasm file and
|
||||||
if (file.name.endsWith('.wat')) {
|
// put it into state
|
||||||
result = await compileWat(file);
|
const ww = await (await import('wabt')).default()
|
||||||
}
|
const myModule = ww.readWasm(new Uint8Array(bufferData), {
|
||||||
else if (file.language === "ts") {
|
readDebugNames: true
|
||||||
result = await compileTs(file);
|
})
|
||||||
}
|
myModule.applyNames()
|
||||||
else if (navigator?.onLine === false) {
|
|
||||||
throw Error('You seem offline, check you internet connection and try again!')
|
const wast = myModule.toText({ foldExprs: false, inlineExport: false })
|
||||||
}
|
|
||||||
else if (file.language === 'c') {
|
file.compiledContent = ref(bufferData)
|
||||||
result = await compileC(file)
|
file.lastCompiled = new Date()
|
||||||
}
|
file.compiledValueSnapshot = file.content
|
||||||
else throw Error("Unknown file type.")
|
file.compiledWatContent = wast
|
||||||
|
} catch (error) {
|
||||||
|
throw Error('Invalid compilation result produced, check your code for errors and try again!')
|
||||||
|
}
|
||||||
|
|
||||||
file.lastCompiled = new Date();
|
|
||||||
file.compiledValueSnapshot = file.content;
|
|
||||||
file.compiledContent = result.compiledContent;
|
|
||||||
file.compiledWatContent = result.compiledWatContent;
|
|
||||||
toast.success('Compiled successfully!', { position: 'bottom-center' })
|
toast.success('Compiled successfully!', { position: 'bottom-center' })
|
||||||
state.logs.push({
|
state.logs.push({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
@@ -52,8 +100,7 @@ export const compileCode = async (activeId: number) => {
|
|||||||
linkText: 'Go to deploy'
|
linkText: 'Go to deploy'
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.log(err)
|
||||||
let message: string;
|
|
||||||
|
|
||||||
if (err instanceof Array && typeof err[0] === 'string') {
|
if (err instanceof Array && typeof err[0] === 'string') {
|
||||||
err.forEach(message => {
|
err.forEach(message => {
|
||||||
@@ -62,157 +109,63 @@ export const compileCode = async (activeId: number) => {
|
|||||||
message
|
message
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
message = "Compilation errors occurred, see logs for more info."
|
|
||||||
} else if (err instanceof Error) {
|
} else if (err instanceof Error) {
|
||||||
message = err.message
|
state.logs.push({
|
||||||
|
type: 'error',
|
||||||
|
message: err.message
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
message = 'Something went wrong, try again later!'
|
state.logs.push({
|
||||||
|
type: 'error',
|
||||||
|
message: 'Something went wrong, come back later!'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
toast.error(message, { position: 'bottom-center' })
|
state.compiling = false
|
||||||
|
toast.error(`Error occurred while compiling!`, { position: 'bottom-center' })
|
||||||
file.containsErrors = true
|
file.containsErrors = true
|
||||||
}
|
}
|
||||||
state.compiling = state.compiling.filter(id => id !== activeId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* compileC sends the code of the active file to compile endpoint
|
export const compileWat = async (activeId: number) => {
|
||||||
* If all goes well you will get base64 encoded wasm file back with
|
if (state.compiling) return;
|
||||||
* some extra logging information if we can provide it. This function
|
const file = state.files[activeId]
|
||||||
* also decodes the returned wasm and creates human readable WAT file
|
state.compiling = true
|
||||||
* out of it and store both in global state.
|
state.logs = []
|
||||||
*/
|
|
||||||
export const compileC = async (file: IFile): Promise<CompilationResult> => {
|
|
||||||
if (!process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT) {
|
|
||||||
throw Error('Missing C compile endpoint!')
|
|
||||||
}
|
|
||||||
let res: Response
|
|
||||||
res = await fetch(process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
output: 'wasm',
|
|
||||||
compress: true,
|
|
||||||
strip: state.compileOptions.strip,
|
|
||||||
files: [
|
|
||||||
{
|
|
||||||
type: 'c',
|
|
||||||
options: state.compileOptions.optimizationLevel || '-O2',
|
|
||||||
name: file.name,
|
|
||||||
src: file.content
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
})
|
|
||||||
const json = await res.json()
|
|
||||||
|
|
||||||
if (!json.success) {
|
|
||||||
const errors = [json.message]
|
|
||||||
if (json.tasks && json.tasks.length > 0) {
|
|
||||||
json.tasks.forEach((task: any) => {
|
|
||||||
if (!task.success) {
|
|
||||||
errors.push(task?.console)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
throw errors
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
// Decode base64 encoded wasm that is coming back from the endpoint
|
const wabt = await (await import('wabt')).default()
|
||||||
const bufferData = await decodeBinary(json.output)
|
const module = wabt.parseWat(file.name, file.content);
|
||||||
|
module.resolveNames();
|
||||||
// Import wabt from and create human readable version of wasm file and
|
module.validate();
|
||||||
// put it into state
|
const { buffer } = module.toBinary({
|
||||||
const ww = await (await import('wabt')).default()
|
log: false,
|
||||||
const myModule = ww.readWasm(new Uint8Array(bufferData), {
|
write_debug_names: true,
|
||||||
readDebugNames: true
|
|
||||||
})
|
|
||||||
myModule.applyNames()
|
|
||||||
|
|
||||||
const wast = myModule.toText({ foldExprs: false, inlineExport: false })
|
|
||||||
|
|
||||||
return {
|
|
||||||
compiledContent: ref(bufferData),
|
|
||||||
compiledWatContent: wast
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
throw Error('Invalid compilation result produced, check your code for errors and try again!')
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export const compileWat = async (file: IFile): Promise<CompilationResult> => {
|
|
||||||
const wabt = await (await import('wabt')).default()
|
|
||||||
const mod = wabt.parseWat(file.name, file.content);
|
|
||||||
mod.resolveNames();
|
|
||||||
mod.validate();
|
|
||||||
const { buffer } = mod.toBinary({
|
|
||||||
log: false,
|
|
||||||
write_debug_names: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
compiledContent: ref(buffer),
|
|
||||||
compiledWatContent: file.content,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const compileTs = async (file: IFile): Promise<CompilationResult> => {
|
|
||||||
return new Promise(async (resolve, reject) => {
|
|
||||||
let result: Partial<CompilationResult> = {}
|
|
||||||
const { error, stdout, stderr } = await asc.main([
|
|
||||||
// Command line options
|
|
||||||
file.name,
|
|
||||||
"--outFile", `${file.name}.wasm`,
|
|
||||||
"--textFile", `${file.name}.wat`,
|
|
||||||
"--runtime", "stub",
|
|
||||||
"--initialMemory", "1",
|
|
||||||
"--maximumMemory", "1",
|
|
||||||
"--noExportMemory",
|
|
||||||
"--optimize",
|
|
||||||
"--topLevelToHook"
|
|
||||||
], {
|
|
||||||
readFile: (name, baseDir) => {
|
|
||||||
const file = state.files.find(file => file.name === name)
|
|
||||||
if (file) {
|
|
||||||
return file.content
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
},
|
|
||||||
writeFile: async (name, data: ArrayBuffer | string, baseDir) => {
|
|
||||||
const ext = getFileExtention(name);
|
|
||||||
if (ext === 'wasm') {
|
|
||||||
result.compiledContent = data as ArrayBuffer;
|
|
||||||
}
|
|
||||||
else if (ext === 'wat') {
|
|
||||||
result.compiledWatContent = data as string;
|
|
||||||
}
|
|
||||||
if (result.compiledContent && result.compiledWatContent) {
|
|
||||||
resolve({ ...result });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
listFiles: (dirname, baseDir) => {
|
|
||||||
return state.files.map(file => file.name)
|
|
||||||
},
|
|
||||||
// reportDiagnostic?: ...,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let logMsg = stdout.toString()
|
file.compiledContent = ref(buffer)
|
||||||
let errMsg = stderr.toString()
|
file.lastCompiled = new Date()
|
||||||
if (logMsg) {
|
file.compiledValueSnapshot = file.content
|
||||||
state.logs.push({
|
file.compiledWatContent = file.content
|
||||||
type: "log",
|
|
||||||
message: logMsg
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (errMsg) {
|
|
||||||
state.logs.push({
|
|
||||||
type: "error",
|
|
||||||
message: errMsg
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error) return reject(error)
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { getFileExtention } from '../../utils/helpers'
|
import { getFileExtention } from '../../utils/helpers'
|
||||||
import state, { IFile, ILang } from '../index'
|
import state, { IFile } from '../index'
|
||||||
|
|
||||||
const languageMapping: Record<string, ILang | undefined> = {
|
const languageMapping: Record<string, string | undefined> = {
|
||||||
ts: 'ts',
|
ts: 'typescript',
|
||||||
js: 'javascript',
|
js: 'javascript',
|
||||||
md: 'markdown',
|
md: 'markdown',
|
||||||
c: 'c',
|
c: 'c',
|
||||||
|
|||||||
@@ -14,4 +14,11 @@ export const deleteAccount = (addr?: string) => {
|
|||||||
if (!acc) return
|
if (!acc) return
|
||||||
acc.label = acc.value
|
acc.label = acc.value
|
||||||
})
|
})
|
||||||
|
transactionsState.transactions
|
||||||
|
.filter(t => t.state.selectedDestAccount?.value === addr)
|
||||||
|
.forEach(t => {
|
||||||
|
const acc = t.state.selectedDestAccount
|
||||||
|
if (!acc) return
|
||||||
|
acc.label = acc.value
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ export const deleteHook = async (account: IAccount & { name?: string }) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.log(err)
|
||||||
toast.error('Error occurred while deleting hook', { id: toastId })
|
toast.error('Error occurred while deleting hook', { id: toastId })
|
||||||
state.deployLogs.push({
|
state.deployLogs.push({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Octokit } from '@octokit/core'
|
import { Octokit } from '@octokit/core'
|
||||||
import state, { IFile, ILang } from '../index'
|
import state, { IFile } from '../index'
|
||||||
import { templateFileIds } from '../constants'
|
import { templateFileIds } from '../constants'
|
||||||
|
|
||||||
const octokit = new Octokit()
|
const octokit = new Octokit()
|
||||||
@@ -48,7 +48,7 @@ export const fetchFiles = async (gistId: string) => {
|
|||||||
|
|
||||||
const files: IFile[] = Object.keys(res.data.files).map(filename => ({
|
const files: IFile[] = Object.keys(res.data.files).map(filename => ({
|
||||||
name: res.data.files?.[filename]?.filename || 'untitled.c',
|
name: res.data.files?.[filename]?.filename || 'untitled.c',
|
||||||
language: res.data.files?.[filename]?.language?.toLowerCase() as ILang | undefined,
|
language: res.data.files?.[filename]?.language?.toLowerCase() || '',
|
||||||
content: res.data.files?.[filename]?.content || ''
|
content: res.data.files?.[filename]?.content || ''
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ 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 {
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export const syncToGist = async (session?: Session | null, createNewGist?: boole
|
|||||||
return toast.success('Updated to gist successfully!', { id: toastId })
|
return toast.success('Updated to gist successfully!', { id: toastId })
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error(err)
|
console.log(err)
|
||||||
state.gistLoading = false
|
state.gistLoading = false
|
||||||
return toast.error(`Could not update Gist, try again later!`, {
|
return toast.error(`Could not update Gist, try again later!`, {
|
||||||
id: toastId
|
id: toastId
|
||||||
@@ -85,7 +85,7 @@ export const syncToGist = async (session?: Session | null, createNewGist?: boole
|
|||||||
return toast.success('Created new gist successfully!', { id: toastId })
|
return toast.success('Created new gist successfully!', { id: toastId })
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error(err)
|
console.log(err)
|
||||||
state.gistLoading = false
|
state.gistLoading = false
|
||||||
return toast.error(`Could not create Gist, try again later!`, {
|
return toast.error(`Could not create Gist, try again later!`, {
|
||||||
id: toastId
|
id: toastId
|
||||||
|
|||||||
@@ -9,10 +9,9 @@ declare module 'valtio' {
|
|||||||
function snapshot<T extends object>(p: T): T
|
function snapshot<T extends object>(p: T): T
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ILang = "ts" | "javascript" | "markdown" | "c" | "text"
|
|
||||||
export interface IFile {
|
export interface IFile {
|
||||||
name: string
|
name: string
|
||||||
language: ILang | undefined
|
language: string
|
||||||
content: string
|
content: string
|
||||||
compiledValueSnapshot?: string
|
compiledValueSnapshot?: string
|
||||||
compiledContent?: ArrayBuffer | null
|
compiledContent?: ArrayBuffer | null
|
||||||
@@ -67,7 +66,7 @@ export interface IState {
|
|||||||
loading: boolean
|
loading: boolean
|
||||||
gistLoading: boolean
|
gistLoading: boolean
|
||||||
zipLoading: boolean
|
zipLoading: boolean
|
||||||
compiling: /* file id */ number[]
|
compiling: boolean
|
||||||
logs: ILog[]
|
logs: ILog[]
|
||||||
deployLogs: ILog[]
|
deployLogs: ILog[]
|
||||||
transactionLogs: ILog[]
|
transactionLogs: ILog[]
|
||||||
@@ -99,7 +98,7 @@ let initialState: IState = {
|
|||||||
// Active file index on the Deploy page editor
|
// Active file index on the Deploy page editor
|
||||||
activeWat: 0,
|
activeWat: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
compiling: [],
|
compiling: false,
|
||||||
logs: [],
|
logs: [],
|
||||||
deployLogs: [],
|
deployLogs: [],
|
||||||
transactionLogs: [],
|
transactionLogs: [],
|
||||||
@@ -136,7 +135,7 @@ if (typeof window !== 'undefined') {
|
|||||||
try {
|
try {
|
||||||
localStorageAccounts = localStorage.getItem('hooksIdeAccounts')
|
localStorageAccounts = localStorage.getItem('hooksIdeAccounts')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`localStorage state broken`)
|
console.log(`localStorage state broken`)
|
||||||
localStorage.removeItem('hooksIdeAccounts')
|
localStorage.removeItem('hooksIdeAccounts')
|
||||||
}
|
}
|
||||||
if (localStorageAccounts) {
|
if (localStorageAccounts) {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { showAlert } from '../state/actions/showAlert'
|
|||||||
import { parseJSON } from '../utils/json'
|
import { parseJSON } from '../utils/json'
|
||||||
import { extractFlags, getFlags } from './constants/flags'
|
import { extractFlags, getFlags } from './constants/flags'
|
||||||
import { fromHex } from '../utils/setHook'
|
import { fromHex } from '../utils/setHook'
|
||||||
import { typeIs } from '../utils/helpers'
|
|
||||||
|
|
||||||
export type SelectOption = {
|
export type SelectOption = {
|
||||||
value: string
|
value: string
|
||||||
@@ -28,6 +27,7 @@ export type Memos = {
|
|||||||
export interface TransactionState {
|
export interface TransactionState {
|
||||||
selectedTransaction: SelectOption | null
|
selectedTransaction: SelectOption | null
|
||||||
selectedAccount: SelectOption | null
|
selectedAccount: SelectOption | null
|
||||||
|
selectedDestAccount: SelectOption | null
|
||||||
selectedFlags: SelectOption[] | null
|
selectedFlags: SelectOption[] | null
|
||||||
hookParameters: HookParameters
|
hookParameters: HookParameters
|
||||||
memos: Memos
|
memos: Memos
|
||||||
@@ -50,6 +50,7 @@ export type TxFields = Omit<
|
|||||||
export const defaultTransaction: TransactionState = {
|
export const defaultTransaction: TransactionState = {
|
||||||
selectedTransaction: null,
|
selectedTransaction: null,
|
||||||
selectedAccount: null,
|
selectedAccount: null,
|
||||||
|
selectedDestAccount: null,
|
||||||
selectedFlags: null,
|
selectedFlags: null,
|
||||||
hookParameters: {},
|
hookParameters: {},
|
||||||
memos: {},
|
memos: {},
|
||||||
@@ -129,51 +130,46 @@ export const modifyTxState = (
|
|||||||
return tx.state
|
return tx.state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// state to tx options
|
||||||
export const prepareTransaction = (data: any) => {
|
export const prepareTransaction = (data: any) => {
|
||||||
let options = { ...data }
|
let options = { ...data }
|
||||||
|
|
||||||
Object.keys(options).forEach(field => {
|
Object.keys(options).forEach(field => {
|
||||||
let _value = options[field]
|
let _value = options[field]
|
||||||
if (!typeIs(_value, 'object')) return
|
// convert xrp
|
||||||
// amount.xrp
|
if (_value && typeof _value === 'object' && _value.$type === 'xrp') {
|
||||||
if (_value.$type === 'amount.xrp') {
|
if (+_value.$value) {
|
||||||
if (_value.$value) {
|
options[field] = (+_value.$value * 1000000 + '') as any
|
||||||
options[field] = (+(_value as any).$value * 1000000 + '')
|
|
||||||
} else {
|
} else {
|
||||||
options[field] = ""
|
options[field] = undefined // 👇 💀
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// amount.token
|
// handle type: `json`
|
||||||
if (_value.$type === 'amount.token') {
|
if (_value && typeof _value === 'object' && _value.$type === 'json') {
|
||||||
if (typeIs(_value.$value, 'string')) {
|
if (typeof _value.$value === 'object') {
|
||||||
options[field] = parseJSON(_value.$value)
|
|
||||||
} else if (typeIs(_value.$value, 'object')) {
|
|
||||||
options[field] = _value.$value
|
options[field] = _value.$value
|
||||||
} else {
|
} else {
|
||||||
options[field] = undefined
|
try {
|
||||||
|
options[field] = JSON.parse(_value.$value)
|
||||||
|
} catch (error) {
|
||||||
|
const message = `Input error for json field '${field}': ${error instanceof Error ? error.message : ''
|
||||||
|
}`
|
||||||
|
console.error(message)
|
||||||
|
options[field] = _value.$value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// account
|
|
||||||
if (_value.$type === 'account') {
|
// delete unnecessary fields
|
||||||
options[field] = (_value.$value as any)?.toString() || ""
|
if (!options[field]) {
|
||||||
}
|
delete options[field]
|
||||||
// json
|
|
||||||
if (_value.$type === 'json') {
|
|
||||||
const val = _value.$value;
|
|
||||||
let res: any = val;
|
|
||||||
if (typeIs(val, ["object", "array"])) {
|
|
||||||
options[field] = res
|
|
||||||
} else if (typeIs(val, "string") && (res = parseJSON(val))) {
|
|
||||||
options[field] = res;
|
|
||||||
} else {
|
|
||||||
options[field] = res;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// editor value to state
|
||||||
export const prepareState = (value: string, transactionType?: string) => {
|
export const prepareState = (value: string, transactionType?: string) => {
|
||||||
const options = parseJSON(value)
|
const options = parseJSON(value)
|
||||||
if (!options) {
|
if (!options) {
|
||||||
@@ -183,7 +179,7 @@ export const prepareState = (value: string, transactionType?: string) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const { Account, TransactionType, HookParameters, Memos, ...rest } = options
|
const { Account, TransactionType, Destination, HookParameters, Memos, ...rest } = options
|
||||||
let tx: Partial<TransactionState> = {}
|
let tx: Partial<TransactionState> = {}
|
||||||
const schema = getTxFields(transactionType)
|
const schema = getTxFields(transactionType)
|
||||||
|
|
||||||
@@ -215,7 +211,7 @@ export const prepareState = (value: string, transactionType?: string) => {
|
|||||||
|
|
||||||
if (HookParameters && HookParameters instanceof Array) {
|
if (HookParameters && HookParameters instanceof Array) {
|
||||||
tx.hookParameters = HookParameters.reduce<TransactionState["hookParameters"]>((acc, cur, idx) => {
|
tx.hookParameters = HookParameters.reduce<TransactionState["hookParameters"]>((acc, cur, idx) => {
|
||||||
const param = { label: fromHex(cur.HookParameter?.HookParameterName || ""), value: cur.HookParameter?.HookParameterValue || "" }
|
const param = { label: fromHex(cur.HookParameter?.HookParameterName || ""), value: fromHex(cur.HookParameter?.HookParameterValue || "") }
|
||||||
acc[idx] = param;
|
acc[idx] = param;
|
||||||
return acc;
|
return acc;
|
||||||
}, {})
|
}, {})
|
||||||
@@ -223,12 +219,30 @@ export const prepareState = (value: string, transactionType?: string) => {
|
|||||||
|
|
||||||
if (Memos && Memos instanceof Array) {
|
if (Memos && Memos instanceof Array) {
|
||||||
tx.memos = Memos.reduce<TransactionState["memos"]>((acc, cur, idx) => {
|
tx.memos = Memos.reduce<TransactionState["memos"]>((acc, cur, idx) => {
|
||||||
const memo = { data: cur.Memo?.MemoData || "", type: fromHex(cur.Memo?.MemoType || ""), format: fromHex(cur.Memo?.MemoFormat || "") }
|
const memo = { data: fromHex(cur.Memo?.MemoData || ""), type: fromHex(cur.Memo?.MemoType || ""), format: fromHex(cur.Memo?.MemoFormat || "") }
|
||||||
acc[idx] = memo;
|
acc[idx] = memo;
|
||||||
return acc;
|
return acc;
|
||||||
}, {})
|
}, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (schema.Destination !== undefined) {
|
||||||
|
const dest = state.accounts.find(acc => acc.address === Destination)
|
||||||
|
if (dest) {
|
||||||
|
tx.selectedDestAccount = {
|
||||||
|
label: dest.name,
|
||||||
|
value: dest.address
|
||||||
|
}
|
||||||
|
} else if (Destination) {
|
||||||
|
tx.selectedDestAccount = {
|
||||||
|
label: Destination,
|
||||||
|
value: Destination
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tx.selectedDestAccount = null
|
||||||
|
}
|
||||||
|
} else if (Destination) {
|
||||||
|
rest.Destination = Destination
|
||||||
|
}
|
||||||
|
|
||||||
if (getFlags(TransactionType) && rest.Flags) {
|
if (getFlags(TransactionType) && rest.Flags) {
|
||||||
const flags = extractFlags(TransactionType, rest.Flags)
|
const flags = extractFlags(TransactionType, rest.Flags)
|
||||||
@@ -240,31 +254,17 @@ export const prepareState = (value: string, transactionType?: string) => {
|
|||||||
Object.keys(rest).forEach(field => {
|
Object.keys(rest).forEach(field => {
|
||||||
const value = rest[field]
|
const value = rest[field]
|
||||||
const schemaVal = schema[field as keyof TxFields]
|
const schemaVal = schema[field as keyof TxFields]
|
||||||
|
const isXrp =
|
||||||
const isAmount = schemaVal &&
|
typeof value !== 'object' &&
|
||||||
typeIs(schemaVal, "object") &&
|
schemaVal &&
|
||||||
schemaVal.$type.startsWith('amount.');
|
typeof schemaVal === 'object' &&
|
||||||
const isAccount = schemaVal &&
|
schemaVal.$type === 'xrp'
|
||||||
typeIs(schemaVal, "object") &&
|
if (isXrp) {
|
||||||
schemaVal.$type.startsWith("account");
|
|
||||||
|
|
||||||
if (isAmount && ["number", "string"].includes(typeof value)) {
|
|
||||||
rest[field] = {
|
rest[field] = {
|
||||||
$type: 'amount.xrp', // TODO narrow typed $type.
|
$type: 'xrp',
|
||||||
$value: +value / 1000000 // ! maybe use bigint?
|
$value: +value / 1000000 // ! maybe use bigint?
|
||||||
}
|
}
|
||||||
} else if (isAmount && typeof value === 'object') {
|
} else if (typeof value === 'object') {
|
||||||
rest[field] = {
|
|
||||||
$type: 'amount.token',
|
|
||||||
$value: value
|
|
||||||
}
|
|
||||||
} else if (isAccount) {
|
|
||||||
rest[field] = {
|
|
||||||
$type: "account",
|
|
||||||
$value: value?.toString() || ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (typeof value === 'object') {
|
|
||||||
rest[field] = {
|
rest[field] = {
|
||||||
$type: 'json',
|
$type: 'json',
|
||||||
$value: value
|
$value: value
|
||||||
|
|||||||
@@ -24,9 +24,6 @@ const estimateFee = async (
|
|||||||
const { signedTransaction } = sign(copyTx, keypair)
|
const { signedTransaction } = sign(copyTx, keypair)
|
||||||
|
|
||||||
const res = await xrplSend({ 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
|
||||||
}
|
}
|
||||||
@@ -34,8 +31,7 @@ const estimateFee = async (
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!opts.silent) {
|
if (!opts.silent) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
const msg = err instanceof Error ? err.message : 'Error estimating fee!';
|
toast.error('Cannot estimate fee.') // ? Some better msg
|
||||||
toast.error(msg);
|
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,23 +19,3 @@ export const getFileExtention = (filename?: string): string | undefined => {
|
|||||||
const ext = (filename.includes('.') && filename.split('.').pop()) || undefined
|
const ext = (filename.includes('.') && filename.split('.').pop()) || undefined
|
||||||
return ext
|
return ext
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getFileNamePart = (filename?: string): string | undefined => {
|
|
||||||
if (!filename) return
|
|
||||||
const name = (filename.includes('.') && filename.split('.').slice(0, -1).join(".")) || filename
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export const tts = {
|
|||||||
export type TTS = typeof tts
|
export type TTS = typeof tts
|
||||||
|
|
||||||
const calculateHookOn = (arr: (keyof TTS)[]) => {
|
const calculateHookOn = (arr: (keyof TTS)[]) => {
|
||||||
let s = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff'
|
let s = '0x3e3ff5bf'
|
||||||
arr.forEach(n => {
|
arr.forEach(n => {
|
||||||
let v = BigInt(s)
|
let v = BigInt(s)
|
||||||
v ^= BigInt(1) << BigInt(tts[n])
|
v ^= BigInt(1) << BigInt(tts[n])
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { typeIs, typeOf } from './helpers'
|
|
||||||
|
|
||||||
export const extractSchemaProps = <O extends object>(obj: O) =>
|
export const extractSchemaProps = <O extends object>(obj: O) =>
|
||||||
Object.entries(obj).reduce((prev, [key, val]) => {
|
Object.entries(obj).reduce((prev, [key, val]) => {
|
||||||
const value = typeIs(val, "object") && '$type' in val && '$value' in val ? val?.$value : val
|
const typeOf = <T>(arg: T) =>
|
||||||
|
arg instanceof Array ? 'array' : arg === null ? 'undefined' : typeof arg
|
||||||
|
|
||||||
|
const value = typeOf(val) === 'object' && '$type' in val && '$value' in val ? val?.$value : val
|
||||||
const type = typeOf(value)
|
const type = typeOf(value)
|
||||||
|
|
||||||
let schema: any = {
|
let schema: any = {
|
||||||
@@ -11,19 +12,19 @@ export const extractSchemaProps = <O extends object>(obj: O) =>
|
|||||||
default: value
|
default: value
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeIs(value, "array")) {
|
if (typeOf(value) === 'array') {
|
||||||
const item = value[0] // TODO merge other item schema's into one
|
const item = value[0] // TODO merge other item schema's into one
|
||||||
if (typeIs(item, "object")) {
|
if (typeOf(item) !== 'object') {
|
||||||
schema.items = {
|
schema.items = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: extractSchemaProps(item),
|
properties: extractSchemaProps(item),
|
||||||
default: item
|
default: item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO primitive-value arrays
|
// TODO support primitive-value arrays
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeIs(value, "object")) {
|
if (typeOf(value) === 'object') {
|
||||||
schema.properties = extractSchemaProps(value)
|
schema.properties = extractSchemaProps(value)
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -85,8 +85,7 @@ export const getInvokeOptions = (content?: string) => {
|
|||||||
export function toHex(str: string) {
|
export function toHex(str: string) {
|
||||||
var result = ''
|
var result = ''
|
||||||
for (var i = 0; i < str.length; i++) {
|
for (var i = 0; i < str.length; i++) {
|
||||||
const hex = str.charCodeAt(i).toString(16)
|
result += str.charCodeAt(i).toString(16)
|
||||||
result += hex.padStart(2, '0')
|
|
||||||
}
|
}
|
||||||
return result.toUpperCase()
|
return result.toUpperCase()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user