@anywalletkit/wallet-btc 0.2.5 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/{chunk-4WUAN3DC.js → chunk-SIR2SRK2.js} +24 -3
- package/dist/chunk-SIR2SRK2.js.map +1 -0
- package/dist/{history-B5fmsCjZ.d.cts → history-CasPAf7l.d.cts} +1 -1
- package/dist/{history-B5fmsCjZ.d.ts → history-CasPAf7l.d.ts} +1 -1
- package/dist/history.cjs +10 -2
- package/dist/history.cjs.map +1 -1
- package/dist/history.d.cts +1 -1
- package/dist/history.d.ts +1 -1
- package/dist/history.js +1 -1
- package/dist/index.cjs +76 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -3
- package/dist/index.d.ts +27 -3
- package/dist/index.js +61 -8
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
- package/dist/chunk-4WUAN3DC.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
BlockbookClient: () => BlockbookClient,
|
|
24
|
+
BroadcastUnknownError: () => BroadcastUnknownError,
|
|
24
25
|
CAIP2_GENESIS: () => CAIP2_GENESIS,
|
|
25
26
|
DEFAULT_MAX_FEE_RATE: () => DEFAULT_MAX_FEE_RATE,
|
|
26
27
|
DUST_LIMIT_P2TR: () => DUST_LIMIT_P2TR,
|
|
@@ -54,6 +55,7 @@ __export(src_exports, {
|
|
|
54
55
|
encodeHistoryCursor: () => encodeCursor,
|
|
55
56
|
encodeMessageSignature: () => encodeMessageSignature,
|
|
56
57
|
estimateVsize: () => estimateVsize,
|
|
58
|
+
isAnsweredRefusal: () => isAnsweredRefusal,
|
|
57
59
|
magicHash: () => magicHash,
|
|
58
60
|
mapBlockbookTx: () => mapBlockbookTx,
|
|
59
61
|
networkParams: () => networkParams,
|
|
@@ -102,9 +104,17 @@ var UnsupportedAddressTypeError = class extends Error {
|
|
|
102
104
|
this.name = "UnsupportedAddressTypeError";
|
|
103
105
|
}
|
|
104
106
|
};
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
var BroadcastUnknownError = class extends Error {
|
|
108
|
+
/** Always true: the transaction may be on its way. */
|
|
109
|
+
maybeSent = true;
|
|
110
|
+
/** The transaction id (hex). */
|
|
111
|
+
hash;
|
|
112
|
+
constructor(hash, cause) {
|
|
113
|
+
super(`Broadcast outcome unknown for ${hash}: ${cause instanceof Error ? cause.message.slice(0, 200) : String(cause).slice(0, 200)}`, { cause });
|
|
114
|
+
this.name = "BroadcastUnknownError";
|
|
115
|
+
this.hash = hash;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
108
118
|
|
|
109
119
|
// src/http.ts
|
|
110
120
|
var import_core2 = require("@anywalletkit/core");
|
|
@@ -117,11 +127,25 @@ async function fetchWithTimeout(fetchFn, url, init, timeoutMs, provider) {
|
|
|
117
127
|
throw new import_core2.NetworkError(`Request failed: ${url}`, { provider, cause: e });
|
|
118
128
|
}
|
|
119
129
|
}
|
|
130
|
+
var refusedStatus = (status) => status >= 400 && status < 500 && status !== 408;
|
|
131
|
+
function refusal(e) {
|
|
132
|
+
return Object.assign(e, { answered: true });
|
|
133
|
+
}
|
|
134
|
+
function isAnsweredRefusal(e) {
|
|
135
|
+
return e?.answered === true;
|
|
136
|
+
}
|
|
137
|
+
function isAlreadyKnown(e) {
|
|
138
|
+
for (let x = e, depth = 0; x instanceof Error && depth < 5; x = x.cause, depth++) {
|
|
139
|
+
if (/txn-already-(in-mempool|known)|already (known|in (the )?(mempool|block ?chain))/i.test(x.message)) return true;
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
120
143
|
var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
121
144
|
var isHex64 = (v) => typeof v === "string" && /^[0-9a-fA-F]{64}$/.test(v);
|
|
122
145
|
var isIntString = (v) => typeof v === "string" && /^-?\d+$/.test(v);
|
|
123
146
|
|
|
124
147
|
// src/blockbook.ts
|
|
148
|
+
var import_core3 = require("@anywalletkit/core");
|
|
125
149
|
var MEMPOOL_SPACE_URL = "https://mempool.space";
|
|
126
150
|
function unconfirmedOutgoing(transactions, address) {
|
|
127
151
|
const pending = (transactions ?? []).filter((tx) => tx.blockHeight === -1);
|
|
@@ -162,7 +186,11 @@ var BlockbookClient = class _BlockbookClient {
|
|
|
162
186
|
async get(path, check) {
|
|
163
187
|
const url = `${this.base}/api${path}`;
|
|
164
188
|
const res = await fetchWithTimeout(this.fetchFn, url, void 0, this.timeoutMs, this.base);
|
|
165
|
-
if (!res.ok)
|
|
189
|
+
if (!res.ok) {
|
|
190
|
+
const reason = refusedStatus(res.status) ? (await res.text().catch(() => "")).slice(0, 200) : "";
|
|
191
|
+
const err = new import_core3.NetworkError(`Blockbook request failed: ${res.status} ${res.statusText} (${url})${reason ? `: ${reason}` : ""}`, { provider: this.base });
|
|
192
|
+
throw refusedStatus(res.status) ? refusal(err) : err;
|
|
193
|
+
}
|
|
166
194
|
let body;
|
|
167
195
|
try {
|
|
168
196
|
body = await res.json();
|
|
@@ -226,7 +254,7 @@ var BlockbookClient = class _BlockbookClient {
|
|
|
226
254
|
}
|
|
227
255
|
async broadcast(rawTx) {
|
|
228
256
|
const data = await this.get(`/v2/sendtx/${rawTx}`, (v) => isRecord(v));
|
|
229
|
-
if (data.error) throw new Error(typeof data.error === "string" ? data.error : data.error.message ?? "broadcast failed");
|
|
257
|
+
if (data.error) throw refusal(new Error(typeof data.error === "string" ? data.error : data.error.message ?? "broadcast failed"));
|
|
230
258
|
if (!data.result) throw new import_core3.NetworkError("Blockbook returned no txid", { provider: this.base });
|
|
231
259
|
return data.result;
|
|
232
260
|
}
|
|
@@ -274,7 +302,11 @@ var EsploraClient = class {
|
|
|
274
302
|
async request(path, init) {
|
|
275
303
|
const url = `${this.base}${path}`;
|
|
276
304
|
const res = await fetchWithTimeout(this.fetchFn, url, init, this.timeoutMs, this.base);
|
|
277
|
-
if (!res.ok)
|
|
305
|
+
if (!res.ok) {
|
|
306
|
+
const reason = refusedStatus(res.status) ? (await res.text().catch(() => "")).slice(0, 200) : "";
|
|
307
|
+
const err = new import_core4.NetworkError(`Esplora request failed: ${res.status} ${res.statusText} (${url})${reason ? `: ${reason}` : ""}`, { provider: this.base });
|
|
308
|
+
throw refusedStatus(res.status) ? refusal(err) : err;
|
|
309
|
+
}
|
|
278
310
|
return res;
|
|
279
311
|
}
|
|
280
312
|
async json(path, check) {
|
|
@@ -379,6 +411,7 @@ function createClient(config) {
|
|
|
379
411
|
const attempts = list.length + (config.retries ?? 3);
|
|
380
412
|
const call = (method) => async (...args) => {
|
|
381
413
|
let last;
|
|
414
|
+
let answered = true;
|
|
382
415
|
for (let i = 0; i < attempts; i++) {
|
|
383
416
|
const c = list[i % list.length];
|
|
384
417
|
const fn = c[method];
|
|
@@ -387,9 +420,11 @@ function createClient(config) {
|
|
|
387
420
|
return await fn.apply(c, args);
|
|
388
421
|
} catch (e) {
|
|
389
422
|
last = e;
|
|
423
|
+
answered &&= isAnsweredRefusal(e);
|
|
390
424
|
}
|
|
391
425
|
}
|
|
392
|
-
|
|
426
|
+
const err = new import_core5.NetworkError(`All BTC clients failed for ${method}`, { cause: last });
|
|
427
|
+
throw answered && last !== void 0 ? refusal(err) : err;
|
|
393
428
|
};
|
|
394
429
|
const out = {};
|
|
395
430
|
for (const m of METHODS) out[m] = call(m);
|
|
@@ -610,11 +645,28 @@ function describeTransaction(tx, inputValues, ctx) {
|
|
|
610
645
|
});
|
|
611
646
|
const outputs = [];
|
|
612
647
|
const warnings = /* @__PURE__ */ new Set();
|
|
648
|
+
const scriptOf = (address) => {
|
|
649
|
+
try {
|
|
650
|
+
return import_base2.hex.encode(decodeAddress(address, ctx.network).script);
|
|
651
|
+
} catch {
|
|
652
|
+
return void 0;
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
const ownScript = scriptOf(ctx.ownAddress);
|
|
656
|
+
const intendedScript = ctx.intended ? scriptOf(ctx.intended.to) : void 0;
|
|
657
|
+
let recipientSeen = false;
|
|
613
658
|
for (let i = 0; i < tx.outputsLength; i++) {
|
|
614
659
|
const out = tx.getOutput(i);
|
|
615
660
|
const value = out.amount ?? 0n;
|
|
616
661
|
const address = tx.getOutputAddress(i, ctx.network);
|
|
617
|
-
const
|
|
662
|
+
const script = out.script ? import_base2.hex.encode(out.script) : void 0;
|
|
663
|
+
const mine = script !== void 0 && script === ownScript;
|
|
664
|
+
let role;
|
|
665
|
+
if (ctx.intended === void 0) role = mine ? "change" : "recipient";
|
|
666
|
+
else if (!recipientSeen && script !== void 0 && script === intendedScript && value === ctx.intended.value) {
|
|
667
|
+
role = "recipient";
|
|
668
|
+
recipientSeen = true;
|
|
669
|
+
} else role = mine ? "change" : "unknown";
|
|
618
670
|
if (out.script && value < dustLimit(scriptKind(out.script))) warnings.add("dust-output");
|
|
619
671
|
outputs.push({ address, value, role });
|
|
620
672
|
}
|
|
@@ -1045,8 +1097,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
1045
1097
|
fee = built.fee;
|
|
1046
1098
|
}
|
|
1047
1099
|
this.checkMax(fee, (0, import_core9.feeLimit)(this._config.transactionMaxFee, options), "transaction");
|
|
1048
|
-
|
|
1049
|
-
return { hash: txid || hash, fee };
|
|
1100
|
+
return { hash: await this.broadcast(client, rawHex, hash), fee };
|
|
1050
1101
|
}
|
|
1051
1102
|
/** Native BTC only (`token` is rejected). Subject to `transferMaxFee`. */
|
|
1052
1103
|
async transfer(o, writeOptions = {}) {
|
|
@@ -1055,8 +1106,21 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
1055
1106
|
const client = this.requireClient("transfer");
|
|
1056
1107
|
const built = await this.signSpend({ to: o.recipient, value: o.amount });
|
|
1057
1108
|
this.checkMax(built.fee, (0, import_core9.feeLimit)(this._config.transferMaxFee, writeOptions), "transfer");
|
|
1058
|
-
|
|
1059
|
-
|
|
1109
|
+
return { hash: await this.broadcast(client, built.tx.hex, built.tx.id), fee: built.fee };
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Send once through the client. Only a node's refusal means nothing went out; "already in the
|
|
1113
|
+
* mempool / block chain" means an earlier attempt delivered it; anything else may have landed and
|
|
1114
|
+
* is a {@link BroadcastUnknownError} with the txid computed before sending (round 3, M12).
|
|
1115
|
+
*/
|
|
1116
|
+
async broadcast(client, rawHex, txid) {
|
|
1117
|
+
try {
|
|
1118
|
+
return await client.broadcast(rawHex) || txid;
|
|
1119
|
+
} catch (e) {
|
|
1120
|
+
if (isAlreadyKnown(e)) return txid;
|
|
1121
|
+
if (isAnsweredRefusal(e)) throw e;
|
|
1122
|
+
throw new BroadcastUnknownError(txid, e);
|
|
1123
|
+
}
|
|
1060
1124
|
}
|
|
1061
1125
|
/** The same address as a {@link WalletAccountReadOnlyBtc}. */
|
|
1062
1126
|
toReadOnlyAccount() {
|