@arkade-os/sdk 0.5.1 → 0.5.2
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 +9 -5
- package/dist/adapters/expo.cjs +5 -5
- package/dist/adapters/expo.d.cts +2 -2
- package/dist/adapters/expo.d.ts +2 -2
- package/dist/adapters/expo.js +3 -3
- package/dist/{asyncStorageTaskQueue-DFt5LgW5.d.cts → asyncStorageTaskQueue-BtEc3Qca.d.cts} +1 -1
- package/dist/{asyncStorageTaskQueue-CdWheMqT.d.ts → asyncStorageTaskQueue-DIn7bTqN.d.ts} +1 -1
- package/dist/{chunk-QKNSMVBW.cjs → chunk-2OCYL6BJ.cjs} +1142 -843
- package/dist/chunk-2OCYL6BJ.cjs.map +1 -0
- package/dist/{chunk-FXVEOEYB.js → chunk-2QRUQXLS.js} +4 -4
- package/dist/{chunk-FXVEOEYB.js.map → chunk-2QRUQXLS.js.map} +1 -1
- package/dist/{chunk-HQNQ3CLQ.cjs → chunk-64R7CRKN.cjs} +18 -18
- package/dist/{chunk-HQNQ3CLQ.cjs.map → chunk-64R7CRKN.cjs.map} +1 -1
- package/dist/{chunk-NKKJEA3Z.js → chunk-AJWXVQYG.js} +657 -358
- package/dist/chunk-AJWXVQYG.js.map +1 -0
- package/dist/{chunk-XLHUGP3G.cjs → chunk-GGKR4O5X.cjs} +3 -3
- package/dist/{chunk-XLHUGP3G.cjs.map → chunk-GGKR4O5X.cjs.map} +1 -1
- package/dist/{chunk-QFB55EOZ.js → chunk-HKG26NUT.js} +3 -3
- package/dist/{chunk-QFB55EOZ.js.map → chunk-HKG26NUT.js.map} +1 -1
- package/dist/{chunk-3M22PJVB.js → chunk-LWRMSC4M.js} +3 -3
- package/dist/{chunk-3M22PJVB.js.map → chunk-LWRMSC4M.js.map} +1 -1
- package/dist/{chunk-XQRIY5K5.js → chunk-MOBDJ7U2.js} +4 -4
- package/dist/{chunk-XQRIY5K5.js.map → chunk-MOBDJ7U2.js.map} +1 -1
- package/dist/{chunk-GW42FS2X.cjs → chunk-OM6FJ76H.cjs} +48 -48
- package/dist/{chunk-GW42FS2X.cjs.map → chunk-OM6FJ76H.cjs.map} +1 -1
- package/dist/{chunk-JJ6QKTFZ.cjs → chunk-PVFQFQBY.cjs} +10 -10
- package/dist/{chunk-JJ6QKTFZ.cjs.map → chunk-PVFQFQBY.cjs.map} +1 -1
- package/dist/{chunk-GQUFPTN7.cjs → chunk-TAIWH3HG.cjs} +16 -16
- package/dist/{chunk-GQUFPTN7.cjs.map → chunk-TAIWH3HG.cjs.map} +1 -1
- package/dist/{chunk-6MIGYYWB.js → chunk-U5XG7SWA.js} +3 -3
- package/dist/{chunk-6MIGYYWB.js.map → chunk-U5XG7SWA.js.map} +1 -1
- package/dist/contracts/handlers/index.cjs +9 -9
- package/dist/contracts/handlers/index.d.cts +3 -3
- package/dist/contracts/handlers/index.d.ts +3 -3
- package/dist/contracts/handlers/index.js +2 -2
- package/dist/{delegate-CFCfrReG.d.cts → delegate-CaL8EN4Q.d.cts} +1 -1
- package/dist/{delegate-BSbGQabI.d.ts → delegate-Jd4zs88Q.d.ts} +1 -1
- package/dist/{index-Fpf4mQZr.d.cts → index-BwdGzNSn.d.cts} +2 -2
- package/dist/{index-CzUVvIxA.d.ts → index-D-xqVzf8.d.ts} +2 -2
- package/dist/{index-B3fRWO5i.d.cts → index-DdKxfC29.d.cts} +26 -16
- package/dist/{index-B3fRWO5i.d.ts → index-DdKxfC29.d.ts} +26 -16
- package/dist/index.cjs +313 -313
- package/dist/index.d.cts +93 -26
- package/dist/index.d.ts +93 -26
- package/dist/index.js +4 -4
- package/dist/repositories/realm/index.cjs +35 -35
- package/dist/repositories/realm/index.d.cts +2 -2
- package/dist/repositories/realm/index.d.ts +2 -2
- package/dist/repositories/realm/index.js +5 -5
- package/dist/repositories/sqlite/index.cjs +33 -33
- package/dist/repositories/sqlite/index.d.cts +2 -2
- package/dist/repositories/sqlite/index.d.ts +2 -2
- package/dist/repositories/sqlite/index.js +5 -5
- package/dist/{taskRunner-Dk6oDAtd.d.cts → taskRunner-67YSEpsz.d.cts} +1 -1
- package/dist/{taskRunner-CdfoCMaw.d.ts → taskRunner-ZyGGjtIq.d.ts} +1 -1
- package/dist/wallet/expo/background.cjs +14 -14
- package/dist/wallet/expo/background.d.cts +3 -3
- package/dist/wallet/expo/background.d.ts +3 -3
- package/dist/wallet/expo/background.js +6 -6
- package/dist/wallet/expo/index.cjs +15 -15
- package/dist/wallet/expo/index.d.cts +4 -4
- package/dist/wallet/expo/index.d.ts +4 -4
- package/dist/wallet/expo/index.js +5 -5
- package/dist/{wallet-D9dvXu-K.d.cts → wallet-B8LW7cV1.d.cts} +6 -5
- package/dist/{wallet-BQHNgc_v.d.ts → wallet-bFS7VybL.d.ts} +6 -5
- package/dist/worker/expo/index.cjs +9 -9
- package/dist/worker/expo/index.d.cts +4 -4
- package/dist/worker/expo/index.d.ts +4 -4
- package/dist/worker/expo/index.js +5 -5
- package/package.json +1 -1
- package/dist/chunk-NKKJEA3Z.js.map +0 -1
- package/dist/chunk-QKNSMVBW.cjs.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { AssetId, AssetGroup, AssetInput, AssetOutput, Metadata,
|
|
2
|
-
import { witnessRefToBytes, validateProgram, toASM, stringifyArtifact, serializeArkadeContractParams, resolveAsm, parseArtifact, getOpcodeValue, getOpcodeName, fromASM, deserializeArkadeContractParams, computeArkadeScriptPublicKey, bytesToASM, asmToBytes, arkadeWitnessHash, arkadeScriptHash, UnsupportedProgramVersionError, SUPPORTED_PROGRAM_VERSION, OPCODE_VALUES, OPCODE_NAMES, bignum_exports, ArkadeScript, ArkadeProgramScript, ARKADE_OPS, ARKADE_OPCODE_VALUES, ARKADE_OPCODE_NAMES, ARKADE_OPCODES, ARKADE_OP, toXOnly, ARKADE_TYPE_ENCODINGS, validateParamValue, contractHandlers, speculativeReceiveMetadata, isMainnetDescriptor, descriptorIsOurs, deriveDescriptorLeafPubKey, deriveDescriptorLeafCompressedPubKey, normalizeToDescriptor, extractPubKey, parseHDDescriptor, identityDescriptor, DefaultVtxo, DelegateVtxo, WALLET_RECEIVE_SOURCE, BoardingContractHandler } from './chunk-
|
|
3
|
-
import { VtxoScript, __export, Transaction, requiresForfeit, CSVMultisigTapscript, networks, getNetwork, DEFAULT_NETWORK, resolveEmulatorPubkey, timelockToSequence, isVtxoSpent, getNormalizedVtxos, setArkPsbtField, PrevArkTxField, ConditionWitness, craftToSpendTx, RestArkProvider, ArkAddress, isVirtualCoin,
|
|
1
|
+
import { Packet, AssetId, AssetGroup, AssetInput, AssetOutput, Metadata, RestIndexerProvider, BufferReader, BufferWriter, AssetRef } from './chunk-U5XG7SWA.js';
|
|
2
|
+
import { witnessRefToBytes, validateProgram, toASM, stringifyArtifact, serializeArkadeContractParams, resolveAsm, parseArtifact, getOpcodeValue, getOpcodeName, fromASM, deserializeArkadeContractParams, computeArkadeScriptPublicKey, bytesToASM, asmToBytes, arkadeWitnessHash, arkadeScriptHash, UnsupportedProgramVersionError, SUPPORTED_PROGRAM_VERSION, OPCODE_VALUES, OPCODE_NAMES, bignum_exports, ArkadeScript, ArkadeProgramScript, ARKADE_OPS, ARKADE_OPCODE_VALUES, ARKADE_OPCODE_NAMES, ARKADE_OPCODES, ARKADE_OP, toXOnly, ARKADE_TYPE_ENCODINGS, validateParamValue, contractHandlers, speculativeReceiveMetadata, isMainnetDescriptor, descriptorIsOurs, deriveDescriptorLeafPubKey, deriveDescriptorLeafCompressedPubKey, normalizeToDescriptor, extractPubKey, parseHDDescriptor, identityDescriptor, DefaultVtxo, DelegateVtxo, WALLET_RECEIVE_SOURCE, BoardingContractHandler } from './chunk-LWRMSC4M.js';
|
|
3
|
+
import { VtxoScript, __export, Transaction, requiresForfeit, CSVMultisigTapscript, networks, getNetwork, DEFAULT_NETWORK, resolveEmulatorPubkey, timelockToSequence, isVtxoSpent, getNormalizedVtxos, setArkPsbtField, PrevArkTxField, ConditionWitness, getArkPsbtFields, craftToSpendTx, RestArkProvider, ArkAddress, isVirtualCoin, CosignerPublicKey, ServerResponseMismatchError, assertNonNegativeMs, isRetryableProviderError, getAllNormalizedVtxos, DEFAULT_PAGE_SIZE, OP_RETURN_EMPTY_PKSCRIPT, canSweepOnchain, sequenceToTimelock, DEFAULT_ARKADE_SERVER_URL, baseFetch, DEFAULT_NETWORK_NAME, sequenceToCanonicalTimelock, assertTimerMs, isEventSourceError, isEventSourceUnavailableError, normalizeVtxo, isPastExpiry, decodeTapscript, scriptFromTapLeafScript, CLTVMultisigTapscript, VtxoTaprootTree, isArkError, ArkErrorName, assertAllowedSighashTypes, Intent, rateGate, formatSighash, getSequence, maybeArkError, toOffchainInputFeeParams, canRecoverOnchain, MultisigTapscript, DEFAULT_ARKADE_EMULATOR_URL, networkFromArkadeInfo, ArkError, toBIP371TapTree, resolveTimeHeight, canSpendOffchain, INFO_FETCH_TIMEOUT_MS, PrevoutTxField, fetchVtxoCreatedAtByTxid, EXPIRY_MIN_PLAUSIBLE_MS, ProviderUnavailableError } from './chunk-HKG26NUT.js';
|
|
4
4
|
import { sha256, equalBytes, hash160, sha256x2, concatBytes, randomPrivateKeyBytes, pubECDSA, pubSchnorr } from '@scure/btc-signer/utils.js';
|
|
5
5
|
import { SigHash, Script, OP, OutScript, Address, RawWitness, p2tr, p2wpkh, TaprootControlBlock, DEFAULT_SEQUENCE, Transaction as Transaction$1 } from '@scure/btc-signer';
|
|
6
6
|
import { hex, base58, base64, bech32m } from '@scure/base';
|
|
@@ -1328,10 +1328,15 @@ async function provisionDescriptor(wallet) {
|
|
|
1328
1328
|
return allocated ?? await identityDescriptor(wallet.identity);
|
|
1329
1329
|
}
|
|
1330
1330
|
async function provisionRefundKey(wallet) {
|
|
1331
|
-
const
|
|
1331
|
+
const descriptorPromise = identityDescriptor(wallet.identity);
|
|
1332
|
+
const identityPubkeyPromise = wallet.identity.xOnlyPublicKey();
|
|
1333
|
+
void identityPubkeyPromise.catch(() => void 0);
|
|
1334
|
+
const descriptor = await descriptorPromise;
|
|
1332
1335
|
const signer = await contractSigner(wallet, descriptor);
|
|
1333
|
-
const pubkey = await
|
|
1334
|
-
|
|
1336
|
+
const [pubkey, identityPubkey] = await Promise.all([
|
|
1337
|
+
signer.xOnlyPublicKey(),
|
|
1338
|
+
identityPubkeyPromise
|
|
1339
|
+
]);
|
|
1335
1340
|
if (!equalBytes(pubkey, identityPubkey)) {
|
|
1336
1341
|
throw new Error(
|
|
1337
1342
|
"provisionRefundKey: descriptor pubkey does not match wallet identity \u2014 the refund key and the refund address would be on different keys"
|
|
@@ -1347,7 +1352,6 @@ async function provisionClaimSecret(wallet, opts = {}) {
|
|
|
1347
1352
|
}
|
|
1348
1353
|
const descriptor = await provisionDescriptor(wallet);
|
|
1349
1354
|
const signer = await contractSigner(wallet, descriptor);
|
|
1350
|
-
const pubkey = await signer.xOnlyPublicKey();
|
|
1351
1355
|
const claim2 = async () => {
|
|
1352
1356
|
if (opts.preimage) return { preimage: opts.preimage, mustPersistPreimage: true };
|
|
1353
1357
|
if (isPerArtifactDescriptor(descriptor)) {
|
|
@@ -1370,7 +1374,10 @@ async function provisionClaimSecret(wallet, opts = {}) {
|
|
|
1370
1374
|
return { preimage: randomBytes(32), mustPersistPreimage: true };
|
|
1371
1375
|
}
|
|
1372
1376
|
};
|
|
1373
|
-
const { preimage, preimageSalt, mustPersistPreimage } = await
|
|
1377
|
+
const [pubkey, { preimage, preimageSalt, mustPersistPreimage }] = await Promise.all([
|
|
1378
|
+
signer.xOnlyPublicKey(),
|
|
1379
|
+
claim2()
|
|
1380
|
+
]);
|
|
1374
1381
|
return {
|
|
1375
1382
|
descriptor,
|
|
1376
1383
|
pubkey,
|
|
@@ -4676,12 +4683,14 @@ var VtxoManager = class _VtxoManager {
|
|
|
4676
4683
|
* ```
|
|
4677
4684
|
*/
|
|
4678
4685
|
async recoverVtxos(eventCallback) {
|
|
4679
|
-
const allVtxos = await
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4686
|
+
const [allVtxos, now] = await Promise.all([
|
|
4687
|
+
this.wallet.getVtxos({
|
|
4688
|
+
withRecoverable: true,
|
|
4689
|
+
withUnrolled: false
|
|
4690
|
+
}),
|
|
4691
|
+
fetchTimeHeight(this.wallet)
|
|
4692
|
+
]);
|
|
4683
4693
|
const dustAmount = getDustAmount(this.wallet);
|
|
4684
|
-
const now = await fetchTimeHeight(this.wallet);
|
|
4685
4694
|
let { vtxosToRecover } = getRecoverableWithSubdust(allVtxos, dustAmount, now);
|
|
4686
4695
|
if (vtxosToRecover.length === 0) {
|
|
4687
4696
|
throw new Error("No recoverable VTXOs found");
|
|
@@ -4782,12 +4791,17 @@ var VtxoManager = class _VtxoManager {
|
|
|
4782
4791
|
* ```
|
|
4783
4792
|
*/
|
|
4784
4793
|
async getRecoverableBalance() {
|
|
4785
|
-
const
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4794
|
+
const infoProvider = this.getInfoProvider();
|
|
4795
|
+
const [allVtxos, now, info, arkAddress] = await Promise.all([
|
|
4796
|
+
this.wallet.getVtxos({
|
|
4797
|
+
withRecoverable: true,
|
|
4798
|
+
withUnrolled: false
|
|
4799
|
+
}),
|
|
4800
|
+
fetchTimeHeight(this.wallet),
|
|
4801
|
+
infoProvider?.getInfo(),
|
|
4802
|
+
this.wallet.getAddress()
|
|
4803
|
+
]);
|
|
4789
4804
|
const dustAmount = getDustAmount(this.wallet);
|
|
4790
|
-
const now = await fetchTimeHeight(this.wallet);
|
|
4791
4805
|
let { vtxosToRecover, includesSubdust } = getRecoverableWithSubdust(
|
|
4792
4806
|
allVtxos,
|
|
4793
4807
|
dustAmount,
|
|
@@ -4805,14 +4819,9 @@ var VtxoManager = class _VtxoManager {
|
|
|
4805
4819
|
now
|
|
4806
4820
|
));
|
|
4807
4821
|
}
|
|
4808
|
-
const info = await this.getInfoProvider()?.getInfo();
|
|
4809
4822
|
const estimator = new Estimator(info?.fees.intentFee ?? {});
|
|
4810
4823
|
const { payable, net } = priceSettlementInputs(vtxosToRecover, estimator);
|
|
4811
|
-
const priced = deductOffchainOutputFee(
|
|
4812
|
-
subtotalOf(payable, net),
|
|
4813
|
-
estimator,
|
|
4814
|
-
await this.wallet.getAddress()
|
|
4815
|
-
);
|
|
4824
|
+
const priced = deductOffchainOutputFee(subtotalOf(payable, net), estimator, arkAddress);
|
|
4816
4825
|
const recoverable = priced > 0n ? priced : 0n;
|
|
4817
4826
|
const subdustAmount = subtotalOf(
|
|
4818
4827
|
payable.filter((v) => BigInt(v.value) < dustAmount),
|
|
@@ -4866,8 +4875,11 @@ var VtxoManager = class _VtxoManager {
|
|
|
4866
4875
|
const cm = await wallet.getContractManager();
|
|
4867
4876
|
const recoveryScripts = (await cm.getContracts()).filter(isRecoveryOnlyContract).map((contract) => contract.script);
|
|
4868
4877
|
if (recoveryScripts.length === 0) return;
|
|
4869
|
-
const
|
|
4870
|
-
const info = await
|
|
4878
|
+
const infoProvider = this.getInfoProvider();
|
|
4879
|
+
const [recoveryContracts, info] = await Promise.all([
|
|
4880
|
+
cm.getContractsWithVtxos({ script: recoveryScripts }),
|
|
4881
|
+
infoProvider?.getInfo()
|
|
4882
|
+
]);
|
|
4871
4883
|
const dustAmount = getDustAmount(wallet);
|
|
4872
4884
|
for (const { contract, vtxos } of recoveryContracts) {
|
|
4873
4885
|
try {
|
|
@@ -4962,12 +4974,13 @@ var VtxoManager = class _VtxoManager {
|
|
|
4962
4974
|
if (this.settlementConfig === false && thresholdMs === void 0) {
|
|
4963
4975
|
return [];
|
|
4964
4976
|
}
|
|
4965
|
-
const
|
|
4977
|
+
const threshold = thresholdMs !== void 0 ? thresholdMs : this.configuredThresholdMs();
|
|
4978
|
+
const vtxosPromise = this.wallet.getSpendableVtxos({
|
|
4966
4979
|
withRecoverable: true,
|
|
4967
4980
|
genericallySpendableOnly: true
|
|
4968
4981
|
});
|
|
4969
|
-
const threshold = thresholdMs !== void 0 ? thresholdMs : this.configuredThresholdMs();
|
|
4970
4982
|
const tip = now ?? await fetchTimeHeight(this.wallet);
|
|
4983
|
+
const vtxos = await vtxosPromise;
|
|
4971
4984
|
await this.warnHeldExpiring(threshold, tip);
|
|
4972
4985
|
return getExpiringAndRecoverableVtxos(vtxos, threshold, getDustAmount(this.wallet), tip);
|
|
4973
4986
|
}
|
|
@@ -5261,8 +5274,10 @@ var VtxoManager = class _VtxoManager {
|
|
|
5261
5274
|
const wallet = this.requireMigrationCapableWallet();
|
|
5262
5275
|
const info = await wallet.arkProvider.getInfo();
|
|
5263
5276
|
await wallet.settleServerInfoChanges?.();
|
|
5264
|
-
const { reports: vtxoReports } = await
|
|
5265
|
-
|
|
5277
|
+
const [{ reports: vtxoReports }, { reports: boardingReports }] = await Promise.all([
|
|
5278
|
+
this.classifyDeprecatedSignerContracts(info),
|
|
5279
|
+
this.classifyDeprecatedSignerBoarding(info)
|
|
5280
|
+
]);
|
|
5266
5281
|
return mergeSignerReports(vtxoReports, boardingReports);
|
|
5267
5282
|
}
|
|
5268
5283
|
/**
|
|
@@ -5284,8 +5299,10 @@ var VtxoManager = class _VtxoManager {
|
|
|
5284
5299
|
return { rotated: rotatedOnRefresh, expired: [], signers: [] };
|
|
5285
5300
|
}
|
|
5286
5301
|
if (walletClass.status === "UNKNOWN_SIGNER") {
|
|
5287
|
-
const { reports: vtxoReports2 } = await
|
|
5288
|
-
|
|
5302
|
+
const [{ reports: vtxoReports2 }, { reports: boardingReports2 }] = await Promise.all([
|
|
5303
|
+
this.classifyDeprecatedSignerContracts(info),
|
|
5304
|
+
this.classifyDeprecatedSignerBoarding(info)
|
|
5305
|
+
]);
|
|
5289
5306
|
return {
|
|
5290
5307
|
// True only if a second rotation landed inside the drain, leaving us on a signer
|
|
5291
5308
|
// newer than `info` (a drained rotation otherwise classifies CURRENT).
|
|
@@ -5296,16 +5313,13 @@ var VtxoManager = class _VtxoManager {
|
|
|
5296
5313
|
};
|
|
5297
5314
|
}
|
|
5298
5315
|
const rotated = await this.ensureReceiveOnActiveSigner(info) || rotatedOnRefresh;
|
|
5299
|
-
const
|
|
5300
|
-
reports: vtxoReports,
|
|
5301
|
-
migratable:
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
migratable: boardingMigratable,
|
|
5307
|
-
expired: boardingExpired
|
|
5308
|
-
} = await this.classifyDeprecatedSignerBoarding(info);
|
|
5316
|
+
const [
|
|
5317
|
+
{ reports: vtxoReports, migratable: vtxoMigratable, expired: vtxoExpired },
|
|
5318
|
+
{ reports: boardingReports, migratable: boardingMigratable, expired: boardingExpired }
|
|
5319
|
+
] = await Promise.all([
|
|
5320
|
+
this.classifyDeprecatedSignerContracts(info),
|
|
5321
|
+
this.classifyDeprecatedSignerBoarding(info)
|
|
5322
|
+
]);
|
|
5309
5323
|
const reports = mergeSignerReports(vtxoReports, boardingReports);
|
|
5310
5324
|
const expiredRefs = [
|
|
5311
5325
|
...vtxoExpired.map(classifiedToRef),
|
|
@@ -6304,11 +6318,6 @@ async function classifyIntent(intent, vtxosByOutpoint, now) {
|
|
|
6304
6318
|
cancellationReason: "reconciliation: an input was unilaterally exited"
|
|
6305
6319
|
});
|
|
6306
6320
|
}
|
|
6307
|
-
if (intent.state === "waiting_to_submit") {
|
|
6308
|
-
return terminal(intent, "cancelled", now, {
|
|
6309
|
-
cancellationReason: "reconciliation: intent was never submitted before restart"
|
|
6310
|
-
});
|
|
6311
|
-
}
|
|
6312
6321
|
if (intent.validUntil !== void 0 && intent.validUntil < now) {
|
|
6313
6322
|
return terminal(intent, "cancelled", now, {
|
|
6314
6323
|
cancellationReason: "reconciliation: intent expired"
|
|
@@ -6510,9 +6519,16 @@ function chainTxTypeToChainedExit(t) {
|
|
|
6510
6519
|
var isCommitment = (t) => t === "INDEXER_CHAINED_TX_TYPE_COMMITMENT" /* COMMITMENT */ || t === "INDEXER_CHAINED_TX_TYPE_UNSPECIFIED" /* UNSPECIFIED */;
|
|
6511
6520
|
async function buildExitDag(params) {
|
|
6512
6521
|
const byTxid = /* @__PURE__ */ new Map();
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6522
|
+
const chains = await Promise.allSettled(
|
|
6523
|
+
params.vtxos.map(async (vtxo) => ({
|
|
6524
|
+
outpoint: `${vtxo.txid}:${vtxo.vout}`,
|
|
6525
|
+
// The resolver returns the full chain (all pages merged, local-first).
|
|
6526
|
+
chain: await params.chain.getVtxoChain({ txid: vtxo.txid, vout: vtxo.vout })
|
|
6527
|
+
}))
|
|
6528
|
+
);
|
|
6529
|
+
for (const result of chains) {
|
|
6530
|
+
if (result.status === "rejected") throw result.reason;
|
|
6531
|
+
const { outpoint, chain } = result.value;
|
|
6516
6532
|
for (const chainTx of chain) {
|
|
6517
6533
|
const existing = byTxid.get(chainTx.txid);
|
|
6518
6534
|
if (existing) {
|
|
@@ -6545,25 +6561,26 @@ async function buildExitDag(params) {
|
|
|
6545
6561
|
if (order.length !== pending.size) {
|
|
6546
6562
|
throw new Error("inconsistent vtxo chain from indexer (cycle detected)");
|
|
6547
6563
|
}
|
|
6548
|
-
const
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
}
|
|
6564
|
+
const confirmed = await Promise.all(
|
|
6565
|
+
order.map(async (tx) => {
|
|
6566
|
+
try {
|
|
6567
|
+
const status = await params.onchain.getTxStatus(tx.txid);
|
|
6568
|
+
return status.confirmed;
|
|
6569
|
+
} catch {
|
|
6570
|
+
return false;
|
|
6571
|
+
}
|
|
6572
|
+
})
|
|
6573
|
+
);
|
|
6574
|
+
return order.map((tx, i) => {
|
|
6557
6575
|
const expires = Number(tx.expiresAt);
|
|
6558
|
-
|
|
6576
|
+
return {
|
|
6559
6577
|
txid: tx.txid,
|
|
6560
6578
|
type: tx.type,
|
|
6561
6579
|
expiresAt: Number.isFinite(expires) && expires > 0 ? expires : void 0,
|
|
6562
6580
|
forVtxos: [...tx.forVtxos].sort(),
|
|
6563
|
-
confirmed
|
|
6564
|
-
}
|
|
6565
|
-
}
|
|
6566
|
-
return nodes;
|
|
6581
|
+
confirmed: confirmed[i]
|
|
6582
|
+
};
|
|
6583
|
+
});
|
|
6567
6584
|
}
|
|
6568
6585
|
function topoSortByDeps(items, idOf, depsOf) {
|
|
6569
6586
|
const produced = new Set(items.map(idOf));
|
|
@@ -6732,11 +6749,12 @@ var RepositoryExitDataSource = class {
|
|
|
6732
6749
|
return chain;
|
|
6733
6750
|
}
|
|
6734
6751
|
async getVirtualTxs(txids) {
|
|
6752
|
+
const stored = await Promise.all(txids.map((txid) => this.repo.getVirtualTx(txid)));
|
|
6735
6753
|
const out = /* @__PURE__ */ new Map();
|
|
6736
|
-
|
|
6737
|
-
const
|
|
6738
|
-
if (
|
|
6739
|
-
}
|
|
6754
|
+
txids.forEach((txid, i) => {
|
|
6755
|
+
const row = stored[i];
|
|
6756
|
+
if (row?.psbt) out.set(txid, row.psbt);
|
|
6757
|
+
});
|
|
6740
6758
|
return out;
|
|
6741
6759
|
}
|
|
6742
6760
|
};
|
|
@@ -7854,8 +7872,10 @@ var DelegateManagerImpl = class {
|
|
|
7854
7872
|
return { delegated: [], failed: [] };
|
|
7855
7873
|
}
|
|
7856
7874
|
const destinationScript = ArkAddress.decode(destination).pkScript;
|
|
7857
|
-
const arkInfo = await
|
|
7858
|
-
|
|
7875
|
+
const [arkInfo, delegateInfo] = await Promise.all([
|
|
7876
|
+
this.arkInfoProvider.getInfo(),
|
|
7877
|
+
this.delegateProvider.getDelegateInfo()
|
|
7878
|
+
]);
|
|
7859
7879
|
const eligible = vtxos.map(normalizeVtxo).filter(
|
|
7860
7880
|
(v) => isAnnotated(v) && findDelegateTapLeaf(v, delegateInfo.pubkey) !== void 0
|
|
7861
7881
|
);
|
|
@@ -10646,8 +10666,10 @@ var ContractManager = class _ContractManager {
|
|
|
10646
10666
|
return contract !== void 0 && contractHandlers.get(contract.type)?.assertSpendableNow !== void 0;
|
|
10647
10667
|
});
|
|
10648
10668
|
if (asking.length === 0) return refused;
|
|
10649
|
-
const tip = await
|
|
10650
|
-
|
|
10669
|
+
const [tip, walletDescriptorValue] = await Promise.all([
|
|
10670
|
+
this.currentChainTip(),
|
|
10671
|
+
walletDescriptor?.()
|
|
10672
|
+
]);
|
|
10651
10673
|
for (const vtxo of asking) {
|
|
10652
10674
|
const contract = byScript.get(vtxo.script);
|
|
10653
10675
|
const handler = contractHandlers.get(contract.type);
|
|
@@ -12548,7 +12570,10 @@ var WalletReceiveRotator = class _WalletReceiveRotator {
|
|
|
12548
12570
|
* watched; it just stops being advertised). The untagged index-0 baseline is never retired.
|
|
12549
12571
|
*/
|
|
12550
12572
|
async rotate(wallet) {
|
|
12551
|
-
const
|
|
12573
|
+
const descriptorPromise = this.provider.getNextSigningDescriptor();
|
|
12574
|
+
const managerPromise = wallet.getContractManager();
|
|
12575
|
+
void managerPromise.catch(() => void 0);
|
|
12576
|
+
const descriptor = await descriptorPromise;
|
|
12552
12577
|
const { tapscript: newTapscript, params } = buildReceiveContract(
|
|
12553
12578
|
wallet.offchainTapscript,
|
|
12554
12579
|
descriptor,
|
|
@@ -12556,7 +12581,7 @@ var WalletReceiveRotator = class _WalletReceiveRotator {
|
|
|
12556
12581
|
true
|
|
12557
12582
|
);
|
|
12558
12583
|
const newScript = params.script;
|
|
12559
|
-
const manager = await
|
|
12584
|
+
const manager = await managerPromise;
|
|
12560
12585
|
await manager.createContract(params);
|
|
12561
12586
|
wallet.setOffchainTapscriptForRotation(newTapscript);
|
|
12562
12587
|
const previousTagged = this.currentTaggedScript;
|
|
@@ -12666,11 +12691,11 @@ function computeOffchainBalance(vtxos, caps) {
|
|
|
12666
12691
|
let settled = 0;
|
|
12667
12692
|
let preconfirmed = 0;
|
|
12668
12693
|
let available = 0;
|
|
12669
|
-
let
|
|
12694
|
+
let contractLocked = 0;
|
|
12670
12695
|
let pendingSettlement = 0;
|
|
12671
12696
|
let reserved = 0;
|
|
12672
12697
|
let recoverable = 0;
|
|
12673
|
-
let
|
|
12698
|
+
let deprecatedSignerLocked = 0;
|
|
12674
12699
|
let unrolled = 0;
|
|
12675
12700
|
const owned = /* @__PURE__ */ new Map();
|
|
12676
12701
|
const spendable = /* @__PURE__ */ new Map();
|
|
@@ -12687,7 +12712,7 @@ function computeOffchainBalance(vtxos, caps) {
|
|
|
12687
12712
|
continue;
|
|
12688
12713
|
}
|
|
12689
12714
|
if (isPendingRecovery(vtxo)) {
|
|
12690
|
-
|
|
12715
|
+
deprecatedSignerLocked += vtxo.value;
|
|
12691
12716
|
continue;
|
|
12692
12717
|
}
|
|
12693
12718
|
if (canRecoverOnchain(vtxo, now)) {
|
|
@@ -12701,7 +12726,7 @@ function computeOffchainBalance(vtxos, caps) {
|
|
|
12701
12726
|
settled += vtxo.value;
|
|
12702
12727
|
}
|
|
12703
12728
|
if (!isGenericallySpendable(vtxo)) {
|
|
12704
|
-
|
|
12729
|
+
contractLocked += vtxo.value;
|
|
12705
12730
|
} else if (!isUnlocked(vtxo)) {
|
|
12706
12731
|
pendingSettlement += vtxo.value;
|
|
12707
12732
|
} else if (isReserved?.(vtxo)) {
|
|
@@ -12719,13 +12744,13 @@ function computeOffchainBalance(vtxos, caps) {
|
|
|
12719
12744
|
settled,
|
|
12720
12745
|
preconfirmed,
|
|
12721
12746
|
available,
|
|
12722
|
-
|
|
12747
|
+
contractLocked,
|
|
12723
12748
|
pendingSettlement,
|
|
12724
12749
|
reserved,
|
|
12725
12750
|
recoverable,
|
|
12726
|
-
|
|
12751
|
+
deprecatedSignerLocked,
|
|
12727
12752
|
unrolled,
|
|
12728
|
-
total: settled + preconfirmed + recoverable +
|
|
12753
|
+
total: settled + preconfirmed + recoverable + deprecatedSignerLocked + unrolled,
|
|
12729
12754
|
assets: toAssets(owned),
|
|
12730
12755
|
availableAssets: toAssets(spendable)
|
|
12731
12756
|
};
|
|
@@ -12750,15 +12775,19 @@ function toWalletBalance(boardingUtxos, offchain) {
|
|
|
12750
12775
|
settled: offchain.settled,
|
|
12751
12776
|
preconfirmed: offchain.preconfirmed,
|
|
12752
12777
|
available: offchain.available,
|
|
12753
|
-
|
|
12778
|
+
contractLocked: offchain.contractLocked,
|
|
12754
12779
|
pendingSettlement: offchain.pendingSettlement,
|
|
12755
12780
|
reserved: offchain.reserved,
|
|
12756
12781
|
recoverable: offchain.recoverable,
|
|
12757
|
-
|
|
12782
|
+
deprecatedSignerLocked: offchain.deprecatedSignerLocked,
|
|
12758
12783
|
unrolled: offchain.unrolled,
|
|
12759
12784
|
total: totalBoarding + offchain.total,
|
|
12760
12785
|
assets: offchain.assets,
|
|
12761
|
-
availableAssets: offchain.availableAssets
|
|
12786
|
+
availableAssets: offchain.availableAssets,
|
|
12787
|
+
// Deprecated aliases, so code written against older releases still reads a value.
|
|
12788
|
+
gated: offchain.contractLocked,
|
|
12789
|
+
intentLocked: offchain.pendingSettlement,
|
|
12790
|
+
pendingRecovery: offchain.deprecatedSignerLocked
|
|
12762
12791
|
};
|
|
12763
12792
|
}
|
|
12764
12793
|
|
|
@@ -13806,22 +13835,42 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
13806
13835
|
const utxos = [];
|
|
13807
13836
|
const commitmentsToIgnore = /* @__PURE__ */ new Set();
|
|
13808
13837
|
const tapscripts = await this.getBoardingTapscripts(this.watchedBoardingSigners());
|
|
13809
|
-
const
|
|
13810
|
-
const
|
|
13811
|
-
|
|
13812
|
-
|
|
13813
|
-
|
|
13814
|
-
|
|
13815
|
-
|
|
13816
|
-
|
|
13817
|
-
|
|
13818
|
-
|
|
13819
|
-
|
|
13838
|
+
const getOne = this.onchainProvider.getTxOutspend?.bind(this.onchainProvider);
|
|
13839
|
+
const listInflight = /* @__PURE__ */ new Map();
|
|
13840
|
+
const oneInflight = /* @__PURE__ */ new Map();
|
|
13841
|
+
const outspendOf = (txid, index) => {
|
|
13842
|
+
if (getOne) {
|
|
13843
|
+
const key = `${txid}:${index}`;
|
|
13844
|
+
let pending2 = oneInflight.get(key);
|
|
13845
|
+
if (!pending2) {
|
|
13846
|
+
pending2 = getOne(txid, index);
|
|
13847
|
+
oneInflight.set(key, pending2);
|
|
13848
|
+
}
|
|
13849
|
+
return pending2;
|
|
13850
|
+
}
|
|
13851
|
+
let pending = listInflight.get(txid);
|
|
13852
|
+
if (!pending) {
|
|
13853
|
+
pending = this.onchainProvider.getTxOutspends(txid);
|
|
13854
|
+
listInflight.set(txid, pending);
|
|
13855
|
+
}
|
|
13856
|
+
return pending.then((statuses) => statuses[index]);
|
|
13820
13857
|
};
|
|
13821
|
-
|
|
13822
|
-
|
|
13823
|
-
|
|
13824
|
-
|
|
13858
|
+
const historyResults = await Promise.allSettled(
|
|
13859
|
+
tapscripts.map(async (tapscript) => {
|
|
13860
|
+
const boardingAddress = tapscript.onchainAddress(this.network);
|
|
13861
|
+
return {
|
|
13862
|
+
boardingAddress,
|
|
13863
|
+
scriptHex: hex.encode(tapscript.pkScript),
|
|
13864
|
+
txs: await this.onchainProvider.getTransactions(boardingAddress)
|
|
13865
|
+
};
|
|
13866
|
+
})
|
|
13867
|
+
);
|
|
13868
|
+
const histories = historyResults.map((result) => {
|
|
13869
|
+
if (result.status === "rejected") throw result.reason;
|
|
13870
|
+
return result.value;
|
|
13871
|
+
});
|
|
13872
|
+
const outputs = [];
|
|
13873
|
+
for (const { boardingAddress, scriptHex, txs } of histories) {
|
|
13825
13874
|
const commitmentByOutpoint = /* @__PURE__ */ new Map();
|
|
13826
13875
|
for (const tx of txs) {
|
|
13827
13876
|
for (const input of tx.vin ?? []) {
|
|
@@ -13831,37 +13880,51 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
13831
13880
|
for (const tx of txs) {
|
|
13832
13881
|
for (let i = 0; i < tx.vout.length; i++) {
|
|
13833
13882
|
const vout = tx.vout[i];
|
|
13834
|
-
if (vout.scriptpubkey_address
|
|
13835
|
-
|
|
13836
|
-
|
|
13837
|
-
|
|
13838
|
-
|
|
13839
|
-
|
|
13840
|
-
|
|
13841
|
-
|
|
13842
|
-
|
|
13843
|
-
|
|
13844
|
-
|
|
13845
|
-
commitmentTxIds: spent && commitmentTxid ? [commitmentTxid] : []
|
|
13846
|
-
};
|
|
13847
|
-
utxos.push({
|
|
13848
|
-
txid: tx.txid,
|
|
13849
|
-
vout: i,
|
|
13850
|
-
value: Number(vout.value),
|
|
13851
|
-
status: {
|
|
13852
|
-
confirmed: tx.status.confirmed,
|
|
13853
|
-
block_time: tx.status.block_time
|
|
13854
|
-
},
|
|
13855
|
-
isUnrolled: true,
|
|
13856
|
-
...boardingFacts,
|
|
13857
|
-
spentBy: "",
|
|
13858
|
-
createdAt: tx.status.confirmed ? new Date(tx.status.block_time * 1e3) : /* @__PURE__ */ new Date(0),
|
|
13859
|
-
script: scriptHex
|
|
13860
|
-
});
|
|
13861
|
-
}
|
|
13883
|
+
if (vout.scriptpubkey_address !== boardingAddress) continue;
|
|
13884
|
+
outputs.push({
|
|
13885
|
+
txid: tx.txid,
|
|
13886
|
+
vout: i,
|
|
13887
|
+
value: Number(vout.value),
|
|
13888
|
+
confirmed: tx.status.confirmed,
|
|
13889
|
+
blockTime: tx.status.block_time,
|
|
13890
|
+
scriptHex,
|
|
13891
|
+
commitmentFromVin: commitmentByOutpoint.get(`${tx.txid}:${i}`),
|
|
13892
|
+
spentStatus: outspendOf(tx.txid, i)
|
|
13893
|
+
});
|
|
13862
13894
|
}
|
|
13863
13895
|
}
|
|
13864
13896
|
}
|
|
13897
|
+
const spentResults = await Promise.allSettled(outputs.map((output) => output.spentStatus));
|
|
13898
|
+
for (const [i, output] of outputs.entries()) {
|
|
13899
|
+
const spentResult = spentResults[i];
|
|
13900
|
+
if (spentResult.status === "rejected") throw spentResult.reason;
|
|
13901
|
+
const spentStatus = spentResult.value;
|
|
13902
|
+
const commitmentTxid = spentStatus?.txid || output.commitmentFromVin;
|
|
13903
|
+
const spent = Boolean(spentStatus?.spent) || commitmentTxid !== void 0;
|
|
13904
|
+
if (spent && commitmentTxid) {
|
|
13905
|
+
commitmentsToIgnore.add(commitmentTxid);
|
|
13906
|
+
}
|
|
13907
|
+
const boardingFacts = {
|
|
13908
|
+
isSpent: spent,
|
|
13909
|
+
isSwept: false,
|
|
13910
|
+
isPreconfirmed: false,
|
|
13911
|
+
commitmentTxIds: spent && commitmentTxid ? [commitmentTxid] : []
|
|
13912
|
+
};
|
|
13913
|
+
utxos.push({
|
|
13914
|
+
txid: output.txid,
|
|
13915
|
+
vout: output.vout,
|
|
13916
|
+
value: output.value,
|
|
13917
|
+
status: {
|
|
13918
|
+
confirmed: output.confirmed,
|
|
13919
|
+
block_time: output.blockTime
|
|
13920
|
+
},
|
|
13921
|
+
isUnrolled: true,
|
|
13922
|
+
...boardingFacts,
|
|
13923
|
+
spentBy: "",
|
|
13924
|
+
createdAt: output.confirmed ? new Date(output.blockTime * 1e3) : /* @__PURE__ */ new Date(0),
|
|
13925
|
+
script: output.scriptHex
|
|
13926
|
+
});
|
|
13927
|
+
}
|
|
13865
13928
|
const unconfirmedTxs = [];
|
|
13866
13929
|
const confirmedTxs = [];
|
|
13867
13930
|
for (const utxo of utxos) {
|
|
@@ -13953,9 +14016,12 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
13953
14016
|
await this.walletRepository.saveUtxos(
|
|
13954
14017
|
new Map(groups.map((group, i) => [addresses[i], group.coins]))
|
|
13955
14018
|
);
|
|
14019
|
+
const cachedByAddress = await Promise.all(
|
|
14020
|
+
addresses.map((address) => collectUtxos(this.walletRepository, address))
|
|
14021
|
+
);
|
|
13956
14022
|
for (const [i, group] of groups.entries()) {
|
|
13957
14023
|
const fresh = new Set(group.coins.map((coin) => `${coin.txid}:${coin.vout}`));
|
|
13958
|
-
const cached =
|
|
14024
|
+
const cached = cachedByAddress[i];
|
|
13959
14025
|
const kept = cached.filter((coin) => fresh.has(`${coin.txid}:${coin.vout}`));
|
|
13960
14026
|
if (kept.length === cached.length) continue;
|
|
13961
14027
|
await this.walletRepository.deleteUtxos(addresses[i]);
|
|
@@ -13978,14 +14044,20 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
13978
14044
|
return groups.flatMap((g) => g.coins);
|
|
13979
14045
|
}
|
|
13980
14046
|
/**
|
|
13981
|
-
* @internal {@link getBoardingUtxos} minus those an in-flight settle is spending
|
|
13982
|
-
* picks. The getter itself lists them: callers that mirror it
|
|
13983
|
-
* broadcasts, balances) must not see a settle's inputs vanish, and then
|
|
14047
|
+
* @internal {@link getBoardingUtxos} minus those an in-flight settle is spending or an
|
|
14048
|
+
* unresolved intent locks, for SDK picks. The getter itself lists them: callers that mirror it
|
|
14049
|
+
* (the worker's cache and broadcasts, balances) must not see a settle's inputs vanish, and then
|
|
14050
|
+
* stay gone if it fails.
|
|
13984
14051
|
*/
|
|
13985
14052
|
async getSelectableBoardingUtxos() {
|
|
13986
|
-
|
|
13987
|
-
|
|
13988
|
-
|
|
14053
|
+
const [utxos, locked] = await Promise.all([
|
|
14054
|
+
this.getBoardingUtxos(),
|
|
14055
|
+
this.lockedOutpoints()
|
|
14056
|
+
]);
|
|
14057
|
+
return utxos.filter((coin) => {
|
|
14058
|
+
const key = `${coin.txid}:${coin.vout}`;
|
|
14059
|
+
return !this._pendingSpendOutpoints.has(key) && !locked.has(key);
|
|
14060
|
+
});
|
|
13989
14061
|
}
|
|
13990
14062
|
async getStoredBoardingUtxos() {
|
|
13991
14063
|
const currentOnly = /* @__PURE__ */ new Set([
|
|
@@ -14010,6 +14082,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
14010
14082
|
* @returns A function that stops the subscriptions
|
|
14011
14083
|
*/
|
|
14012
14084
|
async notifyIncomingFunds(eventCallback) {
|
|
14085
|
+
const cm = this.indexerProvider ? await this.getContractManager() : void 0;
|
|
14013
14086
|
const arkAddress = await this.getAddress();
|
|
14014
14087
|
let onchainStopFunc;
|
|
14015
14088
|
let indexerStopFunc;
|
|
@@ -14062,8 +14135,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
14062
14135
|
void subscribeOnchain();
|
|
14063
14136
|
});
|
|
14064
14137
|
await subscribeOnchain();
|
|
14065
|
-
if (
|
|
14066
|
-
const cm = await this.getContractManager();
|
|
14138
|
+
if (cm && arkAddress) {
|
|
14067
14139
|
let annotationQueue = Promise.resolve();
|
|
14068
14140
|
indexerStopFunc = cm.onContractEvent((event) => {
|
|
14069
14141
|
if (!isContractVtxoEvent(event)) {
|
|
@@ -14209,21 +14281,25 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
14209
14281
|
extraSources: capture?.sources
|
|
14210
14282
|
});
|
|
14211
14283
|
onVtxosPersisted = async (_contract, vtxos) => {
|
|
14212
|
-
|
|
14213
|
-
|
|
14214
|
-
|
|
14215
|
-
|
|
14216
|
-
|
|
14217
|
-
|
|
14218
|
-
|
|
14219
|
-
|
|
14220
|
-
|
|
14221
|
-
|
|
14222
|
-
|
|
14223
|
-
|
|
14284
|
+
await Promise.all(
|
|
14285
|
+
vtxos.map((v) => {
|
|
14286
|
+
if (v.isSpent) return void 0;
|
|
14287
|
+
return captureExitBranch({
|
|
14288
|
+
resolver,
|
|
14289
|
+
repository: virtualTxRepository,
|
|
14290
|
+
vtxo: { txid: v.txid, vout: v.vout },
|
|
14291
|
+
value: v.value,
|
|
14292
|
+
mode: capture?.mode ?? DEFAULT_EXIT_CAPTURE_MODE,
|
|
14293
|
+
minExitWorthSats: capture?.minExitWorthSats ?? DEFAULT_MIN_EXIT_WORTH_SATS
|
|
14294
|
+
}).catch(() => {
|
|
14295
|
+
});
|
|
14296
|
+
})
|
|
14297
|
+
);
|
|
14224
14298
|
};
|
|
14225
14299
|
onVtxosSpent = (vtxos) => pruneExitBranches(virtualTxRepository, vtxos);
|
|
14226
14300
|
}
|
|
14301
|
+
const baselinePubkeyPromise = this.identity.xOnlyPublicKey();
|
|
14302
|
+
void baselinePubkeyPromise.catch(() => void 0);
|
|
14227
14303
|
const manager = await ContractManager.create({
|
|
14228
14304
|
lazyInitialization: this.lazyInitialization,
|
|
14229
14305
|
indexerProvider: this.indexerProvider,
|
|
@@ -14243,7 +14319,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
14243
14319
|
return { height, time };
|
|
14244
14320
|
}
|
|
14245
14321
|
});
|
|
14246
|
-
const baselinePubkey = await
|
|
14322
|
+
const baselinePubkey = await baselinePubkeyPromise;
|
|
14247
14323
|
const delegatePubKey = this.offchainTapscript instanceof DelegateVtxo.Script ? this.offchainTapscript.options.delegatePubKey : void 0;
|
|
14248
14324
|
const baselineSigners = [
|
|
14249
14325
|
this.offchainTapscript.options.serverPubKey,
|
|
@@ -14846,13 +14922,13 @@ var Wallet2 = class extends ReadonlyWallet {
|
|
|
14846
14922
|
async _doRotateServerSigner(xonly, newServerUnrollScript) {
|
|
14847
14923
|
if (equalBytes(xonly, this.arkServerPublicKey)) return;
|
|
14848
14924
|
const manager = await this.getContractManager();
|
|
14849
|
-
const [currentOffchainRow] = await manager.getContracts({
|
|
14850
|
-
script: this.defaultContractScript
|
|
14851
|
-
});
|
|
14852
14925
|
const currentBoardingScript = hex.encode(this._boardingTapscript.pkScript);
|
|
14853
|
-
const [
|
|
14854
|
-
script:
|
|
14855
|
-
|
|
14926
|
+
const [offchainRows, boardingRows] = await Promise.all([
|
|
14927
|
+
manager.getContracts({ script: this.defaultContractScript }),
|
|
14928
|
+
manager.getContracts({ script: currentBoardingScript })
|
|
14929
|
+
]);
|
|
14930
|
+
const [currentOffchainRow] = offchainRows;
|
|
14931
|
+
const [currentBoardingRow] = boardingRows;
|
|
14856
14932
|
const newOffchain = this.offchainTapscript instanceof DelegateVtxo.Script ? new DelegateVtxo.Script({
|
|
14857
14933
|
...this.offchainTapscript.options,
|
|
14858
14934
|
serverPubKey: xonly
|
|
@@ -15035,13 +15111,15 @@ var Wallet2 = class extends ReadonlyWallet {
|
|
|
15035
15111
|
return this._restoreInFlight;
|
|
15036
15112
|
}
|
|
15037
15113
|
async _runRestore(gapLimit) {
|
|
15038
|
-
const manager = await this.getContractManager();
|
|
15039
15114
|
const provider = this._hdProvider;
|
|
15040
15115
|
const hd = provider !== void 0;
|
|
15041
|
-
const staticDescriptor =
|
|
15116
|
+
const [manager, staticDescriptor, arkInfo] = await Promise.all([
|
|
15117
|
+
this.getContractManager(),
|
|
15118
|
+
hd ? void 0 : identityDescriptor(this.identity),
|
|
15119
|
+
this.arkProvider.getInfo()
|
|
15120
|
+
]);
|
|
15042
15121
|
const materialize = (index) => hd ? provider.materializeDescriptorAt(index) : staticDescriptor;
|
|
15043
15122
|
const delegatePubKey = this.offchainTapscript instanceof DelegateVtxo.Script ? this.offchainTapscript.options.delegatePubKey : void 0;
|
|
15044
|
-
const arkInfo = await this.arkProvider.getInfo();
|
|
15045
15123
|
const currentSignerPubKey = toXOnly(hex.decode(arkInfo.signerPubkey), "ark signer key");
|
|
15046
15124
|
const deprecatedSignerPubKeys = arkInfo.deprecatedSigners.map(
|
|
15047
15125
|
(s) => toXOnly(hex.decode(s.pubkey), "deprecated signer key")
|
|
@@ -15589,25 +15667,33 @@ var Wallet2 = class extends ReadonlyWallet {
|
|
|
15589
15667
|
throw error;
|
|
15590
15668
|
}
|
|
15591
15669
|
const inputIds = params.inputs.map((i) => `${i.txid}:${i.vout}`).join(",");
|
|
15670
|
+
let released = !registering;
|
|
15592
15671
|
if (registering) {
|
|
15593
|
-
|
|
15594
|
-
|
|
15595
|
-
|
|
15596
|
-
|
|
15597
|
-
|
|
15598
|
-
|
|
15599
|
-
|
|
15600
|
-
|
|
15601
|
-
|
|
15602
|
-
|
|
15603
|
-
|
|
15604
|
-
intent,
|
|
15605
|
-
deleteIntent,
|
|
15606
|
-
params.inputs,
|
|
15607
|
-
{
|
|
15608
|
-
cancellationReason: error instanceof Error ? error.message : String(error)
|
|
15672
|
+
try {
|
|
15673
|
+
await this.arkProvider.deleteIntent(deleteIntent);
|
|
15674
|
+
released = true;
|
|
15675
|
+
} catch (e) {
|
|
15676
|
+
if (isNoMatchingIntentsError(e)) released = true;
|
|
15677
|
+
else {
|
|
15678
|
+
console.warn(
|
|
15679
|
+
`Failed to delete intent after settle failure for inputs [${inputIds}]; intent may linger on server and cause 'duplicated input' on next settle`,
|
|
15680
|
+
e
|
|
15681
|
+
);
|
|
15682
|
+
}
|
|
15609
15683
|
}
|
|
15610
|
-
|
|
15684
|
+
}
|
|
15685
|
+
if (released) {
|
|
15686
|
+
await this.persistIntentSnapshot(
|
|
15687
|
+
intentTxId,
|
|
15688
|
+
"cancelled",
|
|
15689
|
+
intent,
|
|
15690
|
+
deleteIntent,
|
|
15691
|
+
params.inputs,
|
|
15692
|
+
{
|
|
15693
|
+
cancellationReason: error instanceof Error ? error.message : String(error)
|
|
15694
|
+
}
|
|
15695
|
+
);
|
|
15696
|
+
}
|
|
15611
15697
|
throw error;
|
|
15612
15698
|
} finally {
|
|
15613
15699
|
this._removePendingSpends(params.inputs);
|
|
@@ -18536,6 +18622,7 @@ var WalletMessageHandler = class {
|
|
|
18536
18622
|
indexerProvider;
|
|
18537
18623
|
walletRepository;
|
|
18538
18624
|
incomingFundsSubscription;
|
|
18625
|
+
incomingFundsRegistrations = /* @__PURE__ */ new WeakMap();
|
|
18539
18626
|
contractEventsSubscription;
|
|
18540
18627
|
onNextTick = [];
|
|
18541
18628
|
channel;
|
|
@@ -18547,6 +18634,11 @@ var WalletMessageHandler = class {
|
|
|
18547
18634
|
// stale, folding requests in would truncate an in-flight settle, and
|
|
18548
18635
|
// sharing with boarding would kill every lazy refresh (see `boardingEpoch`).
|
|
18549
18636
|
incomingFundsEpoch = 0;
|
|
18637
|
+
/**
|
|
18638
|
+
* Supersedes older initializers without touching `incomingFundsEpoch`, so a
|
|
18639
|
+
* re-init that fails before registering leaves the live subscription delivering.
|
|
18640
|
+
*/
|
|
18641
|
+
initEpoch = 0;
|
|
18550
18642
|
contractEventsEpoch = 0;
|
|
18551
18643
|
boardingEpoch = 0;
|
|
18552
18644
|
/** Per-request progress callbacks: ends with the handler, not with a re-init. */
|
|
@@ -18571,6 +18663,7 @@ var WalletMessageHandler = class {
|
|
|
18571
18663
|
}
|
|
18572
18664
|
async stop() {
|
|
18573
18665
|
this.requestEpoch++;
|
|
18666
|
+
this.initEpoch++;
|
|
18574
18667
|
this.incomingFundsEpoch++;
|
|
18575
18668
|
this.contractEventsEpoch++;
|
|
18576
18669
|
this.boardingEpoch++;
|
|
@@ -18611,6 +18704,8 @@ var WalletMessageHandler = class {
|
|
|
18611
18704
|
}
|
|
18612
18705
|
epochOf(scope) {
|
|
18613
18706
|
switch (scope) {
|
|
18707
|
+
case "init":
|
|
18708
|
+
return this.initEpoch;
|
|
18614
18709
|
case "incomingFunds":
|
|
18615
18710
|
return this.incomingFundsEpoch;
|
|
18616
18711
|
case "contractEvents":
|
|
@@ -19367,18 +19462,22 @@ var WalletMessageHandler = class {
|
|
|
19367
19462
|
}
|
|
19368
19463
|
async onWalletInitialized() {
|
|
19369
19464
|
const wallet = this.readonlyWallet;
|
|
19465
|
+
const signingWallet = this.wallet;
|
|
19466
|
+
const lazyBoarding = this.lazyBoardingInit;
|
|
19370
19467
|
if (!wallet || !this.arkProvider || !this.indexerProvider || !this.walletRepository) {
|
|
19371
19468
|
return;
|
|
19372
19469
|
}
|
|
19373
|
-
|
|
19470
|
+
this.initEpoch++;
|
|
19471
|
+
const init = this.newGeneration("init");
|
|
19374
19472
|
await this.ensureContractEventBroadcasting();
|
|
19375
19473
|
if (init.stale) return;
|
|
19376
|
-
await this.refreshCachedData({ lazyBoarding
|
|
19474
|
+
await this.refreshCachedData({ lazyBoarding });
|
|
19377
19475
|
if (init.stale) return;
|
|
19378
|
-
if (
|
|
19476
|
+
if (signingWallet) {
|
|
19379
19477
|
try {
|
|
19380
19478
|
const vtxos = await this.getVtxosFromRepo();
|
|
19381
|
-
|
|
19479
|
+
if (init.stale) return;
|
|
19480
|
+
const { pending, finalized } = await signingWallet.finalizePendingTxs(
|
|
19382
19481
|
vtxos.filter((vtxo) => vtxo.isSpent || vtxo.isPreconfirmed && !vtxo.isSwept)
|
|
19383
19482
|
);
|
|
19384
19483
|
console.info(
|
|
@@ -19389,11 +19488,34 @@ var WalletMessageHandler = class {
|
|
|
19389
19488
|
}
|
|
19390
19489
|
if (init.stale) return;
|
|
19391
19490
|
}
|
|
19392
|
-
if (this.incomingFundsSubscription) this.incomingFundsSubscription();
|
|
19393
|
-
this.incomingFundsSubscription = void 0;
|
|
19394
19491
|
this.incomingFundsEpoch++;
|
|
19395
19492
|
const emitter = this.newEmitter("incomingFunds");
|
|
19493
|
+
const provider = wallet.onchainProvider ?? this;
|
|
19494
|
+
const previousRegistration = this.incomingFundsRegistrations.get(provider) ?? Promise.resolve();
|
|
19495
|
+
const registration = previousRegistration.then(
|
|
19496
|
+
() => this.registerIncomingFunds(wallet, emitter)
|
|
19497
|
+
);
|
|
19498
|
+
this.incomingFundsRegistrations.set(
|
|
19499
|
+
provider,
|
|
19500
|
+
registration.catch((error) => {
|
|
19501
|
+
console.error("Error subscribing to incoming funds:", error);
|
|
19502
|
+
})
|
|
19503
|
+
);
|
|
19504
|
+
if (!lazyBoarding) await registration;
|
|
19505
|
+
if (signingWallet && !emitter.stale) {
|
|
19506
|
+
try {
|
|
19507
|
+
await signingWallet.getVtxoManager();
|
|
19508
|
+
} catch (error) {
|
|
19509
|
+
console.error("Error starting VtxoManager:", error);
|
|
19510
|
+
}
|
|
19511
|
+
}
|
|
19512
|
+
}
|
|
19513
|
+
async registerIncomingFunds(wallet, emitter) {
|
|
19514
|
+
if (emitter.stale) return;
|
|
19515
|
+
if (this.incomingFundsSubscription) this.incomingFundsSubscription();
|
|
19516
|
+
this.incomingFundsSubscription = void 0;
|
|
19396
19517
|
const address = await wallet.getAddress();
|
|
19518
|
+
if (emitter.stale) return;
|
|
19397
19519
|
const unsubscribe = await wallet.notifyIncomingFunds(async (funds) => {
|
|
19398
19520
|
if (emitter.stale) return;
|
|
19399
19521
|
if (funds.type === "vtxo") {
|
|
@@ -19462,13 +19584,6 @@ var WalletMessageHandler = class {
|
|
|
19462
19584
|
});
|
|
19463
19585
|
if (emitter.stale) return unsubscribe();
|
|
19464
19586
|
this.incomingFundsSubscription = unsubscribe;
|
|
19465
|
-
if (this.wallet) {
|
|
19466
|
-
try {
|
|
19467
|
-
await this.wallet.getVtxoManager();
|
|
19468
|
-
} catch (error) {
|
|
19469
|
-
console.error("Error starting VtxoManager:", error);
|
|
19470
|
-
}
|
|
19471
|
-
}
|
|
19472
19587
|
}
|
|
19473
19588
|
/**
|
|
19474
19589
|
* Refresh boarding inputs and transaction history. The virtual outputs come
|
|
@@ -19497,24 +19612,30 @@ var WalletMessageHandler = class {
|
|
|
19497
19612
|
payload: { coins }
|
|
19498
19613
|
})
|
|
19499
19614
|
);
|
|
19500
|
-
await this.
|
|
19615
|
+
await this.storeHistoryCache(await this.loadHistoryCache(), emitter);
|
|
19501
19616
|
})().catch((error) => {
|
|
19502
19617
|
console.warn("[wallet] background refresh failed", error);
|
|
19503
19618
|
});
|
|
19504
19619
|
return;
|
|
19505
19620
|
}
|
|
19621
|
+
const history = this.loadHistoryCache();
|
|
19622
|
+
void history.catch(() => void 0);
|
|
19506
19623
|
await this.refreshBoardingCache();
|
|
19507
19624
|
this.boardingLoaded = true;
|
|
19508
|
-
await this.
|
|
19625
|
+
await this.storeHistoryCache(await history);
|
|
19509
19626
|
}
|
|
19510
|
-
/**
|
|
19511
|
-
async
|
|
19627
|
+
/** History rows plus the address they are stored under. Does not write. */
|
|
19628
|
+
async loadHistoryCache() {
|
|
19512
19629
|
const wallet = this.readonlyWallet;
|
|
19513
|
-
|
|
19514
|
-
if (!wallet || !repository) return;
|
|
19630
|
+
if (!wallet || !this.walletRepository) return void 0;
|
|
19515
19631
|
const address = await wallet.getAddress();
|
|
19516
19632
|
const txs = await this.buildTransactionHistoryFromCache();
|
|
19517
|
-
if (txs
|
|
19633
|
+
if (!txs) return void 0;
|
|
19634
|
+
return { address, txs };
|
|
19635
|
+
}
|
|
19636
|
+
async storeHistoryCache(history, gen) {
|
|
19637
|
+
if (!history || gen?.stale || !this.walletRepository) return;
|
|
19638
|
+
await this.walletRepository.saveTransactions(history.address, history.txs);
|
|
19518
19639
|
}
|
|
19519
19640
|
/**
|
|
19520
19641
|
* @param gen - set by a background caller; each write here is skipped once it goes stale.
|
|
@@ -19529,11 +19650,17 @@ var WalletMessageHandler = class {
|
|
|
19529
19650
|
hex.encode(wallet.boardingTapscript.options.serverPubKey)
|
|
19530
19651
|
);
|
|
19531
19652
|
const groups = await wallet.getBoardingUtxosForSigners(/* @__PURE__ */ new Set([currentSigner]));
|
|
19532
|
-
|
|
19653
|
+
if (gen?.stale) return;
|
|
19654
|
+
const cachedByAddress = await Promise.all(
|
|
19655
|
+
groups.map(
|
|
19656
|
+
({ tapscript }) => collectUtxos(repository, tapscript.onchainAddress(wallet.network))
|
|
19657
|
+
)
|
|
19658
|
+
);
|
|
19659
|
+
for (const [i, { tapscript, coins }] of groups.entries()) {
|
|
19533
19660
|
if (gen?.stale) return;
|
|
19534
19661
|
const address = tapscript.onchainAddress(wallet.network);
|
|
19535
19662
|
const freshKeys = new Set(coins.map((coin) => `${coin.txid}:${coin.vout}`));
|
|
19536
|
-
const cached =
|
|
19663
|
+
const cached = cachedByAddress[i];
|
|
19537
19664
|
const kept = cached.filter((coin) => freshKeys.has(`${coin.txid}:${coin.vout}`));
|
|
19538
19665
|
if (kept.length === cached.length) continue;
|
|
19539
19666
|
if (gen?.stale) return;
|
|
@@ -19631,6 +19758,7 @@ var WalletMessageHandler = class {
|
|
|
19631
19758
|
const wallet = this.wallet ?? this.readonlyWallet;
|
|
19632
19759
|
if (!wallet) return;
|
|
19633
19760
|
this.requestEpoch++;
|
|
19761
|
+
this.initEpoch++;
|
|
19634
19762
|
this.incomingFundsEpoch++;
|
|
19635
19763
|
this.contractEventsEpoch++;
|
|
19636
19764
|
this.boardingEpoch++;
|
|
@@ -19682,7 +19810,9 @@ var WalletMessageHandler = class {
|
|
|
19682
19810
|
* {@link getVtxosFromRepo}).
|
|
19683
19811
|
*/
|
|
19684
19812
|
async repoSnapshot(options = {}) {
|
|
19685
|
-
|
|
19813
|
+
const repository = this.walletRepository;
|
|
19814
|
+
const wallet = this.readonlyWallet;
|
|
19815
|
+
if (!repository || !wallet) return { snapshot: [], vtxos: [] };
|
|
19686
19816
|
const seen = /* @__PURE__ */ new Set();
|
|
19687
19817
|
const allVtxos = [];
|
|
19688
19818
|
const addVtxos = (vtxos) => {
|
|
@@ -19697,19 +19827,18 @@ var WalletMessageHandler = class {
|
|
|
19697
19827
|
}
|
|
19698
19828
|
return fresh;
|
|
19699
19829
|
};
|
|
19700
|
-
const
|
|
19830
|
+
const addressPromise = wallet.getAddress();
|
|
19831
|
+
void addressPromise.catch(() => void 0);
|
|
19832
|
+
const manager = await wallet.getContractManager();
|
|
19701
19833
|
const contracts = await manager.getContracts();
|
|
19702
|
-
const
|
|
19703
|
-
|
|
19704
|
-
|
|
19705
|
-
|
|
19706
|
-
|
|
19707
|
-
|
|
19708
|
-
|
|
19709
|
-
|
|
19710
|
-
});
|
|
19711
|
-
}
|
|
19712
|
-
const walletAddress = await this.readonlyWallet.getAddress();
|
|
19834
|
+
const eligible = contracts.filter(
|
|
19835
|
+
(contract) => options.includeRecoveryOnly || !isRecoveryOnlyContract(contract)
|
|
19836
|
+
);
|
|
19837
|
+
const listsPromise = Promise.all(
|
|
19838
|
+
eligible.map((contract) => getVtxosForContract(repository, contract, options))
|
|
19839
|
+
);
|
|
19840
|
+
void listsPromise.catch(() => void 0);
|
|
19841
|
+
const walletAddress = await addressPromise;
|
|
19713
19842
|
let walletScript;
|
|
19714
19843
|
try {
|
|
19715
19844
|
walletScript = scriptFromArkAddress(walletAddress);
|
|
@@ -19718,13 +19847,26 @@ var WalletMessageHandler = class {
|
|
|
19718
19847
|
`WalletMessageHandler.getVtxosFromRepo: failed to derive script from wallet address ${walletAddress}: ${e instanceof Error ? e.message : String(e)}`
|
|
19719
19848
|
);
|
|
19720
19849
|
}
|
|
19721
|
-
|
|
19722
|
-
|
|
19723
|
-
|
|
19850
|
+
const [lists, primaryVtxos] = await Promise.all([
|
|
19851
|
+
listsPromise,
|
|
19852
|
+
// Routed through the same helper as the contract buckets rather than reading the
|
|
19853
|
+
// repository directly, so this bucket normalizes too. Anything left here
|
|
19854
|
+
// is outside every contract row, so it carries no contract to judge it
|
|
19855
|
+
// by — the wallet's own receive address, which is never gated.
|
|
19856
|
+
getVtxosForContract(
|
|
19857
|
+
repository,
|
|
19724
19858
|
{ script: walletScript, address: walletAddress },
|
|
19725
19859
|
options
|
|
19726
19860
|
)
|
|
19727
|
-
);
|
|
19861
|
+
]);
|
|
19862
|
+
const snapshot = [];
|
|
19863
|
+
for (const [i, contract] of eligible.entries()) {
|
|
19864
|
+
snapshot.push({
|
|
19865
|
+
contract,
|
|
19866
|
+
vtxos: addVtxos(lists[i])
|
|
19867
|
+
});
|
|
19868
|
+
}
|
|
19869
|
+
addVtxos(primaryVtxos);
|
|
19728
19870
|
return { snapshot, vtxos: allVtxos };
|
|
19729
19871
|
}
|
|
19730
19872
|
/**
|
|
@@ -20427,7 +20569,12 @@ var ServiceWorkerReadonlyWallet = class _ServiceWorkerReadonlyWallet {
|
|
|
20427
20569
|
};
|
|
20428
20570
|
try {
|
|
20429
20571
|
const response = await this.sendMessage(message);
|
|
20430
|
-
|
|
20572
|
+
const balance = response.payload;
|
|
20573
|
+
return {
|
|
20574
|
+
...balance,
|
|
20575
|
+
contractLocked: balance.contractLocked ?? balance.gated,
|
|
20576
|
+
deprecatedSignerLocked: balance.deprecatedSignerLocked ?? balance.pendingRecovery
|
|
20577
|
+
};
|
|
20431
20578
|
} catch (error) {
|
|
20432
20579
|
throw new Error(`Failed to get balance: ${error}`);
|
|
20433
20580
|
}
|
|
@@ -22621,8 +22768,12 @@ async function attachPrevoutTxs(tx, source) {
|
|
|
22621
22768
|
}
|
|
22622
22769
|
if (pending.length === 0) return;
|
|
22623
22770
|
const resolved = /* @__PURE__ */ new Map();
|
|
22624
|
-
|
|
22625
|
-
|
|
22771
|
+
const txids = [...new Set(pending.map((p) => p.txid))];
|
|
22772
|
+
const fetched = await Promise.allSettled(txids.map((txid) => source.getRawTransaction(txid)));
|
|
22773
|
+
for (let i = 0; i < fetched.length; i++) {
|
|
22774
|
+
const result = fetched[i];
|
|
22775
|
+
if (result.status === "rejected") throw result.reason;
|
|
22776
|
+
resolved.set(txids[i], result.value);
|
|
22626
22777
|
}
|
|
22627
22778
|
for (const { index, txid } of pending) {
|
|
22628
22779
|
setArkPsbtField(tx, index, PrevoutTxField, resolved.get(txid));
|
|
@@ -23076,40 +23227,53 @@ var Unroll;
|
|
|
23076
23227
|
Unroll2.completeUnroll = completeUnroll;
|
|
23077
23228
|
})(Unroll || (Unroll = {}));
|
|
23078
23229
|
async function prepareUnrollTransaction(wallet, vtxoTxIds, outputAddress) {
|
|
23079
|
-
const chainTip = await
|
|
23080
|
-
|
|
23081
|
-
|
|
23230
|
+
const [chainTip, storedVtxos, walletDescriptor] = await Promise.all([
|
|
23231
|
+
wallet.onchainProvider.getChainTip(),
|
|
23232
|
+
wallet.getVtxos({ withUnrolled: true }),
|
|
23233
|
+
wallet.identity.xOnlyPublicKey().then((key) => hex.encode(key))
|
|
23234
|
+
]);
|
|
23235
|
+
const vtxos = storedVtxos.filter((vtxo) => vtxoTxIds.includes(vtxo.txid));
|
|
23082
23236
|
if (vtxos.length === 0) {
|
|
23083
23237
|
throw new Error("No vtxos to complete unroll");
|
|
23084
23238
|
}
|
|
23085
23239
|
const inputs = [];
|
|
23086
23240
|
let totalAmount = 0n;
|
|
23087
23241
|
const txWeightEstimator = TxWeightEstimator.create();
|
|
23088
|
-
const walletDescriptor = hex.encode(await wallet.identity.xOnlyPublicKey());
|
|
23089
23242
|
for (const vtxo of vtxos) {
|
|
23090
23243
|
if (!canSweepOnchain(vtxo)) {
|
|
23091
23244
|
throw new Error(
|
|
23092
23245
|
`Vtxo ${vtxo.txid}:${vtxo.vout} cannot be swept onchain: it is not fully unrolled (use unroll first), or its exit output was already spent`
|
|
23093
23246
|
);
|
|
23094
23247
|
}
|
|
23095
|
-
|
|
23096
|
-
|
|
23097
|
-
|
|
23098
|
-
|
|
23099
|
-
|
|
23100
|
-
|
|
23101
|
-
|
|
23102
|
-
|
|
23103
|
-
|
|
23104
|
-
|
|
23105
|
-
|
|
23106
|
-
|
|
23107
|
-
|
|
23108
|
-
|
|
23109
|
-
|
|
23110
|
-
|
|
23111
|
-
|
|
23112
|
-
|
|
23248
|
+
}
|
|
23249
|
+
const prepared = await Promise.allSettled(
|
|
23250
|
+
vtxos.map(async (vtxo) => {
|
|
23251
|
+
const [txStatus, resolved] = await Promise.all([
|
|
23252
|
+
wallet.onchainProvider.getTxStatus(vtxo.txid),
|
|
23253
|
+
resolveUnilateralPath({
|
|
23254
|
+
vtxo,
|
|
23255
|
+
scriptHex: hex.encode(VtxoScript.decode(vtxo.tapTree).pkScript),
|
|
23256
|
+
contractRepository: wallet.contractRepository,
|
|
23257
|
+
walletDescriptor,
|
|
23258
|
+
currentTime: Date.now()
|
|
23259
|
+
})
|
|
23260
|
+
]);
|
|
23261
|
+
if (!txStatus.confirmed) {
|
|
23262
|
+
throw new Error(`tx ${vtxo.txid} is not confirmed`);
|
|
23263
|
+
}
|
|
23264
|
+
const spendingLeaf = resolved.selection.leaf;
|
|
23265
|
+
const sequence = resolved.selection.sequence;
|
|
23266
|
+
const timelock = sequenceToTimelock(sequence);
|
|
23267
|
+
const elapsed = timelock.type === "blocks" ? chainTip.height >= txStatus.blockHeight + Number(timelock.value) : chainTip.time >= txStatus.blockTime + Number(timelock.value);
|
|
23268
|
+
if (!elapsed) {
|
|
23269
|
+
throw new Error(`no available exit path found for vtxo ${vtxo.txid}:${vtxo.vout}`);
|
|
23270
|
+
}
|
|
23271
|
+
return { vtxo, spendingLeaf, sequence };
|
|
23272
|
+
})
|
|
23273
|
+
);
|
|
23274
|
+
for (const result of prepared) {
|
|
23275
|
+
if (result.status === "rejected") throw result.reason;
|
|
23276
|
+
const { vtxo, spendingLeaf, sequence } = result.value;
|
|
23113
23277
|
totalAmount += BigInt(vtxo.value);
|
|
23114
23278
|
inputs.push({
|
|
23115
23279
|
txid: vtxo.txid,
|
|
@@ -23267,15 +23431,18 @@ async function resolveFeeRate(opts) {
|
|
|
23267
23431
|
async function selectExitVtxos(opts) {
|
|
23268
23432
|
if (!opts.vtxos) return opts.wallet.getVtxos();
|
|
23269
23433
|
const res = await getNormalizedVtxos(opts.wallet.indexerProvider, { outpoints: opts.vtxos });
|
|
23434
|
+
const scripts = [
|
|
23435
|
+
...new Set(res.vtxos.filter((vtxo) => !vtxo.isSpent).map((vtxo) => vtxo.script))
|
|
23436
|
+
];
|
|
23437
|
+
const contracts = scripts.length === 0 ? [] : await collectContracts(opts.wallet.contractRepository, { script: scripts });
|
|
23438
|
+
const byScript = new Map(contracts.map((contract) => [contract.script, contract]));
|
|
23270
23439
|
const tapTrees = /* @__PURE__ */ new Map();
|
|
23271
23440
|
const out = [];
|
|
23272
23441
|
for (const vtxo of res.vtxos) {
|
|
23273
23442
|
if (vtxo.isSpent) continue;
|
|
23274
23443
|
let tapTree = tapTrees.get(vtxo.script);
|
|
23275
23444
|
if (!tapTree) {
|
|
23276
|
-
const
|
|
23277
|
-
script: vtxo.script
|
|
23278
|
-
});
|
|
23445
|
+
const contract = byScript.get(vtxo.script);
|
|
23279
23446
|
if (!contract) {
|
|
23280
23447
|
throw new Error(
|
|
23281
23448
|
`no contract registered for vtxo script ${vtxo.script} \u2014 register the contract before exiting`
|
|
@@ -23294,6 +23461,10 @@ async function selectExitVtxos(opts) {
|
|
|
23294
23461
|
}
|
|
23295
23462
|
async function computeExitLayout(opts, feeRate) {
|
|
23296
23463
|
const { wallet, onchainWallet } = opts;
|
|
23464
|
+
const coinsPromise = onchainWallet.getCoins();
|
|
23465
|
+
const pubkeyPromise = wallet.identity.xOnlyPublicKey();
|
|
23466
|
+
void coinsPromise.catch(() => void 0);
|
|
23467
|
+
void pubkeyPromise.catch(() => void 0);
|
|
23297
23468
|
const vtxos = await selectExitVtxos(opts);
|
|
23298
23469
|
if (vtxos.length === 0) throw new Error("no vtxos to exit");
|
|
23299
23470
|
const resolver = createExitChainResolver({
|
|
@@ -23331,46 +23502,57 @@ async function computeExitLayout(opts, feeRate) {
|
|
|
23331
23502
|
(s) => s.parent.id,
|
|
23332
23503
|
(s) => txInputTxids(s.parent)
|
|
23333
23504
|
);
|
|
23334
|
-
const walletDescriptor = hex.encode(await
|
|
23505
|
+
const walletDescriptor = hex.encode(await pubkeyPromise);
|
|
23506
|
+
const resolvedPaths = await Promise.allSettled(
|
|
23507
|
+
vtxos.map(async (vtxo) => {
|
|
23508
|
+
try {
|
|
23509
|
+
return {
|
|
23510
|
+
vtxo,
|
|
23511
|
+
resolved: await resolveUnilateralPath({
|
|
23512
|
+
vtxo,
|
|
23513
|
+
scriptHex: hex.encode(VtxoScript.decode(vtxo.tapTree).pkScript),
|
|
23514
|
+
contractRepository: wallet.contractRepository,
|
|
23515
|
+
walletDescriptor,
|
|
23516
|
+
currentTime: Date.now()
|
|
23517
|
+
})
|
|
23518
|
+
};
|
|
23519
|
+
} catch (e) {
|
|
23520
|
+
if (e instanceof ExitPathError) return { vtxo, skipped: e.message };
|
|
23521
|
+
throw e;
|
|
23522
|
+
}
|
|
23523
|
+
})
|
|
23524
|
+
);
|
|
23335
23525
|
const infos = [];
|
|
23336
23526
|
const sweeps = [];
|
|
23337
|
-
for (const
|
|
23527
|
+
for (const result of resolvedPaths) {
|
|
23528
|
+
if (result.status === "rejected") throw result.reason;
|
|
23529
|
+
const { vtxo } = result.value;
|
|
23338
23530
|
const outpoint = `${vtxo.txid}:${vtxo.vout}`;
|
|
23339
|
-
|
|
23340
|
-
|
|
23341
|
-
|
|
23342
|
-
|
|
23343
|
-
|
|
23344
|
-
|
|
23345
|
-
|
|
23531
|
+
if ("skipped" in result.value) {
|
|
23532
|
+
infos.push({ outpoint, value: vtxo.value, skipped: result.value.skipped });
|
|
23533
|
+
continue;
|
|
23534
|
+
}
|
|
23535
|
+
const { resolved } = result.value;
|
|
23536
|
+
const sweepFee = sweepFeeFor(
|
|
23537
|
+
resolved.selection,
|
|
23538
|
+
opts.sweepAddress,
|
|
23539
|
+
wallet.network,
|
|
23540
|
+
feeRate
|
|
23541
|
+
);
|
|
23542
|
+
if (vtxo.value - sweepFee < CHILD_OUTPUT_DUST) {
|
|
23543
|
+
infos.push({
|
|
23544
|
+
outpoint,
|
|
23545
|
+
value: vtxo.value,
|
|
23546
|
+
skipped: `uneconomic: value ${vtxo.value} <= sweep fee + dust`
|
|
23346
23547
|
});
|
|
23347
|
-
|
|
23348
|
-
resolved.selection,
|
|
23349
|
-
opts.sweepAddress,
|
|
23350
|
-
wallet.network,
|
|
23351
|
-
feeRate
|
|
23352
|
-
);
|
|
23353
|
-
if (vtxo.value - sweepFee < CHILD_OUTPUT_DUST) {
|
|
23354
|
-
infos.push({
|
|
23355
|
-
outpoint,
|
|
23356
|
-
value: vtxo.value,
|
|
23357
|
-
skipped: `uneconomic: value ${vtxo.value} <= sweep fee + dust`
|
|
23358
|
-
});
|
|
23359
|
-
continue;
|
|
23360
|
-
}
|
|
23361
|
-
const t = sequenceToTimelock(resolved.selection.sequence);
|
|
23362
|
-
const delay = { type: t.type, value: Number(t.value) };
|
|
23363
|
-
infos.push({ outpoint, value: vtxo.value, sweepFee, path: resolved.label, delay });
|
|
23364
|
-
sweeps.push({ vtxo, resolved, sweepFee, delay });
|
|
23365
|
-
} catch (e) {
|
|
23366
|
-
if (e instanceof ExitPathError) {
|
|
23367
|
-
infos.push({ outpoint, value: vtxo.value, skipped: e.message });
|
|
23368
|
-
continue;
|
|
23369
|
-
}
|
|
23370
|
-
throw e;
|
|
23548
|
+
continue;
|
|
23371
23549
|
}
|
|
23550
|
+
const t = sequenceToTimelock(resolved.selection.sequence);
|
|
23551
|
+
const delay = { type: t.type, value: Number(t.value) };
|
|
23552
|
+
infos.push({ outpoint, value: vtxo.value, sweepFee, path: resolved.label, delay });
|
|
23553
|
+
sweeps.push({ vtxo, resolved, sweepFee, delay });
|
|
23372
23554
|
}
|
|
23373
|
-
const coins = (await
|
|
23555
|
+
const coins = (await coinsPromise).filter((c) => c.status.confirmed);
|
|
23374
23556
|
const balance = coins.reduce((sum, c) => sum + c.value, 0);
|
|
23375
23557
|
const fundingTotal = steps.reduce((sum, s) => sum + s.funding, 0);
|
|
23376
23558
|
const splitterFeeFor = (inputCount) => {
|
|
@@ -24770,6 +24952,13 @@ var Arkade = class _Arkade {
|
|
|
24770
24952
|
}
|
|
24771
24953
|
return new ArkadeContract(this, program, args);
|
|
24772
24954
|
}
|
|
24955
|
+
/**
|
|
24956
|
+
* Start a spend with no function of its own: each `.from(coins, contract.unlock.<name>(...))`
|
|
24957
|
+
* names the function its coins are spent through.
|
|
24958
|
+
*/
|
|
24959
|
+
transaction() {
|
|
24960
|
+
return new ArkadeTransactionBuilder(this);
|
|
24961
|
+
}
|
|
24773
24962
|
};
|
|
24774
24963
|
var ArkadeContract = class _ArkadeContract {
|
|
24775
24964
|
constructor(client, program, args = {}, keys) {
|
|
@@ -24831,7 +25020,23 @@ var ArkadeContract = class _ArkadeContract {
|
|
|
24831
25020
|
get functions() {
|
|
24832
25021
|
const out = /* @__PURE__ */ Object.create(null);
|
|
24833
25022
|
for (const fn of this.compiled) {
|
|
24834
|
-
out[fn.name] = (...callArgs) => new ArkadeTransactionBuilder(this,
|
|
25023
|
+
out[fn.name] = (...callArgs) => new ArkadeTransactionBuilder(this.client, {
|
|
25024
|
+
contract: this,
|
|
25025
|
+
fn,
|
|
25026
|
+
args: bindInputs(fn, callArgs)
|
|
25027
|
+
});
|
|
25028
|
+
}
|
|
25029
|
+
return out;
|
|
25030
|
+
}
|
|
25031
|
+
/** Spending paths bound to their arguments, for `.from(coins, contract.unlock.<name>(...))`. */
|
|
25032
|
+
get unlock() {
|
|
25033
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
25034
|
+
for (const fn of this.compiled) {
|
|
25035
|
+
out[fn.name] = (...callArgs) => ({
|
|
25036
|
+
contract: this,
|
|
25037
|
+
fn,
|
|
25038
|
+
args: bindInputs(fn, callArgs)
|
|
25039
|
+
});
|
|
24835
25040
|
}
|
|
24836
25041
|
return out;
|
|
24837
25042
|
}
|
|
@@ -24949,27 +25154,62 @@ var ArkadeContract = class _ArkadeContract {
|
|
|
24949
25154
|
};
|
|
24950
25155
|
var ArkadeTransactionBuilder = class {
|
|
24951
25156
|
/** @internal */
|
|
24952
|
-
constructor(
|
|
24953
|
-
this.
|
|
24954
|
-
this.
|
|
24955
|
-
this.args = args;
|
|
25157
|
+
constructor(client, unlocker) {
|
|
25158
|
+
this.client = client;
|
|
25159
|
+
this.unlocker = unlocker;
|
|
24956
25160
|
}
|
|
24957
|
-
|
|
24958
|
-
|
|
24959
|
-
args;
|
|
25161
|
+
client;
|
|
25162
|
+
unlocker;
|
|
24960
25163
|
outputs = [];
|
|
24961
25164
|
fundingCoins = [];
|
|
24962
25165
|
assetSpecs = [];
|
|
24963
25166
|
extraPackets = [];
|
|
24964
|
-
|
|
25167
|
+
inputs = [];
|
|
24965
25168
|
changeScript;
|
|
24966
|
-
|
|
24967
|
-
|
|
25169
|
+
/**
|
|
25170
|
+
* Spend coins through this builder's function, or through `unlocker`
|
|
25171
|
+
* (`other.unlock.<name>(...args)`, same client), which `ark.transaction()` requires. Calls add
|
|
25172
|
+
* up; contract coins come first, in call order, then `.fund()` inputs.
|
|
25173
|
+
*
|
|
25174
|
+
* Every coin's script sees the same outputs: "output 0 pays X" passes once for all of them.
|
|
25175
|
+
* For one payment per coin, the contract must check `INSPECTNUMINPUTS` or `PUSHCURRENTINPUTINDEX`.
|
|
25176
|
+
*/
|
|
25177
|
+
from(coins, unlocker = this.unlocker) {
|
|
25178
|
+
const list = Array.isArray(coins) ? coins : [coins];
|
|
25179
|
+
if (list.length === 0) throw new Error("from(): no coins given");
|
|
25180
|
+
if (!unlocker) {
|
|
25181
|
+
throw new Error("from(): name the function to spend through: contract.unlock.<name>()");
|
|
25182
|
+
}
|
|
25183
|
+
const { contract, fn } = unlocker;
|
|
25184
|
+
if (contract.client !== this.client) {
|
|
25185
|
+
throw new Error(`from(): ${fn.name} belongs to a contract on another Arkade client`);
|
|
25186
|
+
}
|
|
25187
|
+
const script = hex.encode(contract.pkScript);
|
|
25188
|
+
const taken = new Set(this.inputs.map(({ coin: c }) => `${c.txid}:${c.vout}`));
|
|
25189
|
+
const now = Date.now();
|
|
25190
|
+
for (const coin of list) {
|
|
25191
|
+
const outpoint = `${coin.txid}:${coin.vout}`;
|
|
25192
|
+
if (coin.script && coin.script !== script) {
|
|
25193
|
+
throw new Error(
|
|
25194
|
+
`from(): coin ${outpoint} is not locked by the contract of ${fn.name}`
|
|
25195
|
+
);
|
|
25196
|
+
}
|
|
25197
|
+
if (taken.has(outpoint))
|
|
25198
|
+
throw new Error(`from(): coin ${outpoint} is already an input`);
|
|
25199
|
+
const reason = unspendable(coin, now);
|
|
25200
|
+
if (reason) {
|
|
25201
|
+
throw new Error(
|
|
25202
|
+
`from(): coin ${outpoint} ${reason}; recover it into a batch instead`
|
|
25203
|
+
);
|
|
25204
|
+
}
|
|
25205
|
+
taken.add(outpoint);
|
|
25206
|
+
}
|
|
25207
|
+
this.inputs.push(...list.map((coin) => ({ coin, unlocker })));
|
|
24968
25208
|
return this;
|
|
24969
25209
|
}
|
|
24970
25210
|
/**
|
|
24971
25211
|
* Add extra inputs the caller funds (e.g. a taker's own coins in a swap).
|
|
24972
|
-
* These
|
|
25212
|
+
* These come after the contract coins and are signed with the client identity.
|
|
24973
25213
|
*/
|
|
24974
25214
|
fund(coins) {
|
|
24975
25215
|
this.fundingCoins.push(...coins);
|
|
@@ -25006,23 +25246,39 @@ var ArkadeTransactionBuilder = class {
|
|
|
25006
25246
|
}
|
|
25007
25247
|
/** Assemble the unsigned ark transaction and its checkpoints. */
|
|
25008
25248
|
async build() {
|
|
25009
|
-
|
|
25010
|
-
|
|
25011
|
-
|
|
25012
|
-
|
|
25249
|
+
const { arkTx, checkpoints } = await this.assemble();
|
|
25250
|
+
return { arkTx, checkpoints };
|
|
25251
|
+
}
|
|
25252
|
+
async assemble() {
|
|
25253
|
+
for (const { fn } of this.unlockers()) {
|
|
25254
|
+
if (fn.def.tapscript.csv) {
|
|
25255
|
+
throw new Error(
|
|
25256
|
+
`${fn.name}: CSV leaves require an aged onchain output; the offchain builder creates fresh checkpoints \u2014 use the wallet's unilateral exit flow`
|
|
25257
|
+
);
|
|
25258
|
+
}
|
|
25013
25259
|
}
|
|
25014
25260
|
if (this.outputs.length === 0) {
|
|
25015
25261
|
throw new Error("ArkadeTransactionBuilder: at least one output is required");
|
|
25016
25262
|
}
|
|
25017
25263
|
const outputsSum = this.outputs.reduce((s, o) => s + (o.amount ?? 0n), 0n);
|
|
25018
|
-
|
|
25019
|
-
|
|
25264
|
+
let inputs = [...this.inputs];
|
|
25265
|
+
if (inputs.length === 0) {
|
|
25266
|
+
if (!this.unlocker) {
|
|
25267
|
+
throw new Error(
|
|
25268
|
+
"ArkadeTransactionBuilder: add coins with .from(coins, contract.unlock.<name>())"
|
|
25269
|
+
);
|
|
25270
|
+
}
|
|
25271
|
+
inputs = [
|
|
25272
|
+
{ coin: await this.selectCoin(this.unlocker, outputsSum), unlocker: this.unlocker }
|
|
25273
|
+
];
|
|
25274
|
+
}
|
|
25275
|
+
const coinsSum = inputs.reduce((s, { coin }) => s + BigInt(coin.value), 0n);
|
|
25020
25276
|
const outputs = [...this.outputs];
|
|
25021
25277
|
const fundingSum = this.fundingCoins.reduce((s, f) => s + BigInt(f.value), 0n);
|
|
25022
|
-
const surplus =
|
|
25278
|
+
const surplus = coinsSum + fundingSum - outputsSum;
|
|
25023
25279
|
if (surplus < 0n) {
|
|
25024
25280
|
throw new Error(
|
|
25025
|
-
`ArkadeTransactionBuilder: insufficient inputs \u2014 outputs ${outputsSum} exceed inputs ${
|
|
25281
|
+
`ArkadeTransactionBuilder: insufficient inputs \u2014 outputs ${outputsSum} exceed inputs ${coinsSum + fundingSum}`
|
|
25026
25282
|
);
|
|
25027
25283
|
}
|
|
25028
25284
|
if (surplus > 0n) {
|
|
@@ -25035,23 +25291,23 @@ var ArkadeTransactionBuilder = class {
|
|
|
25035
25291
|
}
|
|
25036
25292
|
const { arkTx, checkpoints } = buildOffchainTx(
|
|
25037
25293
|
[
|
|
25038
|
-
{
|
|
25294
|
+
...inputs.map(({ coin, unlocker }) => ({
|
|
25039
25295
|
txid: coin.txid,
|
|
25040
25296
|
vout: coin.vout,
|
|
25041
25297
|
value: coin.value,
|
|
25042
|
-
tapLeafScript:
|
|
25043
|
-
tapTree:
|
|
25044
|
-
},
|
|
25298
|
+
tapLeafScript: unlocker.fn.tapLeafScript,
|
|
25299
|
+
tapTree: unlocker.contract.tapTree
|
|
25300
|
+
})),
|
|
25045
25301
|
...this.fundingCoins
|
|
25046
25302
|
],
|
|
25047
25303
|
outputs,
|
|
25048
|
-
this.
|
|
25304
|
+
this.client.checkpoint
|
|
25049
25305
|
);
|
|
25050
|
-
|
|
25051
|
-
setArkPsbtField(arkTx,
|
|
25052
|
-
}
|
|
25053
|
-
if (
|
|
25054
|
-
const indexer = this.
|
|
25306
|
+
inputs.forEach(({ coin }, i) => {
|
|
25307
|
+
if (coin.sourceTx) setArkPsbtField(arkTx, i, PrevArkTxField, coin.sourceTx);
|
|
25308
|
+
});
|
|
25309
|
+
if (inputs.some(({ unlocker }) => unlocker.fn.arkadeScript)) {
|
|
25310
|
+
const indexer = this.client.indexer;
|
|
25055
25311
|
if (!indexer) {
|
|
25056
25312
|
throw new PrevTxUnavailableError(
|
|
25057
25313
|
"covenant spends require an `indexer` on the Arkade client to resolve the previous ark tx of each input"
|
|
@@ -25059,56 +25315,76 @@ var ArkadeTransactionBuilder = class {
|
|
|
25059
25315
|
}
|
|
25060
25316
|
await attachPrevArkTxs(
|
|
25061
25317
|
arkTx,
|
|
25062
|
-
[coin
|
|
25318
|
+
[...inputs.map(({ coin }) => coin), ...this.fundingCoins].map((c) => c.txid),
|
|
25063
25319
|
indexer
|
|
25064
25320
|
);
|
|
25065
25321
|
}
|
|
25066
|
-
|
|
25067
|
-
|
|
25068
|
-
|
|
25069
|
-
|
|
25070
|
-
|
|
25322
|
+
inputs.forEach(({ unlocker }, i) => {
|
|
25323
|
+
const condition = (unlocker.fn.def.tapscript.witness ?? []).map(
|
|
25324
|
+
(w) => witnessBytes(unlocker, w)
|
|
25325
|
+
);
|
|
25326
|
+
if (condition.length === 0) return;
|
|
25327
|
+
setArkPsbtField(arkTx, i, ConditionWitness, condition);
|
|
25328
|
+
setArkPsbtField(checkpoints[i], 0, ConditionWitness, condition);
|
|
25329
|
+
});
|
|
25071
25330
|
const packets = [];
|
|
25072
25331
|
if (this.assetSpecs.length > 0) {
|
|
25073
25332
|
packets.push(this.buildAssetPacket());
|
|
25074
25333
|
}
|
|
25075
|
-
const
|
|
25076
|
-
|
|
25077
|
-
|
|
25078
|
-
|
|
25079
|
-
|
|
25080
|
-
|
|
25081
|
-
|
|
25082
|
-
);
|
|
25334
|
+
const entries = inputs.flatMap(({ unlocker }, vin) => {
|
|
25335
|
+
const { arkadeScript, def } = unlocker.fn;
|
|
25336
|
+
if (!arkadeScript) return [];
|
|
25337
|
+
const stack = (def.arkadeScript?.witness ?? []).map((w) => witnessBytes(unlocker, w));
|
|
25338
|
+
return [{ vin, script: arkadeScript, witness: RawWitness.encode(stack) }];
|
|
25339
|
+
});
|
|
25340
|
+
if (entries.length > 0) {
|
|
25341
|
+
packets.push(EmulatorPacket.create(entries));
|
|
25083
25342
|
}
|
|
25084
25343
|
packets.push(...this.extraPackets);
|
|
25085
25344
|
if (packets.length > 0) {
|
|
25086
25345
|
attachExtension(arkTx, packets);
|
|
25087
25346
|
}
|
|
25088
|
-
return { arkTx, checkpoints };
|
|
25347
|
+
return { arkTx, checkpoints, inputs };
|
|
25089
25348
|
}
|
|
25090
25349
|
/** Build, submit and return the finalized transaction. */
|
|
25091
25350
|
async send() {
|
|
25092
|
-
|
|
25093
|
-
|
|
25094
|
-
)) {
|
|
25095
|
-
|
|
25096
|
-
|
|
25097
|
-
)
|
|
25351
|
+
const client = this.client;
|
|
25352
|
+
const userKey = client.userKey;
|
|
25353
|
+
for (const { contract, fn } of this.unlockers()) {
|
|
25354
|
+
if (fn.def.tapscript.signers.some(
|
|
25355
|
+
(signer) => typeof signer === "object" && "tweak" in signer
|
|
25356
|
+
)) {
|
|
25357
|
+
throw new Error(
|
|
25358
|
+
`${fn.name}: covenant-tweaked leaf signing is not supported by Identity; use a signer that supports the covenant tweak with build()`
|
|
25359
|
+
);
|
|
25360
|
+
}
|
|
25361
|
+
const { serverKey } = contract.keys;
|
|
25362
|
+
if (fn.signerKeys.some(
|
|
25363
|
+
(key) => !equalBytes(key, serverKey) && (!userKey || !equalBytes(key, userKey))
|
|
25364
|
+
)) {
|
|
25365
|
+
throw new Error(
|
|
25366
|
+
`${fn.name}: external leaf signatures are required; use build() and sign the PSBT with all required signers`
|
|
25367
|
+
);
|
|
25368
|
+
}
|
|
25098
25369
|
}
|
|
25099
|
-
const {
|
|
25100
|
-
const
|
|
25101
|
-
|
|
25102
|
-
(
|
|
25103
|
-
)
|
|
25370
|
+
const { arkTx, checkpoints, inputs } = await this.assemble();
|
|
25371
|
+
const userInputs = this.userInputIndexes(inputs);
|
|
25372
|
+
const serverOnly = inputs.find(
|
|
25373
|
+
({ unlocker }, i) => !unlocker.fn.arkadeScript && !userInputs.includes(i)
|
|
25374
|
+
);
|
|
25375
|
+
if (serverOnly) {
|
|
25104
25376
|
throw new Error(
|
|
25105
|
-
`${
|
|
25377
|
+
`${serverOnly.unlocker.fn.name}: a leaf without a covenant needs the user's signature, and this one has only the server's`
|
|
25106
25378
|
);
|
|
25107
25379
|
}
|
|
25108
|
-
|
|
25109
|
-
|
|
25110
|
-
|
|
25111
|
-
|
|
25380
|
+
inputs.forEach(({ coin }, vin) => {
|
|
25381
|
+
if (coin.assets?.length && !this.routesAssetsFrom(vin)) {
|
|
25382
|
+
throw new Error(
|
|
25383
|
+
`coin ${coin.txid}:${coin.vout} carries assets; route them with .withAsset() from input ${vin}`
|
|
25384
|
+
);
|
|
25385
|
+
}
|
|
25386
|
+
});
|
|
25387
|
+
if (inputs.some(({ unlocker }) => unlocker.fn.arkadeScript)) {
|
|
25112
25388
|
if (!client.emulator) {
|
|
25113
25389
|
throw new Error("covenant spends require an `emulator` on the Arkade client");
|
|
25114
25390
|
}
|
|
@@ -25133,9 +25409,14 @@ var ArkadeTransactionBuilder = class {
|
|
|
25133
25409
|
assertSubmittedArkTxid(res, signedArk, "submitTx");
|
|
25134
25410
|
const matched = matchServerCheckpoints(res.signedCheckpointTxs, checkpoints, "submitTx");
|
|
25135
25411
|
const finalCps = await Promise.all(
|
|
25136
|
-
matched.map(
|
|
25137
|
-
|
|
25138
|
-
|
|
25412
|
+
matched.map(async ({ server, local }) => {
|
|
25413
|
+
const signed = await client.identity.sign(server, [0]);
|
|
25414
|
+
const [condition] = getArkPsbtFields(local, 0, ConditionWitness);
|
|
25415
|
+
if (condition && getArkPsbtFields(signed, 0, ConditionWitness).length === 0) {
|
|
25416
|
+
setArkPsbtField(signed, 0, ConditionWitness, condition);
|
|
25417
|
+
}
|
|
25418
|
+
return base64.encode(signed.toPSBT());
|
|
25419
|
+
})
|
|
25139
25420
|
);
|
|
25140
25421
|
await ark.finalizeTx(res.arkTxid, finalCps);
|
|
25141
25422
|
return {
|
|
@@ -25147,31 +25428,44 @@ var ArkadeTransactionBuilder = class {
|
|
|
25147
25428
|
/** Sign the client-owned inputs of the ark tx (no-op when there are none). */
|
|
25148
25429
|
async signArk(arkTx, userInputs) {
|
|
25149
25430
|
if (userInputs.length === 0) return arkTx;
|
|
25150
|
-
const { identity } = this.
|
|
25431
|
+
const { identity } = this.client;
|
|
25151
25432
|
if (!identity) {
|
|
25152
25433
|
throw new Error("this spend requires an `identity` to sign its user/funding inputs");
|
|
25153
25434
|
}
|
|
25154
25435
|
return identity.sign(arkTx, userInputs);
|
|
25155
25436
|
}
|
|
25156
|
-
/**
|
|
25157
|
-
|
|
25158
|
-
|
|
25159
|
-
|
|
25160
|
-
|
|
25161
|
-
|
|
25162
|
-
|
|
25163
|
-
|
|
25164
|
-
|
|
25165
|
-
|
|
25437
|
+
/** The functions this spend goes through: the `.from()` ones, else this builder's own. */
|
|
25438
|
+
unlockers() {
|
|
25439
|
+
if (this.inputs.length > 0) return this.inputs.map((i) => i.unlocker);
|
|
25440
|
+
return this.unlocker ? [this.unlocker] : [];
|
|
25441
|
+
}
|
|
25442
|
+
/** Indexes of inputs the client owns and must sign (contract inputs + funded inputs). */
|
|
25443
|
+
userInputIndexes(inputs) {
|
|
25444
|
+
const userKey = this.client.userKey;
|
|
25445
|
+
const idxs = inputs.flatMap(
|
|
25446
|
+
({ unlocker }, i) => userKey && unlocker.fn.signerKeys.some((k) => equalBytes(k, userKey)) ? [i] : []
|
|
25447
|
+
);
|
|
25448
|
+
for (let i = 0; i < this.fundingCoins.length; i++) idxs.push(inputs.length + i);
|
|
25166
25449
|
return idxs;
|
|
25167
25450
|
}
|
|
25168
|
-
async selectCoin(amount) {
|
|
25169
|
-
const
|
|
25451
|
+
async selectCoin(unlocker, amount) {
|
|
25452
|
+
const now = Date.now();
|
|
25453
|
+
const routed = this.routesAssetsFrom(0);
|
|
25454
|
+
const utxos = (await unlocker.contract.getUtxos()).filter(
|
|
25455
|
+
(u) => !unspendable(u, now) && (routed || !u.assets?.length)
|
|
25456
|
+
);
|
|
25170
25457
|
if (utxos.length === 0) throw new Error("no spendable coins for this contract");
|
|
25171
25458
|
const covering = utxos.filter((u) => BigInt(u.value) >= amount).sort((a, b) => a.value - b.value);
|
|
25172
25459
|
if (covering.length > 0) return covering[0];
|
|
25173
25460
|
return [...utxos].sort((a, b) => b.value - a.value)[0];
|
|
25174
25461
|
}
|
|
25462
|
+
/**
|
|
25463
|
+
* Whether input `vin`'s assets are routed: by a `.withAsset()` input, or a caller's asset packet.
|
|
25464
|
+
* A caller's packet counts for every input, unread; arkd checks what it actually routes.
|
|
25465
|
+
*/
|
|
25466
|
+
routesAssetsFrom(vin) {
|
|
25467
|
+
return this.extraPackets.some((p) => p.type() === Packet.PACKET_TYPE) || this.assetSpecs.some((spec) => spec.inputs.some((i) => i.vin === vin));
|
|
25468
|
+
}
|
|
25175
25469
|
buildAssetPacket() {
|
|
25176
25470
|
const groups = this.assetSpecs.map((s) => {
|
|
25177
25471
|
const id = typeof s.assetId === "string" ? AssetId.fromString(s.assetId) : AssetId.fromBytes(s.assetId);
|
|
@@ -25185,10 +25479,15 @@ var ArkadeTransactionBuilder = class {
|
|
|
25185
25479
|
});
|
|
25186
25480
|
return Packet.create(groups);
|
|
25187
25481
|
}
|
|
25188
|
-
witnessBytes(ref) {
|
|
25189
|
-
return witnessRefToBytes(ref, this.args, this.contract.args);
|
|
25190
|
-
}
|
|
25191
25482
|
};
|
|
25483
|
+
function unspendable(coin, now) {
|
|
25484
|
+
if (coin.isSwept) return "was swept";
|
|
25485
|
+
if (coin.expiresAt && now >= new Date(coin.expiresAt).getTime()) return "has expired";
|
|
25486
|
+
return void 0;
|
|
25487
|
+
}
|
|
25488
|
+
function witnessBytes({ contract, args }, ref) {
|
|
25489
|
+
return witnessRefToBytes(ref, args, contract.args);
|
|
25490
|
+
}
|
|
25192
25491
|
function bindInputs(fn, callArgs) {
|
|
25193
25492
|
const inputs = fn.def.inputs ?? [];
|
|
25194
25493
|
if (callArgs.length !== inputs.length) {
|
|
@@ -25688,5 +25987,5 @@ function createDefaultPaymentRouter(wallet) {
|
|
|
25688
25987
|
}
|
|
25689
25988
|
|
|
25690
25989
|
export { ARKADE_MAGIC, ARKADE_SALTED_PREIMAGE_TAG, ARKADE_SWAP_PREIMAGE_TAG, ASSET_CARRIER_SATS, AbortError, ActivityRegistry, AmbiguousRouteError, ArkNote, Arkade, ArkadeCash, ArkadeCashCreateError, ArkadeCashOperatorMismatchError, AssetManager, BIP21, BIP21Error, BIP322, BLOCKED_UPGRADE_TIMEOUT_MS, Batch, CachingArkProvider, ChainedTxType, ConnectionDisposedError, ContractManager, ContractWatcher, DEFAULT_MESSAGE_TIMEOUTS, DEFAULT_MIN_BATCH_EXPIRY_SECONDS, DatabaseUpgradeBlockedError, DelegateManagerImpl, DelegateNotConfiguredError, DescriptorIdentity, DescriptorSigningProviderMissingError, DustChangeError, ELECTRUM_TCP_HOST, ELECTRUM_WS_URL, ESPLORA_URL, ElectrumOnchainProvider, EmulatorPacket, EsploraProvider, Estimator, Extension, ExtensionNotFoundError, ForeignDescriptorError, HDDescriptorProvider, INTENT_TERMINAL_STATES, InMemoryContractRepository, InMemoryIntentRepository, InMemoryVirtualTxRepository, InMemoryWalletRepository, IndexedDBContractRepository, IndexedDBIntentRepository, IndexedDBVirtualTxRepository, IndexedDBWalletRepository, KeyringDescriptorProvider, MAX_PAGE_SIZE, MAX_USED_SIGNING_DESCRIPTORS_LOOK_AHEAD, MESSAGE_BUS_INITIALIZING, MESSAGE_BUS_NOT_INITIALIZED, MessageBus, MessageBusInitializingError, MessageBusNotInitializedError, MissingSigningDescriptorError, MnemonicIdentity, OnchainWallet, OversizedChangeError, P2A, PaymentRouter, PrevTxUnavailableError, REGTEST_MIN_BATCH_EXPIRY_SECONDS, Ramps, ReadonlyAssetManager, ReadonlyDescriptorIdentity, ReadonlySingleKey, ReadonlyWallet, ReadonlyWalletError, RestDelegateProvider, RestEmulatorProvider, SeedIdentity, SendDeadlineExceededError, ServiceWorkerReadonlyWallet, ServiceWorkerTimeoutError, ServiceWorkerWallet, SingleKey, StaticDescriptorProvider, TxTree, TxType, TxWeightEstimator, UnannotatableInputError, UnilateralExit, UnknownPacket, Unroll, VtxoManager, VtxoReservedError, Wallet2 as Wallet, WalletCannotAllocateAddressError, WalletCannotSignError, WalletMessageHandler, WalletNotInitializedError, WsElectrumChainSource, adoptContractDescriptor, arkAssetRail, arkRail, arkTarget, arkade_exports, assertFinalCommitmentMatchesValidated, assertHistoryPageFilter, assertIntentIdUnique, assertNoAssets, assertNoSelection, assertPageRequest, assertRecipientArkadeAddress, assertSendDeadline, assertSendableAmount, assertSubmittedArkTxid, assertValidBatchExpiry, assertValidServerUnrollScript, assetMintResolver, assetsOf, attachPrevArkTxs, attachPrevoutTxs, awaitTransaction, boardingResolver, btcTarget, buildForfeitTx, buildOffchainTx, buildPreimageMessage, buildSaltedPreimageMessage, captureSendDeadline, checkSaveVtxosForScript, claimWithPreimageIdentity, classifyAgainstSignerSet, classifyContractSigner, closeDatabase, collabExitResolver, collectContracts, collectIntents, collectPages, collectScriptVtxos, collectTransactionHistory, collectUtxos, collectVtxos, combineTapscriptSigs, compareHistoryCursors, contractFilterMatchesNothing, contractFromArkContract, contractFromArkContractWithAddress, contractPreimage, contractSigner, createAssetPacket, createDefaultActivityRegistry, createDefaultPaymentRouter, createExitChainResolver, createManagedConnection, createdAtToIso, decodeArkContract, defaultBatchExpiryPolicy, deleteByIndex, deserializeAssets, deserializeExitPackage, deserializeUtxo, deserializeVtxo, encodeArkContract, exitObserverFor, extendVirtualCoinForContract, extractArkProviderUrl, gateExclusion, gatedContracts, getAllByIndexValues, getRandomId, hasBoardingTxExpired, hasCandidates, intentMatchesFilter, invoiceTarget, isAddressAllocationCapable, isArkContract, isBatchSignable, isBtcAddress, isContractGenericallySpendable, isContractVtxoEvent, isCooperativelyMigratable, isDeterministicSigner, isDiscoverable, isHDAllocationCapable, isHDDeterministicSignCapable, isHDWalletCapable, isLightningInvoice, isLnurl, isMessageBusInitializingError, isMessageBusNotInitializedError, isPerArtifactDescriptor, isSigningIdentity, isSubdust, isTerminalIntentState, isValidArkAddress, isVtxoExpiringSoon, iteratePages, legacyVtxoFacts, logExcludedVtxos, makeHandle, matchServerCheckpoints, mergeChainedTxType, notifyExitObserved, onchainRail, openDatabase, outpointExclusion, outpointReasons, pageResult, promisifyRequest, provisionClaimSecret, provisionRefundKey, registerWalletRestoreHook, resolveAssetAmount, resolveBatchExpiryPolicy, resolveDescriptorSigner, resolvePrevTxs, resolveSendAmount, saveVtxosForContract, scriptFromArkAddress, selectCoinsWithAsset, selectVirtualCoins, selectionOf, serializeAssets, serializeExitPackage, serializeUtxo, serializeVtxo, setupServiceWorker, signAndSubmitOffchainTx, signerSetFromInfo, signingDescriptorIndex, strictSigningDescriptorIndex, submitViaEmulator, toXOnlySignerHex, tryResolveSendAmount, utxoEntries, validateBatchRecipients, validateConnectorsTxGraph, validateVtxoTxGraph, verifyTapscriptSignatures, waitForIncomingFunds, walletFeeSource, warnAndFilterVtxosForScript, withPrevTxs };
|
|
25691
|
-
//# sourceMappingURL=chunk-
|
|
25692
|
-
//# sourceMappingURL=chunk-
|
|
25990
|
+
//# sourceMappingURL=chunk-AJWXVQYG.js.map
|
|
25991
|
+
//# sourceMappingURL=chunk-AJWXVQYG.js.map
|