From 45dac228d053c23d280b9f53f3202e757051eef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Wed, 3 Jan 2024 22:15:54 +0100 Subject: [PATCH] feat: make create context revert --- src/Create.tsx | 149 ++++++----- src/Hero.tsx | 13 +- src/History.tsx | 1 - src/Nav.tsx | 3 +- src/Pay.tsx | 9 +- src/Refund.tsx | 91 ++----- src/RefundStep.tsx | 3 +- src/components/AddressInput.tsx | 19 +- src/components/Asset.tsx | 4 +- src/components/AssetSelect.tsx | 22 +- src/components/ConnectMetamask.tsx | 17 +- src/components/CreateButton.tsx | 41 +-- src/components/Fees.tsx | 26 +- src/components/InvoiceInput.tsx | 36 +-- src/components/Refund.tsx | 20 +- src/components/RefundCreate.tsx | 104 ++++++++ src/components/Reverse.tsx | 12 +- src/context/Create.tsx | 163 +++++++++++ src/context/Web3.tsx | 2 +- src/helper.ts | 356 +------------------------ src/index.tsx | 45 ++-- src/signals.ts | 63 +---- src/status/InvoiceSet.tsx | 8 +- src/status/SwapCreated.tsx | 3 +- src/status/TransactionConfirmed.tsx | 4 +- src/utils/calculate.ts | 31 ++- src/utils/claim.ts | 111 ++++++++ src/utils/denomination.ts | 33 ++- src/utils/download.ts | 6 +- src/utils/feeChecker.ts | 19 +- src/utils/refund.ts | 157 +++++++++++ src/utils/swapChecker.ts | 82 +++++- src/utils/swapStatus.ts | 43 ++- src/utils/webln.ts | 18 +- tests/components/AddressInput.spec.tsx | 21 +- tests/components/AssetSelect.spec.tsx | 63 +++-- tests/components/Create.spec.tsx | 130 +++++---- tests/components/Fees.spec.tsx | 48 ++-- tests/components/InvoiceInput.spec.tsx | 106 ++++---- tests/utils/calculate.spec.ts | 45 ++-- tests/utils/denomination.spec.ts | 18 +- tests/utils/feeChecker.spec.ts | 19 +- tests/utils/swapChecker.spec.ts | 33 ++- 43 files changed, 1225 insertions(+), 972 deletions(-) create mode 100644 src/components/RefundCreate.tsx create mode 100644 src/context/Create.tsx create mode 100644 src/utils/claim.ts create mode 100644 src/utils/refund.ts diff --git a/src/Create.tsx b/src/Create.tsx index 903a968ce..78eb65063 100644 --- a/src/Create.tsx +++ b/src/Create.tsx @@ -5,7 +5,6 @@ import { Show, createEffect, createMemo, on, onMount } from "solid-js"; import AddressInput from "./components/AddressInput"; import Asset from "./components/Asset"; import AssetSelect from "./components/AssetSelect"; -import ClickableAmount from "./components/ClickableAmount"; import ConnectMetamask from "./components/ConnectMetamask"; import { CreateButton, setButtonLabel } from "./components/CreateButton"; import Fees from "./components/Fees"; @@ -13,39 +12,9 @@ import InvoiceInput from "./components/InvoiceInput"; import QrScan from "./components/QrScan"; import Reverse from "./components/Reverse"; import { RBTC, sideReceive, sideSend } from "./consts"; -import { isMobile } from "./helper"; +import { useCreateContext } from "./context/Create"; import t from "./i18n"; -import { - addressValid, - amountChanged, - asset, - assetReceive, - assetSelect, - assetSelected, - assetSend, - boltzFee, - denomination, - invoiceValid, - maximum, - minerFee, - minimum, - receiveAmount, - receiveAmountFormatted, - reverse, - sendAmount, - sendAmountFormatted, - sendAmountValid, - setAmountChanged, - setInvoice, - setReceiveAmount, - setReceiveAmountFormatted, - setSendAmount, - setSendAmountFormatted, - setSendAmountValid, - setValid, - wasmSupported, - webln, -} from "./signals"; +import { boltzFee, denomination, minerFee, webln } from "./signals"; import { calculateReceiveAmount, calculateSendAmount } from "./utils/calculate"; import { calculateDigits, @@ -56,16 +25,44 @@ import { import { enableWebln } from "./utils/webln"; const Create = () => { - let receiveAmountRef: HTMLInputElement, sendAmountRef: HTMLInputElement; + let receiveAmountRef: HTMLInputElement; + let sendAmountRef: HTMLInputElement; + + const { + asset, + assetReceive, + assetSend, + addressValid, + reverse, + setInvoice, + invoiceValid, + receiveAmount, + receiveAmountFormatted, + sendAmount, + sendAmountFormatted, + sendAmountValid, + setReceiveAmount, + setReceiveAmountFormatted, + setSendAmount, + setSendAmountFormatted, + setSendAmountValid, + setValid, + assetSelect, + assetSelected, + amountChanged, + setAmountChanged, + minimum, + maximum, + } = useCreateContext(); const changeReceiveAmount = (evt: InputEvent) => { const target = evt.currentTarget as HTMLInputElement; const amount = target.value.trim(); const satAmount = convertAmount(Number(amount), denomination()); - const sendAmount = calculateSendAmount(satAmount); + const sendAmount = calculateSendAmount(satAmount, reverse()); setAmountChanged(sideReceive); - setReceiveAmount(BigNumber(satAmount)); - setSendAmount(BigNumber(sendAmount)); + setReceiveAmount(BigInt(satAmount)); + setSendAmount(BigInt(sendAmount)); validateAmount(); target.setCustomValidity(""); target.classList.remove("invalid"); @@ -75,7 +72,7 @@ const Create = () => { const target = evt.currentTarget as HTMLInputElement; const amount = target.value.trim(); const satAmount = convertAmount(Number(amount), denomination()); - const receiveAmount = calculateReceiveAmount(satAmount); + const receiveAmount = calculateReceiveAmount(satAmount, reverse()); setAmountChanged(sideSend); setSendAmount(BigNumber(satAmount)); setReceiveAmount(BigNumber(receiveAmount)); @@ -87,6 +84,7 @@ const Create = () => { const createWeblnInvoice = async () => { enableWebln(async () => { const amount = Number(receiveAmount()); + // @ts-ignore const invoice = await window.webln.makeInvoice({ amount: amount }); validateAmount(); log.debug("created webln invoice", invoice); @@ -100,7 +98,6 @@ const Create = () => { const hasDot = input.value.includes("."); const regex = denomination() == "sat" || hasDot ? /[0-9]/ : /[0-9]|\./; if (!regex.test(keycode)) { - evt.stopPropagation(); evt.preventDefault(); } }; @@ -108,17 +105,17 @@ const Create = () => { const validatePaste = (evt: ClipboardEvent) => { const clipboardData = evt.clipboardData || globalThis.clipboardData; const pastedData = clipboardData.getData("Text").trim(); - if (!getValidationRegex().test(pastedData)) { + if (!getValidationRegex(maximum(), denomination()).test(pastedData)) { evt.stopPropagation(); evt.preventDefault(); } }; const validateAmount = () => { - const setCustomValidity = (val: string, isZero: boolean) => { + const setCustomValidity = (msg: string, isZero: boolean = true) => { [sendAmountRef, receiveAmountRef].forEach((ref) => { - ref.setCustomValidity(val); - if (!isZero && val !== "") { + ref.setCustomValidity(msg); + if (!isZero && msg !== "") { ref.classList.add("invalid"); } else { ref.classList.remove("invalid"); @@ -133,7 +130,10 @@ const Create = () => { if (lessThanMin || amount > maximum()) { const params = { - amount: formatAmount(lessThanMin ? minimum() : maximum()), + amount: formatAmount( + lessThanMin ? minimum() : maximum(), + denomination(), + ), denomination: denomination(), }; const label = lessThanMin ? "minimum_amount" : "maximum_amount"; @@ -147,8 +147,8 @@ const Create = () => { }; const setAmount = (amount: number) => { - setSendAmount(BigNumber(amount)); - setReceiveAmount(BigNumber(calculateReceiveAmount(amount))); + setSendAmount(BigInt(amount)); + setReceiveAmount(BigInt(calculateReceiveAmount(amount, reverse()))); validateAmount(); sendAmountRef.focus(); }; @@ -161,11 +161,15 @@ const Create = () => { on([boltzFee, minerFee, reverse, asset], () => { if (amountChanged() === sideReceive) { setSendAmount( - BigNumber(calculateSendAmount(receiveAmount().toNumber())), + BigInt( + calculateSendAmount(Number(receiveAmount()), reverse()), + ), ); } else { setReceiveAmount( - BigNumber(calculateReceiveAmount(sendAmount().toNumber())), + BigInt( + calculateReceiveAmount(Number(sendAmount()), reverse()), + ), ); } validateAmount(); @@ -173,10 +177,7 @@ const Create = () => { ); createEffect(() => { - if (assetSelect()) { - return; - } - + if (assetSelect()) return; const ref = assetSelected() === sideSend ? sendAmountRef : receiveAmountRef; ref.focus(); @@ -185,11 +186,15 @@ const Create = () => { createMemo(() => { const rAmount = Number(receiveAmount()); if (rAmount > 0) { - setReceiveAmountFormatted(formatAmount(rAmount).toString()); + setReceiveAmountFormatted( + formatAmount(rAmount, denomination()).toString(), + ); } const sAmount = Number(sendAmount()); if (sAmount > 0) { - setSendAmountFormatted(formatAmount(sAmount).toString()); + setSendAmountFormatted( + formatAmount(sAmount, denomination()).toString(), + ); } }); @@ -227,17 +232,18 @@ const Create = () => {

{t("create_swap")}

{t("create_swap_subline")}
- {t("send")}{" "} - {" "} - + {t("send")} {t("min")}{" "} + setAmount(minimum())}> + {formatAmount(minimum(), denomination())} + {" "} + {t("max")}{" "} + setAmount(maximum())}> + {formatAmount(maximum(), denomination())} +

@@ -247,8 +253,8 @@ const Create = () => { autofocus required type="text" - placeholder={formatAmount(minimum())} - maxlength={calculateDigits()} + placeholder={formatAmount(minimum(), denomination())} + maxlength={calculateDigits(maximum(), denomination())} inputmode={ denomination() == "btc" ? "decimal" : "numeric" } @@ -260,7 +266,7 @@ const Create = () => { : sendAmountFormatted() } onpaste={(e) => validatePaste(e)} - onkeypress={(e) => validateInput(e)} + onKeyPress={(e) => validateInput(e)} onInput={(e) => changeSendAmount(e)} />
@@ -272,9 +278,10 @@ const Create = () => { required type="text" placeholder={formatAmount( - calculateReceiveAmount(minimum()), + calculateReceiveAmount(minimum(), reverse()), + denomination(), )} - maxlength={calculateDigits()} + maxlength={calculateDigits(maximum(), denomination())} inputmode={ denomination() == "btc" ? "decimal" : "numeric" } @@ -286,7 +293,7 @@ const Create = () => { : receiveAmountFormatted() } onpaste={(e) => validatePaste(e)} - onkeypress={(e) => validateInput(e)} + onKeyPress={(e) => validateInput(e)} onInput={(e) => changeReceiveAmount(e)} />
diff --git a/src/Hero.tsx b/src/Hero.tsx index c400863ec..83f198e42 100644 --- a/src/Hero.tsx +++ b/src/Hero.tsx @@ -7,7 +7,8 @@ import bitcoin from "./assets/bitcoin-icon.svg"; import lightning from "./assets/lightning-icon.svg"; import liquid from "./assets/liquid-icon.svg"; import { ambossUrl } from "./config"; -import { fetcher } from "./helper"; +import { BTC } from "./consts"; +import { fetcher, getApiUrl } from "./helper"; import t from "./i18n"; import "./style/hero.scss"; @@ -16,10 +17,10 @@ export const [hideHero, setHideHero] = createSignal(false); export const Hero = () => { const navigate = useNavigate(); const [nodeStats, setNodeStats] = createSignal(null); - const [numChannel, setNumChannel] = createSignal("0"); - const [numPeers, setNumPeers] = createSignal("0"); - const [capacity, setCapacity] = createSignal("0"); - const [oldestChannel, setOldestChannel] = createSignal("0"); + const [numChannel, setNumChannel] = createSignal("0"); + const [numPeers, setNumPeers] = createSignal("0"); + const [capacity, setCapacity] = createSignal("0"); + const [oldestChannel, setOldestChannel] = createSignal("0"); createMemo(() => { const stats = nodeStats(); @@ -36,7 +37,7 @@ export const Hero = () => { window.open(ambossUrl, "_blank"); }; - fetcher("/nodestats", (data: any) => { + fetcher(getApiUrl("/nodestats", BTC), (data: any) => { log.debug("nodestats", data); setNodeStats(data.nodes.BTC); }); diff --git a/src/History.tsx b/src/History.tsx index bf2ac79b0..6eb37faac 100644 --- a/src/History.tsx +++ b/src/History.tsx @@ -93,7 +93,6 @@ const History = () => { setSwapSignal={setSwaps} deleteButton={true} /> -
0}> - -
- -
- + +

{t("refunded")}


diff --git a/src/RefundStep.tsx b/src/RefundStep.tsx index 13141bdc1..14e869084 100644 --- a/src/RefundStep.tsx +++ b/src/RefundStep.tsx @@ -1,6 +1,7 @@ import { useNavigate, useParams } from "@solidjs/router"; import log from "loglevel"; -import { For, createEffect } from "solid-js"; +import { For } from "solid-js"; +import { createEffect } from "solid-js"; import DownloadRefund from "./components/DownloadRefund"; import t from "./i18n"; diff --git a/src/components/AddressInput.tsx b/src/components/AddressInput.tsx index 9b3bdced3..481923afe 100644 --- a/src/components/AddressInput.tsx +++ b/src/components/AddressInput.tsx @@ -2,22 +2,23 @@ import { createEffect, on } from "solid-js"; import { decodeAddress } from "../compat"; import { RBTC } from "../consts"; +import { useCreateContext } from "../context/Create"; import t from "../i18n"; -import { - asset, - onchainAddress, - reverse, - sendAmountValid, - setAddressValid, - setOnchainAddress, -} from "../signals"; import { extractAddress } from "../utils/invoice"; import { setButtonLabel } from "./CreateButton"; const AddressInput = () => { let inputRef: HTMLInputElement; + const { + asset, + onchainAddress, + reverse, + sendAmountValid, + setAddressValid, + setOnchainAddress, + } = useCreateContext(); - const validateAddress = (input: HTMLInputElement) => { + const validateAddress = (input: EventTarget & HTMLInputElement) => { const inputValue = input.value.trim(); const address = extractAddress(inputValue); diff --git a/src/components/Asset.tsx b/src/components/Asset.tsx index 25a203027..cd5add23a 100644 --- a/src/components/Asset.tsx +++ b/src/components/Asset.tsx @@ -1,7 +1,9 @@ -import { setAssetSelect, setAssetSelected } from "../signals"; +import { useCreateContext } from "../context/Create"; import "../style/asset.scss"; const Asset = ({ side, signal }) => { + const { setAssetSelect, setAssetSelected } = useCreateContext(); + const openSelect = () => { setAssetSelected(side); setAssetSelect(true); diff --git a/src/components/AssetSelect.tsx b/src/components/AssetSelect.tsx index bfe001877..170dd66fd 100644 --- a/src/components/AssetSelect.tsx +++ b/src/components/AssetSelect.tsx @@ -1,22 +1,24 @@ import { pairs } from "../config"; import { LN, sideSend } from "../consts"; +import { useCreateContext } from "../context/Create"; import { fetchPairs } from "../helper"; import t from "../i18n"; -import { - assetReceive, - assetSelect, - assetSelected, - assetSend, - setAsset, - setAssetReceive, - setAssetSelect, - setAssetSend, -} from "../signals"; const assets = Object.keys(pairs).map((pair) => pair.split("/")[0]); assets.push(LN); const SelectAsset = () => { + const { + assetSelected, + assetSelect, + setAssetSelect, + assetReceive, + setAssetReceive, + assetSend, + setAssetSend, + setAsset, + } = useCreateContext(); + const setSelectAsset = (isSend: boolean, asset: string) => { const setter = isSend ? setAssetSend : setAssetReceive; setter(asset); diff --git a/src/components/ConnectMetamask.tsx b/src/components/ConnectMetamask.tsx index 7997ce43a..1cc239536 100644 --- a/src/components/ConnectMetamask.tsx +++ b/src/components/ConnectMetamask.tsx @@ -1,16 +1,19 @@ import { Show, createEffect, createSignal } from "solid-js"; +import { useCreateContext } from "../context/Create"; import { useWeb3Signer } from "../context/Web3"; import t from "../i18n"; -import { - addressValid, - sendAmountValid, - setAddressValid, - setOnchainAddress, -} from "../signals"; import { setButtonLabel } from "./CreateButton"; const ConnectMetamask = ({ showAddress }) => { + const { + addressValid, + sendAmountValid, + setAddressValid, + setOnchainAddress, + } = useCreateContext(); + const { getSigner, hasMetamask } = useWeb3Signer(); + const [address, setAddress] = createSignal(); const [buttonText, setButtonText] = createSignal(); @@ -33,8 +36,6 @@ const ConnectMetamask = ({ showAddress }) => { } }); - const { getSigner, hasMetamask } = useWeb3Signer(); - return ( <> diff --git a/src/components/CreateButton.tsx b/src/components/CreateButton.tsx index 51e25f12d..2ca996995 100644 --- a/src/components/CreateButton.tsx +++ b/src/components/CreateButton.tsx @@ -5,31 +5,18 @@ import log from "loglevel"; import { createEffect, createMemo, createSignal } from "solid-js"; import { RBTC } from "../consts"; +import { useCreateContext } from "../context/Create"; import { useWeb3Signer } from "../context/Web3"; import { ECPair } from "../ecpair/ecpair"; -import { feeCheck, fetcher } from "../helper"; +import { feeCheck, fetcher, getApiUrl } from "../helper"; import t from "../i18n"; import { - asset, config, - invoice, - lnurl, - onchainAddress, online, - receiveAmount, - reverse, - sendAmount, - sendAmountValid, - setAddressValid, - setInvoice, - setInvoiceValid, - setLnurl, setNotification, setNotificationType, - setOnchainAddress, setSwaps, swaps, - valid, wasmSupported, } from "../signals"; import { extractAddress, fetchLnurl } from "../utils/invoice"; @@ -47,6 +34,22 @@ export const [buttonLabel, setButtonLabel] = createSignal({ export const CreateButton = () => { const navigate = useNavigate(); const { getEtherSwap } = useWeb3Signer(); + const { + asset, + invoice, + lnurl, + reverse, + setInvoice, + setLnurl, + receiveAmount, + sendAmountValid, + setOnchainAddress, + valid, + sendAmount, + onchainAddress, + setInvoiceValid, + setAddressValid, + } = useCreateContext(); const [buttonDisable, setButtonDisable] = createSignal(true); const [buttonClass, setButtonClass] = createSignal("btn"); @@ -135,7 +138,7 @@ export const CreateButton = () => { } } - if (!(await feeCheck(t("feecheck")))) { + if (!(await feeCheck(t("feecheck"), assetName))) { return; } @@ -143,8 +146,8 @@ export const CreateButton = () => { await new Promise((resolve) => { fetcher( - "/createswap", - (data) => { + getApiUrl("/createswap", assetName), + (data: any) => { data.date = new Date().getTime(); data.reverse = reverse(); data.asset = asset(); @@ -192,7 +195,7 @@ export const CreateButton = () => { async (err: Response) => { const res = await err.json(); if (res.error === "invalid pair hash") { - await feeCheck(t("feecheck")); + await feeCheck(t("feecheck"), assetName); } else { setNotificationType("error"); setNotification(res.error); diff --git a/src/components/Fees.tsx b/src/components/Fees.tsx index 7a53fc9ed..e8a981fca 100644 --- a/src/components/Fees.tsx +++ b/src/components/Fees.tsx @@ -2,21 +2,17 @@ import { createEffect } from "solid-js"; import btcSvg from "../assets/btc.svg"; import satSvg from "../assets/sat.svg"; -import { fetchPairs } from "../helper"; +import { useCreateContext } from "../context/Create"; +import { fetchPairs, isMobile } from "../helper"; import t from "../i18n"; import { - asset, boltzFee, config, denomination, minerFee, - reverse, - sendAmount, setBoltzFee, setDenomination, - setMaximum, setMinerFee, - setMinimum, } from "../signals"; import { calculateBoltzFeeOnSend, @@ -25,6 +21,9 @@ import { import { denominations, formatAmount } from "../utils/denomination"; const Fees = () => { + const { asset, reverse, sendAmount, setMinimum, setMaximum } = + useCreateContext(); + createEffect(() => { const cfg = config()[`${asset()}/BTC`]; @@ -41,7 +40,9 @@ const Fees = () => { } const calculateLimit = (limit: number) => { - return reverse() ? limit : calculateSendAmount(limit); + return reverse() + ? limit + : calculateSendAmount(limit, reverse()); }; setMinimum(calculateLimit(cfg.limits.minimal)); @@ -76,7 +77,7 @@ const Fees = () => {