aboutsummaryrefslogtreecommitdiffstats
path: root/src/ark
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-22 22:06:01 -0700
committerAdam Soltys <[email protected]>2026-10-22 22:06:01 -0700
commitf4997d29a0c97eac61338448854c7ab9724cdc92 (patch)
tree14f2d56a06bd4bfb093c6bb5f6a21a0321de1dfa /src/ark
parent648a5503089d33232855925a24ff60f78cab6df4 (diff)
downloadcoinosv3-f4997d29a0c97eac61338448854c7ab9724cdc92.tar.gz
coinosv3-f4997d29a0c97eac61338448854c7ab9724cdc92.zip
The third custody tier keeps exits cheap by depth, not by date: "Keep my exit cheap" renews a coin once its history is four hops deep
Why: a unilateral exit publishes one on-chain transaction per off-chain hop, so depth — not age — is what makes an exit expensive, and a renewal a week before expiry flattens exactly the same history a renewal in the last hours does. The week-ahead "early" tier only guarded against the server being unreachable at the very end, which is not the case worth selling. (Adam, 2026-10-23.) exit = self (last-hours renewal, free bracket + base, the same grid-snapped backstop) plus: any coin whose genesis chain is 4+ hops deep is renewed in the next round the app runs, paying the top bracket + base — but only when that fee is under a tenth of the coin (flattening a 21-sat coin for 300 sats helps nobody; its exit is uneconomic anyway). Deep coins ride along with an expiry renewal when one is due, or get a round of their own. The server-side backstop stays near-expiry only: depth changes only while the wallet transacts, i.e. while the app is open. A stored 'early' reads as 'exit'. Card copy says what the hop count means and what the two prices are for. Co-Authored-By: Claude Fable 5.1 <[email protected]>
Diffstat (limited to 'src/ark')
-rw-r--r--src/ark/manager.js21
1 files changed, 14 insertions, 7 deletions
diff --git a/src/ark/manager.js b/src/ark/manager.js
index 2b7a5a7..79fdcd0 100644
--- a/src/ark/manager.js
+++ b/src/ark/manager.js
@@ -183,20 +183,27 @@ export const backstopMargin = (network) => (network === 'regtest' ? 6 : 36);
// the balance works like a v2 one, and no renewal is ever paid.
// self renew in the last hours before expiry — the free ppm bracket,
// one base fee per round — so a unilateral exit is always there.
-// early renew a week ahead (the 0.1% bracket + base) for whoever wants
-// a margin against the server being unreachable near the end.
+// exit the same, plus: renew a coin as soon as its history runs
+// `maxDepth` hops deep. A unilateral exit publishes one on-chain
+// transaction per hop, so depth — not age — is what makes an
+// exit expensive; a renewal flattens it to one hop. Early, so
+// the top ppm bracket + base; only for a coin worth it (the fee
+// under a tenth of its value — see maybeAutoRefresh).
// A policy is { window, margin, grid } in blocks: the app renews a coin with
// under `window` blocks left; the server-side backstop is registered `margin`
// blocks before expiry, snapped DOWN to a multiple of `grid` so everyone's
// orders land in the same round (one round's mining fee shared, not one per
-// coin). The snapped height stays inside the same fee bracket: self runs
-// 36–83 blocks out (free under 96), early 1008–1151 (0.1% under 2160).
-export const CUSTODY_TIERS = ['custodial', 'self', 'early'];
+// coin). The snapped height stays inside the free bracket: 36–83 blocks out.
+export const CUSTODY_TIERS = ['custodial', 'self', 'exit'];
export const MAX_SEND_INPUTS = 48;
export function renewPolicy(tier, network) {
const rt = network === 'regtest';
- if (tier === 'early') return { window: rt ? 60 : 1008, margin: rt ? 60 : 1008, grid: rt ? 1 : 144 };
- if (tier === 'self') return { window: rt ? 24 : 96, margin: backstopMargin(network), grid: rt ? 1 : 48 };
+ const self = { window: rt ? 24 : 96, margin: backstopMargin(network), grid: rt ? 1 : 48 };
+ // 'early' was the third tier for a day (2026-10-22): a week-ahead renewal,
+ // which flattens the same history a last-hours one does — mirrors may
+ // still carry the name
+ if (tier === 'exit' || tier === 'early') return { ...self, maxDepth: 4 };
+ if (tier === 'self') return self;
return null;
}