Skip to content

Commit

Permalink
Merge branch 'main' into feat/chatwoot
Browse files Browse the repository at this point in the history
  • Loading branch information
jackstar12 authored Nov 27, 2024
2 parents 17cd469 + ca8059f commit 4ce904a
Show file tree
Hide file tree
Showing 26 changed files with 515 additions and 136 deletions.
51 changes: 51 additions & 0 deletions e2e/chainSwaps/zeroAmount.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { expect, test } from "@playwright/test";

import { getBitcoinAddress } from "../utils";

test.describe("Chain Swap 0-amount", () => {
test("should allow 0-amount chain swaps", async ({ page }) => {
await page.goto("/");

const assetSelector = page.locator("div[class='asset asset-LN'] div");
await assetSelector.click();

await page.locator("div[data-testid='select-L-BTC']").click();

const inputOnchainAddress = page.locator(
"input[data-testid='onchainAddress']",
);
await inputOnchainAddress.fill(await getBitcoinAddress());

const buttonCreateSwap = page.locator(
"button[data-testid='create-swap-button']",
);
await buttonCreateSwap.click();

const skipDownload = page.getByText("Skip download");
await skipDownload.click();
});

test("should not allow 0-amount chain swaps when sending RBTC", async ({
page,
}) => {
await page.goto("/");

const assetSelector = page.locator("div[class='asset asset-LN'] div");
await assetSelector.click();

await page.locator("div[data-testid='select-RBTC']").click();

const inputOnchainAddress = page.locator(
"input[data-testid='onchainAddress']",
);
await inputOnchainAddress.fill(await getBitcoinAddress());

const buttonCreateSwap = page.locator(
"button[data-testid='create-swap-button']",
);
const createButton = await buttonCreateSwap.elementHandle();
await expect(createButton.getAttribute("disabled")).resolves.toEqual(
"",
);
});
});
10 changes: 6 additions & 4 deletions e2e/submarineSwap.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,12 @@ test.describe("Submarine swap", () => {

await generateBitcoinBlock();

await page.getByText("Copy preimage").click();
const preimage = await page.evaluate(() => {
return navigator.clipboard.readText();
});
const validationLink = new URL(
await page.getByText("Show Proof of Payment").getAttribute("href"),
);

expect(validationLink.searchParams.get("invoice")).toEqual(invoice);
const preimage = validationLink.searchParams.get("preimage");

const lookupRes = await lookupInvoiceLnd(invoice);
expect(lookupRes.state).toEqual("SETTLED");
Expand Down
5 changes: 3 additions & 2 deletions src/components/AddressInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,11 @@ const AddressInput = () => {
break;
}
} catch (e) {
log.debug(`Invalid address input: ${formatError(e)}`);

setAddressValid(false);

if (inputValue.length !== 0) {
log.debug(`Invalid address input: ${formatError(e)}`);

const msg = t("invalid_address", { asset: assetReceive() });
input.classList.add("invalid");
input.setCustomValidity(msg);
Expand Down
11 changes: 10 additions & 1 deletion src/components/CreateButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,16 @@ export const CreateButton = () => {
setButtonLabel({ key: "invalid_pair" });
return;
}
if (!amountValid()) {
if (
!amountValid() &&
// Chain swaps with 0-amount that do not have RBTC as sending asset
// can skip this check
!(
swapType() === SwapType.Chain &&
assetSend() !== RBTC &&
sendAmount().isZero()
)
) {
const lessThanMin = Number(sendAmount()) < minimum();
setButtonLabel({
key: lessThanMin ? "minimum_amount" : "maximum_amount",
Expand Down
6 changes: 2 additions & 4 deletions src/components/PayInvoice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import { Show } from "solid-js";
import CopyButton from "../components/CopyButton";
import QrCode from "../components/QrCode";
import { BTC } from "../consts/Assets";
import { Denomination } from "../consts/Enums";
import { useGlobalContext } from "../context/Global";
import { formatAmount } from "../utils/denomination";
import { formatAmount, formatDenomination } from "../utils/denomination";
import { clipboard, cropString, isMobile } from "../utils/helper";
import { invoicePrefix } from "../utils/invoice";
import { enableWebln } from "../utils/webln";
Expand All @@ -30,8 +29,7 @@ const PayInvoice = (props: { sendAmount: number; invoice: string }) => {
denomination(),
separator(),
),
denomination:
denomination() === Denomination.Sat ? "sats" : BTC,
denomination: formatDenomination(denomination(), BTC),
})}
</h2>
<hr />
Expand Down
154 changes: 101 additions & 53 deletions src/components/PayOnchain.tsx
Original file line number Diff line number Diff line change
@@ -1,73 +1,121 @@
import { BigNumber } from "bignumber.js";
import { Show } from "solid-js";
import { Show, createMemo, createResource } from "solid-js";

import CopyButton from "../components/CopyButton";
import QrCode from "../components/QrCode";
import { BTC } from "../consts/Assets";
import { Denomination } from "../consts/Enums";
import { SwapType } from "../consts/Enums";
import { useGlobalContext } from "../context/Global";
import { formatAmount } from "../utils/denomination";
import { clipboard, cropString, isMobile } from "../utils/helper";
import { getPairs } from "../utils/boltzClient";
import { formatAmount, formatDenomination } from "../utils/denomination";
import { clipboard, cropString, getPair, isMobile } from "../utils/helper";
import LoadingSpinner from "./LoadingSpinner";

const PayOnchain = (props: {
asset: string;
type: SwapType;
assetSend: string;
assetReceive: string;
expectedAmount: number;
address: string;
bip21: string;
}) => {
const { t, denomination, separator } = useGlobalContext();
const { t, denomination, separator, setPairs, pairs } = useGlobalContext();

const [pairsFetch] = createResource(async () => {
if (pairs() !== undefined) {
return pairs();
}

const p = await getPairs();
setPairs(p);
return p;
});

const headerText = createMemo(() => {
const denom = formatDenomination(denomination(), props.assetSend);

if (props.expectedAmount > 0) {
return t("send_to", {
denomination: denom,
amount: formatAmount(
BigNumber(props.expectedAmount),
denomination(),
separator(),
),
});
}

if (pairs() === undefined) {
return "";
}

const pair = getPair(
pairs(),
props.type,
props.assetSend,
props.assetReceive,
);
return t("send_between", {
denomination: denom,
min: formatAmount(
BigNumber(pair.limits.minimal),
denomination(),
separator(),
),
max: formatAmount(
BigNumber(pair.limits.maximal),
denomination(),
separator(),
),
});
});

return (
<div>
<h2>
{t("send_to", {
amount: formatAmount(
BigNumber(props.expectedAmount),
denomination(),
separator(),
),
denomination:
denomination() === Denomination.Sat
? "sats"
: props.asset,
})}
</h2>
<hr />
<a href={props.bip21}>
<QrCode asset={props.asset} data={props.bip21} />
</a>
<hr />
<p
onClick={() => clipboard(props.address)}
class="address-box break-word">
{cropString(props.address)}
</p>
<Show when={props.asset === BTC}>
<hr class="spacer" />
<h3>{t("warning_expiry")}</h3>
</Show>
<Show when={isMobile()}>
<Show
when={!pairsFetch.loading || headerText() === ""}
fallback={<LoadingSpinner />}>
<div>
<h2>{headerText()}</h2>
<hr />
<a href={props.bip21} class="btn btn-light">
{t("open_in_wallet")}
<a href={props.bip21}>
<QrCode asset={props.assetSend} data={props.bip21} />
</a>
</Show>
<hr />
<div class="btns" data-testid="pay-onchain-buttons">
<CopyButton
label="copy_amount"
data={() =>
formatAmount(
BigNumber(props.expectedAmount),
denomination(),
separator(),
)
}
/>
<CopyButton label="copy_address" data={props.address} />
<CopyButton label="copy_bip21" data={props.bip21} />
<hr />
<p
onClick={() => clipboard(props.address)}
class="address-box break-word">
{cropString(props.address)}
</p>
<Show when={props.assetSend === BTC}>
<hr class="spacer" />
<h3>{t("warning_expiry")}</h3>
</Show>
<Show when={isMobile()}>
<hr />
<a href={props.bip21} class="btn btn-light">
{t("open_in_wallet")}
</a>
</Show>
<hr />
<div class="btns" data-testid="pay-onchain-buttons">
<Show when={props.expectedAmount > 0}>
<CopyButton
label="copy_amount"
data={() =>
formatAmount(
BigNumber(props.expectedAmount),
denomination(),
separator(),
)
}
/>
</Show>

<CopyButton label="copy_address" data={props.address} />
<CopyButton label="copy_bip21" data={props.bip21} />
</div>
</div>
</div>
</Show>
);
};

Expand Down
1 change: 1 addition & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const defaults = {
email: "hi@bol.tz",
dnsOverHttps: "https://1.1.1.1/dns-query",
chatwootUrl: "https://support.boltz.exchange",
preimageValidation: "https://validate-payment.com",
};

type Asset = {
Expand Down
Loading

0 comments on commit 4ce904a

Please sign in to comment.