diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ark/manager.js | 5 | ||||
| -rw-r--r-- | src/ark/proto.js | 3 | ||||
| -rw-r--r-- | src/features/gifts.js | 7 |
3 files changed, 14 insertions, 1 deletions
diff --git a/src/ark/manager.js b/src/ark/manager.js index 01de06f..f9819a0 100644 --- a/src/ark/manager.js +++ b/src/ark/manager.js @@ -1270,6 +1270,11 @@ export class ArkManager { const tip = await this.chain.tipHeight(); const totalSat = inputs.reduce((n, v) => n + v.amountSat, 0); const feeSat = this.refreshFee(inputs, tip); + // The server refuses round outputs under P2TR dust (330), so a wallet + // this small can't renew — fail before the action marks coins pending. + if (totalSat - feeSat < 330) { + throw new Error(`balance too small to refresh: ${totalSat} sat minus ${feeSat} sat fee is under the 330 sat minimum`); + } const action = { id: `refresh-${Date.now()}`, type: 'refresh', step: 'created', inputIds: inputs.map((v) => v.id), diff --git a/src/ark/proto.js b/src/ark/proto.js index 6729465..81651ee 100644 --- a/src/ark/proto.js +++ b/src/ark/proto.js @@ -29,6 +29,9 @@ export function pbWriter() { const chunks = []; const varint = (n) => { n = BigInt(n); + // a negative sign-extends under >>= forever — the do/while would push + // bytes until the process dies of memory, so refuse it loudly instead + if (n < 0n) throw new Error(`varint: negative value ${n}`); const bs = []; do { let b = Number(n & 0x7fn); n >>= 7n; if (n) b |= 0x80; bs.push(b); } while (n); chunks.push(Uint8Array.from(bs)); diff --git a/src/features/gifts.js b/src/features/gifts.js index bc16619..0543030 100644 --- a/src/features/gifts.js +++ b/src/features/gifts.js @@ -402,11 +402,16 @@ export function giftsFeature(ctx) { // An ark gift: send the amount to a fresh bearer identity, off-chain and // instant — no coin locking, no split logic, no claim fee. + // Gifts go down to 1 sat: the ASP cosigns all-dust arkoor sends, so a + // sub-dust bearer vtxo sweeps to the claimer fine (regtest-proven — + // tools/ark/dust-gift-test.js). The trade a sub-330 coin carries — no + // unilateral exit on its own, renewable in rounds only alongside bigger + // siblings — is the same one dust zaps already make. async function doCreateArkGift() { const info = hook('arkGiftInfo'); const avail = info ? info.spendableSat : 0; const amt = ui.giftMax ? avail : parseAmount(ui.giftAmount, getUnit()); - if (!amt || amt < 330) { ui.giftError = t('giftAmountInvalid', { n: fmtAmount(330) + ' ' + unitLabel() }); render(); return; } + if (!amt || amt < 1) { ui.giftError = t('giftAmountInvalid', { n: fmtAmount(1) + ' ' + unitLabel() }); render(); return; } if (amt > avail) { ui.giftError = t('giftExceedsBalance'); render(); return; } ui.busy = true; ui.giftError = ''; render(); try { |
