Compare commits
	
		
			29 Commits
		
	
	
		
			feat/tx_ha
			...
			feat/preve
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					525338abf7 | ||
| 
						 | 
					ea977816a4 | ||
| 
						 | 
					0ee599a2b6 | ||
| 
						 | 
					02c59f8d79 | ||
| 
						 | 
					3d5b77e60a | ||
| 
						 | 
					92a167d47a | ||
| 
						 | 
					d41e263942 | ||
| 
						 | 
					bd1226fe90 | ||
| 
						 | 
					57403e42dd | ||
| 
						 | 
					2b42a96c4a | ||
| 
						 | 
					80d6bb691d | ||
| 
						 | 
					c7e4cd7c92 | ||
| 
						 | 
					4a22861860 | ||
| 
						 | 
					b09d029931 | ||
| 
						 | 
					b2dc49754f | ||
| 
						 | 
					6f636645f7 | ||
| 
						 | 
					377c963c7a | ||
| 
						 | 
					ae038f17ff | ||
| 
						 | 
					0d8f2c31e7 | ||
| 
						 | 
					da9986eb66 | ||
| 
						 | 
					a21350770e | ||
| 
						 | 
					49dfd43220 | ||
| 
						 | 
					4472957f5c | ||
| 
						 | 
					ca46707bb5 | ||
| 
						 | 
					9a6ef2c393 | ||
| 
						 | 
					56203ce9c6 | ||
| 
						 | 
					933bdb5968 | ||
| 
						 | 
					864711697b | ||
| 
						 | 
					e5eaf09721 | 
@@ -469,7 +469,7 @@ const Accounts: FC<AccountProps> = (props) => {
 | 
			
		||||
                      e.stopPropagation();
 | 
			
		||||
                    }}
 | 
			
		||||
                  >
 | 
			
		||||
                    <SetHookDialog account={account} />
 | 
			
		||||
                    <SetHookDialog accountAddress={account.address} />
 | 
			
		||||
                  </div>
 | 
			
		||||
                )}
 | 
			
		||||
              </Flex>
 | 
			
		||||
 
 | 
			
		||||
