Fee hints in transactions.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,16 +180,19 @@ 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 column key={field} css={{ mb: "$2", pr: "1px" }}>
|
||||
<Flex
|
||||
key={field}
|
||||
row
|
||||
fluid
|
||||
css={{
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
mb: "$3",
|
||||
pr: "1px",
|
||||
}}
|
||||
>
|
||||
<Text muted css={{ mr: "$3" }}>
|
||||
@@ -205,6 +214,12 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState }) => {
|
||||
css={{ width: "70%", flex: "inherit" }}
|
||||
/>
|
||||
</Flex>
|
||||
<Label
|
||||
css={{ color: "$success", textAlign: "right", mt: "$1", mb: 0, fontSize: "$sm" }}
|
||||
>
|
||||
{hint}
|
||||
</Label>
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
</Flex>
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
try {
|
||||
const keypair = derive.familySeed(account.secret)
|
||||
const { signedTransaction } = sign(copyTx, keypair);
|
||||
try {
|
||||
|
||||
const res = await state.client?.send({ command: 'fee', tx_blob: signedTransaction })
|
||||
if (res && res.drops) {
|
||||
return res.drops;
|
||||
}
|
||||
return null
|
||||
} catch (err) {
|
||||
if (!opts.silent)
|
||||
console.log(err)
|
||||
return null
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user