aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-03-01 12:42:16 -0800
committerAdam Soltys <[email protected]>2026-03-01 12:42:16 -0800
commit0167d3d9348d9e2cb1f85cd499ef346f6e85dad1 (patch)
tree8c3048fdf0f4209618ff7a4cb2be612856d3780b /src/lib
parent6ac0d3da9668149cb2c3b6322fa431a2f7f508a9 (diff)
downloadcoinos-ui-0167d3d9348d9e2cb1f85cd499ef346f6e85dad1.tar.gz
coinos-ui-0167d3d9348d9e2cb1f85cd499ef346f6e85dad1.zip
Fix ark e2e tests: consolidate into serial suite, fix VTXO contention
- Skip settle/refresh during tests via ark:skipSettle flag to prevent VTXOs from being registered in never-completing ark rounds - Add 3s timeout on syncPromise wait in sendArk to prevent hangs - Fix ensureArkAccount to wait for autoUnlockArk and create account via API with correct address (Schnorr signatures are non-deterministic) - Consolidate all 6 ark tests into single serial file to prevent server wallet contention - Split playwright config into parallel (non-ark) and serial (ark) projects Co-Authored-By: Claude Opus 4.6 <[email protected]>
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/ark.ts42
1 files changed, 25 insertions, 17 deletions
diff --git a/src/lib/ark.ts b/src/lib/ark.ts
index b59baa04..f6e34cdc 100644
--- a/src/lib/ark.ts
+++ b/src/lib/ark.ts
@@ -354,7 +354,7 @@ export const sendArk = async (address: string, amount: number) => {
// Suppress sync polling and wait for any in-flight sync to finish
arkSending = true;
- if (syncPromise) await syncPromise.catch(() => {});
+ if (syncPromise) await Promise.race([syncPromise.catch(() => {}), new Promise((r) => setTimeout(r, 3_000))]);
try {
// Refresh expired VTXOs before attempting to send
@@ -371,28 +371,36 @@ export const sendArk = async (address: string, amount: number) => {
let txid;
let sendWallet = wallet;
- try {
- txid = await withTimeout(wallet.sendBitcoin({ address, amount }), 45_000, "Ark send");
- } catch (e: any) {
- if (
- e?.message?.includes("VTXO_ALREADY_REGISTERED") ||
- e?.message?.includes("VTXO_ALREADY_SPENT")
- ) {
- walletInstance = undefined;
- walletKey = undefined;
- sendWallet = await getWallet();
- if (!sendWallet) throw new Error("Ark wallet not available after refresh");
- const vtxos = await sendWallet.getVtxos({ spendableOnly: false });
- if (vtxos?.length) addProcessedVtxos(vtxos.map(vtxoKey));
+ // Retry loop for VTXO_ALREADY_REGISTERED — settle() may have registered VTXOs in a
+ // pending round. Wait for the round to abort (~5s) so VTXOs become unregistered.
+ for (let attempt = 0; attempt < 3; attempt++) {
+ try {
txid = await withTimeout(
sendWallet.sendBitcoin({ address, amount }),
45_000,
- "Ark send retry",
+ "Ark send",
);
- } else {
- throw e;
+ break;
+ } catch (e: any) {
+ if (
+ e?.message?.includes("VTXO_ALREADY_REGISTERED") ||
+ e?.message?.includes("VTXO_ALREADY_SPENT")
+ ) {
+ console.log(`ark send attempt ${attempt + 1} failed:`, e.message, "— retrying");
+ walletInstance = undefined;
+ walletKey = undefined;
+ sendWallet = await getWallet();
+ if (!sendWallet) throw new Error("Ark wallet not available after refresh");
+ const vtxos = await sendWallet.getVtxos({ spendableOnly: false });
+ if (vtxos?.length) addProcessedVtxos(vtxos.map(vtxoKey));
+ // Wait for the current round to abort before retrying
+ if (attempt < 2) await new Promise((r) => setTimeout(r, 6_000));
+ } else {
+ throw e;
+ }
}
}
+ if (!txid) throw new Error("Ark send failed after retries");
// Verify the send and mark VTXOs as processed, but don't let
// stalled SDK calls block the flow — the send already succeeded