From b19bb578dd81f4dc6971da76f230f8c9e0222f58 Mon Sep 17 00:00:00 2001
From: jackstar12
Date: Fri, 3 Jan 2025 17:57:53 +0100
Subject: [PATCH] wip
---
src/components/RefundButton.tsx | 7 +-
src/components/SwapList.tsx | 3 +-
src/i18n/i18n.ts | 10 ++
src/index.tsx | 3 +
src/pages/History.tsx | 1 +
src/pages/Refund.tsx | 234 +++----------------------
src/pages/RefundExternal.tsx | 263 +++++++++++++++++++++++++++++
src/style/index.scss | 4 +
src/style/tabs.scss | 28 +++
tests/components/SwapList.spec.tsx | 2 +-
10 files changed, 337 insertions(+), 218 deletions(-)
create mode 100644 src/pages/RefundExternal.tsx
create mode 100644 src/style/tabs.scss
diff --git a/src/components/RefundButton.tsx b/src/components/RefundButton.tsx
index 6699159e..f1c5479e 100644
--- a/src/components/RefundButton.tsx
+++ b/src/components/RefundButton.tsx
@@ -182,10 +182,9 @@ const RefundButton = (props: {
currentSwap.refundTx = res.refundTx;
await setSwapStorage(currentSwap);
setSwap(currentSwap);
- } else {
- if (props.setRefundTxId) {
- props.setRefundTxId(res.refundTx);
- }
+ }
+ if (props.setRefundTxId) {
+ props.setRefundTxId(res.refundTx);
}
} catch (error) {
log.warn("refund failed", error);
diff --git a/src/components/SwapList.tsx b/src/components/SwapList.tsx
index 66999e19..7534f6d1 100644
--- a/src/components/SwapList.tsx
+++ b/src/components/SwapList.tsx
@@ -9,6 +9,7 @@ import { SwapIcons } from "./SwapIcons";
const SwapList = (props: {
swapsSignal: Accessor;
+ action: string;
onDelete?: () => Promise;
}) => {
const navigate = useNavigate();
@@ -51,7 +52,7 @@ const SwapList = (props: {
class="swaplist-item"
onClick={() => navigate(`/swap/${swap.id}`)}>
- {t("view")}
+ {props.action}
diff --git a/src/i18n/i18n.ts b/src/i18n/i18n.ts
index ded9ad2c..644e873b 100644
--- a/src/i18n/i18n.ts
+++ b/src/i18n/i18n.ts
@@ -103,6 +103,16 @@ const dict = {
refund_past_swaps: "Past swaps",
refund_past_swaps_subline:
"Swaps that got saved into your browsers storage",
+ no_refundable_swaps: "No refundable swaps found in your browser history",
+ cant_find_swap: "Can't find your swap?",
+ refund_external_explainer:
+ "Try refunding an external swap via refund file and other emergency methods",
+ refund_external_explainer_rsk:
+ "Connect your Rootstock Wallet to scan for refundable swaps that are not saved in this browser’s swap history.",
+ connected_wallet_no_swaps:
+ "The connected Rootstock Wallet does NOT contain any refundable swaps.",
+ rsk_log_endpoint_not_available: "Log endpoint not available",
+ refund_external_swap: "Refund External Swap",
history_no_swaps: "Looks like you didn't do any swaps yet.",
refund_address_header:
"Enter address of your {{ asset }} wallet to refund",
diff --git a/src/index.tsx b/src/index.tsx
index 2adb8d15..688fc0d0 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -25,6 +25,7 @@ import NotFound from "./pages/NotFound";
import Pay from "./pages/Pay";
import Refund from "./pages/Refund";
import RefundEvm from "./pages/RefundEvm";
+import RefundExternal from "./pages/RefundExternal";
import RefundStep from "./pages/RefundStep";
import "./style/index.scss";
import "./utils/patches";
@@ -105,6 +106,8 @@ const cleanup = render(
} />
+
+
diff --git a/src/pages/History.tsx b/src/pages/History.tsx
index 1535f442..cd7d431c 100644
--- a/src/pages/History.tsx
+++ b/src/pages/History.tsx
@@ -153,6 +153,7 @@ const History = () => {
onDelete={async () => {
setSwaps(await getSwaps());
}}
+ action={t("view")}
/>
0}>
diff --git a/src/pages/Refund.tsx b/src/pages/Refund.tsx
index 46ce02be..d0bc2b25 100644
--- a/src/pages/Refund.tsx
+++ b/src/pages/Refund.tsx
@@ -15,151 +15,19 @@ import { SwapType } from "../consts/Enums";
import { swapStatusFailed, swapStatusSuccess } from "../consts/SwapStatus";
import { useGlobalContext } from "../context/Global";
import { useWeb3Signer } from "../context/Web3";
+import "../style/tabs.scss";
import { getLockupTransaction, getSwapStatus } from "../utils/boltzClient";
-import {
- LogRefundData,
- scanLogsForPossibleRefunds,
-} from "../utils/contractLogs";
-import { qrScanProbe } from "../utils/qrScanProbe";
-import { validateRefundFile } from "../utils/refundFile";
import { SomeSwap } from "../utils/swapCreator";
import ErrorWasm from "./ErrorWasm";
-enum RefundError {
- InvalidData,
- QrScanNotSupported,
-}
-
const Refund = () => {
const navigate = useNavigate();
- const { getSwap, getSwaps, updateSwapStatus, wasmSupported, t } =
- useGlobalContext();
- const { signer, providers, getEtherSwap } = useWeb3Signer();
-
- const [swapFound, setSwapFound] = createSignal(null);
- const [refundInvalid, setRefundInvalid] = createSignal<
- RefundError | undefined
- >(undefined);
- const [refundJson, setRefundJson] = createSignal(null);
- const [refundTxId, setRefundTxId] = createSignal("");
-
- const checkRefundJsonKeys = async (
- input: HTMLInputElement,
- json: Record,
- ) => {
- log.debug("checking refund json", json);
-
- try {
- const data = validateRefundFile(json);
-
- // When the swap id is found in the local storage, show a redirect to it
- const localStorageSwap = await getSwap(data.id);
- if (localStorageSwap !== null) {
- setSwapFound(data.id);
- return;
- }
-
- setRefundJson(data);
- setRefundInvalid(undefined);
- } catch (e) {
- log.warn("Refund json validation failed", e);
- setRefundInvalid(RefundError.InvalidData);
- input.setCustomValidity(t("invalid_refund_file"));
- }
- };
-
- const uploadChange = async (e: Event) => {
- const input = e.currentTarget as HTMLInputElement;
- const inputFile = input.files[0];
- input.setCustomValidity("");
- setRefundJson(null);
- setSwapFound(null);
- setRefundInvalid(undefined);
-
- if (["image/png", "image/jpg", "image/jpeg"].includes(inputFile.type)) {
- if (!(await qrScanProbe())) {
- setRefundInvalid(RefundError.QrScanNotSupported);
- return;
- }
-
- try {
- const res = await QrScanner.scanImage(inputFile, {
- returnDetailedScanResult: true,
- });
- await checkRefundJsonKeys(input, JSON.parse(res.data));
- } catch (e) {
- log.error("invalid QR code upload", e);
- setRefundInvalid(RefundError.InvalidData);
- input.setCustomValidity(t("invalid_refund_file"));
- }
- } else {
- try {
- const data = await inputFile.text();
- await checkRefundJsonKeys(input, JSON.parse(data));
- } catch (e) {
- log.error("invalid file upload", e);
- setRefundInvalid(RefundError.InvalidData);
- input.setCustomValidity(t("invalid_refund_file"));
- }
- }
- };
+ const { getSwaps, updateSwapStatus, wasmSupported, t } = useGlobalContext();
const refundSwapsSanityFilter = (swap: SomeSwap) =>
swap.type !== SwapType.Reverse && swap.refundTx === undefined;
const [refundableSwaps, setRefundableSwaps] = createSignal([]);
- const [logRefundableSwaps, setLogRefundableSwaps] = createSignal<
- LogRefundData[]
- >([]);
- const [refundScanProgress, setRefundScanProgress] = createSignal<
- string | undefined
- >(undefined);
-
- let refundScanAbort: AbortController | undefined = undefined;
-
- onCleanup(() => {
- if (refundScanAbort) {
- refundScanAbort.abort();
- }
- });
-
- // eslint-disable-next-line solid/reactivity
- createEffect(async () => {
- setLogRefundableSwaps([]);
-
- if (refundScanAbort !== undefined) {
- refundScanAbort.abort("signer changed");
- }
-
- if (signer() === undefined) {
- return;
- }
-
- setRefundScanProgress(
- t("logs_scan_progress", {
- value: Number(0).toFixed(2),
- }),
- );
-
- refundScanAbort = new AbortController();
-
- const generator = scanLogsForPossibleRefunds(
- refundScanAbort.signal,
- signer(),
- getEtherSwap(),
- );
-
- for await (const value of generator) {
- setRefundScanProgress(
- t("logs_scan_progress", {
- value: (value.progress * 100).toFixed(2),
- }),
- );
- setLogRefundableSwaps(logRefundableSwaps().concat(value.events));
- }
-
- setRefundScanProgress(undefined);
- });
onMount(async () => {
const addToRefundableSwaps = (swap: SomeSwap) => {
@@ -171,8 +39,8 @@ const Refund = () => {
.filter((swap) =>
[
swapStatusFailed.InvoiceFailedToPay,
- swapStatusFailed.TransactionLockupFailed,
- ].includes(swap.status),
+ swapStatusFailed.TransactionLockupFailed
+ ].includes(swap.status)
);
setRefundableSwaps(swapsToRefund);
@@ -182,7 +50,7 @@ const Refund = () => {
(swap) =>
swap.status !== swapStatusSuccess.TransactionClaimed &&
swapsToRefund.find((found) => found.id === swap.id) ===
- undefined,
+ undefined
)
// eslint-disable-next-line solid/reactivity
.map(async (swap) => {
@@ -211,83 +79,25 @@ const Refund = () => {
}>
-
-
- {t("refund_a_swap")}
- {t("refund_a_swap_subline")}
- >
- }>
- {t("refund_swap", { id: refundJson().id })}
-
-
-
0}>
-
-
-
0}>
-
-
-
uploadChange(e)}
- />
+
+
+ {t("refund_a_swap")}
+
0 &&
- (refundJson() === null ||
- refundJson().assetSend === RBTC)
- }>
-
-
-
-
-
- {t("swap_in_history")}
-
-
-
-
-
-
-
-
-
-
-
-
- {t("refunded")}
-
-
+ when={refundableSwaps().length > 0}
+ fallback={<>
+ {t("no_refundable_swaps")}
+
+ >}>
+
+
{t("cant_find_swap")}
+
{t("refund_external_explainer")}
+
diff --git a/src/pages/RefundExternal.tsx b/src/pages/RefundExternal.tsx
new file mode 100644
index 00000000..0a690516
--- /dev/null
+++ b/src/pages/RefundExternal.tsx
@@ -0,0 +1,263 @@
+import { useNavigate, useParams } from "@solidjs/router";
+import log from "loglevel";
+import QrScanner from "qr-scanner";
+import {
+ Match,
+ Show,
+ Switch,
+ createEffect,
+ createSignal,
+ onCleanup,
+ onMount
+} from "solid-js";
+
+import BlockExplorer from "../components/BlockExplorer";
+import ConnectWallet from "../components/ConnectWallet";
+import RefundButton from "../components/RefundButton";
+import SwapListLogs from "../components/SwapListLogs";
+import SettingsCog from "../components/settings/SettingsCog";
+import SettingsMenu from "../components/settings/SettingsMenu";
+import { useGlobalContext } from "../context/Global";
+import { useWeb3Signer } from "../context/Web3";
+import "../style/tabs.scss";
+import {
+ LogRefundData,
+ scanLogsForPossibleRefunds
+} from "../utils/contractLogs";
+import { qrScanProbe } from "../utils/qrScanProbe";
+import { validateRefundFile } from "../utils/refundFile";
+import ErrorWasm from "./ErrorWasm";
+
+enum RefundError {
+ InvalidData,
+ QrScanNotSupported,
+}
+
+const RefundBtcLike = () => {
+ const { t } = useGlobalContext();
+
+ const [refundInvalid, setRefundInvalid] = createSignal<
+ RefundError | undefined
+ >(undefined);
+ const [refundJson, setRefundJson] = createSignal(null);
+ const [refundTxId, setRefundTxId] = createSignal("");
+
+ const checkRefundJsonKeys = async (
+ input: HTMLInputElement,
+ json: Record
+ ) => {
+ log.debug("checking refund json", json);
+
+ try {
+ const data = validateRefundFile(json);
+
+ setRefundJson(data);
+ setRefundInvalid(undefined);
+ } catch (e) {
+ log.warn("Refund json validation failed", e);
+ setRefundInvalid(RefundError.InvalidData);
+ input.setCustomValidity(t("invalid_refund_file"));
+ }
+ };
+
+ const uploadChange = async (e: Event) => {
+ const input = e.currentTarget as HTMLInputElement;
+ const inputFile = input.files[0];
+ input.setCustomValidity("");
+ setRefundJson(null);
+ setRefundInvalid(undefined);
+
+ if (["image/png", "image/jpg", "image/jpeg"].includes(inputFile.type)) {
+ if (!(await qrScanProbe())) {
+ setRefundInvalid(RefundError.QrScanNotSupported);
+ return;
+ }
+
+ try {
+ const res = await QrScanner.scanImage(inputFile, {
+ returnDetailedScanResult: true
+ });
+ await checkRefundJsonKeys(input, JSON.parse(res.data));
+ } catch (e) {
+ log.error("invalid QR code upload", e);
+ setRefundInvalid(RefundError.InvalidData);
+ input.setCustomValidity(t("invalid_refund_file"));
+ }
+ } else {
+ try {
+ const data = await inputFile.text();
+ await checkRefundJsonKeys(input, JSON.parse(data));
+ } catch (e) {
+ log.error("invalid file upload", e);
+ setRefundInvalid(RefundError.InvalidData);
+ input.setCustomValidity(t("invalid_refund_file"));
+ }
+ }
+ };
+
+ return (
+ <>
+ {t("refund_a_swap_subline")}
+ uploadChange(e)}
+ />
+
+
+
+
+
+
+
+
+
+
+ {t("refunded")}
+
+
+
+
+ >
+ );
+};
+
+const RefundRsk = () => {
+ const { t } = useGlobalContext();
+ const { signer, providers, getEtherSwap } = useWeb3Signer();
+
+ const [logRefundableSwaps, setLogRefundableSwaps] = createSignal<
+ LogRefundData[]
+ >([]);
+ const [refundScanProgress, setRefundScanProgress] = createSignal<
+ string | undefined
+ >(undefined);
+
+ let refundScanAbort: AbortController | undefined = undefined;
+
+ onCleanup(() => {
+ if (refundScanAbort) {
+ refundScanAbort.abort();
+ }
+ });
+
+ // eslint-disable-next-line solid/reactivity
+ createEffect(async () => {
+ setLogRefundableSwaps([]);
+
+ if (refundScanAbort !== undefined) {
+ refundScanAbort.abort("signer changed");
+ }
+
+ if (signer() === undefined) {
+ return;
+ }
+
+ setRefundScanProgress(
+ t("logs_scan_progress", {
+ value: Number(0).toFixed(2)
+ })
+ );
+
+ refundScanAbort = new AbortController();
+
+ const generator = scanLogsForPossibleRefunds(
+ refundScanAbort.signal,
+ signer(),
+ getEtherSwap()
+ );
+
+ for await (const value of generator) {
+ setRefundScanProgress(
+ t("logs_scan_progress", {
+ value: (value.progress * 100).toFixed(2)
+ })
+ );
+ setLogRefundableSwaps(logRefundableSwaps().concat(value.events));
+ }
+
+ setRefundScanProgress(undefined);
+ });
+
+ return (
+ {t("rsk_log_endpoint_not_available")}
}>
+ {t("refund_external_explainer_rsk")}}>
+ 0}>
+
+
+
+ {refundScanProgress()}
+
+
+ {t("connected_wallet_no_swaps")}
+
+
+
+
+
+ );
+};
+
+const RefundExternal = () => {
+ const { wasmSupported, t } = useGlobalContext();
+
+ const params = useParams();
+ const navigate = useNavigate();
+
+ const tabBtc = { name: "Bitcoin / Liquid", value: "btc" };
+ const tabRsk = { name: "Rootstock", value: "rsk" };
+
+ const selected = () => params.type ?? tabBtc.value;
+
+ return (
+ }>
+
+
+
+
+ {t("refund_external_swap")}
+
+
+ {[tabBtc, tabRsk].map((tab) => (
+
+ navigate(`/refund/external/${tab.value}`)
+ }>
+ {tab.name}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default RefundExternal;
diff --git a/src/style/index.scss b/src/style/index.scss
index 23360141..a69c1b8c 100644
--- a/src/style/index.scss
+++ b/src/style/index.scss
@@ -651,3 +651,7 @@ textarea {
font-weight: 600;
text-align: center;
}
+
+header {
+ margin-bottom: 1rem;
+}
\ No newline at end of file
diff --git a/src/style/tabs.scss b/src/style/tabs.scss
new file mode 100644
index 00000000..4834b094
--- /dev/null
+++ b/src/style/tabs.scss
@@ -0,0 +1,28 @@
+@use "sass:color";
+@use "vars";
+
+.tabs {
+ display: flex;
+ gap: 0.5rem;
+ align-items: center;
+ justify-content: center;
+
+ .tab {
+ padding: 0.5rem 1rem;
+ background-color: vars.$background;
+ color: vars.$color;
+ border-radius: 20px;
+ text-align: center;
+ cursor: pointer;
+ transition: all 0.3s ease;
+
+ &:hover {
+ background-color: color.adjust(vars.$background, $lightness: -10%);
+ }
+
+ &.active {
+ background-color: vars.$primary;
+ color: vars.$background;
+ }
+ }
+}
diff --git a/tests/components/SwapList.spec.tsx b/tests/components/SwapList.spec.tsx
index 819afd0d..0cd65858 100644
--- a/tests/components/SwapList.spec.tsx
+++ b/tests/components/SwapList.spec.tsx
@@ -19,7 +19,7 @@ describe("SwapList", () => {
const {
container: { firstChild: firstChild },
- } = render(() => , {
+ } = render(() => , {
wrapper: contextWrapper,
});