Compare commits
	
		
			22 Commits
		
	
	
		
			fix/tx-amo
			...
			fix/compil
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					8b72086c04 | ||
| 
						 | 
					895b34cc68 | ||
| 
						 | 
					3897f2d823 | ||
| 
						 | 
					bf792f1495 | ||
| 
						 | 
					df3210a663 | ||
| 
						 | 
					bad7730c32 | ||
| 
						 | 
					adb6a78549 | ||
| 
						 | 
					8cc27f20c3 | ||
| 
						 | 
					8e49a3f5f1 | ||
| 
						 | 
					3179757469 | ||
| 
						 | 
					554cfb3db9 | ||
| 
						 | 
					637a066f69 | ||
| 
						 | 
					c9a852e9be | ||
| 
						 | 
					307a5407eb | ||
| 
						 | 
					faa28845c8 | ||
| 
						 | 
					168d11d48e | ||
| 
						 | 
					60f2bb558c | ||
| 
						 | 
					fdf33b9f45 | ||
| 
						 | 
					d05180d148 | ||
| 
						 | 
					bfaa6be17d | ||
| 
						 | 
					9e368dec84 | ||
| 
						 | 
					25eec6980f | 
							
								
								
									
										152
									
								
								components/ContextMenu/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								components/ContextMenu/index.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,152 @@
 | 
			
		||||
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";
 | 
			
		||||
 | 
			
		||||
