Compare commits

..

1 Commits

Author SHA1 Message Date
muzam1l
704ebe4b92 Show tx hash instead of server ledger index in deploy log. 2022-05-26 14:55:24 +05:30
5 changed files with 12274 additions and 325 deletions

View File

@@ -469,7 +469,7 @@ const Accounts: FC<AccountProps> = (props) => {
e.stopPropagation(); e.stopPropagation();
}} }}
> >
<SetHookDialog accountAddress={account.address} /> <SetHookDialog account={account} />
</div> </div>
)} )}
</Flex> </Flex>

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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);

View File

@@ -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
} }
} }