@@ -21,11 +21,11 @@ import {
 | 
			
		||||
 | 
			
		||||
import { TTS, tts } from "../utils/hookOnCalculator";
 | 
			
		||||
import { deployHook } from "../state/actions";
 | 
			
		||||
import type { IAccount } from "../state";
 | 
			
		||||
import { useSnapshot } from "valtio";
 | 
			
		||||
import state from "../state";
 | 
			
		||||
import toast from "react-hot-toast";
 | 
			
		||||
import { sha256 } from "../state/actions/deployHook";
 | 
			
		||||
import { prepareDeployHookTx, sha256 } from "../state/actions/deployHook";
 | 
			
		||||
import estimateFee from "../utils/estimateFee";
 | 
			
		||||
 | 
			
		||||
const transactionOptions = Object.keys(tts).map((key) => ({
 | 
			
		||||
  label: key,
 | 
			
		||||
@@ -37,6 +37,7 @@ export type SetHookData = {
 | 
			
		||||
    value: keyof TTS;
 | 
			
		||||
    label: string;
 | 
			
		||||
  }[];
 | 
			
		||||
  Fee: string;
 | 
			
		||||
  HookNamespace: string;
 | 
			
		||||
  HookParameters: {
 | 
			
		||||
    HookParameter: {
 | 
			
		||||
@@ -52,176 +53,285 @@ export type SetHookData = {
 | 
			
		||||
  // }[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const SetHookDialog: React.FC<{ account: IAccount }> = ({ account }) => {
 | 
			
		||||
  const snap = useSnapshot(state);
 | 
			
		||||
  const [isSetHookDialogOpen, setIsSetHookDialogOpen] = useState(false);
 | 
			
		||||
  const {
 | 
			
		||||
    register,
 | 
			
		||||
    handleSubmit,
 | 
			
		||||
    control,
 | 
			
		||||
    watch,
 | 
			
		||||
    setValue,
 | 
			
		||||
    formState: { errors },
 | 
			
		||||
  } = useForm<SetHookData>({
 | 
			
		||||
    defaultValues: {
 | 
			
		||||
      HookNamespace: snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || "",
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
  const { fields, append, remove } = useFieldArray({
 | 
			
		||||
    control,
 | 
			
		||||
    name: "HookParameters", // unique name for your Field Array
 | 
			
		||||
  });
 | 
			
		||||
export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
 | 
			
		||||
  ({ accountAddress }) => {
 | 
			
		||||
    const snap = useSnapshot(state);
 | 
			
		||||
    const account = snap.accounts.find((acc) => acc.address === accountAddress);
 | 
			
		||||
 | 
			
		||||
  // Update value if activeWat changes
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setValue(
 | 
			
		||||
    const [isSetHookDialogOpen, setIsSetHookDialogOpen] = useState(false);
 | 
			
		||||
    const {
 | 
			
		||||
      register,
 | 
			
		||||
      handleSubmit,
 | 
			
		||||
      control,
 | 
			
		||||
      watch,
 | 
			
		||||
      setValue,
 | 
			
		||||
      getValues,
 | 
			
		||||
      formState: { errors },
 | 
			
		||||
    } = useForm<SetHookData>({
 | 
			
		||||
      defaultValues: {
 | 
			
		||||
        HookNamespace:
 | 
			
		||||
          snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || "",
 | 
			
		||||
        Invoke: transactionOptions.filter((to) => to.label === "ttPAYMENT"),
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
    const { fields, append, remove } = useFieldArray({
 | 
			
		||||
      control,
 | 
			
		||||
      name: "HookParameters", // unique name for your Field Array
 | 
			
		||||
    });
 | 
			
		||||
    const [formInitialized, setFormInitialized] = useState(false);
 | 
			
		||||
    const [estimateLoading, setEstimateLoading] = useState(false);
 | 
			
		||||
    const watchedFee = watch("Fee");
 | 
			
		||||
    // Update value if activeWat changes
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
      setValue(
 | 
			
		||||
        "HookNamespace",
 | 
			
		||||
        snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || ""
 | 
			
		||||
      );
 | 
			
		||||
      setFormInitialized(true);
 | 
			
		||||
    }, [snap.activeWat, snap.files, setValue]);
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
      if (
 | 
			
		||||
        watchedFee &&
 | 
			
		||||
        (watchedFee.includes(".") || watchedFee.includes(","))
 | 
			
		||||
      ) {
 | 
			
		||||
        setValue("Fee", watchedFee.replaceAll(".", "").replaceAll(",", ""));
 | 
			
		||||
      }
 | 
			
		||||
    }, [watchedFee, setValue]);
 | 
			
		||||
    // const {
 | 
			
		||||
    //   fields: grantFields,
 | 
			
		||||
    //   append: grantAppend,
 | 
			
		||||
    //   remove: grantRemove,
 | 
			
		||||
    // } = useFieldArray({
 | 
			
		||||
    //   control,
 | 
			
		||||
    //   name: "HookGrants", // unique name for your Field Array
 | 
			
		||||
    // });
 | 
			
		||||
    const [hashedNamespace, setHashedNamespace] = useState("");
 | 
			
		||||
    const namespace = watch(
 | 
			
		||||
      "HookNamespace",
 | 
			
		||||
      snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || ""
 | 
			
		||||
      snap.files?.[snap.active]?.name?.split(".")?.[0] || ""
 | 
			
		||||
    );
 | 
			
		||||
  }, [snap.activeWat, snap.files, setValue]);
 | 
			
		||||
  // const {
 | 
			
		||||
  //   fields: grantFields,
 | 
			
		||||
  //   append: grantAppend,
 | 
			
		||||
  //   remove: grantRemove,
 | 
			
		||||
  // } = useFieldArray({
 | 
			
		||||
  //   control,
 | 
			
		||||
  //   name: "HookGrants", // unique name for your Field Array
 | 
			
		||||
  // });
 | 
			
		||||
  const [hashedNamespace, setHashedNamespace] = useState("");
 | 
			
		||||
  const namespace = watch(
 | 
			
		||||
    "HookNamespace",
 | 
			
		||||
    snap.files?.[snap.active]?.name?.split(".")?.[0] || ""
 | 
			
		||||
  );
 | 
			
		||||
  const calculateHashedValue = useCallback(async () => {
 | 
			
		||||
    const hashedVal = await sha256(namespace);
 | 
			
		||||
    setHashedNamespace(hashedVal.toUpperCase());
 | 
			
		||||
  }, [namespace]);
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    calculateHashedValue();
 | 
			
		||||
  }, [namespace, calculateHashedValue]);
 | 
			
		||||
    const calculateHashedValue = useCallback(async () => {
 | 
			
		||||
      const hashedVal = await sha256(namespace);
 | 
			
		||||
      setHashedNamespace(hashedVal.toUpperCase());
 | 
			
		||||
    }, [namespace]);
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
      calculateHashedValue();
 | 
			
		||||
    }, [namespace, calculateHashedValue]);
 | 
			
		||||
 | 
			
		||||
  if (!account) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const onSubmit: SubmitHandler<SetHookData> = async (data) => {
 | 
			
		||||
    const currAccount = state.accounts.find(
 | 
			
		||||
      (acc) => acc.address === account.address
 | 
			
		||||
    );
 | 
			
		||||
    if (currAccount) currAccount.isLoading = true;
 | 
			
		||||
    const res = await deployHook(account, data);
 | 
			
		||||
    if (currAccount) currAccount.isLoading = false;
 | 
			
		||||
 | 
			
		||||
    if (res && res.engine_result === "tesSUCCESS") {
 | 
			
		||||
      toast.success("Transaction succeeded!");
 | 
			
		||||
      return setIsSetHookDialogOpen(false);
 | 
			
		||||
    }
 | 
			
		||||
    toast.error(`Transaction failed! (${res?.engine_result_message})`);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Dialog open={isSetHookDialogOpen} onOpenChange={setIsSetHookDialogOpen}>
 | 
			
		||||
      <DialogTrigger asChild>
 | 
			
		||||
        <Button
 | 
			
		||||
          ghost
 | 
			
		||||
          size="xs"
 | 
			
		||||
          uppercase
 | 
			
		||||
          variant={"secondary"}
 | 
			
		||||
          disabled={
 | 
			
		||||
            account.isLoading ||
 | 
			
		||||
            !snap.files.filter((file) => file.compiledWatContent).length
 | 
			
		||||
    // Calcucate initial fee estimate when modal opens
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
      if (formInitialized && account) {
 | 
			
		||||
        (async () => {
 | 
			
		||||
          const formValues = getValues();
 | 
			
		||||
          const tx = await prepareDeployHookTx(account, formValues);
 | 
			
		||||
          if (!tx) {
 | 
			
		||||
            return;
 | 
			
		||||
          }
 | 
			
		||||
        >
 | 
			
		||||
          Set Hook
 | 
			
		||||
        </Button>
 | 
			
		||||
      </DialogTrigger>
 | 
			
		||||
      <DialogContent>
 | 
			
		||||
        <form onSubmit={handleSubmit(onSubmit)}>
 | 
			
		||||
          <DialogTitle>Deploy configuration</DialogTitle>
 | 
			
		||||
          <DialogDescription as="div">
 | 
			
		||||
            <Stack css={{ width: "100%", flex: 1 }}>
 | 
			
		||||
              <Box css={{ width: "100%" }}>
 | 
			
		||||
                <Label>Invoke on transactions</Label>
 | 
			
		||||
                <Controller
 | 
			
		||||
                  name="Invoke"
 | 
			
		||||
                  control={control}
 | 
			
		||||
                  defaultValue={transactionOptions.filter(
 | 
			
		||||
                    (to) => to.label === "ttPAYMENT"
 | 
			
		||||
                  )}
 | 
			
		||||
                  render={({ field }) => (
 | 
			
		||||
                    <Select
 | 
			
		||||
                      {...field}
 | 
			
		||||
                      closeMenuOnSelect={false}
 | 
			
		||||
                      isMulti
 | 
			
		||||
                      menuPosition="fixed"
 | 
			
		||||
                      options={transactionOptions}
 | 
			
		||||
                    />
 | 
			
		||||
                  )}
 | 
			
		||||
                />
 | 
			
		||||
              </Box>
 | 
			
		||||
              <Box css={{ width: "100%" }}>
 | 
			
		||||
                <Label>Hook Namespace Seed</Label>
 | 
			
		||||
                <Input
 | 
			
		||||
                  {...register("HookNamespace", { required: true })}
 | 
			
		||||
                  autoComplete={"off"}
 | 
			
		||||
                  defaultValue={
 | 
			
		||||
                    snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || ""
 | 
			
		||||
                  }
 | 
			
		||||
                />
 | 
			
		||||
                {errors.HookNamespace?.type === "required" && (
 | 
			
		||||
                  <Box css={{ display: "inline", color: "$red11" }}>
 | 
			
		||||
                    Namespace is required
 | 
			
		||||
                  </Box>
 | 
			
		||||
                )}
 | 
			
		||||
                <Box css={{ mt: "$3" }}>
 | 
			
		||||
                  <Label>Hook Namespace (sha256)</Label>
 | 
			
		||||
                  <Input readOnly value={hashedNamespace} />
 | 
			
		||||
          const res = await estimateFee(tx, account);
 | 
			
		||||
          if (res && res.base_fee) {
 | 
			
		||||
            setValue("Fee", Math.round(Number(res.base_fee || "")).toString());
 | 
			
		||||
          }
 | 
			
		||||
        })();
 | 
			
		||||
      }
 | 
			
		||||
      // eslint-disable-next-line react-hooks/exhaustive-deps
 | 
			
		||||
    }, [formInitialized]);
 | 
			
		||||
 | 
			
		||||
    if (!account) {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const onSubmit: SubmitHandler<SetHookData> = async (data) => {
 | 
			
		||||
      const currAccount = state.accounts.find(
 | 
			
		||||
        (acc) => acc.address === account.address
 | 
			
		||||
      );
 | 
			
		||||
      if (currAccount) currAccount.isLoading = true;
 | 
			
		||||
      const res = await deployHook(account, data);
 | 
			
		||||
      if (currAccount) currAccount.isLoading = false;
 | 
			
		||||
 | 
			
		||||
      if (res && res.engine_result === "tesSUCCESS") {
 | 
			
		||||
        toast.success("Transaction succeeded!");
 | 
			
		||||
        return setIsSetHookDialogOpen(false);
 | 
			
		||||
      }
 | 
			
		||||
      toast.error(`Transaction failed! (${res?.engine_result_message})`);
 | 
			
		||||
    };
 | 
			
		||||
    return (
 | 
			
		||||
      <Dialog open={isSetHookDialogOpen} onOpenChange={setIsSetHookDialogOpen}>
 | 
			
		||||
        <DialogTrigger asChild>
 | 
			
		||||
          <Button
 | 
			
		||||
            ghost
 | 
			
		||||
            size="xs"
 | 
			
		||||
            uppercase
 | 
			
		||||
            variant={"secondary"}
 | 
			
		||||
            disabled={
 | 
			
		||||
              account.isLoading ||
 | 
			
		||||
              !snap.files.filter((file) => file.compiledWatContent).length
 | 
			
		||||
            }
 | 
			
		||||
          >
 | 
			
		||||
            Set Hook
 | 
			
		||||
          </Button>
 | 
			
		||||
        </DialogTrigger>
 | 
			
		||||
        <DialogContent>
 | 
			
		||||
          <form onSubmit={handleSubmit(onSubmit)}>
 | 
			
		||||
            <DialogTitle>Deploy configuration</DialogTitle>
 | 
			
		||||
            <DialogDescription as="div">
 | 
			
		||||
              <Stack css={{ width: "100%", flex: 1 }}>
 | 
			
		||||
                <Box css={{ width: "100%" }}>
 | 
			
		||||
                  <Label>Invoke on transactions</Label>
 | 
			
		||||
                  <Controller
 | 
			
		||||
                    name="Invoke"
 | 
			
		||||
                    control={control}
 | 
			
		||||
                    defaultValue={transactionOptions.filter(
 | 
			
		||||
                      (to) => to.label === "ttPAYMENT"
 | 
			
		||||
                    )}
 | 
			
		||||
                    render={({ field }) => (
 | 
			
		||||
                      <Select
 | 
			
		||||
                        {...field}
 | 
			
		||||
                        closeMenuOnSelect={false}
 | 
			
		||||
                        isMulti
 | 
			
		||||
                        menuPosition="fixed"
 | 
			
		||||
                        options={transactionOptions}
 | 
			
		||||
                      />
 | 
			
		||||
                    )}
 | 
			
		||||
                  />
 | 
			
		||||
                </Box>
 | 
			
		||||
              </Box>
 | 
			
		||||
              <Box css={{ width: "100%" }}>
 | 
			
		||||
                <Label style={{ marginBottom: "10px", display: "block" }}>
 | 
			
		||||
                  Hook parameters
 | 
			
		||||
                </Label>
 | 
			
		||||
                <Stack>
 | 
			
		||||
                  {fields.map((field, index) => (
 | 
			
		||||
                    <Stack key={field.id}>
 | 
			
		||||
                      <Input
 | 
			
		||||
                        // important to include key with field's id
 | 
			
		||||
                        placeholder="Parameter name"
 | 
			
		||||
                        {...register(
 | 
			
		||||
                          `HookParameters.${index}.HookParameter.HookParameterName`
 | 
			
		||||
                        )}
 | 
			
		||||
                      />
 | 
			
		||||
                      <Input
 | 
			
		||||
                        placeholder="Value (hex-quoted)"
 | 
			
		||||
                        {...register(
 | 
			
		||||
                          `HookParameters.${index}.HookParameter.HookParameterValue`
 | 
			
		||||
                        )}
 | 
			
		||||
                      />
 | 
			
		||||
                      <Button onClick={() => remove(index)} variant="destroy">
 | 
			
		||||
                        <Trash weight="regular" size="16px" />
 | 
			
		||||
                      </Button>
 | 
			
		||||
                    </Stack>
 | 
			
		||||
                  ))}
 | 
			
		||||
                  <Button
 | 
			
		||||
                    outline
 | 
			
		||||
                    fullWidth
 | 
			
		||||
                    type="button"
 | 
			
		||||
                    onClick={() =>
 | 
			
		||||
                      append({
 | 
			
		||||
                        HookParameter: {
 | 
			
		||||
                          HookParameterName: "",
 | 
			
		||||
                          HookParameterValue: "",
 | 
			
		||||
                        },
 | 
			
		||||
                      })
 | 
			
		||||
                <Box css={{ width: "100%" }}>
 | 
			
		||||
                  <Label>Hook Namespace Seed</Label>
 | 
			
		||||
                  <Input
 | 
			
		||||
                    {...register("HookNamespace", { required: true })}
 | 
			
		||||
                    autoComplete={"off"}
 | 
			
		||||
                    defaultValue={
 | 
			
		||||
                      snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || ""
 | 
			
		||||
                    }
 | 
			
		||||
                  >
 | 
			
		||||
                    <Plus size="16px" />
 | 
			
		||||
                    Add Hook Parameter
 | 
			
		||||
                  </Button>
 | 
			
		||||
                </Stack>
 | 
			
		||||
              </Box>
 | 
			
		||||
              {/* <Box css={{ width: "100%" }}>
 | 
			
		||||
                  />
 | 
			
		||||
                  {errors.HookNamespace?.type === "required" && (
 | 
			
		||||
                    <Box css={{ display: "inline", color: "$red11" }}>
 | 
			
		||||
                      Namespace is required
 | 
			
		||||
                    </Box>
 | 
			
		||||
                  )}
 | 
			
		||||
                  <Box css={{ mt: "$3" }}>
 | 
			
		||||
                    <Label>Hook Namespace (sha256)</Label>
 | 
			
		||||
                    <Input readOnly value={hashedNamespace} />
 | 
			
		||||
                  </Box>
 | 
			
		||||
                </Box>
 | 
			
		||||
 | 
			
		||||
                <Box css={{ width: "100%" }}>
 | 
			
		||||
                  <Label style={{ marginBottom: "10px", display: "block" }}>
 | 
			
		||||
                    Hook parameters
 | 
			
		||||
                  </Label>
 | 
			
		||||
                  <Stack>
 | 
			
		||||
                    {fields.map((field, index) => (
 | 
			
		||||
                      <Stack key={field.id}>
 | 
			
		||||
                        <Input
 | 
			
		||||
                          // important to include key with field's id
 | 
			
		||||
                          placeholder="Parameter name"
 | 
			
		||||
                          {...register(
 | 
			
		||||
                            `HookParameters.${index}.HookParameter.HookParameterName`
 | 
			
		||||
                          )}
 | 
			
		||||
                        />
 | 
			
		||||
                        <Input
 | 
			
		||||
                          placeholder="Value (hex-quoted)"
 | 
			
		||||
                          {...register(
 | 
			
		||||
                            `HookParameters.${index}.HookParameter.HookParameterValue`
 | 
			
		||||
                          )}
 | 
			
		||||
                        />
 | 
			
		||||
                        <Button onClick={() => remove(index)} variant="destroy">
 | 
			
		||||
                          <Trash weight="regular" size="16px" />
 | 
			
		||||
                        </Button>
 | 
			
		||||
                      </Stack>
 | 
			
		||||
                    ))}
 | 
			
		||||
                    <Button
 | 
			
		||||
                      outline
 | 
			
		||||
                      fullWidth
 | 
			
		||||
                      type="button"
 | 
			
		||||
                      onClick={() =>
 | 
			
		||||
                        append({
 | 
			
		||||
                          HookParameter: {
 | 
			
		||||
                            HookParameterName: "",
 | 
			
		||||
                            HookParameterValue: "",
 | 
			
		||||
                          },
 | 
			
		||||
                        })
 | 
			
		||||
                      }
 | 
			
		||||
                    >
 | 
			
		||||
                      <Plus size="16px" />
 | 
			
		||||
                      Add Hook Parameter
 | 
			
		||||
                    </Button>
 | 
			
		||||
                  </Stack>
 | 
			
		||||
                </Box>
 | 
			
		||||
                <Box css={{ width: "100%", position: "relative" }}>
 | 
			
		||||
                  <Label>Fee</Label>
 | 
			
		||||
                  <Box css={{ display: "flex", alignItems: "center" }}>
 | 
			
		||||
                    <Input
 | 
			
		||||
                      type="number"
 | 
			
		||||
                      {...register("Fee", { required: true })}
 | 
			
		||||
                      autoComplete={"off"}
 | 
			
		||||
                      onKeyPress={(e) => {
 | 
			
		||||
                        if (e.key === "." || e.key === ",") {
 | 
			
		||||
                          e.preventDefault();
 | 
			
		||||
                        }
 | 
			
		||||
                      }}
 | 
			
		||||
                      step="1"
 | 
			
		||||
                      defaultValue={10000}
 | 
			
		||||
                      css={{
 | 
			
		||||
                        "-moz-appearance": "textfield",
 | 
			
		||||
                        "&::-webkit-outer-spin-button": {
 | 
			
		||||
                          "-webkit-appearance": "none",
 | 
			
		||||
                          margin: 0,
 | 
			
		||||
                        },
 | 
			
		||||
                        "&::-webkit-inner-spin-button ": {
 | 
			
		||||
                          "-webkit-appearance": "none",
 | 
			
		||||
                          margin: 0,
 | 
			
		||||
                        },
 | 
			
		||||
                      }}
 | 
			
		||||
                    />
 | 
			
		||||
                    <Button
 | 
			
		||||
                      size="xs"
 | 
			
		||||
                      variant="primary"
 | 
			
		||||
                      outline
 | 
			
		||||
                      isLoading={estimateLoading}
 | 
			
		||||
                      css={{
 | 
			
		||||
                        position: "absolute",
 | 
			
		||||
                        right: "$2",
 | 
			
		||||
                        fontSize: "$xs",
 | 
			
		||||
                        cursor: "pointer",
 | 
			
		||||
                        alignContent: "center",
 | 
			
		||||
                        display: "flex",
 | 
			
		||||
                      }}
 | 
			
		||||
                      onClick={async (e) => {
 | 
			
		||||
                        e.preventDefault();
 | 
			
		||||
                        setEstimateLoading(true);
 | 
			
		||||
                        const formValues = getValues();
 | 
			
		||||
                        try {
 | 
			
		||||
                          const tx = await prepareDeployHookTx(
 | 
			
		||||
                            account,
 | 
			
		||||
                            formValues
 | 
			
		||||
                          );
 | 
			
		||||
                          if (tx) {
 | 
			
		||||
                            const res = await estimateFee(tx, account);
 | 
			
		||||
 | 
			
		||||
                            if (res && res.base_fee) {
 | 
			
		||||
                              setValue(
 | 
			
		||||
                                "Fee",
 | 
			
		||||
                                Math.round(
 | 
			
		||||
                                  Number(res.base_fee || "")
 | 
			
		||||
                                ).toString()
 | 
			
		||||
                              );
 | 
			
		||||
                            }
 | 
			
		||||
                          }
 | 
			
		||||
                        } catch (err) {}
 | 
			
		||||
 | 
			
		||||
                        setEstimateLoading(false);
 | 
			
		||||
                      }}
 | 
			
		||||
                    >
 | 
			
		||||
                      Suggest
 | 
			
		||||
                    </Button>
 | 
			
		||||
                  </Box>
 | 
			
		||||
                  {errors.Fee?.type === "required" && (
 | 
			
		||||
                    <Box css={{ display: "inline", color: "$red11" }}>
 | 
			
		||||
                      Fee is required
 | 
			
		||||
                    </Box>
 | 
			
		||||
                  )}
 | 
			
		||||
                </Box>
 | 
			
		||||
                {/* <Box css={{ width: "100%" }}>
 | 
			
		||||
                <label style={{ marginBottom: "10px", display: "block" }}>
 | 
			
		||||
                  Hook Grants
 | 
			
		||||
                </label>
 | 
			
		||||
@@ -269,38 +379,41 @@ export const SetHookDialog: React.FC<{ account: IAccount }> = ({ account }) => {
 | 
			
		||||
                  </Button>
 | 
			
		||||
                </Stack>
 | 
			
		||||
              </Box> */}
 | 
			
		||||
            </Stack>
 | 
			
		||||
          </DialogDescription>
 | 
			
		||||
              </Stack>
 | 
			
		||||
            </DialogDescription>
 | 
			
		||||
 | 
			
		||||
          <Flex
 | 
			
		||||
            css={{
 | 
			
		||||
              marginTop: 25,
 | 
			
		||||
              justifyContent: "flex-end",
 | 
			
		||||
              gap: "$3",
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            <DialogClose asChild>
 | 
			
		||||
              <Button outline>Cancel</Button>
 | 
			
		||||
            </DialogClose>
 | 
			
		||||
            {/* <DialogClose asChild> */}
 | 
			
		||||
            <Button
 | 
			
		||||
              variant="primary"
 | 
			
		||||
              type="submit"
 | 
			
		||||
              isLoading={account.isLoading}
 | 
			
		||||
            <Flex
 | 
			
		||||
              css={{
 | 
			
		||||
                marginTop: 25,
 | 
			
		||||
                justifyContent: "flex-end",
 | 
			
		||||
                gap: "$3",
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              Set Hook
 | 
			
		||||
            </Button>
 | 
			
		||||
            {/* </DialogClose> */}
 | 
			
		||||
          </Flex>
 | 
			
		||||
          <DialogClose asChild>
 | 
			
		||||
            <Box css={{ position: "absolute", top: "$3", right: "$3" }}>
 | 
			
		||||
              <X size="20px" />
 | 
			
		||||
            </Box>
 | 
			
		||||
          </DialogClose>
 | 
			
		||||
        </form>
 | 
			
		||||
      </DialogContent>
 | 
			
		||||
    </Dialog>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
              <DialogClose asChild>
 | 
			
		||||
                <Button outline>Cancel</Button>
 | 
			
		||||
              </DialogClose>
 | 
			
		||||
              {/* <DialogClose asChild> */}
 | 
			
		||||
              <Button
 | 
			
		||||
                variant="primary"
 | 
			
		||||
                type="submit"
 | 
			
		||||
                isLoading={account.isLoading}
 | 
			
		||||
              >
 | 
			
		||||
                Set Hook
 | 
			
		||||
              </Button>
 | 
			
		||||
              {/* </DialogClose> */}
 | 
			
		||||
            </Flex>
 | 
			
		||||
            <DialogClose asChild>
 | 
			
		||||
              <Box css={{ position: "absolute", top: "$3", right: "$3" }}>
 | 
			
		||||
                <X size="20px" />
 | 
			
		||||
              </Box>
 | 
			
		||||
            </DialogClose>
 | 
			
		||||
          </form>
 | 
			
		||||
        </DialogContent>
 | 
			
		||||
      </Dialog>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
SetHookDialog.displayName = "SetHookDialog";
 | 
			
		||||
 | 
			
		||||
export default SetHookDialog;
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,8 @@ import Button from "../Button";
 | 
			
		||||
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';
 | 
			
		||||
 | 
			
		||||
export interface TransactionProps {
 | 
			
		||||
  header: string;
 | 
			
		||||
@@ -76,13 +78,19 @@ const Transaction: FC<TransactionProps> = ({
 | 
			
		||||
    } else {
 | 
			
		||||
      setState({ txIsDisabled: false });
 | 
			
		||||
    }
 | 
			
		||||
  }, [selectedAccount?.value, selectedTransaction?.value, setState, txIsLoading]);
 | 
			
		||||
  }, [
 | 
			
		||||
    selectedAccount?.value,
 | 
			
		||||
    selectedTransaction?.value,
 | 
			
		||||
    setState,
 | 
			
		||||
    txIsLoading,
 | 
			
		||||
  ]);
 | 
			
		||||
 | 
			
		||||
  const submitTest = useCallback(async () => {
 | 
			
		||||
    let st: TransactionState | undefined;
 | 
			
		||||
    const tt = txState.selectedTransaction?.value;
 | 
			
		||||
    if (viewType === "json") {
 | 
			
		||||
      // save the editor state first
 | 
			
		||||
      const pst = prepareState(editorValue || '', txState);
 | 
			
		||||
      const pst = prepareState(editorValue || "", tt);
 | 
			
		||||
      if (!pst) return;
 | 
			
		||||
 | 
			
		||||
      st = setState(pst);
 | 
			
		||||
@@ -102,7 +110,7 @@ const Transaction: FC<TransactionProps> = ({
 | 
			
		||||
      const options = prepareOptions(st);
 | 
			
		||||
 | 
			
		||||
      if (options.Destination === null) {
 | 
			
		||||
        throw Error("Destination account cannot be null")
 | 
			
		||||
        throw Error("Destination account cannot be null");
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      await sendTransaction(account, options, { logPrefix });
 | 
			
		||||
@@ -116,7 +124,17 @@ const Transaction: FC<TransactionProps> = ({
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    setState({ txIsLoading: false });
 | 
			
		||||
  }, [viewType, accounts, txIsDisabled, setState, header, editorValue, txState, selectedAccount?.value, prepareOptions]);
 | 
			
		||||
  }, [
 | 
			
		||||
    viewType,
 | 
			
		||||
    accounts,
 | 
			
		||||
    txIsDisabled,
 | 
			
		||||
    setState,
 | 
			
		||||
    header,
 | 
			
		||||
    editorValue,
 | 
			
		||||
    txState,
 | 
			
		||||
    selectedAccount?.value,
 | 
			
		||||
    prepareOptions,
 | 
			
		||||
  ]);
 | 
			
		||||
 | 
			
		||||
  const resetState = useCallback(() => {
 | 
			
		||||
    modifyTransaction(header, { viewType }, { replaceState: true });
 | 
			
		||||
@@ -129,6 +147,31 @@ const Transaction: FC<TransactionProps> = ({
 | 
			
		||||
    [editorSavedValue, editorSettings.tabSize, prepareOptions]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const estimateFee = useCallback(
 | 
			
		||||
    async (st?: TransactionState, opts?: { silent?: boolean }) => {
 | 
			
		||||
      const state = st || txState;
 | 
			
		||||
      const ptx = prepareOptions(state);
 | 
			
		||||
      const account = accounts.find(
 | 
			
		||||
        acc => acc.address === state.selectedAccount?.value
 | 
			
		||||
      );
 | 
			
		||||
      if (!account) {
 | 
			
		||||
        if (!opts?.silent) {
 | 
			
		||||
          toast.error("Please select account from the list.")
 | 
			
		||||
        }
 | 
			
		||||
        return
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      ptx.Account = account.address;
 | 
			
		||||
      ptx.Sequence = account.sequence;
 | 
			
		||||
 | 
			
		||||
      const res = await _estimateFee(ptx, account, opts);
 | 
			
		||||
      const fee = res?.base_fee;
 | 
			
		||||
      setState({ estimatedFee: fee });
 | 
			
		||||
      return fee;
 | 
			
		||||
    },
 | 
			
		||||
    [accounts, prepareOptions, setState, txState]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Box css={{ position: "relative", height: "calc(100% - 28px)" }} {...props}>
 | 
			
		||||
      {viewType === "json" ? (
 | 
			
		||||
@@ -137,9 +180,10 @@ const Transaction: FC<TransactionProps> = ({
 | 
			
		||||
          header={header}
 | 
			
		||||
          state={txState}
 | 
			
		||||
          setState={setState}
 | 
			
		||||
          estimateFee={estimateFee}
 | 
			
		||||
        />
 | 
			
		||||
      ) : (
 | 
			
		||||
        <TxUI state={txState} setState={setState} />
 | 
			
		||||
        <TxUI state={txState} setState={setState} estimateFee={estimateFee} />
 | 
			
		||||
      )}
 | 
			
		||||
      <Flex
 | 
			
		||||
        row
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@ interface JsonProps {
 | 
			
		||||
  header?: string;
 | 
			
		||||
  setState: (pTx?: Partial<TransactionState> | undefined) => void;
 | 
			
		||||
  state: TransactionState;
 | 
			
		||||
  estimateFee?: () => Promise<string | undefined>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const TxJson: FC<JsonProps> = ({
 | 
			
		||||
@@ -38,22 +39,37 @@ export const TxJson: FC<JsonProps> = ({
 | 
			
		||||
  setState,
 | 
			
		||||
}) => {
 | 
			
		||||
  const { editorSettings, accounts } = useSnapshot(state);
 | 
			
		||||
  const { editorValue = value, selectedTransaction } = txState;
 | 
			
		||||
  const { editorValue = value, estimatedFee } = txState;
 | 
			
		||||
  const { theme } = useTheme();
 | 
			
		||||
  const [hasUnsaved, setHasUnsaved] = useState(false);
 | 
			
		||||
  const [currTxType, setCurrTxType] = useState<string | undefined>(
 | 
			
		||||
    txState.selectedTransaction?.value
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setState({ editorValue: value });
 | 
			
		||||
    // eslint-disable-next-line react-hooks/exhaustive-deps
 | 
			
		||||
  }, [value]);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const parsed = parseJSON(editorValue);
 | 
			
		||||
    if (!parsed) return;
 | 
			
		||||
 | 
			
		||||
    const tt = parsed.TransactionType;
 | 
			
		||||
    const tx = transactionsData.find(t => t.TransactionType === tt);
 | 
			
		||||
    if (tx) setCurrTxType(tx.TransactionType);
 | 
			
		||||
    else {
 | 
			
		||||
      setCurrTxType(undefined);
 | 
			
		||||
    }
 | 
			
		||||
  }, [editorValue]);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (editorValue === value) setHasUnsaved(false);
 | 
			
		||||
    else setHasUnsaved(true);
 | 
			
		||||
  }, [editorValue, value]);
 | 
			
		||||
 | 
			
		||||
  const saveState = (value: string, txState: TransactionState) => {
 | 
			
		||||
    const tx = prepareState(value, txState);
 | 
			
		||||
  const saveState = (value: string, transactionType?: string) => {
 | 
			
		||||
    const tx = prepareState(value, transactionType);
 | 
			
		||||
    if (tx) setState(tx);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
@@ -68,7 +84,7 @@ export const TxJson: FC<JsonProps> = ({
 | 
			
		||||
  const onExit = (value: string) => {
 | 
			
		||||
    const options = parseJSON(value);
 | 
			
		||||
    if (options) {
 | 
			
		||||
      saveState(value, txState);
 | 
			
		||||
      saveState(value, currTxType);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    showAlert("Error!", {
 | 
			
		||||
@@ -82,9 +98,10 @@ export const TxJson: FC<JsonProps> = ({
 | 
			
		||||
  const path = `file:///${header}`;
 | 
			
		||||
  const monaco = useMonaco();
 | 
			
		||||
 | 
			
		||||
  const getSchemas = useCallback((): any[] => {
 | 
			
		||||
    const tt = selectedTransaction?.value;
 | 
			
		||||
    const txObj = transactionsData.find(td => td.TransactionType === tt);
 | 
			
		||||
  const getSchemas = useCallback(async (): Promise<any[]> => {
 | 
			
		||||
    const txObj = transactionsData.find(
 | 
			
		||||
      td => td.TransactionType === currTxType
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let genericSchemaProps: any;
 | 
			
		||||
    if (txObj) {
 | 
			
		||||
@@ -98,7 +115,6 @@ export const TxJson: FC<JsonProps> = ({
 | 
			
		||||
        {}
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return [
 | 
			
		||||
      {
 | 
			
		||||
        uri: "file:///main-schema.json", // id of the first schema
 | 
			
		||||
@@ -130,6 +146,9 @@ export const TxJson: FC<JsonProps> = ({
 | 
			
		||||
            Amount: {
 | 
			
		||||
              $ref: "file:///amount-schema.json",
 | 
			
		||||
            },
 | 
			
		||||
            Fee: {
 | 
			
		||||
              $ref: "file:///fee-schema.json",
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
@@ -141,17 +160,30 @@ export const TxJson: FC<JsonProps> = ({
 | 
			
		||||
          enum: accounts.map(acc => acc.address),
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        uri: "file:///fee-schema.json",
 | 
			
		||||
        schema: {
 | 
			
		||||
          type: "string",
 | 
			
		||||
          title: "Fee type",
 | 
			
		||||
          const: estimatedFee,
 | 
			
		||||
          description: estimatedFee
 | 
			
		||||
            ? "Above mentioned value is recommended base fee"
 | 
			
		||||
            : undefined,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        ...amountSchema,
 | 
			
		||||
      },
 | 
			
		||||
    ];
 | 
			
		||||
  }, [accounts, header, selectedTransaction?.value]);
 | 
			
		||||
  }, [accounts, currTxType, estimatedFee, header]);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (!monaco) return;
 | 
			
		||||
    monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
 | 
			
		||||
      validate: true,
 | 
			
		||||
      schemas: getSchemas(),
 | 
			
		||||
    getSchemas().then(schemas => {
 | 
			
		||||
      monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
 | 
			
		||||
        validate: true,
 | 
			
		||||
        schemas,
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  }, [getSchemas, monaco]);
 | 
			
		||||
 | 
			
		||||
@@ -184,19 +216,13 @@ export const TxJson: FC<JsonProps> = ({
 | 
			
		||||
          // register onExit cb
 | 
			
		||||
          const model = editor.getModel();
 | 
			
		||||
          model?.onWillDispose(() => onExit(model.getValue()));
 | 
			
		||||
 | 
			
		||||
          // set json defaults
 | 
			
		||||
          monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
 | 
			
		||||
            validate: true,
 | 
			
		||||
            schemas: getSchemas(),
 | 
			
		||||
          });
 | 
			
		||||
        }}
 | 
			
		||||
        theme={theme === "dark" ? "dark" : "light"}
 | 
			
		||||
      />
 | 
			
		||||
      {hasUnsaved && (
 | 
			
		||||
        <Text muted small css={{ position: "absolute", bottom: 0, right: 0 }}>
 | 
			
		||||
          This file has unsaved changes.{" "}
 | 
			
		||||
          <Link onClick={() => saveState(editorValue, txState)}>save</Link>{" "}
 | 
			
		||||
          <Link onClick={() => saveState(editorValue, currTxType)}>save</Link>{" "}
 | 
			
		||||
          <Link onClick={discardChanges}>discard</Link>
 | 
			
		||||
        </Text>
 | 
			
		||||
      )}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { FC } from "react";
 | 
			
		||||
import { FC, useCallback, useState } from "react";
 | 
			
		||||
import Container from "../Container";
 | 
			
		||||
import Flex from "../Flex";
 | 
			
		||||
import Input from "../Input";
 | 
			
		||||
@@ -9,17 +9,26 @@ import {
 | 
			
		||||
  TransactionState,
 | 
			
		||||
  transactionsData,
 | 
			
		||||
  TxFields,
 | 
			
		||||
  getTxFields,
 | 
			
		||||
} from "../../state/transactions";
 | 
			
		||||
import { useSnapshot } from "valtio";
 | 
			
		||||
import state from "../../state";
 | 
			
		||||
import { streamState } from "../DebugStream";
 | 
			
		||||
import { Button } from "..";
 | 
			
		||||
 | 
			
		||||
interface UIProps {
 | 
			
		||||
  setState: (pTx?: Partial<TransactionState> | undefined) => void;
 | 
			
		||||
  setState: (
 | 
			
		||||
    pTx?: Partial<TransactionState> | undefined
 | 
			
		||||
  ) => TransactionState | undefined;
 | 
			
		||||
  state: TransactionState;
 | 
			
		||||
  estimateFee?: (...arg: any) => Promise<string | undefined>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const TxUI: FC<UIProps> = ({ state: txState, setState }) => {
 | 
			
		||||
export const TxUI: FC<UIProps> = ({
 | 
			
		||||
  state: txState,
 | 
			
		||||
  setState,
 | 
			
		||||
  estimateFee,
 | 
			
		||||
}) => {
 | 
			
		||||
  const { accounts } = useSnapshot(state);
 | 
			
		||||
  const {
 | 
			
		||||
    selectedAccount,
 | 
			
		||||
@@ -28,55 +37,77 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState }) => {
 | 
			
		||||
    txFields,
 | 
			
		||||
  } = txState;
 | 
			
		||||
 | 
			
		||||
  const transactionsOptions = transactionsData.map(tx => ({
 | 
			
		||||
  const transactionsOptions = transactionsData.map((tx) => ({
 | 
			
		||||
    value: tx.TransactionType,
 | 
			
		||||
    label: tx.TransactionType,
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  const accountOptions: SelectOption[] = accounts.map(acc => ({
 | 
			
		||||
  const accountOptions: SelectOption[] = accounts.map((acc) => ({
 | 
			
		||||
    label: acc.name,
 | 
			
		||||
    value: acc.address,
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  const destAccountOptions: SelectOption[] = accounts
 | 
			
		||||
    .map(acc => ({
 | 
			
		||||
    .map((acc) => ({
 | 
			
		||||
      label: acc.name,
 | 
			
		||||
      value: acc.address,
 | 
			
		||||
    }))
 | 
			
		||||
    .filter(acc => acc.value !== selectedAccount?.value);
 | 
			
		||||
    .filter((acc) => acc.value !== selectedAccount?.value);
 | 
			
		||||
 | 
			
		||||
  const resetOptions = (tt: string) => {
 | 
			
		||||
    const txFields: TxFields | undefined = transactionsData.find(
 | 
			
		||||
      tx => tx.TransactionType === tt
 | 
			
		||||
    );
 | 
			
		||||
  const [feeLoading, setFeeLoading] = useState(false);
 | 
			
		||||
 | 
			
		||||
    if (!txFields) return setState({ txFields: {} });
 | 
			
		||||
 | 
			
		||||
    const _txFields = Object.keys(txFields)
 | 
			
		||||
      .filter(key => !["TransactionType", "Account", "Sequence"].includes(key))
 | 
			
		||||
      .reduce<TxFields>(
 | 
			
		||||
        (tf, key) => ((tf[key as keyof TxFields] = (txFields as any)[key]), tf),
 | 
			
		||||
        {}
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
    if (!_txFields.Destination) setState({ selectedDestAccount: null });
 | 
			
		||||
    setState({ txFields: _txFields });
 | 
			
		||||
  };
 | 
			
		||||
  const resetOptions = useCallback(
 | 
			
		||||
    (tt: string) => {
 | 
			
		||||
      const fields = getTxFields(tt);
 | 
			
		||||
      if (!fields.Destination) setState({ selectedDestAccount: null });
 | 
			
		||||
      return setState({ txFields: fields });
 | 
			
		||||
    },
 | 
			
		||||
    [setState]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const handleSetAccount = (acc: SelectOption) => {
 | 
			
		||||
    setState({ selectedAccount: acc });
 | 
			
		||||
    streamState.selectedAccount = acc;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleSetField = useCallback(
 | 
			
		||||
    (field: keyof TxFields, value: string, opFields?: TxFields) => {
 | 
			
		||||
      const fields = opFields || txFields;
 | 
			
		||||
      const obj = fields[field];
 | 
			
		||||
      setState({
 | 
			
		||||
        txFields: {
 | 
			
		||||
          ...fields,
 | 
			
		||||
          [field]: typeof obj === "object" ? { ...obj, $value: value } : value,
 | 
			
		||||
        },
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    [setState, txFields]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const handleEstimateFee = useCallback(
 | 
			
		||||
    async (state?: TransactionState, silent?: boolean) => {
 | 
			
		||||
      setFeeLoading(true);
 | 
			
		||||
 | 
			
		||||
      const fee = await estimateFee?.(state, { silent });
 | 
			
		||||
      if (fee) handleSetField("Fee", fee, state?.txFields);
 | 
			
		||||
 | 
			
		||||
      setFeeLoading(false);
 | 
			
		||||
    },
 | 
			
		||||
    [estimateFee, handleSetField]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const handleChangeTxType = (tt: SelectOption) => {
 | 
			
		||||
    setState({ selectedTransaction: tt });
 | 
			
		||||
    resetOptions(tt.value);
 | 
			
		||||
 | 
			
		||||
    const newState = resetOptions(tt.value);
 | 
			
		||||
 | 
			
		||||
    handleEstimateFee(newState, true);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const specialFields = ["TransactionType", "Account", "Destination"];
 | 
			
		||||
 | 
			
		||||
  const otherFields = Object.keys(txFields).filter(
 | 
			
		||||
    k => !specialFields.includes(k)
 | 
			
		||||
    (k) => !specialFields.includes(k)
 | 
			
		||||
  ) as [keyof TxFields];
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
@@ -87,7 +118,7 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState }) => {
 | 
			
		||||
        height: "calc(100% - 45px)",
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      <Flex column fluid css={{ height: "100%", overflowY: "auto" }}>
 | 
			
		||||
      <Flex column fluid css={{ height: "100%", overflowY: "auto", pr: "$1" }}>
 | 
			
		||||
        <Flex
 | 
			
		||||
          row
 | 
			
		||||
          fluid
 | 
			
		||||
@@ -159,7 +190,7 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState }) => {
 | 
			
		||||
            />
 | 
			
		||||
          </Flex>
 | 
			
		||||
        )}
 | 
			
		||||
        {otherFields.map(field => {
 | 
			
		||||
        {otherFields.map((field) => {
 | 
			
		||||
          let _value = txFields[field];
 | 
			
		||||
 | 
			
		||||
          let value: string | undefined;
 | 
			
		||||
@@ -174,36 +205,54 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState }) => {
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          let isXrp = typeof _value === "object" && _value.$type === "xrp";
 | 
			
		||||
 | 
			
		||||
          const isFee = field === "Fee";
 | 
			
		||||
          return (
 | 
			
		||||
            <Flex
 | 
			
		||||
              key={field}
 | 
			
		||||
              row
 | 
			
		||||
              fluid
 | 
			
		||||
              css={{
 | 
			
		||||
                justifyContent: "flex-end",
 | 
			
		||||
                alignItems: "center",
 | 
			
		||||
                mb: "$3",
 | 
			
		||||
                pr: "1px",
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              <Text muted css={{ mr: "$3" }}>
 | 
			
		||||
                {field + (isXrp ? " (XRP)" : "")}:{" "}
 | 
			
		||||
              </Text>
 | 
			
		||||
              <Input
 | 
			
		||||
                value={value}
 | 
			
		||||
                onChange={e => {
 | 
			
		||||
                  setState({
 | 
			
		||||
                    txFields: {
 | 
			
		||||
                      ...txFields,
 | 
			
		||||
                      [field]:
 | 
			
		||||
                        typeof _value === "object"
 | 
			
		||||
                          ? { ..._value, $value: e.target.value }
 | 
			
		||||
                          : e.target.value,
 | 
			
		||||
                    },
 | 
			
		||||
                  });
 | 
			
		||||
            <Flex column key={field} css={{ mb: "$2", pr: "1px" }}>
 | 
			
		||||
              <Flex
 | 
			
		||||
                row
 | 
			
		||||
                fluid
 | 
			
		||||
                css={{
 | 
			
		||||
                  justifyContent: "flex-end",
 | 
			
		||||
                  alignItems: "center",
 | 
			
		||||
                  position: "relative",
 | 
			
		||||
                }}
 | 
			
		||||
                css={{ width: "70%", flex: "inherit" }}
 | 
			
		||||
              />
 | 
			
		||||
              >
 | 
			
		||||
                <Text muted css={{ mr: "$3" }}>
 | 
			
		||||
                  {field + (isXrp ? " (XRP)" : "")}:{" "}
 | 
			
		||||
                </Text>
 | 
			
		||||
                <Input
 | 
			
		||||
                  value={value}
 | 
			
		||||
                  onChange={(e) => {
 | 
			
		||||
                    let value = e.target.value;
 | 
			
		||||
                    if (value && (value.includes(".") || value.includes(","))) {
 | 
			
		||||
                      value = value.replaceAll(".", "").replaceAll(",", "");
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    handleSetField(field, value);
 | 
			
		||||
                  }}
 | 
			
		||||
                  css={{ width: "70%", flex: "inherit" }}
 | 
			
		||||
                />
 | 
			
		||||
                {isFee && (
 | 
			
		||||
                  <Button
 | 
			
		||||
                    size="xs"
 | 
			
		||||
                    variant="primary"
 | 
			
		||||
                    outline
 | 
			
		||||
                    isLoading={feeLoading}
 | 
			
		||||
                    css={{
 | 
			
		||||
                      position: "absolute",
 | 
			
		||||
                      right: "$2",
 | 
			
		||||
                      fontSize: "$xs",
 | 
			
		||||
                      cursor: "pointer",
 | 
			
		||||
                      alignContent: "center",
 | 
			
		||||
                      display: "flex",
 | 
			
		||||
                    }}
 | 
			
		||||
                    onClick={() => handleEstimateFee()}
 | 
			
		||||
                  >
 | 
			
		||||
                    Suggest
 | 
			
		||||
                  </Button>
 | 
			
		||||
                )}
 | 
			
		||||
              </Flex>
 | 
			
		||||
            </Flex>
 | 
			
		||||
          );
 | 
			
		||||
        })}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12057
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										12057
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,5 +1,4 @@
 | 
			
		||||
import { Label } from "@radix-ui/react-label";
 | 
			
		||||
import { Switch, SwitchThumb } from "../../components/Switch";
 | 
			
		||||
import type { NextPage } from "next";
 | 
			
		||||
import dynamic from "next/dynamic";
 | 
			
		||||
import { Gear, Play } from "phosphor-react";
 | 
			
		||||
@@ -141,59 +140,6 @@ const CompilerSettings = () => {
 | 
			
		||||
          </Button>
 | 
			
		||||
        </ButtonGroup>
 | 
			
		||||
      </Box>
 | 
			
		||||
      <Box css={{ flexDirection: "column" }}>
 | 
			
		||||
        <Label
 | 
			
		||||
          style={{
 | 
			
		||||
            flexDirection: "row",
 | 
			
		||||
            display: "flex",
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          Clean WASM (experimental){" "}
 | 
			
		||||
          <Popover
 | 
			
		||||
            css={{
 | 
			
		||||
              maxWidth: "240px",
 | 
			
		||||
              lineHeight: "1.3",
 | 
			
		||||
              a: {
 | 
			
		||||
                color: "$purple11",
 | 
			
		||||
              },
 | 
			
		||||
              ".dark &": {
 | 
			
		||||
                backgroundColor: "$black !important",
 | 
			
		||||
 | 
			
		||||
                ".arrow": {
 | 
			
		||||
                  fill: "$colors$black",
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
            }}
 | 
			
		||||
            content="Cleaner removes unwanted compiler-provided exports and functions from a wasm binary to make it (more) suitable for being used as a Hook"
 | 
			
		||||
          >
 | 
			
		||||
            <Flex
 | 
			
		||||
              css={{
 | 
			
		||||
                position: "relative",
 | 
			
		||||
                top: "-1px",
 | 
			
		||||
                mx: "$1",
 | 
			
		||||
                backgroundColor: "$mauve8",
 | 
			
		||||
                borderRadius: "$full",
 | 
			
		||||
                cursor: "pointer",
 | 
			
		||||
                width: "16px",
 | 
			
		||||
                height: "16px",
 | 
			
		||||
                alignItems: "center",
 | 
			
		||||
                justifyContent: "center",
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              ?
 | 
			
		||||
            </Flex>
 | 
			
		||||
          </Popover>
 | 
			
		||||
        </Label>
 | 
			
		||||
        <Switch
 | 
			
		||||
          css={{ mt: "$2" }}
 | 
			
		||||
          checked={snap.compileOptions.strip}
 | 
			
		||||
          onCheckedChange={(checked) => {
 | 
			
		||||
            state.compileOptions.strip = checked;
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          <SwitchThumb />
 | 
			
		||||
        </Switch>
 | 
			
		||||
      </Box>
 | 
			
		||||
    </Flex>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
@@ -213,7 +159,7 @@ const Home: NextPage = () => {
 | 
			
		||||
    >
 | 
			
		||||
      <main style={{ display: "flex", flex: 1, position: "relative" }}>
 | 
			
		||||
        <HooksEditor />
 | 
			
		||||
        {snap.files[snap.active]?.name?.split(".")?.[1].toLowerCase() ===
 | 
			
		||||
        {snap.files[snap.active]?.name?.split(".")?.[1]?.toLowerCase() ===
 | 
			
		||||
          "c" && (
 | 
			
		||||
          <Hotkeys
 | 
			
		||||
            keyName="command+b,ctrl+b"
 | 
			
		||||
 
 | 
			
		||||
@@ -50,11 +50,7 @@ function arrayBufferToHex(arrayBuffer?: ArrayBuffer | null) {
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* deployHook function turns the wasm binary into
 | 
			
		||||
 * hex string, signs the transaction and deploys it to
 | 
			
		||||
 * Hooks testnet.
 | 
			
		||||
 */
 | 
			
		||||
export const deployHook = async (
 | 
			
		||||
export const prepareDeployHookTx = async (
 | 
			
		||||
  account: IAccount & { name?: string },
 | 
			
		||||
  data: SetHookData
 | 
			
		||||
) => {
 | 
			
		||||
@@ -93,13 +89,12 @@ export const deployHook = async (
 | 
			
		||||
  //     }
 | 
			
		||||
  //   }
 | 
			
		||||
  // });
 | 
			
		||||
 | 
			
		||||
  if (typeof window !== "undefined") {
 | 
			
		||||
    const tx = {
 | 
			
		||||
      Account: account.address,
 | 
			
		||||
      TransactionType: "SetHook",
 | 
			
		||||
      Sequence: account.sequence,
 | 
			
		||||
      Fee: "100000",
 | 
			
		||||
      Fee: data.Fee,
 | 
			
		||||
      Hooks: [
 | 
			
		||||
        {
 | 
			
		||||
          Hook: {
 | 
			
		||||
@@ -118,15 +113,28 @@ export const deployHook = async (
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    };
 | 
			
		||||
    return tx;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
    const keypair = derive.familySeed(account.secret);
 | 
			
		||||
    try {
 | 
			
		||||
      // Update tx Fee value with network estimation
 | 
			
		||||
      await estimateFee(tx, keypair);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      // use default value what you defined earlier
 | 
			
		||||
      console.log(err);
 | 
			
		||||
/* deployHook function turns the wasm binary into
 | 
			
		||||
 * hex string, signs the transaction and deploys it to
 | 
			
		||||
 * Hooks testnet.
 | 
			
		||||
 */
 | 
			
		||||
export const deployHook = async (
 | 
			
		||||
  account: IAccount & { name?: string },
 | 
			
		||||
  data: SetHookData
 | 
			
		||||
) => {
 | 
			
		||||
  if (typeof window !== "undefined") {
 | 
			
		||||
    const tx = await prepareDeployHookTx(account, data);
 | 
			
		||||
    if (!tx) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    if (!state.client) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const keypair = derive.familySeed(account.secret);
 | 
			
		||||
 | 
			
		||||
    const { signedTransaction } = sign(tx, keypair);
 | 
			
		||||
    const currentAccount = state.accounts.find(
 | 
			
		||||
      (acc) => acc.address === account.address
 | 
			
		||||
@@ -137,7 +145,7 @@ export const deployHook = async (
 | 
			
		||||
    let submitRes;
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      submitRes = await state.client.send({
 | 
			
		||||
      submitRes = await state.client?.send({
 | 
			
		||||
        command: "submit",
 | 
			
		||||
        tx_blob: signedTransaction,
 | 
			
		||||
      });
 | 
			
		||||
@@ -216,7 +224,8 @@ export const deleteHook = async (account: IAccount & { name?: string }) => {
 | 
			
		||||
    const keypair = derive.familySeed(account.secret);
 | 
			
		||||
    try {
 | 
			
		||||
      // Update tx Fee value with network estimation
 | 
			
		||||
      await estimateFee(tx, keypair);
 | 
			
		||||
      const res = await estimateFee(tx, account);
 | 
			
		||||
      tx["Fee"] = res?.base_fee ? res?.base_fee : "1000";
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      // use default value what you defined earlier
 | 
			
		||||
      console.log(err);
 | 
			
		||||
 
 | 
			
		||||
@@ -58,6 +58,29 @@ export const fetchFiles = (gistId: string) => {
 | 
			
		||||
            language: res.data.files?.[filename]?.language?.toLowerCase() || "",
 | 
			
		||||
            content: res.data.files?.[filename]?.content || "",
 | 
			
		||||
          }));
 | 
			
		||||
          // Sort files so that the source files are first
 | 
			
		||||
          // In case of other files leave the order as it its
 | 
			
		||||
          files.sort((a, b) => {
 | 
			
		||||
            const aBasename = a.name.split('.')?.[0];
 | 
			
		||||
            const aCext = a.name?.toLowerCase().endsWith('.c');
 | 
			
		||||
            const bBasename = b.name.split('.')?.[0];
 | 
			
		||||
            const bCext = b.name?.toLowerCase().endsWith('.c');
 | 
			
		||||
            // If a has c extension and b doesn't move a up
 | 
			
		||||
            if (aCext && !bCext) {
 | 
			
		||||
              return -1;
 | 
			
		||||
            }
 | 
			
		||||
            if (!aCext && bCext) {
 | 
			
		||||
              return 1
 | 
			
		||||
            }
 | 
			
		||||
            // Otherwise fallback to default sorting based on basename
 | 
			
		||||
            if (aBasename > bBasename) {
 | 
			
		||||
              return 1;
 | 
			
		||||
            }
 | 
			
		||||
            if (bBasename > aBasename) {
 | 
			
		||||
              return -1;
 | 
			
		||||
            }
 | 
			
		||||
            return 0;
 | 
			
		||||
          })
 | 
			
		||||
          state.loading = false;
 | 
			
		||||
          if (files.length > 0) {
 | 
			
		||||
            state.logs.push({
 | 
			
		||||
 
 | 
			
		||||
@@ -20,8 +20,8 @@ export const sendTransaction = async (account: IAccount, txOptions: TransactionO
 | 
			
		||||
    const { Fee = "1000", ...opts } = txOptions
 | 
			
		||||
    const tx: TransactionOptions = {
 | 
			
		||||
        Account: account.address,
 | 
			
		||||
        Sequence: account.sequence, // TODO auto-fillable
 | 
			
		||||
        Fee,  // TODO auto-fillable
 | 
			
		||||
        Sequence: account.sequence,
 | 
			
		||||
        Fee,  // TODO auto-fillable default
 | 
			
		||||
        ...opts
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,8 @@ export interface TransactionState {
 | 
			
		||||
    txFields: TxFields;
 | 
			
		||||
    viewType: 'json' | 'ui',
 | 
			
		||||
    editorSavedValue: null | string,
 | 
			
		||||
    editorValue?: string
 | 
			
		||||
    editorValue?: string,
 | 
			
		||||
    estimatedFee?: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -93,7 +94,7 @@ export const modifyTransaction = (
 | 
			
		||||
    Object.keys(partialTx).forEach(k => {
 | 
			
		||||
        // Typescript mess here, but is definetly safe!
 | 
			
		||||
        const s = tx.state as any;
 | 
			
		||||
        const p = partialTx as any;
 | 
			
		||||
        const p = partialTx as any; // ? Make copy
 | 
			
		||||
        if (!deepEqual(s[k], p[k])) s[k] = p[k];
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@@ -140,7 +141,7 @@ export const prepareTransaction = (data: any) => {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// editor value to state
 | 
			
		||||
export const prepareState = (value: string, txState: TransactionState) => {
 | 
			
		||||
export const prepareState = (value: string, transactionType?: string) => {
 | 
			
		||||
    const options = parseJSON(value);
 | 
			
		||||
    if (!options) {
 | 
			
		||||
        showAlert("Error!", {
 | 
			
		||||
@@ -151,7 +152,7 @@ export const prepareState = (value: string, txState: TransactionState) => {
 | 
			
		||||
 | 
			
		||||
    const { Account, TransactionType, Destination, ...rest } = options;
 | 
			
		||||
    let tx: Partial<TransactionState> = {};
 | 
			
		||||
    const { txFields } = txState
 | 
			
		||||
    const txFields = getTxFields(transactionType)
 | 
			
		||||
 | 
			
		||||
    if (Account) {
 | 
			
		||||
        const acc = state.accounts.find(acc => acc.address === Account);
 | 
			
		||||
@@ -206,7 +207,7 @@ export const prepareState = (value: string, txState: TransactionState) => {
 | 
			
		||||
        if (isXrp) {
 | 
			
		||||
            rest[field] = {
 | 
			
		||||
                $type: "xrp",
 | 
			
		||||
                $value: +value / 1000000, // TODO maybe use bigint?
 | 
			
		||||
                $value: +value / 1000000, // ! maybe use bigint?
 | 
			
		||||
            };
 | 
			
		||||
        } else if (typeof value === "object") {
 | 
			
		||||
            rest[field] = {
 | 
			
		||||
@@ -222,4 +223,24 @@ export const prepareState = (value: string, txState: TransactionState) => {
 | 
			
		||||
    return tx
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getTxFields = (tt?: string) => {
 | 
			
		||||
    const txFields: TxFields | undefined = transactionsData.find(
 | 
			
		||||
        tx => tx.TransactionType === tt
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (!txFields) return {}
 | 
			
		||||
 | 
			
		||||
    let _txFields = Object.keys(txFields)
 | 
			
		||||
        .filter(
 | 
			
		||||
            key => !["TransactionType", "Account", "Sequence"].includes(key)
 | 
			
		||||
        )
 | 
			
		||||
        .reduce<TxFields>(
 | 
			
		||||
            (tf, key) => (
 | 
			
		||||
                (tf[key as keyof TxFields] = (txFields as any)[key]), tf
 | 
			
		||||
            ),
 | 
			
		||||
            {}
 | 
			
		||||
        );
 | 
			
		||||
    return _txFields
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export { transactionsData }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,29 @@
 | 
			
		||||
import { sign, XRPL_Account } from "xrpl-accountlib"
 | 
			
		||||
import state from "../state"
 | 
			
		||||
import toast from 'react-hot-toast';
 | 
			
		||||
import { derive, sign } from "xrpl-accountlib"
 | 
			
		||||
import state, { IAccount } from "../state"
 | 
			
		||||
 | 
			
		||||
// Mutate tx object with network estimated fee value
 | 
			
		||||
const estimateFee = async (tx: Record<string, unknown>, keypair: XRPL_Account): Promise<null | { base_fee: string, median_fee: string; minimum_fee: string; open_ledger_fee: string; }> => {
 | 
			
		||||
  const copyTx = JSON.parse(JSON.stringify(tx))
 | 
			
		||||
  delete copyTx['SigningPubKey']
 | 
			
		||||
  const { signedTransaction } = sign(copyTx, keypair);
 | 
			
		||||
const estimateFee = async (tx: Record<string, unknown>, account: IAccount, opts: { silent?: boolean } = {}): Promise<null | { base_fee: string, median_fee: string; minimum_fee: string; open_ledger_fee: string; }> => {
 | 
			
		||||
  try {
 | 
			
		||||
    const copyTx = JSON.parse(JSON.stringify(tx))
 | 
			
		||||
    delete copyTx['SigningPubKey']
 | 
			
		||||
    if (!copyTx.Fee) {
 | 
			
		||||
      copyTx.Fee = '1000'
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    const keypair = derive.familySeed(account.secret)
 | 
			
		||||
    const { signedTransaction } = sign(copyTx, keypair);
 | 
			
		||||
 | 
			
		||||
    const res = await state.client?.send({ command: 'fee', tx_blob: signedTransaction })
 | 
			
		||||
    if (res && res.drops) {
 | 
			
		||||
      return tx['Fee'] = res.drops.base_fee;
 | 
			
		||||
      return res.drops;
 | 
			
		||||
    }
 | 
			
		||||
    return null
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    throw Error('Cannot estimate fee')
 | 
			
		||||
    if (!opts.silent) {
 | 
			
		||||
      console.error(err)
 | 
			
		||||
      toast.error("Cannot estimate fee.") // ? Some better msg
 | 
			
		||||
    }
 | 
			
		||||
    return null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -41,6 +41,7 @@ import hooksSkipHashBufLen from "./md/hooks-skip-hash-buf-len.md";
 | 
			
		||||
import hooksStateBufLen from "./md/hooks-state-buf-len.md";
 | 
			
		||||
import hooksTransactionHashBufLen from "./md/hooks-transaction-hash-buf-len.md";
 | 
			
		||||
import hooksTransactionSlotLimit from "./md/hooks-transaction-slot-limit.md";
 | 
			
		||||
import hooksTrivialCbak from "./md/hooks-trivial-cbak.md";
 | 
			
		||||
import hooksValidateBufLen from "./md/hooks-validate-buf-len.md";
 | 
			
		||||
import hooksVerifyBufLen from "./md/hooks-verify-buf-len.md";
 | 
			
		||||
 | 
			
		||||
@@ -90,6 +91,7 @@ const docs: { [key: string]: string; } = {
 | 
			
		||||
  "hooks-state-buf-len": hooksStateBufLen,
 | 
			
		||||
  "hooks-transaction-hash-buf-len": hooksTransactionHashBufLen,
 | 
			
		||||
  "hooks-transaction-slot-limit": hooksTransactionSlotLimit,
 | 
			
		||||
  "hooks-trivial-cbak": hooksTrivialCbak,
 | 
			
		||||
  "hooks-validate-buf-len": hooksValidateBufLen,
 | 
			
		||||
  "hooks-verify-buf-len": hooksVerifyBufLen,
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
# hooks-entry-points
 | 
			
		||||
 | 
			
		||||
A Hook always implements and exports exactly two functions: [cbak](https://xrpl-hooks.readme.io/v2.0/reference/cbak) and [hook](https://xrpl-hooks.readme.io/v2.0/reference/hook).
 | 
			
		||||
A Hook always implements and exports a [hook](https://xrpl-hooks.readme.io/v2.0/reference/hook) function.
 | 
			
		||||
 | 
			
		||||
This check shows error on translation units that do not have them.
 | 
			
		||||
This check shows error on translation units that do not have it.
 | 
			
		||||
 | 
			
		||||
[Read more](https://xrpl-hooks.readme.io/v2.0/docs/compiling-hooks)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
# hooks-hash-buf-len
 | 
			
		||||
 | 
			
		||||
Functions [util_sha512h](https://xrpl-hooks.readme.io/v2.0/reference/util_sha512h), [hook_hash](https://xrpl-hooks.readme.io/v2.0/reference/hook_hash), [ledger_last_hash](https://xrpl-hooks.readme.io/v2.0/reference/ledger_last_hash) and [nonce](https://xrpl-hooks.readme.io/v2.0/reference/nonce) have fixed-size hash output.
 | 
			
		||||
Functions [util_sha512h](https://xrpl-hooks.readme.io/v2.0/reference/util_sha512h), [hook_hash](https://xrpl-hooks.readme.io/v2.0/reference/hook_hash), [ledger_last_hash](https://xrpl-hooks.readme.io/v2.0/reference/ledger_last_hash), [etxn_nonce](https://xrpl-hooks.readme.io/v2.0/reference/etxn_nonce) and [ledger_nonce](https://xrpl-hooks.readme.io/v2.0/reference/ledger_nonce) have fixed-size hash output.
 | 
			
		||||
 | 
			
		||||
This check warns about too-small size of their output buffer (if it's specified by a constant - variable parameter is ignored).
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								xrpl-hooks-docs/md/hooks-trivial-cbak.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								xrpl-hooks-docs/md/hooks-trivial-cbak.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
# hooks-trivial-cbak
 | 
			
		||||
 | 
			
		||||
A Hook may implement and export a [cbak](https://xrpl-hooks.readme.io/v2.0/reference/cbak) function.
 | 
			
		||||
 | 
			
		||||
But the function is optional, and defining it so that it doesn't do anything besides returning a constant value is unnecessary (except for some debugging scenarios) and just increases the hook size. This check warns about such implementations.
 | 
			
		||||
 | 
			
		||||
[Read more](https://xrpl-hooks.readme.io/v2.0/docs/compiling-hooks)
 | 
			
		||||
		Reference in New Issue
	
	Block a user