From 0a412c94ef1517e7b471e31ccadb867078826721 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Fri, 23 Oct 2026 09:18:39 -0700 Subject: On a server that does not honor expired coins the custody tier in force is "self": renewal stays automatic there, in the app and the worker "Coinos holds my coins" was meant to exist only on servers that keep cosigning a coin past its timelock (the presets carry honorsExpired), and the custody card hid it elsewhere and showed "self". But the tier the renewal pass and the worker mirror read came from the stored choice or the custodial default regardless of server, so a wallet on Second or a custom captaind (where an expired coin is gone) renewed nothing and warned of nothing since 648a550 (2026-10-22): no backstop orders, no renewal in the last hours, and the worker registering nothing while closed. getCustody() now returns "self" in place of "custodial" whenever the active server does not honor expired coins ("exit" stays "exit"); the mirror carries the same, so the worker follows. The stored choice is left alone for a later switch back to a coinos server, and choosing a tier always writes it. tools/ark/custody-foreign-test.js: a custom server with no honorsExpired, nothing stored or "custodial" stored, reads as "self", the card offers only self/exit, and the renewal pass registers backstops and renews the coin at its timelock. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4 --- src/features/ark.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/features/ark.js b/src/features/ark.js index eccc3b8..739dd5e 100644 --- a/src/features/ark.js +++ b/src/features/ark.js @@ -2677,7 +2677,7 @@ export function arkFeature(ctx) { const view = arkView(); const info = (view && view.info) || (cachedArkInfo(cfg && cfg.ark) || {}).info || null; const honors = !!(cfg && cfg.honorsExpired); - const cur = honors ? getCustody() : 'self'; + const cur = getCustody(); // never 'custodial' on a server that doesn't honor expired coins const base = (info && info.refreshFees && info.refreshFees.baseFeeSat) || 0; const table = (info && info.refreshFees && info.refreshFees.ppmExpiryTable) || []; const ppmAt = (blocks) => (table.filter((e) => e.thresholdBlocks <= blocks).pop()?.ppm) ?? 0; @@ -3402,10 +3402,20 @@ export function arkFeature(ctx) { // happening anyway, for next to nothing; the Manage page consolidates on // request. const CUSTODY_DEFAULT = 'custodial'; + // The tier in force: the stored choice, except that "coinos holds my coins" + // exists only on a server that honors a coin past its timelock. Anywhere + // else (Second, a custom captaind) an expired coin is gone, so renewal is + // automatic there whatever is stored — the stored default stays on the + // record for a later switch back to a coinos server. From 2026-10-22 to + // 10-23 the stored default applied everywhere, and a wallet on such a + // server renewed nothing and warned of nothing, in the app and the worker. function getCustody() { let v = wallet.loadFeatureState ? wallet.loadFeatureState('arkCustody', null) : null; if (v === 'early') v = 'exit'; // the third tier's first name (one day in October 2026) - return CUSTODY_TIERS.includes(v) ? v : CUSTODY_DEFAULT; + const tier = CUSTODY_TIERS.includes(v) ? v : CUSTODY_DEFAULT; + if (tier !== 'custodial') return tier; + const cfg = getArkConfig(); + return cfg && !cfg.honorsExpired ? 'self' : tier; } function setCustody(tier) { if (wallet.saveFeatureState) wallet.saveFeatureState('arkCustody', tier); } const custodyPolicy = () => renewPolicy(getCustody(), getNetwork()); @@ -3416,8 +3426,10 @@ export function arkFeature(ctx) { // (there is no cancel; a spent input voids one) — they were priced when // registered and renew one coin each, once. function applyCustody(tier) { - if (!CUSTODY_TIERS.includes(tier) || tier === getCustody()) return; - setCustody(tier); + if (!CUSTODY_TIERS.includes(tier)) return; + const same = tier === getCustody(); + setCustody(tier); // the record keeps the choice even when it is already in force here + if (same) return; arkAutoRefreshAt = 0; arkBackstopAt = 0; arkRenewWarn = null; if (ark && ark.state) maybeAutoRefresh(ark).catch(() => {}); writeBgMirror().catch(() => {}); -- cgit v1.2.3