Merge branch 'main' into feat/engine-code-links
This commit is contained in:
@@ -1,54 +1,52 @@
|
||||
import { derive, sign } from "xrpl-accountlib";
|
||||
import toast from "react-hot-toast";
|
||||
import { derive, sign } from 'xrpl-accountlib'
|
||||
import toast from 'react-hot-toast'
|
||||
|
||||
import state, { IAccount } from "../index";
|
||||
import calculateHookOn, { TTS } from "../../utils/hookOnCalculator";
|
||||
import { Link } from "../../components";
|
||||
import { ref } from "valtio";
|
||||
import estimateFee from "../../utils/estimateFee";
|
||||
import { SetHookData } from "../../utils/setHook";
|
||||
import ResultLink from "../../components/ResultLink";
|
||||
import state, { IAccount } from '../index'
|
||||
import calculateHookOn, { TTS } from '../../utils/hookOnCalculator'
|
||||
import { Link } from '../../components'
|
||||
import { ref } from 'valtio'
|
||||
import estimateFee from '../../utils/estimateFee'
|
||||
import { SetHookData } from '../../utils/setHook'
|
||||
import ResultLink from '../../components/ResultLink'
|
||||
|
||||
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;
|
||||
};
|
||||
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 = "";
|
||||
var result = ''
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
result += str.charCodeAt(i).toString(16);
|
||||
result += str.charCodeAt(i).toString(16)
|
||||
}
|
||||
return result.toUpperCase();
|
||||
return result.toUpperCase()
|
||||
}
|
||||
|
||||
function arrayBufferToHex(arrayBuffer?: ArrayBuffer | null) {
|
||||
if (!arrayBuffer) {
|
||||
return "";
|
||||
return ''
|
||||
}
|
||||
if (
|
||||
typeof arrayBuffer !== "object" ||
|
||||
typeof arrayBuffer !== 'object' ||
|
||||
arrayBuffer === null ||
|
||||
typeof arrayBuffer.byteLength !== "number"
|
||||
typeof arrayBuffer.byteLength !== 'number'
|
||||
) {
|
||||
throw new TypeError("Expected input to be an ArrayBuffer");
|
||||
throw new TypeError('Expected input to be an ArrayBuffer')
|
||||
}
|
||||
|
||||
var view = new Uint8Array(arrayBuffer);
|
||||
var result = "";
|
||||
var value;
|
||||
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;
|
||||
value = view[i].toString(16)
|
||||
result += value.length === 1 ? '0' + value : value
|
||||
}
|
||||
|
||||
return result;
|
||||
return result
|
||||
}
|
||||
|
||||
export const prepareDeployHookTx = async (
|
||||
@@ -57,30 +55,29 @@ export const prepareDeployHookTx = async (
|
||||
) => {
|
||||
const activeFile = state.files[state.active]?.compiledContent
|
||||
? state.files[state.active]
|
||||
: state.files.filter(file => file.compiledContent)[0];
|
||||
: state.files.filter(file => file.compiledContent)[0]
|
||||
|
||||
if (!state.files || state.files.length === 0) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
if (!activeFile?.compiledContent) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (!state.client) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
const HookNamespace = (await sha256(data.HookNamespace)).toUpperCase();
|
||||
const hookOnValues: (keyof TTS)[] = data.Invoke.map(tt => tt.value);
|
||||
const { HookParameters } = data;
|
||||
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
|
||||
hp => hp.HookParameter.HookParameterName && hp.HookParameter.HookParameterValue
|
||||
)?.map(aa => ({
|
||||
HookParameter: {
|
||||
HookParameterName: toHex(aa.HookParameter.HookParameterName || ""),
|
||||
HookParameterValue: aa.HookParameter.HookParameterValue || "",
|
||||
},
|
||||
}));
|
||||
HookParameterName: toHex(aa.HookParameter.HookParameterName || ''),
|
||||
HookParameterValue: aa.HookParameter.HookParameterValue || ''
|
||||
}
|
||||
}))
|
||||
// const filteredHookGrants = HookGrants.filter(hg => hg.HookGrant.Authorize || hg.HookGrant.HookHash).map(hg => {
|
||||
// return {
|
||||
// HookGrant: {
|
||||
@@ -90,79 +87,71 @@ export const prepareDeployHookTx = async (
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
if (typeof window !== "undefined") {
|
||||
if (typeof window !== 'undefined') {
|
||||
const tx = {
|
||||
Account: account.address,
|
||||
TransactionType: "SetHook",
|
||||
TransactionType: 'SetHook',
|
||||
Sequence: account.sequence,
|
||||
Fee: data.Fee,
|
||||
Hooks: [
|
||||
{
|
||||
Hook: {
|
||||
CreateCode: arrayBufferToHex(
|
||||
activeFile?.compiledContent
|
||||
).toUpperCase(),
|
||||
CreateCode: arrayBufferToHex(activeFile?.compiledContent).toUpperCase(),
|
||||
HookOn: calculateHookOn(hookOnValues),
|
||||
HookNamespace,
|
||||
HookApiVersion: 0,
|
||||
Flags: 1,
|
||||
// ...(filteredHookGrants.length > 0 && { HookGrants: filteredHookGrants }),
|
||||
...(filteredHookParameters.length > 0 && {
|
||||
HookParameters: filteredHookParameters,
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
return tx;
|
||||
HookParameters: filteredHookParameters
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
return tx
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* 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") {
|
||||
export const deployHook = async (account: IAccount & { name?: string }, data: SetHookData) => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const activeFile = state.files[state.active]?.compiledContent
|
||||
? state.files[state.active]
|
||||
: state.files.filter(file => file.compiledContent)[0];
|
||||
state.deployValues[activeFile.name] = data;
|
||||
const tx = await prepareDeployHookTx(account, data);
|
||||
: state.files.filter(file => file.compiledContent)[0]
|
||||
state.deployValues[activeFile.name] = data
|
||||
const tx = await prepareDeployHookTx(account, data)
|
||||
if (!tx) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (!state.client) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
const keypair = derive.familySeed(account.secret);
|
||||
const keypair = derive.familySeed(account.secret)
|
||||
|
||||
const { signedTransaction } = sign(tx, keypair);
|
||||
const currentAccount = state.accounts.find(
|
||||
acc => acc.address === account.address
|
||||
);
|
||||
const { signedTransaction } = sign(tx, keypair)
|
||||
const currentAccount = state.accounts.find(acc => acc.address === account.address)
|
||||
if (currentAccount) {
|
||||
currentAccount.isLoading = true;
|
||||
currentAccount.isLoading = true
|
||||
}
|
||||
let submitRes;
|
||||
let submitRes
|
||||
|
||||
try {
|
||||
submitRes = await state.client?.send({
|
||||
command: "submit",
|
||||
tx_blob: signedTransaction,
|
||||
});
|
||||
command: 'submit',
|
||||
tx_blob: signedTransaction
|
||||
})
|
||||
|
||||
const txHash = submitRes.tx_json?.hash;
|
||||
const txHash = submitRes.tx_json?.hash
|
||||
const resultMsg = ref(
|
||||
<>
|
||||
[<ResultLink result={submitRes.engine_result} />]{" "}
|
||||
{submitRes.engine_result_message}{" "}
|
||||
[<ResultLink result={submitRes.engine_result} />] {submitRes.engine_result_message}{' '}
|
||||
{txHash && (
|
||||
<>
|
||||
Transaction hash:{" "}
|
||||
Transaction hash:{' '}
|
||||
<Link
|
||||
as="a"
|
||||
href={`https://${process.env.NEXT_PUBLIC_EXPLORER_URL}/${txHash}`}
|
||||
@@ -174,123 +163,120 @@ export const deployHook = async (
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
if (submitRes.engine_result === "tesSUCCESS") {
|
||||
)
|
||||
if (submitRes.engine_result === 'tesSUCCESS') {
|
||||
state.deployLogs.push({
|
||||
type: "success",
|
||||
message: "Hook deployed successfully ✅",
|
||||
});
|
||||
type: 'success',
|
||||
message: 'Hook deployed successfully ✅'
|
||||
})
|
||||
state.deployLogs.push({
|
||||
type: "success",
|
||||
message: resultMsg,
|
||||
});
|
||||
type: 'success',
|
||||
message: resultMsg
|
||||
})
|
||||
} else if (submitRes.engine_result) {
|
||||
state.deployLogs.push({
|
||||
type: "error",
|
||||
message: resultMsg,
|
||||
});
|
||||
type: 'error',
|
||||
message: resultMsg
|
||||
})
|
||||
} else {
|
||||
state.deployLogs.push({
|
||||
type: "error",
|
||||
message: `[${submitRes.error}] ${submitRes.error_exception}`,
|
||||
});
|
||||
type: 'error',
|
||||
message: `[${submitRes.error}] ${submitRes.error_exception}`
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
console.log(err)
|
||||
state.deployLogs.push({
|
||||
type: "error",
|
||||
message: "Error occurred while deploying",
|
||||
});
|
||||
type: 'error',
|
||||
message: 'Error occurred while deploying'
|
||||
})
|
||||
}
|
||||
if (currentAccount) {
|
||||
currentAccount.isLoading = false;
|
||||
currentAccount.isLoading = false
|
||||
}
|
||||
return submitRes;
|
||||
return submitRes
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const deleteHook = async (account: IAccount & { name?: string }) => {
|
||||
if (!state.client) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
const currentAccount = state.accounts.find(
|
||||
acc => acc.address === account.address
|
||||
);
|
||||
const currentAccount = state.accounts.find(acc => acc.address === account.address)
|
||||
if (currentAccount?.isLoading || !currentAccount?.hooks.length) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (typeof window !== "undefined") {
|
||||
if (typeof window !== 'undefined') {
|
||||
const tx = {
|
||||
Account: account.address,
|
||||
TransactionType: "SetHook",
|
||||
TransactionType: 'SetHook',
|
||||
Sequence: account.sequence,
|
||||
Fee: "100000",
|
||||
Fee: '100000',
|
||||
Hooks: [
|
||||
{
|
||||
Hook: {
|
||||
CreateCode: "",
|
||||
Flags: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
CreateCode: '',
|
||||
Flags: 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const keypair = derive.familySeed(account.secret);
|
||||
const keypair = derive.familySeed(account.secret)
|
||||
try {
|
||||
// Update tx Fee value with network estimation
|
||||
const res = await estimateFee(tx, account);
|
||||
tx["Fee"] = res?.base_fee ? res?.base_fee : "1000";
|
||||
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);
|
||||
console.log(err)
|
||||
}
|
||||
const { signedTransaction } = sign(tx, keypair);
|
||||
const { signedTransaction } = sign(tx, keypair)
|
||||
|
||||
if (currentAccount) {
|
||||
currentAccount.isLoading = true;
|
||||
currentAccount.isLoading = true
|
||||
}
|
||||
let submitRes;
|
||||
const toastId = toast.loading("Deleting hook...");
|
||||
let submitRes
|
||||
const toastId = toast.loading('Deleting hook...')
|
||||
try {
|
||||
submitRes = await state.client.send({
|
||||
command: "submit",
|
||||
tx_blob: signedTransaction,
|
||||
});
|
||||
command: 'submit',
|
||||
tx_blob: signedTransaction
|
||||
})
|
||||
|
||||
if (submitRes.engine_result === "tesSUCCESS") {
|
||||
toast.success("Hook deleted successfully ✅", { id: toastId });
|
||||
if (submitRes.engine_result === 'tesSUCCESS') {
|
||||
toast.success('Hook deleted successfully ✅', { id: toastId })
|
||||
state.deployLogs.push({
|
||||
type: "success",
|
||||
message: "Hook deleted successfully ✅",
|
||||
});
|
||||
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 = [];
|
||||
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 }
|
||||
);
|
||||
toast.error(`${submitRes.engine_result_message || submitRes.error_exception}`, {
|
||||
id: toastId
|
||||
})
|
||||
state.deployLogs.push({
|
||||
type: "error",
|
||||
type: 'error',
|
||||
message: `[${submitRes.engine_result || submitRes.error}] ${
|
||||
submitRes.engine_result_message || submitRes.error_exception
|
||||
}`,
|
||||
});
|
||||
}`
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
toast.error("Error occurred while deleting hook", { id: toastId });
|
||||
console.log(err)
|
||||
toast.error('Error occurred while deleting hook', { id: toastId })
|
||||
state.deployLogs.push({
|
||||
type: "error",
|
||||
message: "Error occurred while deleting hook",
|
||||
});
|
||||
type: 'error',
|
||||
message: 'Error occurred while deleting hook'
|
||||
})
|
||||
}
|
||||
if (currentAccount) {
|
||||
currentAccount.isLoading = false;
|
||||
currentAccount.isLoading = false
|
||||
}
|
||||
return submitRes;
|
||||
return submitRes
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user