update set hook functionality
This commit is contained in:
245
components/SetHookDialog.tsx
Normal file
245
components/SetHookDialog.tsx
Normal file
@@ -0,0 +1,245 @@
|
||||
import { Plus, Trash, X } from "phosphor-react";
|
||||
import React from "react";
|
||||
import Button from "./Button";
|
||||
import Box from "./Box";
|
||||
import { Stack, Flex, Select } from ".";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
DialogClose,
|
||||
DialogTrigger,
|
||||
} from "./Dialog";
|
||||
import { Input } from "./Input";
|
||||
import {
|
||||
Controller,
|
||||
SubmitHandler,
|
||||
useFieldArray,
|
||||
useForm,
|
||||
} from "react-hook-form";
|
||||
|
||||
import { TTS, tts } from "../utils/hookOnCalculator";
|
||||
import { deployHook } from "../state/actions";
|
||||
import type { IAccount } from "../state";
|
||||
import { useSnapshot } from "valtio";
|
||||
import state from "../state";
|
||||
|
||||
const transactionOptions = Object.keys(tts).map((key) => ({
|
||||
label: key,
|
||||
value: key as keyof TTS,
|
||||
}));
|
||||
|
||||
export type SetHookData = {
|
||||
Invoke: {
|
||||
value: keyof TTS;
|
||||
label: string;
|
||||
}[];
|
||||
HookParameters: {
|
||||
HookParameter: {
|
||||
HookParameterName: string;
|
||||
HookParameterValue: string;
|
||||
};
|
||||
}[];
|
||||
// HookGrants: {
|
||||
// HookGrant: {
|
||||
// Authorize: string;
|
||||
// HookHash: string;
|
||||
// };
|
||||
// }[];
|
||||
};
|
||||
|
||||
export const SetHookDialog: React.FC<{ account: IAccount }> = ({ account }) => {
|
||||
const snap = useSnapshot(state);
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
control,
|
||||
// formState: { errors },
|
||||
} = useForm<SetHookData>();
|
||||
const { fields, append, remove } = useFieldArray({
|
||||
control,
|
||||
name: "HookParameters", // unique name for your Field Array
|
||||
});
|
||||
// const {
|
||||
// fields: grantFields,
|
||||
// append: grantAppend,
|
||||
// remove: grantRemove,
|
||||
// } = useFieldArray({
|
||||
// control,
|
||||
// name: "HookGrants", // unique name for your Field Array
|
||||
// });
|
||||
if (!account) {
|
||||
return null;
|
||||
}
|
||||
const onSubmit: SubmitHandler<SetHookData> = (data) => {
|
||||
deployHook(account, data);
|
||||
};
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
ghost
|
||||
size="xs"
|
||||
uppercase
|
||||
variant={"secondary"}
|
||||
disabled={
|
||||
account.isLoading ||
|
||||
!snap.files.filter((file) => file.compiledWatContent).length
|
||||
}
|
||||
>
|
||||
Set Hook
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<DialogContent css={{ overflowY: "auto" }}>
|
||||
<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="absolute"
|
||||
styles={{
|
||||
menuPortal: (provided) => ({
|
||||
...provided,
|
||||
zIndex: 9000,
|
||||
}),
|
||||
}}
|
||||
options={transactionOptions}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</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="Parameter value"
|
||||
{...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%" }}>
|
||||
<label style={{ marginBottom: "10px", display: "block" }}>
|
||||
Hook Grants
|
||||
</label>
|
||||
<Stack>
|
||||
{grantFields.map((field, index) => (
|
||||
<Stack key={field.id}>
|
||||
<Input
|
||||
// important to include key with field's id
|
||||
placeholder="Authorize"
|
||||
{...register(
|
||||
`HookGrants.${index}.HookGrant.Authorize`,
|
||||
{ minLength: 5 }
|
||||
)}
|
||||
/>
|
||||
<Input
|
||||
placeholder="HookHash"
|
||||
{...register(`HookGrants.${index}.HookGrant.HookHash`, {
|
||||
minLength: 64,
|
||||
maxLength: 64,
|
||||
})}
|
||||
/>
|
||||
<Button
|
||||
onClick={() => grantRemove(index)}
|
||||
variant="destroy"
|
||||
>
|
||||
<Trash weight="regular" size="16px" />
|
||||
</Button>
|
||||
</Stack>
|
||||
))}
|
||||
<Button
|
||||
outline
|
||||
fullWidth
|
||||
type="button"
|
||||
onClick={() =>
|
||||
grantAppend({
|
||||
HookGrant: {
|
||||
Authorize: "",
|
||||
HookHash: "",
|
||||
},
|
||||
})
|
||||
}
|
||||
>
|
||||
<Plus size="16px" />
|
||||
Add Hook Grant
|
||||
</Button>
|
||||
</Stack>
|
||||
</Box> */}
|
||||
</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">
|
||||
Set Hook
|
||||
</Button>
|
||||
{/* </DialogClose> */}
|
||||
</Flex>
|
||||
<DialogClose asChild>
|
||||
<Box css={{ position: "absolute", top: "$3", right: "$3" }}>
|
||||
<X size="20px" />
|
||||
</Box>
|
||||
</DialogClose>
|
||||
</DialogContent>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default SetHookDialog;
|
||||
@@ -1,7 +1,19 @@
|
||||
import { derive, sign } from "xrpl-accountlib";
|
||||
|
||||
import state, { IAccount } from "../index";
|
||||
import calculateHookOn from "../../utils/hookOnCalculator";
|
||||
import calculateHookOn, { TTS } from "../../utils/hookOnCalculator";
|
||||
import { SetHookData } from "../../components/SetHookDialog";
|
||||
|
||||
const hash = async (string: string) => {
|
||||
const utf8 = new TextEncoder().encode(string);
|
||||
const hashBuffer = await crypto.subtle.digest('SHA-256', utf8);
|
||||
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
||||
const hashHex = hashArray
|
||||
.map((bytes) => bytes.toString(16).padStart(2, '0'))
|
||||
.join('');
|
||||
return hashHex;
|
||||
}
|
||||
|
||||
|
||||
function arrayBufferToHex(arrayBuffer?: ArrayBuffer | null) {
|
||||
if (!arrayBuffer) {
|
||||
@@ -31,7 +43,7 @@ function arrayBufferToHex(arrayBuffer?: ArrayBuffer | null) {
|
||||
* hex string, signs the transaction and deploys it to
|
||||
* Hooks testnet.
|
||||
*/
|
||||
export const deployHook = async (account: IAccount & { name?: string }) => {
|
||||
export const deployHook = async (account: IAccount & { name?: string }, data: SetHookData) => {
|
||||
if (
|
||||
!state.files ||
|
||||
state.files.length === 0 ||
|
||||
@@ -46,6 +58,21 @@ export const deployHook = async (account: IAccount & { name?: string }) => {
|
||||
if (!state.client) {
|
||||
return;
|
||||
}
|
||||
const HookNamespace = await hash(arrayBufferToHex(
|
||||
state.files?.[state.active]?.compiledContent
|
||||
).toUpperCase());
|
||||
const hookOnValues: (keyof TTS)[] = data.Invoke.map(tt => tt.value);
|
||||
const { HookParameters } = data;
|
||||
const filteredHookParameters = HookParameters.filter(hp => hp.HookParameter.HookParameterName && hp.HookParameter.HookParameterValue);
|
||||
// const filteredHookGrants = HookGrants.filter(hg => hg.HookGrant.Authorize || hg.HookGrant.HookHash).map(hg => {
|
||||
// return {
|
||||
// HookGrant: {
|
||||
// ...(hg.HookGrant.Authorize && { Authorize: hg.HookGrant.Authorize }),
|
||||
// // HookHash: hg.HookGrant.HookHash || undefined
|
||||
// ...(hg.HookGrant.HookHash && { HookHash: hg.HookGrant.HookHash })
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
if (typeof window !== "undefined") {
|
||||
const tx = {
|
||||
Account: account.address,
|
||||
@@ -53,25 +80,17 @@ export const deployHook = async (account: IAccount & { name?: string }) => {
|
||||
Sequence: account.sequence,
|
||||
Fee: "100000",
|
||||
Hooks: [
|
||||
// {
|
||||
// Hook: {
|
||||
// CreateCode:
|
||||
// HookApiVersion: 0,
|
||||
// HookNamespace: "Kissa",
|
||||
// HookOn: calculateHookOn([]),
|
||||
// Flags: 2
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// [
|
||||
{
|
||||
Hook: {
|
||||
CreateCode: arrayBufferToHex(
|
||||
state.files?.[state.active]?.compiledContent
|
||||
).toUpperCase(),
|
||||
HookOn: calculateHookOn([]),
|
||||
HookNamespace: "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
|
||||
HookOn: calculateHookOn(hookOnValues),
|
||||
HookNamespace,
|
||||
HookApiVersion: 0,
|
||||
Flags: 1,
|
||||
// ...(filteredHookGrants.length > 0 && { HookGrants: filteredHookGrants }),
|
||||
...(filteredHookParameters.length > 0 && { HookParameters: filteredHookParameters }),
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user