@anywalletkit/wallet-btc 0.2.4 → 0.3.0
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-TZNOUBSP.js} +24 -3
- package/dist/chunk-TZNOUBSP.js.map +1 -0
- package/dist/history.cjs +10 -2
- package/dist/history.cjs.map +1 -1
- package/dist/history.js +1 -1
- package/dist/index.cjs +84 -20
- 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 +69 -16
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/dist/chunk-4WUAN3DC.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -30,6 +30,24 @@ declare class TooManyInputsError extends Error {
|
|
|
30
30
|
declare class UnsupportedAddressTypeError extends Error {
|
|
31
31
|
constructor(message: string);
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* The transaction was handed to the network but no definitive answer came back (no response, a
|
|
35
|
+
* timeout, a 5xx, an unreadable body): it MAY be in the mempool. Do not build another spend of the
|
|
36
|
+
* same coins — watch `hash`, the txid computed before sending (round 3, M12).
|
|
37
|
+
*/
|
|
38
|
+
declare class BroadcastUnknownError extends Error {
|
|
39
|
+
/** Always true: the transaction may be on its way. */
|
|
40
|
+
readonly maybeSent = true;
|
|
41
|
+
/** The transaction id (hex). */
|
|
42
|
+
readonly hash: string;
|
|
43
|
+
constructor(hash: string, cause: unknown);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* An endpoint answered with a refusal (HTTP 4xx other than 408, or an error body): nothing was
|
|
48
|
+
* taken. A thrown fetch, a timeout, a 5xx or a body that cannot be read may have been.
|
|
49
|
+
*/
|
|
50
|
+
declare function isAnsweredRefusal(e: unknown): boolean;
|
|
33
51
|
|
|
34
52
|
/**
|
|
35
53
|
* Esplora REST transport — the API served by mempool.space (`https://mempool.space/api`)
|
|
@@ -335,13 +353,13 @@ declare class WalletAccountBtc extends WalletAccountReadOnlyBtc implements IWall
|
|
|
335
353
|
*/
|
|
336
354
|
private signInputs;
|
|
337
355
|
private ctx;
|
|
338
|
-
private
|
|
356
|
+
private signPlan;
|
|
339
357
|
/**
|
|
340
358
|
* Plan → sign → measure. The plan's fee assumes 72-byte DER signatures; if the
|
|
341
359
|
* signed vsize somehow needs more, the difference comes out of change (never the
|
|
342
360
|
* recipient) and the tx is re-signed once.
|
|
343
361
|
*/
|
|
344
|
-
protected
|
|
362
|
+
protected signSpend(spend: BtcSpend): Promise<Built>;
|
|
345
363
|
private checkMax;
|
|
346
364
|
/**
|
|
347
365
|
* BIP-143 (p2wpkh) commits each input's signature to that input's CLAIMED amount
|
|
@@ -367,6 +385,12 @@ declare class WalletAccountBtc extends WalletAccountReadOnlyBtc implements IWall
|
|
|
367
385
|
sendTransaction(tx: BtcTransaction, options?: WriteOptions): Promise<TransferResult>;
|
|
368
386
|
/** Native BTC only (`token` is rejected). Subject to `transferMaxFee`. */
|
|
369
387
|
transfer(o: BtcTransferOptions, writeOptions?: WriteOptions): Promise<TransferResult>;
|
|
388
|
+
/**
|
|
389
|
+
* Send once through the client. Only a node's refusal means nothing went out; "already in the
|
|
390
|
+
* mempool / block chain" means an earlier attempt delivered it; anything else may have landed and
|
|
391
|
+
* is a {@link BroadcastUnknownError} with the txid computed before sending (round 3, M12).
|
|
392
|
+
*/
|
|
393
|
+
private broadcast;
|
|
370
394
|
/** The same address as a {@link WalletAccountReadOnlyBtc}. */
|
|
371
395
|
toReadOnlyAccount(): IWalletAccountReadOnly;
|
|
372
396
|
dispose(): void;
|
|
@@ -402,4 +426,4 @@ declare class WalletManagerBtc extends WalletManager<WalletAccountBtc, BtcConfig
|
|
|
402
426
|
getFeeRates(): Promise<FeeRates>;
|
|
403
427
|
}
|
|
404
428
|
|
|
405
|
-
export { BlockbookTx, BtcAddressType, BtcClient, BtcConfig, BtcMaxSpendableResult, BtcNetwork, BtcReceipt, BtcSpend, BtcTransaction, BtcTransferOptions, BtcTxDescription, type CoinSelectOptions, type CoinSelection, DEFAULT_MAX_FEE_RATE, DUST_LIMIT_P2TR, DUST_LIMIT_P2WPKH, type DecodedAddress, type DescribeContext, EsploraClient, FINAL_SEQUENCE, INPUT_WEIGHT, MAX_UTXO_INPUTS, RBF_SEQUENCE, REGTEST_NETWORK, type ScriptKind, type SegwitType, type SelectionTarget, type SpendPlan, TooManyInputsError, type UnsafeTransactionCode, UnsafeTransactionError, UnsupportedAddressTypeError, Utxo, WalletAccountBtc, WalletAccountReadOnlyBtc, WalletManagerBtc, addressFor, addressFromPublicKey, addressFromScript, assertSafe, coinType, createClient, decodeAddress, WalletManagerBtc as default, describeTransaction, dustLimit, encodeMessageSignature, estimateVsize, magicHash, networkParams, outputVBytes, parseRawTransaction, purposeFor, recoverMessagePublicKey, scriptKind, selectAll, selectCoins, signMessage, signMessageWith, spendableUtxos };
|
|
429
|
+
export { BlockbookTx, BroadcastUnknownError, BtcAddressType, BtcClient, BtcConfig, BtcMaxSpendableResult, BtcNetwork, BtcReceipt, BtcSpend, BtcTransaction, BtcTransferOptions, BtcTxDescription, type CoinSelectOptions, type CoinSelection, DEFAULT_MAX_FEE_RATE, DUST_LIMIT_P2TR, DUST_LIMIT_P2WPKH, type DecodedAddress, type DescribeContext, EsploraClient, FINAL_SEQUENCE, INPUT_WEIGHT, MAX_UTXO_INPUTS, RBF_SEQUENCE, REGTEST_NETWORK, type ScriptKind, type SegwitType, type SelectionTarget, type SpendPlan, TooManyInputsError, type UnsafeTransactionCode, UnsafeTransactionError, UnsupportedAddressTypeError, Utxo, WalletAccountBtc, WalletAccountReadOnlyBtc, WalletManagerBtc, addressFor, addressFromPublicKey, addressFromScript, assertSafe, coinType, createClient, decodeAddress, WalletManagerBtc as default, describeTransaction, dustLimit, encodeMessageSignature, estimateVsize, isAnsweredRefusal, magicHash, networkParams, outputVBytes, parseRawTransaction, purposeFor, recoverMessagePublicKey, scriptKind, selectAll, selectCoins, signMessage, signMessageWith, spendableUtxos };
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,24 @@ declare class TooManyInputsError extends Error {
|
|
|
30
30
|
declare class UnsupportedAddressTypeError extends Error {
|
|
31
31
|
constructor(message: string);
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* The transaction was handed to the network but no definitive answer came back (no response, a
|
|
35
|
+
* timeout, a 5xx, an unreadable body): it MAY be in the mempool. Do not build another spend of the
|
|
36
|
+
* same coins — watch `hash`, the txid computed before sending (round 3, M12).
|
|
37
|
+
*/
|
|
38
|
+
declare class BroadcastUnknownError extends Error {
|
|
39
|
+
/** Always true: the transaction may be on its way. */
|
|
40
|
+
readonly maybeSent = true;
|
|
41
|
+
/** The transaction id (hex). */
|
|
42
|
+
readonly hash: string;
|
|
43
|
+
constructor(hash: string, cause: unknown);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* An endpoint answered with a refusal (HTTP 4xx other than 408, or an error body): nothing was
|
|
48
|
+
* taken. A thrown fetch, a timeout, a 5xx or a body that cannot be read may have been.
|
|
49
|
+
*/
|
|
50
|
+
declare function isAnsweredRefusal(e: unknown): boolean;
|
|
33
51
|
|
|
34
52
|
/**
|
|
35
53
|
* Esplora REST transport — the API served by mempool.space (`https://mempool.space/api`)
|
|
@@ -335,13 +353,13 @@ declare class WalletAccountBtc extends WalletAccountReadOnlyBtc implements IWall
|
|
|
335
353
|
*/
|
|
336
354
|
private signInputs;
|
|
337
355
|
private ctx;
|
|
338
|
-
private
|
|
356
|
+
private signPlan;
|
|
339
357
|
/**
|
|
340
358
|
* Plan → sign → measure. The plan's fee assumes 72-byte DER signatures; if the
|
|
341
359
|
* signed vsize somehow needs more, the difference comes out of change (never the
|
|
342
360
|
* recipient) and the tx is re-signed once.
|
|
343
361
|
*/
|
|
344
|
-
protected
|
|
362
|
+
protected signSpend(spend: BtcSpend): Promise<Built>;
|
|
345
363
|
private checkMax;
|
|
346
364
|
/**
|
|
347
365
|
* BIP-143 (p2wpkh) commits each input's signature to that input's CLAIMED amount
|
|
@@ -367,6 +385,12 @@ declare class WalletAccountBtc extends WalletAccountReadOnlyBtc implements IWall
|
|
|
367
385
|
sendTransaction(tx: BtcTransaction, options?: WriteOptions): Promise<TransferResult>;
|
|
368
386
|
/** Native BTC only (`token` is rejected). Subject to `transferMaxFee`. */
|
|
369
387
|
transfer(o: BtcTransferOptions, writeOptions?: WriteOptions): Promise<TransferResult>;
|
|
388
|
+
/**
|
|
389
|
+
* Send once through the client. Only a node's refusal means nothing went out; "already in the
|
|
390
|
+
* mempool / block chain" means an earlier attempt delivered it; anything else may have landed and
|
|
391
|
+
* is a {@link BroadcastUnknownError} with the txid computed before sending (round 3, M12).
|
|
392
|
+
*/
|
|
393
|
+
private broadcast;
|
|
370
394
|
/** The same address as a {@link WalletAccountReadOnlyBtc}. */
|
|
371
395
|
toReadOnlyAccount(): IWalletAccountReadOnly;
|
|
372
396
|
dispose(): void;
|
|
@@ -402,4 +426,4 @@ declare class WalletManagerBtc extends WalletManager<WalletAccountBtc, BtcConfig
|
|
|
402
426
|
getFeeRates(): Promise<FeeRates>;
|
|
403
427
|
}
|
|
404
428
|
|
|
405
|
-
export { BlockbookTx, BtcAddressType, BtcClient, BtcConfig, BtcMaxSpendableResult, BtcNetwork, BtcReceipt, BtcSpend, BtcTransaction, BtcTransferOptions, BtcTxDescription, type CoinSelectOptions, type CoinSelection, DEFAULT_MAX_FEE_RATE, DUST_LIMIT_P2TR, DUST_LIMIT_P2WPKH, type DecodedAddress, type DescribeContext, EsploraClient, FINAL_SEQUENCE, INPUT_WEIGHT, MAX_UTXO_INPUTS, RBF_SEQUENCE, REGTEST_NETWORK, type ScriptKind, type SegwitType, type SelectionTarget, type SpendPlan, TooManyInputsError, type UnsafeTransactionCode, UnsafeTransactionError, UnsupportedAddressTypeError, Utxo, WalletAccountBtc, WalletAccountReadOnlyBtc, WalletManagerBtc, addressFor, addressFromPublicKey, addressFromScript, assertSafe, coinType, createClient, decodeAddress, WalletManagerBtc as default, describeTransaction, dustLimit, encodeMessageSignature, estimateVsize, magicHash, networkParams, outputVBytes, parseRawTransaction, purposeFor, recoverMessagePublicKey, scriptKind, selectAll, selectCoins, signMessage, signMessageWith, spendableUtxos };
|
|
429
|
+
export { BlockbookTx, BroadcastUnknownError, BtcAddressType, BtcClient, BtcConfig, BtcMaxSpendableResult, BtcNetwork, BtcReceipt, BtcSpend, BtcTransaction, BtcTransferOptions, BtcTxDescription, type CoinSelectOptions, type CoinSelection, DEFAULT_MAX_FEE_RATE, DUST_LIMIT_P2TR, DUST_LIMIT_P2WPKH, type DecodedAddress, type DescribeContext, EsploraClient, FINAL_SEQUENCE, INPUT_WEIGHT, MAX_UTXO_INPUTS, RBF_SEQUENCE, REGTEST_NETWORK, type ScriptKind, type SegwitType, type SelectionTarget, type SpendPlan, TooManyInputsError, type UnsafeTransactionCode, UnsafeTransactionError, UnsupportedAddressTypeError, Utxo, WalletAccountBtc, WalletAccountReadOnlyBtc, WalletManagerBtc, addressFor, addressFromPublicKey, addressFromScript, assertSafe, coinType, createClient, decodeAddress, WalletManagerBtc as default, describeTransaction, dustLimit, encodeMessageSignature, estimateVsize, isAnsweredRefusal, magicHash, networkParams, outputVBytes, parseRawTransaction, purposeFor, recoverMessagePublicKey, scriptKind, selectAll, selectCoins, signMessage, signMessageWith, spendableUtxos };
|
package/dist/index.js
CHANGED
|
@@ -8,11 +8,15 @@ import {
|
|
|
8
8
|
decodeCursor,
|
|
9
9
|
encodeCursor,
|
|
10
10
|
fetchWithTimeout,
|
|
11
|
+
isAlreadyKnown,
|
|
12
|
+
isAnsweredRefusal,
|
|
11
13
|
isHex64,
|
|
12
14
|
isRecord,
|
|
13
15
|
mapBlockbookTx,
|
|
16
|
+
refusal,
|
|
17
|
+
refusedStatus,
|
|
14
18
|
unconfirmedOutgoing
|
|
15
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-TZNOUBSP.js";
|
|
16
20
|
|
|
17
21
|
// src/errors.ts
|
|
18
22
|
import { InsufficientFundsError, NetworkError, SignerError } from "@anywalletkit/core";
|
|
@@ -38,6 +42,17 @@ var UnsupportedAddressTypeError = class extends Error {
|
|
|
38
42
|
this.name = "UnsupportedAddressTypeError";
|
|
39
43
|
}
|
|
40
44
|
};
|
|
45
|
+
var BroadcastUnknownError = class extends Error {
|
|
46
|
+
/** Always true: the transaction may be on its way. */
|
|
47
|
+
maybeSent = true;
|
|
48
|
+
/** The transaction id (hex). */
|
|
49
|
+
hash;
|
|
50
|
+
constructor(hash, cause) {
|
|
51
|
+
super(`Broadcast outcome unknown for ${hash}: ${cause instanceof Error ? cause.message.slice(0, 200) : String(cause).slice(0, 200)}`, { cause });
|
|
52
|
+
this.name = "BroadcastUnknownError";
|
|
53
|
+
this.hash = hash;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
41
56
|
|
|
42
57
|
// src/esplora.ts
|
|
43
58
|
import { NetworkError as NetworkError2 } from "@anywalletkit/core";
|
|
@@ -60,7 +75,11 @@ var EsploraClient = class {
|
|
|
60
75
|
async request(path, init) {
|
|
61
76
|
const url = `${this.base}${path}`;
|
|
62
77
|
const res = await fetchWithTimeout(this.fetchFn, url, init, this.timeoutMs, this.base);
|
|
63
|
-
if (!res.ok)
|
|
78
|
+
if (!res.ok) {
|
|
79
|
+
const reason = refusedStatus(res.status) ? (await res.text().catch(() => "")).slice(0, 200) : "";
|
|
80
|
+
const err = new NetworkError2(`Esplora request failed: ${res.status} ${res.statusText} (${url})${reason ? `: ${reason}` : ""}`, { provider: this.base });
|
|
81
|
+
throw refusedStatus(res.status) ? refusal(err) : err;
|
|
82
|
+
}
|
|
64
83
|
return res;
|
|
65
84
|
}
|
|
66
85
|
async json(path, check) {
|
|
@@ -165,6 +184,7 @@ function createClient(config) {
|
|
|
165
184
|
const attempts = list.length + (config.retries ?? 3);
|
|
166
185
|
const call = (method) => async (...args) => {
|
|
167
186
|
let last;
|
|
187
|
+
let answered = true;
|
|
168
188
|
for (let i = 0; i < attempts; i++) {
|
|
169
189
|
const c = list[i % list.length];
|
|
170
190
|
const fn = c[method];
|
|
@@ -173,9 +193,11 @@ function createClient(config) {
|
|
|
173
193
|
return await fn.apply(c, args);
|
|
174
194
|
} catch (e) {
|
|
175
195
|
last = e;
|
|
196
|
+
answered &&= isAnsweredRefusal(e);
|
|
176
197
|
}
|
|
177
198
|
}
|
|
178
|
-
|
|
199
|
+
const err = new NetworkError3(`All BTC clients failed for ${method}`, { cause: last });
|
|
200
|
+
throw answered && last !== void 0 ? refusal(err) : err;
|
|
179
201
|
};
|
|
180
202
|
const out = {};
|
|
181
203
|
for (const m of METHODS) out[m] = call(m);
|
|
@@ -396,11 +418,28 @@ function describeTransaction(tx, inputValues, ctx) {
|
|
|
396
418
|
});
|
|
397
419
|
const outputs = [];
|
|
398
420
|
const warnings = /* @__PURE__ */ new Set();
|
|
421
|
+
const scriptOf = (address) => {
|
|
422
|
+
try {
|
|
423
|
+
return hex.encode(decodeAddress(address, ctx.network).script);
|
|
424
|
+
} catch {
|
|
425
|
+
return void 0;
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
const ownScript = scriptOf(ctx.ownAddress);
|
|
429
|
+
const intendedScript = ctx.intended ? scriptOf(ctx.intended.to) : void 0;
|
|
430
|
+
let recipientSeen = false;
|
|
399
431
|
for (let i = 0; i < tx.outputsLength; i++) {
|
|
400
432
|
const out = tx.getOutput(i);
|
|
401
433
|
const value = out.amount ?? 0n;
|
|
402
434
|
const address = tx.getOutputAddress(i, ctx.network);
|
|
403
|
-
const
|
|
435
|
+
const script = out.script ? hex.encode(out.script) : void 0;
|
|
436
|
+
const mine = script !== void 0 && script === ownScript;
|
|
437
|
+
let role;
|
|
438
|
+
if (ctx.intended === void 0) role = mine ? "change" : "recipient";
|
|
439
|
+
else if (!recipientSeen && script !== void 0 && script === intendedScript && value === ctx.intended.value) {
|
|
440
|
+
role = "recipient";
|
|
441
|
+
recipientSeen = true;
|
|
442
|
+
} else role = mine ? "change" : "unknown";
|
|
404
443
|
if (out.script && value < dustLimit(scriptKind(out.script))) warnings.add("dust-output");
|
|
405
444
|
outputs.push({ address, value, role });
|
|
406
445
|
}
|
|
@@ -687,7 +726,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
687
726
|
ctx(intended) {
|
|
688
727
|
return { network: this._network, ownAddress: this.address, intended, maxFeeRate: this.maxFeeRate };
|
|
689
728
|
}
|
|
690
|
-
async
|
|
729
|
+
async signPlan(plan, to, recipientValue, change, lockTime, rbf) {
|
|
691
730
|
const spend = spendInfo(this.publicKey, this._network, this.addressType);
|
|
692
731
|
const tx = new Transaction2({ lockTime });
|
|
693
732
|
const sequence = rbf ? RBF_SEQUENCE : FINAL_SEQUENCE;
|
|
@@ -705,7 +744,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
705
744
|
* signed vsize somehow needs more, the difference comes out of change (never the
|
|
706
745
|
* recipient) and the tx is re-signed once.
|
|
707
746
|
*/
|
|
708
|
-
async
|
|
747
|
+
async signSpend(spend) {
|
|
709
748
|
this.live();
|
|
710
749
|
const plan = await this.planSpend(spend);
|
|
711
750
|
await this.verifyPrevouts(plan.utxos);
|
|
@@ -713,7 +752,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
713
752
|
const rbf = spend.rbf ?? this._config.rbf ?? true;
|
|
714
753
|
let change = plan.change;
|
|
715
754
|
let fee = plan.fee;
|
|
716
|
-
let tx = await this.
|
|
755
|
+
let tx = await this.signPlan(plan, spend.to, plan.amount, change, lockTime, rbf);
|
|
717
756
|
const required = BigInt(tx.vsize) * plan.feeRate;
|
|
718
757
|
if (required > fee) {
|
|
719
758
|
const delta = required - fee;
|
|
@@ -726,7 +765,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
726
765
|
fee = plan.fee + plan.change;
|
|
727
766
|
change = 0n;
|
|
728
767
|
} else fee = required;
|
|
729
|
-
tx = await this.
|
|
768
|
+
tx = await this.signPlan(plan, spend.to, plan.amount, change, lockTime, rbf);
|
|
730
769
|
if (BigInt(tx.vsize) * plan.feeRate > fee) throw new Error("Fee shortfall after output rebalance.");
|
|
731
770
|
}
|
|
732
771
|
const ctx = this.ctx({ to: spend.to, value: plan.amount });
|
|
@@ -797,7 +836,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
797
836
|
/** "What you sign": the fully resolved spend (signed, NOT broadcast) or a caller's raw hex. */
|
|
798
837
|
async describeTransaction(tx) {
|
|
799
838
|
if (typeof tx === "string") return (await this.describeRaw(tx)).desc;
|
|
800
|
-
return (await this.
|
|
839
|
+
return (await this.signSpend(tx)).desc;
|
|
801
840
|
}
|
|
802
841
|
/** Sign without broadcasting. Returns the raw transaction hex. Raw hex input gets the checks `sendTransaction` runs (prevouts through the client) and is returned unchanged, never re-signed. */
|
|
803
842
|
async signTransaction(tx, options = {}) {
|
|
@@ -807,7 +846,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
807
846
|
this.checkMax(desc.fee, feeLimit(this._config.transactionMaxFee, options), "transaction");
|
|
808
847
|
return tx;
|
|
809
848
|
}
|
|
810
|
-
const { tx: built, fee } = await this.
|
|
849
|
+
const { tx: built, fee } = await this.signSpend(tx);
|
|
811
850
|
this.checkMax(fee, feeLimit(this._config.transactionMaxFee, options), "transaction");
|
|
812
851
|
return built.hex;
|
|
813
852
|
}
|
|
@@ -825,24 +864,36 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
825
864
|
hash = parsed.id;
|
|
826
865
|
fee = desc.fee;
|
|
827
866
|
} else {
|
|
828
|
-
const built = await this.
|
|
867
|
+
const built = await this.signSpend(tx);
|
|
829
868
|
rawHex = built.tx.hex;
|
|
830
869
|
hash = built.tx.id;
|
|
831
870
|
fee = built.fee;
|
|
832
871
|
}
|
|
833
872
|
this.checkMax(fee, feeLimit(this._config.transactionMaxFee, options), "transaction");
|
|
834
|
-
|
|
835
|
-
return { hash: txid || hash, fee };
|
|
873
|
+
return { hash: await this.broadcast(client, rawHex, hash), fee };
|
|
836
874
|
}
|
|
837
875
|
/** Native BTC only (`token` is rejected). Subject to `transferMaxFee`. */
|
|
838
876
|
async transfer(o, writeOptions = {}) {
|
|
839
877
|
if (o.token) throw new Error("Tokens are not supported on the bitcoin blockchain.");
|
|
840
878
|
this.live();
|
|
841
879
|
const client = this.requireClient("transfer");
|
|
842
|
-
const built = await this.
|
|
880
|
+
const built = await this.signSpend({ to: o.recipient, value: o.amount });
|
|
843
881
|
this.checkMax(built.fee, feeLimit(this._config.transferMaxFee, writeOptions), "transfer");
|
|
844
|
-
|
|
845
|
-
|
|
882
|
+
return { hash: await this.broadcast(client, built.tx.hex, built.tx.id), fee: built.fee };
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* Send once through the client. Only a node's refusal means nothing went out; "already in the
|
|
886
|
+
* mempool / block chain" means an earlier attempt delivered it; anything else may have landed and
|
|
887
|
+
* is a {@link BroadcastUnknownError} with the txid computed before sending (round 3, M12).
|
|
888
|
+
*/
|
|
889
|
+
async broadcast(client, rawHex, txid) {
|
|
890
|
+
try {
|
|
891
|
+
return await client.broadcast(rawHex) || txid;
|
|
892
|
+
} catch (e) {
|
|
893
|
+
if (isAlreadyKnown(e)) return txid;
|
|
894
|
+
if (isAnsweredRefusal(e)) throw e;
|
|
895
|
+
throw new BroadcastUnknownError(txid, e);
|
|
896
|
+
}
|
|
846
897
|
}
|
|
847
898
|
/** The same address as a {@link WalletAccountReadOnlyBtc}. */
|
|
848
899
|
toReadOnlyAccount() {
|
|
@@ -916,6 +967,7 @@ var WalletManagerBtc = class extends WalletManager {
|
|
|
916
967
|
};
|
|
917
968
|
export {
|
|
918
969
|
BlockbookClient,
|
|
970
|
+
BroadcastUnknownError,
|
|
919
971
|
CAIP2_GENESIS,
|
|
920
972
|
DEFAULT_MAX_FEE_RATE,
|
|
921
973
|
DUST_LIMIT_P2TR,
|
|
@@ -949,6 +1001,7 @@ export {
|
|
|
949
1001
|
encodeCursor as encodeHistoryCursor,
|
|
950
1002
|
encodeMessageSignature,
|
|
951
1003
|
estimateVsize,
|
|
1004
|
+
isAnsweredRefusal,
|
|
952
1005
|
magicHash,
|
|
953
1006
|
mapBlockbookTx,
|
|
954
1007
|
networkParams,
|