aboutsummaryrefslogtreecommitdiffstats
path: root/src/ark/proto.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-07-05 09:05:20 -0700
committerAdam Soltys <[email protected]>2026-07-05 09:05:20 -0700
commitf9fc0bb3e38c3387f8af5d151aa03d83376ba9cc (patch)
tree4f7754e3b6b0be893e4c21acc8a881e9347d32b0 /src/ark/proto.js
parentdb7e70fac51e16c7f389e031a80355b2d9d0f57e (diff)
downloadcoinosv3-f9fc0bb3e38c3387f8af5d151aa03d83376ba9cc.tar.gz
coinosv3-f9fc0bb3e38c3387f8af5d151aa03d83376ba9cc.zip
Collaborative offboard: move the ark balance back on-chain
PrepareOffboard/FinishOffboard against captaind — per-input ownership attestations, connector-based musig forfeits (with the deterministic fanout tx when there is more than one input), and fee math that mirrors the server bit-for-bit (weight fee rounds up, ppm-by-expiry rounds down) since it rejects any other net amount. The receive pane gains a 'Move back on-chain' button that withdraws everything to the wallet's own fresh address; the incoming tx is labeled 'Ark withdrawal' in History and the ark side records the movement. Crash-safe: the txid persists before FinishOffboard and resume asks GetVtxoStatus whether the forfeits landed (the server broadcasts the tx itself). Also fixes boarding to applySentTx after broadcasting the funding tx — without it the spent coin lingered in the utxo set (a second board double-spent it), and the post-board scan() could resurrect it by racing the explorer's indexing. Co-Authored-By: Claude Fable 5 <[email protected]>
Diffstat (limited to 'src/ark/proto.js')
-rw-r--r--src/ark/proto.js23
1 files changed, 22 insertions, 1 deletions
diff --git a/src/ark/proto.js b/src/ark/proto.js
index 1cbc0ef..ae2342e 100644
--- a/src/ark/proto.js
+++ b/src/ark/proto.js
@@ -364,7 +364,11 @@ export function decodeVtxo(bytes) {
export async function getArkInfo(ark) {
const data = await grpcCall(ark, 'bark_server.ArkService/GetArkInfo', new Uint8Array(0));
// Fee schedules a server never sends (or sends all-zero) mean "free".
- const info = { boardFees: {}, refreshFees: { baseFeeSat: 0, ppmExpiryTable: [] } };
+ const info = {
+ boardFees: {},
+ refreshFees: { baseFeeSat: 0, ppmExpiryTable: [] },
+ offboardFees: { baseFeeSat: 0, fixedAdditionalVb: 0, ppmExpiryTable: [] },
+ };
for (const { field, value } of pbFields(data)) {
if (field === 1) info.network = td.decode(value);
if (field === 2) info.serverPubkey = hex.encode(value);
@@ -374,6 +378,7 @@ export async function getArkInfo(ark) {
if (field === 10) info.requiredBoardConfirmations = Number(value);
if (field === 13) info.minBoardAmountSat = Number(value);
if (field === 16) info.mailboxPubkey = hex.encode(value);
+ if (field === 20) info.maxOffboardInputs = Number(value);
if (field === 18) { // FeeSchedule
for (const f of pbFields(value)) {
if (f.field === 1) { // board
@@ -385,6 +390,22 @@ export async function getArkInfo(ark) {
}
info.boardFees = b;
}
+ if (f.field === 2) { // offboard
+ const ob = { baseFeeSat: 0, fixedAdditionalVb: 0, ppmExpiryTable: [] };
+ for (const g of pbFields(f.value)) {
+ if (g.field === 1) ob.baseFeeSat = Number(g.value);
+ if (g.field === 2) ob.fixedAdditionalVb = Number(g.value);
+ if (g.field === 3) {
+ const e = { thresholdBlocks: 0, ppm: 0 };
+ for (const h of pbFields(g.value)) {
+ if (h.field === 1) e.thresholdBlocks = Number(h.value);
+ if (h.field === 2) e.ppm = Number(h.value);
+ }
+ ob.ppmExpiryTable.push(e);
+ }
+ }
+ info.offboardFees = ob;
+ }
if (f.field === 3) { // refresh
const rf = { baseFeeSat: 0, ppmExpiryTable: [] };
for (const g of pbFields(f.value)) {