aboutsummaryrefslogtreecommitdiffstats
path: root/src/ark
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-01 17:10:36 -0700
committerAdam Soltys <[email protected]>2026-10-01 17:10:36 -0700
commit5c661b68c61d53a4684e47c2f306ce43cc76c1c0 (patch)
tree74972845e7adfa7ab79309e83f740084f948c3ce /src/ark
parent08929aa42cdc74c3fe29007ceee3c2f8e06e6667 (diff)
downloadcoinosv3-5c661b68c61d53a4684e47c2f306ce43cc76c1c0.tar.gz
coinosv3-5c661b68c61d53a4684e47c2f306ce43cc76c1c0.zip
Sending on-chain from Spending states the fee and minimum up front.
The form asks the ASP for a co-operative offboard quote (mining share + service fee, minimum = fee + dust) for the address on screen, disables Send for a balance below it and points to Lightning. A too-small amount reads in words instead of the manager's internal error, and the unilateral-exit note only appears when co-operation actually failed. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01VP5Ljqk7EYcm5QQAbzDwog
Diffstat (limited to 'src/ark')
-rw-r--r--src/ark/manager.js22
1 files changed, 21 insertions, 1 deletions
diff --git a/src/ark/manager.js b/src/ark/manager.js
index b0985de..4b65b27 100644
--- a/src/ark/manager.js
+++ b/src/ark/manager.js
@@ -696,6 +696,23 @@ export class ArkManager {
return action;
}
+ // What a cooperative offboard to `spk` would cost right now, before any
+ // coin is touched: the miners' share at the ASP's current rate plus its
+ // service fee, and the smallest balance that still lands above dust.
+ async offboardQuote(spk, vtxoIds) {
+ const inputs = vtxoIds
+ ? vtxoIds.map((id) => this._vtxo(id)).filter(Boolean)
+ : this.state.vtxos.filter((v) => v.state === 'spendable');
+ const tip = await this.chain.tipHeight();
+ const satVkb = await getOffboardFeeRate(this.arkUrl);
+ const parts = offboardFeeParts({
+ spkLen: spk.length, satVkb, fees: this.info.offboardFees, tip,
+ inputs: (inputs.length ? inputs : [{ amountSat: 0, expiryHeight: tip }])
+ .map((v) => ({ amountSat: v.amountSat, expiryHeight: v.expiryHeight })),
+ });
+ return { ...parts, minSat: parts.totalSat + P2TR_DUST };
+ }
+
// ---- offboard (collaborative exit: spendable vtxos -> one on-chain output) ----
// Whole vtxos only — there is no change on this path. Default is everything
// spendable; vtxoIds selects a subset (the feature splits an exact-amount
@@ -721,7 +738,10 @@ export class ArkManager {
});
const feeSat = feeParts.totalSat;
const netSat = grossSat - feeSat;
- if (netSat < P2TR_DUST) throw new Error('ark balance too small to offboard after fees');
+ if (netSat < P2TR_DUST) {
+ throw Object.assign(new Error('ark balance too small to offboard after fees'),
+ { tooSmall: true, feeSat, minSat: feeSat + P2TR_DUST });
+ }
const action = {
id: `offboard-${Date.now()}`, type: 'offboard', step: 'created',
inputIds: inputs.map((v) => v.id), address, spkHex: hex.encode(spk),