@arkade-os/sdk 0.5.1 → 0.5.3
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-CdWheMqT.d.ts → asyncStorageTaskQueue-B3U5gQ-i.d.ts} +1 -1
- package/dist/{asyncStorageTaskQueue-DFt5LgW5.d.cts → asyncStorageTaskQueue-C9PkPcW_.d.cts} +1 -1
- package/dist/{chunk-JJ6QKTFZ.cjs → chunk-5WRA66RV.cjs} +10 -10
- package/dist/{chunk-JJ6QKTFZ.cjs.map → chunk-5WRA66RV.cjs.map} +1 -1
- package/dist/{chunk-6MIGYYWB.js → chunk-6FL4N4S2.js} +3 -3
- package/dist/{chunk-6MIGYYWB.js.map → chunk-6FL4N4S2.js.map} +1 -1
- package/dist/{chunk-GW42FS2X.cjs → chunk-BYPBVHVA.cjs} +48 -48
- package/dist/{chunk-GW42FS2X.cjs.map → chunk-BYPBVHVA.cjs.map} +1 -1
- package/dist/{chunk-NKKJEA3Z.js → chunk-HAQAWBYG.js} +739 -392
- package/dist/chunk-HAQAWBYG.js.map +1 -0
- package/dist/{chunk-XLHUGP3G.cjs → chunk-KW7ZEWYS.cjs} +6 -3
- package/dist/chunk-KW7ZEWYS.cjs.map +1 -0
- package/dist/{chunk-QFB55EOZ.js → chunk-OPX2HRWM.js} +6 -3
- package/dist/chunk-OPX2HRWM.js.map +1 -0
- package/dist/{chunk-QKNSMVBW.cjs → chunk-OUTWUYJ4.cjs} +1222 -875
- package/dist/chunk-OUTWUYJ4.cjs.map +1 -0
- package/dist/{chunk-3M22PJVB.js → chunk-P7B4LQ3H.js} +3 -3
- package/dist/{chunk-3M22PJVB.js.map → chunk-P7B4LQ3H.js.map} +1 -1
- package/dist/{chunk-FXVEOEYB.js → chunk-T7C3E2HL.js} +4 -4
- package/dist/{chunk-FXVEOEYB.js.map → chunk-T7C3E2HL.js.map} +1 -1
- package/dist/{chunk-XQRIY5K5.js → chunk-Y4BL3YYX.js} +4 -4
- package/dist/{chunk-XQRIY5K5.js.map → chunk-Y4BL3YYX.js.map} +1 -1
- package/dist/{chunk-HQNQ3CLQ.cjs → chunk-ZMSM3NOH.cjs} +18 -18
- package/dist/{chunk-HQNQ3CLQ.cjs.map → chunk-ZMSM3NOH.cjs.map} +1 -1
- package/dist/{chunk-GQUFPTN7.cjs → chunk-ZPF4I3K2.cjs} +16 -16
- package/dist/{chunk-GQUFPTN7.cjs.map → chunk-ZPF4I3K2.cjs.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-ByfKszb6.d.cts} +1 -1
- package/dist/{delegate-BSbGQabI.d.ts → delegate-Cd28gxpM.d.ts} +1 -1
- package/dist/{index-CzUVvIxA.d.ts → index-B6X2E7W_.d.ts} +2 -2
- package/dist/{index-B3fRWO5i.d.cts → index-BURzbRup.d.cts} +38 -16
- package/dist/{index-B3fRWO5i.d.ts → index-BURzbRup.d.ts} +38 -16
- package/dist/{index-Fpf4mQZr.d.cts → index-DgH_0j14.d.cts} +2 -2
- package/dist/index.cjs +313 -313
- package/dist/index.d.cts +107 -27
- package/dist/index.d.ts +107 -27
- 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-CdfoCMaw.d.ts → taskRunner-BaNCSxqK.d.ts} +1 -1
- package/dist/{taskRunner-Dk6oDAtd.d.cts → taskRunner-i56-drQV.d.cts} +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 +18 -15
- package/dist/wallet/expo/index.cjs.map +1 -1
- package/dist/wallet/expo/index.d.cts +5 -4
- package/dist/wallet/expo/index.d.ts +5 -4
- package/dist/wallet/expo/index.js +8 -5
- package/dist/wallet/expo/index.js.map +1 -1
- package/dist/{wallet-D9dvXu-K.d.cts → wallet-BjQugVUt.d.cts} +11 -6
- package/dist/{wallet-BQHNgc_v.d.ts → wallet-BnmhohgZ.d.ts} +11 -6
- 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-QFB55EOZ.js.map +0 -1
- package/dist/chunk-QKNSMVBW.cjs.map +0 -1
- package/dist/chunk-XLHUGP3G.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, getArkPsbtFields, 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,
|
|
1
|
+
import { Packet, AssetId, AssetGroup, AssetInput, AssetOutput, Metadata, RestIndexerProvider, BufferReader, BufferWriter, AssetRef } from './chunk-6FL4N4S2.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-P7B4LQ3H.js';
|
|
3
|
+
import { VtxoScript, __export, Transaction, requiresForfeit, CSVMultisigTapscript, networks, getNetwork, DEFAULT_NETWORK, resolveEmulatorPubkey, timelockToSequence, isVtxoSpent, getNormalizedVtxos, setArkPsbtField, PrevArkTxField, ConditionWitness, craftToSpendTx, RestArkProvider, ArkAddress, isVirtualCoin, getArkPsbtFields, CosignerPublicKey, ServerResponseMismatchError, assertNonNegativeMs, isRetryableProviderError, getAllNormalizedVtxos, DEFAULT_PAGE_SIZE, assertAllowedSighashTypes, OP_RETURN_EMPTY_PKSCRIPT, canSweepOnchain, sequenceToTimelock, DEFAULT_ARKADE_SERVER_URL, baseFetch, DEFAULT_NETWORK_NAME, sequenceToCanonicalTimelock, assertTimerMs, isEventSourceError, isEventSourceUnavailableError, normalizeVtxo, isPastExpiry, decodeTapscript, scriptFromTapLeafScript, CLTVMultisigTapscript, VtxoTaprootTree, formatSighash, isArkError, ArkErrorName, Intent, rateGate, 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-OPX2HRWM.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,
|
|
@@ -3826,32 +3833,36 @@ function assertCheckpointsMatchInputs(checkpoints, inputs, unrollCandidates, con
|
|
|
3826
3833
|
}
|
|
3827
3834
|
}
|
|
3828
3835
|
async function submitOffchainTx(provider, offchainTx, signer, hooks, options) {
|
|
3829
|
-
const
|
|
3836
|
+
const oneShot = options?.oneShot !== false;
|
|
3837
|
+
const { arkTx: signedArkTx, userSignedCheckpoints: batchSigned } = await signer.signArkTx(
|
|
3830
3838
|
offchainTx.arkTx,
|
|
3831
3839
|
offchainTx.checkpoints
|
|
3832
3840
|
);
|
|
3833
|
-
if (
|
|
3841
|
+
if (batchSigned && batchSigned.length !== offchainTx.checkpoints.length) {
|
|
3834
3842
|
throw new Error(
|
|
3835
|
-
`signer returned ${
|
|
3843
|
+
`signer returned ${batchSigned.length} signed checkpoints, expected ${offchainTx.checkpoints.length}`
|
|
3836
3844
|
);
|
|
3837
3845
|
}
|
|
3846
|
+
const userSignedCheckpoints = (batchSigned ?? (oneShot ? await Promise.all(offchainTx.checkpoints.map((c) => signer.signCheckpoint(c.clone()))) : void 0))?.map((c) => withArkInputWitness(c, signedArkTx));
|
|
3838
3847
|
await hooks?.beforeSubmit?.();
|
|
3839
3848
|
const response = await provider.submitTx(
|
|
3840
3849
|
base64.encode(signedArkTx.toPSBT()),
|
|
3841
|
-
offchainTx.checkpoints.map((c) => base64.encode(c.toPSBT()))
|
|
3850
|
+
(oneShot ? userSignedCheckpoints : offchainTx.checkpoints.map((c) => withArkInputWitness(c, signedArkTx))).map((c) => base64.encode(c.toPSBT()))
|
|
3842
3851
|
);
|
|
3843
3852
|
assertSubmittedArkTxid(response, signedArkTx, "submitTx");
|
|
3844
3853
|
const { arkTxid, signedCheckpointTxs } = response;
|
|
3845
3854
|
const matched = matchServerCheckpoints(signedCheckpointTxs, offchainTx.checkpoints, "submitTx");
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
+
if (!oneShot) {
|
|
3856
|
+
matched.forEach(({ server }, index) => {
|
|
3857
|
+
try {
|
|
3858
|
+
assertAllowedSighashTypes(server);
|
|
3859
|
+
} catch (error) {
|
|
3860
|
+
throw new ServerResponseMismatchError(
|
|
3861
|
+
`submitTx checkpoint ${index}: ${error instanceof Error ? error.message : String(error)}`
|
|
3862
|
+
);
|
|
3863
|
+
}
|
|
3864
|
+
});
|
|
3865
|
+
}
|
|
3855
3866
|
const verify = options?.verifyServerSignatures;
|
|
3856
3867
|
if (verify) {
|
|
3857
3868
|
if (response.finalArkTx === void 0) {
|
|
@@ -3878,26 +3889,62 @@ async function submitOffchainTx(provider, offchainTx, signer, hooks, options) {
|
|
|
3878
3889
|
({ server, local }, index) => assertServerSignedLeaf(server, local, 0, serverPubkeys, `submitTx checkpoint ${index}`)
|
|
3879
3890
|
);
|
|
3880
3891
|
}
|
|
3892
|
+
const result = { arkTxid, finalArkTx: response.finalArkTx, signedCheckpointTxs };
|
|
3881
3893
|
let finalCheckpoints;
|
|
3882
3894
|
if (userSignedCheckpoints) {
|
|
3883
3895
|
const userByTxid = new Map(
|
|
3884
3896
|
userSignedCheckpoints.map((c, i) => [offchainTx.checkpoints[i].id, c])
|
|
3885
3897
|
);
|
|
3898
|
+
if (oneShot && matched.every(
|
|
3899
|
+
({ server, local }) => carriesTapscriptSigs(server, userByTxid.get(local.id))
|
|
3900
|
+
)) {
|
|
3901
|
+
await hooks?.afterFinalize?.();
|
|
3902
|
+
return result;
|
|
3903
|
+
}
|
|
3886
3904
|
finalCheckpoints = matched.map(({ server, local }) => {
|
|
3887
|
-
|
|
3905
|
+
const userSigned = userByTxid.get(local.id);
|
|
3906
|
+
combineTapscriptSigs(userSigned, server);
|
|
3907
|
+
carryConditionWitness(userSigned, server);
|
|
3888
3908
|
return base64.encode(server.toPSBT());
|
|
3889
3909
|
});
|
|
3890
3910
|
} else {
|
|
3891
3911
|
finalCheckpoints = await Promise.all(
|
|
3892
|
-
matched.map(async ({ server }) => {
|
|
3912
|
+
matched.map(async ({ server, local }) => {
|
|
3893
3913
|
const signed = await signer.signCheckpoint(server);
|
|
3914
|
+
carryConditionWitness(local, signed);
|
|
3894
3915
|
return base64.encode(signed.toPSBT());
|
|
3895
3916
|
})
|
|
3896
3917
|
);
|
|
3897
3918
|
}
|
|
3898
3919
|
await provider.finalizeTx(arkTxid, finalCheckpoints);
|
|
3899
3920
|
await hooks?.afterFinalize?.();
|
|
3900
|
-
return
|
|
3921
|
+
return result;
|
|
3922
|
+
}
|
|
3923
|
+
function carryConditionWitness(ours, server) {
|
|
3924
|
+
if (getArkPsbtFields(server, 0, ConditionWitness).length > 0) return;
|
|
3925
|
+
const [witness] = getArkPsbtFields(ours, 0, ConditionWitness);
|
|
3926
|
+
if (witness) setArkPsbtField(server, 0, ConditionWitness, witness);
|
|
3927
|
+
}
|
|
3928
|
+
function withArkInputWitness(checkpoint, signedArkTx) {
|
|
3929
|
+
if (getArkPsbtFields(checkpoint, 0, ConditionWitness).length > 0) return checkpoint;
|
|
3930
|
+
for (let i = 0; i < signedArkTx.inputsLength; i++) {
|
|
3931
|
+
const { txid } = signedArkTx.getInput(i);
|
|
3932
|
+
if (!txid || hex.encode(txid) !== checkpoint.id) continue;
|
|
3933
|
+
const [witness] = getArkPsbtFields(signedArkTx, i, ConditionWitness);
|
|
3934
|
+
if (!witness) return checkpoint;
|
|
3935
|
+
const carried = checkpoint.clone();
|
|
3936
|
+
setArkPsbtField(carried, 0, ConditionWitness, witness);
|
|
3937
|
+
return carried;
|
|
3938
|
+
}
|
|
3939
|
+
return checkpoint;
|
|
3940
|
+
}
|
|
3941
|
+
function carriesTapscriptSigs(server, userSigned) {
|
|
3942
|
+
const entries = (tx) => (tx.getInput(0).tapScriptSig ?? []).map(
|
|
3943
|
+
([{ pubKey, leafHash }, sig]) => hex.encode(pubKey) + hex.encode(leafHash) + hex.encode(sig)
|
|
3944
|
+
);
|
|
3945
|
+
const have = new Set(entries(server));
|
|
3946
|
+
const want = entries(userSigned);
|
|
3947
|
+
return want.length > 0 && want.every((entry) => have.has(entry));
|
|
3901
3948
|
}
|
|
3902
3949
|
async function signAndSubmitOffchainTx(params) {
|
|
3903
3950
|
const offchainTx = buildOffchainTx(params.inputs, params.outputs, params.serverUnrollScript);
|
|
@@ -3906,7 +3953,8 @@ async function signAndSubmitOffchainTx(params) {
|
|
|
3906
3953
|
signCheckpoint: (checkpoint) => params.identity.sign(checkpoint)
|
|
3907
3954
|
};
|
|
3908
3955
|
const { arkTxid } = await submitOffchainTx(params.provider, offchainTx, signer, void 0, {
|
|
3909
|
-
verifyServerSignatures: params.verifyServerSignatures
|
|
3956
|
+
verifyServerSignatures: params.verifyServerSignatures,
|
|
3957
|
+
oneShot: params.oneShot
|
|
3910
3958
|
});
|
|
3911
3959
|
return arkTxid;
|
|
3912
3960
|
}
|
|
@@ -4676,12 +4724,14 @@ var VtxoManager = class _VtxoManager {
|
|
|
4676
4724
|
* ```
|
|
4677
4725
|
*/
|
|
4678
4726
|
async recoverVtxos(eventCallback) {
|
|
4679
|
-
const allVtxos = await
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4727
|
+
const [allVtxos, now] = await Promise.all([
|
|
4728
|
+
this.wallet.getVtxos({
|
|
4729
|
+
withRecoverable: true,
|
|
4730
|
+
withUnrolled: false
|
|
4731
|
+
}),
|
|
4732
|
+
fetchTimeHeight(this.wallet)
|
|
4733
|
+
]);
|
|
4683
4734
|
const dustAmount = getDustAmount(this.wallet);
|
|
4684
|
-
const now = await fetchTimeHeight(this.wallet);
|
|
4685
4735
|
let { vtxosToRecover } = getRecoverableWithSubdust(allVtxos, dustAmount, now);
|
|
4686
4736
|
if (vtxosToRecover.length === 0) {
|
|
4687
4737
|
throw new Error("No recoverable VTXOs found");
|
|
@@ -4782,12 +4832,17 @@ var VtxoManager = class _VtxoManager {
|
|
|
4782
4832
|
* ```
|
|
4783
4833
|
*/
|
|
4784
4834
|
async getRecoverableBalance() {
|
|
4785
|
-
const
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4835
|
+
const infoProvider = this.getInfoProvider();
|
|
4836
|
+
const [allVtxos, now, info, arkAddress] = await Promise.all([
|
|
4837
|
+
this.wallet.getVtxos({
|
|
4838
|
+
withRecoverable: true,
|
|
4839
|
+
withUnrolled: false
|
|
4840
|
+
}),
|
|
4841
|
+
fetchTimeHeight(this.wallet),
|
|
4842
|
+
infoProvider?.getInfo(),
|
|
4843
|
+
this.wallet.getAddress()
|
|
4844
|
+
]);
|
|
4789
4845
|
const dustAmount = getDustAmount(this.wallet);
|
|
4790
|
-
const now = await fetchTimeHeight(this.wallet);
|
|
4791
4846
|
let { vtxosToRecover, includesSubdust } = getRecoverableWithSubdust(
|
|
4792
4847
|
allVtxos,
|
|
4793
4848
|
dustAmount,
|
|
@@ -4805,14 +4860,9 @@ var VtxoManager = class _VtxoManager {
|
|
|
4805
4860
|
now
|
|
4806
4861
|
));
|
|
4807
4862
|
}
|
|
4808
|
-
const info = await this.getInfoProvider()?.getInfo();
|
|
4809
4863
|
const estimator = new Estimator(info?.fees.intentFee ?? {});
|
|
4810
4864
|
const { payable, net } = priceSettlementInputs(vtxosToRecover, estimator);
|
|
4811
|
-
const priced = deductOffchainOutputFee(
|
|
4812
|
-
subtotalOf(payable, net),
|
|
4813
|
-
estimator,
|
|
4814
|
-
await this.wallet.getAddress()
|
|
4815
|
-
);
|
|
4865
|
+
const priced = deductOffchainOutputFee(subtotalOf(payable, net), estimator, arkAddress);
|
|
4816
4866
|
const recoverable = priced > 0n ? priced : 0n;
|
|
4817
4867
|
const subdustAmount = subtotalOf(
|
|
4818
4868
|
payable.filter((v) => BigInt(v.value) < dustAmount),
|
|
@@ -4866,8 +4916,11 @@ var VtxoManager = class _VtxoManager {
|
|
|
4866
4916
|
const cm = await wallet.getContractManager();
|
|
4867
4917
|
const recoveryScripts = (await cm.getContracts()).filter(isRecoveryOnlyContract).map((contract) => contract.script);
|
|
4868
4918
|
if (recoveryScripts.length === 0) return;
|
|
4869
|
-
const
|
|
4870
|
-
const info = await
|
|
4919
|
+
const infoProvider = this.getInfoProvider();
|
|
4920
|
+
const [recoveryContracts, info] = await Promise.all([
|
|
4921
|
+
cm.getContractsWithVtxos({ script: recoveryScripts }),
|
|
4922
|
+
infoProvider?.getInfo()
|
|
4923
|
+
]);
|
|
4871
4924
|
const dustAmount = getDustAmount(wallet);
|
|
4872
4925
|
for (const { contract, vtxos } of recoveryContracts) {
|
|
4873
4926
|
try {
|
|
@@ -4962,12 +5015,13 @@ var VtxoManager = class _VtxoManager {
|
|
|
4962
5015
|
if (this.settlementConfig === false && thresholdMs === void 0) {
|
|
4963
5016
|
return [];
|
|
4964
5017
|
}
|
|
4965
|
-
const
|
|
5018
|
+
const threshold = thresholdMs !== void 0 ? thresholdMs : this.configuredThresholdMs();
|
|
5019
|
+
const vtxosPromise = this.wallet.getSpendableVtxos({
|
|
4966
5020
|
withRecoverable: true,
|
|
4967
5021
|
genericallySpendableOnly: true
|
|
4968
5022
|
});
|
|
4969
|
-
const threshold = thresholdMs !== void 0 ? thresholdMs : this.configuredThresholdMs();
|
|
4970
5023
|
const tip = now ?? await fetchTimeHeight(this.wallet);
|
|
5024
|
+
const vtxos = await vtxosPromise;
|
|
4971
5025
|
await this.warnHeldExpiring(threshold, tip);
|
|
4972
5026
|
return getExpiringAndRecoverableVtxos(vtxos, threshold, getDustAmount(this.wallet), tip);
|
|
4973
5027
|
}
|
|
@@ -5261,8 +5315,10 @@ var VtxoManager = class _VtxoManager {
|
|
|
5261
5315
|
const wallet = this.requireMigrationCapableWallet();
|
|
5262
5316
|
const info = await wallet.arkProvider.getInfo();
|
|
5263
5317
|
await wallet.settleServerInfoChanges?.();
|
|
5264
|
-
const { reports: vtxoReports } = await
|
|
5265
|
-
|
|
5318
|
+
const [{ reports: vtxoReports }, { reports: boardingReports }] = await Promise.all([
|
|
5319
|
+
this.classifyDeprecatedSignerContracts(info),
|
|
5320
|
+
this.classifyDeprecatedSignerBoarding(info)
|
|
5321
|
+
]);
|
|
5266
5322
|
return mergeSignerReports(vtxoReports, boardingReports);
|
|
5267
5323
|
}
|
|
5268
5324
|
/**
|
|
@@ -5284,8 +5340,10 @@ var VtxoManager = class _VtxoManager {
|
|
|
5284
5340
|
return { rotated: rotatedOnRefresh, expired: [], signers: [] };
|
|
5285
5341
|
}
|
|
5286
5342
|
if (walletClass.status === "UNKNOWN_SIGNER") {
|
|
5287
|
-
const { reports: vtxoReports2 } = await
|
|
5288
|
-
|
|
5343
|
+
const [{ reports: vtxoReports2 }, { reports: boardingReports2 }] = await Promise.all([
|
|
5344
|
+
this.classifyDeprecatedSignerContracts(info),
|
|
5345
|
+
this.classifyDeprecatedSignerBoarding(info)
|
|
5346
|
+
]);
|
|
5289
5347
|
return {
|
|
5290
5348
|
// True only if a second rotation landed inside the drain, leaving us on a signer
|
|
5291
5349
|
// newer than `info` (a drained rotation otherwise classifies CURRENT).
|
|
@@ -5296,16 +5354,13 @@ var VtxoManager = class _VtxoManager {
|
|
|
5296
5354
|
};
|
|
5297
5355
|
}
|
|
5298
5356
|
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);
|
|
5357
|
+
const [
|
|
5358
|
+
{ reports: vtxoReports, migratable: vtxoMigratable, expired: vtxoExpired },
|
|
5359
|
+
{ reports: boardingReports, migratable: boardingMigratable, expired: boardingExpired }
|
|
5360
|
+
] = await Promise.all([
|
|
5361
|
+
this.classifyDeprecatedSignerContracts(info),
|
|
5362
|
+
this.classifyDeprecatedSignerBoarding(info)
|
|
5363
|
+
]);
|
|
5309
5364
|
const reports = mergeSignerReports(vtxoReports, boardingReports);
|
|
5310
5365
|
const expiredRefs = [
|
|
5311
5366
|
...vtxoExpired.map(classifiedToRef),
|
|
@@ -6304,11 +6359,6 @@ async function classifyIntent(intent, vtxosByOutpoint, now) {
|
|
|
6304
6359
|
cancellationReason: "reconciliation: an input was unilaterally exited"
|
|
6305
6360
|
});
|
|
6306
6361
|
}
|
|
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
6362
|
if (intent.validUntil !== void 0 && intent.validUntil < now) {
|
|
6313
6363
|
return terminal(intent, "cancelled", now, {
|
|
6314
6364
|
cancellationReason: "reconciliation: intent expired"
|
|
@@ -6510,9 +6560,16 @@ function chainTxTypeToChainedExit(t) {
|
|
|
6510
6560
|
var isCommitment = (t) => t === "INDEXER_CHAINED_TX_TYPE_COMMITMENT" /* COMMITMENT */ || t === "INDEXER_CHAINED_TX_TYPE_UNSPECIFIED" /* UNSPECIFIED */;
|
|
6511
6561
|
async function buildExitDag(params) {
|
|
6512
6562
|
const byTxid = /* @__PURE__ */ new Map();
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6563
|
+
const chains = await Promise.allSettled(
|
|
6564
|
+
params.vtxos.map(async (vtxo) => ({
|
|
6565
|
+
outpoint: `${vtxo.txid}:${vtxo.vout}`,
|
|
6566
|
+
// The resolver returns the full chain (all pages merged, local-first).
|
|
6567
|
+
chain: await params.chain.getVtxoChain({ txid: vtxo.txid, vout: vtxo.vout })
|
|
6568
|
+
}))
|
|
6569
|
+
);
|
|
6570
|
+
for (const result of chains) {
|
|
6571
|
+
if (result.status === "rejected") throw result.reason;
|
|
6572
|
+
const { outpoint, chain } = result.value;
|
|
6516
6573
|
for (const chainTx of chain) {
|
|
6517
6574
|
const existing = byTxid.get(chainTx.txid);
|
|
6518
6575
|
if (existing) {
|
|
@@ -6545,25 +6602,26 @@ async function buildExitDag(params) {
|
|
|
6545
6602
|
if (order.length !== pending.size) {
|
|
6546
6603
|
throw new Error("inconsistent vtxo chain from indexer (cycle detected)");
|
|
6547
6604
|
}
|
|
6548
|
-
const
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
}
|
|
6605
|
+
const confirmed = await Promise.all(
|
|
6606
|
+
order.map(async (tx) => {
|
|
6607
|
+
try {
|
|
6608
|
+
const status = await params.onchain.getTxStatus(tx.txid);
|
|
6609
|
+
return status.confirmed;
|
|
6610
|
+
} catch {
|
|
6611
|
+
return false;
|
|
6612
|
+
}
|
|
6613
|
+
})
|
|
6614
|
+
);
|
|
6615
|
+
return order.map((tx, i) => {
|
|
6557
6616
|
const expires = Number(tx.expiresAt);
|
|
6558
|
-
|
|
6617
|
+
return {
|
|
6559
6618
|
txid: tx.txid,
|
|
6560
6619
|
type: tx.type,
|
|
6561
6620
|
expiresAt: Number.isFinite(expires) && expires > 0 ? expires : void 0,
|
|
6562
6621
|
forVtxos: [...tx.forVtxos].sort(),
|
|
6563
|
-
confirmed
|
|
6564
|
-
}
|
|
6565
|
-
}
|
|
6566
|
-
return nodes;
|
|
6622
|
+
confirmed: confirmed[i]
|
|
6623
|
+
};
|
|
6624
|
+
});
|
|
6567
6625
|
}
|
|
6568
6626
|
function topoSortByDeps(items, idOf, depsOf) {
|
|
6569
6627
|
const produced = new Set(items.map(idOf));
|
|
@@ -6732,11 +6790,12 @@ var RepositoryExitDataSource = class {
|
|
|
6732
6790
|
return chain;
|
|
6733
6791
|
}
|
|
6734
6792
|
async getVirtualTxs(txids) {
|
|
6793
|
+
const stored = await Promise.all(txids.map((txid) => this.repo.getVirtualTx(txid)));
|
|
6735
6794
|
const out = /* @__PURE__ */ new Map();
|
|
6736
|
-
|
|
6737
|
-
const
|
|
6738
|
-
if (
|
|
6739
|
-
}
|
|
6795
|
+
txids.forEach((txid, i) => {
|
|
6796
|
+
const row = stored[i];
|
|
6797
|
+
if (row?.psbt) out.set(txid, row.psbt);
|
|
6798
|
+
});
|
|
6740
6799
|
return out;
|
|
6741
6800
|
}
|
|
6742
6801
|
};
|
|
@@ -7854,8 +7913,10 @@ var DelegateManagerImpl = class {
|
|
|
7854
7913
|
return { delegated: [], failed: [] };
|
|
7855
7914
|
}
|
|
7856
7915
|
const destinationScript = ArkAddress.decode(destination).pkScript;
|
|
7857
|
-
const arkInfo = await
|
|
7858
|
-
|
|
7916
|
+
const [arkInfo, delegateInfo] = await Promise.all([
|
|
7917
|
+
this.arkInfoProvider.getInfo(),
|
|
7918
|
+
this.delegateProvider.getDelegateInfo()
|
|
7919
|
+
]);
|
|
7859
7920
|
const eligible = vtxos.map(normalizeVtxo).filter(
|
|
7860
7921
|
(v) => isAnnotated(v) && findDelegateTapLeaf(v, delegateInfo.pubkey) !== void 0
|
|
7861
7922
|
);
|
|
@@ -10646,8 +10707,10 @@ var ContractManager = class _ContractManager {
|
|
|
10646
10707
|
return contract !== void 0 && contractHandlers.get(contract.type)?.assertSpendableNow !== void 0;
|
|
10647
10708
|
});
|
|
10648
10709
|
if (asking.length === 0) return refused;
|
|
10649
|
-
const tip = await
|
|
10650
|
-
|
|
10710
|
+
const [tip, walletDescriptorValue] = await Promise.all([
|
|
10711
|
+
this.currentChainTip(),
|
|
10712
|
+
walletDescriptor?.()
|
|
10713
|
+
]);
|
|
10651
10714
|
for (const vtxo of asking) {
|
|
10652
10715
|
const contract = byScript.get(vtxo.script);
|
|
10653
10716
|
const handler = contractHandlers.get(contract.type);
|
|
@@ -12548,7 +12611,10 @@ var WalletReceiveRotator = class _WalletReceiveRotator {
|
|
|
12548
12611
|
* watched; it just stops being advertised). The untagged index-0 baseline is never retired.
|
|
12549
12612
|
*/
|
|
12550
12613
|
async rotate(wallet) {
|
|
12551
|
-
const
|
|
12614
|
+
const descriptorPromise = this.provider.getNextSigningDescriptor();
|
|
12615
|
+
const managerPromise = wallet.getContractManager();
|
|
12616
|
+
void managerPromise.catch(() => void 0);
|
|
12617
|
+
const descriptor = await descriptorPromise;
|
|
12552
12618
|
const { tapscript: newTapscript, params } = buildReceiveContract(
|
|
12553
12619
|
wallet.offchainTapscript,
|
|
12554
12620
|
descriptor,
|
|
@@ -12556,7 +12622,7 @@ var WalletReceiveRotator = class _WalletReceiveRotator {
|
|
|
12556
12622
|
true
|
|
12557
12623
|
);
|
|
12558
12624
|
const newScript = params.script;
|
|
12559
|
-
const manager = await
|
|
12625
|
+
const manager = await managerPromise;
|
|
12560
12626
|
await manager.createContract(params);
|
|
12561
12627
|
wallet.setOffchainTapscriptForRotation(newTapscript);
|
|
12562
12628
|
const previousTagged = this.currentTaggedScript;
|
|
@@ -12666,11 +12732,11 @@ function computeOffchainBalance(vtxos, caps) {
|
|
|
12666
12732
|
let settled = 0;
|
|
12667
12733
|
let preconfirmed = 0;
|
|
12668
12734
|
let available = 0;
|
|
12669
|
-
let
|
|
12735
|
+
let contractLocked = 0;
|
|
12670
12736
|
let pendingSettlement = 0;
|
|
12671
12737
|
let reserved = 0;
|
|
12672
12738
|
let recoverable = 0;
|
|
12673
|
-
let
|
|
12739
|
+
let deprecatedSignerLocked = 0;
|
|
12674
12740
|
let unrolled = 0;
|
|
12675
12741
|
const owned = /* @__PURE__ */ new Map();
|
|
12676
12742
|
const spendable = /* @__PURE__ */ new Map();
|
|
@@ -12687,7 +12753,7 @@ function computeOffchainBalance(vtxos, caps) {
|
|
|
12687
12753
|
continue;
|
|
12688
12754
|
}
|
|
12689
12755
|
if (isPendingRecovery(vtxo)) {
|
|
12690
|
-
|
|
12756
|
+
deprecatedSignerLocked += vtxo.value;
|
|
12691
12757
|
continue;
|
|
12692
12758
|
}
|
|
12693
12759
|
if (canRecoverOnchain(vtxo, now)) {
|
|
@@ -12701,7 +12767,7 @@ function computeOffchainBalance(vtxos, caps) {
|
|
|
12701
12767
|
settled += vtxo.value;
|
|
12702
12768
|
}
|
|
12703
12769
|
if (!isGenericallySpendable(vtxo)) {
|
|
12704
|
-
|
|
12770
|
+
contractLocked += vtxo.value;
|
|
12705
12771
|
} else if (!isUnlocked(vtxo)) {
|
|
12706
12772
|
pendingSettlement += vtxo.value;
|
|
12707
12773
|
} else if (isReserved?.(vtxo)) {
|
|
@@ -12719,13 +12785,13 @@ function computeOffchainBalance(vtxos, caps) {
|
|
|
12719
12785
|
settled,
|
|
12720
12786
|
preconfirmed,
|
|
12721
12787
|
available,
|
|
12722
|
-
|
|
12788
|
+
contractLocked,
|
|
12723
12789
|
pendingSettlement,
|
|
12724
12790
|
reserved,
|
|
12725
12791
|
recoverable,
|
|
12726
|
-
|
|
12792
|
+
deprecatedSignerLocked,
|
|
12727
12793
|
unrolled,
|
|
12728
|
-
total: settled + preconfirmed + recoverable +
|
|
12794
|
+
total: settled + preconfirmed + recoverable + deprecatedSignerLocked + unrolled,
|
|
12729
12795
|
assets: toAssets(owned),
|
|
12730
12796
|
availableAssets: toAssets(spendable)
|
|
12731
12797
|
};
|
|
@@ -12750,15 +12816,19 @@ function toWalletBalance(boardingUtxos, offchain) {
|
|
|
12750
12816
|
settled: offchain.settled,
|
|
12751
12817
|
preconfirmed: offchain.preconfirmed,
|
|
12752
12818
|
available: offchain.available,
|
|
12753
|
-
|
|
12819
|
+
contractLocked: offchain.contractLocked,
|
|
12754
12820
|
pendingSettlement: offchain.pendingSettlement,
|
|
12755
12821
|
reserved: offchain.reserved,
|
|
12756
12822
|
recoverable: offchain.recoverable,
|
|
12757
|
-
|
|
12823
|
+
deprecatedSignerLocked: offchain.deprecatedSignerLocked,
|
|
12758
12824
|
unrolled: offchain.unrolled,
|
|
12759
12825
|
total: totalBoarding + offchain.total,
|
|
12760
12826
|
assets: offchain.assets,
|
|
12761
|
-
availableAssets: offchain.availableAssets
|
|
12827
|
+
availableAssets: offchain.availableAssets,
|
|
12828
|
+
// Deprecated aliases, so code written against older releases still reads a value.
|
|
12829
|
+
gated: offchain.contractLocked,
|
|
12830
|
+
intentLocked: offchain.pendingSettlement,
|
|
12831
|
+
pendingRecovery: offchain.deprecatedSignerLocked
|
|
12762
12832
|
};
|
|
12763
12833
|
}
|
|
12764
12834
|
|
|
@@ -13806,22 +13876,42 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
13806
13876
|
const utxos = [];
|
|
13807
13877
|
const commitmentsToIgnore = /* @__PURE__ */ new Set();
|
|
13808
13878
|
const tapscripts = await this.getBoardingTapscripts(this.watchedBoardingSigners());
|
|
13809
|
-
const
|
|
13810
|
-
const
|
|
13811
|
-
|
|
13812
|
-
|
|
13813
|
-
|
|
13814
|
-
|
|
13815
|
-
|
|
13816
|
-
|
|
13817
|
-
|
|
13818
|
-
|
|
13819
|
-
|
|
13879
|
+
const getOne = this.onchainProvider.getTxOutspend?.bind(this.onchainProvider);
|
|
13880
|
+
const listInflight = /* @__PURE__ */ new Map();
|
|
13881
|
+
const oneInflight = /* @__PURE__ */ new Map();
|
|
13882
|
+
const outspendOf = (txid, index) => {
|
|
13883
|
+
if (getOne) {
|
|
13884
|
+
const key = `${txid}:${index}`;
|
|
13885
|
+
let pending2 = oneInflight.get(key);
|
|
13886
|
+
if (!pending2) {
|
|
13887
|
+
pending2 = getOne(txid, index);
|
|
13888
|
+
oneInflight.set(key, pending2);
|
|
13889
|
+
}
|
|
13890
|
+
return pending2;
|
|
13891
|
+
}
|
|
13892
|
+
let pending = listInflight.get(txid);
|
|
13893
|
+
if (!pending) {
|
|
13894
|
+
pending = this.onchainProvider.getTxOutspends(txid);
|
|
13895
|
+
listInflight.set(txid, pending);
|
|
13896
|
+
}
|
|
13897
|
+
return pending.then((statuses) => statuses[index]);
|
|
13820
13898
|
};
|
|
13821
|
-
|
|
13822
|
-
|
|
13823
|
-
|
|
13824
|
-
|
|
13899
|
+
const historyResults = await Promise.allSettled(
|
|
13900
|
+
tapscripts.map(async (tapscript) => {
|
|
13901
|
+
const boardingAddress = tapscript.onchainAddress(this.network);
|
|
13902
|
+
return {
|
|
13903
|
+
boardingAddress,
|
|
13904
|
+
scriptHex: hex.encode(tapscript.pkScript),
|
|
13905
|
+
txs: await this.onchainProvider.getTransactions(boardingAddress)
|
|
13906
|
+
};
|
|
13907
|
+
})
|
|
13908
|
+
);
|
|
13909
|
+
const histories = historyResults.map((result) => {
|
|
13910
|
+
if (result.status === "rejected") throw result.reason;
|
|
13911
|
+
return result.value;
|
|
13912
|
+
});
|
|
13913
|
+
const outputs = [];
|
|
13914
|
+
for (const { boardingAddress, scriptHex, txs } of histories) {
|
|
13825
13915
|
const commitmentByOutpoint = /* @__PURE__ */ new Map();
|
|
13826
13916
|
for (const tx of txs) {
|
|
13827
13917
|
for (const input of tx.vin ?? []) {
|
|
@@ -13831,37 +13921,51 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
13831
13921
|
for (const tx of txs) {
|
|
13832
13922
|
for (let i = 0; i < tx.vout.length; i++) {
|
|
13833
13923
|
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
|
-
}
|
|
13924
|
+
if (vout.scriptpubkey_address !== boardingAddress) continue;
|
|
13925
|
+
outputs.push({
|
|
13926
|
+
txid: tx.txid,
|
|
13927
|
+
vout: i,
|
|
13928
|
+
value: Number(vout.value),
|
|
13929
|
+
confirmed: tx.status.confirmed,
|
|
13930
|
+
blockTime: tx.status.block_time,
|
|
13931
|
+
scriptHex,
|
|
13932
|
+
commitmentFromVin: commitmentByOutpoint.get(`${tx.txid}:${i}`),
|
|
13933
|
+
spentStatus: outspendOf(tx.txid, i)
|
|
13934
|
+
});
|
|
13862
13935
|
}
|
|
13863
13936
|
}
|
|
13864
13937
|
}
|
|
13938
|
+
const spentResults = await Promise.allSettled(outputs.map((output) => output.spentStatus));
|
|
13939
|
+
for (const [i, output] of outputs.entries()) {
|
|
13940
|
+
const spentResult = spentResults[i];
|
|
13941
|
+
if (spentResult.status === "rejected") throw spentResult.reason;
|
|
13942
|
+
const spentStatus = spentResult.value;
|
|
13943
|
+
const commitmentTxid = spentStatus?.txid || output.commitmentFromVin;
|
|
13944
|
+
const spent = Boolean(spentStatus?.spent) || commitmentTxid !== void 0;
|
|
13945
|
+
if (spent && commitmentTxid) {
|
|
13946
|
+
commitmentsToIgnore.add(commitmentTxid);
|
|
13947
|
+
}
|
|
13948
|
+
const boardingFacts = {
|
|
13949
|
+
isSpent: spent,
|
|
13950
|
+
isSwept: false,
|
|
13951
|
+
isPreconfirmed: false,
|
|
13952
|
+
commitmentTxIds: spent && commitmentTxid ? [commitmentTxid] : []
|
|
13953
|
+
};
|
|
13954
|
+
utxos.push({
|
|
13955
|
+
txid: output.txid,
|
|
13956
|
+
vout: output.vout,
|
|
13957
|
+
value: output.value,
|
|
13958
|
+
status: {
|
|
13959
|
+
confirmed: output.confirmed,
|
|
13960
|
+
block_time: output.blockTime
|
|
13961
|
+
},
|
|
13962
|
+
isUnrolled: true,
|
|
13963
|
+
...boardingFacts,
|
|
13964
|
+
spentBy: "",
|
|
13965
|
+
createdAt: output.confirmed ? new Date(output.blockTime * 1e3) : /* @__PURE__ */ new Date(0),
|
|
13966
|
+
script: output.scriptHex
|
|
13967
|
+
});
|
|
13968
|
+
}
|
|
13865
13969
|
const unconfirmedTxs = [];
|
|
13866
13970
|
const confirmedTxs = [];
|
|
13867
13971
|
for (const utxo of utxos) {
|
|
@@ -13953,9 +14057,12 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
13953
14057
|
await this.walletRepository.saveUtxos(
|
|
13954
14058
|
new Map(groups.map((group, i) => [addresses[i], group.coins]))
|
|
13955
14059
|
);
|
|
14060
|
+
const cachedByAddress = await Promise.all(
|
|
14061
|
+
addresses.map((address) => collectUtxos(this.walletRepository, address))
|
|
14062
|
+
);
|
|
13956
14063
|
for (const [i, group] of groups.entries()) {
|
|
13957
14064
|
const fresh = new Set(group.coins.map((coin) => `${coin.txid}:${coin.vout}`));
|
|
13958
|
-
const cached =
|
|
14065
|
+
const cached = cachedByAddress[i];
|
|
13959
14066
|
const kept = cached.filter((coin) => fresh.has(`${coin.txid}:${coin.vout}`));
|
|
13960
14067
|
if (kept.length === cached.length) continue;
|
|
13961
14068
|
await this.walletRepository.deleteUtxos(addresses[i]);
|
|
@@ -13978,14 +14085,20 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
13978
14085
|
return groups.flatMap((g) => g.coins);
|
|
13979
14086
|
}
|
|
13980
14087
|
/**
|
|
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
|
|
14088
|
+
* @internal {@link getBoardingUtxos} minus those an in-flight settle is spending or an
|
|
14089
|
+
* unresolved intent locks, for SDK picks. The getter itself lists them: callers that mirror it
|
|
14090
|
+
* (the worker's cache and broadcasts, balances) must not see a settle's inputs vanish, and then
|
|
14091
|
+
* stay gone if it fails.
|
|
13984
14092
|
*/
|
|
13985
14093
|
async getSelectableBoardingUtxos() {
|
|
13986
|
-
|
|
13987
|
-
|
|
13988
|
-
|
|
14094
|
+
const [utxos, locked] = await Promise.all([
|
|
14095
|
+
this.getBoardingUtxos(),
|
|
14096
|
+
this.lockedOutpoints()
|
|
14097
|
+
]);
|
|
14098
|
+
return utxos.filter((coin) => {
|
|
14099
|
+
const key = `${coin.txid}:${coin.vout}`;
|
|
14100
|
+
return !this._pendingSpendOutpoints.has(key) && !locked.has(key);
|
|
14101
|
+
});
|
|
13989
14102
|
}
|
|
13990
14103
|
async getStoredBoardingUtxos() {
|
|
13991
14104
|
const currentOnly = /* @__PURE__ */ new Set([
|
|
@@ -14010,6 +14123,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
14010
14123
|
* @returns A function that stops the subscriptions
|
|
14011
14124
|
*/
|
|
14012
14125
|
async notifyIncomingFunds(eventCallback) {
|
|
14126
|
+
const cm = this.indexerProvider ? await this.getContractManager() : void 0;
|
|
14013
14127
|
const arkAddress = await this.getAddress();
|
|
14014
14128
|
let onchainStopFunc;
|
|
14015
14129
|
let indexerStopFunc;
|
|
@@ -14062,8 +14176,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
14062
14176
|
void subscribeOnchain();
|
|
14063
14177
|
});
|
|
14064
14178
|
await subscribeOnchain();
|
|
14065
|
-
if (
|
|
14066
|
-
const cm = await this.getContractManager();
|
|
14179
|
+
if (cm && arkAddress) {
|
|
14067
14180
|
let annotationQueue = Promise.resolve();
|
|
14068
14181
|
indexerStopFunc = cm.onContractEvent((event) => {
|
|
14069
14182
|
if (!isContractVtxoEvent(event)) {
|
|
@@ -14209,21 +14322,25 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
14209
14322
|
extraSources: capture?.sources
|
|
14210
14323
|
});
|
|
14211
14324
|
onVtxosPersisted = async (_contract, vtxos) => {
|
|
14212
|
-
|
|
14213
|
-
|
|
14214
|
-
|
|
14215
|
-
|
|
14216
|
-
|
|
14217
|
-
|
|
14218
|
-
|
|
14219
|
-
|
|
14220
|
-
|
|
14221
|
-
|
|
14222
|
-
|
|
14223
|
-
|
|
14325
|
+
await Promise.all(
|
|
14326
|
+
vtxos.map((v) => {
|
|
14327
|
+
if (v.isSpent) return void 0;
|
|
14328
|
+
return captureExitBranch({
|
|
14329
|
+
resolver,
|
|
14330
|
+
repository: virtualTxRepository,
|
|
14331
|
+
vtxo: { txid: v.txid, vout: v.vout },
|
|
14332
|
+
value: v.value,
|
|
14333
|
+
mode: capture?.mode ?? DEFAULT_EXIT_CAPTURE_MODE,
|
|
14334
|
+
minExitWorthSats: capture?.minExitWorthSats ?? DEFAULT_MIN_EXIT_WORTH_SATS
|
|
14335
|
+
}).catch(() => {
|
|
14336
|
+
});
|
|
14337
|
+
})
|
|
14338
|
+
);
|
|
14224
14339
|
};
|
|
14225
14340
|
onVtxosSpent = (vtxos) => pruneExitBranches(virtualTxRepository, vtxos);
|
|
14226
14341
|
}
|
|
14342
|
+
const baselinePubkeyPromise = this.identity.xOnlyPublicKey();
|
|
14343
|
+
void baselinePubkeyPromise.catch(() => void 0);
|
|
14227
14344
|
const manager = await ContractManager.create({
|
|
14228
14345
|
lazyInitialization: this.lazyInitialization,
|
|
14229
14346
|
indexerProvider: this.indexerProvider,
|
|
@@ -14243,7 +14360,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
14243
14360
|
return { height, time };
|
|
14244
14361
|
}
|
|
14245
14362
|
});
|
|
14246
|
-
const baselinePubkey = await
|
|
14363
|
+
const baselinePubkey = await baselinePubkeyPromise;
|
|
14247
14364
|
const delegatePubKey = this.offchainTapscript instanceof DelegateVtxo.Script ? this.offchainTapscript.options.delegatePubKey : void 0;
|
|
14248
14365
|
const baselineSigners = [
|
|
14249
14366
|
this.offchainTapscript.options.serverPubKey,
|
|
@@ -14337,7 +14454,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
14337
14454
|
}
|
|
14338
14455
|
};
|
|
14339
14456
|
var Wallet2 = class extends ReadonlyWallet {
|
|
14340
|
-
constructor(identity, network, onchainProvider, arkProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, forfeitPubkey, dustAmount, walletRepository, contractRepository, delegateProvider, watcherConfig, settlementConfig, walletContractTimelocks, receiveRotator, descriptorProvider, keyring, lookAheadWindow, batchExpiryPolicy) {
|
|
14457
|
+
constructor(identity, network, onchainProvider, arkProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, forfeitPubkey, dustAmount, walletRepository, contractRepository, delegateProvider, watcherConfig, settlementConfig, walletContractTimelocks, receiveRotator, descriptorProvider, keyring, lookAheadWindow, batchExpiryPolicy, oneShotSubmit = true) {
|
|
14341
14458
|
super(
|
|
14342
14459
|
identity,
|
|
14343
14460
|
network,
|
|
@@ -14357,6 +14474,7 @@ var Wallet2 = class extends ReadonlyWallet {
|
|
|
14357
14474
|
this.forfeitOutputScript = forfeitOutputScript;
|
|
14358
14475
|
this.forfeitPubkey = forfeitPubkey;
|
|
14359
14476
|
this.batchExpiryPolicy = batchExpiryPolicy;
|
|
14477
|
+
this.oneShotSubmit = oneShotSubmit;
|
|
14360
14478
|
this.identity = identity;
|
|
14361
14479
|
this.settlementConfig = settlementConfig !== void 0 ? settlementConfig : { ...DEFAULT_SETTLEMENT_CONFIG };
|
|
14362
14480
|
this._delegateManager = delegateProvider ? new DelegateManagerImpl(delegateProvider, arkProvider, identity) : void 0;
|
|
@@ -14378,6 +14496,7 @@ var Wallet2 = class extends ReadonlyWallet {
|
|
|
14378
14496
|
forfeitOutputScript;
|
|
14379
14497
|
forfeitPubkey;
|
|
14380
14498
|
batchExpiryPolicy;
|
|
14499
|
+
oneShotSubmit;
|
|
14381
14500
|
static MIN_FEE_RATE = 1;
|
|
14382
14501
|
// sats/vbyte
|
|
14383
14502
|
identity;
|
|
@@ -14846,13 +14965,13 @@ var Wallet2 = class extends ReadonlyWallet {
|
|
|
14846
14965
|
async _doRotateServerSigner(xonly, newServerUnrollScript) {
|
|
14847
14966
|
if (equalBytes(xonly, this.arkServerPublicKey)) return;
|
|
14848
14967
|
const manager = await this.getContractManager();
|
|
14849
|
-
const [currentOffchainRow] = await manager.getContracts({
|
|
14850
|
-
script: this.defaultContractScript
|
|
14851
|
-
});
|
|
14852
14968
|
const currentBoardingScript = hex.encode(this._boardingTapscript.pkScript);
|
|
14853
|
-
const [
|
|
14854
|
-
script:
|
|
14855
|
-
|
|
14969
|
+
const [offchainRows, boardingRows] = await Promise.all([
|
|
14970
|
+
manager.getContracts({ script: this.defaultContractScript }),
|
|
14971
|
+
manager.getContracts({ script: currentBoardingScript })
|
|
14972
|
+
]);
|
|
14973
|
+
const [currentOffchainRow] = offchainRows;
|
|
14974
|
+
const [currentBoardingRow] = boardingRows;
|
|
14856
14975
|
const newOffchain = this.offchainTapscript instanceof DelegateVtxo.Script ? new DelegateVtxo.Script({
|
|
14857
14976
|
...this.offchainTapscript.options,
|
|
14858
14977
|
serverPubKey: xonly
|
|
@@ -15035,13 +15154,15 @@ var Wallet2 = class extends ReadonlyWallet {
|
|
|
15035
15154
|
return this._restoreInFlight;
|
|
15036
15155
|
}
|
|
15037
15156
|
async _runRestore(gapLimit) {
|
|
15038
|
-
const manager = await this.getContractManager();
|
|
15039
15157
|
const provider = this._hdProvider;
|
|
15040
15158
|
const hd = provider !== void 0;
|
|
15041
|
-
const staticDescriptor =
|
|
15159
|
+
const [manager, staticDescriptor, arkInfo] = await Promise.all([
|
|
15160
|
+
this.getContractManager(),
|
|
15161
|
+
hd ? void 0 : identityDescriptor(this.identity),
|
|
15162
|
+
this.arkProvider.getInfo()
|
|
15163
|
+
]);
|
|
15042
15164
|
const materialize = (index) => hd ? provider.materializeDescriptorAt(index) : staticDescriptor;
|
|
15043
15165
|
const delegatePubKey = this.offchainTapscript instanceof DelegateVtxo.Script ? this.offchainTapscript.options.delegatePubKey : void 0;
|
|
15044
|
-
const arkInfo = await this.arkProvider.getInfo();
|
|
15045
15166
|
const currentSignerPubKey = toXOnly(hex.decode(arkInfo.signerPubkey), "ark signer key");
|
|
15046
15167
|
const deprecatedSignerPubKeys = arkInfo.deprecatedSigners.map(
|
|
15047
15168
|
(s) => toXOnly(hex.decode(s.pubkey), "deprecated signer key")
|
|
@@ -15221,7 +15342,8 @@ var Wallet2 = class extends ReadonlyWallet {
|
|
|
15221
15342
|
{
|
|
15222
15343
|
advertisedVtxoTreeExpiry: setup.info.vtxoTreeExpiry,
|
|
15223
15344
|
...config.minBatchExpirySeconds !== void 0 ? { minSeconds: config.minBatchExpirySeconds } : {}
|
|
15224
|
-
}
|
|
15345
|
+
},
|
|
15346
|
+
config.oneShotSubmit
|
|
15225
15347
|
);
|
|
15226
15348
|
wallet.lazyInitialization = config.lazyInitialization ?? false;
|
|
15227
15349
|
wallet._serverInfoSource = setup.serverInfoSource;
|
|
@@ -15589,25 +15711,33 @@ var Wallet2 = class extends ReadonlyWallet {
|
|
|
15589
15711
|
throw error;
|
|
15590
15712
|
}
|
|
15591
15713
|
const inputIds = params.inputs.map((i) => `${i.txid}:${i.vout}`).join(",");
|
|
15714
|
+
let released = !registering;
|
|
15592
15715
|
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)
|
|
15716
|
+
try {
|
|
15717
|
+
await this.arkProvider.deleteIntent(deleteIntent);
|
|
15718
|
+
released = true;
|
|
15719
|
+
} catch (e) {
|
|
15720
|
+
if (isNoMatchingIntentsError(e)) released = true;
|
|
15721
|
+
else {
|
|
15722
|
+
console.warn(
|
|
15723
|
+
`Failed to delete intent after settle failure for inputs [${inputIds}]; intent may linger on server and cause 'duplicated input' on next settle`,
|
|
15724
|
+
e
|
|
15725
|
+
);
|
|
15726
|
+
}
|
|
15609
15727
|
}
|
|
15610
|
-
|
|
15728
|
+
}
|
|
15729
|
+
if (released) {
|
|
15730
|
+
await this.persistIntentSnapshot(
|
|
15731
|
+
intentTxId,
|
|
15732
|
+
"cancelled",
|
|
15733
|
+
intent,
|
|
15734
|
+
deleteIntent,
|
|
15735
|
+
params.inputs,
|
|
15736
|
+
{
|
|
15737
|
+
cancellationReason: error instanceof Error ? error.message : String(error)
|
|
15738
|
+
}
|
|
15739
|
+
);
|
|
15740
|
+
}
|
|
15611
15741
|
throw error;
|
|
15612
15742
|
} finally {
|
|
15613
15743
|
this._removePendingSpends(params.inputs);
|
|
@@ -16254,7 +16384,8 @@ var Wallet2 = class extends ReadonlyWallet {
|
|
|
16254
16384
|
}
|
|
16255
16385
|
],
|
|
16256
16386
|
outputs: [{ script: myPkScript, amount: BigInt(vtxo.value) }],
|
|
16257
|
-
serverUnrollScript
|
|
16387
|
+
serverUnrollScript,
|
|
16388
|
+
oneShot: this.oneShotSubmit
|
|
16258
16389
|
});
|
|
16259
16390
|
swept += vtxo.value;
|
|
16260
16391
|
} catch (error) {
|
|
@@ -17096,7 +17227,8 @@ var Wallet2 = class extends ReadonlyWallet {
|
|
|
17096
17227
|
deprecatedServerPubkeys: [...this._deprecatedSigners.keys()].map(
|
|
17097
17228
|
(h) => hex.decode(h)
|
|
17098
17229
|
)
|
|
17099
|
-
}
|
|
17230
|
+
},
|
|
17231
|
+
oneShot: this.oneShotSubmit
|
|
17100
17232
|
}
|
|
17101
17233
|
);
|
|
17102
17234
|
} finally {
|
|
@@ -17744,6 +17876,7 @@ var MessageBus = class {
|
|
|
17744
17876
|
lazyInitialization: config.lazyInitialization,
|
|
17745
17877
|
watcherConfig: config.watcherConfig,
|
|
17746
17878
|
lookAheadWindow: config.lookAheadWindow,
|
|
17879
|
+
oneShotSubmit: config.oneShotSubmit,
|
|
17747
17880
|
minBatchExpirySeconds: config.minBatchExpirySeconds
|
|
17748
17881
|
});
|
|
17749
17882
|
return { wallet, arkProvider, readonlyWallet: wallet };
|
|
@@ -18536,6 +18669,7 @@ var WalletMessageHandler = class {
|
|
|
18536
18669
|
indexerProvider;
|
|
18537
18670
|
walletRepository;
|
|
18538
18671
|
incomingFundsSubscription;
|
|
18672
|
+
incomingFundsRegistrations = /* @__PURE__ */ new WeakMap();
|
|
18539
18673
|
contractEventsSubscription;
|
|
18540
18674
|
onNextTick = [];
|
|
18541
18675
|
channel;
|
|
@@ -18547,6 +18681,11 @@ var WalletMessageHandler = class {
|
|
|
18547
18681
|
// stale, folding requests in would truncate an in-flight settle, and
|
|
18548
18682
|
// sharing with boarding would kill every lazy refresh (see `boardingEpoch`).
|
|
18549
18683
|
incomingFundsEpoch = 0;
|
|
18684
|
+
/**
|
|
18685
|
+
* Supersedes older initializers without touching `incomingFundsEpoch`, so a
|
|
18686
|
+
* re-init that fails before registering leaves the live subscription delivering.
|
|
18687
|
+
*/
|
|
18688
|
+
initEpoch = 0;
|
|
18550
18689
|
contractEventsEpoch = 0;
|
|
18551
18690
|
boardingEpoch = 0;
|
|
18552
18691
|
/** Per-request progress callbacks: ends with the handler, not with a re-init. */
|
|
@@ -18571,6 +18710,7 @@ var WalletMessageHandler = class {
|
|
|
18571
18710
|
}
|
|
18572
18711
|
async stop() {
|
|
18573
18712
|
this.requestEpoch++;
|
|
18713
|
+
this.initEpoch++;
|
|
18574
18714
|
this.incomingFundsEpoch++;
|
|
18575
18715
|
this.contractEventsEpoch++;
|
|
18576
18716
|
this.boardingEpoch++;
|
|
@@ -18611,6 +18751,8 @@ var WalletMessageHandler = class {
|
|
|
18611
18751
|
}
|
|
18612
18752
|
epochOf(scope) {
|
|
18613
18753
|
switch (scope) {
|
|
18754
|
+
case "init":
|
|
18755
|
+
return this.initEpoch;
|
|
18614
18756
|
case "incomingFunds":
|
|
18615
18757
|
return this.incomingFundsEpoch;
|
|
18616
18758
|
case "contractEvents":
|
|
@@ -19367,18 +19509,22 @@ var WalletMessageHandler = class {
|
|
|
19367
19509
|
}
|
|
19368
19510
|
async onWalletInitialized() {
|
|
19369
19511
|
const wallet = this.readonlyWallet;
|
|
19512
|
+
const signingWallet = this.wallet;
|
|
19513
|
+
const lazyBoarding = this.lazyBoardingInit;
|
|
19370
19514
|
if (!wallet || !this.arkProvider || !this.indexerProvider || !this.walletRepository) {
|
|
19371
19515
|
return;
|
|
19372
19516
|
}
|
|
19373
|
-
|
|
19517
|
+
this.initEpoch++;
|
|
19518
|
+
const init = this.newGeneration("init");
|
|
19374
19519
|
await this.ensureContractEventBroadcasting();
|
|
19375
19520
|
if (init.stale) return;
|
|
19376
|
-
await this.refreshCachedData({ lazyBoarding
|
|
19521
|
+
await this.refreshCachedData({ lazyBoarding });
|
|
19377
19522
|
if (init.stale) return;
|
|
19378
|
-
if (
|
|
19523
|
+
if (signingWallet) {
|
|
19379
19524
|
try {
|
|
19380
19525
|
const vtxos = await this.getVtxosFromRepo();
|
|
19381
|
-
|
|
19526
|
+
if (init.stale) return;
|
|
19527
|
+
const { pending, finalized } = await signingWallet.finalizePendingTxs(
|
|
19382
19528
|
vtxos.filter((vtxo) => vtxo.isSpent || vtxo.isPreconfirmed && !vtxo.isSwept)
|
|
19383
19529
|
);
|
|
19384
19530
|
console.info(
|
|
@@ -19389,11 +19535,34 @@ var WalletMessageHandler = class {
|
|
|
19389
19535
|
}
|
|
19390
19536
|
if (init.stale) return;
|
|
19391
19537
|
}
|
|
19392
|
-
if (this.incomingFundsSubscription) this.incomingFundsSubscription();
|
|
19393
|
-
this.incomingFundsSubscription = void 0;
|
|
19394
19538
|
this.incomingFundsEpoch++;
|
|
19395
19539
|
const emitter = this.newEmitter("incomingFunds");
|
|
19540
|
+
const provider = wallet.onchainProvider ?? this;
|
|
19541
|
+
const previousRegistration = this.incomingFundsRegistrations.get(provider) ?? Promise.resolve();
|
|
19542
|
+
const registration = previousRegistration.then(
|
|
19543
|
+
() => this.registerIncomingFunds(wallet, emitter)
|
|
19544
|
+
);
|
|
19545
|
+
this.incomingFundsRegistrations.set(
|
|
19546
|
+
provider,
|
|
19547
|
+
registration.catch((error) => {
|
|
19548
|
+
console.error("Error subscribing to incoming funds:", error);
|
|
19549
|
+
})
|
|
19550
|
+
);
|
|
19551
|
+
if (!lazyBoarding) await registration;
|
|
19552
|
+
if (signingWallet && !emitter.stale) {
|
|
19553
|
+
try {
|
|
19554
|
+
await signingWallet.getVtxoManager();
|
|
19555
|
+
} catch (error) {
|
|
19556
|
+
console.error("Error starting VtxoManager:", error);
|
|
19557
|
+
}
|
|
19558
|
+
}
|
|
19559
|
+
}
|
|
19560
|
+
async registerIncomingFunds(wallet, emitter) {
|
|
19561
|
+
if (emitter.stale) return;
|
|
19562
|
+
if (this.incomingFundsSubscription) this.incomingFundsSubscription();
|
|
19563
|
+
this.incomingFundsSubscription = void 0;
|
|
19396
19564
|
const address = await wallet.getAddress();
|
|
19565
|
+
if (emitter.stale) return;
|
|
19397
19566
|
const unsubscribe = await wallet.notifyIncomingFunds(async (funds) => {
|
|
19398
19567
|
if (emitter.stale) return;
|
|
19399
19568
|
if (funds.type === "vtxo") {
|
|
@@ -19462,13 +19631,6 @@ var WalletMessageHandler = class {
|
|
|
19462
19631
|
});
|
|
19463
19632
|
if (emitter.stale) return unsubscribe();
|
|
19464
19633
|
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
19634
|
}
|
|
19473
19635
|
/**
|
|
19474
19636
|
* Refresh boarding inputs and transaction history. The virtual outputs come
|
|
@@ -19497,24 +19659,30 @@ var WalletMessageHandler = class {
|
|
|
19497
19659
|
payload: { coins }
|
|
19498
19660
|
})
|
|
19499
19661
|
);
|
|
19500
|
-
await this.
|
|
19662
|
+
await this.storeHistoryCache(await this.loadHistoryCache(), emitter);
|
|
19501
19663
|
})().catch((error) => {
|
|
19502
19664
|
console.warn("[wallet] background refresh failed", error);
|
|
19503
19665
|
});
|
|
19504
19666
|
return;
|
|
19505
19667
|
}
|
|
19668
|
+
const history = this.loadHistoryCache();
|
|
19669
|
+
void history.catch(() => void 0);
|
|
19506
19670
|
await this.refreshBoardingCache();
|
|
19507
19671
|
this.boardingLoaded = true;
|
|
19508
|
-
await this.
|
|
19672
|
+
await this.storeHistoryCache(await history);
|
|
19509
19673
|
}
|
|
19510
|
-
/**
|
|
19511
|
-
async
|
|
19674
|
+
/** History rows plus the address they are stored under. Does not write. */
|
|
19675
|
+
async loadHistoryCache() {
|
|
19512
19676
|
const wallet = this.readonlyWallet;
|
|
19513
|
-
|
|
19514
|
-
if (!wallet || !repository) return;
|
|
19677
|
+
if (!wallet || !this.walletRepository) return void 0;
|
|
19515
19678
|
const address = await wallet.getAddress();
|
|
19516
19679
|
const txs = await this.buildTransactionHistoryFromCache();
|
|
19517
|
-
if (txs
|
|
19680
|
+
if (!txs) return void 0;
|
|
19681
|
+
return { address, txs };
|
|
19682
|
+
}
|
|
19683
|
+
async storeHistoryCache(history, gen) {
|
|
19684
|
+
if (!history || gen?.stale || !this.walletRepository) return;
|
|
19685
|
+
await this.walletRepository.saveTransactions(history.address, history.txs);
|
|
19518
19686
|
}
|
|
19519
19687
|
/**
|
|
19520
19688
|
* @param gen - set by a background caller; each write here is skipped once it goes stale.
|
|
@@ -19529,11 +19697,17 @@ var WalletMessageHandler = class {
|
|
|
19529
19697
|
hex.encode(wallet.boardingTapscript.options.serverPubKey)
|
|
19530
19698
|
);
|
|
19531
19699
|
const groups = await wallet.getBoardingUtxosForSigners(/* @__PURE__ */ new Set([currentSigner]));
|
|
19532
|
-
|
|
19700
|
+
if (gen?.stale) return;
|
|
19701
|
+
const cachedByAddress = await Promise.all(
|
|
19702
|
+
groups.map(
|
|
19703
|
+
({ tapscript }) => collectUtxos(repository, tapscript.onchainAddress(wallet.network))
|
|
19704
|
+
)
|
|
19705
|
+
);
|
|
19706
|
+
for (const [i, { tapscript, coins }] of groups.entries()) {
|
|
19533
19707
|
if (gen?.stale) return;
|
|
19534
19708
|
const address = tapscript.onchainAddress(wallet.network);
|
|
19535
19709
|
const freshKeys = new Set(coins.map((coin) => `${coin.txid}:${coin.vout}`));
|
|
19536
|
-
const cached =
|
|
19710
|
+
const cached = cachedByAddress[i];
|
|
19537
19711
|
const kept = cached.filter((coin) => freshKeys.has(`${coin.txid}:${coin.vout}`));
|
|
19538
19712
|
if (kept.length === cached.length) continue;
|
|
19539
19713
|
if (gen?.stale) return;
|
|
@@ -19631,6 +19805,7 @@ var WalletMessageHandler = class {
|
|
|
19631
19805
|
const wallet = this.wallet ?? this.readonlyWallet;
|
|
19632
19806
|
if (!wallet) return;
|
|
19633
19807
|
this.requestEpoch++;
|
|
19808
|
+
this.initEpoch++;
|
|
19634
19809
|
this.incomingFundsEpoch++;
|
|
19635
19810
|
this.contractEventsEpoch++;
|
|
19636
19811
|
this.boardingEpoch++;
|
|
@@ -19682,7 +19857,9 @@ var WalletMessageHandler = class {
|
|
|
19682
19857
|
* {@link getVtxosFromRepo}).
|
|
19683
19858
|
*/
|
|
19684
19859
|
async repoSnapshot(options = {}) {
|
|
19685
|
-
|
|
19860
|
+
const repository = this.walletRepository;
|
|
19861
|
+
const wallet = this.readonlyWallet;
|
|
19862
|
+
if (!repository || !wallet) return { snapshot: [], vtxos: [] };
|
|
19686
19863
|
const seen = /* @__PURE__ */ new Set();
|
|
19687
19864
|
const allVtxos = [];
|
|
19688
19865
|
const addVtxos = (vtxos) => {
|
|
@@ -19697,19 +19874,18 @@ var WalletMessageHandler = class {
|
|
|
19697
19874
|
}
|
|
19698
19875
|
return fresh;
|
|
19699
19876
|
};
|
|
19700
|
-
const
|
|
19877
|
+
const addressPromise = wallet.getAddress();
|
|
19878
|
+
void addressPromise.catch(() => void 0);
|
|
19879
|
+
const manager = await wallet.getContractManager();
|
|
19701
19880
|
const contracts = await manager.getContracts();
|
|
19702
|
-
const
|
|
19703
|
-
|
|
19704
|
-
|
|
19705
|
-
|
|
19706
|
-
|
|
19707
|
-
|
|
19708
|
-
|
|
19709
|
-
|
|
19710
|
-
});
|
|
19711
|
-
}
|
|
19712
|
-
const walletAddress = await this.readonlyWallet.getAddress();
|
|
19881
|
+
const eligible = contracts.filter(
|
|
19882
|
+
(contract) => options.includeRecoveryOnly || !isRecoveryOnlyContract(contract)
|
|
19883
|
+
);
|
|
19884
|
+
const listsPromise = Promise.all(
|
|
19885
|
+
eligible.map((contract) => getVtxosForContract(repository, contract, options))
|
|
19886
|
+
);
|
|
19887
|
+
void listsPromise.catch(() => void 0);
|
|
19888
|
+
const walletAddress = await addressPromise;
|
|
19713
19889
|
let walletScript;
|
|
19714
19890
|
try {
|
|
19715
19891
|
walletScript = scriptFromArkAddress(walletAddress);
|
|
@@ -19718,13 +19894,26 @@ var WalletMessageHandler = class {
|
|
|
19718
19894
|
`WalletMessageHandler.getVtxosFromRepo: failed to derive script from wallet address ${walletAddress}: ${e instanceof Error ? e.message : String(e)}`
|
|
19719
19895
|
);
|
|
19720
19896
|
}
|
|
19721
|
-
|
|
19722
|
-
|
|
19723
|
-
|
|
19897
|
+
const [lists, primaryVtxos] = await Promise.all([
|
|
19898
|
+
listsPromise,
|
|
19899
|
+
// Routed through the same helper as the contract buckets rather than reading the
|
|
19900
|
+
// repository directly, so this bucket normalizes too. Anything left here
|
|
19901
|
+
// is outside every contract row, so it carries no contract to judge it
|
|
19902
|
+
// by — the wallet's own receive address, which is never gated.
|
|
19903
|
+
getVtxosForContract(
|
|
19904
|
+
repository,
|
|
19724
19905
|
{ script: walletScript, address: walletAddress },
|
|
19725
19906
|
options
|
|
19726
19907
|
)
|
|
19727
|
-
);
|
|
19908
|
+
]);
|
|
19909
|
+
const snapshot = [];
|
|
19910
|
+
for (const [i, contract] of eligible.entries()) {
|
|
19911
|
+
snapshot.push({
|
|
19912
|
+
contract,
|
|
19913
|
+
vtxos: addVtxos(lists[i])
|
|
19914
|
+
});
|
|
19915
|
+
}
|
|
19916
|
+
addVtxos(primaryVtxos);
|
|
19728
19917
|
return { snapshot, vtxos: allVtxos };
|
|
19729
19918
|
}
|
|
19730
19919
|
/**
|
|
@@ -20427,7 +20616,12 @@ var ServiceWorkerReadonlyWallet = class _ServiceWorkerReadonlyWallet {
|
|
|
20427
20616
|
};
|
|
20428
20617
|
try {
|
|
20429
20618
|
const response = await this.sendMessage(message);
|
|
20430
|
-
|
|
20619
|
+
const balance = response.payload;
|
|
20620
|
+
return {
|
|
20621
|
+
...balance,
|
|
20622
|
+
contractLocked: balance.contractLocked ?? balance.gated,
|
|
20623
|
+
deprecatedSignerLocked: balance.deprecatedSignerLocked ?? balance.pendingRecovery
|
|
20624
|
+
};
|
|
20431
20625
|
} catch (error) {
|
|
20432
20626
|
throw new Error(`Failed to get balance: ${error}`);
|
|
20433
20627
|
}
|
|
@@ -20924,9 +21118,10 @@ var ServiceWorkerReadonlyWallet = class _ServiceWorkerReadonlyWallet {
|
|
|
20924
21118
|
}
|
|
20925
21119
|
};
|
|
20926
21120
|
var ServiceWorkerWallet = class _ServiceWorkerWallet extends ServiceWorkerReadonlyWallet {
|
|
20927
|
-
constructor(serviceWorker, identity, walletRepository, contractRepository, messageTag, hasDelegate) {
|
|
21121
|
+
constructor(serviceWorker, identity, walletRepository, contractRepository, messageTag, hasDelegate, oneShotSubmit) {
|
|
20928
21122
|
super(serviceWorker, identity, walletRepository, contractRepository, messageTag);
|
|
20929
21123
|
this.serviceWorker = serviceWorker;
|
|
21124
|
+
this.oneShotSubmit = oneShotSubmit;
|
|
20930
21125
|
this.identity = identity;
|
|
20931
21126
|
this.walletRepository = walletRepository;
|
|
20932
21127
|
this.contractRepository = contractRepository;
|
|
@@ -20937,6 +21132,7 @@ var ServiceWorkerWallet = class _ServiceWorkerWallet extends ServiceWorkerReadon
|
|
|
20937
21132
|
this.hasDelegate = hasDelegate;
|
|
20938
21133
|
}
|
|
20939
21134
|
serviceWorker;
|
|
21135
|
+
oneShotSubmit;
|
|
20940
21136
|
walletRepository;
|
|
20941
21137
|
contractRepository;
|
|
20942
21138
|
identity;
|
|
@@ -20973,7 +21169,8 @@ var ServiceWorkerWallet = class _ServiceWorkerWallet extends ServiceWorkerReadon
|
|
|
20973
21169
|
walletRepository,
|
|
20974
21170
|
contractRepository,
|
|
20975
21171
|
messageTag,
|
|
20976
|
-
!!options.delegateUrl
|
|
21172
|
+
!!options.delegateUrl,
|
|
21173
|
+
options.oneShotSubmit
|
|
20977
21174
|
);
|
|
20978
21175
|
wallet._concurrentSpending = options.concurrentSpending === true;
|
|
20979
21176
|
return _ServiceWorkerWallet.bootstrap(wallet, options, serializedWallet, {
|
|
@@ -20984,6 +21181,7 @@ var ServiceWorkerWallet = class _ServiceWorkerWallet extends ServiceWorkerReadon
|
|
|
20984
21181
|
lazyInitialization: options.lazyInitialization,
|
|
20985
21182
|
watcherConfig: options.watcherConfig,
|
|
20986
21183
|
lookAheadWindow: options.lookAheadWindow,
|
|
21184
|
+
oneShotSubmit: options.oneShotSubmit,
|
|
20987
21185
|
minBatchExpirySeconds: options.minBatchExpirySeconds
|
|
20988
21186
|
});
|
|
20989
21187
|
}
|
|
@@ -22621,8 +22819,12 @@ async function attachPrevoutTxs(tx, source) {
|
|
|
22621
22819
|
}
|
|
22622
22820
|
if (pending.length === 0) return;
|
|
22623
22821
|
const resolved = /* @__PURE__ */ new Map();
|
|
22624
|
-
|
|
22625
|
-
|
|
22822
|
+
const txids = [...new Set(pending.map((p) => p.txid))];
|
|
22823
|
+
const fetched = await Promise.allSettled(txids.map((txid) => source.getRawTransaction(txid)));
|
|
22824
|
+
for (let i = 0; i < fetched.length; i++) {
|
|
22825
|
+
const result = fetched[i];
|
|
22826
|
+
if (result.status === "rejected") throw result.reason;
|
|
22827
|
+
resolved.set(txids[i], result.value);
|
|
22626
22828
|
}
|
|
22627
22829
|
for (const { index, txid } of pending) {
|
|
22628
22830
|
setArkPsbtField(tx, index, PrevoutTxField, resolved.get(txid));
|
|
@@ -23076,40 +23278,53 @@ var Unroll;
|
|
|
23076
23278
|
Unroll2.completeUnroll = completeUnroll;
|
|
23077
23279
|
})(Unroll || (Unroll = {}));
|
|
23078
23280
|
async function prepareUnrollTransaction(wallet, vtxoTxIds, outputAddress) {
|
|
23079
|
-
const chainTip = await
|
|
23080
|
-
|
|
23081
|
-
|
|
23281
|
+
const [chainTip, storedVtxos, walletDescriptor] = await Promise.all([
|
|
23282
|
+
wallet.onchainProvider.getChainTip(),
|
|
23283
|
+
wallet.getVtxos({ withUnrolled: true }),
|
|
23284
|
+
wallet.identity.xOnlyPublicKey().then((key) => hex.encode(key))
|
|
23285
|
+
]);
|
|
23286
|
+
const vtxos = storedVtxos.filter((vtxo) => vtxoTxIds.includes(vtxo.txid));
|
|
23082
23287
|
if (vtxos.length === 0) {
|
|
23083
23288
|
throw new Error("No vtxos to complete unroll");
|
|
23084
23289
|
}
|
|
23085
23290
|
const inputs = [];
|
|
23086
23291
|
let totalAmount = 0n;
|
|
23087
23292
|
const txWeightEstimator = TxWeightEstimator.create();
|
|
23088
|
-
const walletDescriptor = hex.encode(await wallet.identity.xOnlyPublicKey());
|
|
23089
23293
|
for (const vtxo of vtxos) {
|
|
23090
23294
|
if (!canSweepOnchain(vtxo)) {
|
|
23091
23295
|
throw new Error(
|
|
23092
23296
|
`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
23297
|
);
|
|
23094
23298
|
}
|
|
23095
|
-
|
|
23096
|
-
|
|
23097
|
-
|
|
23098
|
-
|
|
23099
|
-
|
|
23100
|
-
|
|
23101
|
-
|
|
23102
|
-
|
|
23103
|
-
|
|
23104
|
-
|
|
23105
|
-
|
|
23106
|
-
|
|
23107
|
-
|
|
23108
|
-
|
|
23109
|
-
|
|
23110
|
-
|
|
23111
|
-
|
|
23112
|
-
|
|
23299
|
+
}
|
|
23300
|
+
const prepared = await Promise.allSettled(
|
|
23301
|
+
vtxos.map(async (vtxo) => {
|
|
23302
|
+
const [txStatus, resolved] = await Promise.all([
|
|
23303
|
+
wallet.onchainProvider.getTxStatus(vtxo.txid),
|
|
23304
|
+
resolveUnilateralPath({
|
|
23305
|
+
vtxo,
|
|
23306
|
+
scriptHex: hex.encode(VtxoScript.decode(vtxo.tapTree).pkScript),
|
|
23307
|
+
contractRepository: wallet.contractRepository,
|
|
23308
|
+
walletDescriptor,
|
|
23309
|
+
currentTime: Date.now()
|
|
23310
|
+
})
|
|
23311
|
+
]);
|
|
23312
|
+
if (!txStatus.confirmed) {
|
|
23313
|
+
throw new Error(`tx ${vtxo.txid} is not confirmed`);
|
|
23314
|
+
}
|
|
23315
|
+
const spendingLeaf = resolved.selection.leaf;
|
|
23316
|
+
const sequence = resolved.selection.sequence;
|
|
23317
|
+
const timelock = sequenceToTimelock(sequence);
|
|
23318
|
+
const elapsed = timelock.type === "blocks" ? chainTip.height >= txStatus.blockHeight + Number(timelock.value) : chainTip.time >= txStatus.blockTime + Number(timelock.value);
|
|
23319
|
+
if (!elapsed) {
|
|
23320
|
+
throw new Error(`no available exit path found for vtxo ${vtxo.txid}:${vtxo.vout}`);
|
|
23321
|
+
}
|
|
23322
|
+
return { vtxo, spendingLeaf, sequence };
|
|
23323
|
+
})
|
|
23324
|
+
);
|
|
23325
|
+
for (const result of prepared) {
|
|
23326
|
+
if (result.status === "rejected") throw result.reason;
|
|
23327
|
+
const { vtxo, spendingLeaf, sequence } = result.value;
|
|
23113
23328
|
totalAmount += BigInt(vtxo.value);
|
|
23114
23329
|
inputs.push({
|
|
23115
23330
|
txid: vtxo.txid,
|
|
@@ -23267,15 +23482,18 @@ async function resolveFeeRate(opts) {
|
|
|
23267
23482
|
async function selectExitVtxos(opts) {
|
|
23268
23483
|
if (!opts.vtxos) return opts.wallet.getVtxos();
|
|
23269
23484
|
const res = await getNormalizedVtxos(opts.wallet.indexerProvider, { outpoints: opts.vtxos });
|
|
23485
|
+
const scripts = [
|
|
23486
|
+
...new Set(res.vtxos.filter((vtxo) => !vtxo.isSpent).map((vtxo) => vtxo.script))
|
|
23487
|
+
];
|
|
23488
|
+
const contracts = scripts.length === 0 ? [] : await collectContracts(opts.wallet.contractRepository, { script: scripts });
|
|
23489
|
+
const byScript = new Map(contracts.map((contract) => [contract.script, contract]));
|
|
23270
23490
|
const tapTrees = /* @__PURE__ */ new Map();
|
|
23271
23491
|
const out = [];
|
|
23272
23492
|
for (const vtxo of res.vtxos) {
|
|
23273
23493
|
if (vtxo.isSpent) continue;
|
|
23274
23494
|
let tapTree = tapTrees.get(vtxo.script);
|
|
23275
23495
|
if (!tapTree) {
|
|
23276
|
-
const
|
|
23277
|
-
script: vtxo.script
|
|
23278
|
-
});
|
|
23496
|
+
const contract = byScript.get(vtxo.script);
|
|
23279
23497
|
if (!contract) {
|
|
23280
23498
|
throw new Error(
|
|
23281
23499
|
`no contract registered for vtxo script ${vtxo.script} \u2014 register the contract before exiting`
|
|
@@ -23294,6 +23512,10 @@ async function selectExitVtxos(opts) {
|
|
|
23294
23512
|
}
|
|
23295
23513
|
async function computeExitLayout(opts, feeRate) {
|
|
23296
23514
|
const { wallet, onchainWallet } = opts;
|
|
23515
|
+
const coinsPromise = onchainWallet.getCoins();
|
|
23516
|
+
const pubkeyPromise = wallet.identity.xOnlyPublicKey();
|
|
23517
|
+
void coinsPromise.catch(() => void 0);
|
|
23518
|
+
void pubkeyPromise.catch(() => void 0);
|
|
23297
23519
|
const vtxos = await selectExitVtxos(opts);
|
|
23298
23520
|
if (vtxos.length === 0) throw new Error("no vtxos to exit");
|
|
23299
23521
|
const resolver = createExitChainResolver({
|
|
@@ -23331,46 +23553,57 @@ async function computeExitLayout(opts, feeRate) {
|
|
|
23331
23553
|
(s) => s.parent.id,
|
|
23332
23554
|
(s) => txInputTxids(s.parent)
|
|
23333
23555
|
);
|
|
23334
|
-
const walletDescriptor = hex.encode(await
|
|
23556
|
+
const walletDescriptor = hex.encode(await pubkeyPromise);
|
|
23557
|
+
const resolvedPaths = await Promise.allSettled(
|
|
23558
|
+
vtxos.map(async (vtxo) => {
|
|
23559
|
+
try {
|
|
23560
|
+
return {
|
|
23561
|
+
vtxo,
|
|
23562
|
+
resolved: await resolveUnilateralPath({
|
|
23563
|
+
vtxo,
|
|
23564
|
+
scriptHex: hex.encode(VtxoScript.decode(vtxo.tapTree).pkScript),
|
|
23565
|
+
contractRepository: wallet.contractRepository,
|
|
23566
|
+
walletDescriptor,
|
|
23567
|
+
currentTime: Date.now()
|
|
23568
|
+
})
|
|
23569
|
+
};
|
|
23570
|
+
} catch (e) {
|
|
23571
|
+
if (e instanceof ExitPathError) return { vtxo, skipped: e.message };
|
|
23572
|
+
throw e;
|
|
23573
|
+
}
|
|
23574
|
+
})
|
|
23575
|
+
);
|
|
23335
23576
|
const infos = [];
|
|
23336
23577
|
const sweeps = [];
|
|
23337
|
-
for (const
|
|
23578
|
+
for (const result of resolvedPaths) {
|
|
23579
|
+
if (result.status === "rejected") throw result.reason;
|
|
23580
|
+
const { vtxo } = result.value;
|
|
23338
23581
|
const outpoint = `${vtxo.txid}:${vtxo.vout}`;
|
|
23339
|
-
|
|
23340
|
-
|
|
23341
|
-
|
|
23342
|
-
|
|
23343
|
-
|
|
23344
|
-
|
|
23345
|
-
|
|
23582
|
+
if ("skipped" in result.value) {
|
|
23583
|
+
infos.push({ outpoint, value: vtxo.value, skipped: result.value.skipped });
|
|
23584
|
+
continue;
|
|
23585
|
+
}
|
|
23586
|
+
const { resolved } = result.value;
|
|
23587
|
+
const sweepFee = sweepFeeFor(
|
|
23588
|
+
resolved.selection,
|
|
23589
|
+
opts.sweepAddress,
|
|
23590
|
+
wallet.network,
|
|
23591
|
+
feeRate
|
|
23592
|
+
);
|
|
23593
|
+
if (vtxo.value - sweepFee < CHILD_OUTPUT_DUST) {
|
|
23594
|
+
infos.push({
|
|
23595
|
+
outpoint,
|
|
23596
|
+
value: vtxo.value,
|
|
23597
|
+
skipped: `uneconomic: value ${vtxo.value} <= sweep fee + dust`
|
|
23346
23598
|
});
|
|
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;
|
|
23599
|
+
continue;
|
|
23371
23600
|
}
|
|
23601
|
+
const t = sequenceToTimelock(resolved.selection.sequence);
|
|
23602
|
+
const delay = { type: t.type, value: Number(t.value) };
|
|
23603
|
+
infos.push({ outpoint, value: vtxo.value, sweepFee, path: resolved.label, delay });
|
|
23604
|
+
sweeps.push({ vtxo, resolved, sweepFee, delay });
|
|
23372
23605
|
}
|
|
23373
|
-
const coins = (await
|
|
23606
|
+
const coins = (await coinsPromise).filter((c) => c.status.confirmed);
|
|
23374
23607
|
const balance = coins.reduce((sum, c) => sum + c.value, 0);
|
|
23375
23608
|
const fundingTotal = steps.reduce((sum, s) => sum + s.funding, 0);
|
|
23376
23609
|
const splitterFeeFor = (inputCount) => {
|
|
@@ -24578,6 +24811,7 @@ var Arkade = class _Arkade {
|
|
|
24578
24811
|
userKey;
|
|
24579
24812
|
/** The contract manager, when contract persistence is wired up (passed in or owned). */
|
|
24580
24813
|
contractManager;
|
|
24814
|
+
oneShotSubmit;
|
|
24581
24815
|
/** Whether {@link connect} built {@link contractManager}, so {@link dispose} must stop it. */
|
|
24582
24816
|
ownsContractManager;
|
|
24583
24817
|
/** {@link ArkadeContract.on} handles by contract script (hex). */
|
|
@@ -24597,6 +24831,7 @@ var Arkade = class _Arkade {
|
|
|
24597
24831
|
this.identity = fields.identity;
|
|
24598
24832
|
this.userKey = fields.userKey;
|
|
24599
24833
|
this.contractManager = fields.contractManager;
|
|
24834
|
+
this.oneShotSubmit = fields.oneShotSubmit;
|
|
24600
24835
|
this.ownsContractManager = fields.ownsContractManager ?? false;
|
|
24601
24836
|
}
|
|
24602
24837
|
/**
|
|
@@ -24673,7 +24908,8 @@ var Arkade = class _Arkade {
|
|
|
24673
24908
|
identity: opts.identity,
|
|
24674
24909
|
userKey,
|
|
24675
24910
|
contractManager,
|
|
24676
|
-
ownsContractManager
|
|
24911
|
+
ownsContractManager,
|
|
24912
|
+
oneShotSubmit: opts.oneShotSubmit
|
|
24677
24913
|
});
|
|
24678
24914
|
}
|
|
24679
24915
|
/**
|
|
@@ -24770,6 +25006,13 @@ var Arkade = class _Arkade {
|
|
|
24770
25006
|
}
|
|
24771
25007
|
return new ArkadeContract(this, program, args);
|
|
24772
25008
|
}
|
|
25009
|
+
/**
|
|
25010
|
+
* Start a spend with no function of its own: each `.from(coins, contract.unlock.<name>(...))`
|
|
25011
|
+
* names the function its coins are spent through.
|
|
25012
|
+
*/
|
|
25013
|
+
transaction() {
|
|
25014
|
+
return new ArkadeTransactionBuilder(this);
|
|
25015
|
+
}
|
|
24773
25016
|
};
|
|
24774
25017
|
var ArkadeContract = class _ArkadeContract {
|
|
24775
25018
|
constructor(client, program, args = {}, keys) {
|
|
@@ -24831,7 +25074,23 @@ var ArkadeContract = class _ArkadeContract {
|
|
|
24831
25074
|
get functions() {
|
|
24832
25075
|
const out = /* @__PURE__ */ Object.create(null);
|
|
24833
25076
|
for (const fn of this.compiled) {
|
|
24834
|
-
out[fn.name] = (...callArgs) => new ArkadeTransactionBuilder(this,
|
|
25077
|
+
out[fn.name] = (...callArgs) => new ArkadeTransactionBuilder(this.client, {
|
|
25078
|
+
contract: this,
|
|
25079
|
+
fn,
|
|
25080
|
+
args: bindInputs(fn, callArgs)
|
|
25081
|
+
});
|
|
25082
|
+
}
|
|
25083
|
+
return out;
|
|
25084
|
+
}
|
|
25085
|
+
/** Spending paths bound to their arguments, for `.from(coins, contract.unlock.<name>(...))`. */
|
|
25086
|
+
get unlock() {
|
|
25087
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
25088
|
+
for (const fn of this.compiled) {
|
|
25089
|
+
out[fn.name] = (...callArgs) => ({
|
|
25090
|
+
contract: this,
|
|
25091
|
+
fn,
|
|
25092
|
+
args: bindInputs(fn, callArgs)
|
|
25093
|
+
});
|
|
24835
25094
|
}
|
|
24836
25095
|
return out;
|
|
24837
25096
|
}
|
|
@@ -24949,27 +25208,62 @@ var ArkadeContract = class _ArkadeContract {
|
|
|
24949
25208
|
};
|
|
24950
25209
|
var ArkadeTransactionBuilder = class {
|
|
24951
25210
|
/** @internal */
|
|
24952
|
-
constructor(
|
|
24953
|
-
this.
|
|
24954
|
-
this.
|
|
24955
|
-
this.args = args;
|
|
25211
|
+
constructor(client, unlocker) {
|
|
25212
|
+
this.client = client;
|
|
25213
|
+
this.unlocker = unlocker;
|
|
24956
25214
|
}
|
|
24957
|
-
|
|
24958
|
-
|
|
24959
|
-
args;
|
|
25215
|
+
client;
|
|
25216
|
+
unlocker;
|
|
24960
25217
|
outputs = [];
|
|
24961
25218
|
fundingCoins = [];
|
|
24962
25219
|
assetSpecs = [];
|
|
24963
25220
|
extraPackets = [];
|
|
24964
|
-
|
|
25221
|
+
inputs = [];
|
|
24965
25222
|
changeScript;
|
|
24966
|
-
|
|
24967
|
-
|
|
25223
|
+
/**
|
|
25224
|
+
* Spend coins through this builder's function, or through `unlocker`
|
|
25225
|
+
* (`other.unlock.<name>(...args)`, same client), which `ark.transaction()` requires. Calls add
|
|
25226
|
+
* up; contract coins come first, in call order, then `.fund()` inputs.
|
|
25227
|
+
*
|
|
25228
|
+
* Every coin's script sees the same outputs: "output 0 pays X" passes once for all of them.
|
|
25229
|
+
* For one payment per coin, the contract must check `INSPECTNUMINPUTS` or `PUSHCURRENTINPUTINDEX`.
|
|
25230
|
+
*/
|
|
25231
|
+
from(coins, unlocker = this.unlocker) {
|
|
25232
|
+
const list = Array.isArray(coins) ? coins : [coins];
|
|
25233
|
+
if (list.length === 0) throw new Error("from(): no coins given");
|
|
25234
|
+
if (!unlocker) {
|
|
25235
|
+
throw new Error("from(): name the function to spend through: contract.unlock.<name>()");
|
|
25236
|
+
}
|
|
25237
|
+
const { contract, fn } = unlocker;
|
|
25238
|
+
if (contract.client !== this.client) {
|
|
25239
|
+
throw new Error(`from(): ${fn.name} belongs to a contract on another Arkade client`);
|
|
25240
|
+
}
|
|
25241
|
+
const script = hex.encode(contract.pkScript);
|
|
25242
|
+
const taken = new Set(this.inputs.map(({ coin: c }) => `${c.txid}:${c.vout}`));
|
|
25243
|
+
const now = Date.now();
|
|
25244
|
+
for (const coin of list) {
|
|
25245
|
+
const outpoint = `${coin.txid}:${coin.vout}`;
|
|
25246
|
+
if (coin.script && coin.script !== script) {
|
|
25247
|
+
throw new Error(
|
|
25248
|
+
`from(): coin ${outpoint} is not locked by the contract of ${fn.name}`
|
|
25249
|
+
);
|
|
25250
|
+
}
|
|
25251
|
+
if (taken.has(outpoint))
|
|
25252
|
+
throw new Error(`from(): coin ${outpoint} is already an input`);
|
|
25253
|
+
const reason = unspendable(coin, now);
|
|
25254
|
+
if (reason) {
|
|
25255
|
+
throw new Error(
|
|
25256
|
+
`from(): coin ${outpoint} ${reason}; recover it into a batch instead`
|
|
25257
|
+
);
|
|
25258
|
+
}
|
|
25259
|
+
taken.add(outpoint);
|
|
25260
|
+
}
|
|
25261
|
+
this.inputs.push(...list.map((coin) => ({ coin, unlocker })));
|
|
24968
25262
|
return this;
|
|
24969
25263
|
}
|
|
24970
25264
|
/**
|
|
24971
25265
|
* Add extra inputs the caller funds (e.g. a taker's own coins in a swap).
|
|
24972
|
-
* These
|
|
25266
|
+
* These come after the contract coins and are signed with the client identity.
|
|
24973
25267
|
*/
|
|
24974
25268
|
fund(coins) {
|
|
24975
25269
|
this.fundingCoins.push(...coins);
|
|
@@ -25006,23 +25300,39 @@ var ArkadeTransactionBuilder = class {
|
|
|
25006
25300
|
}
|
|
25007
25301
|
/** Assemble the unsigned ark transaction and its checkpoints. */
|
|
25008
25302
|
async build() {
|
|
25009
|
-
|
|
25010
|
-
|
|
25011
|
-
|
|
25012
|
-
|
|
25303
|
+
const { arkTx, checkpoints } = await this.assemble();
|
|
25304
|
+
return { arkTx, checkpoints };
|
|
25305
|
+
}
|
|
25306
|
+
async assemble() {
|
|
25307
|
+
for (const { fn } of this.unlockers()) {
|
|
25308
|
+
if (fn.def.tapscript.csv) {
|
|
25309
|
+
throw new Error(
|
|
25310
|
+
`${fn.name}: CSV leaves require an aged onchain output; the offchain builder creates fresh checkpoints \u2014 use the wallet's unilateral exit flow`
|
|
25311
|
+
);
|
|
25312
|
+
}
|
|
25013
25313
|
}
|
|
25014
25314
|
if (this.outputs.length === 0) {
|
|
25015
25315
|
throw new Error("ArkadeTransactionBuilder: at least one output is required");
|
|
25016
25316
|
}
|
|
25017
25317
|
const outputsSum = this.outputs.reduce((s, o) => s + (o.amount ?? 0n), 0n);
|
|
25018
|
-
|
|
25019
|
-
|
|
25318
|
+
let inputs = [...this.inputs];
|
|
25319
|
+
if (inputs.length === 0) {
|
|
25320
|
+
if (!this.unlocker) {
|
|
25321
|
+
throw new Error(
|
|
25322
|
+
"ArkadeTransactionBuilder: add coins with .from(coins, contract.unlock.<name>())"
|
|
25323
|
+
);
|
|
25324
|
+
}
|
|
25325
|
+
inputs = [
|
|
25326
|
+
{ coin: await this.selectCoin(this.unlocker, outputsSum), unlocker: this.unlocker }
|
|
25327
|
+
];
|
|
25328
|
+
}
|
|
25329
|
+
const coinsSum = inputs.reduce((s, { coin }) => s + BigInt(coin.value), 0n);
|
|
25020
25330
|
const outputs = [...this.outputs];
|
|
25021
25331
|
const fundingSum = this.fundingCoins.reduce((s, f) => s + BigInt(f.value), 0n);
|
|
25022
|
-
const surplus =
|
|
25332
|
+
const surplus = coinsSum + fundingSum - outputsSum;
|
|
25023
25333
|
if (surplus < 0n) {
|
|
25024
25334
|
throw new Error(
|
|
25025
|
-
`ArkadeTransactionBuilder: insufficient inputs \u2014 outputs ${outputsSum} exceed inputs ${
|
|
25335
|
+
`ArkadeTransactionBuilder: insufficient inputs \u2014 outputs ${outputsSum} exceed inputs ${coinsSum + fundingSum}`
|
|
25026
25336
|
);
|
|
25027
25337
|
}
|
|
25028
25338
|
if (surplus > 0n) {
|
|
@@ -25035,23 +25345,23 @@ var ArkadeTransactionBuilder = class {
|
|
|
25035
25345
|
}
|
|
25036
25346
|
const { arkTx, checkpoints } = buildOffchainTx(
|
|
25037
25347
|
[
|
|
25038
|
-
{
|
|
25348
|
+
...inputs.map(({ coin, unlocker }) => ({
|
|
25039
25349
|
txid: coin.txid,
|
|
25040
25350
|
vout: coin.vout,
|
|
25041
25351
|
value: coin.value,
|
|
25042
|
-
tapLeafScript:
|
|
25043
|
-
tapTree:
|
|
25044
|
-
},
|
|
25352
|
+
tapLeafScript: unlocker.fn.tapLeafScript,
|
|
25353
|
+
tapTree: unlocker.contract.tapTree
|
|
25354
|
+
})),
|
|
25045
25355
|
...this.fundingCoins
|
|
25046
25356
|
],
|
|
25047
25357
|
outputs,
|
|
25048
|
-
this.
|
|
25358
|
+
this.client.checkpoint
|
|
25049
25359
|
);
|
|
25050
|
-
|
|
25051
|
-
setArkPsbtField(arkTx,
|
|
25052
|
-
}
|
|
25053
|
-
if (
|
|
25054
|
-
const indexer = this.
|
|
25360
|
+
inputs.forEach(({ coin }, i) => {
|
|
25361
|
+
if (coin.sourceTx) setArkPsbtField(arkTx, i, PrevArkTxField, coin.sourceTx);
|
|
25362
|
+
});
|
|
25363
|
+
if (inputs.some(({ unlocker }) => unlocker.fn.arkadeScript)) {
|
|
25364
|
+
const indexer = this.client.indexer;
|
|
25055
25365
|
if (!indexer) {
|
|
25056
25366
|
throw new PrevTxUnavailableError(
|
|
25057
25367
|
"covenant spends require an `indexer` on the Arkade client to resolve the previous ark tx of each input"
|
|
@@ -25059,56 +25369,76 @@ var ArkadeTransactionBuilder = class {
|
|
|
25059
25369
|
}
|
|
25060
25370
|
await attachPrevArkTxs(
|
|
25061
25371
|
arkTx,
|
|
25062
|
-
[coin
|
|
25372
|
+
[...inputs.map(({ coin }) => coin), ...this.fundingCoins].map((c) => c.txid),
|
|
25063
25373
|
indexer
|
|
25064
25374
|
);
|
|
25065
25375
|
}
|
|
25066
|
-
|
|
25067
|
-
|
|
25068
|
-
|
|
25069
|
-
|
|
25070
|
-
|
|
25376
|
+
inputs.forEach(({ unlocker }, i) => {
|
|
25377
|
+
const condition = (unlocker.fn.def.tapscript.witness ?? []).map(
|
|
25378
|
+
(w) => witnessBytes(unlocker, w)
|
|
25379
|
+
);
|
|
25380
|
+
if (condition.length === 0) return;
|
|
25381
|
+
setArkPsbtField(arkTx, i, ConditionWitness, condition);
|
|
25382
|
+
setArkPsbtField(checkpoints[i], 0, ConditionWitness, condition);
|
|
25383
|
+
});
|
|
25071
25384
|
const packets = [];
|
|
25072
25385
|
if (this.assetSpecs.length > 0) {
|
|
25073
25386
|
packets.push(this.buildAssetPacket());
|
|
25074
25387
|
}
|
|
25075
|
-
const
|
|
25076
|
-
|
|
25077
|
-
|
|
25078
|
-
|
|
25079
|
-
|
|
25080
|
-
|
|
25081
|
-
|
|
25082
|
-
);
|
|
25388
|
+
const entries = inputs.flatMap(({ unlocker }, vin) => {
|
|
25389
|
+
const { arkadeScript, def } = unlocker.fn;
|
|
25390
|
+
if (!arkadeScript) return [];
|
|
25391
|
+
const stack = (def.arkadeScript?.witness ?? []).map((w) => witnessBytes(unlocker, w));
|
|
25392
|
+
return [{ vin, script: arkadeScript, witness: RawWitness.encode(stack) }];
|
|
25393
|
+
});
|
|
25394
|
+
if (entries.length > 0) {
|
|
25395
|
+
packets.push(EmulatorPacket.create(entries));
|
|
25083
25396
|
}
|
|
25084
25397
|
packets.push(...this.extraPackets);
|
|
25085
25398
|
if (packets.length > 0) {
|
|
25086
25399
|
attachExtension(arkTx, packets);
|
|
25087
25400
|
}
|
|
25088
|
-
return { arkTx, checkpoints };
|
|
25401
|
+
return { arkTx, checkpoints, inputs };
|
|
25089
25402
|
}
|
|
25090
25403
|
/** Build, submit and return the finalized transaction. */
|
|
25091
25404
|
async send() {
|
|
25092
|
-
|
|
25093
|
-
|
|
25094
|
-
)) {
|
|
25095
|
-
|
|
25096
|
-
|
|
25097
|
-
)
|
|
25405
|
+
const client = this.client;
|
|
25406
|
+
const userKey = client.userKey;
|
|
25407
|
+
for (const { contract, fn } of this.unlockers()) {
|
|
25408
|
+
if (fn.def.tapscript.signers.some(
|
|
25409
|
+
(signer) => typeof signer === "object" && "tweak" in signer
|
|
25410
|
+
)) {
|
|
25411
|
+
throw new Error(
|
|
25412
|
+
`${fn.name}: covenant-tweaked leaf signing is not supported by Identity; use a signer that supports the covenant tweak with build()`
|
|
25413
|
+
);
|
|
25414
|
+
}
|
|
25415
|
+
const { serverKey } = contract.keys;
|
|
25416
|
+
if (fn.signerKeys.some(
|
|
25417
|
+
(key) => !equalBytes(key, serverKey) && (!userKey || !equalBytes(key, userKey))
|
|
25418
|
+
)) {
|
|
25419
|
+
throw new Error(
|
|
25420
|
+
`${fn.name}: external leaf signatures are required; use build() and sign the PSBT with all required signers`
|
|
25421
|
+
);
|
|
25422
|
+
}
|
|
25098
25423
|
}
|
|
25099
|
-
const {
|
|
25100
|
-
const
|
|
25101
|
-
|
|
25102
|
-
(
|
|
25103
|
-
)
|
|
25424
|
+
const { arkTx, checkpoints, inputs } = await this.assemble();
|
|
25425
|
+
const userInputs = this.userInputIndexes(inputs);
|
|
25426
|
+
const serverOnly = inputs.find(
|
|
25427
|
+
({ unlocker }, i) => !unlocker.fn.arkadeScript && !userInputs.includes(i)
|
|
25428
|
+
);
|
|
25429
|
+
if (serverOnly) {
|
|
25104
25430
|
throw new Error(
|
|
25105
|
-
`${
|
|
25431
|
+
`${serverOnly.unlocker.fn.name}: a leaf without a covenant needs the user's signature, and this one has only the server's`
|
|
25106
25432
|
);
|
|
25107
25433
|
}
|
|
25108
|
-
|
|
25109
|
-
|
|
25110
|
-
|
|
25111
|
-
|
|
25434
|
+
inputs.forEach(({ coin }, vin) => {
|
|
25435
|
+
if (coin.assets?.length && !this.routesAssetsFrom(vin)) {
|
|
25436
|
+
throw new Error(
|
|
25437
|
+
`coin ${coin.txid}:${coin.vout} carries assets; route them with .withAsset() from input ${vin}`
|
|
25438
|
+
);
|
|
25439
|
+
}
|
|
25440
|
+
});
|
|
25441
|
+
if (inputs.some(({ unlocker }) => unlocker.fn.arkadeScript)) {
|
|
25112
25442
|
if (!client.emulator) {
|
|
25113
25443
|
throw new Error("covenant spends require an `emulator` on the Arkade client");
|
|
25114
25444
|
}
|
|
@@ -25116,7 +25446,8 @@ var ArkadeTransactionBuilder = class {
|
|
|
25116
25446
|
inputIndexes: userInputs
|
|
25117
25447
|
});
|
|
25118
25448
|
}
|
|
25119
|
-
|
|
25449
|
+
const identity = client.identity;
|
|
25450
|
+
if (!identity) {
|
|
25120
25451
|
throw new Error("a signing identity is required for non-covenant spends");
|
|
25121
25452
|
}
|
|
25122
25453
|
const ark = client.arkProvider;
|
|
@@ -25125,21 +25456,19 @@ var ArkadeTransactionBuilder = class {
|
|
|
25125
25456
|
"broadcasting requires an `arkade` provider with `submitTx`/`finalizeTx` on the Arkade client"
|
|
25126
25457
|
);
|
|
25127
25458
|
}
|
|
25128
|
-
const
|
|
25129
|
-
|
|
25130
|
-
|
|
25131
|
-
|
|
25132
|
-
|
|
25133
|
-
|
|
25134
|
-
|
|
25135
|
-
|
|
25136
|
-
|
|
25137
|
-
async ({ server }) => base64.encode((await client.identity.sign(server, [0])).toPSBT())
|
|
25138
|
-
)
|
|
25459
|
+
const res = await submitOffchainTx(
|
|
25460
|
+
ark,
|
|
25461
|
+
{ arkTx, checkpoints },
|
|
25462
|
+
{
|
|
25463
|
+
signArkTx: async (tx) => ({ arkTx: await this.signArk(tx, userInputs) }),
|
|
25464
|
+
signCheckpoint: (checkpoint) => identity.sign(checkpoint, [0])
|
|
25465
|
+
},
|
|
25466
|
+
void 0,
|
|
25467
|
+
{ oneShot: client.oneShotSubmit }
|
|
25139
25468
|
);
|
|
25140
|
-
await ark.finalizeTx(res.arkTxid, finalCps);
|
|
25141
25469
|
return {
|
|
25142
25470
|
txid: res.arkTxid,
|
|
25471
|
+
// Required by ArkadeBroadcaster; RestArkProvider.submitTx rejects a response without it.
|
|
25143
25472
|
signedArkTx: res.finalArkTx,
|
|
25144
25473
|
signedCheckpointTxs: res.signedCheckpointTxs
|
|
25145
25474
|
};
|
|
@@ -25147,31 +25476,44 @@ var ArkadeTransactionBuilder = class {
|
|
|
25147
25476
|
/** Sign the client-owned inputs of the ark tx (no-op when there are none). */
|
|
25148
25477
|
async signArk(arkTx, userInputs) {
|
|
25149
25478
|
if (userInputs.length === 0) return arkTx;
|
|
25150
|
-
const { identity } = this.
|
|
25479
|
+
const { identity } = this.client;
|
|
25151
25480
|
if (!identity) {
|
|
25152
25481
|
throw new Error("this spend requires an `identity` to sign its user/funding inputs");
|
|
25153
25482
|
}
|
|
25154
25483
|
return identity.sign(arkTx, userInputs);
|
|
25155
25484
|
}
|
|
25156
|
-
/**
|
|
25157
|
-
|
|
25158
|
-
|
|
25159
|
-
|
|
25160
|
-
|
|
25161
|
-
|
|
25162
|
-
|
|
25163
|
-
|
|
25164
|
-
|
|
25165
|
-
|
|
25485
|
+
/** The functions this spend goes through: the `.from()` ones, else this builder's own. */
|
|
25486
|
+
unlockers() {
|
|
25487
|
+
if (this.inputs.length > 0) return this.inputs.map((i) => i.unlocker);
|
|
25488
|
+
return this.unlocker ? [this.unlocker] : [];
|
|
25489
|
+
}
|
|
25490
|
+
/** Indexes of inputs the client owns and must sign (contract inputs + funded inputs). */
|
|
25491
|
+
userInputIndexes(inputs) {
|
|
25492
|
+
const userKey = this.client.userKey;
|
|
25493
|
+
const idxs = inputs.flatMap(
|
|
25494
|
+
({ unlocker }, i) => userKey && unlocker.fn.signerKeys.some((k) => equalBytes(k, userKey)) ? [i] : []
|
|
25495
|
+
);
|
|
25496
|
+
for (let i = 0; i < this.fundingCoins.length; i++) idxs.push(inputs.length + i);
|
|
25166
25497
|
return idxs;
|
|
25167
25498
|
}
|
|
25168
|
-
async selectCoin(amount) {
|
|
25169
|
-
const
|
|
25499
|
+
async selectCoin(unlocker, amount) {
|
|
25500
|
+
const now = Date.now();
|
|
25501
|
+
const routed = this.routesAssetsFrom(0);
|
|
25502
|
+
const utxos = (await unlocker.contract.getUtxos()).filter(
|
|
25503
|
+
(u) => !unspendable(u, now) && (routed || !u.assets?.length)
|
|
25504
|
+
);
|
|
25170
25505
|
if (utxos.length === 0) throw new Error("no spendable coins for this contract");
|
|
25171
25506
|
const covering = utxos.filter((u) => BigInt(u.value) >= amount).sort((a, b) => a.value - b.value);
|
|
25172
25507
|
if (covering.length > 0) return covering[0];
|
|
25173
25508
|
return [...utxos].sort((a, b) => b.value - a.value)[0];
|
|
25174
25509
|
}
|
|
25510
|
+
/**
|
|
25511
|
+
* Whether input `vin`'s assets are routed: by a `.withAsset()` input, or a caller's asset packet.
|
|
25512
|
+
* A caller's packet counts for every input, unread; arkd checks what it actually routes.
|
|
25513
|
+
*/
|
|
25514
|
+
routesAssetsFrom(vin) {
|
|
25515
|
+
return this.extraPackets.some((p) => p.type() === Packet.PACKET_TYPE) || this.assetSpecs.some((spec) => spec.inputs.some((i) => i.vin === vin));
|
|
25516
|
+
}
|
|
25175
25517
|
buildAssetPacket() {
|
|
25176
25518
|
const groups = this.assetSpecs.map((s) => {
|
|
25177
25519
|
const id = typeof s.assetId === "string" ? AssetId.fromString(s.assetId) : AssetId.fromBytes(s.assetId);
|
|
@@ -25185,10 +25527,15 @@ var ArkadeTransactionBuilder = class {
|
|
|
25185
25527
|
});
|
|
25186
25528
|
return Packet.create(groups);
|
|
25187
25529
|
}
|
|
25188
|
-
witnessBytes(ref) {
|
|
25189
|
-
return witnessRefToBytes(ref, this.args, this.contract.args);
|
|
25190
|
-
}
|
|
25191
25530
|
};
|
|
25531
|
+
function unspendable(coin, now) {
|
|
25532
|
+
if (coin.isSwept) return "was swept";
|
|
25533
|
+
if (coin.expiresAt && now >= new Date(coin.expiresAt).getTime()) return "has expired";
|
|
25534
|
+
return void 0;
|
|
25535
|
+
}
|
|
25536
|
+
function witnessBytes({ contract, args }, ref) {
|
|
25537
|
+
return witnessRefToBytes(ref, args, contract.args);
|
|
25538
|
+
}
|
|
25192
25539
|
function bindInputs(fn, callArgs) {
|
|
25193
25540
|
const inputs = fn.def.inputs ?? [];
|
|
25194
25541
|
if (callArgs.length !== inputs.length) {
|
|
@@ -25688,5 +26035,5 @@ function createDefaultPaymentRouter(wallet) {
|
|
|
25688
26035
|
}
|
|
25689
26036
|
|
|
25690
26037
|
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-
|
|
26038
|
+
//# sourceMappingURL=chunk-HAQAWBYG.js.map
|
|
26039
|
+
//# sourceMappingURL=chunk-HAQAWBYG.js.map
|