221 lines
6.7 KiB
TypeScript
221 lines
6.7 KiB
TypeScript
import { derive, sign } from "xrpl-accountlib";
|
|
import toast from "react-hot-toast";
|
|
|
|
import state, { IAccount } from "../index";
|
|
import calculateHookOn, { TTS } from "../../utils/hookOnCalculator";
|
|
import { SetHookData } from "../../components/SetHookDialog";
|
|
|
|
export const sha256 = 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 toHex(str: string) {
|
|
var result = '';
|
|
for (var i = 0; i < str.length; i++) {
|
|
result += str.charCodeAt(i).toString(16);
|
|
}
|
|
return result.toUpperCase();
|
|
}
|
|
|
|
function arrayBufferToHex(arrayBuffer?: ArrayBuffer | null) {
|
|
if (!arrayBuffer) {
|
|
return "";
|
|
}
|
|
if (
|
|
typeof arrayBuffer !== "object" ||
|
|
arrayBuffer === null ||
|
|
typeof arrayBuffer.byteLength !== "number"
|
|
) {
|
|
throw new TypeError("Expected input to be an ArrayBuffer");
|
|
}
|
|
|
|
var view = new Uint8Array(arrayBuffer);
|
|
var result = "";
|
|
var value;
|
|
|
|
for (var i = 0; i < view.length; i++) {
|
|
value = view[i].toString(16);
|
|
result += value.length === 1 ? "0" + value : value;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* 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 (
|
|
!state.files ||
|
|
state.files.length === 0 ||
|
|
!state.files?.[state.active]?.compiledContent
|
|
) {
|
|
return;
|
|
}
|
|
|
|
if (!state.files?.[state.active]?.compiledContent) {
|
|
return;
|
|
}
|
|
if (!state.client) {
|
|
return;
|
|
}
|
|
const HookNamespace = (await sha256(data.HookNamespace)).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)?.map(aa => ({ HookParameter: { HookParameterName: toHex(aa.HookParameter.HookParameterName || ''), HookParameterValue: toHex(aa.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,
|
|
TransactionType: "SetHook",
|
|
Sequence: account.sequence,
|
|
Fee: "100000",
|
|
Hooks: [
|
|
{
|
|
Hook: {
|
|
CreateCode: arrayBufferToHex(
|
|
state.files?.[state.active]?.compiledContent
|
|
).toUpperCase(),
|
|
HookOn: calculateHookOn(hookOnValues),
|
|
HookNamespace,
|
|
HookApiVersion: 0,
|
|
Flags: 1,
|
|
// ...(filteredHookGrants.length > 0 && { HookGrants: filteredHookGrants }),
|
|
...(filteredHookParameters.length > 0 && { HookParameters: filteredHookParameters }),
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
const keypair = derive.familySeed(account.secret);
|
|
const { signedTransaction } = sign(tx, keypair);
|
|
const currentAccount = state.accounts.find(
|
|
(acc) => acc.address === account.address
|
|
);
|
|
if (currentAccount) {
|
|
currentAccount.isLoading = true;
|
|
}
|
|
let submitRes;
|
|
try {
|
|
submitRes = await state.client.send({
|
|
command: "submit",
|
|
tx_blob: signedTransaction,
|
|
});
|
|
|
|
if (submitRes.engine_result === "tesSUCCESS") {
|
|
state.deployLogs.push({
|
|
type: "success",
|
|
message: "Hook deployed successfully ✅",
|
|
});
|
|
state.deployLogs.push({
|
|
type: "success",
|
|
message: `[${submitRes.engine_result}] ${submitRes.engine_result_message} Validated ledger index: ${submitRes.validated_ledger_index}`,
|
|
});
|
|
} else {
|
|
state.deployLogs.push({
|
|
type: "error",
|
|
message: `[${submitRes.engine_result || submitRes.error}] ${submitRes.engine_result_message || submitRes.error_exception}`,
|
|
});
|
|
}
|
|
} catch (err) {
|
|
console.log(err);
|
|
state.deployLogs.push({
|
|
type: "error",
|
|
message: "Error occured while deploying",
|
|
});
|
|
}
|
|
if (currentAccount) {
|
|
currentAccount.isLoading = false;
|
|
}
|
|
return submitRes;
|
|
}
|
|
};
|
|
|
|
export const deleteHook = async (account: IAccount & { name?: string }) => {
|
|
if (!state.client) {
|
|
return;
|
|
}
|
|
const currentAccount = state.accounts.find(
|
|
(acc) => acc.address === account.address
|
|
);
|
|
if (currentAccount?.isLoading || !currentAccount?.hooks.length) {
|
|
return
|
|
}
|
|
if (typeof window !== "undefined") {
|
|
const tx = {
|
|
Account: account.address,
|
|
TransactionType: "SetHook",
|
|
Sequence: account.sequence,
|
|
Fee: "100000",
|
|
Hooks: [
|
|
{
|
|
Hook: {
|
|
CreateCode: "",
|
|
Flags: 1,
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
const keypair = derive.familySeed(account.secret);
|
|
const { signedTransaction } = sign(tx, keypair);
|
|
|
|
if (currentAccount) {
|
|
currentAccount.isLoading = true;
|
|
}
|
|
let submitRes;
|
|
const toastId = toast.loading("Deleting hook...");
|
|
try {
|
|
submitRes = await state.client.send({
|
|
command: "submit",
|
|
tx_blob: signedTransaction,
|
|
});
|
|
|
|
if (submitRes.engine_result === "tesSUCCESS") {
|
|
toast.success('Hook deleted successfully ✅', { id: toastId })
|
|
state.deployLogs.push({
|
|
type: "success",
|
|
message: "Hook deleted successfully ✅",
|
|
});
|
|
state.deployLogs.push({
|
|
type: "success",
|
|
message: `[${submitRes.engine_result}] ${submitRes.engine_result_message} Validated ledger index: ${submitRes.validated_ledger_index}`,
|
|
});
|
|
currentAccount.hooks = [];
|
|
} else {
|
|
toast.error(`${submitRes.engine_result_message || submitRes.error_exception}`, { id: toastId })
|
|
state.deployLogs.push({
|
|
type: "error",
|
|
message: `[${submitRes.engine_result || submitRes.error}] ${submitRes.engine_result_message || submitRes.error_exception}`,
|
|
});
|
|
}
|
|
} catch (err) {
|
|
console.log(err);
|
|
toast.error('Error occured while deleting hoook', { id: toastId })
|
|
state.deployLogs.push({
|
|
type: "error",
|
|
message: "Error occured while deleting hook",
|
|
});
|
|
}
|
|
if (currentAccount) {
|
|
currentAccount.isLoading = false;
|
|
}
|
|
return submitRes;
|
|
}
|
|
}; |