Skip to content

Commit

Permalink
feat: Submarine Swap preimage copy button
Browse files Browse the repository at this point in the history
  • Loading branch information
michael1011 committed Nov 14, 2024
1 parent f5ebe1f commit ed539c3
Show file tree
Hide file tree
Showing 10 changed files with 106 additions and 10 deletions.
14 changes: 12 additions & 2 deletions e2e/submarineSwap.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
bitcoinSendToAddress,
generateBitcoinBlock,
generateInvoiceLnd,
lookupInvoiceLnd,
} from "./utils";

test.describe("Submarine swap", () => {
Expand All @@ -28,7 +29,8 @@ test.describe("Submarine swap", () => {
await expect(inputSendAmount).toHaveValue(sendAmount);

const invoiceInput = page.locator("textarea[data-testid='invoice']");
await invoiceInput.fill(await generateInvoiceLnd(1000000));
const invoice = await generateInvoiceLnd(1000000);
await invoiceInput.fill(invoice);
const buttonCreateSwap = page.locator(
"button[data-testid='create-swap-button']",
);
Expand All @@ -48,7 +50,15 @@ test.describe("Submarine swap", () => {
await bitcoinSendToAddress(sendAddress, sendAmount);

await generateBitcoinBlock();
// TODO: verify amounts

await page.getByText("Copy preimage").click();
const preimage = await page.evaluate(() => {
return navigator.clipboard.readText();
});

const lookupRes = await lookupInvoiceLnd(invoice);
expect(lookupRes.state).toEqual("SETTLED");
expect(lookupRes.r_preimage).toEqual(preimage);
});

test("Create with LNURL", async ({ page }) => {
Expand Down
20 changes: 20 additions & 0 deletions e2e/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import bolt11 from "bolt11";
import { exec } from "child_process";
import { promisify } from "util";

Expand Down Expand Up @@ -60,6 +61,25 @@ export const generateInvoiceLnd = async (amount: number): Promise<string> => {
).payment_request as string;
};

export const lookupInvoiceLnd = async (
invoice: string,
): Promise<{ state: string; r_preimage: string }> => {
const decoded = bolt11.decode(invoice);
let paymentHash: string | undefined;

for (const tag of decoded.tags) {
switch (tag.tagName) {
case "payment_hash":
paymentHash = tag.data as string;
break;
}
}

return JSON.parse(
await execCommand(`lncli-sim 1 lookupinvoice ${paymentHash}`),
) as never;
};

export const getBolt12Offer = async (): Promise<string> => {
return JSON.parse(await execCommand("lightning-cli-sim 1 offer any ''"))
.bolt12 as string;
Expand Down
2 changes: 1 addition & 1 deletion regtest
1 change: 1 addition & 0 deletions src/i18n/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ const dict = {
timeout: "Timeout",
wallet_connect_failed: "Wallet connection failed: {{ error }}",
ledger_open_app_prompt: "Open Ethereum or RSK app",
copy_preimage: "Copy preimage",
},
de: {
language: "Deutsch",
Expand Down
38 changes: 36 additions & 2 deletions src/status/TransactionClaimed.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { useNavigate } from "@solidjs/router";
import { BigNumber } from "bignumber.js";
import { Show, createEffect, createSignal } from "solid-js";
import log from "loglevel";
import { Show, createEffect, createResource, createSignal } from "solid-js";

import CopyButton from "../components/CopyButton";
import LoadingSpinner from "../components/LoadingSpinner";
import { RBTC } from "../consts/Assets";
import { SwapType } from "../consts/Enums";
import { useGlobalContext } from "../context/Global";
import { usePayContext } from "../context/Pay";
import { getSubmarinePreimage } from "../utils/boltzClient";
import { formatAmount } from "../utils/denomination";
import { formatError } from "../utils/errors";
import { checkInvoicePreimage } from "../utils/invoice";
import { SubmarineSwap } from "../utils/swapCreator";

const Broadcasting = () => {
const { t } = useGlobalContext();
Expand All @@ -22,13 +28,38 @@ const Broadcasting = () => {

const TransactionClaimed = () => {
const navigate = useNavigate();

const { notify } = useGlobalContext();
const { swap } = usePayContext();
const { t, denomination, separator } = useGlobalContext();
const { t, denomination, separator, setSwapStorage } = useGlobalContext();

const [claimBroadcast, setClaimBroadcast] = createSignal<
boolean | undefined
>(undefined);

const [preimage] = createResource(async () => {
const submarine = swap() as SubmarineSwap;
if (submarine?.type !== SwapType.Submarine) {
return undefined;
}

if (submarine.preimage !== undefined) {
return submarine.preimage;
}

const res = await getSubmarinePreimage(submarine.id);
try {
await checkInvoicePreimage(submarine.invoice, res.preimage);
} catch (e) {
log.error("Preimage check failed", e);
notify("error", formatError(e));
}

submarine.preimage = res.preimage;
await setSwapStorage(submarine);
return res.preimage;
});

createEffect(() => {
const s = swap();
if (s === undefined || s === null) {
Expand Down Expand Up @@ -62,6 +93,9 @@ const TransactionClaimed = () => {
<span class="btn" onClick={() => navigate("/swap")}>
{t("new_swap")}
</span>
<Show when={!preimage.loading && preimage() !== undefined}>
<CopyButton label={"copy_preimage"} data={preimage()} />
</Show>
</Show>
</div>
);
Expand Down
3 changes: 3 additions & 0 deletions src/utils/boltzClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,9 @@ export const getChainSwapNewQuote = (id: string) =>
export const acceptChainSwapNewQuote = (id: string, amount: number) =>
fetcher<object>(`/v2/swap/chain/${id}/quote`, { amount });

export const getSubmarinePreimage = (id: string) =>
fetcher<{ preimage: string }>(`/v2/swap/submarine/${id}/preimage`);

export {
Pairs,
Contracts,
Expand Down
13 changes: 13 additions & 0 deletions src/utils/invoice.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { bech32, utf8 } from "@scure/base";
import { BigNumber } from "bignumber.js";
import { crypto } from "bitcoinjs-lib";
import bolt11 from "bolt11";
import log from "loglevel";

Expand Down Expand Up @@ -302,3 +303,15 @@ export const validateInvoiceForOffer = async (

throw "invoice does not belong to offer";
};

export const checkInvoicePreimage = async (
invoice: string,
preimage: string,
) => {
const dec = await decodeInvoice(invoice);
const hash = crypto.sha256(Buffer.from(preimage, "hex")).toString("hex");

if (hash !== dec.preimageHash) {
throw "invalid preimage";
}
};
1 change: 1 addition & 0 deletions src/utils/swapCreator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export type SwapBase = {
export type SubmarineSwap = SwapBase &
SubmarineCreatedResponse & {
invoice: string;
preimage?: string;
refundPrivateKey?: string;
};

Expand Down
11 changes: 6 additions & 5 deletions tests/status/TransactionClaimed.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { render, screen } from "@solidjs/testing-library";

import { BTC, LBTC, RBTC } from "../../src/consts/Assets";
import { SwapType } from "../../src/consts/Enums";
import i18n from "../../src/i18n/i18n";
import TransactionClaimed from "../../src/status/TransactionClaimed";
import { TestComponent, contextWrapper, payContext } from "../helper";
Expand All @@ -18,13 +19,13 @@ describe("TransactionClaimed", () => {

test.each`
name | swap
${"normal swaps"} | ${{ reverse: false }}
${"normal swaps"} | ${{ type: SwapType.Submarine }}
${"reverse swaps to RBTC"} | ${{
reverse: true,
asset: RBTC,
type: SwapType.Reverse,
assetReceive: RBTC,
}}
${"reverse swaps to BTC with claim transactions"} | ${{ reverse: true, asset: BTC, claimTx: "txid" }}
${"reverse swaps to L-BTC with claim transactions"} | ${{ reverse: true, asset: LBTC, claimTx: "txid" }}
${"reverse swaps to BTC with claim transactions"} | ${{ type: SwapType.Reverse, assetReceive: BTC, claimTx: "txid" }}
${"reverse swaps to L-BTC with claim transactions"} | ${{ type: SwapType.Reverse, assetReceive: LBTC, claimTx: "txid" }}
`("should show success for $name", async ({ swap }) => {
render(
() => (
Expand Down
13 changes: 13 additions & 0 deletions tests/utils/invoice.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import bolt11 from "bolt11";

import { setConfig } from "../../src/config";
import {
checkInvoicePreimage,
decodeInvoice,
extractAddress,
extractInvoice,
Expand Down Expand Up @@ -153,4 +154,16 @@ describe("invoice", () => {
},
);
});

describe("checkInvoicePreimage", () => {
test.each`
preimage | invoice
${"4c8176e16eab0a2282bb47ac8dfceddabffa73e849ea9b6662c44f868a2b4d2a"} | ${"lnbcrt1m1pnnvd2tpp55g70uxevgu3ddpkkn8yvwu6ehvme7lr464gdv7thelmzjz7fyqsqdqqcqzzsxqyz5vqsp5l6n65lrnqmp7lhx9wen493zvkg9gfrksx0lren5m34wy5ge2x6fq9p4gqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpqysgqdxrky9sy3sk6zuh6x9gtga2trtqapyj69zel0lp6xv8xmrqzst3ja2mxsfsaq7ccffnl27pyzyhj8t8eylq792khl3ha3x8qgxca87qpfyqk7l"}
`(
"should check preimage for invoice",
async ({ invoice, preimage }) => {
await checkInvoicePreimage(invoice, preimage);
},
);
});
});

0 comments on commit ed539c3

Please sign in to comment.