Compare commits
22 Commits
feat/fee-e
...
feat/defau
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a11f83fea | ||
|
|
ea977816a4 | ||
|
|
0ee599a2b6 | ||
|
|
02c59f8d79 | ||
|
|
3d5b77e60a | ||
|
|
d41e263942 | ||
|
|
bd1226fe90 | ||
|
|
57403e42dd | ||
|
|
2b42a96c4a | ||
|
|
c7e4cd7c92 | ||
|
|
4a22861860 | ||
|
|
b09d029931 | ||
|
|
b2dc49754f | ||
|
|
6f636645f7 | ||
|
|
377c963c7a | ||
|
|
ae038f17ff | ||
|
|
0d8f2c31e7 | ||
|
|
da9986eb66 | ||
|
|
a21350770e | ||
|
|
49dfd43220 | ||
|
|
ca46707bb5 | ||
|
|
704ebe4b92 |
@@ -14,6 +14,8 @@ import Button from "../Button";
|
||||
import Flex from "../Flex";
|
||||
import { TxJson } from "./json";
|
||||
import { TxUI } from "./ui";
|
||||
import { default as _estimateFee } from "../../utils/estimateFee";
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
export interface TransactionProps {
|
||||
header: string;
|
||||
@@ -76,13 +78,19 @@ 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;
|
||||
const tt = txState.selectedTransaction?.value;
|
||||
if (viewType === "json") {
|
||||
// save the editor state first
|
||||
const pst = prepareState(editorValue || '', txState);
|
||||
const pst = prepareState(editorValue || "", tt);
|
||||
if (!pst) return;
|
||||
|
||||
st = setState(pst);
|
||||
@@ -102,7 +110,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 +124,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 +147,31 @@ const Transaction: FC<TransactionProps> = ({
|
||||
[editorSavedValue, editorSettings.tabSize, prepareOptions]
|
||||
);
|
||||
|
||||
const estimateFee = useCallback(
|
||||
async (st?: TransactionState, opts?: { silent?: boolean }) => {
|
||||
const state = st || txState;
|
||||
const ptx = prepareOptions(state);
|
||||
const account = accounts.find(
|
||||
acc => acc.address === state.selectedAccount?.value
|
||||
);
|
||||
if (!account) {
|
||||
if (!opts?.silent) {
|
||||
toast.error("Please select account from the list.")
|
||||
}
|
||||
return
|
||||
};
|
||||
|
||||
ptx.Account = account.address;
|
||||
ptx.Sequence = account.sequence;
|
||||
|
||||
const res = await _estimateFee(ptx, account, opts);
|
||||
const fee = res?.base_fee;
|
||||
setState({ estimatedFee: fee });
|
||||
return fee;
|
||||
},
|
||||
[accounts, prepareOptions, setState, txState]
|
||||
);
|
||||
|
||||
return (
|
||||
<Box css={{ position: "relative", height: "calc(100% - 28px)" }} {...props}>
|
||||
{viewType === "json" ? (
|
||||
@@ -137,9 +180,10 @@ const Transaction: FC<TransactionProps> = ({
|
||||
header={header}
|
||||
state={txState}
|
||||
setState={setState}
|
||||
estimateFee={estimateFee}
|
||||
/>
|
||||
) : (
|
||||
<TxUI state={txState} setState={setState} />
|
||||
<TxUI state={txState} setState={setState} estimateFee={estimateFee} />
|
||||
)}
|
||||
<Flex
|
||||
row
|
||||
|
||||
@@ -29,6 +29,7 @@ interface JsonProps {
|
||||
header?: string;
|
||||
setState: (pTx?: Partial<TransactionState> | undefined) => void;
|
||||
state: TransactionState;
|
||||
estimateFee?: () => Promise<string | undefined>;
|
||||
}
|
||||
|
||||
export const TxJson: FC<JsonProps> = ({
|
||||
@@ -38,22 +39,37 @@ export const TxJson: FC<JsonProps> = ({
|
||||
setState,
|
||||
}) => {
|
||||
const { editorSettings, accounts } = useSnapshot(state);
|
||||
const { editorValue = value, selectedTransaction } = txState;
|
||||
const { editorValue = value, estimatedFee } = txState;
|
||||
const { theme } = useTheme();
|
||||
const [hasUnsaved, setHasUnsaved] = useState(false);
|
||||
const [currTxType, setCurrTxType] = useState<string | undefined>(
|
||||
txState.selectedTransaction?.value
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setState({ editorValue: value });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [value]);
|
||||
|
||||
useEffect(() => {
|
||||
const parsed = parseJSON(editorValue);
|
||||
if (!parsed) return;
|
||||
|
||||
const tt = parsed.TransactionType;
|
||||
const tx = transactionsData.find(t => t.TransactionType === tt);
|
||||
if (tx) setCurrTxType(tx.TransactionType);
|
||||
else {
|
||||
setCurrTxType(undefined);
|
||||
}
|
||||
}, [editorValue]);
|
||||
|
||||
useEffect(() => {
|
||||
if (editorValue === value) setHasUnsaved(false);
|
||||
else setHasUnsaved(true);
|
||||
}, [editorValue, value]);
|
||||
|
||||
const saveState = (value: string, txState: TransactionState) => {
|
||||
const tx = prepareState(value, txState);
|
||||
const saveState = (value: string, transactionType?: string) => {
|
||||
const tx = prepareState(value, transactionType);
|
||||
if (tx) setState(tx);
|
||||
};
|
||||
|
||||
@@ -68,7 +84,7 @@ export const TxJson: FC<JsonProps> = ({
|
||||
const onExit = (value: string) => {
|
||||
const options = parseJSON(value);
|
||||
if (options) {
|
||||
saveState(value, txState);
|
||||
saveState(value, currTxType);
|
||||
return;
|
||||
}
|
||||
showAlert("Error!", {
|
||||
@@ -82,9 +98,10 @@ export const TxJson: FC<JsonProps> = ({
|
||||
const path = `file:///${header}`;
|
||||
const monaco = useMonaco();
|
||||
|
||||
const getSchemas = useCallback((): any[] => {
|
||||
const tt = selectedTransaction?.value;
|
||||
const txObj = transactionsData.find(td => td.TransactionType === tt);
|
||||
const getSchemas = useCallback(async (): Promise<any[]> => {
|
||||
const txObj = transactionsData.find(
|
||||
td => td.TransactionType === currTxType
|
||||
);
|
||||
|
||||
let genericSchemaProps: any;
|
||||
if (txObj) {
|
||||
@@ -98,7 +115,6 @@ export const TxJson: FC<JsonProps> = ({
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
uri: "file:///main-schema.json", // id of the first schema
|
||||
@@ -130,6 +146,9 @@ export const TxJson: FC<JsonProps> = ({
|
||||
Amount: {
|
||||
$ref: "file:///amount-schema.json",
|
||||
},
|
||||
Fee: {
|
||||
$ref: "file:///fee-schema.json",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -141,17 +160,30 @@ 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: estimatedFee
|
||||
? "Above mentioned value is recommended base fee"
|
||||
: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
...amountSchema,
|
||||
},
|
||||
];
|
||||
}, [accounts, header, selectedTransaction?.value]);
|
||||
}, [accounts, currTxType, estimatedFee, header]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!monaco) return;
|
||||
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
|
||||
validate: true,
|
||||
schemas: getSchemas(),
|
||||
getSchemas().then(schemas => {
|
||||
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
|
||||
validate: true,
|
||||
schemas,
|
||||
});
|
||||
});
|
||||
}, [getSchemas, monaco]);
|
||||
|
||||
@@ -184,19 +216,13 @@ export const TxJson: FC<JsonProps> = ({
|
||||
// register onExit cb
|
||||
const model = editor.getModel();
|
||||
model?.onWillDispose(() => onExit(model.getValue()));
|
||||
|
||||
// set json defaults
|
||||
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
|
||||
validate: true,
|
||||
schemas: getSchemas(),
|
||||
});
|
||||
}}
|
||||
theme={theme === "dark" ? "dark" : "light"}
|
||||
/>
|
||||
{hasUnsaved && (
|
||||
<Text muted small css={{ position: "absolute", bottom: 0, right: 0 }}>
|
||||
This file has unsaved changes.{" "}
|
||||
<Link onClick={() => saveState(editorValue, txState)}>save</Link>{" "}
|
||||
<Link onClick={() => saveState(editorValue, currTxType)}>save</Link>{" "}
|
||||
<Link onClick={discardChanges}>discard</Link>
|
||||
</Text>
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FC } from "react";
|
||||
import { FC, useCallback, useEffect, useState } from "react";
|
||||
import Container from "../Container";
|
||||
import Flex from "../Flex";
|
||||
import Input from "../Input";
|
||||
@@ -9,17 +9,26 @@ import {
|
||||
TransactionState,
|
||||
transactionsData,
|
||||
TxFields,
|
||||
getTxFields,
|
||||
} from "../../state/transactions";
|
||||
import { useSnapshot } from "valtio";
|
||||
import state from "../../state";
|
||||
import { streamState } from "../DebugStream";
|
||||
import { Button } from "..";
|
||||
|
||||
interface UIProps {
|
||||
setState: (pTx?: Partial<TransactionState> | undefined) => void;
|
||||
setState: (
|
||||
pTx?: Partial<TransactionState> | undefined
|
||||
) => TransactionState | undefined;
|
||||
state: TransactionState;
|
||||
estimateFee?: (...arg: any) => Promise<string | undefined>;
|
||||
}
|
||||
|
||||
export const TxUI: FC<UIProps> = ({ state: txState, setState }) => {
|
||||
export const TxUI: FC<UIProps> = ({
|
||||
state: txState,
|
||||
setState,
|
||||
estimateFee,
|
||||
}) => {
|
||||
const { accounts } = useSnapshot(state);
|
||||
const {
|
||||
selectedAccount,
|
||||
@@ -45,32 +54,54 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState }) => {
|
||||
}))
|
||||
.filter(acc => acc.value !== selectedAccount?.value);
|
||||
|
||||
const resetOptions = (tt: string) => {
|
||||
const txFields: TxFields | undefined = transactionsData.find(
|
||||
tx => tx.TransactionType === tt
|
||||
);
|
||||
const [feeLoading, setFeeLoading] = useState(false);
|
||||
|
||||
if (!txFields) return setState({ txFields: {} });
|
||||
|
||||
const _txFields = Object.keys(txFields)
|
||||
.filter(key => !["TransactionType", "Account", "Sequence"].includes(key))
|
||||
.reduce<TxFields>(
|
||||
(tf, key) => ((tf[key as keyof TxFields] = (txFields as any)[key]), tf),
|
||||
{}
|
||||
);
|
||||
|
||||
if (!_txFields.Destination) setState({ selectedDestAccount: null });
|
||||
setState({ txFields: _txFields });
|
||||
};
|
||||
const resetOptions = useCallback(
|
||||
(tt: string) => {
|
||||
const fields = getTxFields(tt);
|
||||
if (!fields.Destination) setState({ selectedDestAccount: null });
|
||||
return setState({ txFields: fields });
|
||||
},
|
||||
[setState]
|
||||
);
|
||||
|
||||
const handleSetAccount = (acc: SelectOption) => {
|
||||
setState({ selectedAccount: acc });
|
||||
streamState.selectedAccount = acc;
|
||||
};
|
||||
|
||||
const handleSetField = useCallback(
|
||||
(field: keyof TxFields, value: string, opFields?: TxFields) => {
|
||||
const fields = opFields || txFields;
|
||||
const obj = fields[field];
|
||||
setState({
|
||||
txFields: {
|
||||
...fields,
|
||||
[field]: typeof obj === "object" ? { ...obj, $value: value } : value,
|
||||
},
|
||||
});
|
||||
},
|
||||
[setState, txFields]
|
||||
);
|
||||
|
||||
const handleEstimateFee = useCallback(
|
||||
async (state?: TransactionState, silent?: boolean) => {
|
||||
setFeeLoading(true);
|
||||
|
||||
const fee = await estimateFee?.(state, { silent });
|
||||
if (fee) handleSetField("Fee", fee, state?.txFields);
|
||||
|
||||
setFeeLoading(false);
|
||||
},
|
||||
[estimateFee, handleSetField]
|
||||
);
|
||||
|
||||
const handleChangeTxType = (tt: SelectOption) => {
|
||||
setState({ selectedTransaction: tt });
|
||||
resetOptions(tt.value);
|
||||
|
||||
const newState = resetOptions(tt.value);
|
||||
|
||||
handleEstimateFee(newState, true);
|
||||
};
|
||||
|
||||
const specialFields = ["TransactionType", "Account", "Destination"];
|
||||
@@ -79,6 +110,16 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState }) => {
|
||||
k => !specialFields.includes(k)
|
||||
) as [keyof TxFields];
|
||||
|
||||
useEffect(() => {
|
||||
const defaultOption = transactionsOptions.find(
|
||||
tt => tt.value === "Payment"
|
||||
);
|
||||
if (defaultOption) {
|
||||
handleChangeTxType(defaultOption);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Container
|
||||
css={{
|
||||
@@ -87,7 +128,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 +215,49 @@ export const TxUI: FC<UIProps> = ({ state: txState, setState }) => {
|
||||
}
|
||||
|
||||
let isXrp = typeof _value === "object" && _value.$type === "xrp";
|
||||
|
||||
const isFee = field === "Fee";
|
||||
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",
|
||||
position: "relative",
|
||||
}}
|
||||
css={{ width: "70%", flex: "inherit" }}
|
||||
/>
|
||||
>
|
||||
<Text muted css={{ mr: "$3" }}>
|
||||
{field + (isXrp ? " (XRP)" : "")}:{" "}
|
||||
</Text>
|
||||
<Input
|
||||
value={value}
|
||||
onChange={e => {
|
||||
handleSetField(field, e.target.value);
|
||||
}}
|
||||
css={{ width: "70%", flex: "inherit" }}
|
||||
/>
|
||||
{isFee && (
|
||||
<Button
|
||||
size="xs"
|
||||
variant="primary"
|
||||
outline
|
||||
isLoading={feeLoading}
|
||||
css={{
|
||||
position: "absolute",
|
||||
right: "$2",
|
||||
fontSize: "$xs",
|
||||
cursor: "pointer",
|
||||
alignContent: "center",
|
||||
display: "flex",
|
||||
}}
|
||||
onClick={() => handleEstimateFee()}
|
||||
>
|
||||
Suggest
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Label } from "@radix-ui/react-label";
|
||||
import { Switch, SwitchThumb } from "../../components/Switch";
|
||||
import type { NextPage } from "next";
|
||||
import dynamic from "next/dynamic";
|
||||
import { Gear, Play } from "phosphor-react";
|
||||
@@ -141,59 +140,6 @@ const CompilerSettings = () => {
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Box>
|
||||
<Box css={{ flexDirection: "column" }}>
|
||||
<Label
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
display: "flex",
|
||||
}}
|
||||
>
|
||||
Clean WASM (experimental){" "}
|
||||
<Popover
|
||||
css={{
|
||||
maxWidth: "240px",
|
||||
lineHeight: "1.3",
|
||||
a: {
|
||||
color: "$purple11",
|
||||
},
|
||||
".dark &": {
|
||||
backgroundColor: "$black !important",
|
||||
|
||||
".arrow": {
|
||||
fill: "$colors$black",
|
||||
},
|
||||
},
|
||||
}}
|
||||
content="Cleaner removes unwanted compiler-provided exports and functions from a wasm binary to make it (more) suitable for being used as a Hook"
|
||||
>
|
||||
<Flex
|
||||
css={{
|
||||
position: "relative",
|
||||
top: "-1px",
|
||||
mx: "$1",
|
||||
backgroundColor: "$mauve8",
|
||||
borderRadius: "$full",
|
||||
cursor: "pointer",
|
||||
width: "16px",
|
||||
height: "16px",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
?
|
||||
</Flex>
|
||||
</Popover>
|
||||
</Label>
|
||||
<Switch
|
||||
css={{ mt: "$2" }}
|
||||
checked={snap.compileOptions.strip}
|
||||
onCheckedChange={(checked) => {
|
||||
state.compileOptions.strip = checked;
|
||||
}}
|
||||
>
|
||||
<SwitchThumb />
|
||||
</Switch>
|
||||
</Box>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
@@ -213,7 +159,7 @@ const Home: NextPage = () => {
|
||||
>
|
||||
<main style={{ display: "flex", flex: 1, position: "relative" }}>
|
||||
<HooksEditor />
|
||||
{snap.files[snap.active]?.name?.split(".")?.[1].toLowerCase() ===
|
||||
{snap.files[snap.active]?.name?.split(".")?.[1]?.toLowerCase() ===
|
||||
"c" && (
|
||||
<Hotkeys
|
||||
keyName="command+b,ctrl+b"
|
||||
|
||||
@@ -160,14 +160,14 @@ export const deployHook = async (
|
||||
message: ref(
|
||||
<>
|
||||
[{submitRes.engine_result}] {submitRes.engine_result_message}{" "}
|
||||
Validated ledger index:{" "}
|
||||
Transaction hash:{" "}
|
||||
<Link
|
||||
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"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{submitRes.validated_ledger_index}
|
||||
{submitRes.tx_json?.hash}
|
||||
</Link>
|
||||
</>
|
||||
),
|
||||
|
||||
@@ -58,6 +58,29 @@ export const fetchFiles = (gistId: string) => {
|
||||
language: res.data.files?.[filename]?.language?.toLowerCase() || "",
|
||||
content: res.data.files?.[filename]?.content || "",
|
||||
}));
|
||||
// Sort files so that the source files are first
|
||||
// In case of other files leave the order as it its
|
||||
files.sort((a, b) => {
|
||||
const aBasename = a.name.split('.')?.[0];
|
||||
const aCext = a.name?.toLowerCase().endsWith('.c');
|
||||
const bBasename = b.name.split('.')?.[0];
|
||||
const bCext = b.name?.toLowerCase().endsWith('.c');
|
||||
// If a has c extension and b doesn't move a up
|
||||
if (aCext && !bCext) {
|
||||
return -1;
|
||||
}
|
||||
if (!aCext && bCext) {
|
||||
return 1
|
||||
}
|
||||
// Otherwise fallback to default sorting based on basename
|
||||
if (aBasename > bBasename) {
|
||||
return 1;
|
||||
}
|
||||
if (bBasename > aBasename) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
})
|
||||
state.loading = false;
|
||||
if (files.length > 0) {
|
||||
state.logs.push({
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -19,7 +19,8 @@ export interface TransactionState {
|
||||
txFields: TxFields;
|
||||
viewType: 'json' | 'ui',
|
||||
editorSavedValue: null | string,
|
||||
editorValue?: string
|
||||
editorValue?: string,
|
||||
estimatedFee?: string
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +94,7 @@ export const modifyTransaction = (
|
||||
Object.keys(partialTx).forEach(k => {
|
||||
// Typescript mess here, but is definetly safe!
|
||||
const s = tx.state as any;
|
||||
const p = partialTx as any;
|
||||
const p = partialTx as any; // ? Make copy
|
||||
if (!deepEqual(s[k], p[k])) s[k] = p[k];
|
||||
});
|
||||
|
||||
@@ -140,7 +141,7 @@ export const prepareTransaction = (data: any) => {
|
||||
}
|
||||
|
||||
// editor value to state
|
||||
export const prepareState = (value: string, txState: TransactionState) => {
|
||||
export const prepareState = (value: string, transactionType?: string) => {
|
||||
const options = parseJSON(value);
|
||||
if (!options) {
|
||||
showAlert("Error!", {
|
||||
@@ -151,7 +152,7 @@ export const prepareState = (value: string, txState: TransactionState) => {
|
||||
|
||||
const { Account, TransactionType, Destination, ...rest } = options;
|
||||
let tx: Partial<TransactionState> = {};
|
||||
const { txFields } = txState
|
||||
const txFields = getTxFields(transactionType)
|
||||
|
||||
if (Account) {
|
||||
const acc = state.accounts.find(acc => acc.address === Account);
|
||||
@@ -206,7 +207,7 @@ export const prepareState = (value: string, txState: TransactionState) => {
|
||||
if (isXrp) {
|
||||
rest[field] = {
|
||||
$type: "xrp",
|
||||
$value: +value / 1000000, // TODO maybe use bigint?
|
||||
$value: +value / 1000000, // ! maybe use bigint?
|
||||
};
|
||||
} else if (typeof value === "object") {
|
||||
rest[field] = {
|
||||
@@ -222,4 +223,24 @@ export const prepareState = (value: string, txState: TransactionState) => {
|
||||
return tx
|
||||
}
|
||||
|
||||
export const getTxFields = (tt?: string) => {
|
||||
const txFields: TxFields | undefined = transactionsData.find(
|
||||
tx => tx.TransactionType === tt
|
||||
);
|
||||
|
||||
if (!txFields) return {}
|
||||
|
||||
let _txFields = Object.keys(txFields)
|
||||
.filter(
|
||||
key => !["TransactionType", "Account", "Sequence"].includes(key)
|
||||
)
|
||||
.reduce<TxFields>(
|
||||
(tf, key) => (
|
||||
(tf[key as keyof TxFields] = (txFields as any)[key]), tf
|
||||
),
|
||||
{}
|
||||
);
|
||||
return _txFields
|
||||
}
|
||||
|
||||
export { transactionsData }
|
||||
|
||||
@@ -1,24 +1,29 @@
|
||||
import toast from 'react-hot-toast';
|
||||
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 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);
|
||||
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; }> => {
|
||||
try {
|
||||
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);
|
||||
|
||||
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.error(err)
|
||||
toast.error("Cannot estimate fee.") // ? Some better msg
|
||||
}
|
||||
return null
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ import hooksSkipHashBufLen from "./md/hooks-skip-hash-buf-len.md";
|
||||
import hooksStateBufLen from "./md/hooks-state-buf-len.md";
|
||||
import hooksTransactionHashBufLen from "./md/hooks-transaction-hash-buf-len.md";
|
||||
import hooksTransactionSlotLimit from "./md/hooks-transaction-slot-limit.md";
|
||||
import hooksTrivialCbak from "./md/hooks-trivial-cbak.md";
|
||||
import hooksValidateBufLen from "./md/hooks-validate-buf-len.md";
|
||||
import hooksVerifyBufLen from "./md/hooks-verify-buf-len.md";
|
||||
|
||||
@@ -90,6 +91,7 @@ const docs: { [key: string]: string; } = {
|
||||
"hooks-state-buf-len": hooksStateBufLen,
|
||||
"hooks-transaction-hash-buf-len": hooksTransactionHashBufLen,
|
||||
"hooks-transaction-slot-limit": hooksTransactionSlotLimit,
|
||||
"hooks-trivial-cbak": hooksTrivialCbak,
|
||||
"hooks-validate-buf-len": hooksValidateBufLen,
|
||||
"hooks-verify-buf-len": hooksVerifyBufLen,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# hooks-entry-points
|
||||
|
||||
A Hook always implements and exports exactly two functions: [cbak](https://xrpl-hooks.readme.io/v2.0/reference/cbak) and [hook](https://xrpl-hooks.readme.io/v2.0/reference/hook).
|
||||
A Hook always implements and exports a [hook](https://xrpl-hooks.readme.io/v2.0/reference/hook) function.
|
||||
|
||||
This check shows error on translation units that do not have them.
|
||||
This check shows error on translation units that do not have it.
|
||||
|
||||
[Read more](https://xrpl-hooks.readme.io/v2.0/docs/compiling-hooks)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# hooks-hash-buf-len
|
||||
|
||||
Functions [util_sha512h](https://xrpl-hooks.readme.io/v2.0/reference/util_sha512h), [hook_hash](https://xrpl-hooks.readme.io/v2.0/reference/hook_hash), [ledger_last_hash](https://xrpl-hooks.readme.io/v2.0/reference/ledger_last_hash) and [nonce](https://xrpl-hooks.readme.io/v2.0/reference/nonce) have fixed-size hash output.
|
||||
Functions [util_sha512h](https://xrpl-hooks.readme.io/v2.0/reference/util_sha512h), [hook_hash](https://xrpl-hooks.readme.io/v2.0/reference/hook_hash), [ledger_last_hash](https://xrpl-hooks.readme.io/v2.0/reference/ledger_last_hash), [etxn_nonce](https://xrpl-hooks.readme.io/v2.0/reference/etxn_nonce) and [ledger_nonce](https://xrpl-hooks.readme.io/v2.0/reference/ledger_nonce) have fixed-size hash output.
|
||||
|
||||
This check warns about too-small size of their output buffer (if it's specified by a constant - variable parameter is ignored).
|
||||
|
||||
7
xrpl-hooks-docs/md/hooks-trivial-cbak.md
Normal file
7
xrpl-hooks-docs/md/hooks-trivial-cbak.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# hooks-trivial-cbak
|
||||
|
||||
A Hook may implement and export a [cbak](https://xrpl-hooks.readme.io/v2.0/reference/cbak) function.
|
||||
|
||||
But the function is optional, and defining it so that it doesn't do anything besides returning a constant value is unnecessary (except for some debugging scenarios) and just increases the hook size. This check warns about such implementations.
|
||||
|
||||
[Read more](https://xrpl-hooks.readme.io/v2.0/docs/compiling-hooks)
|
||||
Reference in New Issue
Block a user