Linkify engine error codes and some refactor.
This commit is contained in:
@@ -5,6 +5,7 @@ import { subscribeKey } from "valtio/utils";
|
|||||||
import { Select } from ".";
|
import { Select } from ".";
|
||||||
import state, { ILog, transactionsState } from "../state";
|
import state, { ILog, transactionsState } from "../state";
|
||||||
import { extractJSON } from "../utils/json";
|
import { extractJSON } from "../utils/json";
|
||||||
|
import EnrichAccounts from "./EnrichAccounts";
|
||||||
import LogBox from "./LogBox";
|
import LogBox from "./LogBox";
|
||||||
|
|
||||||
interface ISelect<T = string> {
|
interface ISelect<T = string> {
|
||||||
@@ -99,12 +100,17 @@ const addListeners = (account: ISelect | null) => {
|
|||||||
|
|
||||||
subscribeKey(streamState, "selectedAccount", addListeners);
|
subscribeKey(streamState, "selectedAccount", addListeners);
|
||||||
|
|
||||||
|
const clearLog = () => {
|
||||||
|
streamState.logs = [];
|
||||||
|
streamState.statusChangeTimestamp = Date.now();
|
||||||
|
};
|
||||||
|
|
||||||
const DebugStream = () => {
|
const DebugStream = () => {
|
||||||
const { selectedAccount, logs } = useSnapshot(streamState);
|
const { selectedAccount, logs } = useSnapshot(streamState);
|
||||||
const { activeHeader: activeTxTab } = useSnapshot(transactionsState);
|
const { activeHeader: activeTxTab } = useSnapshot(transactionsState);
|
||||||
const { accounts } = useSnapshot(state);
|
const { accounts } = useSnapshot(state);
|
||||||
|
|
||||||
const accountOptions = accounts.map((acc) => ({
|
const accountOptions = accounts.map(acc => ({
|
||||||
label: acc.name,
|
label: acc.name,
|
||||||
value: acc.address,
|
value: acc.address,
|
||||||
}));
|
}));
|
||||||
@@ -117,7 +123,7 @@ const DebugStream = () => {
|
|||||||
options={accountOptions}
|
options={accountOptions}
|
||||||
hideSelectedOptions
|
hideSelectedOptions
|
||||||
value={selectedAccount}
|
value={selectedAccount}
|
||||||
onChange={(acc) => {
|
onChange={acc => {
|
||||||
streamState.socket?.close(
|
streamState.socket?.close(
|
||||||
4999,
|
4999,
|
||||||
"Old connection closed because user switched account"
|
"Old connection closed because user switched account"
|
||||||
@@ -131,18 +137,13 @@ const DebugStream = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const account = transactionsState.transactions.find(
|
const account = transactionsState.transactions.find(
|
||||||
(tx) => tx.header === activeTxTab
|
tx => tx.header === activeTxTab
|
||||||
)?.state.selectedAccount;
|
)?.state.selectedAccount;
|
||||||
|
|
||||||
if (account && account.value !== streamState.selectedAccount?.value)
|
if (account && account.value !== streamState.selectedAccount?.value)
|
||||||
streamState.selectedAccount = account;
|
streamState.selectedAccount = account;
|
||||||
}, [activeTxTab]);
|
}, [activeTxTab]);
|
||||||
|
|
||||||
const clearLog = () => {
|
|
||||||
streamState.logs = [];
|
|
||||||
streamState.statusChangeTimestamp = Date.now();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LogBox
|
<LogBox
|
||||||
enhanced
|
enhanced
|
||||||
@@ -170,13 +171,17 @@ export const pushLog = (
|
|||||||
const timestring = !timestamp ? tm : new Date(timestamp).toLocaleTimeString();
|
const timestring = !timestamp ? tm : new Date(timestamp).toLocaleTimeString();
|
||||||
|
|
||||||
const extracted = extractJSON(msg);
|
const extracted = extractJSON(msg);
|
||||||
const message = !extracted
|
const _message = !extracted
|
||||||
? msg
|
? msg
|
||||||
: msg.slice(0, extracted.start) + msg.slice(extracted.end + 1);
|
: msg.slice(0, extracted.start) + msg.slice(extracted.end + 1);
|
||||||
|
const message = ref(<EnrichAccounts str={_message} />);
|
||||||
|
|
||||||
const jsonData = extracted
|
const _jsonData = extracted
|
||||||
? JSON.stringify(extracted.result, null, 2)
|
? JSON.stringify(extracted.result, null, 2)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
const jsonData = _jsonData
|
||||||
|
? ref(<EnrichAccounts str={_jsonData} />)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
if (extracted?.result?.id?._Request?.includes("hooks-builder-req")) {
|
if (extracted?.result?.id?._Request?.includes("hooks-builder-req")) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
48
components/EnrichAccounts.tsx
Normal file
48
components/EnrichAccounts.tsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { FC, useState } from "react";
|
||||||
|
import regexifyString from "regexify-string";
|
||||||
|
import { useSnapshot } from "valtio";
|
||||||
|
import { Link } from ".";
|
||||||
|
import state from "../state";
|
||||||
|
import { AccountDialog } from "./Accounts";
|
||||||
|
|
||||||
|
interface EnrichAccountsProps {
|
||||||
|
str?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EnrichAccounts: FC<EnrichAccountsProps> = ({ str }) => {
|
||||||
|
const { accounts } = useSnapshot(state);
|
||||||
|
const [dialogAccount, setDialogAccount] = useState<string | null>(null);
|
||||||
|
if (!str || !accounts.length) return <>{str}</>;
|
||||||
|
|
||||||
|
const pattern = `(${accounts.map(acc => acc.address).join("|")})`;
|
||||||
|
const res = regexifyString({
|
||||||
|
pattern: new RegExp(pattern, "gim"),
|
||||||
|
decorator: (match, idx) => {
|
||||||
|
const name = accounts.find(acc => acc.address === match)?.name;
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={match + idx}
|
||||||
|
as="a"
|
||||||
|
onClick={() => setDialogAccount(match)}
|
||||||
|
title={match}
|
||||||
|
highlighted
|
||||||
|
>
|
||||||
|
{name || match}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
input: str,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{res}
|
||||||
|
<AccountDialog
|
||||||
|
setActiveAccountAddress={setDialogAccount}
|
||||||
|
activeAccountAddress={dialogAccount}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EnrichAccounts;
|
||||||
@@ -1,22 +1,12 @@
|
|||||||
import {
|
import { useRef, useLayoutEffect, ReactNode, FC, useState } from "react";
|
||||||
useRef,
|
|
||||||
useLayoutEffect,
|
|
||||||
ReactNode,
|
|
||||||
FC,
|
|
||||||
useState,
|
|
||||||
useCallback,
|
|
||||||
} from "react";
|
|
||||||
import { IconProps, Notepad, Prohibit } from "phosphor-react";
|
import { IconProps, Notepad, Prohibit } from "phosphor-react";
|
||||||
import useStayScrolled from "react-stay-scrolled";
|
import useStayScrolled from "react-stay-scrolled";
|
||||||
import NextLink from "next/link";
|
import NextLink from "next/link";
|
||||||
|
|
||||||
import Container from "./Container";
|
import Container from "./Container";
|
||||||
import LogText from "./LogText";
|
import LogText from "./LogText";
|
||||||
import state, { ILog } from "../state";
|
import { ILog } from "../state";
|
||||||
import { Pre, Link, Heading, Button, Text, Flex, Box } from ".";
|
import { Pre, Link, Heading, Button, Text, Flex, Box } from ".";
|
||||||
import regexifyString from "regexify-string";
|
|
||||||
import { useSnapshot } from "valtio";
|
|
||||||
import { AccountDialog } from "./Accounts";
|
|
||||||
|
|
||||||
interface ILogBox {
|
interface ILogBox {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -150,63 +140,17 @@ const LogBox: FC<ILogBox> = ({
|
|||||||
export const Log: FC<ILog> = ({
|
export const Log: FC<ILog> = ({
|
||||||
type,
|
type,
|
||||||
timestring,
|
timestring,
|
||||||
message: _message,
|
message,
|
||||||
link,
|
link,
|
||||||
linkText,
|
linkText,
|
||||||
defaultCollapsed,
|
defaultCollapsed,
|
||||||
jsonData: _jsonData,
|
jsonData,
|
||||||
}) => {
|
}) => {
|
||||||
const [expanded, setExpanded] = useState(!defaultCollapsed);
|
const [expanded, setExpanded] = useState(!defaultCollapsed);
|
||||||
const { accounts } = useSnapshot(state);
|
|
||||||
const [dialogAccount, setDialogAccount] = useState<string | null>(null);
|
|
||||||
|
|
||||||
const enrichAccounts = useCallback(
|
|
||||||
(str?: string): ReactNode => {
|
|
||||||
if (!str || !accounts.length) return str;
|
|
||||||
|
|
||||||
const pattern = `(${accounts.map(acc => acc.address).join("|")})`;
|
|
||||||
const res = regexifyString({
|
|
||||||
pattern: new RegExp(pattern, "gim"),
|
|
||||||
decorator: (match, idx) => {
|
|
||||||
const name = accounts.find(acc => acc.address === match)?.name;
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
key={match + idx}
|
|
||||||
as="a"
|
|
||||||
onClick={() => setDialogAccount(match)}
|
|
||||||
title={match}
|
|
||||||
highlighted
|
|
||||||
>
|
|
||||||
{name || match}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
input: str,
|
|
||||||
});
|
|
||||||
|
|
||||||
return <>{res}</>;
|
|
||||||
},
|
|
||||||
[accounts]
|
|
||||||
);
|
|
||||||
|
|
||||||
let message: ReactNode;
|
|
||||||
|
|
||||||
if (typeof _message === "string") {
|
|
||||||
_message = _message.trim().replace(/\n /gi, "\n");
|
|
||||||
if (_message) message = enrichAccounts(_message);
|
|
||||||
else message = <Text muted>{'""'}</Text>
|
|
||||||
} else {
|
|
||||||
message = _message;
|
|
||||||
}
|
|
||||||
|
|
||||||
const jsonData = enrichAccounts(_jsonData);
|
|
||||||
|
|
||||||
|
if (!message) message = <Text muted>{'""'}</Text>;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AccountDialog
|
|
||||||
setActiveAccountAddress={setDialogAccount}
|
|
||||||
activeAccountAddress={dialogAccount}
|
|
||||||
/>
|
|
||||||
<LogText variant={type}>
|
<LogText variant={type}>
|
||||||
{timestring && (
|
{timestring && (
|
||||||
<Text muted monospace>
|
<Text muted monospace>
|
||||||
@@ -226,7 +170,6 @@ export const Log: FC<ILog> = ({
|
|||||||
)}
|
)}
|
||||||
{expanded && jsonData && <Pre block>{jsonData}</Pre>}
|
{expanded && jsonData && <Pre block>{jsonData}</Pre>}
|
||||||
</LogText>
|
</LogText>
|
||||||
<br />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
23
components/ResultLink.tsx
Normal file
23
components/ResultLink.tsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { Link } from ".";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
result?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResultLink: FC<Props> = ({ result }) => {
|
||||||
|
if (!result) return null;
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
as="a"
|
||||||
|
title={result}
|
||||||
|
href={"https://xrpl.org/transaction-results.html"}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{result}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ResultLink;
|
||||||
@@ -6,14 +6,15 @@ import calculateHookOn, { TTS } from "../../utils/hookOnCalculator";
|
|||||||
import { Link } from "../../components";
|
import { Link } from "../../components";
|
||||||
import { ref } from "valtio";
|
import { ref } from "valtio";
|
||||||
import estimateFee from "../../utils/estimateFee";
|
import estimateFee from "../../utils/estimateFee";
|
||||||
import { SetHookData } from '../../utils/setHook';
|
import { SetHookData } from "../../utils/setHook";
|
||||||
|
import ResultLink from "../../components/ResultLink";
|
||||||
|
|
||||||
export const sha256 = async (string: string) => {
|
export const sha256 = async (string: string) => {
|
||||||
const utf8 = new TextEncoder().encode(string);
|
const utf8 = new TextEncoder().encode(string);
|
||||||
const hashBuffer = await crypto.subtle.digest("SHA-256", utf8);
|
const hashBuffer = await crypto.subtle.digest("SHA-256", utf8);
|
||||||
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
||||||
const hashHex = hashArray
|
const hashHex = hashArray
|
||||||
.map((bytes) => bytes.toString(16).padStart(2, "0"))
|
.map(bytes => bytes.toString(16).padStart(2, "0"))
|
||||||
.join("");
|
.join("");
|
||||||
return hashHex;
|
return hashHex;
|
||||||
};
|
};
|
||||||
@@ -56,7 +57,7 @@ export const prepareDeployHookTx = async (
|
|||||||
) => {
|
) => {
|
||||||
const activeFile = state.files[state.active]?.compiledContent
|
const activeFile = state.files[state.active]?.compiledContent
|
||||||
? state.files[state.active]
|
? state.files[state.active]
|
||||||
: state.files.filter((file) => file.compiledContent)[0];
|
: state.files.filter(file => file.compiledContent)[0];
|
||||||
|
|
||||||
if (!state.files || state.files.length === 0) {
|
if (!state.files || state.files.length === 0) {
|
||||||
return;
|
return;
|
||||||
@@ -69,12 +70,12 @@ export const prepareDeployHookTx = async (
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const HookNamespace = (await sha256(data.HookNamespace)).toUpperCase();
|
const HookNamespace = (await sha256(data.HookNamespace)).toUpperCase();
|
||||||
const hookOnValues: (keyof TTS)[] = data.Invoke.map((tt) => tt.value);
|
const hookOnValues: (keyof TTS)[] = data.Invoke.map(tt => tt.value);
|
||||||
const { HookParameters } = data;
|
const { HookParameters } = data;
|
||||||
const filteredHookParameters = HookParameters.filter(
|
const filteredHookParameters = HookParameters.filter(
|
||||||
(hp) =>
|
hp =>
|
||||||
hp.HookParameter.HookParameterName && hp.HookParameter.HookParameterValue
|
hp.HookParameter.HookParameterName && hp.HookParameter.HookParameterValue
|
||||||
)?.map((aa) => ({
|
)?.map(aa => ({
|
||||||
HookParameter: {
|
HookParameter: {
|
||||||
HookParameterName: toHex(aa.HookParameter.HookParameterName || ""),
|
HookParameterName: toHex(aa.HookParameter.HookParameterName || ""),
|
||||||
HookParameterValue: aa.HookParameter.HookParameterValue || "",
|
HookParameterValue: aa.HookParameter.HookParameterValue || "",
|
||||||
@@ -128,7 +129,7 @@ export const deployHook = async (
|
|||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
const activeFile = state.files[state.active]?.compiledContent
|
const activeFile = state.files[state.active]?.compiledContent
|
||||||
? state.files[state.active]
|
? state.files[state.active]
|
||||||
: state.files.filter((file) => file.compiledContent)[0];
|
: state.files.filter(file => file.compiledContent)[0];
|
||||||
state.deployValues[activeFile.name] = data;
|
state.deployValues[activeFile.name] = data;
|
||||||
const tx = await prepareDeployHookTx(account, data);
|
const tx = await prepareDeployHookTx(account, data);
|
||||||
if (!tx) {
|
if (!tx) {
|
||||||
@@ -141,7 +142,7 @@ export const deployHook = async (
|
|||||||
|
|
||||||
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
|
||||||
);
|
);
|
||||||
if (currentAccount) {
|
if (currentAccount) {
|
||||||
currentAccount.isLoading = true;
|
currentAccount.isLoading = true;
|
||||||
@@ -154,6 +155,26 @@ export const deployHook = async (
|
|||||||
tx_blob: signedTransaction,
|
tx_blob: signedTransaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const txHash = submitRes.tx_json?.hash;
|
||||||
|
const resultMsg = ref(
|
||||||
|
<>
|
||||||
|
[<ResultLink result={submitRes.engine_result} />]{" "}
|
||||||
|
{submitRes.engine_result_message}{" "}
|
||||||
|
{txHash && (
|
||||||
|
<>
|
||||||
|
Transaction hash:{" "}
|
||||||
|
<Link
|
||||||
|
as="a"
|
||||||
|
href={`https://${process.env.NEXT_PUBLIC_EXPLORER_URL}/${txHash}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{txHash}
|
||||||
|
</Link>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
if (submitRes.engine_result === "tesSUCCESS") {
|
if (submitRes.engine_result === "tesSUCCESS") {
|
||||||
state.deployLogs.push({
|
state.deployLogs.push({
|
||||||
type: "success",
|
type: "success",
|
||||||
@@ -161,28 +182,17 @@ export const deployHook = async (
|
|||||||
});
|
});
|
||||||
state.deployLogs.push({
|
state.deployLogs.push({
|
||||||
type: "success",
|
type: "success",
|
||||||
message: ref(
|
message: resultMsg,
|
||||||
<>
|
});
|
||||||
[{submitRes.engine_result}] {submitRes.engine_result_message}{" "}
|
} else if (submitRes.engine_result) {
|
||||||
Transaction hash:{" "}
|
state.deployLogs.push({
|
||||||
<Link
|
type: "error",
|
||||||
as="a"
|
message: resultMsg,
|
||||||
href={`https://${process.env.NEXT_PUBLIC_EXPLORER_URL}/${submitRes.tx_json?.hash}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
{submitRes.tx_json?.hash}
|
|
||||||
</Link>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
// message: `[${submitRes.engine_result}] ${submitRes.engine_result_message} Validated ledger index: ${submitRes.validated_ledger_index}`,
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
state.deployLogs.push({
|
state.deployLogs.push({
|
||||||
type: "error",
|
type: "error",
|
||||||
message: `[${submitRes.engine_result || submitRes.error}] ${
|
message: `[${submitRes.error}] ${submitRes.error_exception}`,
|
||||||
submitRes.engine_result_message || submitRes.error_exception
|
|
||||||
}`,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -204,7 +214,7 @@ export const deleteHook = async (account: IAccount & { name?: string }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const currentAccount = state.accounts.find(
|
const currentAccount = state.accounts.find(
|
||||||
(acc) => acc.address === account.address
|
acc => acc.address === account.address
|
||||||
);
|
);
|
||||||
if (currentAccount?.isLoading || !currentAccount?.hooks.length) {
|
if (currentAccount?.isLoading || !currentAccount?.hooks.length) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
import { derive, sign } from "xrpl-accountlib";
|
|
||||||
|
|
||||||
import state from '..'
|
|
||||||
import type { IAccount } from "..";
|
|
||||||
|
|
||||||
interface TransactionOptions {
|
|
||||||
TransactionType: string,
|
|
||||||
Account?: string,
|
|
||||||
Fee?: string,
|
|
||||||
Destination?: string
|
|
||||||
[index: string]: any
|
|
||||||
}
|
|
||||||
interface OtherOptions {
|
|
||||||
logPrefix?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const sendTransaction = async (account: IAccount, txOptions: TransactionOptions, options?: OtherOptions) => {
|
|
||||||
if (!state.client) throw Error('XRPL client not initalized')
|
|
||||||
|
|
||||||
const { Fee = "1000", ...opts } = txOptions
|
|
||||||
const tx: TransactionOptions = {
|
|
||||||
Account: account.address,
|
|
||||||
Sequence: account.sequence,
|
|
||||||
Fee, // TODO auto-fillable default
|
|
||||||
...opts
|
|
||||||
};
|
|
||||||
const { logPrefix = '' } = options || {}
|
|
||||||
try {
|
|
||||||
const signedAccount = derive.familySeed(account.secret);
|
|
||||||
const { signedTransaction } = sign(tx, signedAccount);
|
|
||||||
const response = await state.client.send({
|
|
||||||
command: "submit",
|
|
||||||
tx_blob: signedTransaction,
|
|
||||||
});
|
|
||||||
if (response.engine_result === "tesSUCCESS") {
|
|
||||||
state.transactionLogs.push({
|
|
||||||
type: 'success',
|
|
||||||
message: `${logPrefix}[${response.engine_result}] ${response.engine_result_message}`
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
state.transactionLogs.push({
|
|
||||||
type: "error",
|
|
||||||
message: `${logPrefix}[${response.error || response.engine_result}] ${response.error_exception || response.engine_result_message}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const currAcc = state.accounts.find(acc => acc.address === account.address);
|
|
||||||
if (currAcc && response.account_sequence_next) {
|
|
||||||
currAcc.sequence = response.account_sequence_next;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
state.transactionLogs.push({
|
|
||||||
type: "error",
|
|
||||||
message: err instanceof Error ? `${logPrefix}Error: ${err.message}` : `${logPrefix}Something went wrong, try again later`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
78
state/actions/sendTransaction.tsx
Normal file
78
state/actions/sendTransaction.tsx
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import { derive, sign } from "xrpl-accountlib";
|
||||||
|
|
||||||
|
import state from "..";
|
||||||
|
import type { IAccount } from "..";
|
||||||
|
import ResultLink from "../../components/ResultLink";
|
||||||
|
import { ref } from "valtio";
|
||||||
|
|
||||||
|
interface TransactionOptions {
|
||||||
|
TransactionType: string;
|
||||||
|
Account?: string;
|
||||||
|
Fee?: string;
|
||||||
|
Destination?: string;
|
||||||
|
[index: string]: any;
|
||||||
|
}
|
||||||
|
interface OtherOptions {
|
||||||
|
logPrefix?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sendTransaction = async (
|
||||||
|
account: IAccount,
|
||||||
|
txOptions: TransactionOptions,
|
||||||
|
options?: OtherOptions
|
||||||
|
) => {
|
||||||
|
if (!state.client) throw Error("XRPL client not initalized");
|
||||||
|
|
||||||
|
const { Fee = "1000", ...opts } = txOptions;
|
||||||
|
const tx: TransactionOptions = {
|
||||||
|
Account: account.address,
|
||||||
|
Sequence: account.sequence,
|
||||||
|
Fee, // TODO auto-fillable default
|
||||||
|
...opts,
|
||||||
|
};
|
||||||
|
const { logPrefix = "" } = options || {};
|
||||||
|
try {
|
||||||
|
const signedAccount = derive.familySeed(account.secret);
|
||||||
|
const { signedTransaction } = sign(tx, signedAccount);
|
||||||
|
const response = await state.client.send({
|
||||||
|
command: "submit",
|
||||||
|
tx_blob: signedTransaction,
|
||||||
|
});
|
||||||
|
|
||||||
|
const resultMsg = ref(
|
||||||
|
<>
|
||||||
|
{logPrefix}[<ResultLink result={response.engine_result} />]{" "}
|
||||||
|
{response.engine_result_message}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
if (response.engine_result === "tesSUCCESS") {
|
||||||
|
state.transactionLogs.push({
|
||||||
|
type: "success",
|
||||||
|
message: resultMsg,
|
||||||
|
});
|
||||||
|
} else if (response.engine_result) {
|
||||||
|
state.transactionLogs.push({
|
||||||
|
type: "error",
|
||||||
|
message: resultMsg,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
state.transactionLogs.push({
|
||||||
|
type: "error",
|
||||||
|
message: `${logPrefix}[${response.error}] ${response.error_exception}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const currAcc = state.accounts.find(acc => acc.address === account.address);
|
||||||
|
if (currAcc && response.account_sequence_next) {
|
||||||
|
currAcc.sequence = response.account_sequence_next;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
state.transactionLogs.push({
|
||||||
|
type: "error",
|
||||||
|
message:
|
||||||
|
err instanceof Error
|
||||||
|
? `${logPrefix}Error: ${err.message}`
|
||||||
|
: `${logPrefix}Something went wrong, try again later`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user