Fee hints in transactions.

This commit is contained in:
muzam1l
2022-05-27 16:44:01 +05:30
parent 49dfd43220
commit a21350770e
5 changed files with 111 additions and 45 deletions

View File

@@ -1,5 +1,5 @@
import { Play } from "phosphor-react";
import { FC, useCallback, useEffect, useMemo } from "react";
import { FC, useCallback, useEffect, useMemo, useState } from "react";
import { useSnapshot } from "valtio";
import state from "../../state";
import {
@@ -14,6 +14,7 @@ import Button from "../Button";
import Flex from "../Flex";
import { TxJson } from "./json";
import { TxUI } from "./ui";
import estimateFee from "../../utils/estimateFee";
export interface TransactionProps {
header: string;
@@ -76,13 +77,18 @@ const Transaction: FC<TransactionProps> = ({
} else {
setState({ txIsDisabled: false });
}
}, [selectedAccount?.value, selectedTransaction?.value, setState, txIsLoading]);
}, [
selectedAccount?.value,
selectedTransaction?.value,
setState,
txIsLoading,
]);
const submitTest = useCallback(async () => {
let st: TransactionState | undefined;
if (viewType === "json") {
// save the editor state first
const pst = prepareState(editorValue || '', txState);
const pst = prepareState(editorValue || "", txState);
if (!pst) return;
st = setState(pst);
@@ -102,7 +108,7 @@ const Transaction: FC<TransactionProps> = ({
const options = prepareOptions(st);
if (options.Destination === null) {
throw Error("Destination account cannot be null")
throw Error("Destination account cannot be null");
}
await sendTransaction(account, options, { logPrefix });
@@ -116,7 +122,17 @@ const Transaction: FC<TransactionProps> = ({
}
}
setState({ txIsLoading: false });
}, [viewType, accounts, txIsDisabled, setState, header, editorValue, txState, selectedAccount?.value, prepareOptions]);
}, [
viewType,
accounts,
txIsDisabled,
setState,
header,
editorValue,
txState,
selectedAccount?.value,
prepareOptions,
]);
const resetState = useCallback(() => {
modifyTransaction(header, { viewType }, { replaceState: true });
@@ -129,6 +145,25 @@ const Transaction: FC<TransactionProps> = ({
[editorSavedValue, editorSettings.tabSize, prepareOptions]
);
const [estimatedFee, setEstimatedFee] = useState<string>();
useEffect(() => {
const ptx = prepareOptions(txState);
const account = accounts.find(
acc => acc.address === selectedAccount?.value
);
if (!account) return;
ptx.Account = account.address;
ptx.Sequence = account.sequence;
console.log("estimating fee...");
estimateFee(ptx, account, { silent: true })
.then(res => res?.base_fee)
.then(fee => {
setEstimatedFee(fee)
});
}, [accounts, prepareOptions, selectedAccount?.value, txState]);
return (
<Box css={{ position: "relative", height: "calc(100% - 28px)" }} {...props}>
{viewType === "json" ? (
@@ -137,9 +172,10 @@ const Transaction: FC<TransactionProps> = ({
header={header}
state={txState}
setState={setState}
estimatedFee={estimatedFee}
/>
) : (
<TxUI state={txState} setState={setState} />
<TxUI state={txState} setState={setState} estimatedFee={estimatedFee} />
)}
<Flex
row

View File

@@ -29,6 +29,7 @@ interface JsonProps {
header?: string;
setState: (pTx?: Partial<TransactionState> | undefined) => void;
state: TransactionState;
estimatedFee?: string
}
export const TxJson: FC<JsonProps> = ({
@@ -36,6 +37,7 @@ export const TxJson: FC<JsonProps> = ({
state: txState,
header,
setState,
estimatedFee
}) => {
const { editorSettings, accounts } = useSnapshot(state);
const { editorValue = value, selectedTransaction } = txState;
@@ -98,7 +100,7 @@ export const TxJson: FC<JsonProps> = ({
{}
);
}
console.log({ estimatedFee})
return [
{
uri: "file:///main-schema.json", // id of the first schema
@@ -130,6 +132,9 @@ export const TxJson: FC<JsonProps> = ({
Amount: {
$ref: "file:///amount-schema.json",
},
Fee: {
$ref: "file:///fee-schema.json",
},
},
},
},
@@ -141,11 +146,20 @@ export const TxJson: FC<JsonProps> = ({
enum: accounts.map(acc => acc.address),
},
},
{
uri: "file:///fee-schema.json",
schema: {
type: "string",
title: "Fee type",
const: estimatedFee,
description: "Above mentioned value is recommended base fee",
},
},
{
...amountSchema,
},
];
}, [accounts, header, selectedTransaction?.value]);
}, [accounts, estimatedFee, header, selectedTransaction?.value]);
useEffect(() => {
if (!monaco) return;

View File

@@ -13,13 +13,19 @@ import {
import { useSnapshot } from "valtio";
import state from "../../state";
import { streamState } from "../DebugStream";
import { Label } from "..";
interface UIProps {
setState: (pTx?: Partial<TransactionState> | undefined) => void;
state: TransactionState;
estimatedFee?: string;
}
export const TxUI: FC<UIProps> = ({ state: txState, setState }) => {
export const TxUI: FC<UIProps> = ({
state: txState,
setState,
estimatedFee,
}) => {
const { accounts } = useSnapshot(state);
const {
selectedAccount,
@@ -87,7 +93,7 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState }) => {
height: "calc(100% - 45px)",
}}
>
<Flex column fluid css={{ height: "100%", overflowY: "auto" }}>
<Flex column fluid css={{ height: "100%", overflowY: "auto", pr: "$1" }}>
<Flex
row
fluid
@@ -174,36 +180,45 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState }) => {
}
let isXrp = typeof _value === "object" && _value.$type === "xrp";
const hint =
field === "Fee" && estimatedFee
? `Suggested Fee: ${estimatedFee}`
: undefined;
return (
<Flex
key={field}
row
fluid
css={{
justifyContent: "flex-end",
alignItems: "center",
mb: "$3",
pr: "1px",
}}
>
<Text muted css={{ mr: "$3" }}>
{field + (isXrp ? " (XRP)" : "")}:{" "}
</Text>
<Input
value={value}
onChange={e => {
setState({
txFields: {
...txFields,
[field]:
typeof _value === "object"
? { ..._value, $value: e.target.value }
: e.target.value,
},
});
<Flex column key={field} css={{ mb: "$2", pr: "1px" }}>
<Flex
row
fluid
css={{
justifyContent: "flex-end",
alignItems: "center",
}}
css={{ width: "70%", flex: "inherit" }}
/>
>
<Text muted css={{ mr: "$3" }}>
{field + (isXrp ? " (XRP)" : "")}:{" "}
</Text>
<Input
value={value}
onChange={e => {
setState({
txFields: {
...txFields,
[field]:
typeof _value === "object"
? { ..._value, $value: e.target.value }
: e.target.value,
},
});
}}
css={{ width: "70%", flex: "inherit" }}
/>
</Flex>
<Label
css={{ color: "$success", textAlign: "right", mt: "$1", mb: 0, fontSize: "$sm" }}
>
{hint}
</Label>
</Flex>
);
})}

View File

@@ -20,8 +20,8 @@ export const sendTransaction = async (account: IAccount, txOptions: TransactionO
const { Fee = "1000", ...opts } = txOptions
const tx: TransactionOptions = {
Account: account.address,
Sequence: account.sequence, // TODO auto-fillable
Fee, // TODO auto-fillable
Sequence: account.sequence,
Fee, // TODO auto-fillable default
...opts
};

View File

@@ -1,24 +1,25 @@
import { derive, sign } from "xrpl-accountlib"
import state, { IAccount } 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; }> => {
const estimateFee = async (tx: Record<string, unknown>, account: IAccount, opts: { silent?: boolean } = {}): Promise<null | { base_fee: string, median_fee: string; minimum_fee: string; open_ledger_fee: string; }> => {
const copyTx = JSON.parse(JSON.stringify(tx))
delete copyTx['SigningPubKey']
if (!copyTx.Fee) {
copyTx.Fee = '1000'
}
const keypair = derive.familySeed(account.secret)
const { signedTransaction } = sign(copyTx, keypair);
try {
const keypair = derive.familySeed(account.secret)
const { signedTransaction } = sign(copyTx, keypair);
const res = await state.client?.send({ command: 'fee', tx_blob: signedTransaction })
if (res && res.drops) {
return res.drops;
}
return null
} catch (err) {
console.log(err)
if (!opts.silent)
console.log(err)
return null
}
}