[
 | 
			
		||||
  {
 | 
			
		||||
    label: "Show bookmarks",
 | 
			
		||||
    type: "checkbox",
 | 
			
		||||
    checked: true,
 | 
			
		||||
    indicator: "*",
 | 
			
		||||
    onCheckedChange: () => {},
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    type: "radio",
 | 
			
		||||
    label: "People",
 | 
			
		||||
    value: "pedro",
 | 
			
		||||
    onValueChange: () => {},
 | 
			
		||||
    options: [
 | 
			
		||||
      {
 | 
			
		||||
        value: "pedro",
 | 
			
		||||
        label: "Pedro Duarte",
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        value: "colm",
 | 
			
		||||
        label: "Colm Tuite",
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export type TextOption = {
 | 
			
		||||
  type: "text";
 | 
			
		||||
  label: ReactNode;
 | 
			
		||||
  onClick?: () => any;
 | 
			
		||||
  children?: Option[];
 | 
			
		||||
};
 | 
			
		||||
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 };
 | 
			
		||||
 | 
			
		||||
type Option = (TextOption | SeparatorOption | CheckboxOption | RadioOption) &
 | 
			
		||||
  WithCommons;
 | 
			
		||||
 | 
			
		||||
interface IContextMenu {
 | 
			
		||||
  options?: Option[];
 | 
			
		||||
  isNested?: boolean;
 | 
			
		||||
}
 | 
			
		||||
export const ContextMenu: FC<IContextMenu> = ({
 | 
			
		||||
  children,
 | 
			
		||||
  options,
 | 
			
		||||
  isNested,
 | 
			
		||||
}) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <ContextMenuRoot>
 | 
			
		||||
      {isNested ? (
 | 
			
		||||
        <ContextMenuTriggerItem>{children}</ContextMenuTriggerItem>
 | 
			
		||||
      ) : (
 | 
			
		||||
        <ContextMenuTrigger>{children}</ContextMenuTrigger>
 | 
			
		||||
      )}
 | 
			
		||||
      {options && (
 | 
			
		||||
        <ContextMenuContent sideOffset={isNested ? 2 : 5}>
 | 
			
		||||
          {options.map(({ key, ...option }) => {
 | 
			
		||||
            if (option.type === "text") {
 | 
			
		||||
              const { children, label } = 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}>{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;
 | 
			
		||||
							
								
								
									
										107
									
								
								components/ContextMenu/primitive.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								components/ContextMenu/primitive.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
			
		||||
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;
 | 
			
		||||
@@ -13,7 +13,7 @@ import state from "../state";
 | 
			
		||||
import wat from "../utils/wat-highlight";
 | 
			
		||||
 | 
			
		||||
import EditorNavigation from "./EditorNavigation";
 | 
			
		||||
import { Button, Text, Link, Flex } from ".";
 | 
			
		||||
import { Button, Text, Link, Flex, Tabs, Tab } from ".";
 | 
			
		||||
import Monaco from "./Monaco";
 | 
			
		||||
 | 
			
		||||
const FILESIZE_BREAKPOINTS: [number, number] = [2 * 1024, 5 * 1024];
 | 
			
		||||
@@ -25,9 +25,20 @@ const DeployEditor = () => {
 | 
			
		||||
 | 
			
		||||
  const [showContent, setShowContent] = useState(false);
 | 
			
		||||
 | 
			
		||||
  const activeFile = snap.files[snap.active]?.compiledContent
 | 
			
		||||
    ? snap.files[snap.active]
 | 
			
		||||
    : snap.files.filter(file => file.compiledContent)[0];
 | 
			
		||||
  const compiledFiles = snap.files.filter(file => file.compiledContent);
 | 
			
		||||
  const activeFile = compiledFiles[snap.activeWat];
 | 
			
		||||
 | 
			
		||||
  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 color =
 | 
			
		||||
    compiledSize > FILESIZE_BREAKPOINTS[1]
 | 
			
		||||
@@ -38,7 +49,7 @@ const DeployEditor = () => {
 | 
			
		||||
 | 
			
		||||
  const isContentChanged =
 | 
			
		||||
    activeFile && activeFile.compiledValueSnapshot !== activeFile.content;
 | 
			
		||||
  // const hasDeployErros = activeFile && activeFile.containsErrors;
 | 
			
		||||
  // const hasDeployErrors = activeFile && activeFile.containsErrors;
 | 
			
		||||
 | 
			
		||||
  const CompiledStatView = activeFile && (
 | 
			
		||||
    <Flex
 | 
			
		||||
@@ -99,6 +110,7 @@ const DeployEditor = () => {
 | 
			
		||||
      </NextLink>
 | 
			
		||||
    </Text>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const isContent =
 | 
			
		||||
    snap.files?.filter(file => file.compiledWatContent).length > 0 &&
 | 
			
		||||
    router.isReady;
 | 
			
		||||
@@ -113,7 +125,7 @@ const DeployEditor = () => {
 | 
			
		||||
        width: "100%",
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      <EditorNavigation showWat />
 | 
			
		||||
      <EditorNavigation renderNav={renderNav} />
 | 
			
		||||
      <Container
 | 
			
		||||
        css={{
 | 
			
		||||
          display: "flex",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,27 +1,7 @@
 | 
			
		||||
import { keyframes } from "@stitches/react";
 | 
			
		||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
 | 
			
		||||
 | 
			
		||||
import { styled } from "../stitches.config";
 | 
			
		||||
 | 
			
		||||
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)" },
 | 
			
		||||
});
 | 
			
		||||
import { slideDownAndFade, slideLeftAndFade, slideRightAndFade, slideUpAndFade } from '../styles/keyframes';
 | 
			
		||||
 | 
			
		||||
const StyledContent = styled(DropdownMenuPrimitive.Content, {
 | 
			
		||||
  minWidth: 220,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,10 @@
 | 
			
		||||
import React, { useState, useEffect, useCallback, useRef } from "react";
 | 
			
		||||
import React, {
 | 
			
		||||
  useState,
 | 
			
		||||
  useEffect,
 | 
			
		||||
  useRef,
 | 
			
		||||
  ReactNode,
 | 
			
		||||
} from "react";
 | 
			
		||||
import {
 | 
			
		||||
  Plus,
 | 
			
		||||
  Share,
 | 
			
		||||
  DownloadSimple,
 | 
			
		||||
  Gear,
 | 
			
		||||
@@ -28,7 +32,6 @@ import { useSnapshot } from "valtio";
 | 
			
		||||
import toast from "react-hot-toast";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  createNewFile,
 | 
			
		||||
  syncToGist,
 | 
			
		||||
  updateEditorSettings,
 | 
			
		||||
  downloadAsZip,
 | 
			
		||||
@@ -48,36 +51,23 @@ import {
 | 
			
		||||
import Flex from "./Flex";
 | 
			
		||||
import Stack from "./Stack";
 | 
			
		||||
import { Input, Label } from "./Input";
 | 
			
		||||
import Text from "./Text";
 | 
			
		||||
import Tooltip from "./Tooltip";
 | 
			
		||||
import { styled } from "../stitches.config";
 | 
			
		||||
import { showAlert } from "../state/actions/showAlert";
 | 
			
		||||
 | 
			
		||||
const ErrorText = styled(Text, {
 | 
			
		||||
  color: "$error",
 | 
			
		||||
  mt: "$1",
 | 
			
		||||
  display: "block",
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
const EditorNavigation = ({ renderNav }: { renderNav?: () => ReactNode }) => {
 | 
			
		||||
  const snap = useSnapshot(state);
 | 
			
		||||
  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 [popup, setPopUp] = useState(false);
 | 
			
		||||
  const [editorSettings, setEditorSettings] = useState(snap.editorSettings);
 | 
			
		||||
  
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (session && session.user && popup) {
 | 
			
		||||
      setPopUp(false);
 | 
			
		||||
    }
 | 
			
		||||
  }, [session, popup]);
 | 
			
		||||
 | 
			
		||||
  // when filename changes, reset error
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setNewfileError(null);
 | 
			
		||||
  }, [filename, setNewfileError]);
 | 
			
		||||
 | 
			
		||||
  const showNewGistAlert = () => {
 | 
			
		||||
    showAlert("Are you sure?", {
 | 
			
		||||
@@ -95,46 +85,8 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  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 containerRef = useRef<HTMLDivElement>(null);
 | 
			
		||||
  const files = snap.files;
 | 
			
		||||
  return (
 | 
			
		||||
    <Flex css={{ flexShrink: 0, gap: "$0" }}>
 | 
			
		||||
      <Flex
 | 
			
		||||
@@ -174,131 +126,14 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
            scrollbarWidth: "thin",
 | 
			
		||||
          },
 | 
			
		||||
        }}
 | 
			
		||||
        onWheelCapture={(e) => {
 | 
			
		||||
        onWheelCapture={e => {
 | 
			
		||||
          if (scrollRef.current) {
 | 
			
		||||
            scrollRef.current.scrollLeft += e.deltaY;
 | 
			
		||||
          }
 | 
			
		||||
        }}
 | 
			
		||||
      >
 | 
			
		||||
        <Container css={{ flex: 1 }} ref={containerRef}>
 | 
			
		||||
          <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>
 | 
			
		||||
          {renderNav?.()}
 | 
			
		||||
        </Container>
 | 
			
		||||
      </Flex>
 | 
			
		||||
      <Flex
 | 
			
		||||
@@ -542,8 +377,8 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
 | 
			
		||||
              type="number"
 | 
			
		||||
              min="1"
 | 
			
		||||
              value={editorSettings.tabSize}
 | 
			
		||||
              onChange={(e) =>
 | 
			
		||||
                setEditorSettings((curr) => ({
 | 
			
		||||
              onChange={e =>
 | 
			
		||||
                setEditorSettings(curr => ({
 | 
			
		||||
                  ...curr,
 | 
			
		||||
                  tabSize: Number(e.target.value),
 | 
			
		||||
                }))
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ import { useRouter } from "next/router";
 | 
			
		||||
 | 
			
		||||
import Box from "./Box";
 | 
			
		||||
import Container from "./Container";
 | 
			
		||||
import { saveFile } from "../state/actions";
 | 
			
		||||
import { createNewFile, saveFile } from "../state/actions";
 | 
			
		||||
import { apiHeaderFiles } from "../state/constants";
 | 
			
		||||
import state from "../state";
 | 
			
		||||
 | 
			
		||||
@@ -20,7 +20,8 @@ import ReconnectingWebSocket from "reconnecting-websocket";
 | 
			
		||||
 | 
			
		||||
import docs from "../xrpl-hooks-docs/docs";
 | 
			
		||||
import Monaco from "./Monaco";
 | 
			
		||||
import { saveAllFiles } from '../state/actions/saveFile';
 | 
			
		||||
import { saveAllFiles } from "../state/actions/saveFile";
 | 
			
		||||
import { Tab, Tabs } from "./Tabs";
 | 
			
		||||
 | 
			
		||||
const validateWritability = (editor: monaco.editor.IStandaloneCodeEditor) => {
 | 
			
		||||
  const currPath = editor.getModel()?.uri.path;
 | 
			
		||||
@@ -119,6 +120,26 @@ const HooksEditor = () => {
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  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)}
 | 
			
		||||
      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 (
 | 
			
		||||
    <Box
 | 
			
		||||
      css={{
 | 
			
		||||
@@ -131,7 +152,7 @@ const HooksEditor = () => {
 | 
			
		||||
        width: "100%",
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      <EditorNavigation />
 | 
			
		||||
      <EditorNavigation renderNav={renderNav} />
 | 
			
		||||
      {snap.files.length > 0 && router.isReady ? (
 | 
			
		||||
        <Monaco
 | 
			
		||||
          keepCurrentModel
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ import {
 | 
			
		||||
import { TTS, tts } from "../utils/hookOnCalculator";
 | 
			
		||||
import { deployHook } from "../state/actions";
 | 
			
		||||
import { useSnapshot } from "valtio";
 | 
			
		||||
import state, { SelectOption } from "../state";
 | 
			
		||||
import state, { IFile, SelectOption } from "../state";
 | 
			
		||||
import toast from "react-hot-toast";
 | 
			
		||||
import { prepareDeployHookTx, sha256 } from "../state/actions/deployHook";
 | 
			
		||||
import estimateFee from "../utils/estimateFee";
 | 
			
		||||
@@ -56,9 +56,9 @@ export type SetHookData = {
 | 
			
		||||
export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
 | 
			
		||||
  ({ accountAddress }) => {
 | 
			
		||||
    const snap = useSnapshot(state);
 | 
			
		||||
    const activeFile = snap.files[snap.active]?.compiledContent
 | 
			
		||||
      ? snap.files[snap.active]
 | 
			
		||||
      : snap.files.filter(file => file.compiledContent)[0];
 | 
			
		||||
    const compiledFiles = snap.files.filter(file => file.compiledContent);
 | 
			
		||||
    const activeFile = compiledFiles[snap.activeWat] as IFile | undefined;
 | 
			
		||||
 | 
			
		||||
    const [isSetHookDialogOpen, setIsSetHookDialogOpen] = useState(false);
 | 
			
		||||
 | 
			
		||||
    const accountOptions: SelectOption[] = snap.accounts.map(acc => ({
 | 
			
		||||
@@ -72,6 +72,15 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
 | 
			
		||||
    const account = snap.accounts.find(
 | 
			
		||||
      acc => acc.address === selectedAccount?.value
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const getHookNamespace = useCallback(
 | 
			
		||||
      () =>
 | 
			
		||||
        activeFile && snap.deployValues[activeFile.name]
 | 
			
		||||
          ? snap.deployValues[activeFile.name].HookNamespace
 | 
			
		||||
          : activeFile?.name.split(".")[0] || "",
 | 
			
		||||
      [activeFile, snap.deployValues]
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const {
 | 
			
		||||
      register,
 | 
			
		||||
      handleSubmit,
 | 
			
		||||
@@ -81,13 +90,10 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
 | 
			
		||||
      getValues,
 | 
			
		||||
      formState: { errors },
 | 
			
		||||
    } = useForm<SetHookData>({
 | 
			
		||||
      defaultValues: snap.deployValues?.[activeFile?.name]
 | 
			
		||||
        ? snap.deployValues[activeFile?.name]
 | 
			
		||||
        : {
 | 
			
		||||
            HookNamespace:
 | 
			
		||||
              snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || "",
 | 
			
		||||
            Invoke: transactionOptions.filter(to => to.label === "ttPAYMENT"),
 | 
			
		||||
          },
 | 
			
		||||
      defaultValues: (activeFile && snap.deployValues[activeFile.name]) || {
 | 
			
		||||
        HookNamespace: activeFile?.name.split(".")[0] || "",
 | 
			
		||||
        Invoke: transactionOptions.filter(to => to.label === "ttPAYMENT"),
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
    const { fields, append, remove } = useFieldArray({
 | 
			
		||||
      control,
 | 
			
		||||
@@ -97,20 +103,15 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
 | 
			
		||||
    const [estimateLoading, setEstimateLoading] = useState(false);
 | 
			
		||||
    const watchedFee = watch("Fee");
 | 
			
		||||
 | 
			
		||||
    // Update value if activeWat changes
 | 
			
		||||
    // Update value if activeFile changes
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
      const defaultValue = snap.deployValues?.[activeFile?.name]
 | 
			
		||||
        ? snap.deployValues?.[activeFile?.name].HookNamespace
 | 
			
		||||
        : snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || "";
 | 
			
		||||
      if (!activeFile) return;
 | 
			
		||||
      const defaultValue = getHookNamespace();
 | 
			
		||||
 | 
			
		||||
      setValue("HookNamespace", defaultValue);
 | 
			
		||||
      setFormInitialized(true);
 | 
			
		||||
    }, [
 | 
			
		||||
      snap.activeWat,
 | 
			
		||||
      snap.files,
 | 
			
		||||
      setValue,
 | 
			
		||||
      activeFile?.name,
 | 
			
		||||
      snap.deployValues,
 | 
			
		||||
    ]);
 | 
			
		||||
    }, [setValue, activeFile, snap.deployValues, getHookNamespace]);
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
      if (
 | 
			
		||||
        watchedFee &&
 | 
			
		||||
@@ -128,21 +129,19 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
 | 
			
		||||
    //   name: "HookGrants", // unique name for your Field Array
 | 
			
		||||
    // });
 | 
			
		||||
    const [hashedNamespace, setHashedNamespace] = useState("");
 | 
			
		||||
    const namespace = watch(
 | 
			
		||||
      "HookNamespace",
 | 
			
		||||
      snap.deployValues?.[activeFile?.name]
 | 
			
		||||
        ? snap.deployValues?.[activeFile?.name].HookNamespace
 | 
			
		||||
        : snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || ""
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const namespace = watch("HookNamespace", getHookNamespace());
 | 
			
		||||
 | 
			
		||||
    const calculateHashedValue = useCallback(async () => {
 | 
			
		||||
      const hashedVal = await sha256(namespace);
 | 
			
		||||
      setHashedNamespace(hashedVal.toUpperCase());
 | 
			
		||||
    }, [namespace]);
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
      calculateHashedValue();
 | 
			
		||||
    }, [namespace, calculateHashedValue]);
 | 
			
		||||
 | 
			
		||||
    // Calcucate initial fee estimate when modal opens
 | 
			
		||||
    // Calculate initial fee estimate when modal opens
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
      if (formInitialized && account) {
 | 
			
		||||
        (async () => {
 | 
			
		||||
@@ -161,18 +160,15 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
 | 
			
		||||
    }, [formInitialized]);
 | 
			
		||||
 | 
			
		||||
    const tooLargeFile = () => {
 | 
			
		||||
      const activeFile = snap.files[snap.active].compiledContent
 | 
			
		||||
        ? snap.files[snap.active]
 | 
			
		||||
        : snap.files.filter(file => file.compiledContent)[0];
 | 
			
		||||
      return Boolean(
 | 
			
		||||
        activeFile?.compiledContent?.byteLength &&
 | 
			
		||||
          activeFile?.compiledContent?.byteLength >= 64000
 | 
			
		||||
      );
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const onSubmit: SubmitHandler<SetHookData> = async (data) => {
 | 
			
		||||
    const onSubmit: SubmitHandler<SetHookData> = async data => {
 | 
			
		||||
      const currAccount = state.accounts.find(
 | 
			
		||||
        (acc) => acc.address === account?.address
 | 
			
		||||
        acc => acc.address === account?.address
 | 
			
		||||
      );
 | 
			
		||||
      if (!account) return;
 | 
			
		||||
      if (currAccount) currAccount.isLoading = true;
 | 
			
		||||
@@ -194,10 +190,7 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
 | 
			
		||||
            uppercase
 | 
			
		||||
            variant={"secondary"}
 | 
			
		||||
            disabled={
 | 
			
		||||
              !account ||
 | 
			
		||||
              account.isLoading ||
 | 
			
		||||
              !snap.files.filter(file => file.compiledWatContent).length ||
 | 
			
		||||
              tooLargeFile()
 | 
			
		||||
              !account || account.isLoading || !activeFile || tooLargeFile()
 | 
			
		||||
            }
 | 
			
		||||
          >
 | 
			
		||||
            Set Hook
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,8 @@ import {
 | 
			
		||||
} from "./Dialog";
 | 
			
		||||
import { Plus, X } from "phosphor-react";
 | 
			
		||||
import { styled } from "../stitches.config";
 | 
			
		||||
import { capitalize } from "../utils/helpers";
 | 
			
		||||
import ContextMenu from "./ContextMenu";
 | 
			
		||||
 | 
			
		||||
const ErrorText = styled(Text, {
 | 
			
		||||
  color: "$error",
 | 
			
		||||
@@ -25,11 +27,11 @@ const ErrorText = styled(Text, {
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
interface TabProps {
 | 
			
		||||
  header?: string;
 | 
			
		||||
  children: ReactNode;
 | 
			
		||||
  header: string;
 | 
			
		||||
  children?: ReactNode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO customise messages shown
 | 
			
		||||
// TODO customize messages shown
 | 
			
		||||
interface Props {
 | 
			
		||||
  label?: string;
 | 
			
		||||
  activeIndex?: number;
 | 
			
		||||
@@ -38,8 +40,12 @@ interface Props {
 | 
			
		||||
  children: ReactElement<TabProps>[];
 | 
			
		||||
  keepAllAlive?: boolean;
 | 
			
		||||
  defaultExtension?: string;
 | 
			
		||||
  appendDefaultExtension?: boolean;
 | 
			
		||||
  extensionRequired?: boolean;
 | 
			
		||||
  allowedExtensions?: string[];
 | 
			
		||||
  headerExtraValidation?: {
 | 
			
		||||
    regex: string | RegExp;
 | 
			
		||||
    error: string;
 | 
			
		||||
  };
 | 
			
		||||
  onCreateNewTab?: (name: string) => any;
 | 
			
		||||
  onCloseTab?: (index: number, header?: string) => any;
 | 
			
		||||
  onChangeActive?: (index: number, header?: string) => any;
 | 
			
		||||
@@ -57,8 +63,9 @@ export const Tabs = ({
 | 
			
		||||
  onCreateNewTab,
 | 
			
		||||
  onCloseTab,
 | 
			
		||||
  onChangeActive,
 | 
			
		||||
  headerExtraValidation,
 | 
			
		||||
  extensionRequired,
 | 
			
		||||
  defaultExtension = "",
 | 
			
		||||
  appendDefaultExtension = false,
 | 
			
		||||
  allowedExtensions,
 | 
			
		||||
}: Props) => {
 | 
			
		||||
  const [active, setActive] = useState(activeIndex || 0);
 | 
			
		||||
@@ -87,16 +94,38 @@ export const Tabs = ({
 | 
			
		||||
 | 
			
		||||
  const validateTabname = useCallback(
 | 
			
		||||
    (tabname: string): { error: string | null } => {
 | 
			
		||||
      if (tabs.find(tab => tab.header === tabname)) {
 | 
			
		||||
        return { error: "Name already exists." };
 | 
			
		||||
      if (!tabname) {
 | 
			
		||||
        return { error: `Please enter ${label.toLocaleLowerCase()} name.` };
 | 
			
		||||
      }
 | 
			
		||||
      if (tabs.find(tab => tab.header === tabname)) {
 | 
			
		||||
        return { error: `${capitalize(label)} name already exists.` };
 | 
			
		||||
      }
 | 
			
		||||
      const ext =
 | 
			
		||||
        (tabname.includes(".") && tabname.split(".").pop()) ||
 | 
			
		||||
        defaultExtension ||
 | 
			
		||||
        "";
 | 
			
		||||
      if (extensionRequired && !ext) {
 | 
			
		||||
        return { error: "File extension is required!" };
 | 
			
		||||
      }
 | 
			
		||||
      const ext = tabname.split(".").pop() || "";
 | 
			
		||||
      if (allowedExtensions && !allowedExtensions.includes(ext)) {
 | 
			
		||||
        return { error: "This file extension is not allowed!" };
 | 
			
		||||
      }
 | 
			
		||||
      if (
 | 
			
		||||
        headerExtraValidation &&
 | 
			
		||||
        !tabname.match(headerExtraValidation.regex)
 | 
			
		||||
      ) {
 | 
			
		||||
        return { error: headerExtraValidation.error };
 | 
			
		||||
      }
 | 
			
		||||
      return { error: null };
 | 
			
		||||
    },
 | 
			
		||||
    [allowedExtensions, tabs]
 | 
			
		||||
    [
 | 
			
		||||
      allowedExtensions,
 | 
			
		||||
      defaultExtension,
 | 
			
		||||
      extensionRequired,
 | 
			
		||||
      headerExtraValidation,
 | 
			
		||||
      label,
 | 
			
		||||
      tabs,
 | 
			
		||||
    ]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const handleActiveChange = useCallback(
 | 
			
		||||
@@ -108,31 +137,25 @@ export const Tabs = ({
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const handleCreateTab = useCallback(() => {
 | 
			
		||||
    // add default extension in case omitted
 | 
			
		||||
    let _tabname = tabname.includes(".")
 | 
			
		||||
      ? tabname
 | 
			
		||||
      : `${tabname}.${defaultExtension}`;
 | 
			
		||||
    if (appendDefaultExtension && !_tabname.endsWith(defaultExtension)) {
 | 
			
		||||
      _tabname = `${_tabname}.${defaultExtension}`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const chk = validateTabname(_tabname);
 | 
			
		||||
    const chk = validateTabname(tabname);
 | 
			
		||||
    if (chk.error) {
 | 
			
		||||
      setNewtabError(`Error: ${chk.error}`);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    let _tabname = tabname;
 | 
			
		||||
    if (defaultExtension && !_tabname.endsWith(defaultExtension)) {
 | 
			
		||||
      _tabname = `${_tabname}.${defaultExtension}`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setIsNewtabDialogOpen(false);
 | 
			
		||||
    setTabname("");
 | 
			
		||||
 | 
			
		||||
    onCreateNewTab?.(_tabname);
 | 
			
		||||
 | 
			
		||||
    // switch to new tab?
 | 
			
		||||
    handleActiveChange(tabs.length, _tabname);
 | 
			
		||||
  }, [
 | 
			
		||||
    tabname,
 | 
			
		||||
    defaultExtension,
 | 
			
		||||
    appendDefaultExtension,
 | 
			
		||||
    validateTabname,
 | 
			
		||||
    onCreateNewTab,
 | 
			
		||||
    handleActiveChange,
 | 
			
		||||
@@ -146,8 +169,10 @@ export const Tabs = ({
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      onCloseTab?.(idx, tabs[idx].header);
 | 
			
		||||
 | 
			
		||||
      handleActiveChange(idx, tabs[idx].header);
 | 
			
		||||
    },
 | 
			
		||||
    [active, onCloseTab, tabs]
 | 
			
		||||
    [active, handleActiveChange, onCloseTab, tabs]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
@@ -164,46 +189,47 @@ export const Tabs = ({
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          {tabs.map((tab, idx) => (
 | 
			
		||||
            <Button
 | 
			
		||||
              key={tab.header}
 | 
			
		||||
              role="tab"
 | 
			
		||||
              tabIndex={idx}
 | 
			
		||||
              onClick={() => handleActiveChange(idx, tab.header)}
 | 
			
		||||
              onKeyPress={() => handleActiveChange(idx, tab.header)}
 | 
			
		||||
              outline={active !== idx}
 | 
			
		||||
              size="sm"
 | 
			
		||||
              css={{
 | 
			
		||||
                "&:hover": {
 | 
			
		||||
                  span: {
 | 
			
		||||
                    visibility: "visible",
 | 
			
		||||
                  },
 | 
			
		||||
                },
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              {tab.header || idx}
 | 
			
		||||
              {onCloseTab && (
 | 
			
		||||
                <Box
 | 
			
		||||
                  as="span"
 | 
			
		||||
                  css={{
 | 
			
		||||
                    display: "flex",
 | 
			
		||||
                    p: "2px",
 | 
			
		||||
                    borderRadius: "$full",
 | 
			
		||||
                    mr: "-4px",
 | 
			
		||||
                    "&:hover": {
 | 
			
		||||
                      // boxSizing: "0px 0px 1px",
 | 
			
		||||
                      backgroundColor: "$mauve2",
 | 
			
		||||
                      color: "$mauve12",
 | 
			
		||||
            <ContextMenu key={tab.header}>
 | 
			
		||||
              <Button
 | 
			
		||||
                role="tab"
 | 
			
		||||
                tabIndex={idx}
 | 
			
		||||
                onClick={() => handleActiveChange(idx, tab.header)}
 | 
			
		||||
                onKeyPress={() => handleActiveChange(idx, tab.header)}
 | 
			
		||||
                outline={active !== idx}
 | 
			
		||||
                size="sm"
 | 
			
		||||
                css={{
 | 
			
		||||
                  "&:hover": {
 | 
			
		||||
                    span: {
 | 
			
		||||
                      visibility: "visible",
 | 
			
		||||
                    },
 | 
			
		||||
                  }}
 | 
			
		||||
                  onClick={(ev: React.MouseEvent<HTMLElement>) => {
 | 
			
		||||
                    ev.stopPropagation();
 | 
			
		||||
                    handleCloseTab(idx);
 | 
			
		||||
                  }}
 | 
			
		||||
                >
 | 
			
		||||
                  <X size="9px" weight="bold" />
 | 
			
		||||
                </Box>
 | 
			
		||||
              )}
 | 
			
		||||
            </Button>
 | 
			
		||||
                  },
 | 
			
		||||
                }}
 | 
			
		||||
              >
 | 
			
		||||
                {tab.header || idx}
 | 
			
		||||
                {onCloseTab && (
 | 
			
		||||
                  <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();
 | 
			
		||||
                      handleCloseTab(idx);
 | 
			
		||||
                    }}
 | 
			
		||||
                  >
 | 
			
		||||
                    <X size="9px" weight="bold" />
 | 
			
		||||
                  </Box>
 | 
			
		||||
                )}
 | 
			
		||||
              </Button>
 | 
			
		||||
            </ContextMenu>
 | 
			
		||||
          ))}
 | 
			
		||||
          {onCreateNewTab && (
 | 
			
		||||
            <Dialog
 | 
			
		||||
@@ -216,11 +242,14 @@ export const Tabs = ({
 | 
			
		||||
                  size="sm"
 | 
			
		||||
                  css={{ alignItems: "center", px: "$2", mr: "$3" }}
 | 
			
		||||
                >
 | 
			
		||||
                  <Plus size="16px" /> {tabs.length === 0 && `Add new ${label.toLocaleLowerCase()}`}
 | 
			
		||||
                  <Plus size="16px" />{" "}
 | 
			
		||||
                  {tabs.length === 0 && `Add new ${label.toLocaleLowerCase()}`}
 | 
			
		||||
                </Button>
 | 
			
		||||
              </DialogTrigger>
 | 
			
		||||
              <DialogContent>
 | 
			
		||||
                <DialogTitle>Create new {label.toLocaleLowerCase()}</DialogTitle>
 | 
			
		||||
                <DialogTitle>
 | 
			
		||||
                  Create new {label.toLocaleLowerCase()}
 | 
			
		||||
                </DialogTitle>
 | 
			
		||||
                <DialogDescription>
 | 
			
		||||
                  <Label>{label} name</Label>
 | 
			
		||||
                  <Input
 | 
			
		||||
@@ -259,29 +288,32 @@ export const Tabs = ({
 | 
			
		||||
          )}
 | 
			
		||||
        </Stack>
 | 
			
		||||
      )}
 | 
			
		||||
      {keepAllAlive ? (
 | 
			
		||||
        tabs.map((tab, idx) => {
 | 
			
		||||
          // TODO Maybe rule out fragments as children
 | 
			
		||||
          if (!isValidElement(tab.children)) {
 | 
			
		||||
            if (active !== idx) return null;
 | 
			
		||||
            return tab.children;
 | 
			
		||||
          }
 | 
			
		||||
          let key = tab.children.key || tab.header || idx;
 | 
			
		||||
          let { children } = tab;
 | 
			
		||||
          let { style, ...props } = children.props;
 | 
			
		||||
          return (
 | 
			
		||||
            <children.type
 | 
			
		||||
              key={key}
 | 
			
		||||
              {...props}
 | 
			
		||||
              style={{ ...style, display: active !== idx ? "none" : undefined }}
 | 
			
		||||
            />
 | 
			
		||||
          );
 | 
			
		||||
        })
 | 
			
		||||
      ) : (
 | 
			
		||||
        <Fragment key={tabs[active].header || active}>
 | 
			
		||||
          {tabs[active].children}
 | 
			
		||||
        </Fragment>
 | 
			
		||||
      )}
 | 
			
		||||
      {keepAllAlive
 | 
			
		||||
        ? tabs.map((tab, idx) => {
 | 
			
		||||
            // TODO Maybe rule out fragments as children
 | 
			
		||||
            if (!isValidElement(tab.children)) {
 | 
			
		||||
              if (active !== idx) return null;
 | 
			
		||||
              return tab.children;
 | 
			
		||||
            }
 | 
			
		||||
            let key = tab.children.key || tab.header || idx;
 | 
			
		||||
            let { children } = tab;
 | 
			
		||||
            let { style, ...props } = children.props;
 | 
			
		||||
            return (
 | 
			
		||||
              <children.type
 | 
			
		||||
                key={key}
 | 
			
		||||
                {...props}
 | 
			
		||||
                style={{
 | 
			
		||||
                  ...style,
 | 
			
		||||
                  display: active !== idx ? "none" : undefined,
 | 
			
		||||
                }}
 | 
			
		||||
              />
 | 
			
		||||
            );
 | 
			
		||||
          })
 | 
			
		||||
        : tabs[active] && (
 | 
			
		||||
            <Fragment key={tabs[active].header || active}>
 | 
			
		||||
              {tabs[active].children}
 | 
			
		||||
            </Fragment>
 | 
			
		||||
          )}
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,14 @@
 | 
			
		||||
import { Play } from "phosphor-react";
 | 
			
		||||
import { FC, useCallback, useEffect, useMemo } from "react";
 | 
			
		||||
import { FC, useCallback, useEffect } from "react";
 | 
			
		||||
import { useSnapshot } from "valtio";
 | 
			
		||||
import state from "../../state";
 | 
			
		||||
import {
 | 
			
		||||
  defaultTransactionType,
 | 
			
		||||
  getTxFields,
 | 
			
		||||
  modifyTransaction,
 | 
			
		||||
  prepareState,
 | 
			
		||||
  prepareTransaction,
 | 
			
		||||
  SelectOption,
 | 
			
		||||
  TransactionState,
 | 
			
		||||
} from "../../state/transactions";
 | 
			
		||||
import { sendTransaction } from "../../state/actions";
 | 
			
		||||
@@ -15,7 +18,7 @@ import Flex from "../Flex";
 | 
			
		||||
import { TxJson } from "./json";
 | 
			
		||||
import { TxUI } from "./ui";
 | 
			
		||||
import { default as _estimateFee } from "../../utils/estimateFee";
 | 
			
		||||
import toast from 'react-hot-toast';
 | 
			
		||||
import toast from "react-hot-toast";
 | 
			
		||||
 | 
			
		||||
export interface TransactionProps {
 | 
			
		||||
  header: string;
 | 
			
		||||
@@ -34,7 +37,6 @@ const Transaction: FC<TransactionProps> = ({
 | 
			
		||||
    txIsDisabled,
 | 
			
		||||
    txIsLoading,
 | 
			
		||||
    viewType,
 | 
			
		||||
    editorSavedValue,
 | 
			
		||||
    editorValue,
 | 
			
		||||
  } = txState;
 | 
			
		||||
 | 
			
		||||
@@ -46,7 +48,7 @@ const Transaction: FC<TransactionProps> = ({
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const prepareOptions = useCallback(
 | 
			
		||||
    (state: TransactionState = txState) => {
 | 
			
		||||
    (state: Partial<TransactionState> = txState) => {
 | 
			
		||||
      const {
 | 
			
		||||
        selectedTransaction,
 | 
			
		||||
        selectedDestAccount,
 | 
			
		||||
@@ -55,9 +57,7 @@ const Transaction: FC<TransactionProps> = ({
 | 
			
		||||
      } = state;
 | 
			
		||||
 | 
			
		||||
      const TransactionType = selectedTransaction?.value || null;
 | 
			
		||||
      const Destination =
 | 
			
		||||
        selectedDestAccount?.value ||
 | 
			
		||||
        ("Destination" in txFields ? null : undefined);
 | 
			
		||||
      const Destination = selectedDestAccount?.value || txFields?.Destination;
 | 
			
		||||
      const Account = selectedAccount?.value || null;
 | 
			
		||||
 | 
			
		||||
      return prepareTransaction({
 | 
			
		||||
@@ -109,8 +109,9 @@ const Transaction: FC<TransactionProps> = ({
 | 
			
		||||
      }
 | 
			
		||||
      const options = prepareOptions(st);
 | 
			
		||||
 | 
			
		||||
      if (options.Destination === null) {
 | 
			
		||||
        throw Error("Destination account cannot be null");
 | 
			
		||||
      const fields = getTxFields(options.TransactionType);
 | 
			
		||||
      if (fields.Destination && !options.Destination) {
 | 
			
		||||
        throw Error("Destination account is required!");
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      await sendTransaction(account, options, { logPrefix });
 | 
			
		||||
@@ -136,15 +137,38 @@ const Transaction: FC<TransactionProps> = ({
 | 
			
		||||
    prepareOptions,
 | 
			
		||||
  ]);
 | 
			
		||||
 | 
			
		||||
  const resetState = useCallback(() => {
 | 
			
		||||
    modifyTransaction(header, { viewType }, { replaceState: true });
 | 
			
		||||
  }, [header, viewType]);
 | 
			
		||||
  const getJsonString = useCallback(
 | 
			
		||||
    (state?: Partial<TransactionState>) =>
 | 
			
		||||
      JSON.stringify(
 | 
			
		||||
        prepareOptions?.(state) || {},
 | 
			
		||||
        null,
 | 
			
		||||
        editorSettings.tabSize
 | 
			
		||||
      ),
 | 
			
		||||
    [editorSettings.tabSize, prepareOptions]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const jsonValue = useMemo(
 | 
			
		||||
    () =>
 | 
			
		||||
      editorSavedValue ||
 | 
			
		||||
      JSON.stringify(prepareOptions?.() || {}, null, editorSettings.tabSize),
 | 
			
		||||
    [editorSavedValue, editorSettings.tabSize, prepareOptions]
 | 
			
		||||
  const resetState = useCallback(
 | 
			
		||||
    (transactionType: SelectOption | undefined = defaultTransactionType) => {
 | 
			
		||||
      const fields = getTxFields(transactionType?.value);
 | 
			
		||||
 | 
			
		||||
      const nwState: Partial<TransactionState> = {
 | 
			
		||||
        viewType,
 | 
			
		||||
        selectedTransaction: transactionType,
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      if (fields.Destination !== undefined) {
 | 
			
		||||
        nwState.selectedDestAccount = null;
 | 
			
		||||
        fields.Destination = "";
 | 
			
		||||
      } else {
 | 
			
		||||
        fields.Destination = undefined;
 | 
			
		||||
      }
 | 
			
		||||
      nwState.txFields = fields;
 | 
			
		||||
 | 
			
		||||
      const state = modifyTransaction(header, nwState, { replaceState: true });
 | 
			
		||||
      const editorValue = getJsonString(state);
 | 
			
		||||
      return setState({ editorValue });
 | 
			
		||||
    },
 | 
			
		||||
    [getJsonString, header, setState, viewType]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const estimateFee = useCallback(
 | 
			
		||||
@@ -156,10 +180,10 @@ const Transaction: FC<TransactionProps> = ({
 | 
			
		||||
      );
 | 
			
		||||
      if (!account) {
 | 
			
		||||
        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.Sequence = account.sequence;
 | 
			
		||||
@@ -176,7 +200,7 @@ const Transaction: FC<TransactionProps> = ({
 | 
			
		||||
    <Box css={{ position: "relative", height: "calc(100% - 28px)" }} {...props}>
 | 
			
		||||
      {viewType === "json" ? (
 | 
			
		||||
        <TxJson
 | 
			
		||||
          value={jsonValue}
 | 
			
		||||
          getJsonString={getJsonString}
 | 
			
		||||
          header={header}
 | 
			
		||||
          state={txState}
 | 
			
		||||
          setState={setState}
 | 
			
		||||
@@ -199,7 +223,7 @@ const Transaction: FC<TransactionProps> = ({
 | 
			
		||||
        <Button
 | 
			
		||||
          onClick={() => {
 | 
			
		||||
            if (viewType === "ui") {
 | 
			
		||||
              setState({ editorSavedValue: null, viewType: "json" });
 | 
			
		||||
              setState({ viewType: "json" });
 | 
			
		||||
            } else setState({ viewType: "ui" });
 | 
			
		||||
          }}
 | 
			
		||||
          outline
 | 
			
		||||
@@ -207,7 +231,7 @@ const Transaction: FC<TransactionProps> = ({
 | 
			
		||||
          {viewType === "ui" ? "EDIT AS JSON" : "EXIT JSON MODE"}
 | 
			
		||||
        </Button>
 | 
			
		||||
        <Flex row>
 | 
			
		||||
          <Button onClick={resetState} outline css={{ mr: "$3" }}>
 | 
			
		||||
          <Button onClick={() => resetState()} outline css={{ mr: "$3" }}>
 | 
			
		||||
            RESET
 | 
			
		||||
          </Button>
 | 
			
		||||
          <Button
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { FC, useCallback, useEffect, useState } from "react";
 | 
			
		||||
import { FC, useCallback, useEffect, useMemo, useState } from "react";
 | 
			
		||||
import { useSnapshot } from "valtio";
 | 
			
		||||
import state, {
 | 
			
		||||
  prepareState,
 | 
			
		||||
@@ -15,7 +15,7 @@ import Monaco from "../Monaco";
 | 
			
		||||
import type monaco from "monaco-editor";
 | 
			
		||||
 | 
			
		||||
interface JsonProps {
 | 
			
		||||
  value?: string;
 | 
			
		||||
  getJsonString?: (state?: Partial<TransactionState>) => string;
 | 
			
		||||
  header?: string;
 | 
			
		||||
  setState: (pTx?: Partial<TransactionState> | undefined) => void;
 | 
			
		||||
  state: TransactionState;
 | 
			
		||||
@@ -23,22 +23,23 @@ interface JsonProps {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const TxJson: FC<JsonProps> = ({
 | 
			
		||||
  value = "",
 | 
			
		||||
  getJsonString,
 | 
			
		||||
  state: txState,
 | 
			
		||||
  header,
 | 
			
		||||
  setState,
 | 
			
		||||
}) => {
 | 
			
		||||
  const { editorSettings, accounts } = useSnapshot(state);
 | 
			
		||||
  const { editorValue = value, estimatedFee } = txState;
 | 
			
		||||
  const [hasUnsaved, setHasUnsaved] = useState(false);
 | 
			
		||||
  const { editorValue, estimatedFee } = txState;
 | 
			
		||||
  const [currTxType, setCurrTxType] = useState<string | undefined>(
 | 
			
		||||
    txState.selectedTransaction?.value
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setState({ editorValue: value });
 | 
			
		||||
    setState({
 | 
			
		||||
      editorValue: getJsonString?.(),
 | 
			
		||||
    });
 | 
			
		||||
    // eslint-disable-next-line react-hooks/exhaustive-deps
 | 
			
		||||
  }, [value]);
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const parsed = parseJSON(editorValue);
 | 
			
		||||
@@ -52,21 +53,22 @@ export const TxJson: FC<JsonProps> = ({
 | 
			
		||||
    }
 | 
			
		||||
  }, [editorValue]);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (editorValue === value) setHasUnsaved(false);
 | 
			
		||||
    else setHasUnsaved(true);
 | 
			
		||||
  }, [editorValue, value]);
 | 
			
		||||
 | 
			
		||||
  const saveState = (value: string, transactionType?: string) => {
 | 
			
		||||
    const tx = prepareState(value, transactionType);
 | 
			
		||||
    if (tx) setState(tx);
 | 
			
		||||
    if (tx) {
 | 
			
		||||
      setState(tx);
 | 
			
		||||
      setState({
 | 
			
		||||
        editorValue: getJsonString?.(tx),
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const discardChanges = () => {
 | 
			
		||||
    showAlert("Confirm", {
 | 
			
		||||
      body: "Are you sure to discard these changes?",
 | 
			
		||||
      confirmText: "Yes",
 | 
			
		||||
      onConfirm: () => setState({ editorValue: value }),
 | 
			
		||||
      onCancel: () => {},
 | 
			
		||||
      onConfirm: () => setState({ editorValue: getJsonString?.() }),
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
@@ -79,8 +81,8 @@ export const TxJson: FC<JsonProps> = ({
 | 
			
		||||
    showAlert("Error!", {
 | 
			
		||||
      body: `Malformed Transaction in ${header}, would you like to discard these changes?`,
 | 
			
		||||
      confirmText: "Discard",
 | 
			
		||||
      onConfirm: () => setState({ editorValue: value }),
 | 
			
		||||
      onCancel: () => setState({ viewType: "json", editorSavedValue: value }),
 | 
			
		||||
      onConfirm: () => setState({ editorValue: getJsonString?.() }),
 | 
			
		||||
      onCancel: () => setState({ viewType: "json" }),
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
@@ -174,6 +176,11 @@ export const TxJson: FC<JsonProps> = ({
 | 
			
		||||
    });
 | 
			
		||||
  }, [getSchemas, monacoInst]);
 | 
			
		||||
 | 
			
		||||
  const hasUnsaved = useMemo(
 | 
			
		||||
    () => editorValue !== getJsonString?.(),
 | 
			
		||||
    [editorValue, getJsonString]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Monaco
 | 
			
		||||
      rootProps={{
 | 
			
		||||
@@ -203,14 +210,14 @@ export const TxJson: FC<JsonProps> = ({
 | 
			
		||||
          <Flex
 | 
			
		||||
            row
 | 
			
		||||
            align="center"
 | 
			
		||||
            css={{ fontSize: "$xs", color: "$textMuted", ml: 'auto' }}
 | 
			
		||||
            css={{ fontSize: "$xs", color: "$textMuted", ml: "auto" }}
 | 
			
		||||
          >
 | 
			
		||||
            <Text muted small>
 | 
			
		||||
              This file has unsaved changes.
 | 
			
		||||
            </Text>
 | 
			
		||||
            <Link
 | 
			
		||||
              css={{ ml: "$1" }}
 | 
			
		||||
              onClick={() => saveState(editorValue, currTxType)}
 | 
			
		||||
              onClick={() => saveState(editorValue || "", currTxType)}
 | 
			
		||||
            >
 | 
			
		||||
              save
 | 
			
		||||
            </Link>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { FC, useCallback, useEffect, useState } from "react";
 | 
			
		||||
import { FC, useCallback, useEffect, useMemo, useState } from "react";
 | 
			
		||||
import Container from "../Container";
 | 
			
		||||
import Flex from "../Flex";
 | 
			
		||||
import Input from "../Input";
 | 
			
		||||
@@ -7,9 +7,10 @@ import Text from "../Text";
 | 
			
		||||
import {
 | 
			
		||||
  SelectOption,
 | 
			
		||||
  TransactionState,
 | 
			
		||||
  transactionsData,
 | 
			
		||||
  transactionsOptions,
 | 
			
		||||
  TxFields,
 | 
			
		||||
  getTxFields,
 | 
			
		||||
  defaultTransactionType,
 | 
			
		||||
} from "../../state/transactions";
 | 
			
		||||
import { useSnapshot } from "valtio";
 | 
			
		||||
import state from "../../state";
 | 
			
		||||
@@ -38,12 +39,6 @@ export const TxUI: FC<UIProps> = ({
 | 
			
		||||
    txFields,
 | 
			
		||||
  } = txState;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  const transactionsOptions = transactionsData.map(tx => ({
 | 
			
		||||
    value: tx.TransactionType,
 | 
			
		||||
    label: tx.TransactionType,
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  const accountOptions: SelectOption[] = accounts.map(acc => ({
 | 
			
		||||
    label: acc.name,
 | 
			
		||||
    value: acc.address,
 | 
			
		||||
@@ -58,10 +53,16 @@ export const TxUI: FC<UIProps> = ({
 | 
			
		||||
 | 
			
		||||
  const [feeLoading, setFeeLoading] = useState(false);
 | 
			
		||||
 | 
			
		||||
  const resetOptions = useCallback(
 | 
			
		||||
  const resetFields = useCallback(
 | 
			
		||||
    (tt: string) => {
 | 
			
		||||
      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 });
 | 
			
		||||
    },
 | 
			
		||||
    [setState]
 | 
			
		||||
@@ -102,33 +103,37 @@ export const TxUI: FC<UIProps> = ({
 | 
			
		||||
    (tt: SelectOption) => {
 | 
			
		||||
      setState({ selectedTransaction: tt });
 | 
			
		||||
 | 
			
		||||
      const newState = resetOptions(tt.value);
 | 
			
		||||
      const newState = resetFields(tt.value);
 | 
			
		||||
 | 
			
		||||
      handleEstimateFee(newState, true);
 | 
			
		||||
    },
 | 
			
		||||
    [handleEstimateFee, resetOptions, setState]
 | 
			
		||||
    [handleEstimateFee, resetFields, setState]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  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" });
 | 
			
		||||
  const switchToJson = () => setState({ viewType: "json" });
 | 
			
		||||
 | 
			
		||||
  // default tx
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (selectedTransaction?.value) return;
 | 
			
		||||
 | 
			
		||||
    const defaultOption = transactionsOptions.find(
 | 
			
		||||
      tt => tt.value === "Payment"
 | 
			
		||||
    );
 | 
			
		||||
    if (defaultOption) {
 | 
			
		||||
      handleChangeTxType(defaultOption);
 | 
			
		||||
    if (defaultTransactionType) {
 | 
			
		||||
      handleChangeTxType(defaultTransactionType);
 | 
			
		||||
    }
 | 
			
		||||
  }, [handleChangeTxType, selectedTransaction?.value, transactionsOptions]);
 | 
			
		||||
  }, [handleChangeTxType, selectedTransaction?.value]);
 | 
			
		||||
 | 
			
		||||
  const fields = useMemo(
 | 
			
		||||
    () => getTxFields(selectedTransaction?.value),
 | 
			
		||||
    [selectedTransaction?.value]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const specialFields = ["TransactionType", "Account"];
 | 
			
		||||
  if (fields.Destination !== undefined) {
 | 
			
		||||
    specialFields.push("Destination");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const otherFields = Object.keys(txFields).filter(
 | 
			
		||||
    k => !specialFields.includes(k)
 | 
			
		||||
  ) as [keyof TxFields];
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Container
 | 
			
		||||
@@ -185,7 +190,7 @@ export const TxUI: FC<UIProps> = ({
 | 
			
		||||
            onChange={(acc: any) => handleSetAccount(acc)} // TODO make react-select have correct types for acc
 | 
			
		||||
          />
 | 
			
		||||
        </Flex>
 | 
			
		||||
        {txFields.Destination !== undefined && (
 | 
			
		||||
        {fields.Destination !== undefined && (
 | 
			
		||||
          <Flex
 | 
			
		||||
            row
 | 
			
		||||
            fluid
 | 
			
		||||
 
 | 
			
		||||
@@ -40,9 +40,9 @@
 | 
			
		||||
      {
 | 
			
		||||
        "label": "Token",
 | 
			
		||||
        "body": {
 | 
			
		||||
          "currency": "${1:13.1}",
 | 
			
		||||
          "value": "${2:FOO}",
 | 
			
		||||
          "description": "${3:rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpns}"
 | 
			
		||||
          "currency": "${1:USD}",
 | 
			
		||||
          "value": "${2:100}",
 | 
			
		||||
          "issuer": "${3:rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpns}"
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,8 @@
 | 
			
		||||
    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
			
		||||
    "TransactionType": "EscrowCancel",
 | 
			
		||||
    "Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
			
		||||
    "OfferSequence": 7
 | 
			
		||||
    "OfferSequence": 7,
 | 
			
		||||
    "Fee": "10"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
			
		||||
@@ -69,7 +70,8 @@
 | 
			
		||||
    "FinishAfter": 533171558,
 | 
			
		||||
    "Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
 | 
			
		||||
    "DestinationTag": 23480,
 | 
			
		||||
    "SourceTag": 11747
 | 
			
		||||
    "SourceTag": 11747,
 | 
			
		||||
    "Fee": "10"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
			
		||||
@@ -77,32 +79,50 @@
 | 
			
		||||
    "Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
			
		||||
    "OfferSequence": 7,
 | 
			
		||||
    "Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
 | 
			
		||||
    "Fulfillment": "A0028000"
 | 
			
		||||
    "Fulfillment": "A0028000",
 | 
			
		||||
    "Fee": "10"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "TransactionType": "NFTokenMint",
 | 
			
		||||
    "Account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
 | 
			
		||||
    "Fee": "10",
 | 
			
		||||
    "NFTokenTaxon": 0,
 | 
			
		||||
    "URI": "697066733A2F2F516D614374444B5A4656767666756676626479346573745A626851483744586831364354707631686F776D424779"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "TransactionType": "NFTokenBurn",
 | 
			
		||||
    "Account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
 | 
			
		||||
    "Fee": "10",
 | 
			
		||||
    "TokenID": "000B013A95F14B0044F78A264E41713C64B5F89242540EE208C3098E00000D65"
 | 
			
		||||
    "NFTokenID": "000B013A95F14B0044F78A264E41713C64B5F89242540EE208C3098E00000D65"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "TransactionType": "NFTokenAcceptOffer",
 | 
			
		||||
    "Fee": "10"
 | 
			
		||||
    "Account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
 | 
			
		||||
    "Fee": "10",
 | 
			
		||||
    "NFTokenSellOffer": "A2FA1A9911FE2AEF83DAB05F437768E26A301EF899BD31EB85E704B3D528FF18",
 | 
			
		||||
    "NFTokenBuyOffer": "4AAAEEA76E3C8148473CB3840CE637676E561FB02BD4CA22CA59729EA815B862",
 | 
			
		||||
    "NFTokenBrokerFee": "10"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "TransactionType": "NFTokenCancelOffer",
 | 
			
		||||
    "Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
 | 
			
		||||
    "TokenIDs": "000100001E962F495F07A990F4ED55ACCFEEF365DBAA76B6A048C0A200000007"
 | 
			
		||||
    "Fee": "10",
 | 
			
		||||
    "NFTokenOffers": {
 | 
			
		||||
      "$type": "json",
 | 
			
		||||
      "$value":  ["4AAAEEA76E3C8148473CB3840CE637676E561FB02BD4CA22CA59729EA815B862"]
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "TransactionType": "NFTokenCreateOffer",
 | 
			
		||||
    "Account": "rs8jBmmfpwgmrSPgwMsh7CvKRmRt1JTVSX",
 | 
			
		||||
    "TokenID": "000100001E962F495F07A990F4ED55ACCFEEF365DBAA76B6A048C0A200000007",
 | 
			
		||||
    "NFTokenID": "000100001E962F495F07A990F4ED55ACCFEEF365DBAA76B6A048C0A200000007",
 | 
			
		||||
    "Amount": {
 | 
			
		||||
      "$value": "100",
 | 
			
		||||
      "$type": "xrp"
 | 
			
		||||
    },
 | 
			
		||||
    "Flags": 1
 | 
			
		||||
    "Flags": 1,
 | 
			
		||||
    "Destination": "",
 | 
			
		||||
    "Fee": "10"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "TransactionType": "OfferCancel",
 | 
			
		||||
@@ -150,7 +170,8 @@
 | 
			
		||||
    "PublicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A",
 | 
			
		||||
    "CancelAfter": 533171558,
 | 
			
		||||
    "DestinationTag": 23480,
 | 
			
		||||
    "SourceTag": 11747
 | 
			
		||||
    "SourceTag": 11747,
 | 
			
		||||
    "Fee": "10"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
 | 
			
		||||
@@ -160,7 +181,8 @@
 | 
			
		||||
      "$value": "200",
 | 
			
		||||
      "$type": "xrp"
 | 
			
		||||
    },
 | 
			
		||||
    "Expiration": 543171558
 | 
			
		||||
    "Expiration": 543171558,
 | 
			
		||||
    "Fee": "10"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Flags": 0,
 | 
			
		||||
@@ -212,9 +234,13 @@
 | 
			
		||||
    "Fee": "12",
 | 
			
		||||
    "Flags": 262144,
 | 
			
		||||
    "LastLedgerSequence": 8007750,
 | 
			
		||||
    "Amount": {
 | 
			
		||||
      "$value": "100",
 | 
			
		||||
      "$type": "xrp"
 | 
			
		||||
    "LimitAmount": {
 | 
			
		||||
      "$type": "json",
 | 
			
		||||
      "$value": {
 | 
			
		||||
        "currency": "USD",
 | 
			
		||||
        "issuer": "rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc",
 | 
			
		||||
        "value": "100"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "Sequence": 12
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								package.json
									
									
									
									
									
								
							@@ -16,8 +16,9 @@
 | 
			
		||||
    "@octokit/core": "^3.5.1",
 | 
			
		||||
    "@radix-ui/colors": "^0.1.7",
 | 
			
		||||
    "@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-dropdown-menu": "^0.1.1",
 | 
			
		||||
    "@radix-ui/react-dropdown-menu": "^0.1.6",
 | 
			
		||||
    "@radix-ui/react-id": "^0.1.1",
 | 
			
		||||
    "@radix-ui/react-label": "^0.1.5",
 | 
			
		||||
    "@radix-ui/react-popover": "^0.1.6",
 | 
			
		||||
@@ -35,7 +36,7 @@
 | 
			
		||||
    "lodash.xor": "^4.5.0",
 | 
			
		||||
    "monaco-editor": "^0.33.0",
 | 
			
		||||
    "next": "^12.0.4",
 | 
			
		||||
    "next-auth": "^4.0.0-beta.5",
 | 
			
		||||
    "next-auth": "^4.10.1",
 | 
			
		||||
    "next-plausible": "^3.2.0",
 | 
			
		||||
    "next-themes": "^0.1.1",
 | 
			
		||||
    "normalize-url": "^7.0.2",
 | 
			
		||||
@@ -61,7 +62,7 @@
 | 
			
		||||
    "vscode-languageserver": "^7.0.0",
 | 
			
		||||
    "vscode-uri": "^3.0.2",
 | 
			
		||||
    "wabt": "1.0.16",
 | 
			
		||||
    "xrpl-accountlib": "^1.3.2",
 | 
			
		||||
    "xrpl-accountlib": "^1.5.2",
 | 
			
		||||
    "xrpl-client": "^1.9.4"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
@@ -75,5 +76,8 @@
 | 
			
		||||
    "eslint-config-next": "11.1.2",
 | 
			
		||||
    "raw-loader": "^4.0.2",
 | 
			
		||||
    "typescript": "4.4.4"
 | 
			
		||||
  },
 | 
			
		||||
  "resolutions": {
 | 
			
		||||
    "ripple-binary-codec": "=1.4.2"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -29,25 +29,30 @@ export const compileCode = async (activeId: number) => {
 | 
			
		||||
  const file = state.files[activeId]
 | 
			
		||||
  try {
 | 
			
		||||
    file.containsErrors = false
 | 
			
		||||
    const 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,
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      }),
 | 
			
		||||
    });
 | 
			
		||||
    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) {
 | 
			
		||||
@@ -61,29 +66,34 @@ export const compileCode = async (activeId: number) => {
 | 
			
		||||
      }
 | 
			
		||||
      throw errors
 | 
			
		||||
    }
 | 
			
		||||
    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
 | 
			
		||||
    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
 | 
			
		||||
    // put it into state
 | 
			
		||||
    import("wabt").then((wabt) => {
 | 
			
		||||
      const ww = wabt.default();
 | 
			
		||||
    try {
 | 
			
		||||
      // Decode base64 encoded wasm that is coming back from the endpoint
 | 
			
		||||
      const bufferData = await decodeBinary(json.output);
 | 
			
		||||
 | 
			
		||||
      // Import wabt from and create human readable version of wasm file and
 | 
			
		||||
      // put it into state
 | 
			
		||||
      const ww = (await import('wabt')).default()
 | 
			
		||||
      const myModule = ww.readWasm(new Uint8Array(bufferData), {
 | 
			
		||||
        readDebugNames: true,
 | 
			
		||||
      });
 | 
			
		||||
      myModule.applyNames();
 | 
			
		||||
 | 
			
		||||
      const wast = myModule.toText({ foldExprs: false, inlineExport: false });
 | 
			
		||||
      state.files[state.active].compiledWatContent = wast;
 | 
			
		||||
      toast.success("Compiled successfully!", { position: "bottom-center" });
 | 
			
		||||
 | 
			
		||||
      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" });
 | 
			
		||||
    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);
 | 
			
		||||
@@ -96,12 +106,19 @@ export const compileCode = async (activeId: number) => {
 | 
			
		||||
        });
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
    else if (err instanceof Error) {
 | 
			
		||||
      state.logs.push({
 | 
			
		||||
        type: "error",
 | 
			
		||||
        message: err.message,
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      state.logs.push({
 | 
			
		||||
        type: "error",
 | 
			
		||||
        message: "Something went wrong, check your connection try again later!",
 | 
			
		||||
        message: "Something went wrong, come back later!",
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    state.compiling = false;
 | 
			
		||||
    toast.error(`Error occurred while compiling!`, { position: "bottom-center" });
 | 
			
		||||
    file.containsErrors = true
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,6 @@ export const sendTransaction = async (account: IAccount, txOptions: TransactionO
 | 
			
		||||
        Fee,  // TODO auto-fillable default
 | 
			
		||||
        ...opts
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const { logPrefix = '' } = options || {}
 | 
			
		||||
    try {
 | 
			
		||||
        const signedAccount = derive.familySeed(account.secret);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import type monaco from "monaco-editor";
 | 
			
		||||
import { proxy, ref, subscribe } from "valtio";
 | 
			
		||||
import { devtools } from 'valtio/utils';
 | 
			
		||||
import { devtools, subscribeKey } from 'valtio/utils';
 | 
			
		||||
import { XrplClient } from "xrpl-client";
 | 
			
		||||
import { SplitSize } from "./actions/persistSplits";
 | 
			
		||||
 | 
			
		||||
@@ -168,16 +168,23 @@ if (process.env.NODE_ENV !== "production") {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (typeof window !== "undefined") {
 | 
			
		||||
  subscribe(state, () => {
 | 
			
		||||
    const { accounts, active } = state;
 | 
			
		||||
  subscribe(state.accounts, () => {
 | 
			
		||||
    const { accounts } = state;
 | 
			
		||||
    const accountsNoLoading = accounts.map(acc => ({ ...acc, isLoading: false }))
 | 
			
		||||
    localStorage.setItem("hooksIdeAccounts", JSON.stringify(accountsNoLoading));
 | 
			
		||||
    if (!state.files[active]?.compiledWatContent) {
 | 
			
		||||
      state.activeWat = 0;
 | 
			
		||||
    } else {
 | 
			
		||||
      state.activeWat = active;
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const updateActiveWat = () => {
 | 
			
		||||
    const filename = state.files[state.active]?.name
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,14 +18,13 @@ export interface TransactionState {
 | 
			
		||||
    txIsDisabled: boolean;
 | 
			
		||||
    txFields: TxFields;
 | 
			
		||||
    viewType: 'json' | 'ui',
 | 
			
		||||
    editorSavedValue: null | string,
 | 
			
		||||
    editorValue?: string,
 | 
			
		||||
    estimatedFee?: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export type TxFields = Omit<
 | 
			
		||||
    typeof transactionsData[0],
 | 
			
		||||
    Partial<typeof transactionsData[0]>,
 | 
			
		||||
    "Account" | "Sequence" | "TransactionType"
 | 
			
		||||
>;
 | 
			
		||||
 | 
			
		||||
@@ -36,15 +35,14 @@ export const defaultTransaction: TransactionState = {
 | 
			
		||||
    txIsLoading: false,
 | 
			
		||||
    txIsDisabled: false,
 | 
			
		||||
    txFields: {},
 | 
			
		||||
    viewType: 'ui',
 | 
			
		||||
    editorSavedValue: null
 | 
			
		||||
    viewType: 'ui'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const transactionsState = proxy({
 | 
			
		||||
    transactions: [
 | 
			
		||||
        {
 | 
			
		||||
            header: "test1.json",
 | 
			
		||||
            state: defaultTransaction,
 | 
			
		||||
            state: { ...defaultTransaction },
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
    activeHeader: "test1.json"
 | 
			
		||||
@@ -92,7 +90,7 @@ export const modifyTransaction = (
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Object.keys(partialTx).forEach(k => {
 | 
			
		||||
        // Typescript mess here, but is definetly safe!
 | 
			
		||||
        // Typescript mess here, but is definitely safe!
 | 
			
		||||
        const s = tx.state as any;
 | 
			
		||||
        const p = partialTx as any; // ? Make copy
 | 
			
		||||
        if (!deepEqual(s[k], p[k])) s[k] = p[k];
 | 
			
		||||
@@ -118,7 +116,7 @@ export const prepareTransaction = (data: any) => {
 | 
			
		||||
        // handle type: `json`
 | 
			
		||||
        if (_value && typeof _value === "object" && _value.$type === "json") {
 | 
			
		||||
            if (typeof _value.$value === "object") {
 | 
			
		||||
                options[field] = _value.$value as any;
 | 
			
		||||
                options[field] = _value.$value;
 | 
			
		||||
            } else {
 | 
			
		||||
                try {
 | 
			
		||||
                    options[field] = JSON.parse(_value.$value);
 | 
			
		||||
@@ -131,8 +129,8 @@ export const prepareTransaction = (data: any) => {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // delete unneccesary fields
 | 
			
		||||
        if (options[field] === undefined) {
 | 
			
		||||
        // delete unnecessary fields
 | 
			
		||||
        if (!options[field]) {
 | 
			
		||||
            delete options[field];
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
@@ -152,7 +150,7 @@ export const prepareState = (value: string, transactionType?: string) => {
 | 
			
		||||
 | 
			
		||||
    const { Account, TransactionType, Destination, ...rest } = options;
 | 
			
		||||
    let tx: Partial<TransactionState> = {};
 | 
			
		||||
    const txFields = getTxFields(transactionType)
 | 
			
		||||
    const schema = getTxFields(transactionType)
 | 
			
		||||
 | 
			
		||||
    if (Account) {
 | 
			
		||||
        const acc = state.accounts.find(acc => acc.address === Account);
 | 
			
		||||
@@ -180,9 +178,8 @@ export const prepareState = (value: string, transactionType?: string) => {
 | 
			
		||||
        tx.selectedTransaction = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (txFields.Destination !== undefined) {
 | 
			
		||||
    if (schema.Destination !== undefined) {
 | 
			
		||||
        const dest = state.accounts.find(acc => acc.address === Destination);
 | 
			
		||||
        rest.Destination = null
 | 
			
		||||
        if (dest) {
 | 
			
		||||
            tx.selectedDestAccount = {
 | 
			
		||||
                label: dest.name,
 | 
			
		||||
@@ -199,11 +196,14 @@ export const prepareState = (value: string, transactionType?: string) => {
 | 
			
		||||
            tx.selectedDestAccount = null
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if (Destination) {
 | 
			
		||||
        rest.Destination = Destination
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Object.keys(rest).forEach(field => {
 | 
			
		||||
        const value = rest[field];
 | 
			
		||||
        const origValue = txFields[field as keyof TxFields]
 | 
			
		||||
        const isXrp = typeof value !== 'object' && origValue && typeof origValue === 'object' && origValue.$type === 'xrp'
 | 
			
		||||
        const schemaVal = schema[field as keyof TxFields]
 | 
			
		||||
        const isXrp = typeof value !== 'object' && schemaVal && typeof schemaVal === 'object' && schemaVal.$type === 'xrp'
 | 
			
		||||
        if (isXrp) {
 | 
			
		||||
            rest[field] = {
 | 
			
		||||
                $type: "xrp",
 | 
			
		||||
@@ -218,7 +218,6 @@ export const prepareState = (value: string, transactionType?: string) => {
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    tx.txFields = rest;
 | 
			
		||||
    tx.editorSavedValue = null;
 | 
			
		||||
 | 
			
		||||
    return tx
 | 
			
		||||
}
 | 
			
		||||
@@ -244,3 +243,10 @@ export const getTxFields = (tt?: string) => {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export { transactionsData }
 | 
			
		||||
 | 
			
		||||
export const transactionsOptions = transactionsData.map(tx => ({
 | 
			
		||||
    value: tx.TransactionType,
 | 
			
		||||
    label: tx.TransactionType,
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
export const defaultTransactionType = transactionsOptions.find(tt => tt.value === 'Payment')
 | 
			
		||||
@@ -53,6 +53,7 @@ export const {
 | 
			
		||||
      accent: "#9D2DFF",
 | 
			
		||||
      background: "$gray1",
 | 
			
		||||
      backgroundAlt: "$gray4",
 | 
			
		||||
      backgroundOverlay: "$mauve2",
 | 
			
		||||
      text: "$gray12",
 | 
			
		||||
      textMuted: "$gray10",
 | 
			
		||||
      primary: "$plum",
 | 
			
		||||
@@ -365,6 +366,7 @@ export const darkTheme = createTheme("dark", {
 | 
			
		||||
    ...greenDark,
 | 
			
		||||
    ...redDark,
 | 
			
		||||
    deep: "rgb(10, 10, 10)",
 | 
			
		||||
    backgroundOverlay: "$mauve5"
 | 
			
		||||
    // backgroundA: transparentize(0.1, grayDark.gray1),
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								styles/keyframes.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								styles/keyframes.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
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)" },
 | 
			
		||||
});
 | 
			
		||||
@@ -18,13 +18,18 @@ export const tts = {
 | 
			
		||||
  ttDEPOSIT_PREAUTH: 19,
 | 
			
		||||
  ttTRUST_SET: 20,
 | 
			
		||||
  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;
 | 
			
		||||
 | 
			
		||||
const calculateHookOn = (arr: (keyof TTS)[]) => {
 | 
			
		||||
  let start = '0x00000000003ff5bf';
 | 
			
		||||
  let start = '0x000000003e3ff5bf';
 | 
			
		||||
  arr.forEach(n => {
 | 
			
		||||
    let v = BigInt(start);
 | 
			
		||||
    v ^= (BigInt(1) << BigInt(tts[n as keyof TTS]));
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										91
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										91
									
								
								yarn.lock
									
									
									
									
									
								
							@@ -594,6 +594,18 @@
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@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":
 | 
			
		||||
  version "0.1.1"
 | 
			
		||||
  resolved "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-0.1.1.tgz"
 | 
			
		||||
@@ -635,9 +647,9 @@
 | 
			
		||||
    "@radix-ui/react-use-callback-ref" "0.1.0"
 | 
			
		||||
    "@radix-ui/react-use-escape-keydown" "0.1.0"
 | 
			
		||||
 | 
			
		||||
"@radix-ui/react-dropdown-menu@^0.1.1":
 | 
			
		||||
"@radix-ui/react-dropdown-menu@^0.1.6":
 | 
			
		||||
  version "0.1.6"
 | 
			
		||||
  resolved "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-0.1.6.tgz"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-0.1.6.tgz#3203229788cd57e552c9f19dcc7008e2b545919c"
 | 
			
		||||
  integrity sha512-RZhtzjWwJ4ZBN7D8ek4Zn+ilHzYuYta9yIxFnbC0pfqMnSi67IQNONo1tuuNqtFh9SRHacPKc65zo+kBBlxtdg==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@babel/runtime" "^7.13.10"
 | 
			
		||||
@@ -1280,14 +1292,6 @@ babel-plugin-macros@^2.6.1:
 | 
			
		||||
    cosmiconfig "^6.0.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:
 | 
			
		||||
  version "1.0.2"
 | 
			
		||||
  resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
 | 
			
		||||
@@ -1552,11 +1556,6 @@ core-js-pure@^3.20.2:
 | 
			
		||||
  resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz"
 | 
			
		||||
  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:
 | 
			
		||||
  version "1.0.3"
 | 
			
		||||
  resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz"
 | 
			
		||||
@@ -2954,10 +2953,10 @@ natural-compare@^1.4.0:
 | 
			
		||||
  resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
 | 
			
		||||
  integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
 | 
			
		||||
 | 
			
		||||
next-auth@^4.0.0-beta.5:
 | 
			
		||||
  version "4.2.1"
 | 
			
		||||
  resolved "https://registry.npmjs.org/next-auth/-/next-auth-4.2.1.tgz"
 | 
			
		||||
  integrity sha512-XDtt7nqevkNf4EJ2zKAKkI+MFsURf11kx11vPwxrBYA1MHeqWwaWbGOUOI2ekNTvfAg4nTEJJUH3LV2cLrH3Tg==
 | 
			
		||||
next-auth@^4.10.1:
 | 
			
		||||
  version "4.10.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-4.10.1.tgz#33b29265d12287bb2f6d267c8d415a407c27f0e9"
 | 
			
		||||
  integrity sha512-F00vtwBdyMIIJ8IORHOAOHjVGTDEhhm9+HpB2BQ8r40WtGxqToWWLN7Z+2ZW/z2RFlo3zhcuAtUCPUzVJxtZwQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@babel/runtime" "^7.16.3"
 | 
			
		||||
    "@panva/hkdf" "^1.0.1"
 | 
			
		||||
@@ -3545,11 +3544,6 @@ reconnecting-websocket@^4.4.0:
 | 
			
		||||
  resolved "https://registry.npmjs.org/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz"
 | 
			
		||||
  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:
 | 
			
		||||
  version "0.13.9"
 | 
			
		||||
  resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz"
 | 
			
		||||
@@ -3648,30 +3642,25 @@ ripple-address-codec@^4.1.0, ripple-address-codec@^4.1.1, ripple-address-codec@^
 | 
			
		||||
    base-x "3.0.9"
 | 
			
		||||
    create-hash "^1.1.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==
 | 
			
		||||
ripple-address-codec@^4.2.4:
 | 
			
		||||
  version "4.2.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/ripple-address-codec/-/ripple-address-codec-4.2.4.tgz#a56c2168c8bb81269ea4d15ed96d6824c5a866f8"
 | 
			
		||||
  integrity sha512-roAOjKz94+FboTItey1XRh5qynwt4xvfBLvbbcx+FiR94Yw2x3LrKLF2GVCMCSAh5I6PkcpADg6AbYsUbGN3nA==
 | 
			
		||||
  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"
 | 
			
		||||
    base-x "3.0.9"
 | 
			
		||||
    create-hash "^1.1.2"
 | 
			
		||||
 | 
			
		||||
ripple-binary-codec@^1.1.3, ripple-binary-codec@^1.3.0:
 | 
			
		||||
  version "1.3.2"
 | 
			
		||||
  resolved "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-1.3.2.tgz"
 | 
			
		||||
  integrity sha512-8VG1vfb3EM1J7ZdPXo9E57Zv2hF4cxT64gP6rGSQzODVgMjiBCWozhN3729qNTGtHItz0e82Oix8v95vWYBQ3A==
 | 
			
		||||
ripple-binary-codec@=1.4.2, ripple-binary-codec@^0.2.4, ripple-binary-codec@^1.1.3, ripple-binary-codec@^1.4.2:
 | 
			
		||||
  version "1.4.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/ripple-binary-codec/-/ripple-binary-codec-1.4.2.tgz#cdc35353e4bc7c3a704719247c82b4c4d0b57dd3"
 | 
			
		||||
  integrity sha512-EDKIyZMa/6Ay/oNgCwjD9b9CJv0zmBreeHVQeG4BYwy+9GPnIQjNeT5e/aB6OjAnhcmpgbPeBmzwmNVwzxlt0w==
 | 
			
		||||
  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-address-codec "^4.2.4"
 | 
			
		||||
 | 
			
		||||
ripple-bs58@^4.0.0:
 | 
			
		||||
  version "4.0.1"
 | 
			
		||||
@@ -4328,10 +4317,10 @@ ws@^7.2.0:
 | 
			
		||||
  resolved "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz"
 | 
			
		||||
  integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==
 | 
			
		||||
 | 
			
		||||
xrpl-accountlib@^1.3.2:
 | 
			
		||||
  version "1.3.2"
 | 
			
		||||
  resolved "https://registry.npmjs.org/xrpl-accountlib/-/xrpl-accountlib-1.3.2.tgz"
 | 
			
		||||
  integrity sha512-mXwoumGp0xUiZ7Ty/1o4FHVRK4uLnqngxdYmikZs50drMjlgCUP6GXun2Vf4Uus1fnVnxhXIw+E7peH5OjiOJA==
 | 
			
		||||
xrpl-accountlib@^1.5.2:
 | 
			
		||||
  version "1.5.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/xrpl-accountlib/-/xrpl-accountlib-1.5.2.tgz#8f16abe449fd60ba9ed75597f6ce3f0c45dfff43"
 | 
			
		||||
  integrity sha512-lieY2/5G9DySqdtgQ0AD/aMMG5Sy/MLAmbIsmsCaF06scM5DpR8s4SsEzgHni7dOG68Wjnb2Uz6tf5aV+l4/Kg==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    assert "^2.0.0"
 | 
			
		||||
    bip32 "^2.0.5"
 | 
			
		||||
@@ -4340,13 +4329,13 @@ xrpl-accountlib@^1.3.2:
 | 
			
		||||
    elliptic "6.5.4"
 | 
			
		||||
    hash.js "^1.1.7"
 | 
			
		||||
    ripple-address-codec "^4.1.0"
 | 
			
		||||
    ripple-binary-codec "^1.3.0"
 | 
			
		||||
    ripple-binary-codec "^1.4.2"
 | 
			
		||||
    ripple-hashes "^0.3.4"
 | 
			
		||||
    ripple-keypairs "^1.0.3"
 | 
			
		||||
    ripple-lib "^1.6.4"
 | 
			
		||||
    ripple-secret-codec "^1.0.2"
 | 
			
		||||
    xrpl-secret-numbers "^0.3.3"
 | 
			
		||||
    xrpl-sign-keypairs "^2.0.1"
 | 
			
		||||
    xrpl-sign-keypairs "^2.1.1"
 | 
			
		||||
 | 
			
		||||
xrpl-client@^1.9.4:
 | 
			
		||||
  version "1.9.4"
 | 
			
		||||
@@ -4365,13 +4354,13 @@ xrpl-secret-numbers@^0.3.3:
 | 
			
		||||
    brorand "^1.1.0"
 | 
			
		||||
    ripple-keypairs "^1.0.3"
 | 
			
		||||
 | 
			
		||||
xrpl-sign-keypairs@^2.0.1:
 | 
			
		||||
  version "2.0.1"
 | 
			
		||||
  resolved "https://registry.npmjs.org/xrpl-sign-keypairs/-/xrpl-sign-keypairs-2.0.1.tgz"
 | 
			
		||||
  integrity sha512-84QbE3trxetaw0hqDADCWMx0HH1VAWnTJp0TGoKTGRf1jzTqjI7eNNNw5lmcay2MH8bW/waNzJIF8vSAJSkVrQ==
 | 
			
		||||
xrpl-sign-keypairs@^2.1.1:
 | 
			
		||||
  version "2.1.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/xrpl-sign-keypairs/-/xrpl-sign-keypairs-2.1.1.tgz#2f7f2855799c5d4ba091007963825eef1db21a4e"
 | 
			
		||||
  integrity sha512-rKQmUCx+x7gjjJ5zv/Z7bOYR+8I36JwUCFlpuD9UzYD4w2msGQDG0rmxVENyZSfThDBVQ1kEArVn6SMDMe9LUQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    big-integer latest
 | 
			
		||||
    ripple-binary-codec "^1.3.0"
 | 
			
		||||
    ripple-binary-codec "^1.4.2"
 | 
			
		||||
    ripple-bs58check latest
 | 
			
		||||
    ripple-hashes latest
 | 
			
		||||
    ripple-keypairs latest
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user