aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-02-24 13:01:02 -0800
committerAdam Soltys <[email protected]>2026-02-24 13:01:02 -0800
commitdb59579a2efa8416f78c855faa7d7e62048447e5 (patch)
tree28444081de0120f21f7596198937bdd52fe43495
parent7eec7ac0b322b510b4e2cedef62422b6043d6405 (diff)
downloadcoinos-ui-db59579a2efa8416f78c855faa7d7e62048447e5.tar.gz
coinos-ui-db59579a2efa8416f78c855faa7d7e62048447e5.zip
lint
-rw-r--r--.github/workflows/lint.yml15
-rw-r--r--.husky/pre-commit1
-rw-r--r--bun.lock86
-rw-r--r--lint-staged.config.js3
-rw-r--r--package.json5
-rw-r--r--src/lib/ark.ts22
-rw-r--r--src/lib/crypto.ts15
-rw-r--r--src/lib/nip07.ts164
-rw-r--r--src/lib/nip17.ts45
-rw-r--r--src/lib/nip44.ts4
-rw-r--r--src/lib/walletEntropy.ts3
-rw-r--r--src/routes/(app)/ln/[lnurl]/+page.server.ts4
-rw-r--r--src/routes/(app)/payment/[id]/+page.svelte17
-rw-r--r--src/routes/(static)/login/+page.server.ts3
-rw-r--r--svelte.config.js8
-rw-r--r--test/e2e/ark-custodial-receive.spec.ts17
-rw-r--r--test/e2e/ark-send-custodial.spec.ts5
-rw-r--r--test/e2e/custodial-bitcoin-send.spec.ts20
-rw-r--r--test/e2e/custodial-receive-bitcoin.spec.ts18
-rw-r--r--test/e2e/custodial-receive-lightning.spec.ts26
-rw-r--r--test/e2e/custodial-send-external-btc.spec.ts4
-rw-r--r--test/e2e/custodial-send-external-ln.spec.ts4
-rw-r--r--test/e2e/custodial-send-lightning.spec.ts18
-rw-r--r--test/e2e/custodial-send-username.spec.ts10
-rw-r--r--test/e2e/helpers.ts92
-rw-r--r--test/e2e/vault-receive-bitcoin.spec.ts12
-rw-r--r--test/e2e/vault-send-bitcoin.spec.ts27
-rw-r--r--test/e2e/vault-send-external-btc.spec.ts24
-rw-r--r--test/e2e/vault-send-to-custodial-btc.spec.ts20
29 files changed, 342 insertions, 350 deletions
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 00000000..c7c9dd41
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,15 @@
+name: Lint & Format
+
+on:
+ push:
+ branches: [master]
+ pull_request:
+
+jobs:
+ lint:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: oven-sh/setup-bun@v2
+ - run: bun install --frozen-lockfile
+ - run: bun run lint
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100644
index 00000000..ea5a55b6
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1 @@
+bunx lint-staged
diff --git a/bun.lock b/bun.lock
index 2a85c910..6587c9e7 100644
--- a/bun.lock
+++ b/bun.lock
@@ -38,8 +38,10 @@
"daisyui": "^5.5.17",
"date-fns": "^4.1.0",
"esptool-js": "^0.5.7",
+ "husky": "^9.1.7",
"iconify-icon": "^3.0.2",
"js-cookie": "^3.0.5",
+ "lint-staged": "^16.2.7",
"nostr-tools": "^2.23.0",
"oxfmt": "^0.35.0",
"oxlint": "^1.35.0",
@@ -490,6 +492,8 @@
"aggregate-error": ["[email protected]", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="],
+ "ansi-escapes": ["[email protected]", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg=="],
+
"ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
@@ -576,6 +580,10 @@
"clean-stack": ["[email protected]", "", {}, "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="],
+ "cli-cursor": ["[email protected]", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="],
+
+ "cli-truncate": ["[email protected]", "", { "dependencies": { "slice-ansi": "^7.1.0", "string-width": "^8.0.0" } }, "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A=="],
+
"cliui": ["[email protected]", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
"clsx": ["[email protected]", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
@@ -588,6 +596,8 @@
"color-string": ["[email protected]", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="],
+ "colorette": ["[email protected]", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="],
+
"commander": ["[email protected]", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
"commondir": ["[email protected]", "", {}, "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="],
@@ -704,6 +714,8 @@
"env-paths": ["[email protected]", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="],
+ "environment": ["[email protected]", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="],
+
"error-ex": ["[email protected]", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="],
"esbuild": ["[email protected]", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.2", "@esbuild/android-arm": "0.27.2", "@esbuild/android-arm64": "0.27.2", "@esbuild/android-x64": "0.27.2", "@esbuild/darwin-arm64": "0.27.2", "@esbuild/darwin-x64": "0.27.2", "@esbuild/freebsd-arm64": "0.27.2", "@esbuild/freebsd-x64": "0.27.2", "@esbuild/linux-arm": "0.27.2", "@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-mips64el": "0.27.2", "@esbuild/linux-ppc64": "0.27.2", "@esbuild/linux-riscv64": "0.27.2", "@esbuild/linux-s390x": "0.27.2", "@esbuild/linux-x64": "0.27.2", "@esbuild/netbsd-arm64": "0.27.2", "@esbuild/netbsd-x64": "0.27.2", "@esbuild/openbsd-arm64": "0.27.2", "@esbuild/openbsd-x64": "0.27.2", "@esbuild/openharmony-arm64": "0.27.2", "@esbuild/sunos-x64": "0.27.2", "@esbuild/win32-arm64": "0.27.2", "@esbuild/win32-ia32": "0.27.2", "@esbuild/win32-x64": "0.27.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw=="],
@@ -720,6 +732,8 @@
"estree-walker": ["[email protected]", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
+ "eventemitter3": ["[email protected]", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="],
+
"expand-template": ["[email protected]", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="],
"fast-fifo": ["[email protected]", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="],
@@ -756,6 +770,8 @@
"get-caller-file": ["[email protected]", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
+ "get-east-asian-width": ["[email protected]", "", {}, "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA=="],
+
"get-pkg-repo": ["[email protected]", "", { "dependencies": { "@hutson/parse-repository-url": "^3.0.0", "hosted-git-info": "^4.0.0", "through2": "^2.0.0", "yargs": "^16.2.0" }, "bin": { "get-pkg-repo": "src/cli.js" } }, "sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA=="],
"git-raw-commits": ["[email protected]", "", { "dependencies": { "dargs": "^7.0.0", "lodash": "^4.17.15", "meow": "^8.0.0", "split2": "^3.0.0", "through2": "^4.0.0" }, "bin": { "git-raw-commits": "cli.js" } }, "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A=="],
@@ -790,6 +806,8 @@
"hosted-git-info": ["[email protected]", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="],
+ "husky": ["[email protected]", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="],
+
"iconify-icon": ["[email protected]", "", { "dependencies": { "@iconify/types": "^2.0.0" } }, "sha512-DYPAumiUeUeT/GHT8x2wrAVKn1FqZJqFH0Y5pBefapWRreV1BBvqBVMb0020YQ2njmbR59r/IathL2d2OrDrxA=="],
"ieee754": ["[email protected]", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
@@ -888,6 +906,10 @@
"lines-and-columns": ["[email protected]", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
+ "lint-staged": ["[email protected]", "", { "dependencies": { "commander": "^14.0.2", "listr2": "^9.0.5", "micromatch": "^4.0.8", "nano-spawn": "^2.0.0", "pidtree": "^0.6.0", "string-argv": "^0.3.2", "yaml": "^2.8.1" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow=="],
+
+ "listr2": ["[email protected]", "", { "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g=="],
+
"load-json-file": ["[email protected]", "", { "dependencies": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", "pify": "^3.0.0", "strip-bom": "^3.0.0" } }, "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw=="],
"locate-character": ["[email protected]", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
@@ -900,6 +922,8 @@
"lodash.merge": ["[email protected]", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
+ "log-update": ["[email protected]", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="],
+
"lru-cache": ["[email protected]", "", {}, "sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ=="],
"magic-string": ["[email protected]", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
@@ -918,6 +942,8 @@
"micromatch": ["[email protected]", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
+ "mimic-function": ["[email protected]", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="],
+
"mimic-response": ["[email protected]", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
"min-indent": ["[email protected]", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="],
@@ -944,6 +970,8 @@
"ms": ["[email protected]", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="],
+ "nano-spawn": ["[email protected]", "", {}, "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw=="],
+
"nanoid": ["[email protected]", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"napi-build-utils": ["[email protected]", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="],
@@ -972,6 +1000,8 @@
"once": ["[email protected]", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
+ "onetime": ["[email protected]", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="],
+
"open": ["[email protected]", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="],
"oxfmt": ["[email protected]", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.35.0", "@oxfmt/binding-android-arm64": "0.35.0", "@oxfmt/binding-darwin-arm64": "0.35.0", "@oxfmt/binding-darwin-x64": "0.35.0", "@oxfmt/binding-freebsd-x64": "0.35.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.35.0", "@oxfmt/binding-linux-arm-musleabihf": "0.35.0", "@oxfmt/binding-linux-arm64-gnu": "0.35.0", "@oxfmt/binding-linux-arm64-musl": "0.35.0", "@oxfmt/binding-linux-ppc64-gnu": "0.35.0", "@oxfmt/binding-linux-riscv64-gnu": "0.35.0", "@oxfmt/binding-linux-riscv64-musl": "0.35.0", "@oxfmt/binding-linux-s390x-gnu": "0.35.0", "@oxfmt/binding-linux-x64-gnu": "0.35.0", "@oxfmt/binding-linux-x64-musl": "0.35.0", "@oxfmt/binding-openharmony-arm64": "0.35.0", "@oxfmt/binding-win32-arm64-msvc": "0.35.0", "@oxfmt/binding-win32-ia32-msvc": "0.35.0", "@oxfmt/binding-win32-x64-msvc": "0.35.0" }, "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-QYeXWkP+aLt7utt5SLivNIk09glWx9QE235ODjgcEZ3sd1VMaUBSpLymh6ZRCA76gD2rMP4bXanUz/fx+nLM9Q=="],
@@ -1010,6 +1040,8 @@
"picomatch": ["[email protected]", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
+ "pidtree": ["[email protected]", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="],
+
"pify": ["[email protected]", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
"playwright": ["[email protected]", "", { "dependencies": { "playwright-core": "1.58.2" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A=="],
@@ -1070,8 +1102,12 @@
"resolve": ["[email protected]", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
+ "restore-cursor": ["[email protected]", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="],
+
"reusify": ["[email protected]", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
+ "rfdc": ["[email protected]", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="],
+
"rimraf": ["[email protected]", "", { "dependencies": { "glob": "^11.0.0", "package-json-from-dist": "^1.0.0" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A=="],
"rolldown": ["[email protected]", "", { "dependencies": { "@oxc-project/types": "=0.111.0", "@rolldown/pluginutils": "1.0.0-rc.2" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.2", "@rolldown/binding-darwin-arm64": "1.0.0-rc.2", "@rolldown/binding-darwin-x64": "1.0.0-rc.2", "@rolldown/binding-freebsd-x64": "1.0.0-rc.2", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.2", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.2", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.2", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.2", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.2", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.2", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.2", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.2", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.2" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-1g/8Us9J8sgJGn3hZfBecX1z4U3y5KO7V/aV2U1M/9UUzLNqHA8RfFQ/NPT7HLxOIldyIgrcjaYTRvA81KhJIg=="],
@@ -1144,6 +1180,8 @@
"streamx": ["[email protected]", "", { "dependencies": { "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" }, "optionalDependencies": { "bare-events": "^2.2.0" } }, "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA=="],
+ "string-argv": ["[email protected]", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="],
+
"string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
"string-width-cjs": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
@@ -1290,6 +1328,8 @@
"yallist": ["[email protected]", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
+ "yaml": ["[email protected]", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="],
+
"yargs": ["[email protected]", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
"yargs-parser": ["[email protected]", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
@@ -1400,6 +1440,10 @@
"chalk/ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
+ "cli-truncate/slice-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="],
+
+ "cli-truncate/string-width": ["[email protected]", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw=="],
+
"conventional-changelog-writer/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"conventional-commits-parser/split2": ["[email protected]", "", { "dependencies": { "readable-stream": "^3.0.0" } }, "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg=="],
@@ -1436,8 +1480,18 @@
"hosted-git-info/lru-cache": ["[email protected]", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
+ "lint-staged/commander": ["[email protected]", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="],
+
+ "listr2/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
+
"load-json-file/pify": ["[email protected]", "", {}, "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="],
+ "log-update/slice-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="],
+
+ "log-update/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
+
+ "log-update/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
+
"meow/read-pkg-up": ["[email protected]", "", { "dependencies": { "find-up": "^4.1.0", "read-pkg": "^5.2.0", "type-fest": "^0.8.1" } }, "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg=="],
"meow/type-fest": ["[email protected]", "", {}, "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw=="],
@@ -1474,6 +1528,8 @@
"replace/yargs": ["[email protected]", "", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="],
+ "restore-cursor/signal-exit": ["[email protected]", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
+
"rollup/fsevents": ["[email protected]", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"sharp/node-addon-api": ["[email protected]", "", {}, "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="],
@@ -1544,6 +1600,12 @@
"chalk/ansi-styles/color-convert": ["[email protected]", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
+ "cli-truncate/slice-ansi/ansi-styles": ["[email protected]", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
+
+ "cli-truncate/slice-ansi/is-fullwidth-code-point": ["[email protected]", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="],
+
+ "cli-truncate/string-width/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
+
"del/rimraf/glob": ["[email protected]", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
"fs-minipass/minipass/yallist": ["[email protected]", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
@@ -1556,6 +1618,22 @@
"hosted-git-info/lru-cache/yallist": ["[email protected]", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
+ "listr2/wrap-ansi/ansi-styles": ["[email protected]", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
+
+ "listr2/wrap-ansi/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
+
+ "listr2/wrap-ansi/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
+
+ "log-update/slice-ansi/ansi-styles": ["[email protected]", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
+
+ "log-update/slice-ansi/is-fullwidth-code-point": ["[email protected]", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="],
+
+ "log-update/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
+
+ "log-update/wrap-ansi/ansi-styles": ["[email protected]", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
+
+ "log-update/wrap-ansi/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
+
"meow/read-pkg-up/read-pkg": ["[email protected]", "", { "dependencies": { "@types/normalize-package-data": "^2.4.0", "normalize-package-data": "^2.5.0", "parse-json": "^5.0.0", "type-fest": "^0.6.0" } }, "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg=="],
"meow/read-pkg-up/type-fest": ["[email protected]", "", {}, "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="],
@@ -1610,12 +1688,20 @@
"chalk/ansi-styles/color-convert/color-name": ["[email protected]", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
+ "cli-truncate/string-width/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
+
"del/rimraf/glob/minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
"get-pkg-repo/through2/readable-stream/safe-buffer": ["[email protected]", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
"get-pkg-repo/through2/readable-stream/string_decoder": ["[email protected]", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
+ "listr2/wrap-ansi/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
+
+ "listr2/wrap-ansi/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
+
+ "log-update/wrap-ansi/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
+
"meow/read-pkg-up/read-pkg/normalize-package-data": ["[email protected]", "", { "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" } }, "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA=="],
"meow/read-pkg-up/read-pkg/parse-json": ["[email protected]", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="],
diff --git a/lint-staged.config.js b/lint-staged.config.js
new file mode 100644
index 00000000..ecf030de
--- /dev/null
+++ b/lint-staged.config.js
@@ -0,0 +1,3 @@
+export default {
+ "*.{js,ts,svelte}": () => "oxfmt .",
+};
diff --git a/package.json b/package.json
index a5bdef2d..14e672b9 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,8 @@
"test:e2e:ark:debug": "test/e2e/run.sh debug",
"lint": "oxlint . && oxfmt --check .",
"lint:fix": "oxlint --fix . && oxfmt .",
- "format": "oxfmt ."
+ "format": "oxfmt .",
+ "prepare": "husky"
},
"dependencies": {
"@arkade-os/sdk": "^0.3.12",
@@ -52,8 +53,10 @@
"daisyui": "^5.5.17",
"date-fns": "^4.1.0",
"esptool-js": "^0.5.7",
+ "husky": "^9.1.7",
"iconify-icon": "^3.0.2",
"js-cookie": "^3.0.5",
+ "lint-staged": "^16.2.7",
"nostr-tools": "^2.23.0",
"oxfmt": "^0.35.0",
"oxlint": "^1.35.0",
diff --git a/src/lib/ark.ts b/src/lib/ark.ts
index a3939a40..82d1ccce 100644
--- a/src/lib/ark.ts
+++ b/src/lib/ark.ts
@@ -104,7 +104,11 @@ export const syncTransactions = async (aid: string) => {
if (arkSending) return;
const p = _syncTransactions(aid);
syncPromise = p;
- try { return await p; } finally { if (syncPromise === p) syncPromise = null; }
+ try {
+ return await p;
+ } finally {
+ if (syncPromise === p) syncPromise = null;
+ }
};
export const vtxoKey = (v: { txid: string; vout: number }) => `${v.txid}:${v.vout}`;
@@ -131,9 +135,7 @@ let syncPromise: Promise<any> | null = null;
const withTimeout = <T>(promise: Promise<T>, ms: number, label: string) =>
Promise.race([
promise,
- new Promise<T>((_, reject) =>
- setTimeout(() => reject(new Error(`${label} timed out`)), ms),
- ),
+ new Promise<T>((_, reject) => setTimeout(() => reject(new Error(`${label} timed out`)), ms)),
]);
export const sendArk = async (address: string, amount: number) => {
@@ -158,21 +160,13 @@ export const sendArk = async (address: string, amount: number) => {
}
let txid;
try {
- txid = await withTimeout(
- wallet.sendBitcoin({ address, amount }),
- 45_000,
- "Ark send",
- );
+ txid = await withTimeout(wallet.sendBitcoin({ address, amount }), 45_000, "Ark send");
} catch (e: any) {
if (e?.message?.includes("VTXO_ALREADY_REGISTERED")) {
await wallet.settle();
const vtxos = await wallet.getVtxos({ spendableOnly: false });
if (vtxos?.length) addProcessedVtxos(vtxos.map(vtxoKey));
- txid = await withTimeout(
- wallet.sendBitcoin({ address, amount }),
- 45_000,
- "Ark send retry",
- );
+ txid = await withTimeout(wallet.sendBitcoin({ address, amount }), 45_000, "Ark send retry");
} else {
throw e;
}
diff --git a/src/lib/crypto.ts b/src/lib/crypto.ts
index d941d894..80a6364a 100644
--- a/src/lib/crypto.ts
+++ b/src/lib/crypto.ts
@@ -2,17 +2,11 @@ export function isPrfEncrypted(value: string): boolean {
return value.startsWith("{");
}
-async function importPrfKey(
- prfKey: ArrayBuffer,
- usages: KeyUsage[],
-): Promise<CryptoKey> {
+async function importPrfKey(prfKey: ArrayBuffer, usages: KeyUsage[]): Promise<CryptoKey> {
return crypto.subtle.importKey("raw", prfKey, "AES-GCM", false, usages);
}
-export async function prfEncrypt(
- prfKey: ArrayBuffer,
- plaintext: Uint8Array,
-): Promise<string> {
+export async function prfEncrypt(prfKey: ArrayBuffer, plaintext: Uint8Array): Promise<string> {
const key = await importPrfKey(prfKey, ["encrypt"]);
const iv = crypto.getRandomValues(new Uint8Array(12));
const ciphertext = new Uint8Array(
@@ -29,10 +23,7 @@ export async function prfEncrypt(
});
}
-export async function prfDecrypt(
- prfKey: ArrayBuffer,
- encrypted: string,
-): Promise<Uint8Array> {
+export async function prfDecrypt(prfKey: ArrayBuffer, encrypted: string): Promise<Uint8Array> {
const { iv, ct } = JSON.parse(encrypted);
const key = await importPrfKey(prfKey, ["decrypt"]);
const ivBytes = fromBase64Url(iv);
diff --git a/src/lib/nip07.ts b/src/lib/nip07.ts
index a0af0861..093e2ead 100644
--- a/src/lib/nip07.ts
+++ b/src/lib/nip07.ts
@@ -1,102 +1,108 @@
-import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
-import { finalizeEvent } from 'nostr-tools/pure';
-import * as libnip44 from '$lib/nip44';
-import { u } from '$lib/nip44';
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { finalizeEvent } from "nostr-tools/pure";
+import * as libnip44 from "$lib/nip44";
+import { u } from "$lib/nip44";
-import { eventToSign, signer } from '$lib/store';
-import { get } from 'svelte/store';
+import { eventToSign, signer } from "$lib/store";
+import { get } from "svelte/store";
// Ensures we have access to a secret key, triggering the signer modal if needed.
// Returns the sk as Uint8Array, or null if using NIP-07 extension.
export const ensureSigner = async (userPubkey: string): Promise<Uint8Array | null> => {
- // Check if NIP-07 extension is available and matches user
- if (window.nostr) {
- try {
- const extPubkey = await window.nostr.getPublicKey();
- if (extPubkey === userPubkey) {
- return null; // Use NIP-07
- }
- } catch (e) {
- // Extension didn't work, fall through to signer
- }
+ // Check if NIP-07 extension is available and matches user
+ if (window.nostr) {
+ try {
+ const extPubkey = await window.nostr.getPublicKey();
+ if (extPubkey === userPubkey) {
+ return null; // Use NIP-07
+ }
+ } catch (e) {
+ // Extension didn't work, fall through to signer
}
+ }
- // Check if signer already has a valid sk (64 hex chars = 32 bytes)
- const currentSigner = get(signer);
- if (currentSigner?.ready && currentSigner?.params?.sk && currentSigner.params.sk.length === 64) {
- return hexToBytes(currentSigner.params.sk);
- }
+ // Check if signer already has a valid sk (64 hex chars = 32 bytes)
+ const currentSigner = get(signer);
+ if (currentSigner?.ready && currentSigner?.params?.sk && currentSigner.params.sk.length === 64) {
+ return hexToBytes(currentSigner.params.sk);
+ }
- // Clear invalid signer state before triggering modal
- if (currentSigner?.ready && (!currentSigner?.params?.sk || currentSigner.params.sk.length !== 64)) {
- signer.set(undefined);
- }
+ // Clear invalid signer state before triggering modal
+ if (
+ currentSigner?.ready &&
+ (!currentSigner?.params?.sk || currentSigner.params.sk.length !== 64)
+ ) {
+ signer.set(undefined);
+ }
- // Trigger the signer modal with a dummy event
- const dummyEvent = { kind: 14, content: 'DM encryption', tags: [], created_at: Math.floor(Date.now() / 1000) };
- eventToSign.set(dummyEvent);
+ // Trigger the signer modal with a dummy event
+ const dummyEvent = {
+ kind: 14,
+ content: "DM encryption",
+ tags: [],
+ created_at: Math.floor(Date.now() / 1000),
+ };
+ eventToSign.set(dummyEvent);
- // Wait for signer to be ready
- const signerValue = await new Promise<any>((resolve, reject) => {
- let unsubscribe: () => void;
- unsubscribe = signer.subscribe((v) => {
- if (v === 'cancel') {
- setTimeout(() => unsubscribe?.());
- eventToSign.set(null);
- reject(new Error('cancelled'));
- }
- if (v?.ready) {
- setTimeout(() => unsubscribe?.());
- eventToSign.set(null);
- resolve(v);
- }
- });
+ // Wait for signer to be ready
+ const signerValue = await new Promise<any>((resolve, reject) => {
+ let unsubscribe: () => void;
+ unsubscribe = signer.subscribe((v) => {
+ if (v === "cancel") {
+ setTimeout(() => unsubscribe?.());
+ eventToSign.set(null);
+ reject(new Error("cancelled"));
+ }
+ if (v?.ready) {
+ setTimeout(() => unsubscribe?.());
+ eventToSign.set(null);
+ resolve(v);
+ }
});
+ });
- if (signerValue?.params?.sk) {
- return hexToBytes(signerValue.params.sk);
- }
+ if (signerValue?.params?.sk) {
+ return hexToBytes(signerValue.params.sk);
+ }
- // If extension method was selected
- if (signerValue?.method === 'extension') {
- return null; // Use NIP-07
- }
+ // If extension method was selected
+ if (signerValue?.method === "extension") {
+ return null; // Use NIP-07
+ }
- throw new Error('No signing method available');
-}
+ throw new Error("No signing method available");
+};
// Signs an event using NIP-07 if available, otherwise getPrivateKey()
export const signEvent = async (event: any, user: any): Promise<any> => {
- const sk = await ensureSigner(user.pubkey);
- if (sk === null) {
- return (window as any).nostr.signEvent(event);
- } else {
- return finalizeEvent(event, sk);
- }
-}
+ const sk = await ensureSigner(user.pubkey);
+ if (sk === null) {
+ return (window as any).nostr.signEvent(event);
+ } else {
+ return finalizeEvent(event, sk);
+ }
+};
// Encrypts an event in NIP-44 using NIP-07 if available, otherwise getPrivateKey()
export const encrypt = async (json: any, user: any, receiverPK: string): Promise<string> => {
- const senderSK = await ensureSigner(user.pubkey);
- if (senderSK === null) {
- return (window as any).nostr.nip44.encrypt(
- receiverPK, JSON.stringify(json));
- } else {
- const senderSKString = bytesToHex(senderSK);
- const ckey = u.getConversationKey(senderSKString, receiverPK);
- return libnip44.encrypt(JSON.stringify(json), ckey);
- }
-}
+ const senderSK = await ensureSigner(user.pubkey);
+ if (senderSK === null) {
+ return (window as any).nostr.nip44.encrypt(receiverPK, JSON.stringify(json));
+ } else {
+ const senderSKString = bytesToHex(senderSK);
+ const ckey = u.getConversationKey(senderSKString, receiverPK);
+ return libnip44.encrypt(JSON.stringify(json), ckey);
+ }
+};
// Decrypts an event in NIP-44 using NIP-07 if available, otherwise getPrivateKey()
export const decrypt = async (encrypted: string, user: any, receiverPK: string): Promise<any> => {
- const senderSK = await ensureSigner(user.pubkey);
- if (senderSK === null) {
- return JSON.parse(await (window as any).nostr.nip44.decrypt(
- receiverPK, encrypted));
- } else {
- const senderSKString = bytesToHex(senderSK);
- const ckey = u.getConversationKey(senderSKString, receiverPK);
- return JSON.parse(libnip44.decrypt(encrypted, ckey));
- }
-}
+ const senderSK = await ensureSigner(user.pubkey);
+ if (senderSK === null) {
+ return JSON.parse(await (window as any).nostr.nip44.decrypt(receiverPK, encrypted));
+ } else {
+ const senderSKString = bytesToHex(senderSK);
+ const ckey = u.getConversationKey(senderSKString, receiverPK);
+ return JSON.parse(libnip44.decrypt(encrypted, ckey));
+ }
+};
diff --git a/src/lib/nip17.ts b/src/lib/nip17.ts
index 0398c426..97191a18 100644
--- a/src/lib/nip17.ts
+++ b/src/lib/nip17.ts
@@ -5,18 +5,18 @@ import { SimplePool } from "nostr-tools/pool";
import { Relay } from "nostr-tools/relay";
import { unwrapEvent } from "nostr-tools/nip17";
-import { ensureSigner } from '$lib/nip07';
-import { relaysSupporting } from '$lib/nip11';
-import { expired, expiration } from '$lib/nip40';
-import { encrypt, u } from '$lib/nip44';
+import { ensureSigner } from "$lib/nip07";
+import { relaysSupporting } from "$lib/nip11";
+import { expired, expiration } from "$lib/nip40";
+import { encrypt, u } from "$lib/nip44";
const TWO_DAYS = 2 * 24 * 60 * 60;
const randomTimestamp = () => Math.floor(Date.now() / 1000 - Math.random() * TWO_DAYS);
const pool = new SimplePool();
let relayListCache = new Map();
-import { PUBLIC_DM_RELAYS } from '$env/static/public';
-const DM_RELAYS_LIST = PUBLIC_DM_RELAYS.split(',');
+import { PUBLIC_DM_RELAYS } from "$env/static/public";
+const DM_RELAYS_LIST = PUBLIC_DM_RELAYS.split(",");
const DM_FETCH_LIMIT = 65536;
// Create a NIP-17 message using a provided sender secret key.
@@ -283,7 +283,14 @@ const publishToPreferred = async (
}
};
-const createAndPublishMessageEvent = async (sk: Uint8Array | null, message: string, userPK: string, recipientPK: string, eventFor: string, expiryDays?: number) => {
+const createAndPublishMessageEvent = async (
+ sk: Uint8Array | null,
+ message: string,
+ userPK: string,
+ recipientPK: string,
+ eventFor: string,
+ expiryDays?: number,
+) => {
let event;
if (sk === null) {
event = await createNIP17MessageNIP07(message, userPK, recipientPK, eventFor, expiryDays);
@@ -303,12 +310,30 @@ export const send = async (message: string, user: any, recipient: any, expiryDay
if (user.pubkey === recipient.pubkey) {
return createAndPublishMessageEvent(
- sk, message, user.pubkey, recipient.pubkey, user.pubkey, expiryDays);
+ sk,
+ message,
+ user.pubkey,
+ recipient.pubkey,
+ user.pubkey,
+ expiryDays,
+ );
} else {
const p1 = createAndPublishMessageEvent(
- sk, message, user.pubkey, recipient.pubkey, recipient.pubkey, expiryDays);
+ sk,
+ message,
+ user.pubkey,
+ recipient.pubkey,
+ recipient.pubkey,
+ expiryDays,
+ );
const p2 = createAndPublishMessageEvent(
- sk, message, user.pubkey, recipient.pubkey, user.pubkey, expiryDays);
+ sk,
+ message,
+ user.pubkey,
+ recipient.pubkey,
+ user.pubkey,
+ expiryDays,
+ );
return Promise.all([p1, p2]);
}
};
diff --git a/src/lib/nip44.ts b/src/lib/nip44.ts
index 6025a54f..19384b9b 100644
--- a/src/lib/nip44.ts
+++ b/src/lib/nip44.ts
@@ -24,7 +24,9 @@ export const u = {
},
getConversationKey(privkeyA: string, pubkeyB: string): Uint8Array {
- const sharedX = secp256k1.getSharedSecret(hexToBytes(privkeyA), hexToBytes("02" + pubkeyB)).subarray(1, 33);
+ const sharedX = secp256k1
+ .getSharedSecret(hexToBytes(privkeyA), hexToBytes("02" + pubkeyB))
+ .subarray(1, 33);
return hkdf_extract(sha256, sharedX, utf8ToBytes("nip44-v2"));
},
diff --git a/src/lib/walletEntropy.ts b/src/lib/walletEntropy.ts
index f022a2cc..25bd49b8 100644
--- a/src/lib/walletEntropy.ts
+++ b/src/lib/walletEntropy.ts
@@ -10,8 +10,7 @@ const DERIVATION_EVENT = {
async function hashSignature(sig: string): Promise<ArrayBuffer> {
const sigBytes = new Uint8Array(sig.length / 2);
- for (let i = 0; i < sig.length; i += 2)
- sigBytes[i / 2] = parseInt(sig.slice(i, i + 2), 16);
+ for (let i = 0; i < sig.length; i += 2) sigBytes[i / 2] = parseInt(sig.slice(i, i + 2), 16);
return crypto.subtle.digest("SHA-256", sigBytes);
}
diff --git a/src/routes/(app)/ln/[lnurl]/+page.server.ts b/src/routes/(app)/ln/[lnurl]/+page.server.ts
index eec5ce31..39bc0e72 100644
--- a/src/routes/(app)/ln/[lnurl]/+page.server.ts
+++ b/src/routes/(app)/ln/[lnurl]/+page.server.ts
@@ -12,7 +12,9 @@ export async function load({ params, parent }) {
let data;
const { lnurl } = params;
- const url = Buffer.from(fromWords(decode(lnurl as `${string}1${string}`, 20000).words)).toString();
+ const url = Buffer.from(
+ fromWords(decode(lnurl as `${string}1${string}`, 20000).words),
+ ).toString();
try {
data = await get(`/decode?text=${lnurl}`);
diff --git a/src/routes/(app)/payment/[id]/+page.svelte b/src/routes/(app)/payment/[id]/+page.svelte
index 415581b6..84a728a0 100644
--- a/src/routes/(app)/payment/[id]/+page.svelte
+++ b/src/routes/(app)/payment/[id]/+page.svelte
@@ -144,7 +144,7 @@
<div>
<span class="text-lg text-secondary">{$t("payments.date")}</span>
<div>
- {format(new Date(created), "MMM d, yyyy", { locale })} {format(new Date(created), "h:mmaaa", { locale })}
+ {format(new Date(created), "MMM d, yyyy", { locale })}{format(new Date(created), "h:mmaaa", { locale })}
</div>
</div>
@@ -199,7 +199,10 @@
<span class="text-lg text-secondary">Txid</span>
<div class="flex">
<div>{hash}</div>
- <button class="flex font-bold hover:opacity-80 mb-auto my-auto" onclick={() => copy(hash)} aria-label="Copy transaction ID"
+ <button
+ class="flex font-bold hover:opacity-80 mb-auto my-auto"
+ onclick={() => copy(hash)}
+ aria-label="Copy transaction ID"
></button>
</div>
</div>
@@ -218,7 +221,10 @@
{txid}
</a>
</div>
- <button class="flex font-bold hover:opacity-80 mb-auto my-auto" onclick={() => copy(txid)} aria-label="Copy transaction ID"
+ <button
+ class="flex font-bold hover:opacity-80 mb-auto my-auto"
+ onclick={() => copy(txid)}
+ aria-label="Copy transaction ID"
></button>
</div>
</div>
@@ -229,7 +235,10 @@
<span class="text-lg text-secondary">Path</span>
<div class="flex">
<div>{path}</div>
- <button class="flex font-bold hover:opacity-80 mb-auto my-auto" onclick={() => copy(path)} aria-label="Copy path"
+ <button
+ class="flex font-bold hover:opacity-80 mb-auto my-auto"
+ onclick={() => copy(path)}
+ aria-label="Copy path"
></button>
</div>
</div>
diff --git a/src/routes/(static)/login/+page.server.ts b/src/routes/(static)/login/+page.server.ts
index 18f9cb20..4200bc01 100644
--- a/src/routes/(static)/login/+page.server.ts
+++ b/src/routes/(static)/login/+page.server.ts
@@ -42,8 +42,7 @@ export const actions = {
passkey: async ({ cookies, fetch, request }) => {
const form = await fd(request);
let { credential, challengeId, loginRedirect } = form;
- if (loginRedirect === "null" || loginRedirect === "undefined")
- loginRedirect = undefined;
+ if (loginRedirect === "null" || loginRedirect === "undefined") loginRedirect = undefined;
credential = JSON.parse(credential);
const maxAge = 380 * 24 * 60 * 60;
diff --git a/svelte.config.js b/svelte.config.js
index ef65bfeb..ae2de4cb 100644
--- a/svelte.config.js
+++ b/svelte.config.js
@@ -18,7 +18,13 @@ const config = {
},
},
onwarn: (warning, handler) => {
- if (warning.code.includes("caption") || warning.filename.includes("Toast") || warning.code === "css_unknown_at_rule" || warning.code === "state_referenced_locally") return;
+ if (
+ warning.code.includes("caption") ||
+ warning.filename.includes("Toast") ||
+ warning.code === "css_unknown_at_rule" ||
+ warning.code === "state_referenced_locally"
+ )
+ return;
handler(warning);
},
};
diff --git a/test/e2e/ark-custodial-receive.spec.ts b/test/e2e/ark-custodial-receive.spec.ts
index d50cda55..41b63733 100644
--- a/test/e2e/ark-custodial-receive.spec.ts
+++ b/test/e2e/ark-custodial-receive.spec.ts
@@ -9,9 +9,7 @@ import {
sendArkFromTestEndpoint,
} from "./helpers";
-test("custodial account receives ark payment via vault forward", async ({
- page,
-}) => {
+test("custodial account receives ark payment via vault forward", async ({ page }) => {
test.setTimeout(120_000);
test.skip(!testSecret, "E2E_TEST_SECRET is required");
@@ -20,9 +18,7 @@ test("custodial account receives ark payment via vault forward", async ({
// 2. Create ark invoice via the vault account (needs wallet unlock)
const { invoiceId, address } = await createArkInvoiceViaUI(page, bobPassword);
- console.log(
- `[e2e] Created ark invoice ${invoiceId} on vault account, address: ${address}`,
- );
+ console.log(`[e2e] Created ark invoice ${invoiceId} on vault account, address: ${address}`);
// 3. Stay on invoice page (subscribe to websocket notifications)
await page.goto(`/invoice/${invoiceId}`);
@@ -39,9 +35,7 @@ test("custodial account receives ark payment via vault forward", async ({
let status: any;
for (let i = 0; i < 60; i++) {
- const statusResponse = await page.request.get(
- `${apiBaseUrl}/invoice/${invoiceId}`,
- );
+ const statusResponse = await page.request.get(`${apiBaseUrl}/invoice/${invoiceId}`);
if (statusResponse.ok()) {
status = await statusResponse.json();
if ((status?.received || 0) >= 1000) {
@@ -52,10 +46,7 @@ test("custodial account receives ark payment via vault forward", async ({
await page.waitForTimeout(2000);
}
- expect(
- paid,
- `Custodial ark forward did not complete: ${JSON.stringify(status)}`,
- ).toBeTruthy();
+ expect(paid, `Custodial ark forward did not complete: ${JSON.stringify(status)}`).toBeTruthy();
console.log(
`[e2e] Custodial ark receive complete: invoice ${invoiceId} received=${status?.received}`,
diff --git a/test/e2e/ark-send-custodial.spec.ts b/test/e2e/ark-send-custodial.spec.ts
index 881a7f08..5806ca72 100644
--- a/test/e2e/ark-send-custodial.spec.ts
+++ b/test/e2e/ark-send-custodial.spec.ts
@@ -24,10 +24,7 @@ test("custodial sends to external ark address", async ({ browser }) => {
alicePassword,
);
- const { address: arkAddress } = await createArkInvoiceViaUI(
- alicePage,
- alicePassword,
- );
+ const { address: arkAddress } = await createArkInvoiceViaUI(alicePage, alicePassword);
console.log(`[e2e] Alice ark address: ${arkAddress}`);
await aliceContext.close();
diff --git a/test/e2e/custodial-bitcoin-send.spec.ts b/test/e2e/custodial-bitcoin-send.spec.ts
index 8b4c8be9..c8543dac 100644
--- a/test/e2e/custodial-bitcoin-send.spec.ts
+++ b/test/e2e/custodial-bitcoin-send.spec.ts
@@ -21,8 +21,10 @@ test("custodial-to-custodial via bitcoin address sends internally and notifies r
alicePassword,
);
- const { invoiceId, address: bitcoinAddress } =
- await createBitcoinInvoiceViaUI(alicePage, aliceUsername);
+ const { invoiceId, address: bitcoinAddress } = await createBitcoinInvoiceViaUI(
+ alicePage,
+ aliceUsername,
+ );
console.log(`[e2e] Alice custodial bitcoin address: ${bitcoinAddress}`);
console.log(`[e2e] Alice invoice ID: ${invoiceId}`);
@@ -43,14 +45,8 @@ test("custodial-to-custodial via bitcoin address sends internally and notifies r
console.log(`[e2e] Bob navigated to: ${bobUrl}`);
// Verify it went to /send/invoice/ (internal send via original invoice)
- expect(
- bobUrl,
- `Expected /send/invoice/ URL but got: ${bobUrl}`,
- ).toContain("/send/invoice/");
- expect(
- bobUrl,
- `Should NOT redirect to /pay/ but got: ${bobUrl}`,
- ).not.toContain("/pay/");
+ expect(bobUrl, `Expected /send/invoice/ URL but got: ${bobUrl}`).toContain("/send/invoice/");
+ expect(bobUrl, `Should NOT redirect to /pay/ but got: ${bobUrl}`).not.toContain("/pay/");
// Bob should see the Numpad (no amount set on the invoice)
const amountInput = bobPage.locator('[aria-label="Amount input"]');
@@ -89,7 +85,9 @@ test("custodial-to-custodial via bitcoin address sends internally and notifies r
// Verify Alice sees the payment success page
const successText = await alicePage.locator("h1").first().innerText({ timeout: 5_000 });
expect(
- successText.toLowerCase().includes("payment") || successText.toLowerCase().includes("success") || successText.toLowerCase().includes("detected"),
+ successText.toLowerCase().includes("payment") ||
+ successText.toLowerCase().includes("success") ||
+ successText.toLowerCase().includes("detected"),
`Expected payment success text, got: ${successText}`,
).toBeTruthy();
diff --git a/test/e2e/custodial-receive-bitcoin.spec.ts b/test/e2e/custodial-receive-bitcoin.spec.ts
index 11ccd3be..934f76a7 100644
--- a/test/e2e/custodial-receive-bitcoin.spec.ts
+++ b/test/e2e/custodial-receive-bitcoin.spec.ts
@@ -10,18 +10,13 @@ import {
waitForPaidRedirect,
} from "./helpers";
-test("custodial receives external bitcoin payment from bitcoind", async ({
- page,
-}) => {
+test("custodial receives external bitcoin payment from bitcoind", async ({ page }) => {
test.setTimeout(120_000);
// --- Alice: log in and create bitcoin invoice ---
await login(page, aliceUsername, alicePassword);
- const { invoiceId, address } = await createBitcoinInvoiceViaUI(
- page,
- aliceUsername,
- );
+ const { invoiceId, address } = await createBitcoinInvoiceViaUI(page, aliceUsername);
console.log(`[e2e] Alice bitcoin address: ${address}`);
console.log(`[e2e] Alice invoice ID: ${invoiceId}`);
@@ -40,18 +35,13 @@ test("custodial receives external bitcoin payment from bitcoind", async ({
interval: 2000,
maxAttempts: 30,
});
- console.log(
- `[e2e] Invoice paid: received=${status?.received}, pending=${status?.pending}`,
- );
+ console.log(`[e2e] Invoice paid: received=${status?.received}, pending=${status?.pending}`);
// --- Alice: should be redirected to /paid ---
await waitForPaidRedirect(page, invoiceId, 30_000);
console.log(`[e2e] Alice redirected to paid: ${page.url()}`);
- const successText = await page
- .locator("h1")
- .first()
- .innerText({ timeout: 5_000 });
+ const successText = await page.locator("h1").first().innerText({ timeout: 5_000 });
expect(
successText.toLowerCase().includes("payment") ||
successText.toLowerCase().includes("success") ||
diff --git a/test/e2e/custodial-receive-lightning.spec.ts b/test/e2e/custodial-receive-lightning.spec.ts
index 545ea24e..5c35b557 100644
--- a/test/e2e/custodial-receive-lightning.spec.ts
+++ b/test/e2e/custodial-receive-lightning.spec.ts
@@ -9,9 +9,7 @@ import {
waitForPaidRedirect,
} from "./helpers";
-test("custodial receives external lightning payment from clb", async ({
- page,
-}) => {
+test("custodial receives external lightning payment from clb", async ({ page }) => {
test.setTimeout(90_000);
// --- Alice: log in and create a lightning invoice with amount via API ---
@@ -35,15 +33,11 @@ test("custodial receives external lightning payment from clb", async ({
expect(invoiceId).toBeTruthy();
// Get the bolt11 payreq
- const invDetailRes = await page.request.get(
- `${apiBaseUrl}/invoice/${invoiceId}`,
- );
+ const invDetailRes = await page.request.get(`${apiBaseUrl}/invoice/${invoiceId}`);
const invDetail = await invDetailRes.json();
const bolt11 = invDetail.text;
expect(bolt11).toBeTruthy();
- console.log(
- `[e2e] Alice lightning invoice: ${invoiceId}, bolt11: ${bolt11.substring(0, 40)}...`,
- );
+ console.log(`[e2e] Alice lightning invoice: ${invoiceId}, bolt11: ${bolt11.substring(0, 40)}...`);
// Navigate to the invoice page (Alice subscribes to websocket notifications)
await page.goto(`/invoice/${invoiceId}`);
@@ -62,25 +56,17 @@ test("custodial receives external lightning payment from clb", async ({
interval: 1000,
maxAttempts: 15,
});
- console.log(
- `[e2e] Invoice paid: received=${status?.received}, pending=${status?.pending}`,
- );
+ console.log(`[e2e] Invoice paid: received=${status?.received}, pending=${status?.pending}`);
// --- Alice: wait for redirect to /paid (with fallback) ---
const paidReached = await page
- .waitForURL(
- new RegExp(`/invoice/${invoiceId}/paid(?:[/?#]|$)`),
- { timeout: 10_000 },
- )
+ .waitForURL(new RegExp(`/invoice/${invoiceId}/paid(?:[/?#]|$)`), { timeout: 10_000 })
.then(() => true)
.catch(() => false);
if (paidReached) {
console.log(`[e2e] Alice redirected to paid: ${page.url()}`);
- const successText = await page
- .locator("h1")
- .first()
- .innerText({ timeout: 5_000 });
+ const successText = await page.locator("h1").first().innerText({ timeout: 5_000 });
expect(
successText.toLowerCase().includes("payment") ||
successText.toLowerCase().includes("success") ||
diff --git a/test/e2e/custodial-send-external-btc.spec.ts b/test/e2e/custodial-send-external-btc.spec.ts
index f03aa81e..a19e3dea 100644
--- a/test/e2e/custodial-send-external-btc.spec.ts
+++ b/test/e2e/custodial-send-external-btc.spec.ts
@@ -9,9 +9,7 @@ import {
waitForSentRedirect,
} from "./helpers";
-test("custodial sends to external bitcoin address (bitcoind)", async ({
- browser,
-}) => {
+test("custodial sends to external bitcoin address (bitcoind)", async ({ browser }) => {
test.setTimeout(90_000);
// --- Get external bitcoin address from 'external' wallet (NOT 'coinos' hot wallet) ---
diff --git a/test/e2e/custodial-send-external-ln.spec.ts b/test/e2e/custodial-send-external-ln.spec.ts
index 34e14c65..6142d9c8 100644
--- a/test/e2e/custodial-send-external-ln.spec.ts
+++ b/test/e2e/custodial-send-external-ln.spec.ts
@@ -9,9 +9,7 @@ import {
waitForSentRedirect,
} from "./helpers";
-test("custodial sends to external lightning node (clb) via bolt11", async ({
- browser,
-}) => {
+test("custodial sends to external lightning node (clb) via bolt11", async ({ browser }) => {
test.setTimeout(90_000);
// --- Create bolt11 on external node (clb) ---
diff --git a/test/e2e/custodial-send-lightning.spec.ts b/test/e2e/custodial-send-lightning.spec.ts
index bddff32a..885da1ae 100644
--- a/test/e2e/custodial-send-lightning.spec.ts
+++ b/test/e2e/custodial-send-lightning.spec.ts
@@ -42,15 +42,11 @@ test("custodial-to-custodial via lightning bolt11 sends internally and notifies
expect(invoiceId).toBeTruthy();
// Get the bolt11 payreq
- const invDetailRes = await alicePage.request.get(
- `${apiBaseUrl}/invoice/${invoiceId}`,
- );
+ const invDetailRes = await alicePage.request.get(`${apiBaseUrl}/invoice/${invoiceId}`);
const invDetail = await invDetailRes.json();
const bolt11 = invDetail.text;
expect(bolt11).toBeTruthy();
- console.log(
- `[e2e] Alice bolt11 invoice: ${invoiceId}, bolt11: ${bolt11.substring(0, 40)}...`,
- );
+ console.log(`[e2e] Alice bolt11 invoice: ${invoiceId}, bolt11: ${bolt11.substring(0, 40)}...`);
// Alice navigates to invoice page (subscribes to websocket notifications)
await alicePage.goto(`/invoice/${invoiceId}`);
@@ -76,10 +72,7 @@ test("custodial-to-custodial via lightning bolt11 sends internally and notifies
await noTipButton.click();
// After skipping tip, redirects to /invoice/{id} → then to /send/invoice/{id}
- await bobPage.waitForURL(
- (url) => !url.pathname.includes("/tip"),
- { timeout: 15_000 },
- );
+ await bobPage.waitForURL((url) => !url.pathname.includes("/tip"), { timeout: 15_000 });
bobUrl = bobPage.url();
console.log(`[e2e] After tip skip: ${bobUrl}`);
}
@@ -114,10 +107,7 @@ test("custodial-to-custodial via lightning bolt11 sends internally and notifies
await waitForPaidRedirect(alicePage, invoiceId, 30_000);
console.log(`[e2e] Alice redirected to: ${alicePage.url()}`);
- const successText = await alicePage
- .locator("h1")
- .first()
- .innerText({ timeout: 5_000 });
+ const successText = await alicePage.locator("h1").first().innerText({ timeout: 5_000 });
expect(
successText.toLowerCase().includes("payment") ||
successText.toLowerCase().includes("success") ||
diff --git a/test/e2e/custodial-send-username.spec.ts b/test/e2e/custodial-send-username.spec.ts
index 8dff65ab..f8b4a002 100644
--- a/test/e2e/custodial-send-username.spec.ts
+++ b/test/e2e/custodial-send-username.spec.ts
@@ -54,10 +54,7 @@ test("custodial-to-custodial via username (/pay/) sends and notifies recipient",
// The form creates an invoice. If Alice has tip prompt, we go to /invoice/{id}/tip
// Otherwise we go to /invoice/{id} which loads the send page
- await bobPage.waitForURL(
- (url) => !url.pathname.startsWith("/pay/"),
- { timeout: 15_000 },
- );
+ await bobPage.waitForURL((url) => !url.pathname.startsWith("/pay/"), { timeout: 15_000 });
const afterPayUrl = bobPage.url();
console.log(`[e2e] After /pay/ submit: ${afterPayUrl}`);
@@ -71,10 +68,7 @@ test("custodial-to-custodial via username (/pay/) sends and notifies recipient",
// After skipping tip, we get redirected to /invoice/{id}
// which should then load as /send/invoice/{id} for the payer
- await bobPage.waitForURL(
- (url) => !url.pathname.includes("/tip"),
- { timeout: 15_000 },
- );
+ await bobPage.waitForURL((url) => !url.pathname.includes("/tip"), { timeout: 15_000 });
console.log(`[e2e] After tip skip: ${bobPage.url()}`);
}
diff --git a/test/e2e/helpers.ts b/test/e2e/helpers.ts
index 1d85825a..08d19810 100644
--- a/test/e2e/helpers.ts
+++ b/test/e2e/helpers.ts
@@ -8,8 +8,7 @@ export const alicePassword = process.env.E2E_ALICE_PASSWORD || "pw";
export const bobUsername = process.env.E2E_BOB_USERNAME || "bob";
export const bobPassword = process.env.E2E_BOB_PASSWORD || "pw";
export const testSecret = process.env.E2E_TEST_SECRET;
-export const apiBaseUrl =
- process.env.E2E_API_BASE_URL || "http://localhost:3119";
+export const apiBaseUrl = process.env.E2E_API_BASE_URL || "http://localhost:3119";
export const pauseAtEnd = process.env.E2E_PAUSE_AT_END === "true";
// --- Login helpers ---
@@ -32,11 +31,7 @@ export async function login(page: Page, username: string, password: string) {
throw new Error(`Login failed for ${username} after 5 attempts`);
}
-export async function loginNewContext(
- browser: Browser,
- username: string,
- password: string,
-) {
+export async function loginNewContext(browser: Browser, username: string, password: string) {
const context = await browser.newContext();
const page = await context.newPage();
await login(page, username, password);
@@ -66,20 +61,14 @@ export async function apiGetInvoice(page: Page, invoiceId: string) {
// --- Invoice creation via UI ---
-export async function createBitcoinInvoiceViaUI(
- page: Page,
- username: string,
-) {
+export async function createBitcoinInvoiceViaUI(page: Page, username: string) {
await page.goto(`/${username}/receive?address_type=bech32`);
await page.waitForURL(/\/invoice\/[^/?#]+/, { timeout: 15_000 });
const invoiceId = page.url().match(/\/invoice\/([^/?#]+)/)?.[1];
expect(invoiceId, "Should have invoice ID in URL").toBeTruthy();
- const invoiceText = await page
- .getByTestId("invoice-text")
- .first()
- .innerText({ timeout: 10_000 });
+ const invoiceText = await page.getByTestId("invoice-text").first().innerText({ timeout: 10_000 });
const address = invoiceText.trim().replace(/\s+/g, "");
expect(
address.startsWith("bcrt1") || address.startsWith("bc1"),
@@ -89,10 +78,7 @@ export async function createBitcoinInvoiceViaUI(
return { invoiceId: invoiceId!, address };
}
-export async function createLightningInvoiceViaUI(
- page: Page,
- username: string,
-) {
+export async function createLightningInvoiceViaUI(page: Page, username: string) {
await page.goto(`/${username}/receive`);
await page.waitForURL(/\/invoice\/[^/?#]+/, { timeout: 15_000 });
@@ -118,8 +104,7 @@ export async function createLightningInvoiceViaUI(
const bolt11 = invoiceText.replace(/\s+/g, "");
expect(
- bolt11.toLowerCase().startsWith("lnbcrt") ||
- bolt11.toLowerCase().startsWith("lnbc"),
+ bolt11.toLowerCase().startsWith("lnbcrt") || bolt11.toLowerCase().startsWith("lnbc"),
`Expected bolt11, got: ${bolt11.substring(0, 30)}`,
).toBeTruthy();
@@ -127,9 +112,7 @@ export async function createLightningInvoiceViaUI(
}
export async function createArkInvoiceViaUI(page: Page, password: string) {
- const arkCard = page.locator(
- '[data-testid="account-card"][data-account-type="ark"]',
- );
+ const arkCard = page.locator('[data-testid="account-card"][data-account-type="ark"]');
await expect(arkCard.first()).toBeVisible({ timeout: 10_000 });
const walletPassInput = page.getByTestId("walletpass-input");
@@ -140,10 +123,7 @@ export async function createArkInvoiceViaUI(page: Page, password: string) {
const card = arkCard.nth(i);
await card.scrollIntoViewIfNeeded();
- const accountHref = await card
- .locator('a[href^="/account/"]')
- .first()
- .getAttribute("href");
+ const accountHref = await card.locator('a[href^="/account/"]').first().getAttribute("href");
const accountId = accountHref?.split("/account/")[1];
await card.locator('a[href="/invoice"]').first().click();
@@ -188,9 +168,7 @@ export async function createArkInvoiceViaUI(page: Page, password: string) {
expect(invoiceOpened).toBeTruthy();
- const invoiceText = (
- await page.getByTestId("invoice-text").first().innerText()
- ).trim();
+ const invoiceText = (await page.getByTestId("invoice-text").first().innerText()).trim();
const addressMatch = invoiceText.match(/t?ark1[a-z0-9]+/i);
expect(addressMatch).toBeTruthy();
@@ -201,18 +179,13 @@ export async function createArkInvoiceViaUI(page: Page, password: string) {
}
export async function createVaultBitcoinInvoiceViaUI(page: Page) {
- const vaultCard = page.locator(
- '[data-testid="account-card"][data-account-type="bitcoin"]',
- );
+ const vaultCard = page.locator('[data-testid="account-card"][data-account-type="bitcoin"]');
await expect(vaultCard.first()).toBeVisible({ timeout: 10_000 });
await vaultCard.first().getByTestId("account-receive").click();
await page.waitForURL(/\/invoice\/[^/?#]+/, { timeout: 10_000 });
- const invoiceText = await page
- .getByTestId("invoice-text")
- .first()
- .innerText({ timeout: 10_000 });
+ const invoiceText = await page.getByTestId("invoice-text").first().innerText({ timeout: 10_000 });
const address = invoiceText.trim().replace(/\s+/g, "");
expect(
address.startsWith("bcrt1") || address.startsWith("bc1"),
@@ -237,10 +210,9 @@ export async function pasteAndSend(page: Page, text: string) {
await page.locator('button[type="submit"]').first().click();
// Wait for navigation away from /send
- await page.waitForURL(
- (url) => url.pathname !== "/send" && url.pathname !== "/send/",
- { timeout: 15_000 },
- );
+ await page.waitForURL((url) => url.pathname !== "/send" && url.pathname !== "/send/", {
+ timeout: 15_000,
+ });
}
export async function fillNumpadAmount(page: Page, amount: number) {
@@ -265,15 +237,8 @@ export async function waitForSentRedirect(page: Page, timeout = 15_000) {
await page.waitForURL(/\/sent\//, { timeout });
}
-export async function waitForPaidRedirect(
- page: Page,
- invoiceId: string,
- timeout = 30_000,
-) {
- await page.waitForURL(
- new RegExp(`/invoice/${invoiceId}/paid(?:[/?#]|$)`),
- { timeout },
- );
+export async function waitForPaidRedirect(page: Page, invoiceId: string, timeout = 30_000) {
+ await page.waitForURL(new RegExp(`/invoice/${invoiceId}/paid(?:[/?#]|$)`), { timeout });
}
// --- Docker helpers ---
@@ -290,18 +255,12 @@ export function lightningPay(bolt11: string): string {
}
export function lightningInvoice(amountMsat: number, label: string): string {
- const result = dockerExec(
- "clb",
- `lightning-cli invoice ${amountMsat} "${label}" "${label}"`,
- );
+ const result = dockerExec("clb", `lightning-cli invoice ${amountMsat} "${label}" "${label}"`);
return JSON.parse(result).bolt11 as string;
}
export function getBitcoinAddress(): string {
- return dockerExec(
- "bc",
- "bitcoin-cli -regtest -rpcwallet=external getnewaddress",
- );
+ return dockerExec("bc", "bitcoin-cli -regtest -rpcwallet=external getnewaddress");
}
export function bitcoinSend(address: string, amountBtc: string): string {
@@ -312,14 +271,8 @@ export function bitcoinSend(address: string, amountBtc: string): string {
}
export function mineBlocks(n: number): void {
- const minerAddr = dockerExec(
- "bc",
- "bitcoin-cli -regtest -rpcwallet=coinos getnewaddress",
- );
- dockerExec(
- "bc",
- `bitcoin-cli -regtest -rpcwallet=coinos generatetoaddress ${n} "${minerAddr}"`,
- );
+ const minerAddr = dockerExec("bc", "bitcoin-cli -regtest -rpcwallet=coinos getnewaddress");
+ dockerExec("bc", `bitcoin-cli -regtest -rpcwallet=coinos generatetoaddress ${n} "${minerAddr}"`);
}
// --- Polling helpers ---
@@ -335,10 +288,7 @@ export async function waitForInvoicePaid(
const res = await page.request.get(`${apiBaseUrl}/invoice/${invoiceId}`);
if (res.ok()) {
status = await res.json();
- if (
- (status?.received || 0) >= amount ||
- (status?.pending || 0) >= amount
- ) {
+ if ((status?.received || 0) >= amount || (status?.pending || 0) >= amount) {
return status;
}
}
diff --git a/test/e2e/vault-receive-bitcoin.spec.ts b/test/e2e/vault-receive-bitcoin.spec.ts
index e1be643a..013bb562 100644
--- a/test/e2e/vault-receive-bitcoin.spec.ts
+++ b/test/e2e/vault-receive-bitcoin.spec.ts
@@ -10,9 +10,7 @@ import {
waitForPaidRedirect,
} from "./helpers";
-test("bitcoin vault receives external bitcoin payment from bitcoind", async ({
- page,
-}) => {
+test("bitcoin vault receives external bitcoin payment from bitcoind", async ({ page }) => {
test.setTimeout(120_000);
// --- Alice: log in and create vault bitcoin invoice ---
@@ -37,9 +35,7 @@ test("bitcoin vault receives external bitcoin payment from bitcoind", async ({
interval: 2000,
maxAttempts: 30,
});
- console.log(
- `[e2e] Vault invoice paid: received=${status?.received}, pending=${status?.pending}`,
- );
+ console.log(`[e2e] Vault invoice paid: received=${status?.received}, pending=${status?.pending}`);
// --- Alice: should be redirected to /paid ---
const paidReached = await page
@@ -52,9 +48,7 @@ test("bitcoin vault receives external bitcoin payment from bitcoind", async ({
if (paidReached) {
console.log(`[e2e] Alice redirected to paid: ${page.url()}`);
} else {
- console.log(
- `[e2e] No /paid redirect (vault may not auto-redirect), current: ${page.url()}`,
- );
+ console.log(`[e2e] No /paid redirect (vault may not auto-redirect), current: ${page.url()}`);
}
// Vault payments may show as pending — verify the invoice API shows received/pending
diff --git a/test/e2e/vault-send-bitcoin.spec.ts b/test/e2e/vault-send-bitcoin.spec.ts
index bd3d7172..bfb69f3b 100644
--- a/test/e2e/vault-send-bitcoin.spec.ts
+++ b/test/e2e/vault-send-bitcoin.spec.ts
@@ -8,9 +8,7 @@ import {
pasteAndSend,
} from "./helpers";
-test("pasting vault bitcoin address redirects to /send/bitcoin, not /pay", async ({
- browser,
-}) => {
+test("pasting vault bitcoin address redirects to /send/bitcoin, not /pay", async ({ browser }) => {
test.setTimeout(60_000);
// --- Alice: generate a vault invoice and grab the address ---
@@ -21,24 +19,17 @@ test("pasting vault bitcoin address redirects to /send/bitcoin, not /pay", async
);
// Find the bitcoin vault account card and click Receive
- const vaultCard = alicePage.locator(
- '[data-testid="account-card"][data-account-type="bitcoin"]',
- );
+ const vaultCard = alicePage.locator('[data-testid="account-card"][data-account-type="bitcoin"]');
await expect(vaultCard.first()).toBeVisible({ timeout: 10_000 });
- const receiveBtn = vaultCard
- .first()
- .getByTestId("account-receive");
+ const receiveBtn = vaultCard.first().getByTestId("account-receive");
await receiveBtn.click();
// Wait for the invoice page
await alicePage.waitForURL(/\/invoice\/[^/?#]+/, { timeout: 10_000 });
// Grab the bitcoin address from the invoice text
- const invoiceText = await alicePage
- .getByTestId("invoice-text")
- .first()
- .innerText();
+ const invoiceText = await alicePage.getByTestId("invoice-text").first().innerText();
const vaultAddress = invoiceText.trim().replace(/\s+/g, "");
expect(
vaultAddress.startsWith("bcrt1") || vaultAddress.startsWith("bc1"),
@@ -62,14 +53,8 @@ test("pasting vault bitcoin address redirects to /send/bitcoin, not /pay", async
console.log(`[e2e] After paste, Bob navigated to: ${finalUrl}`);
// Should go to /send/bitcoin, NOT /pay/alice
- expect(
- finalUrl,
- `Expected /send/bitcoin/ URL but got: ${finalUrl}`,
- ).toContain("/send/bitcoin/");
- expect(
- finalUrl,
- `Should NOT redirect to /pay/ but got: ${finalUrl}`,
- ).not.toContain("/pay/");
+ expect(finalUrl, `Expected /send/bitcoin/ URL but got: ${finalUrl}`).toContain("/send/bitcoin/");
+ expect(finalUrl, `Should NOT redirect to /pay/ but got: ${finalUrl}`).not.toContain("/pay/");
await bobContext.close();
});
diff --git a/test/e2e/vault-send-external-btc.spec.ts b/test/e2e/vault-send-external-btc.spec.ts
index 01a64716..c8431b59 100644
--- a/test/e2e/vault-send-external-btc.spec.ts
+++ b/test/e2e/vault-send-external-btc.spec.ts
@@ -10,9 +10,7 @@ import {
setActiveAccount,
} from "./helpers";
-test("bitcoin vault sends to external bitcoin address with PSBT signing", async ({
- page,
-}) => {
+test("bitcoin vault sends to external bitcoin address with PSBT signing", async ({ page }) => {
test.setTimeout(120_000);
// Capture console errors for debugging
@@ -30,9 +28,7 @@ test("bitcoin vault sends to external bitcoin address with PSBT signing", async
await login(page, aliceUsername, alicePassword);
// --- Find the bitcoin vault account and switch to it ---
- const vaultCard = page.locator(
- '[data-testid="account-card"][data-account-type="bitcoin"]',
- );
+ const vaultCard = page.locator('[data-testid="account-card"][data-account-type="bitcoin"]');
const vaultCount = await vaultCard.count();
if (vaultCount === 0) {
test.skip(true, "No bitcoin vault account found");
@@ -81,9 +77,7 @@ test("bitcoin vault sends to external bitcoin address with PSBT signing", async
}
// Click Send — this triggers the WalletPass dialog for vault accounts
- const sendButton = page.locator(
- 'button[type="submit"]:not([data-testid="walletpass-submit"])',
- );
+ const sendButton = page.locator('button[type="submit"]:not([data-testid="walletpass-submit"])');
await expect(sendButton).toBeVisible({ timeout: 5_000 });
await sendButton.click();
@@ -130,16 +124,12 @@ test("bitcoin vault sends to external bitcoin address with PSBT signing", async
// Client-side PSBT signing may fail if esplora/PSBT format is not
// properly configured in the test environment
const signingError = consoleErrors.some(
- (e) =>
- e.includes("invalid tag") ||
- e.includes("decrypt") ||
- e.includes("PSBT"),
+ (e) => e.includes("invalid tag") || e.includes("decrypt") || e.includes("PSBT"),
);
if (signingError) {
test.skip(
true,
- "PSBT signing failed (likely test env issue): " +
- consoleErrors.join("; "),
+ "PSBT signing failed (likely test env issue): " + consoleErrors.join("; "),
);
return;
}
@@ -148,9 +138,7 @@ test("bitcoin vault sends to external bitcoin address with PSBT signing", async
}
}
} else {
- console.log(
- "[e2e] No WalletPass (cached password), waiting for redirect...",
- );
+ console.log("[e2e] No WalletPass (cached password), waiting for redirect...");
await waitForSentRedirect(page, 30_000);
}
diff --git a/test/e2e/vault-send-to-custodial-btc.spec.ts b/test/e2e/vault-send-to-custodial-btc.spec.ts
index 7ee522d2..aa52213c 100644
--- a/test/e2e/vault-send-to-custodial-btc.spec.ts
+++ b/test/e2e/vault-send-to-custodial-btc.spec.ts
@@ -39,9 +39,7 @@ test("vault sending to custodial bitcoin address stays on /send/bitcoin, not /se
const { id: invoiceId } = await invoiceRes.json();
expect(invoiceId).toBeTruthy();
- const invDetail = await (
- await bobPage.request.get(`${apiBaseUrl}/invoice/${invoiceId}`)
- ).json();
+ const invDetail = await (await bobPage.request.get(`${apiBaseUrl}/invoice/${invoiceId}`)).json();
const custodialAddress = invDetail.text;
expect(custodialAddress).toBeTruthy();
console.log(`[e2e] Bob custodial bitcoin address: ${custodialAddress}`);
@@ -55,9 +53,7 @@ test("vault sending to custodial bitcoin address stays on /send/bitcoin, not /se
);
// Find the bitcoin vault account and switch to it
- const vaultCard = alicePage.locator(
- '[data-testid="account-card"][data-account-type="bitcoin"]',
- );
+ const vaultCard = alicePage.locator('[data-testid="account-card"][data-account-type="bitcoin"]');
const vaultCount = await vaultCard.count();
if (vaultCount === 0) {
test.skip(true, "No bitcoin vault account found");
@@ -83,14 +79,10 @@ test("vault sending to custodial bitcoin address stays on /send/bitcoin, not /se
console.log(`[e2e] After paste, Alice navigated to: ${finalUrl}`);
// Should stay on /send/bitcoin (on-chain), NOT redirect to /send/invoice (internal)
- expect(
- finalUrl,
- `Expected /send/bitcoin/ URL but got: ${finalUrl}`,
- ).toContain("/send/bitcoin/");
- expect(
- finalUrl,
- `Should NOT redirect to /send/invoice/ but got: ${finalUrl}`,
- ).not.toContain("/send/invoice/");
+ expect(finalUrl, `Expected /send/bitcoin/ URL but got: ${finalUrl}`).toContain("/send/bitcoin/");
+ expect(finalUrl, `Should NOT redirect to /send/invoice/ but got: ${finalUrl}`).not.toContain(
+ "/send/invoice/",
+ );
await aliceContext.close();
});