@anywalletkit/wallet-zcash 0.2.5 → 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 +12 -6
- package/dist/{chunk-6C7LNZNX.js → chunk-KICTVRL7.js} +23 -3
- package/dist/chunk-KICTVRL7.js.map +1 -0
- package/dist/{history-DQNeEwXE.d.cts → history-BpA-R7Tf.d.cts} +5 -4
- package/dist/{history-DQNeEwXE.d.ts → history-BpA-R7Tf.d.ts} +5 -4
- package/dist/history.cjs +9 -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 +174 -91
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -8
- package/dist/index.d.ts +42 -8
- package/dist/index.js +93 -20
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/dist/chunk-6C7LNZNX.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -22,6 +22,7 @@ var src_exports = {};
|
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
BLOCKCHAIR_URL: () => BLOCKCHAIR_URL,
|
|
24
24
|
BlockchairClient: () => BlockchairClient,
|
|
25
|
+
BroadcastUnknownError: () => BroadcastUnknownError,
|
|
25
26
|
CAIP2: () => CAIP2,
|
|
26
27
|
CURRENT_BRANCH_ID: () => CURRENT_BRANCH_ID,
|
|
27
28
|
DEFAULT_EXPIRY_DELTA: () => DEFAULT_EXPIRY_DELTA,
|
|
@@ -65,6 +66,7 @@ __export(src_exports, {
|
|
|
65
66
|
estimateSize: () => estimateSize,
|
|
66
67
|
hash160: () => hash160,
|
|
67
68
|
headerDigest: () => headerDigest,
|
|
69
|
+
isAnsweredRefusal: () => isAnsweredRefusal,
|
|
68
70
|
logicalActions: () => logicalActions,
|
|
69
71
|
magicHash: () => magicHash,
|
|
70
72
|
mapDashboardTx: () => mapDashboardTx,
|
|
@@ -164,77 +166,6 @@ function outputSize(script) {
|
|
|
164
166
|
}
|
|
165
167
|
var DUST_LIMIT = 546n;
|
|
166
168
|
|
|
167
|
-
// src/fee.ts
|
|
168
|
-
var import_core = require("@anywalletkit/core");
|
|
169
|
-
var MARGINAL_FEE = 5000n;
|
|
170
|
-
var GRACE_ACTIONS = 2;
|
|
171
|
-
var P2PKH_STANDARD_INPUT_SIZE = 150;
|
|
172
|
-
var P2PKH_STANDARD_OUTPUT_SIZE = 34;
|
|
173
|
-
var P2PKH_INPUT_SIZE = 149;
|
|
174
|
-
var MAX_UTXO_INPUTS = 200;
|
|
175
|
-
function logicalActions(inputSizes2, outputSizes) {
|
|
176
|
-
const inBytes = inputSizes2.reduce((s, n) => s + n, 0);
|
|
177
|
-
const outBytes = outputSizes.reduce((s, n) => s + n, 0);
|
|
178
|
-
return Math.max(Math.ceil(inBytes / P2PKH_STANDARD_INPUT_SIZE), Math.ceil(outBytes / P2PKH_STANDARD_OUTPUT_SIZE));
|
|
179
|
-
}
|
|
180
|
-
function conventionalFee(inputSizes2, outputSizes) {
|
|
181
|
-
return MARGINAL_FEE * BigInt(Math.max(GRACE_ACTIONS, logicalActions(inputSizes2, outputSizes)));
|
|
182
|
-
}
|
|
183
|
-
function estimateSize(inputs, outputSizes) {
|
|
184
|
-
const cs = (n) => n < 253 ? 1 : 3;
|
|
185
|
-
return 20 + cs(inputs) + inputs * P2PKH_INPUT_SIZE + cs(outputSizes.length) + outputSizes.reduce((s, n) => s + n, 0) + 3;
|
|
186
|
-
}
|
|
187
|
-
var TooManyInputsError = class extends Error {
|
|
188
|
-
maxInputs;
|
|
189
|
-
constructor(maxInputs) {
|
|
190
|
-
super(`The spend needs more than ${maxInputs} inputs; consolidate the UTXOs first or raise maxInputs.`);
|
|
191
|
-
this.name = "TooManyInputsError";
|
|
192
|
-
this.maxInputs = maxInputs;
|
|
193
|
-
}
|
|
194
|
-
};
|
|
195
|
-
var cmpUtxo = (a, b) => a.value > b.value ? -1 : a.value < b.value ? 1 : a.txid < b.txid ? -1 : a.txid > b.txid ? 1 : a.vout - b.vout;
|
|
196
|
-
function spendableUtxos(utxos) {
|
|
197
|
-
return utxos.filter((u) => u.value > MARGINAL_FEE).sort(cmpUtxo);
|
|
198
|
-
}
|
|
199
|
-
function selectCoins(o) {
|
|
200
|
-
const maxInputs = o.maxInputs ?? MAX_UTXO_INPUTS;
|
|
201
|
-
const dust = o.changeDust ?? DUST_LIMIT;
|
|
202
|
-
const candidates = spendableUtxos(o.utxos);
|
|
203
|
-
const available = o.utxos.reduce((s, u) => s + u.value, 0n);
|
|
204
|
-
const inputs = (n) => Array.from({ length: n }, () => P2PKH_INPUT_SIZE);
|
|
205
|
-
const selected = [];
|
|
206
|
-
let sum = 0n;
|
|
207
|
-
for (const u of candidates) {
|
|
208
|
-
if (selected.length >= maxInputs) throw new TooManyInputsError(maxInputs);
|
|
209
|
-
selected.push(u);
|
|
210
|
-
sum += u.value;
|
|
211
|
-
const n = selected.length;
|
|
212
|
-
const convNoChange = conventionalFee(inputs(n), [o.recipientOutputSize]);
|
|
213
|
-
const feeNoChange = o.fee ?? convNoChange;
|
|
214
|
-
if (sum < o.target + feeNoChange) continue;
|
|
215
|
-
const convWithChange = conventionalFee(inputs(n), [o.recipientOutputSize, o.changeOutputSize]);
|
|
216
|
-
const feeWithChange = o.fee ?? convWithChange;
|
|
217
|
-
const change = sum - o.target - feeWithChange;
|
|
218
|
-
if (change >= dust) {
|
|
219
|
-
return { utxos: selected, fee: feeWithChange, conventionalFee: convWithChange, change, changeFolded: false, size: estimateSize(n, [o.recipientOutputSize, o.changeOutputSize]) };
|
|
220
|
-
}
|
|
221
|
-
return { utxos: selected, fee: sum - o.target, conventionalFee: convNoChange, change: 0n, changeFolded: sum - o.target > feeNoChange, size: estimateSize(n, [o.recipientOutputSize]) };
|
|
222
|
-
}
|
|
223
|
-
const need = o.target + (o.fee ?? conventionalFee(inputs(Math.max(selected.length, 1)), [o.recipientOutputSize]));
|
|
224
|
-
throw new import_core.InsufficientFundsError(need, available, "ZEC");
|
|
225
|
-
}
|
|
226
|
-
function selectAll(o) {
|
|
227
|
-
const maxInputs = o.maxInputs ?? MAX_UTXO_INPUTS;
|
|
228
|
-
const utxos = spendableUtxos(o.utxos).slice(0, maxInputs);
|
|
229
|
-
if (utxos.length === 0) return { utxos, fee: 0n, amount: 0n, size: 0 };
|
|
230
|
-
const fee = o.fee ?? conventionalFee(Array.from({ length: utxos.length }, () => P2PKH_INPUT_SIZE), [o.outputSize]);
|
|
231
|
-
const amount = utxos.reduce((s, u) => s + u.value, 0n) - fee;
|
|
232
|
-
return { utxos, fee, amount: amount > 0n ? amount : 0n, size: estimateSize(utxos.length, [o.outputSize]) };
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// src/tx.ts
|
|
236
|
-
var import_blake2 = require("@noble/hashes/blake2.js");
|
|
237
|
-
|
|
238
169
|
// src/encoding.ts
|
|
239
170
|
var import_utils = require("@noble/hashes/utils.js");
|
|
240
171
|
function compactSize(n) {
|
|
@@ -351,7 +282,76 @@ function reverseBytes(b) {
|
|
|
351
282
|
return Uint8Array.from(b).reverse();
|
|
352
283
|
}
|
|
353
284
|
|
|
285
|
+
// src/fee.ts
|
|
286
|
+
var import_core = require("@anywalletkit/core");
|
|
287
|
+
var MARGINAL_FEE = 5000n;
|
|
288
|
+
var GRACE_ACTIONS = 2;
|
|
289
|
+
var P2PKH_STANDARD_INPUT_SIZE = 150;
|
|
290
|
+
var P2PKH_STANDARD_OUTPUT_SIZE = 34;
|
|
291
|
+
var P2PKH_INPUT_SIZE = 149;
|
|
292
|
+
var MAX_UTXO_INPUTS = 200;
|
|
293
|
+
function logicalActions(inputSizes2, outputSizes) {
|
|
294
|
+
const inBytes = inputSizes2.reduce((s, n) => s + n, 0);
|
|
295
|
+
const outBytes = outputSizes.reduce((s, n) => s + n, 0);
|
|
296
|
+
return Math.max(Math.ceil(inBytes / P2PKH_STANDARD_INPUT_SIZE), Math.ceil(outBytes / P2PKH_STANDARD_OUTPUT_SIZE));
|
|
297
|
+
}
|
|
298
|
+
function conventionalFee(inputSizes2, outputSizes) {
|
|
299
|
+
return MARGINAL_FEE * BigInt(Math.max(GRACE_ACTIONS, logicalActions(inputSizes2, outputSizes)));
|
|
300
|
+
}
|
|
301
|
+
function estimateSize(inputs, outputSizes) {
|
|
302
|
+
const cs = (n) => n < 253 ? 1 : 3;
|
|
303
|
+
return 20 + cs(inputs) + inputs * P2PKH_INPUT_SIZE + cs(outputSizes.length) + outputSizes.reduce((s, n) => s + n, 0) + 3;
|
|
304
|
+
}
|
|
305
|
+
var TooManyInputsError = class extends Error {
|
|
306
|
+
maxInputs;
|
|
307
|
+
constructor(maxInputs) {
|
|
308
|
+
super(`The spend needs more than ${maxInputs} inputs; consolidate the UTXOs first or raise maxInputs.`);
|
|
309
|
+
this.name = "TooManyInputsError";
|
|
310
|
+
this.maxInputs = maxInputs;
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
var cmpUtxo = (a, b) => a.value > b.value ? -1 : a.value < b.value ? 1 : a.txid < b.txid ? -1 : a.txid > b.txid ? 1 : a.vout - b.vout;
|
|
314
|
+
function spendableUtxos(utxos) {
|
|
315
|
+
return utxos.filter((u) => u.value > MARGINAL_FEE).sort(cmpUtxo);
|
|
316
|
+
}
|
|
317
|
+
function selectCoins(o) {
|
|
318
|
+
const maxInputs = o.maxInputs ?? MAX_UTXO_INPUTS;
|
|
319
|
+
const dust = o.changeDust ?? DUST_LIMIT;
|
|
320
|
+
const candidates = spendableUtxos(o.utxos);
|
|
321
|
+
const available = o.utxos.reduce((s, u) => s + u.value, 0n);
|
|
322
|
+
const inputs = (n) => Array.from({ length: n }, () => P2PKH_INPUT_SIZE);
|
|
323
|
+
const selected = [];
|
|
324
|
+
let sum = 0n;
|
|
325
|
+
for (const u of candidates) {
|
|
326
|
+
if (selected.length >= maxInputs) throw new TooManyInputsError(maxInputs);
|
|
327
|
+
selected.push(u);
|
|
328
|
+
sum += u.value;
|
|
329
|
+
const n = selected.length;
|
|
330
|
+
const convNoChange = conventionalFee(inputs(n), [o.recipientOutputSize]);
|
|
331
|
+
const feeNoChange = o.fee ?? convNoChange;
|
|
332
|
+
if (sum < o.target + feeNoChange) continue;
|
|
333
|
+
const convWithChange = conventionalFee(inputs(n), [o.recipientOutputSize, o.changeOutputSize]);
|
|
334
|
+
const feeWithChange = o.fee ?? convWithChange;
|
|
335
|
+
const change = sum - o.target - feeWithChange;
|
|
336
|
+
if (change >= dust) {
|
|
337
|
+
return { utxos: selected, fee: feeWithChange, conventionalFee: convWithChange, change, changeFolded: false, size: estimateSize(n, [o.recipientOutputSize, o.changeOutputSize]) };
|
|
338
|
+
}
|
|
339
|
+
return { utxos: selected, fee: sum - o.target, conventionalFee: convNoChange, change: 0n, changeFolded: sum - o.target > feeNoChange, size: estimateSize(n, [o.recipientOutputSize]) };
|
|
340
|
+
}
|
|
341
|
+
const need = o.target + (o.fee ?? conventionalFee(inputs(Math.max(selected.length, 1)), [o.recipientOutputSize]));
|
|
342
|
+
throw new import_core.InsufficientFundsError(need, available, "ZEC");
|
|
343
|
+
}
|
|
344
|
+
function selectAll(o) {
|
|
345
|
+
const maxInputs = o.maxInputs ?? MAX_UTXO_INPUTS;
|
|
346
|
+
const utxos = spendableUtxos(o.utxos).slice(0, maxInputs);
|
|
347
|
+
if (utxos.length === 0) return { utxos, fee: 0n, amount: 0n, size: 0 };
|
|
348
|
+
const fee = o.fee ?? conventionalFee(Array.from({ length: utxos.length }, () => P2PKH_INPUT_SIZE), [o.outputSize]);
|
|
349
|
+
const amount = utxos.reduce((s, u) => s + u.value, 0n) - fee;
|
|
350
|
+
return { utxos, fee, amount: amount > 0n ? amount : 0n, size: estimateSize(utxos.length, [o.outputSize]) };
|
|
351
|
+
}
|
|
352
|
+
|
|
354
353
|
// src/tx.ts
|
|
354
|
+
var import_blake2 = require("@noble/hashes/blake2.js");
|
|
355
355
|
var TX_VERSION_V5 = 2147483653;
|
|
356
356
|
var VERSION_GROUP_ID_V5 = 648488714;
|
|
357
357
|
var SIGHASH_ALL = 1;
|
|
@@ -529,9 +529,26 @@ function describeTransaction(tx, spent, ctx) {
|
|
|
529
529
|
const inputs = tx.inputs.map((i, k) => ({ txid: i.txid, vout: i.vout, value: spent[k].value, address: addressFromScript(spent[k].script, ctx.network) }));
|
|
530
530
|
const outputs = [];
|
|
531
531
|
const warnings = /* @__PURE__ */ new Set();
|
|
532
|
+
const scriptOf = (address) => {
|
|
533
|
+
try {
|
|
534
|
+
return (0, import_utils.bytesToHex)(decodeAddress(address, ctx.network).script);
|
|
535
|
+
} catch {
|
|
536
|
+
return void 0;
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
const ownScript = scriptOf(ctx.ownAddress);
|
|
540
|
+
const intendedScript = ctx.intended ? scriptOf(ctx.intended.to) : void 0;
|
|
541
|
+
let recipientSeen = false;
|
|
532
542
|
for (const o of tx.outputs) {
|
|
533
543
|
const address = addressFromScript(o.script, ctx.network);
|
|
534
|
-
const
|
|
544
|
+
const script = (0, import_utils.bytesToHex)(o.script);
|
|
545
|
+
const mine = script === ownScript;
|
|
546
|
+
let role;
|
|
547
|
+
if (ctx.intended === void 0) role = mine ? "change" : "recipient";
|
|
548
|
+
else if (!recipientSeen && script === intendedScript && o.value === ctx.intended.value) {
|
|
549
|
+
role = "recipient";
|
|
550
|
+
recipientSeen = true;
|
|
551
|
+
} else role = mine ? "change" : "unknown";
|
|
535
552
|
if (scriptKind(o.script) !== "unknown" && o.value < DUST_LIMIT) warnings.add("dust-output");
|
|
536
553
|
outputs.push({ address, value: o.value, role });
|
|
537
554
|
}
|
|
@@ -591,6 +608,19 @@ async function fetchWithTimeout(fetchFn, url, init, timeoutMs, provider) {
|
|
|
591
608
|
throw new import_core2.NetworkError(`Request failed: ${url}`, { provider, cause: e });
|
|
592
609
|
}
|
|
593
610
|
}
|
|
611
|
+
var refusedStatus = (status) => status >= 400 && status < 500 && status !== 408;
|
|
612
|
+
function refusal(e) {
|
|
613
|
+
return Object.assign(e, { answered: true });
|
|
614
|
+
}
|
|
615
|
+
function isAnsweredRefusal(e) {
|
|
616
|
+
return e?.answered === true;
|
|
617
|
+
}
|
|
618
|
+
function isAlreadyKnown(e) {
|
|
619
|
+
for (let x = e, depth = 0; x instanceof Error && depth < 5; x = x.cause, depth++) {
|
|
620
|
+
if (/txn-already-(in-mempool|known)|already (known|in (the )?(mempool|block ?chain))/i.test(x.message)) return true;
|
|
621
|
+
}
|
|
622
|
+
return false;
|
|
623
|
+
}
|
|
594
624
|
var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
595
625
|
var isIntString = (v) => typeof v === "string" && /^-?\d+$/.test(v);
|
|
596
626
|
var isInt = (v) => typeof v === "number" && Number.isInteger(v);
|
|
@@ -725,14 +755,17 @@ var BlockchairClient = class {
|
|
|
725
755
|
const url = this.url("push/transaction");
|
|
726
756
|
const res = await fetchWithTimeout(this.fetchFn, url, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body }, this.timeoutMs, this.base);
|
|
727
757
|
let env = {};
|
|
758
|
+
let parsed = false;
|
|
728
759
|
try {
|
|
729
760
|
env = await res.json();
|
|
761
|
+
parsed = true;
|
|
730
762
|
} catch {
|
|
731
763
|
}
|
|
732
764
|
const hash = env.data?.transaction_hash;
|
|
733
765
|
if (hash) return hash;
|
|
734
|
-
const reason = env.context?.error
|
|
735
|
-
throw new Error(`Broadcast rejected: ${reason}`);
|
|
766
|
+
const reason = env.context?.error;
|
|
767
|
+
if (parsed && reason && (res.ok || refusedStatus(res.status))) throw refusal(new Error(`Broadcast rejected: ${reason}`));
|
|
768
|
+
throw new import_core3.NetworkError(`Blockchair broadcast: no answer to rely on (${res.status} ${res.statusText})`, { provider: this.base });
|
|
736
769
|
}
|
|
737
770
|
async getBlockHeight() {
|
|
738
771
|
const { data } = await this.request("stats", {}, void 0, (v) => isRecord(v) && isInt(v.best_block_height));
|
|
@@ -767,7 +800,11 @@ var ZcashBlockbookClient = class _ZcashBlockbookClient {
|
|
|
767
800
|
async get(path, check) {
|
|
768
801
|
const url = `${this.base}/api${path}`;
|
|
769
802
|
const res = await fetchWithTimeout(this.fetchFn, url, { headers: this.headers }, this.timeoutMs, this.base);
|
|
770
|
-
if (!res.ok)
|
|
803
|
+
if (!res.ok) {
|
|
804
|
+
const reason = refusedStatus(res.status) ? (await res.text().catch(() => "")).slice(0, 200) : "";
|
|
805
|
+
const err = new import_core4.NetworkError(`Blockbook request failed: ${res.status} ${res.statusText} (${url})${reason ? `: ${reason}` : ""}`, { provider: this.base });
|
|
806
|
+
throw refusedStatus(res.status) ? refusal(err) : err;
|
|
807
|
+
}
|
|
771
808
|
let body;
|
|
772
809
|
try {
|
|
773
810
|
body = await res.json();
|
|
@@ -817,7 +854,7 @@ var ZcashBlockbookClient = class _ZcashBlockbookClient {
|
|
|
817
854
|
}
|
|
818
855
|
async broadcast(rawTx) {
|
|
819
856
|
const data = await this.get(`/v2/sendtx/${rawTx}`, (v) => isRecord(v));
|
|
820
|
-
if (data.error) throw new Error(`Broadcast rejected: ${typeof data.error === "string" ? data.error : data.error.message ?? "broadcast failed"}`);
|
|
857
|
+
if (data.error) throw refusal(new Error(`Broadcast rejected: ${typeof data.error === "string" ? data.error : data.error.message ?? "broadcast failed"}`));
|
|
821
858
|
if (!data.result) throw new import_core4.NetworkError("Blockbook returned no txid", { provider: this.base });
|
|
822
859
|
return data.result;
|
|
823
860
|
}
|
|
@@ -853,12 +890,19 @@ function createClient(config) {
|
|
|
853
890
|
const attempts = list.length + (config.retries ?? 3);
|
|
854
891
|
const call = (method) => async (...args) => {
|
|
855
892
|
let last;
|
|
893
|
+
let silent = false;
|
|
856
894
|
for (let i = 0; i < attempts; i++) {
|
|
857
895
|
const c = list[i % list.length];
|
|
858
896
|
try {
|
|
859
897
|
return await c[method].apply(c, args);
|
|
860
898
|
} catch (e) {
|
|
861
|
-
if (method === "broadcast"
|
|
899
|
+
if (method === "broadcast") {
|
|
900
|
+
if (isAnsweredRefusal(e)) {
|
|
901
|
+
if (!silent) throw e;
|
|
902
|
+
throw new import_core5.NetworkError("A Zcash client refused the broadcast after another never answered: it may be out", { cause: e });
|
|
903
|
+
}
|
|
904
|
+
silent = true;
|
|
905
|
+
}
|
|
862
906
|
last = e;
|
|
863
907
|
}
|
|
864
908
|
}
|
|
@@ -869,6 +913,19 @@ function createClient(config) {
|
|
|
869
913
|
return out;
|
|
870
914
|
}
|
|
871
915
|
|
|
916
|
+
// src/errors.ts
|
|
917
|
+
var BroadcastUnknownError = class extends Error {
|
|
918
|
+
/** Always true: the transaction may be on its way. */
|
|
919
|
+
maybeSent = true;
|
|
920
|
+
/** The transaction id (hex). */
|
|
921
|
+
hash;
|
|
922
|
+
constructor(hash, cause) {
|
|
923
|
+
super(`Broadcast outcome unknown for ${hash}: ${cause instanceof Error ? cause.message.slice(0, 200) : String(cause).slice(0, 200)}`, { cause });
|
|
924
|
+
this.name = "BroadcastUnknownError";
|
|
925
|
+
this.hash = hash;
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
|
|
872
929
|
// src/message.ts
|
|
873
930
|
var import_sha22 = require("@noble/hashes/sha2.js");
|
|
874
931
|
var import_secp256k1 = require("@noble/curves/secp256k1.js");
|
|
@@ -1078,13 +1135,27 @@ var WalletAccountZcash = class _WalletAccountZcash extends WalletAccountReadOnly
|
|
|
1078
1135
|
ctx(intended) {
|
|
1079
1136
|
return { network: this._network, ownAddress: this.address, intended, maxFee: this.maxFee };
|
|
1080
1137
|
}
|
|
1081
|
-
/**
|
|
1138
|
+
/**
|
|
1139
|
+
* Lock time, expiry and branch id for a transaction that lands in the next block (round 3, L7):
|
|
1140
|
+
* expiry = tip + 1 + `expiryDelta`, the branch id in force at tip + 1 (mainnet table; off mainnet
|
|
1141
|
+
* `consensusBranchId` must be configured — testnet and regtest activate upgrades at other heights).
|
|
1142
|
+
* Without a tip nothing is signed when the expiry, anti-fee-sniping or the branch id need it: a
|
|
1143
|
+
* transaction signed with expiry 0 never expires.
|
|
1144
|
+
*/
|
|
1082
1145
|
async chainParams() {
|
|
1083
|
-
const tip = await this.tipHeight();
|
|
1084
1146
|
const delta = this._config.expiryDelta ?? DEFAULT_EXPIRY_DELTA;
|
|
1085
|
-
const
|
|
1086
|
-
|
|
1087
|
-
|
|
1147
|
+
const configured = this._config.consensusBranchId;
|
|
1148
|
+
if (configured === void 0 && this._network !== "mainnet") {
|
|
1149
|
+
throw new UnsafeTransactionError("unknown-branch-id", `On ${this._network} the consensus branch id depends on that network's upgrade heights; set consensusBranchId in the config.`);
|
|
1150
|
+
}
|
|
1151
|
+
const antiSnipe = this._config.antiFeeSniping !== false;
|
|
1152
|
+
const needTip = delta > 0 || antiSnipe || configured === void 0;
|
|
1153
|
+
const tip = needTip ? await this.tipHeight() : void 0;
|
|
1154
|
+
if (needTip && tip === void 0) throw new import_core7.NetworkError("The Zcash chain tip is unknown, so the expiry height and the branch id cannot be set; nothing was signed.");
|
|
1155
|
+
const next = tip === void 0 ? void 0 : tip + 1;
|
|
1156
|
+
const consensusBranchId = configured ?? branchIdForHeight(next);
|
|
1157
|
+
const lockTime = tip !== void 0 && antiSnipe ? tip : 0;
|
|
1158
|
+
const expiryHeight = next !== void 0 && delta > 0 ? next + delta : 0;
|
|
1088
1159
|
return { lockTime, expiryHeight, consensusBranchId };
|
|
1089
1160
|
}
|
|
1090
1161
|
/** Sign every input through the signer; the scriptSig is `<DER ‖ 0x01> <pubkey>`. */
|
|
@@ -1096,7 +1167,7 @@ var WalletAccountZcash = class _WalletAccountZcash extends WalletAccountReadOnly
|
|
|
1096
1167
|
tx.inputs[i].scriptSig = p2pkhScriptSig(der, this.publicKey);
|
|
1097
1168
|
}
|
|
1098
1169
|
}
|
|
1099
|
-
async
|
|
1170
|
+
async signSpend(spend) {
|
|
1100
1171
|
this.live();
|
|
1101
1172
|
const plan = await this.planSpend(spend);
|
|
1102
1173
|
const params = await this.chainParams();
|
|
@@ -1140,7 +1211,7 @@ var WalletAccountZcash = class _WalletAccountZcash extends WalletAccountReadOnly
|
|
|
1140
1211
|
/** "What you sign": the fully resolved spend (signed, NOT broadcast) or a caller's raw hex. */
|
|
1141
1212
|
async describeTransaction(tx) {
|
|
1142
1213
|
if (typeof tx === "string") return (await this.describeRaw(tx)).desc;
|
|
1143
|
-
return (await this.
|
|
1214
|
+
return (await this.signSpend(tx)).desc;
|
|
1144
1215
|
}
|
|
1145
1216
|
/** 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. */
|
|
1146
1217
|
async signTransaction(tx, options = {}) {
|
|
@@ -1150,7 +1221,7 @@ var WalletAccountZcash = class _WalletAccountZcash extends WalletAccountReadOnly
|
|
|
1150
1221
|
this.checkMax(desc.fee, (0, import_core7.feeLimit)(this._config.transactionMaxFee, options), "transaction");
|
|
1151
1222
|
return tx;
|
|
1152
1223
|
}
|
|
1153
|
-
const built = await this.
|
|
1224
|
+
const built = await this.signSpend(tx);
|
|
1154
1225
|
this.checkMax(built.fee, (0, import_core7.feeLimit)(this._config.transactionMaxFee, options), "transaction");
|
|
1155
1226
|
return built.hex;
|
|
1156
1227
|
}
|
|
@@ -1168,24 +1239,36 @@ var WalletAccountZcash = class _WalletAccountZcash extends WalletAccountReadOnly
|
|
|
1168
1239
|
hash = txid(parsed);
|
|
1169
1240
|
fee = desc.fee;
|
|
1170
1241
|
} else {
|
|
1171
|
-
const built = await this.
|
|
1242
|
+
const built = await this.signSpend(tx);
|
|
1172
1243
|
rawHex = built.hex;
|
|
1173
1244
|
hash = built.txid;
|
|
1174
1245
|
fee = built.fee;
|
|
1175
1246
|
}
|
|
1176
1247
|
this.checkMax(fee, (0, import_core7.feeLimit)(this._config.transactionMaxFee, options), "transaction");
|
|
1177
|
-
|
|
1178
|
-
return { hash: id || hash, fee };
|
|
1248
|
+
return { hash: await this.broadcast(client, rawHex, hash), fee };
|
|
1179
1249
|
}
|
|
1180
1250
|
/** Native ZEC only (`token` is rejected). Subject to `transferMaxFee`. */
|
|
1181
1251
|
async transfer(o, writeOptions = {}) {
|
|
1182
1252
|
if (o.token) throw new Error("Tokens are not supported on the zcash blockchain.");
|
|
1183
1253
|
this.live();
|
|
1184
1254
|
const client = this.requireClient("transfer");
|
|
1185
|
-
const built = await this.
|
|
1255
|
+
const built = await this.signSpend({ to: o.recipient, value: o.amount });
|
|
1186
1256
|
this.checkMax(built.fee, (0, import_core7.feeLimit)(this._config.transferMaxFee, writeOptions), "transfer");
|
|
1187
|
-
|
|
1188
|
-
|
|
1257
|
+
return { hash: await this.broadcast(client, built.hex, built.txid), fee: built.fee };
|
|
1258
|
+
}
|
|
1259
|
+
/**
|
|
1260
|
+
* Send once through the client. Only a node's refusal means nothing went out; "already in the
|
|
1261
|
+
* mempool / block chain" means an earlier attempt delivered it; anything else may have landed and
|
|
1262
|
+
* is a {@link BroadcastUnknownError} with the txid computed before sending (round 3, M12).
|
|
1263
|
+
*/
|
|
1264
|
+
async broadcast(client, rawHex, txid2) {
|
|
1265
|
+
try {
|
|
1266
|
+
return await client.broadcast(rawHex) || txid2;
|
|
1267
|
+
} catch (e) {
|
|
1268
|
+
if (isAlreadyKnown(e)) return txid2;
|
|
1269
|
+
if (isAnsweredRefusal(e)) throw e;
|
|
1270
|
+
throw new BroadcastUnknownError(txid2, e);
|
|
1271
|
+
}
|
|
1189
1272
|
}
|
|
1190
1273
|
/** The same address as a {@link WalletAccountReadOnlyZcash}. */
|
|
1191
1274
|
toReadOnlyAccount() {
|