Compare commits

..

2 Commits

Author SHA1 Message Date
muzam1l
b0a6815cdd Update TrustSet transaction default fields. 2022-07-14 19:29:13 +05:30
muzam1l
3d24f0f50c Update wrong amount schema. 2022-07-14 19:19:59 +05:30
32 changed files with 1366 additions and 1852 deletions

View File

@@ -32,7 +32,6 @@ import { SetHookDialog } from "./SetHookDialog";
import { addFunds } from "../state/actions/addFaucetAccount"; import { addFunds } from "../state/actions/addFaucetAccount";
import { deleteHook } from "../state/actions/deployHook"; import { deleteHook } from "../state/actions/deployHook";
import { capitalize } from "../utils/helpers"; import { capitalize } from "../utils/helpers";
import { deleteAccount } from '../state/actions/deleteAccount';
export const AccountDialog = ({ export const AccountDialog = ({
activeAccountAddress, activeAccountAddress,
@@ -100,7 +99,10 @@ export const AccountDialog = ({
css={{ ml: "auto", mr: "$9" }} css={{ ml: "auto", mr: "$9" }}
tabIndex={-1} tabIndex={-1}
onClick={() => { onClick={() => {
deleteAccount(activeAccount?.address); const index = state.accounts.findIndex(
acc => acc.address === activeAccount?.address
);
state.accounts.splice(index, 1);
}} }}
> >
Delete Account <Trash size="15px" /> Delete Account <Trash size="15px" />

View File

@@ -1,135 +0,0 @@
import { CaretRight, Check, Circle } from "phosphor-react";
import { FC, Fragment, ReactNode } from "react";
import { Flex, Text } from "../";
import {
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuItem,
ContextMenuItemIndicator,
ContextMenuLabel,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuRoot,
ContextMenuSeparator,
ContextMenuTrigger,
ContextMenuTriggerItem,
} from "./primitive";
export type TextOption = {
type: "text";
label: ReactNode;
onSelect?: () => any;
children?: ContentMenuOption[];
};
export type SeparatorOption = { type: "separator" };
export type CheckboxOption = {
type: "checkbox";
label: ReactNode;
checked?: boolean;
onCheckedChange?: (isChecked: boolean) => any;
};
export type RadioOption<T extends string = string> = {
type: "radio";
label: ReactNode;
onValueChange?: (value: string) => any;
value: T;
options?: { value: T; label?: ReactNode }[];
};
type WithCommons = { key: string; disabled?: boolean };
export type ContentMenuOption = (
| TextOption
| SeparatorOption
| CheckboxOption
| RadioOption
) &
WithCommons;
export interface IContextMenu {
options?: ContentMenuOption[];
isNested?: boolean;
}
export const ContextMenu: FC<IContextMenu> = ({
children,
options,
isNested,
}) => {
return (
<ContextMenuRoot>
{isNested ? (
<ContextMenuTriggerItem>{children}</ContextMenuTriggerItem>
) : (
<ContextMenuTrigger>{children}</ContextMenuTrigger>
)}
{options && !!options.length && (
<ContextMenuContent sideOffset={isNested ? 2 : 5}>
{options.map(({ key, ...option }) => {
if (option.type === "text") {
const { children, label, onSelect } = option;
if (children)
return (
<ContextMenu isNested key={key} options={children}>
<Flex fluid row justify="space-between" align="center">
<Text>{label}</Text>
<CaretRight />
</Flex>
</ContextMenu>
);
return (
<ContextMenuItem key={key} onSelect={onSelect}>
{label}
</ContextMenuItem>
);
}
if (option.type === "checkbox") {
const { label, checked, onCheckedChange } = option;
return (
<ContextMenuCheckboxItem
key={key}
checked={checked}
onCheckedChange={onCheckedChange}
>
<Flex row align="center">
<ContextMenuItemIndicator>
<Check />
</ContextMenuItemIndicator>
<Text css={{ ml: checked ? "$4" : undefined }}>
{label}
</Text>
</Flex>
</ContextMenuCheckboxItem>
);
}
if (option.type === "radio") {
const { label, options, onValueChange, value } = option;
return (
<Fragment key={key}>
<ContextMenuLabel>{label}</ContextMenuLabel>
<ContextMenuRadioGroup
value={value}
onValueChange={onValueChange}
>
{options?.map(({ value: v, label }) => {
return (
<ContextMenuRadioItem key={v} value={v}>
<ContextMenuItemIndicator>
<Circle weight="fill" />
</ContextMenuItemIndicator>
<Text css={{ ml: "$4" }}>{label}</Text>
</ContextMenuRadioItem>
);
})}
</ContextMenuRadioGroup>
</Fragment>
);
}
return <ContextMenuSeparator key={key} />;
})}
</ContextMenuContent>
)}
</ContextMenuRoot>
);
};
export default ContextMenu;

View File

@@ -1,107 +0,0 @@
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
import { styled } from "../../stitches.config";
import {
slideDownAndFade,
slideLeftAndFade,
slideRightAndFade,
slideUpAndFade,
} from "../../styles/keyframes";
const StyledContent = styled(ContextMenuPrimitive.Content, {
minWidth: 140,
backgroundColor: "$backgroundOverlay",
borderRadius: 6,
overflow: "hidden",
padding: "5px",
boxShadow:
"0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2)",
"@media (prefers-reduced-motion: no-preference)": {
animationDuration: "400ms",
animationTimingFunction: "cubic-bezier(0.16, 1, 0.3, 1)",
willChange: "transform, opacity",
'&[data-state="open"]': {
'&[data-side="top"]': { animationName: slideDownAndFade },
'&[data-side="right"]': { animationName: slideLeftAndFade },
'&[data-side="bottom"]': { animationName: slideUpAndFade },
'&[data-side="left"]': { animationName: slideRightAndFade },
},
},
".dark &": {
boxShadow:
"0px 10px 38px -10px rgba(22, 23, 24, 0.85), 0px 10px 20px -15px rgba(22, 23, 24, 0.6)",
},
});
const itemStyles = {
all: "unset",
fontSize: 13,
lineHeight: 1,
color: "$text",
borderRadius: 3,
display: "flex",
alignItems: "center",
height: 28,
padding: "0 7px",
position: "relative",
paddingLeft: 10,
userSelect: "none",
"&[data-disabled]": {
color: "$textMuted",
pointerEvents: "none",
},
"&:focus": {
backgroundColor: "$purple9",
color: "$white",
},
};
const StyledItem = styled(ContextMenuPrimitive.Item, { ...itemStyles });
const StyledCheckboxItem = styled(ContextMenuPrimitive.CheckboxItem, {
...itemStyles,
});
const StyledRadioItem = styled(ContextMenuPrimitive.RadioItem, {
...itemStyles,
});
const StyledTriggerItem = styled(ContextMenuPrimitive.TriggerItem, {
'&[data-state="open"]': {
backgroundColor: "$purple9",
color: "$purple9",
},
...itemStyles,
});
const StyledLabel = styled(ContextMenuPrimitive.Label, {
paddingLeft: 10,
fontSize: 12,
lineHeight: "25px",
color: "$text",
});
const StyledSeparator = styled(ContextMenuPrimitive.Separator, {
height: 1,
backgroundColor: "$backgroundAlt",
margin: 5,
});
const StyledItemIndicator = styled(ContextMenuPrimitive.ItemIndicator, {
position: "absolute",
left: 0,
width: 25,
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
});
export const ContextMenuRoot = ContextMenuPrimitive.Root;
export const ContextMenuTrigger = ContextMenuPrimitive.Trigger;
export const ContextMenuContent = StyledContent;
export const ContextMenuItem = StyledItem;
export const ContextMenuCheckboxItem = StyledCheckboxItem;
export const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
export const ContextMenuRadioItem = StyledRadioItem;
export const ContextMenuItemIndicator = StyledItemIndicator;
export const ContextMenuTriggerItem = StyledTriggerItem;
export const ContextMenuLabel = StyledLabel;
export const ContextMenuSeparator = StyledSeparator;

View File

@@ -13,7 +13,7 @@ import state from "../state";
import wat from "../utils/wat-highlight"; import wat from "../utils/wat-highlight";
import EditorNavigation from "./EditorNavigation"; import EditorNavigation from "./EditorNavigation";
import { Button, Text, Link, Flex, Tabs, Tab } from "."; import { Button, Text, Link, Flex } from ".";
import Monaco from "./Monaco"; import Monaco from "./Monaco";
const FILESIZE_BREAKPOINTS: [number, number] = [2 * 1024, 5 * 1024]; const FILESIZE_BREAKPOINTS: [number, number] = [2 * 1024, 5 * 1024];
@@ -25,20 +25,9 @@ const DeployEditor = () => {
const [showContent, setShowContent] = useState(false); const [showContent, setShowContent] = useState(false);
const compiledFiles = snap.files.filter(file => file.compiledContent); const activeFile = snap.files[snap.active]?.compiledContent
const activeFile = compiledFiles[snap.activeWat]; ? snap.files[snap.active]
: snap.files.filter(file => file.compiledContent)[0];
const renderNav = () => (
<Tabs
activeIndex={snap.activeWat}
onChangeActive={idx => (state.activeWat = idx)}
>
{compiledFiles.map((file, index) => {
return <Tab key={file.name} header={`${file.name}.wat`} />;
})}
</Tabs>
);
const compiledSize = activeFile?.compiledContent?.byteLength || 0; const compiledSize = activeFile?.compiledContent?.byteLength || 0;
const color = const color =
compiledSize > FILESIZE_BREAKPOINTS[1] compiledSize > FILESIZE_BREAKPOINTS[1]
@@ -49,7 +38,7 @@ const DeployEditor = () => {
const isContentChanged = const isContentChanged =
activeFile && activeFile.compiledValueSnapshot !== activeFile.content; activeFile && activeFile.compiledValueSnapshot !== activeFile.content;
// const hasDeployErrors = activeFile && activeFile.containsErrors; // const hasDeployErros = activeFile && activeFile.containsErrors;
const CompiledStatView = activeFile && ( const CompiledStatView = activeFile && (
<Flex <Flex
@@ -110,7 +99,6 @@ const DeployEditor = () => {
</NextLink> </NextLink>
</Text> </Text>
); );
const isContent = const isContent =
snap.files?.filter(file => file.compiledWatContent).length > 0 && snap.files?.filter(file => file.compiledWatContent).length > 0 &&
router.isReady; router.isReady;
@@ -125,7 +113,7 @@ const DeployEditor = () => {
width: "100%", width: "100%",
}} }}
> >
<EditorNavigation renderNav={renderNav} /> <EditorNavigation showWat />
<Container <Container
css={{ css={{
display: "flex", display: "flex",

View File

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

View File

@@ -1,7 +1,27 @@
import { keyframes } from "@stitches/react";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import { styled } from "../stitches.config"; import { styled } from "../stitches.config";
import { slideDownAndFade, slideLeftAndFade, slideRightAndFade, slideUpAndFade } from '../styles/keyframes';
const slideUpAndFade = keyframes({
"0%": { opacity: 0, transform: "translateY(2px)" },
"100%": { opacity: 1, transform: "translateY(0)" },
});
const slideRightAndFade = keyframes({
"0%": { opacity: 0, transform: "translateX(-2px)" },
"100%": { opacity: 1, transform: "translateX(0)" },
});
const slideDownAndFade = keyframes({
"0%": { opacity: 0, transform: "translateY(-2px)" },
"100%": { opacity: 1, transform: "translateY(0)" },
});
const slideLeftAndFade = keyframes({
"0%": { opacity: 0, transform: "translateX(2px)" },
"100%": { opacity: 1, transform: "translateX(0)" },
});
const StyledContent = styled(DropdownMenuPrimitive.Content, { const StyledContent = styled(DropdownMenuPrimitive.Content, {
minWidth: 220, minWidth: 220,

View File

@@ -1,10 +1,6 @@
import React, { import React, { useState, useEffect, useCallback, useRef } from "react";
useState,
useEffect,
useRef,
ReactNode,
} from "react";
import { import {
Plus,
Share, Share,
DownloadSimple, DownloadSimple,
Gear, Gear,
@@ -32,6 +28,7 @@ import { useSnapshot } from "valtio";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { import {
createNewFile,
syncToGist, syncToGist,
updateEditorSettings, updateEditorSettings,
downloadAsZip, downloadAsZip,
@@ -51,23 +48,36 @@ import {
import Flex from "./Flex"; import Flex from "./Flex";
import Stack from "./Stack"; import Stack from "./Stack";
import { Input, Label } from "./Input"; import { Input, Label } from "./Input";
import Text from "./Text";
import Tooltip from "./Tooltip"; import Tooltip from "./Tooltip";
import { styled } from "../stitches.config";
import { showAlert } from "../state/actions/showAlert"; import { showAlert } from "../state/actions/showAlert";
const ErrorText = styled(Text, {
color: "$error",
mt: "$1",
display: "block",
});
const EditorNavigation = ({ renderNav }: { renderNav?: () => ReactNode }) => { const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
const snap = useSnapshot(state); const snap = useSnapshot(state);
const [editorSettingsOpen, setEditorSettingsOpen] = useState(false); const [editorSettingsOpen, setEditorSettingsOpen] = useState(false);
const [isNewfileDialogOpen, setIsNewfileDialogOpen] = useState(false);
const [newfileError, setNewfileError] = useState<string | null>(null);
const [filename, setFilename] = useState("");
const { data: session, status } = useSession(); const { data: session, status } = useSession();
const [popup, setPopUp] = useState(false); const [popup, setPopUp] = useState(false);
const [editorSettings, setEditorSettings] = useState(snap.editorSettings); const [editorSettings, setEditorSettings] = useState(snap.editorSettings);
useEffect(() => { useEffect(() => {
if (session && session.user && popup) { if (session && session.user && popup) {
setPopUp(false); setPopUp(false);
} }
}, [session, popup]); }, [session, popup]);
// when filename changes, reset error
useEffect(() => {
setNewfileError(null);
}, [filename, setNewfileError]);
const showNewGistAlert = () => { const showNewGistAlert = () => {
showAlert("Are you sure?", { showAlert("Are you sure?", {
@@ -85,8 +95,46 @@ const EditorNavigation = ({ renderNav }: { renderNav?: () => ReactNode }) => {
}); });
}; };
const validateFilename = useCallback(
(filename: string): { error: string | null } => {
// check if filename already exists
if (!filename) {
return { error: "You need to add filename" };
}
if (snap.files.find((file) => file.name === filename)) {
return { error: "Filename already exists." };
}
if (!filename.includes(".") || filename[filename.length - 1] === ".") {
return { error: "Filename should include file extension" };
}
// check for illegal characters
const ALPHA_NUMERICAL_REGEX = /^[A-Za-z0-9_-]+[.][A-Za-z0-9]{1,4}$/g;
if (!filename.match(ALPHA_NUMERICAL_REGEX)) {
return {
error: `Filename can contain only characters from a-z, A-Z, 0-9, "_" and "-" and it needs to have file extension (e.g. ".c")`,
};
}
return { error: null };
},
[snap.files]
);
const handleConfirm = useCallback(() => {
// add default extension in case omitted
const chk = validateFilename(filename);
if (chk && chk.error) {
setNewfileError(`Error: ${chk.error}`);
return;
}
setIsNewfileDialogOpen(false);
createNewFile(filename);
setFilename("");
}, [filename, setIsNewfileDialogOpen, setFilename, validateFilename]);
const scrollRef = useRef<HTMLDivElement>(null); const scrollRef = useRef<HTMLDivElement>(null);
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const files = snap.files;
return ( return (
<Flex css={{ flexShrink: 0, gap: "$0" }}> <Flex css={{ flexShrink: 0, gap: "$0" }}>
<Flex <Flex
@@ -126,14 +174,131 @@ const EditorNavigation = ({ renderNav }: { renderNav?: () => ReactNode }) => {
scrollbarWidth: "thin", scrollbarWidth: "thin",
}, },
}} }}
onWheelCapture={e => { onWheelCapture={(e) => {
if (scrollRef.current) { if (scrollRef.current) {
scrollRef.current.scrollLeft += e.deltaY; scrollRef.current.scrollLeft += e.deltaY;
} }
}} }}
> >
<Container css={{ flex: 1 }} ref={containerRef}> <Container css={{ flex: 1 }} ref={containerRef}>
{renderNav?.()} <Stack
css={{
gap: "$3",
flex: 1,
flexWrap: "nowrap",
marginBottom: "-1px",
}}
>
{files &&
files.length > 0 &&
files.map((file, index) => {
if (!file.compiledContent && showWat) {
return null;
}
return (
<Button
size="sm"
outline={
showWat ? snap.activeWat !== index : snap.active !== index
}
onClick={() => (state.active = index)}
key={file.name + index}
css={{
"&:hover": {
span: {
visibility: "visible",
},
},
}}
>
{file.name}
{showWat && ".wat"}
{!showWat && (
<Box
as="span"
css={{
display: "flex",
p: "2px",
borderRadius: "$full",
mr: "-4px",
"&:hover": {
// boxSizing: "0px 0px 1px",
backgroundColor: "$mauve2",
color: "$mauve12",
},
}}
onClick={(ev: React.MouseEvent<HTMLElement>) => {
ev.stopPropagation();
// Remove file from state
state.files.splice(index, 1);
// Change active file state
// If deleted file is behind active tab
// we keep the current state otherwise
// select previous file on the list
state.active =
index > snap.active ? snap.active : snap.active - 1;
}}
>
<X size="9px" weight="bold" />
</Box>
)}
</Button>
);
})}
{!showWat && (
<Dialog
open={isNewfileDialogOpen}
onOpenChange={setIsNewfileDialogOpen}
>
<DialogTrigger asChild>
<Button
ghost
size="sm"
css={{ alignItems: "center", px: "$2", mr: "$3" }}
>
<Plus size="16px" />{" "}
{snap.files.length === 0 && "Add new file"}
</Button>
</DialogTrigger>
<DialogContent>
<DialogTitle>Create new file</DialogTitle>
<DialogDescription>
<Label>Filename</Label>
<Input
value={filename}
onChange={(e) => setFilename(e.target.value)}
onKeyPress={(e) => {
if (e.key === "Enter") {
handleConfirm();
}
}}
/>
<ErrorText>{newfileError}</ErrorText>
</DialogDescription>
<Flex
css={{
marginTop: 25,
justifyContent: "flex-end",
gap: "$3",
}}
>
<DialogClose asChild>
<Button outline>Cancel</Button>
</DialogClose>
<Button variant="primary" onClick={handleConfirm}>
Create file
</Button>
</Flex>
<DialogClose asChild>
<Box css={{ position: "absolute", top: "$3", right: "$3" }}>
<X size="20px" />
</Box>
</DialogClose>
</DialogContent>
</Dialog>
)}
</Stack>
</Container> </Container>
</Flex> </Flex>
<Flex <Flex
@@ -377,8 +542,8 @@ const EditorNavigation = ({ renderNav }: { renderNav?: () => ReactNode }) => {
type="number" type="number"
min="1" min="1"
value={editorSettings.tabSize} value={editorSettings.tabSize}
onChange={e => onChange={(e) =>
setEditorSettings(curr => ({ setEditorSettings((curr) => ({
...curr, ...curr,
tabSize: Number(e.target.value), tabSize: Number(e.target.value),
})) }))

View File

@@ -7,7 +7,7 @@ import { useRouter } from "next/router";
import Box from "./Box"; import Box from "./Box";
import Container from "./Container"; import Container from "./Container";
import { createNewFile, saveFile } from "../state/actions"; import { saveFile } from "../state/actions";
import { apiHeaderFiles } from "../state/constants"; import { apiHeaderFiles } from "../state/constants";
import state from "../state"; import state from "../state";
@@ -20,9 +20,7 @@ import ReconnectingWebSocket from "reconnecting-websocket";
import docs from "../xrpl-hooks-docs/docs"; import docs from "../xrpl-hooks-docs/docs";
import Monaco from "./Monaco"; import Monaco from "./Monaco";
import { saveAllFiles } from "../state/actions/saveFile"; import { saveAllFiles } from '../state/actions/saveFile';
import { Tab, Tabs } from "./Tabs";
import { renameFile } from "../state/actions/createNewFile";
const validateWritability = (editor: monaco.editor.IStandaloneCodeEditor) => { const validateWritability = (editor: monaco.editor.IStandaloneCodeEditor) => {
const currPath = editor.getModel()?.uri.path; const currPath = editor.getModel()?.uri.path;
@@ -121,27 +119,6 @@ const HooksEditor = () => {
}, []); }, []);
const file = snap.files[snap.active]; const file = snap.files[snap.active];
const renderNav = () => (
<Tabs
label="File"
activeIndex={snap.active}
onChangeActive={idx => (state.active = idx)}
extensionRequired
onCreateNewTab={createNewFile}
onCloseTab={idx => state.files.splice(idx, 1)}
onRenameTab={(idx, nwName, oldName = "") => renameFile(oldName, nwName)}
headerExtraValidation={{
regex: /^[A-Za-z0-9_-]+[.][A-Za-z0-9]{1,4}$/g,
error:
'Filename can contain only characters from a-z, A-Z, 0-9, "_" and "-"',
}}
>
{snap.files.map((file, index) => {
return <Tab key={file.name} header={file.name} />;
})}
</Tabs>
);
return ( return (
<Box <Box
css={{ css={{
@@ -154,7 +131,7 @@ const HooksEditor = () => {
width: "100%", width: "100%",
}} }}
> >
<EditorNavigation renderNav={renderNav} /> <EditorNavigation />
{snap.files.length > 0 && router.isReady ? ( {snap.files.length > 0 && router.isReady ? (
<Monaco <Monaco
keepCurrentModel keepCurrentModel

View File

@@ -162,7 +162,7 @@ export const Log: FC<ILog> = ({
const enrichAccounts = useCallback( const enrichAccounts = useCallback(
(str?: string): ReactNode => { (str?: string): ReactNode => {
if (!str || !accounts.length) return str; if (!str || !accounts.length) return null;
const pattern = `(${accounts.map(acc => acc.address).join("|")})`; const pattern = `(${accounts.map(acc => acc.address).join("|")})`;
const res = regexifyString({ const res = regexifyString({

View File

@@ -91,7 +91,7 @@ const Select = forwardRef<any, Props>((props, ref) => {
...provided, ...provided,
color: colors.searchText, color: colors.searchText,
backgroundColor: backgroundColor:
state.isFocused state.isSelected || state.isFocused
? colors.activeLight ? colors.activeLight
: colors.dropDownBg, : colors.dropDownBg,
":hover": { ":hover": {

View File

@@ -1,6 +1,7 @@
import React, { useCallback, useEffect, useState } from "react"; import React, { useCallback, useEffect, useState } from "react";
import { Plus, Trash, X } from "phosphor-react"; import { Plus, Trash, X } from "phosphor-react";
import { Button, Box, Text } from "."; import Button from "./Button";
import Box from "./Box";
import { Stack, Flex, Select } from "."; import { Stack, Flex, Select } from ".";
import { import {
Dialog, Dialog,
@@ -18,30 +19,48 @@ import {
useForm, useForm,
} from "react-hook-form"; } from "react-hook-form";
import { TTS, tts } from "../utils/hookOnCalculator";
import { deployHook } from "../state/actions"; import { deployHook } from "../state/actions";
import { useSnapshot } from "valtio"; import { useSnapshot } from "valtio";
import state, { IFile, SelectOption } from "../state"; import state, { SelectOption } from "../state";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { prepareDeployHookTx, sha256 } from "../state/actions/deployHook"; import { prepareDeployHookTx, sha256 } from "../state/actions/deployHook";
import estimateFee from "../utils/estimateFee"; import estimateFee from "../utils/estimateFee";
import {
getParameters, const transactionOptions = Object.keys(tts).map(key => ({
getInvokeOptions, label: key,
transactionOptions, value: key as keyof TTS,
SetHookData, }));
} from "../utils/setHook";
import { capitalize } from "../utils/helpers"; export type SetHookData = {
Invoke: {
value: keyof TTS;
label: string;
}[];
Fee: string;
HookNamespace: string;
HookParameters: {
HookParameter: {
HookParameterName: string;
HookParameterValue: string;
};
}[];
// HookGrants: {
// HookGrant: {
// Authorize: string;
// HookHash: string;
// };
// }[];
};
export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo( export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
({ accountAddress }) => { ({ accountAddress }) => {
const snap = useSnapshot(state); const snap = useSnapshot(state);
const activeFile = snap.files[snap.active]?.compiledContent
const [estimateLoading, setEstimateLoading] = useState(false); ? snap.files[snap.active]
: snap.files.filter(file => file.compiledContent)[0];
const [isSetHookDialogOpen, setIsSetHookDialogOpen] = useState(false); const [isSetHookDialogOpen, setIsSetHookDialogOpen] = useState(false);
const compiledFiles = snap.files.filter(file => file.compiledContent);
const activeFile = compiledFiles[snap.activeWat] as IFile | undefined;
const accountOptions: SelectOption[] = snap.accounts.map(acc => ({ const accountOptions: SelectOption[] = snap.accounts.map(acc => ({
label: acc.name, label: acc.name,
value: acc.address, value: acc.address,
@@ -53,26 +72,6 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
const account = snap.accounts.find( const account = snap.accounts.find(
acc => acc.address === selectedAccount?.value acc => acc.address === selectedAccount?.value
); );
const getHookNamespace = useCallback(
() =>
(activeFile && snap.deployValues[activeFile.name]?.HookNamespace) ||
activeFile?.name.split(".")[0] ||
"",
[activeFile, snap.deployValues]
);
const getDefaultValues = useCallback((): Partial<SetHookData> => {
const content = activeFile?.compiledValueSnapshot;
return (
(activeFile && snap.deployValues[activeFile.name]) || {
HookNamespace: getHookNamespace(),
Invoke: getInvokeOptions(content),
HookParameters: getParameters(content),
}
);
}, [activeFile, getHookNamespace, snap.deployValues]);
const { const {
register, register,
handleSubmit, handleSubmit,
@@ -80,26 +79,38 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
watch, watch,
setValue, setValue,
getValues, getValues,
reset,
formState: { errors }, formState: { errors },
} = useForm<SetHookData>({ } = useForm<SetHookData>({
defaultValues: getDefaultValues(), defaultValues: snap.deployValues?.[activeFile?.name]
? snap.deployValues[activeFile?.name]
: {
HookNamespace:
snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || "",
Invoke: transactionOptions.filter(to => to.label === "ttPAYMENT"),
},
}); });
const { fields, append, remove } = useFieldArray({ const { fields, append, remove } = useFieldArray({
control, control,
name: "HookParameters", // unique name for your Field Array name: "HookParameters", // unique name for your Field Array
}); });
const [formInitialized, setFormInitialized] = useState(false);
const [estimateLoading, setEstimateLoading] = useState(false);
const watchedFee = watch("Fee"); const watchedFee = watch("Fee");
// Reset form if activeFile changes // Update value if activeWat changes
useEffect(() => { useEffect(() => {
if (!activeFile) return; const defaultValue = snap.deployValues?.[activeFile?.name]
const defaultValues = getDefaultValues(); ? snap.deployValues?.[activeFile?.name].HookNamespace
: snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || "";
reset(defaultValues); setValue("HookNamespace", defaultValue);
}, [activeFile, getDefaultValues, reset]); setFormInitialized(true);
}, [
snap.activeWat,
snap.files,
setValue,
activeFile?.name,
snap.deployValues,
]);
useEffect(() => { useEffect(() => {
if ( if (
watchedFee && watchedFee &&
@@ -117,21 +128,24 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
// name: "HookGrants", // unique name for your Field Array // name: "HookGrants", // unique name for your Field Array
// }); // });
const [hashedNamespace, setHashedNamespace] = useState(""); const [hashedNamespace, setHashedNamespace] = useState("");
const namespace = watch(
const namespace = watch("HookNamespace", getHookNamespace()); "HookNamespace",
snap.deployValues?.[activeFile?.name]
? snap.deployValues?.[activeFile?.name].HookNamespace
: snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || ""
);
const calculateHashedValue = useCallback(async () => { const calculateHashedValue = useCallback(async () => {
const hashedVal = await sha256(namespace); const hashedVal = await sha256(namespace);
setHashedNamespace(hashedVal.toUpperCase()); setHashedNamespace(hashedVal.toUpperCase());
}, [namespace]); }, [namespace]);
useEffect(() => { useEffect(() => {
calculateHashedValue(); calculateHashedValue();
}, [namespace, calculateHashedValue]); }, [namespace, calculateHashedValue]);
const calculateFee = useCallback(async () => { // Calcucate initial fee estimate when modal opens
if (!account) return; useEffect(() => {
if (formInitialized && account) {
(async () => {
const formValues = getValues(); const formValues = getValues();
const tx = await prepareDeployHookTx(account, formValues); const tx = await prepareDeployHookTx(account, formValues);
if (!tx) { if (!tx) {
@@ -141,27 +155,27 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
if (res && res.base_fee) { if (res && res.base_fee) {
setValue("Fee", Math.round(Number(res.base_fee || "")).toString()); setValue("Fee", Math.round(Number(res.base_fee || "")).toString());
} }
}, [account, getValues, setValue]); })();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [formInitialized]);
const tooLargeFile = () => { const tooLargeFile = () => {
const activeFile = snap.files[snap.active].compiledContent
? snap.files[snap.active]
: snap.files.filter(file => file.compiledContent)[0];
return Boolean( return Boolean(
activeFile?.compiledContent?.byteLength && activeFile?.compiledContent?.byteLength &&
activeFile?.compiledContent?.byteLength >= 64000 activeFile?.compiledContent?.byteLength >= 64000
); );
}; };
const onSubmit: SubmitHandler<SetHookData> = async data => { const onSubmit: SubmitHandler<SetHookData> = async (data) => {
const currAccount = state.accounts.find( const currAccount = state.accounts.find(
acc => acc.address === account?.address (acc) => acc.address === account?.address
); );
if (!account) return; if (!account) return;
if (currAccount) currAccount.isLoading = true; if (currAccount) currAccount.isLoading = true;
data.HookParameters.forEach(param => {
delete param.$metaData;
return param;
});
const res = await deployHook(account, data); const res = await deployHook(account, data);
if (currAccount) currAccount.isLoading = false; if (currAccount) currAccount.isLoading = false;
@@ -171,14 +185,8 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
} }
toast.error(`Transaction failed! (${res?.engine_result_message})`); toast.error(`Transaction failed! (${res?.engine_result_message})`);
}; };
const onOpenChange = useCallback((open: boolean) => {
setIsSetHookDialogOpen(open);
if (open) calculateFee();
}, [calculateFee]);
return ( return (
<Dialog open={isSetHookDialogOpen} onOpenChange={onOpenChange}> <Dialog open={isSetHookDialogOpen} onOpenChange={setIsSetHookDialogOpen}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button <Button
ghost ghost
@@ -186,7 +194,10 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
uppercase uppercase
variant={"secondary"} variant={"secondary"}
disabled={ disabled={
!account || account.isLoading || !activeFile || tooLargeFile() !account ||
account.isLoading ||
!snap.files.filter(file => file.compiledWatContent).length ||
tooLargeFile()
} }
> >
Set Hook Set Hook
@@ -202,6 +213,7 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
<Select <Select
instanceId="deploy-account" instanceId="deploy-account"
placeholder="Select account" placeholder="Select account"
hideSelectedOptions
options={accountOptions} options={accountOptions}
value={selectedAccount} value={selectedAccount}
onChange={(acc: any) => setSelectedAccount(acc)} onChange={(acc: any) => setSelectedAccount(acc)}
@@ -247,39 +259,22 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
<Stack> <Stack>
{fields.map((field, index) => ( {fields.map((field, index) => (
<Stack key={field.id}> <Stack key={field.id}>
<Flex column>
<Flex row>
<Input <Input
// important to include key with field's id // important to include key with field's id
placeholder="Parameter name" placeholder="Parameter name"
readOnly={field.$metaData?.required}
{...register( {...register(
`HookParameters.${index}.HookParameter.HookParameterName` `HookParameters.${index}.HookParameter.HookParameterName`
)} )}
/> />
<Input <Input
css={{ mx: "$2" }}
placeholder="Value (hex-quoted)" placeholder="Value (hex-quoted)"
{...register( {...register(
`HookParameters.${index}.HookParameter.HookParameterValue`, `HookParameters.${index}.HookParameter.HookParameterValue`
{ required: field.$metaData?.required }
)} )}
/> />
<Button <Button onClick={() => remove(index)} variant="destroy">
onClick={() => remove(index)}
variant="destroy"
>
<Trash weight="regular" size="16px" /> <Trash weight="regular" size="16px" />
</Button> </Button>
</Flex>
{errors.HookParameters?.[index]?.HookParameter
?.HookParameterValue?.type === "required" && (
<Text error>This field is required</Text>
)}
<Label css={{ fontSize: "$sm", mt: "$1" }}>
{capitalize(field.$metaData?.description)}
</Label>
</Flex>
</Stack> </Stack>
))} ))}
<Button <Button

View File

@@ -6,7 +6,7 @@ import React, {
useCallback, useCallback,
} from "react"; } from "react";
import type { ReactNode, ReactElement } from "react"; import type { ReactNode, ReactElement } from "react";
import { Box, Button, Flex, Input, Label, Pre, Stack, Text } from "."; import { Box, Button, Flex, Input, Label, Stack, Text } from ".";
import { import {
Dialog, Dialog,
DialogTrigger, DialogTrigger,
@@ -17,8 +17,6 @@ import {
} from "./Dialog"; } from "./Dialog";
import { Plus, X } from "phosphor-react"; import { Plus, X } from "phosphor-react";
import { styled } from "../stitches.config"; import { styled } from "../stitches.config";
import { capitalize } from "../utils/helpers";
import ContextMenu, { ContentMenuOption } from "./ContextMenu";
const ErrorText = styled(Text, { const ErrorText = styled(Text, {
color: "$error", color: "$error",
@@ -26,14 +24,12 @@ const ErrorText = styled(Text, {
display: "block", display: "block",
}); });
type Nullable<T> = T | null | undefined | false;
interface TabProps { interface TabProps {
header: string; header?: string;
children?: ReactNode; children: ReactNode;
} }
// TODO customize messages shown // TODO customise messages shown
interface Props { interface Props {
label?: string; label?: string;
activeIndex?: number; activeIndex?: number;
@@ -42,14 +38,9 @@ interface Props {
children: ReactElement<TabProps>[]; children: ReactElement<TabProps>[];
keepAllAlive?: boolean; keepAllAlive?: boolean;
defaultExtension?: string; defaultExtension?: string;
extensionRequired?: boolean; appendDefaultExtension?: boolean;
allowedExtensions?: string[]; allowedExtensions?: string[];
headerExtraValidation?: {
regex: string | RegExp;
error: string;
};
onCreateNewTab?: (name: string) => any; onCreateNewTab?: (name: string) => any;
onRenameTab?: (index: number, nwName: string, oldName?: string) => any;
onCloseTab?: (index: number, header?: string) => any; onCloseTab?: (index: number, header?: string) => any;
onChangeActive?: (index: number, header?: string) => any; onChangeActive?: (index: number, header?: string) => any;
} }
@@ -66,19 +57,16 @@ export const Tabs = ({
onCreateNewTab, onCreateNewTab,
onCloseTab, onCloseTab,
onChangeActive, onChangeActive,
onRenameTab,
headerExtraValidation,
extensionRequired,
defaultExtension = "", defaultExtension = "",
appendDefaultExtension = false,
allowedExtensions, allowedExtensions,
}: Props) => { }: Props) => {
const [active, setActive] = useState(activeIndex || 0); const [active, setActive] = useState(activeIndex || 0);
const tabs: TabProps[] = children.map(elem => elem.props); const tabs: TabProps[] = children.map(elem => elem.props);
const [isNewtabDialogOpen, setIsNewtabDialogOpen] = useState(false); const [isNewtabDialogOpen, setIsNewtabDialogOpen] = useState(false);
const [renamingTab, setRenamingTab] = useState<number | null>(null);
const [tabname, setTabname] = useState(""); const [tabname, setTabname] = useState("");
const [tabnameError, setTabnameError] = useState<string | null>(null); const [newtabError, setNewtabError] = useState<string | null>(null);
useEffect(() => { useEffect(() => {
if (activeIndex) setActive(activeIndex); if (activeIndex) setActive(activeIndex);
@@ -94,46 +82,21 @@ export const Tabs = ({
// when filename changes, reset error // when filename changes, reset error
useEffect(() => { useEffect(() => {
setTabnameError(null); setNewtabError(null);
}, [tabname, setTabnameError]); }, [tabname, setNewtabError]);
const validateTabname = useCallback( const validateTabname = useCallback(
(tabname: string): { error?: string, result?: string } => { (tabname: string): { error: string | null } => {
if (!tabname) {
return { error: `Please enter ${label.toLocaleLowerCase()} name.` };
}
let ext =
(tabname.includes(".") && tabname.split(".").pop()) || "";
if (!ext && defaultExtension) {
ext = defaultExtension
tabname = `${tabname}.${defaultExtension}`
}
if (tabs.find(tab => tab.header === tabname)) { if (tabs.find(tab => tab.header === tabname)) {
return { error: `${capitalize(label)} name already exists.` }; return { error: "Name already exists." };
}
if (extensionRequired && !ext) {
return { error: "File extension is required!" };
} }
const ext = tabname.split(".").pop() || "";
if (allowedExtensions && !allowedExtensions.includes(ext)) { if (allowedExtensions && !allowedExtensions.includes(ext)) {
return { error: "This file extension is not allowed!" }; return { error: "This file extension is not allowed!" };
} }
if ( return { error: null };
headerExtraValidation &&
!tabname.match(headerExtraValidation.regex)
) {
return { error: headerExtraValidation.error };
}
return { result: tabname };
}, },
[ [allowedExtensions, tabs]
allowedExtensions,
defaultExtension,
extensionRequired,
headerExtraValidation,
label,
tabs,
]
); );
const handleActiveChange = useCallback( const handleActiveChange = useCallback(
@@ -144,41 +107,37 @@ export const Tabs = ({
[onChangeActive] [onChangeActive]
); );
const handleRenameTab = useCallback(() => {
if (renamingTab === null) return;
const res = validateTabname(tabname);
if (res.error) {
setTabnameError(`Error: ${res.error}`);
return;
}
const { result: _tabname = tabname } = res
setRenamingTab(null);
setTabname("");
const oldName = tabs[renamingTab]?.header;
onRenameTab?.(renamingTab, _tabname, oldName);
handleActiveChange(renamingTab);
}, [handleActiveChange, onRenameTab, renamingTab, tabname, tabs, validateTabname]);
const handleCreateTab = useCallback(() => { const handleCreateTab = useCallback(() => {
const res = validateTabname(tabname); // add default extension in case omitted
if (res.error) { let _tabname = tabname.includes(".")
setTabnameError(`Error: ${res.error}`); ? tabname
: `${tabname}.${defaultExtension}`;
if (appendDefaultExtension && !_tabname.endsWith(defaultExtension)) {
_tabname = `${_tabname}.${defaultExtension}`;
}
const chk = validateTabname(_tabname);
if (chk.error) {
setNewtabError(`Error: ${chk.error}`);
return; return;
} }
const { result: _tabname = tabname } = res
setIsNewtabDialogOpen(false); setIsNewtabDialogOpen(false);
setTabname(""); setTabname("");
onCreateNewTab?.(_tabname); onCreateNewTab?.(_tabname);
// switch to new tab?
handleActiveChange(tabs.length, _tabname); handleActiveChange(tabs.length, _tabname);
}, [validateTabname, tabname, onCreateNewTab, handleActiveChange, tabs.length]); }, [
tabname,
defaultExtension,
appendDefaultExtension,
validateTabname,
onCreateNewTab,
handleActiveChange,
tabs.length,
]);
const handleCloseTab = useCallback( const handleCloseTab = useCallback(
(idx: number) => { (idx: number) => {
@@ -187,27 +146,10 @@ export const Tabs = ({
} }
onCloseTab?.(idx, tabs[idx].header); onCloseTab?.(idx, tabs[idx].header);
handleActiveChange(idx, tabs[idx].header);
}, },
[active, handleActiveChange, onCloseTab, tabs] [active, onCloseTab, tabs]
); );
const closeOption = (idx: number): Nullable<ContentMenuOption> =>
onCloseTab && {
type: "text",
label: "Close",
key: "close",
onSelect: () => handleCloseTab(idx),
};
const renameOption = (idx: number): Nullable<ContentMenuOption> =>
onRenameTab && {
type: "text",
label: "Rename",
key: "rename",
onSelect: () => setRenamingTab(idx),
};
return ( return (
<> <>
{!headless && ( {!headless && (
@@ -222,15 +164,8 @@ export const Tabs = ({
}} }}
> >
{tabs.map((tab, idx) => ( {tabs.map((tab, idx) => (
<ContextMenu
key={tab.header}
options={
[closeOption(idx), renameOption(idx)].filter(
Boolean
) as ContentMenuOption[]
}
>
<Button <Button
key={tab.header}
role="tab" role="tab"
tabIndex={idx} tabIndex={idx}
onClick={() => handleActiveChange(idx, tab.header)} onClick={() => handleActiveChange(idx, tab.header)}
@@ -269,7 +204,6 @@ export const Tabs = ({
</Box> </Box>
)} )}
</Button> </Button>
</ContextMenu>
))} ))}
{onCreateNewTab && ( {onCreateNewTab && (
<Dialog <Dialog
@@ -282,14 +216,11 @@ export const Tabs = ({
size="sm" size="sm"
css={{ alignItems: "center", px: "$2", mr: "$3" }} css={{ alignItems: "center", px: "$2", mr: "$3" }}
> >
<Plus size="16px" />{" "} <Plus size="16px" /> {tabs.length === 0 && `Add new ${label.toLocaleLowerCase()}`}
{tabs.length === 0 && `Add new ${label.toLocaleLowerCase()}`}
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent> <DialogContent>
<DialogTitle> <DialogTitle>Create new {label.toLocaleLowerCase()}</DialogTitle>
Create new {label.toLocaleLowerCase()}
</DialogTitle>
<DialogDescription> <DialogDescription>
<Label>{label} name</Label> <Label>{label} name</Label>
<Input <Input
@@ -301,7 +232,7 @@ export const Tabs = ({
} }
}} }}
/> />
<ErrorText>{tabnameError}</ErrorText> <ErrorText>{newtabError}</ErrorText>
</DialogDescription> </DialogDescription>
<Flex <Flex
@@ -326,55 +257,10 @@ export const Tabs = ({
</DialogContent> </DialogContent>
</Dialog> </Dialog>
)} )}
{onRenameTab && (
<Dialog
open={renamingTab !== null}
onOpenChange={() => setRenamingTab(null)}
>
<DialogContent>
<DialogTitle>
Rename <Pre>{tabs[renamingTab || 0]?.header}</Pre>
</DialogTitle>
<DialogDescription>
<Label>Enter new name</Label>
<Input
value={tabname}
onChange={e => setTabname(e.target.value)}
onKeyPress={e => {
if (e.key === "Enter") {
handleRenameTab();
}
}}
/>
<ErrorText>{tabnameError}</ErrorText>
</DialogDescription>
<Flex
css={{
marginTop: 25,
justifyContent: "flex-end",
gap: "$3",
}}
>
<DialogClose asChild>
<Button outline>Cancel</Button>
</DialogClose>
<Button variant="primary" onClick={handleRenameTab}>
Confirm
</Button>
</Flex>
<DialogClose asChild>
<Box css={{ position: "absolute", top: "$3", right: "$3" }}>
<X size="20px" />
</Box>
</DialogClose>
</DialogContent>
</Dialog>
)}
</Stack> </Stack>
)} )}
{keepAllAlive {keepAllAlive ? (
? tabs.map((tab, idx) => { tabs.map((tab, idx) => {
// TODO Maybe rule out fragments as children // TODO Maybe rule out fragments as children
if (!isValidElement(tab.children)) { if (!isValidElement(tab.children)) {
if (active !== idx) return null; if (active !== idx) return null;
@@ -387,14 +273,11 @@ export const Tabs = ({
<children.type <children.type
key={key} key={key}
{...props} {...props}
style={{ style={{ ...style, display: active !== idx ? "none" : undefined }}
...style,
display: active !== idx ? "none" : undefined,
}}
/> />
); );
}) })
: tabs[active] && ( ) : (
<Fragment key={tabs[active].header || active}> <Fragment key={tabs[active].header || active}>
{tabs[active].children} {tabs[active].children}
</Fragment> </Fragment>

View File

@@ -1,14 +1,11 @@
import { Play } from "phosphor-react"; import { Play } from "phosphor-react";
import { FC, useCallback, useEffect } from "react"; import { FC, useCallback, useEffect, useMemo } from "react";
import { useSnapshot } from "valtio"; import { useSnapshot } from "valtio";
import state from "../../state"; import state from "../../state";
import { import {
defaultTransactionType, modifyTransaction,
getTxFields,
modifyTxState,
prepareState, prepareState,
prepareTransaction, prepareTransaction,
SelectOption,
TransactionState, TransactionState,
} from "../../state/transactions"; } from "../../state/transactions";
import { sendTransaction } from "../../state/actions"; import { sendTransaction } from "../../state/actions";
@@ -18,7 +15,7 @@ import Flex from "../Flex";
import { TxJson } from "./json"; import { TxJson } from "./json";
import { TxUI } from "./ui"; import { TxUI } from "./ui";
import { default as _estimateFee } from "../../utils/estimateFee"; import { default as _estimateFee } from "../../utils/estimateFee";
import toast from "react-hot-toast"; import toast from 'react-hot-toast';
export interface TransactionProps { export interface TransactionProps {
header: string; header: string;
@@ -37,18 +34,19 @@ const Transaction: FC<TransactionProps> = ({
txIsDisabled, txIsDisabled,
txIsLoading, txIsLoading,
viewType, viewType,
editorSavedValue,
editorValue, editorValue,
} = txState; } = txState;
const setState = useCallback( const setState = useCallback(
(pTx?: Partial<TransactionState>) => { (pTx?: Partial<TransactionState>) => {
return modifyTxState(header, pTx); return modifyTransaction(header, pTx);
}, },
[header] [header]
); );
const prepareOptions = useCallback( const prepareOptions = useCallback(
(state: Partial<TransactionState> = txState) => { (state: TransactionState = txState) => {
const { const {
selectedTransaction, selectedTransaction,
selectedDestAccount, selectedDestAccount,
@@ -57,7 +55,9 @@ const Transaction: FC<TransactionProps> = ({
} = state; } = state;
const TransactionType = selectedTransaction?.value || null; const TransactionType = selectedTransaction?.value || null;
const Destination = selectedDestAccount?.value || txFields?.Destination; const Destination =
selectedDestAccount?.value ||
("Destination" in txFields ? null : undefined);
const Account = selectedAccount?.value || null; const Account = selectedAccount?.value || null;
return prepareTransaction({ return prepareTransaction({
@@ -109,9 +109,8 @@ const Transaction: FC<TransactionProps> = ({
} }
const options = prepareOptions(st); const options = prepareOptions(st);
const fields = getTxFields(options.TransactionType); if (options.Destination === null) {
if (fields.Destination && !options.Destination) { throw Error("Destination account cannot be null");
throw Error("Destination account is required!");
} }
await sendTransaction(account, options, { logPrefix }); await sendTransaction(account, options, { logPrefix });
@@ -137,38 +136,15 @@ const Transaction: FC<TransactionProps> = ({
prepareOptions, prepareOptions,
]); ]);
const getJsonString = useCallback( const resetState = useCallback(() => {
(state?: Partial<TransactionState>) => modifyTransaction(header, { viewType }, { replaceState: true });
JSON.stringify( }, [header, viewType]);
prepareOptions?.(state) || {},
null,
editorSettings.tabSize
),
[editorSettings.tabSize, prepareOptions]
);
const resetState = useCallback( const jsonValue = useMemo(
(transactionType: SelectOption | undefined = defaultTransactionType) => { () =>
const fields = getTxFields(transactionType?.value); editorSavedValue ||
JSON.stringify(prepareOptions?.() || {}, null, editorSettings.tabSize),
const nwState: Partial<TransactionState> = { [editorSavedValue, editorSettings.tabSize, prepareOptions]
viewType,
selectedTransaction: transactionType,
};
if (fields.Destination !== undefined) {
nwState.selectedDestAccount = null;
fields.Destination = "";
} else {
fields.Destination = undefined;
}
nwState.txFields = fields;
const state = modifyTxState(header, nwState, { replaceState: true });
const editorValue = getJsonString(state);
return setState({ editorValue });
},
[getJsonString, header, setState, viewType]
); );
const estimateFee = useCallback( const estimateFee = useCallback(
@@ -180,10 +156,10 @@ const Transaction: FC<TransactionProps> = ({
); );
if (!account) { if (!account) {
if (!opts?.silent) { if (!opts?.silent) {
toast.error("Please select account from the list."); toast.error("Please select account from the list.")
}
return;
} }
return
};
ptx.Account = account.address; ptx.Account = account.address;
ptx.Sequence = account.sequence; ptx.Sequence = account.sequence;
@@ -200,7 +176,7 @@ const Transaction: FC<TransactionProps> = ({
<Box css={{ position: "relative", height: "calc(100% - 28px)" }} {...props}> <Box css={{ position: "relative", height: "calc(100% - 28px)" }} {...props}>
{viewType === "json" ? ( {viewType === "json" ? (
<TxJson <TxJson
getJsonString={getJsonString} value={jsonValue}
header={header} header={header}
state={txState} state={txState}
setState={setState} setState={setState}
@@ -223,7 +199,7 @@ const Transaction: FC<TransactionProps> = ({
<Button <Button
onClick={() => { onClick={() => {
if (viewType === "ui") { if (viewType === "ui") {
setState({ viewType: "json" }); setState({ editorSavedValue: null, viewType: "json" });
} else setState({ viewType: "ui" }); } else setState({ viewType: "ui" });
}} }}
outline outline
@@ -231,7 +207,7 @@ const Transaction: FC<TransactionProps> = ({
{viewType === "ui" ? "EDIT AS JSON" : "EXIT JSON MODE"} {viewType === "ui" ? "EDIT AS JSON" : "EXIT JSON MODE"}
</Button> </Button>
<Flex row> <Flex row>
<Button onClick={() => resetState()} outline css={{ mr: "$3" }}> <Button onClick={resetState} outline css={{ mr: "$3" }}>
RESET RESET
</Button> </Button>
<Button <Button

View File

@@ -1,4 +1,4 @@
import { FC, useCallback, useEffect, useMemo, useState } from "react"; import { FC, useCallback, useEffect, useState } from "react";
import { useSnapshot } from "valtio"; import { useSnapshot } from "valtio";
import state, { import state, {
prepareState, prepareState,
@@ -15,7 +15,7 @@ import Monaco from "../Monaco";
import type monaco from "monaco-editor"; import type monaco from "monaco-editor";
interface JsonProps { interface JsonProps {
getJsonString?: (state?: Partial<TransactionState>) => string; value?: string;
header?: string; header?: string;
setState: (pTx?: Partial<TransactionState> | undefined) => void; setState: (pTx?: Partial<TransactionState> | undefined) => void;
state: TransactionState; state: TransactionState;
@@ -23,23 +23,22 @@ interface JsonProps {
} }
export const TxJson: FC<JsonProps> = ({ export const TxJson: FC<JsonProps> = ({
getJsonString, value = "",
state: txState, state: txState,
header, header,
setState, setState,
}) => { }) => {
const { editorSettings, accounts } = useSnapshot(state); const { editorSettings, accounts } = useSnapshot(state);
const { editorValue, estimatedFee } = txState; const { editorValue = value, estimatedFee } = txState;
const [hasUnsaved, setHasUnsaved] = useState(false);
const [currTxType, setCurrTxType] = useState<string | undefined>( const [currTxType, setCurrTxType] = useState<string | undefined>(
txState.selectedTransaction?.value txState.selectedTransaction?.value
); );
useEffect(() => { useEffect(() => {
setState({ setState({ editorValue: value });
editorValue: getJsonString?.(),
});
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, [value]);
useEffect(() => { useEffect(() => {
const parsed = parseJSON(editorValue); const parsed = parseJSON(editorValue);
@@ -53,22 +52,21 @@ export const TxJson: FC<JsonProps> = ({
} }
}, [editorValue]); }, [editorValue]);
useEffect(() => {
if (editorValue === value) setHasUnsaved(false);
else setHasUnsaved(true);
}, [editorValue, value]);
const saveState = (value: string, transactionType?: string) => { const saveState = (value: string, transactionType?: string) => {
const tx = prepareState(value, transactionType); const tx = prepareState(value, transactionType);
if (tx) { if (tx) setState(tx);
setState(tx);
setState({
editorValue: getJsonString?.(tx),
});
}
}; };
const discardChanges = () => { const discardChanges = () => {
showAlert("Confirm", { showAlert("Confirm", {
body: "Are you sure to discard these changes?", body: "Are you sure to discard these changes?",
confirmText: "Yes", confirmText: "Yes",
onCancel: () => {}, onConfirm: () => setState({ editorValue: value }),
onConfirm: () => setState({ editorValue: getJsonString?.() }),
}); });
}; };
@@ -81,8 +79,8 @@ export const TxJson: FC<JsonProps> = ({
showAlert("Error!", { showAlert("Error!", {
body: `Malformed Transaction in ${header}, would you like to discard these changes?`, body: `Malformed Transaction in ${header}, would you like to discard these changes?`,
confirmText: "Discard", confirmText: "Discard",
onConfirm: () => setState({ editorValue: getJsonString?.() }), onConfirm: () => setState({ editorValue: value }),
onCancel: () => setState({ viewType: "json" }), onCancel: () => setState({ viewType: "json", editorSavedValue: value }),
}); });
}; };
@@ -176,11 +174,6 @@ export const TxJson: FC<JsonProps> = ({
}); });
}, [getSchemas, monacoInst]); }, [getSchemas, monacoInst]);
const hasUnsaved = useMemo(
() => editorValue !== getJsonString?.(),
[editorValue, getJsonString]
);
return ( return (
<Monaco <Monaco
rootProps={{ rootProps={{
@@ -210,14 +203,14 @@ export const TxJson: FC<JsonProps> = ({
<Flex <Flex
row row
align="center" align="center"
css={{ fontSize: "$xs", color: "$textMuted", ml: "auto" }} css={{ fontSize: "$xs", color: "$textMuted", ml: 'auto' }}
> >
<Text muted small> <Text muted small>
This file has unsaved changes. This file has unsaved changes.
</Text> </Text>
<Link <Link
css={{ ml: "$1" }} css={{ ml: "$1" }}
onClick={() => saveState(editorValue || "", currTxType)} onClick={() => saveState(editorValue, currTxType)}
> >
save save
</Link> </Link>

View File

@@ -1,4 +1,4 @@
import { FC, useCallback, useEffect, useMemo, useState } from "react"; import { FC, useCallback, useEffect, useState } from "react";
import Container from "../Container"; import Container from "../Container";
import Flex from "../Flex"; import Flex from "../Flex";
import Input from "../Input"; import Input from "../Input";
@@ -7,10 +7,9 @@ import Text from "../Text";
import { import {
SelectOption, SelectOption,
TransactionState, TransactionState,
transactionsOptions, transactionsData,
TxFields, TxFields,
getTxFields, getTxFields,
defaultTransactionType,
} from "../../state/transactions"; } from "../../state/transactions";
import { useSnapshot } from "valtio"; import { useSnapshot } from "valtio";
import state from "../../state"; import state from "../../state";
@@ -39,6 +38,12 @@ export const TxUI: FC<UIProps> = ({
txFields, txFields,
} = txState; } = txState;
const transactionsOptions = transactionsData.map(tx => ({
value: tx.TransactionType,
label: tx.TransactionType,
}));
const accountOptions: SelectOption[] = accounts.map(acc => ({ const accountOptions: SelectOption[] = accounts.map(acc => ({
label: acc.name, label: acc.name,
value: acc.address, value: acc.address,
@@ -53,16 +58,10 @@ export const TxUI: FC<UIProps> = ({
const [feeLoading, setFeeLoading] = useState(false); const [feeLoading, setFeeLoading] = useState(false);
const resetFields = useCallback( const resetOptions = useCallback(
(tt: string) => { (tt: string) => {
const fields = getTxFields(tt); const fields = getTxFields(tt);
if (!fields.Destination) setState({ selectedDestAccount: null });
if (fields.Destination !== undefined) {
setState({ selectedDestAccount: null });
fields.Destination = "";
} else {
fields.Destination = undefined;
}
return setState({ txFields: fields }); return setState({ txFields: fields });
}, },
[setState] [setState]
@@ -103,37 +102,33 @@ export const TxUI: FC<UIProps> = ({
(tt: SelectOption) => { (tt: SelectOption) => {
setState({ selectedTransaction: tt }); setState({ selectedTransaction: tt });
const newState = resetFields(tt.value); const newState = resetOptions(tt.value);
handleEstimateFee(newState, true); handleEstimateFee(newState, true);
}, },
[handleEstimateFee, resetFields, setState] [handleEstimateFee, resetOptions, setState]
); );
const switchToJson = () => setState({ viewType: "json" }); const specialFields = ["TransactionType", "Account", "Destination"];
const otherFields = Object.keys(txFields).filter(
k => !specialFields.includes(k)
) as [keyof TxFields];
const switchToJson = () =>
setState({ editorSavedValue: null, viewType: "json" });
// default tx // default tx
useEffect(() => { useEffect(() => {
if (selectedTransaction?.value) return; if (selectedTransaction?.value) return;
if (defaultTransactionType) { const defaultOption = transactionsOptions.find(
handleChangeTxType(defaultTransactionType); tt => tt.value === "Payment"
}
}, [handleChangeTxType, selectedTransaction?.value]);
const fields = useMemo(
() => getTxFields(selectedTransaction?.value),
[selectedTransaction?.value]
); );
if (defaultOption) {
const specialFields = ["TransactionType", "Account"]; handleChangeTxType(defaultOption);
if (fields.Destination !== undefined) {
specialFields.push("Destination");
} }
}, [handleChangeTxType, selectedTransaction?.value, transactionsOptions]);
const otherFields = Object.keys(txFields).filter(
k => !specialFields.includes(k)
) as [keyof TxFields];
return ( return (
<Container <Container
@@ -190,7 +185,7 @@ export const TxUI: FC<UIProps> = ({
onChange={(acc: any) => handleSetAccount(acc)} // TODO make react-select have correct types for acc onChange={(acc: any) => handleSetAccount(acc)} // TODO make react-select have correct types for acc
/> />
</Flex> </Flex>
{fields.Destination !== undefined && ( {txFields.Destination !== undefined && (
<Flex <Flex
row row
fluid fluid

View File

@@ -55,8 +55,7 @@
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"TransactionType": "EscrowCancel", "TransactionType": "EscrowCancel",
"Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"OfferSequence": 7, "OfferSequence": 7
"Fee": "10"
}, },
{ {
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
@@ -70,8 +69,7 @@
"FinishAfter": 533171558, "FinishAfter": 533171558,
"Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100", "Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
"DestinationTag": 23480, "DestinationTag": 23480,
"SourceTag": 11747, "SourceTag": 11747
"Fee": "10"
}, },
{ {
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
@@ -79,50 +77,32 @@
"Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"OfferSequence": 7, "OfferSequence": 7,
"Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100", "Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
"Fulfillment": "A0028000", "Fulfillment": "A0028000"
"Fee": "10"
},
{
"TransactionType": "NFTokenMint",
"Account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"Fee": "10",
"NFTokenTaxon": 0,
"URI": "697066733A2F2F516D614374444B5A4656767666756676626479346573745A626851483744586831364354707631686F776D424779"
}, },
{ {
"TransactionType": "NFTokenBurn", "TransactionType": "NFTokenBurn",
"Account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B", "Account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"Fee": "10", "Fee": "10",
"NFTokenID": "000B013A95F14B0044F78A264E41713C64B5F89242540EE208C3098E00000D65" "TokenID": "000B013A95F14B0044F78A264E41713C64B5F89242540EE208C3098E00000D65"
}, },
{ {
"TransactionType": "NFTokenAcceptOffer", "TransactionType": "NFTokenAcceptOffer",
"Account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B", "Fee": "10"
"Fee": "10",
"NFTokenSellOffer": "A2FA1A9911FE2AEF83DAB05F437768E26A301EF899BD31EB85E704B3D528FF18",
"NFTokenBuyOffer": "4AAAEEA76E3C8148473CB3840CE637676E561FB02BD4CA22CA59729EA815B862",
"NFTokenBrokerFee": "10"
}, },
{ {
"TransactionType": "NFTokenCancelOffer", "TransactionType": "NFTokenCancelOffer",
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX", "Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
"Fee": "10", "TokenIDs": "000100001E962F495F07A990F4ED55ACCFEEF365DBAA76B6A048C0A200000007"
"NFTokenOffers": {
"$type": "json",
"$value": ["4AAAEEA76E3C8148473CB3840CE637676E561FB02BD4CA22CA59729EA815B862"]
}
}, },
{ {
"TransactionType": "NFTokenCreateOffer", "TransactionType": "NFTokenCreateOffer",
"Account": "rs8jBmmfpwgmrSPgwMsh7CvKRmRt1JTVSX", "Account": "rs8jBmmfpwgmrSPgwMsh7CvKRmRt1JTVSX",
"NFTokenID": "000100001E962F495F07A990F4ED55ACCFEEF365DBAA76B6A048C0A200000007", "TokenID": "000100001E962F495F07A990F4ED55ACCFEEF365DBAA76B6A048C0A200000007",
"Amount": { "Amount": {
"$value": "100", "$value": "100",
"$type": "xrp" "$type": "xrp"
}, },
"Flags": 1, "Flags": 1
"Destination": "",
"Fee": "10"
}, },
{ {
"TransactionType": "OfferCancel", "TransactionType": "OfferCancel",
@@ -170,8 +150,7 @@
"PublicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A", "PublicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A",
"CancelAfter": 533171558, "CancelAfter": 533171558,
"DestinationTag": 23480, "DestinationTag": 23480,
"SourceTag": 11747, "SourceTag": 11747
"Fee": "10"
}, },
{ {
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
@@ -181,8 +160,7 @@
"$value": "200", "$value": "200",
"$type": "xrp" "$type": "xrp"
}, },
"Expiration": 543171558, "Expiration": 543171558
"Fee": "10"
}, },
{ {
"Flags": 0, "Flags": 0,

View File

@@ -16,9 +16,8 @@
"@octokit/core": "^3.5.1", "@octokit/core": "^3.5.1",
"@radix-ui/colors": "^0.1.7", "@radix-ui/colors": "^0.1.7",
"@radix-ui/react-alert-dialog": "^0.1.1", "@radix-ui/react-alert-dialog": "^0.1.1",
"@radix-ui/react-context-menu": "^0.1.6",
"@radix-ui/react-dialog": "^0.1.1", "@radix-ui/react-dialog": "^0.1.1",
"@radix-ui/react-dropdown-menu": "^0.1.6", "@radix-ui/react-dropdown-menu": "^0.1.1",
"@radix-ui/react-id": "^0.1.1", "@radix-ui/react-id": "^0.1.1",
"@radix-ui/react-label": "^0.1.5", "@radix-ui/react-label": "^0.1.5",
"@radix-ui/react-popover": "^0.1.6", "@radix-ui/react-popover": "^0.1.6",
@@ -36,7 +35,7 @@
"lodash.xor": "^4.5.0", "lodash.xor": "^4.5.0",
"monaco-editor": "^0.33.0", "monaco-editor": "^0.33.0",
"next": "^12.0.4", "next": "^12.0.4",
"next-auth": "^4.10.1", "next-auth": "^4.0.0-beta.5",
"next-plausible": "^3.2.0", "next-plausible": "^3.2.0",
"next-themes": "^0.1.1", "next-themes": "^0.1.1",
"normalize-url": "^7.0.2", "normalize-url": "^7.0.2",
@@ -62,7 +61,7 @@
"vscode-languageserver": "^7.0.0", "vscode-languageserver": "^7.0.0",
"vscode-uri": "^3.0.2", "vscode-uri": "^3.0.2",
"wabt": "1.0.16", "wabt": "1.0.16",
"xrpl-accountlib": "^1.5.2", "xrpl-accountlib": "^1.3.2",
"xrpl-client": "^1.9.4" "xrpl-client": "^1.9.4"
}, },
"devDependencies": { "devDependencies": {
@@ -76,8 +75,5 @@
"eslint-config-next": "11.1.2", "eslint-config-next": "11.1.2",
"raw-loader": "^4.0.2", "raw-loader": "^4.0.2",
"typescript": "4.4.4" "typescript": "4.4.4"
},
"resolutions": {
"ripple-binary-codec": "=1.4.2"
} }
} }

View File

@@ -3,12 +3,12 @@ import Split from "react-split";
import { useSnapshot } from "valtio"; import { useSnapshot } from "valtio";
import { Box, Container, Flex, Tab, Tabs } from "../../components"; import { Box, Container, Flex, Tab, Tabs } from "../../components";
import Transaction from "../../components/Transaction"; import Transaction from "../../components/Transaction";
import state, { renameTxState } from "../../state"; import state from "../../state";
import { getSplit, saveSplit } from "../../state/actions/persistSplits"; import { getSplit, saveSplit } from "../../state/actions/persistSplits";
import { transactionsState, modifyTxState } from "../../state"; import { transactionsState, modifyTransaction } from "../../state";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { FileJs } from "phosphor-react"; import { FileJs } from "phosphor-react";
import RunScript from "../../components/RunScript"; import RunScript from '../../components/RunScript';
const DebugStream = dynamic(() => import("../../components/DebugStream"), { const DebugStream = dynamic(() => import("../../components/DebugStream"), {
ssr: false, ssr: false,
@@ -96,12 +96,9 @@ const Test = () => {
keepAllAlive keepAllAlive
defaultExtension="json" defaultExtension="json"
allowedExtensions={["json"]} allowedExtensions={["json"]}
onCreateNewTab={header => modifyTxState(header, {})} onCreateNewTab={header => modifyTransaction(header, {})}
onRenameTab={(idx, nwName, oldName = "") =>
renameTxState(oldName, nwName)
}
onCloseTab={(idx, header) => onCloseTab={(idx, header) =>
header && modifyTxState(header, undefined) header && modifyTransaction(header, undefined)
} }
> >
{transactions.map(({ header, state }) => ( {transactions.map(({ header, state }) => (

View File

@@ -28,7 +28,12 @@ export const names = [
* is protected with CORS so that's why we did our own endpoint * is protected with CORS so that's why we did our own endpoint
*/ */
export const addFaucetAccount = async (name?: string, showToast: boolean = false) => { export const addFaucetAccount = async (name?: string, showToast: boolean = false) => {
if (typeof window === undefined) return // Lets limit the number of faucet accounts to 5 for now
if (state.accounts.length > 5) {
return toast.error("You can only have maximum 6 accounts");
}
if (typeof window !== 'undefined') {
const toastId = showToast ? toast.loading("Creating account") : ""; const toastId = showToast ? toast.loading("Creating account") : "";
const res = await fetch(`${window.location.origin}/api/faucet`, { const res = await fetch(`${window.location.origin}/api/faucet`, {
@@ -57,6 +62,7 @@ export const addFaucetAccount = async (name?: string, showToast: boolean = false
version: '2' version: '2'
}); });
} }
}
}; };
// fetch initial faucets // fetch initial faucets

View File

@@ -29,9 +29,7 @@ export const compileCode = async (activeId: number) => {
const file = state.files[activeId] const file = state.files[activeId]
try { try {
file.containsErrors = false file.containsErrors = false
let res: Response const res = await fetch(process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT, {
try {
res = await fetch(process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT, {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -50,9 +48,6 @@ export const compileCode = async (activeId: number) => {
], ],
}), }),
}); });
} catch (error) {
throw Error("Something went wrong, check your network connection and try again!")
}
const json = await res.json(); const json = await res.json();
state.compiling = false; state.compiling = false;
if (!json.success) { if (!json.success) {
@@ -66,34 +61,29 @@ export const compileCode = async (activeId: number) => {
} }
throw errors throw errors
} }
try { 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",
});
// Decode base64 encoded wasm that is coming back from the endpoint // Decode base64 encoded wasm that is coming back from the endpoint
const bufferData = await decodeBinary(json.output); const bufferData = await decodeBinary(json.output);
file.compiledContent = ref(bufferData);
file.lastCompiled = new Date();
file.compiledValueSnapshot = file.content
// Import wabt from and create human readable version of wasm file and // Import wabt from and create human readable version of wasm file and
// put it into state // put it into state
const ww = (await import('wabt')).default() import("wabt").then((wabt) => {
const ww = wabt.default();
const myModule = ww.readWasm(new Uint8Array(bufferData), { const myModule = ww.readWasm(new Uint8Array(bufferData), {
readDebugNames: true, readDebugNames: true,
}); });
myModule.applyNames(); myModule.applyNames();
const wast = myModule.toText({ foldExprs: false, inlineExport: false }); const wast = myModule.toText({ foldExprs: false, inlineExport: false });
state.files[state.active].compiledWatContent = wast;
file.compiledContent = ref(bufferData);
file.lastCompiled = new Date();
file.compiledValueSnapshot = file.content
file.compiledWatContent = wast;
} catch (error) {
throw Error("Invalid compilation result produced, check your code for errors and try again!")
}
toast.success("Compiled successfully!", { position: "bottom-center" }); 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) { } catch (err) {
console.log(err); console.log(err);
@@ -106,19 +96,12 @@ export const compileCode = async (activeId: number) => {
}); });
}) })
} }
else if (err instanceof Error) {
state.logs.push({
type: "error",
message: err.message,
});
}
else { else {
state.logs.push({ state.logs.push({
type: "error", type: "error",
message: "Something went wrong, come back later!", message: "Something went wrong, check your connection try again later!",
}); });
} }
state.compiling = false; state.compiling = false;
toast.error(`Error occurred while compiling!`, { position: "bottom-center" }); toast.error(`Error occurred while compiling!`, { position: "bottom-center" });
file.containsErrors = true file.containsErrors = true

View File

@@ -15,10 +15,3 @@ export const createNewFile = (name: string) => {
state.files.push(emptyFile); state.files.push(emptyFile);
state.active = state.files.length - 1; state.active = state.files.length - 1;
}; };
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}`)
file.name = nwName
};

View File

@@ -1,24 +0,0 @@
import state, { transactionsState } from '..';
export const deleteAccount = (addr?: string) => {
if (!addr) return;
const index = state.accounts.findIndex(acc => acc.address === addr);
if (index === -1) return;
state.accounts.splice(index, 1);
// update selected accounts
transactionsState.transactions
.filter(t => t.state.selectedAccount?.value === addr)
.forEach(t => {
const acc = t.state.selectedAccount;
if (!acc) return;
acc.label = acc.value;
});
transactionsState.transactions
.filter(t => t.state.selectedDestAccount?.value === addr)
.forEach(t => {
const acc = t.state.selectedDestAccount;
if (!acc) return;
acc.label = acc.value;
});
};

View File

@@ -3,10 +3,10 @@ import toast from "react-hot-toast";
import state, { IAccount } from "../index"; import state, { IAccount } from "../index";
import calculateHookOn, { TTS } from "../../utils/hookOnCalculator"; import calculateHookOn, { TTS } from "../../utils/hookOnCalculator";
import { SetHookData } from "../../components/SetHookDialog";
import { Link } from "../../components"; import { Link } from "../../components";
import { ref } from "valtio"; import { ref } from "valtio";
import estimateFee from "../../utils/estimateFee"; import estimateFee from "../../utils/estimateFee";
import { SetHookData } from '../../utils/setHook';
export const sha256 = async (string: string) => { export const sha256 = async (string: string) => {
const utf8 = new TextEncoder().encode(string); const utf8 = new TextEncoder().encode(string);

View File

@@ -24,6 +24,7 @@ export const sendTransaction = async (account: IAccount, txOptions: TransactionO
Fee, // TODO auto-fillable default Fee, // TODO auto-fillable default
...opts ...opts
}; };
const { logPrefix = '' } = options || {} const { logPrefix = '' } = options || {}
try { try {
const signedAccount = derive.familySeed(account.secret); const signedAccount = derive.familySeed(account.secret);

View File

@@ -1,6 +1,6 @@
import type monaco from "monaco-editor"; import type monaco from "monaco-editor";
import { proxy, ref, subscribe } from "valtio"; import { proxy, ref, subscribe } from "valtio";
import { devtools, subscribeKey } from 'valtio/utils'; import { devtools } from 'valtio/utils';
import { XrplClient } from "xrpl-client"; import { XrplClient } from "xrpl-client";
import { SplitSize } from "./actions/persistSplits"; import { SplitSize } from "./actions/persistSplits";
@@ -168,23 +168,16 @@ if (process.env.NODE_ENV !== "production") {
} }
if (typeof window !== "undefined") { if (typeof window !== "undefined") {
subscribe(state.accounts, () => { subscribe(state, () => {
const { accounts } = state; const { accounts, active } = state;
const accountsNoLoading = accounts.map(acc => ({ ...acc, isLoading: false })) const accountsNoLoading = accounts.map(acc => ({ ...acc, isLoading: false }))
localStorage.setItem("hooksIdeAccounts", JSON.stringify(accountsNoLoading)); localStorage.setItem("hooksIdeAccounts", JSON.stringify(accountsNoLoading));
}); if (!state.files[active]?.compiledWatContent) {
state.activeWat = 0;
const updateActiveWat = () => { } else {
const filename = state.files[state.active]?.name state.activeWat = active;
const compiledFiles = state.files.filter(
file => file.compiledContent)
const idx = compiledFiles.findIndex(file => file.name === filename)
if (idx !== -1) state.activeWat = idx
} }
subscribeKey(state, 'active', updateActiveWat) });
subscribeKey(state, 'files', updateActiveWat)
} }
export default state export default state

View File

@@ -18,13 +18,14 @@ export interface TransactionState {
txIsDisabled: boolean; txIsDisabled: boolean;
txFields: TxFields; txFields: TxFields;
viewType: 'json' | 'ui', viewType: 'json' | 'ui',
editorSavedValue: null | string,
editorValue?: string, editorValue?: string,
estimatedFee?: string estimatedFee?: string
} }
export type TxFields = Omit< export type TxFields = Omit<
Partial<typeof transactionsData[0]>, typeof transactionsData[0],
"Account" | "Sequence" | "TransactionType" "Account" | "Sequence" | "TransactionType"
>; >;
@@ -35,34 +36,27 @@ export const defaultTransaction: TransactionState = {
txIsLoading: false, txIsLoading: false,
txIsDisabled: false, txIsDisabled: false,
txFields: {}, txFields: {},
viewType: 'ui' viewType: 'ui',
editorSavedValue: null
}; };
export const transactionsState = proxy({ export const transactionsState = proxy({
transactions: [ transactions: [
{ {
header: "test1.json", header: "test1.json",
state: { ...defaultTransaction }, state: defaultTransaction,
}, },
], ],
activeHeader: "test1.json" activeHeader: "test1.json"
}); });
export const renameTxState = (oldName: string, nwName: string) => {
const tx = transactionsState.transactions.find(tx => tx.header === oldName);
if (!tx) throw Error(`No transaction state exists with given header name ${oldName}`);
tx.header = nwName
}
/** /**
* Simple transaction state changer * Simple transaction state changer
* @param header Unique key and tab name for the transaction tab * @param header Unique key and tab name for the transaction tab
* @param partialTx partial transaction state, `undefined` deletes the transaction * @param partialTx partial transaction state, `undefined` deletes the transaction
* *
*/ */
export const modifyTxState = ( export const modifyTransaction = (
header: string, header: string,
partialTx?: Partial<TransactionState>, partialTx?: Partial<TransactionState>,
opts: { replaceState?: boolean } = {} opts: { replaceState?: boolean } = {}
@@ -98,7 +92,7 @@ export const modifyTxState = (
} }
Object.keys(partialTx).forEach(k => { Object.keys(partialTx).forEach(k => {
// Typescript mess here, but is definitely safe! // Typescript mess here, but is definetly safe!
const s = tx.state as any; const s = tx.state as any;
const p = partialTx as any; // ? Make copy const p = partialTx as any; // ? Make copy
if (!deepEqual(s[k], p[k])) s[k] = p[k]; if (!deepEqual(s[k], p[k])) s[k] = p[k];
@@ -124,7 +118,7 @@ export const prepareTransaction = (data: any) => {
// handle type: `json` // handle type: `json`
if (_value && typeof _value === "object" && _value.$type === "json") { if (_value && typeof _value === "object" && _value.$type === "json") {
if (typeof _value.$value === "object") { if (typeof _value.$value === "object") {
options[field] = _value.$value; options[field] = { ..._value.$value } as any;
} else { } else {
try { try {
options[field] = JSON.parse(_value.$value); options[field] = JSON.parse(_value.$value);
@@ -138,7 +132,7 @@ export const prepareTransaction = (data: any) => {
} }
// delete unnecessary fields // delete unnecessary fields
if (!options[field]) { if (options[field] === undefined) {
delete options[field]; delete options[field];
} }
}); });
@@ -158,7 +152,7 @@ export const prepareState = (value: string, transactionType?: string) => {
const { Account, TransactionType, Destination, ...rest } = options; const { Account, TransactionType, Destination, ...rest } = options;
let tx: Partial<TransactionState> = {}; let tx: Partial<TransactionState> = {};
const schema = getTxFields(transactionType) const txFields = getTxFields(transactionType)
if (Account) { if (Account) {
const acc = state.accounts.find(acc => acc.address === Account); const acc = state.accounts.find(acc => acc.address === Account);
@@ -186,8 +180,9 @@ export const prepareState = (value: string, transactionType?: string) => {
tx.selectedTransaction = null; tx.selectedTransaction = null;
} }
if (schema.Destination !== undefined) { if (txFields.Destination !== undefined) {
const dest = state.accounts.find(acc => acc.address === Destination); const dest = state.accounts.find(acc => acc.address === Destination);
rest.Destination = null
if (dest) { if (dest) {
tx.selectedDestAccount = { tx.selectedDestAccount = {
label: dest.name, label: dest.name,
@@ -204,14 +199,11 @@ export const prepareState = (value: string, transactionType?: string) => {
tx.selectedDestAccount = null tx.selectedDestAccount = null
} }
} }
else if (Destination) {
rest.Destination = Destination
}
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 origValue = txFields[field as keyof TxFields]
const isXrp = typeof value !== 'object' && schemaVal && typeof schemaVal === 'object' && schemaVal.$type === 'xrp' const isXrp = typeof value !== 'object' && origValue && typeof origValue === 'object' && origValue.$type === 'xrp'
if (isXrp) { if (isXrp) {
rest[field] = { rest[field] = {
$type: "xrp", $type: "xrp",
@@ -226,6 +218,7 @@ export const prepareState = (value: string, transactionType?: string) => {
}); });
tx.txFields = rest; tx.txFields = rest;
tx.editorSavedValue = null;
return tx return tx
} }
@@ -251,10 +244,3 @@ export const getTxFields = (tt?: string) => {
} }
export { transactionsData } export { transactionsData }
export const transactionsOptions = transactionsData.map(tx => ({
value: tx.TransactionType,
label: tx.TransactionType,
}));
export const defaultTransactionType = transactionsOptions.find(tt => tt.value === 'Payment')

View File

@@ -53,7 +53,6 @@ export const {
accent: "#9D2DFF", accent: "#9D2DFF",
background: "$gray1", background: "$gray1",
backgroundAlt: "$gray4", backgroundAlt: "$gray4",
backgroundOverlay: "$mauve2",
text: "$gray12", text: "$gray12",
textMuted: "$gray10", textMuted: "$gray10",
primary: "$plum", primary: "$plum",
@@ -366,7 +365,6 @@ export const darkTheme = createTheme("dark", {
...greenDark, ...greenDark,
...redDark, ...redDark,
deep: "rgb(10, 10, 10)", deep: "rgb(10, 10, 10)",
backgroundOverlay: "$mauve5"
// backgroundA: transparentize(0.1, grayDark.gray1), // backgroundA: transparentize(0.1, grayDark.gray1),
}, },
}); });

View File

@@ -1,21 +0,0 @@
import { keyframes } from '../stitches.config';
export const slideUpAndFade = keyframes({
"0%": { opacity: 0, transform: "translateY(2px)" },
"100%": { opacity: 1, transform: "translateY(0)" },
});
export const slideRightAndFade = keyframes({
"0%": { opacity: 0, transform: "translateX(-2px)" },
"100%": { opacity: 1, transform: "translateX(0)" },
});
export const slideDownAndFade = keyframes({
"0%": { opacity: 0, transform: "translateY(-2px)" },
"100%": { opacity: 1, transform: "translateY(0)" },
});
export const slideLeftAndFade = keyframes({
"0%": { opacity: 0, transform: "translateX(2px)" },
"100%": { opacity: 1, transform: "translateX(0)" },
});

View File

@@ -18,18 +18,13 @@ export const tts = {
ttDEPOSIT_PREAUTH: 19, ttDEPOSIT_PREAUTH: 19,
ttTRUST_SET: 20, ttTRUST_SET: 20,
ttACCOUNT_DELETE: 21, ttACCOUNT_DELETE: 21,
ttHOOK_SET: 22, ttHOOK_SET: 22
ttNFTOKEN_MINT: 25,
ttNFTOKEN_BURN: 26,
ttNFTOKEN_CREATE_OFFER: 27,
ttNFTOKEN_CANCEL_OFFER: 28,
ttNFTOKEN_ACCEPT_OFFER: 29
}; };
export type TTS = typeof tts; export type TTS = typeof tts;
const calculateHookOn = (arr: (keyof TTS)[]) => { const calculateHookOn = (arr: (keyof TTS)[]) => {
let start = '0x000000003e3ff5bf'; let start = '0x00000000003ff5bf';
arr.forEach(n => { arr.forEach(n => {
let v = BigInt(start); let v = BigInt(start);
v ^= (BigInt(1) << BigInt(tts[n as keyof TTS])); v ^= (BigInt(1) << BigInt(tts[n as keyof TTS]));

View File

@@ -1,78 +0,0 @@
import { getTags } from './comment-parser';
import { tts, TTS } from './hookOnCalculator';
export const transactionOptions = Object.keys(tts).map(key => ({
label: key,
value: key as keyof TTS,
}));
export type SetHookData = {
Invoke: {
value: keyof TTS;
label: string;
}[];
Fee: string;
HookNamespace: string;
HookParameters: {
HookParameter: {
HookParameterName: string;
HookParameterValue: string;
};
$metaData?: any;
}[];
// HookGrants: {
// HookGrant: {
// Authorize: string;
// HookHash: string;
// };
// }[];
};
export const getParameters = (content?: string) => {
const fieldTags = ["field", "param", "arg", "argument"];
const tags = getTags(content)
.filter(tag => fieldTags.includes(tag.tag))
.filter(tag => !!tag.name);
const paramters: SetHookData["HookParameters"] = tags.map(tag => ({
HookParameter: {
HookParameterName: tag.name,
HookParameterValue: tag.default || "",
},
$metaData: {
description: tag.description,
required: !tag.optional
},
}));
return paramters;
};
export const getInvokeOptions = (content?: string) => {
const invokeTags = ["invoke", "invoke-on"];
const options = getTags(content)
.filter(tag => invokeTags.includes(tag.tag))
.reduce((cumm, curr) => {
const combined = curr.type || `${curr.name} ${curr.description}`
const opts = combined.split(' ')
return cumm.concat(opts as any)
}, [] as (keyof TTS)[])
.filter(opt => Object.keys(tts).includes(opt))
const invokeOptions: SetHookData['Invoke'] = options.map(opt => ({
label: opt,
value: opt
}))
// default
if (!invokeOptions.length) {
const payment = transactionOptions.find(tx => tx.value === "ttPAYMENT")
if (payment) return [payment]
}
return invokeOptions;
};

View File

@@ -594,18 +594,6 @@
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
"@radix-ui/react-context-menu@^0.1.6":
version "0.1.6"
resolved "https://registry.yarnpkg.com/@radix-ui/react-context-menu/-/react-context-menu-0.1.6.tgz#0c75f2faffec6c8697247a4b685a432b3c4d07f0"
integrity sha512-0qa6ABaeqD+WYI+8iT0jH0QLLcV8Kv0xI+mZL4FFnG4ec9H0v+yngb5cfBBfs9e/KM8mDzFFpaeegqsQlLNqyQ==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "0.1.0"
"@radix-ui/react-context" "0.1.1"
"@radix-ui/react-menu" "0.1.6"
"@radix-ui/react-primitive" "0.1.4"
"@radix-ui/react-use-callback-ref" "0.1.0"
"@radix-ui/react-context@0.1.1": "@radix-ui/react-context@0.1.1":
version "0.1.1" version "0.1.1"
resolved "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-0.1.1.tgz" resolved "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-0.1.1.tgz"
@@ -647,9 +635,9 @@
"@radix-ui/react-use-callback-ref" "0.1.0" "@radix-ui/react-use-callback-ref" "0.1.0"
"@radix-ui/react-use-escape-keydown" "0.1.0" "@radix-ui/react-use-escape-keydown" "0.1.0"
"@radix-ui/react-dropdown-menu@^0.1.6": "@radix-ui/react-dropdown-menu@^0.1.1":
version "0.1.6" version "0.1.6"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-0.1.6.tgz#3203229788cd57e552c9f19dcc7008e2b545919c" resolved "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-0.1.6.tgz"
integrity sha512-RZhtzjWwJ4ZBN7D8ek4Zn+ilHzYuYta9yIxFnbC0pfqMnSi67IQNONo1tuuNqtFh9SRHacPKc65zo+kBBlxtdg== integrity sha512-RZhtzjWwJ4ZBN7D8ek4Zn+ilHzYuYta9yIxFnbC0pfqMnSi67IQNONo1tuuNqtFh9SRHacPKc65zo+kBBlxtdg==
dependencies: dependencies:
"@babel/runtime" "^7.13.10" "@babel/runtime" "^7.13.10"
@@ -1292,6 +1280,14 @@ babel-plugin-macros@^2.6.1:
cosmiconfig "^6.0.0" cosmiconfig "^6.0.0"
resolve "^1.12.0" resolve "^1.12.0"
babel-runtime@^6.26.0:
version "6.26.0"
resolved "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz"
integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
dependencies:
core-js "^2.4.0"
regenerator-runtime "^0.11.0"
balanced-match@^1.0.0: balanced-match@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
@@ -1556,6 +1552,11 @@ core-js-pure@^3.20.2:
resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz" resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz"
integrity sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ== integrity sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==
core-js@^2.4.0:
version "2.6.12"
resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz"
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
core-util-is@~1.0.0: core-util-is@~1.0.0:
version "1.0.3" version "1.0.3"
resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz"
@@ -2953,10 +2954,10 @@ natural-compare@^1.4.0:
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
next-auth@^4.10.1: next-auth@^4.0.0-beta.5:
version "4.10.1" version "4.2.1"
resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-4.10.1.tgz#33b29265d12287bb2f6d267c8d415a407c27f0e9" resolved "https://registry.npmjs.org/next-auth/-/next-auth-4.2.1.tgz"
integrity sha512-F00vtwBdyMIIJ8IORHOAOHjVGTDEhhm9+HpB2BQ8r40WtGxqToWWLN7Z+2ZW/z2RFlo3zhcuAtUCPUzVJxtZwQ== integrity sha512-XDtt7nqevkNf4EJ2zKAKkI+MFsURf11kx11vPwxrBYA1MHeqWwaWbGOUOI2ekNTvfAg4nTEJJUH3LV2cLrH3Tg==
dependencies: dependencies:
"@babel/runtime" "^7.16.3" "@babel/runtime" "^7.16.3"
"@panva/hkdf" "^1.0.1" "@panva/hkdf" "^1.0.1"
@@ -3544,6 +3545,11 @@ reconnecting-websocket@^4.4.0:
resolved "https://registry.npmjs.org/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz" resolved "https://registry.npmjs.org/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz"
integrity sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng== integrity sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==
regenerator-runtime@^0.11.0:
version "0.11.1"
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz"
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
regenerator-runtime@^0.13.4: regenerator-runtime@^0.13.4:
version "0.13.9" version "0.13.9"
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz" resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz"
@@ -3650,7 +3656,32 @@ ripple-address-codec@^4.2.4:
base-x "3.0.9" base-x "3.0.9"
create-hash "^1.1.2" create-hash "^1.1.2"
ripple-binary-codec@=1.4.2, ripple-binary-codec@^0.2.4, ripple-binary-codec@^1.1.3, ripple-binary-codec@^1.4.2: ripple-binary-codec@^0.2.4:
version "0.2.7"
resolved "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-0.2.7.tgz"
integrity sha512-VD+sHgZK76q3kmO765klFHPDCEveS5SUeg/bUNVpNrj7w2alyDNkbF17XNbAjFv+kSYhfsUudQanoaSs2Y6uzw==
dependencies:
babel-runtime "^6.26.0"
bn.js "^5.1.1"
create-hash "^1.2.0"
decimal.js "^10.2.0"
inherits "^2.0.4"
lodash "^4.17.15"
ripple-address-codec "^4.1.0"
ripple-binary-codec@^1.1.3:
version "1.3.2"
resolved "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-1.3.2.tgz"
integrity sha512-8VG1vfb3EM1J7ZdPXo9E57Zv2hF4cxT64gP6rGSQzODVgMjiBCWozhN3729qNTGtHItz0e82Oix8v95vWYBQ3A==
dependencies:
assert "^2.0.0"
big-integer "^1.6.48"
buffer "5.6.0"
create-hash "^1.2.0"
decimal.js "^10.2.0"
ripple-address-codec "^4.2.3"
ripple-binary-codec@^1.4.2:
version "1.4.2" version "1.4.2"
resolved "https://registry.yarnpkg.com/ripple-binary-codec/-/ripple-binary-codec-1.4.2.tgz#cdc35353e4bc7c3a704719247c82b4c4d0b57dd3" resolved "https://registry.yarnpkg.com/ripple-binary-codec/-/ripple-binary-codec-1.4.2.tgz#cdc35353e4bc7c3a704719247c82b4c4d0b57dd3"
integrity sha512-EDKIyZMa/6Ay/oNgCwjD9b9CJv0zmBreeHVQeG4BYwy+9GPnIQjNeT5e/aB6OjAnhcmpgbPeBmzwmNVwzxlt0w== integrity sha512-EDKIyZMa/6Ay/oNgCwjD9b9CJv0zmBreeHVQeG4BYwy+9GPnIQjNeT5e/aB6OjAnhcmpgbPeBmzwmNVwzxlt0w==
@@ -4337,10 +4368,10 @@ xrpl-accountlib@^1.5.2:
xrpl-secret-numbers "^0.3.3" xrpl-secret-numbers "^0.3.3"
xrpl-sign-keypairs "^2.1.1" xrpl-sign-keypairs "^2.1.1"
xrpl-client@^1.9.4: xrpl-client@^1.9.5:
version "1.9.4" version "1.9.5"
resolved "https://registry.npmjs.org/xrpl-client/-/xrpl-client-1.9.4.tgz" resolved "https://registry.yarnpkg.com/xrpl-client/-/xrpl-client-1.9.5.tgz#adab5ec233a8988178ddb77b764734f5986409f6"
integrity sha512-0+O5TbJB4GBAuZVvIrZje8VMSTTQKU8pyvuOggSmX9fhqed5c7+GGOSmKD7RWNmyQ1dZT2I70tDpzocZybtYyg== integrity sha512-B8gt/NdYbBsZ1a6iiZcA4WyFoUvqDaESekyyzo3Q2zbesN65TbA6oRU8g86HK/ll/9qA9U4Aguh/R2OoEdRe2g==
dependencies: dependencies:
debug "^4.1.1" debug "^4.1.1"
websocket "^1.0.34" websocket "^1.0.34"