Compare commits
1 Commits
feat/fee-e
...
feat/tx_ha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
704ebe4b92 |
@@ -469,7 +469,7 @@ const Accounts: FC<AccountProps> = (props) => {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SetHookDialog accountAddress={account.address} />
|
<SetHookDialog account={account} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ import {
|
|||||||
|
|
||||||
import { TTS, tts } from "../utils/hookOnCalculator";
|
import { TTS, tts } from "../utils/hookOnCalculator";
|
||||||
import { deployHook } from "../state/actions";
|
import { deployHook } from "../state/actions";
|
||||||
|
import type { IAccount } from "../state";
|
||||||
import { useSnapshot } from "valtio";
|
import { useSnapshot } from "valtio";
|
||||||
import state from "../state";
|
import state from "../state";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import { prepareDeployHookTx, sha256 } from "../state/actions/deployHook";
|
import { sha256 } from "../state/actions/deployHook";
|
||||||
import estimateFee from "../utils/estimateFee";
|
|
||||||
|
|
||||||
const transactionOptions = Object.keys(tts).map((key) => ({
|
const transactionOptions = Object.keys(tts).map((key) => ({
|
||||||
label: key,
|
label: key,
|
||||||
@@ -37,7 +37,6 @@ export type SetHookData = {
|
|||||||
value: keyof TTS;
|
value: keyof TTS;
|
||||||
label: string;
|
label: string;
|
||||||
}[];
|
}[];
|
||||||
Fee: string;
|
|
||||||
HookNamespace: string;
|
HookNamespace: string;
|
||||||
HookParameters: {
|
HookParameters: {
|
||||||
HookParameter: {
|
HookParameter: {
|
||||||
@@ -53,11 +52,8 @@ export type SetHookData = {
|
|||||||
// }[];
|
// }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
export const SetHookDialog: React.FC<{ account: IAccount }> = ({ account }) => {
|
||||||
({ accountAddress }) => {
|
|
||||||
const snap = useSnapshot(state);
|
const snap = useSnapshot(state);
|
||||||
const account = snap.accounts.find((acc) => acc.address === accountAddress);
|
|
||||||
|
|
||||||
const [isSetHookDialogOpen, setIsSetHookDialogOpen] = useState(false);
|
const [isSetHookDialogOpen, setIsSetHookDialogOpen] = useState(false);
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@@ -65,21 +61,16 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
|||||||
control,
|
control,
|
||||||
watch,
|
watch,
|
||||||
setValue,
|
setValue,
|
||||||
getValues,
|
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
} = useForm<SetHookData>({
|
} = useForm<SetHookData>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
HookNamespace:
|
HookNamespace: snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || "",
|
||||||
snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || "",
|
|
||||||
Invoke: transactionOptions.filter((to) => to.label === "ttPAYMENT"),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const { fields, append, remove } = useFieldArray({
|
const { fields, append, remove } = useFieldArray({
|
||||||
control,
|
control,
|
||||||
name: "HookParameters", // unique name for your Field Array
|
name: "HookParameters", // unique name for your Field Array
|
||||||
});
|
});
|
||||||
const [formInitialized, setFormInitialized] = useState(false);
|
|
||||||
const [estimateLoading, setEstimateLoading] = useState(false);
|
|
||||||
|
|
||||||
// Update value if activeWat changes
|
// Update value if activeWat changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -87,7 +78,6 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
|||||||
"HookNamespace",
|
"HookNamespace",
|
||||||
snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || ""
|
snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || ""
|
||||||
);
|
);
|
||||||
setFormInitialized(true);
|
|
||||||
}, [snap.activeWat, snap.files, setValue]);
|
}, [snap.activeWat, snap.files, setValue]);
|
||||||
// const {
|
// const {
|
||||||
// fields: grantFields,
|
// fields: grantFields,
|
||||||
@@ -110,24 +100,6 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
|||||||
calculateHashedValue();
|
calculateHashedValue();
|
||||||
}, [namespace, calculateHashedValue]);
|
}, [namespace, calculateHashedValue]);
|
||||||
|
|
||||||
// Calcucate initial fee estimate when modal opens
|
|
||||||
useEffect(() => {
|
|
||||||
if (formInitialized && account) {
|
|
||||||
(async () => {
|
|
||||||
const formValues = getValues();
|
|
||||||
const tx = await prepareDeployHookTx(account, formValues);
|
|
||||||
if (!tx) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const res = await estimateFee(tx, account);
|
|
||||||
if (res && res.base_fee) {
|
|
||||||
setValue("Fee", res.base_fee);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [formInitialized]);
|
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -146,6 +118,7 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
|||||||
}
|
}
|
||||||
toast.error(`Transaction failed! (${res?.engine_result_message})`);
|
toast.error(`Transaction failed! (${res?.engine_result_message})`);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={isSetHookDialogOpen} onOpenChange={setIsSetHookDialogOpen}>
|
<Dialog open={isSetHookDialogOpen} onOpenChange={setIsSetHookDialogOpen}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
@@ -205,7 +178,6 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
|||||||
<Input readOnly value={hashedNamespace} />
|
<Input readOnly value={hashedNamespace} />
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box css={{ width: "100%" }}>
|
<Box css={{ width: "100%" }}>
|
||||||
<Label style={{ marginBottom: "10px", display: "block" }}>
|
<Label style={{ marginBottom: "10px", display: "block" }}>
|
||||||
Hook parameters
|
Hook parameters
|
||||||
@@ -249,69 +221,6 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
|||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</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"}
|
|
||||||
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", res.base_fee);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} 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%" }}>
|
{/* <Box css={{ width: "100%" }}>
|
||||||
<label style={{ marginBottom: "10px", display: "block" }}>
|
<label style={{ marginBottom: "10px", display: "block" }}>
|
||||||
Hook Grants
|
Hook Grants
|
||||||
@@ -392,9 +301,6 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
);
|
|
||||||
|
|
||||||
SetHookDialog.displayName = "SetHookDialog";
|
|
||||||
|
|
||||||
export default SetHookDialog;
|
export default SetHookDialog;
|
||||||
|
|||||||
12057
package-lock.json
generated
Normal file
12057
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -50,7 +50,11 @@ function arrayBufferToHex(arrayBuffer?: ArrayBuffer | null) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const prepareDeployHookTx = async (
|
/* 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 },
|
account: IAccount & { name?: string },
|
||||||
data: SetHookData
|
data: SetHookData
|
||||||
) => {
|
) => {
|
||||||
@@ -89,12 +93,13 @@ export const prepareDeployHookTx = async (
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// });
|
// });
|
||||||
|
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
const tx = {
|
const tx = {
|
||||||
Account: account.address,
|
Account: account.address,
|
||||||
TransactionType: "SetHook",
|
TransactionType: "SetHook",
|
||||||
Sequence: account.sequence,
|
Sequence: account.sequence,
|
||||||
Fee: data.Fee,
|
Fee: "100000",
|
||||||
Hooks: [
|
Hooks: [
|
||||||
{
|
{
|
||||||
Hook: {
|
Hook: {
|
||||||
@@ -113,28 +118,15 @@ export const prepareDeployHookTx = async (
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
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") {
|
|
||||||
const tx = await prepareDeployHookTx(account, data);
|
|
||||||
if (!tx) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!state.client) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const keypair = derive.familySeed(account.secret);
|
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);
|
||||||
|
}
|
||||||
const { signedTransaction } = sign(tx, keypair);
|
const { signedTransaction } = sign(tx, keypair);
|
||||||
const currentAccount = state.accounts.find(
|
const currentAccount = state.accounts.find(
|
||||||
(acc) => acc.address === account.address
|
(acc) => acc.address === account.address
|
||||||
@@ -145,7 +137,7 @@ export const deployHook = async (
|
|||||||
let submitRes;
|
let submitRes;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
submitRes = await state.client?.send({
|
submitRes = await state.client.send({
|
||||||
command: "submit",
|
command: "submit",
|
||||||
tx_blob: signedTransaction,
|
tx_blob: signedTransaction,
|
||||||
});
|
});
|
||||||
@@ -160,14 +152,14 @@ export const deployHook = async (
|
|||||||
message: ref(
|
message: ref(
|
||||||
<>
|
<>
|
||||||
[{submitRes.engine_result}] {submitRes.engine_result_message}{" "}
|
[{submitRes.engine_result}] {submitRes.engine_result_message}{" "}
|
||||||
Validated ledger index:{" "}
|
Transaction hash:{" "}
|
||||||
<Link
|
<Link
|
||||||
as="a"
|
as="a"
|
||||||
href={`https://${process.env.NEXT_PUBLIC_EXPLORER_URL}/${submitRes.validated_ledger_index}`}
|
href={`https://${process.env.NEXT_PUBLIC_EXPLORER_URL}/${submitRes.tx_json?.hash}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
{submitRes.validated_ledger_index}
|
{submitRes.tx_json?.hash}
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
@@ -224,8 +216,7 @@ export const deleteHook = async (account: IAccount & { name?: string }) => {
|
|||||||
const keypair = derive.familySeed(account.secret);
|
const keypair = derive.familySeed(account.secret);
|
||||||
try {
|
try {
|
||||||
// Update tx Fee value with network estimation
|
// Update tx Fee value with network estimation
|
||||||
const res = await estimateFee(tx, account);
|
await estimateFee(tx, keypair);
|
||||||
tx["Fee"] = res?.base_fee ? res?.base_fee : "1000";
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// use default value what you defined earlier
|
// use default value what you defined earlier
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
|||||||
@@ -1,24 +1,19 @@
|
|||||||
import { derive, sign } from "xrpl-accountlib"
|
import { sign, XRPL_Account } from "xrpl-accountlib"
|
||||||
import state, { IAccount } from "../state"
|
import state from "../state"
|
||||||
|
|
||||||
const estimateFee = async (tx: Record<string, unknown>, account: IAccount): Promise<null | { base_fee: string, median_fee: string; minimum_fee: string; open_ledger_fee: string; }> => {
|
// 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))
|
const copyTx = JSON.parse(JSON.stringify(tx))
|
||||||
delete copyTx['SigningPubKey']
|
delete copyTx['SigningPubKey']
|
||||||
if (!copyTx.Fee) {
|
|
||||||
copyTx.Fee = '1000'
|
|
||||||
}
|
|
||||||
const keypair = derive.familySeed(account.secret)
|
|
||||||
const { signedTransaction } = sign(copyTx, keypair);
|
const { signedTransaction } = sign(copyTx, keypair);
|
||||||
try {
|
try {
|
||||||
const res = await state.client?.send({ command: 'fee', tx_blob: signedTransaction })
|
const res = await state.client?.send({ command: 'fee', tx_blob: signedTransaction })
|
||||||
if (res && res.drops) {
|
if (res && res.drops) {
|
||||||
return res.drops;
|
return tx['Fee'] = res.drops.base_fee;
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
throw Error('Cannot estimate fee')
|
||||||
return null
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user