diff options
| author | Adam Soltys <[email protected]> | 2026-02-15 10:32:40 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-02-15 10:32:40 -0800 |
| commit | 4d3377ba1d37147b194c1fb0504edee3c73211a5 (patch) | |
| tree | 43943fe52e3ba574f07f37453fabd273ec8b7757 /lib | |
| parent | 4a0fcb5f851190eccc7a33907a180ee584db583c (diff) | |
| download | coinos-server-4d3377ba1d37147b194c1fb0504edee3c73211a5.tar.gz coinos-server-4d3377ba1d37147b194c1fb0504edee3c73211a5.zip | |
ark refresh
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/ark.ts | 107 | ||||
| -rw-r--r-- | lib/invoices.ts | 1 |
2 files changed, 107 insertions, 1 deletions
@@ -2,7 +2,14 @@ import { EventSource } from "eventsource"; if (!globalThis.EventSource) globalThis.EventSource = EventSource; import config from "$config"; -import { SingleKey, Wallet } from "@arkade-os/sdk"; +import { + SingleKey, + Wallet, + Ramps, + RestArkProvider, + VtxoManager, +} from "@arkade-os/sdk"; +import { l, warn } from "$lib/logging"; let wallet: any; @@ -16,6 +23,104 @@ const getWallet = async () => { return wallet; }; +let refreshing = false; + +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), + ), + ]); + +export const refreshArkWallet = async () => { + if (refreshing) return; + try { + refreshing = true; + const w = await getWallet(); + const balance = await w.getBalance(); + l( + "ark wallet check — available:", + balance.available, + "recoverable:", + balance.recoverable, + "boarding:", + balance.boarding.confirmed, + ); + + const manager = new VtxoManager(w); + + // Recover swept/expired VTXOs + if (balance.recoverable > 0) { + try { + const txid = await withTimeout( + manager.recoverVtxos(), + 60_000, + "ark recovery", + ); + l("ark recovered swept vtxos, txid:", txid); + } catch (e: any) { + warn("ark vtxo recovery failed:", e.message); + } + } + + // Renew VTXOs approaching expiry (SDK default: 3 days) + try { + const expiring = await manager.getExpiringVtxos(); + if (expiring.length > 0) { + l("ark renewing", expiring.length, "expiring vtxos"); + const txid = await withTimeout( + manager.renewVtxos(), + 60_000, + "ark renewal", + ); + l("ark renewed vtxos, txid:", txid); + } + } catch (e: any) { + warn("ark vtxo renewal failed:", e.message); + } + + // Onboard confirmed boarding UTXOs + const boardingUtxos = await w.getBoardingUtxos(); + const confirmed = boardingUtxos.filter((u: any) => u.status?.confirmed); + + if (confirmed.length > 0) { + const provider = new RestArkProvider(config.ark.arkServerUrl); + const info = await provider.getInfo(); + const ramps = new Ramps(w); + + // Try each boarding UTXO, freshest first + const sorted = [...confirmed].sort( + (a: any, b: any) => + (b.status?.block_height || 0) - (a.status?.block_height || 0), + ); + + for (const utxo of sorted) { + try { + const txid = await withTimeout( + ramps.onboard(info.fees, [utxo], undefined, () => {}), + 60_000, + "ark onboard", + ); + l("ark onboarded boarding utxo:", utxo.value, "sats, txid:", txid); + break; + } catch (e: any) { + warn("ark onboard failed:", utxo.value, "sats:", e.message); + } + } + } + } catch (e: any) { + warn("ark wallet refresh failed:", e.message); + } finally { + refreshing = false; + } +}; + +// Check every 2 minutes +setInterval(refreshArkWallet, 2 * 60 * 1000); +// Initial check after 30s startup delay +setTimeout(refreshArkWallet, 30_000); + export const sendArk = async (address: string, amount: number) => { const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error("Ark send timed out")), 45000), diff --git a/lib/invoices.ts b/lib/invoices.ts index f7bb6897..c5194ce3 100644 --- a/lib/invoices.ts +++ b/lib/invoices.ts @@ -88,6 +88,7 @@ export const generate = async ({ invoice, user }) => { type = PaymentType.ark; text = account.arkAddress; hash = id; + await db.del(`custodial-ark-invoice:${account.arkAddress}`); } else if (account.seed || (account.pubkey && account.fingerprint)) { type = PaymentType.bitcoin; const nextIndex = account.nextIndex || 0; |
