@arkade-os/sdk 0.3.7 → 0.3.8

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.
@@ -138,6 +138,15 @@ class Worker {
138
138
  scripts: [script],
139
139
  });
140
140
  const vtxos = response.vtxos.map((vtxo) => (0, utils_1.extendVirtualCoin)(this.wallet, vtxo));
141
+ try {
142
+ // recover pending transactions
143
+ const { finalized, pending } = await this.wallet.finalizePendingTxs(vtxos.filter((vtxo) => vtxo.virtualStatus.state !== "swept" &&
144
+ vtxo.virtualStatus.state !== "settled"));
145
+ console.info(`Recovered ${finalized.length}/${pending.length} pending transactions: ${finalized.join(", ")}`);
146
+ }
147
+ catch (error) {
148
+ console.error("Error recovering pending transactions:", error);
149
+ }
141
150
  // Get wallet address and save vtxos using unified repository
142
151
  const address = await this.wallet.getAddress();
143
152
  await this.walletRepository.saveVtxos(address, vtxos);
@@ -1011,6 +1011,67 @@ class Wallet {
1011
1011
  message: encodedMessage,
1012
1012
  };
1013
1013
  }
1014
+ async makeGetPendingTxIntentSignature(vtxos) {
1015
+ const inputs = this.prepareIntentProofInputs(vtxos);
1016
+ const message = {
1017
+ type: "get-pending-tx",
1018
+ expire_at: 0,
1019
+ };
1020
+ const encodedMessage = JSON.stringify(message, null, 0);
1021
+ const proof = intent_1.Intent.create(encodedMessage, inputs, []);
1022
+ const signedProof = await this.identity.sign(proof);
1023
+ return {
1024
+ proof: base_1.base64.encode(signedProof.toPSBT()),
1025
+ message: encodedMessage,
1026
+ };
1027
+ }
1028
+ /**
1029
+ * Finalizes pending transactions by retrieving them from the server and finalizing each one.
1030
+ * @param vtxos - Optional list of VTXOs to use instead of retrieving them from the server
1031
+ * @returns Array of transaction IDs that were finalized
1032
+ */
1033
+ async finalizePendingTxs(vtxos) {
1034
+ const MAX_INPUTS_PER_INTENT = 20;
1035
+ if (!vtxos || vtxos.length === 0) {
1036
+ // get non-swept VTXOs, rely on the indexer only in case DB doesn't have the right state
1037
+ const scripts = [base_1.hex.encode(this.offchainTapscript.pkScript)];
1038
+ let { vtxos: fetchedVtxos } = await this.indexerProvider.getVtxos({
1039
+ scripts,
1040
+ });
1041
+ fetchedVtxos = fetchedVtxos.filter((vtxo) => vtxo.virtualStatus.state !== "swept" &&
1042
+ vtxo.virtualStatus.state !== "settled");
1043
+ if (fetchedVtxos.length === 0) {
1044
+ return { finalized: [], pending: [] };
1045
+ }
1046
+ vtxos = fetchedVtxos.map((v) => (0, utils_1.extendVirtualCoin)(this, v));
1047
+ }
1048
+ const finalized = [];
1049
+ const pending = [];
1050
+ for (let i = 0; i < vtxos.length; i += MAX_INPUTS_PER_INTENT) {
1051
+ const batch = vtxos.slice(i, i + MAX_INPUTS_PER_INTENT);
1052
+ const intent = await this.makeGetPendingTxIntentSignature(batch);
1053
+ const pendingTxs = await this.arkProvider.getPendingTxs(intent);
1054
+ // finalize each transaction by signing the checkpoints
1055
+ for (const pendingTx of pendingTxs) {
1056
+ pending.push(pendingTx.arkTxid);
1057
+ try {
1058
+ // sign the checkpoints
1059
+ const finalCheckpoints = await Promise.all(pendingTx.signedCheckpointTxs.map(async (c) => {
1060
+ const tx = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(c));
1061
+ const signedCheckpoint = await this.identity.sign(tx);
1062
+ return base_1.base64.encode(signedCheckpoint.toPSBT());
1063
+ }));
1064
+ await this.arkProvider.finalizeTx(pendingTx.arkTxid, finalCheckpoints);
1065
+ finalized.push(pendingTx.arkTxid);
1066
+ }
1067
+ catch (error) {
1068
+ console.error(`Failed to finalize transaction ${pendingTx.arkTxid}:`, error);
1069
+ // continue with other transactions even if one fails
1070
+ }
1071
+ }
1072
+ }
1073
+ return { finalized, pending };
1074
+ }
1014
1075
  prepareIntentProofInputs(coins) {
1015
1076
  const inputs = [];
1016
1077
  for (const input of coins) {
@@ -135,6 +135,15 @@ export class Worker {
135
135
  scripts: [script],
136
136
  });
137
137
  const vtxos = response.vtxos.map((vtxo) => extendVirtualCoin(this.wallet, vtxo));
138
+ try {
139
+ // recover pending transactions
140
+ const { finalized, pending } = await this.wallet.finalizePendingTxs(vtxos.filter((vtxo) => vtxo.virtualStatus.state !== "swept" &&
141
+ vtxo.virtualStatus.state !== "settled"));
142
+ console.info(`Recovered ${finalized.length}/${pending.length} pending transactions: ${finalized.join(", ")}`);
143
+ }
144
+ catch (error) {
145
+ console.error("Error recovering pending transactions:", error);
146
+ }
138
147
  // Get wallet address and save vtxos using unified repository
139
148
  const address = await this.wallet.getAddress();
140
149
  await this.walletRepository.saveVtxos(address, vtxos);
@@ -974,6 +974,67 @@ export class Wallet {
974
974
  message: encodedMessage,
975
975
  };
976
976
  }
977
+ async makeGetPendingTxIntentSignature(vtxos) {
978
+ const inputs = this.prepareIntentProofInputs(vtxos);
979
+ const message = {
980
+ type: "get-pending-tx",
981
+ expire_at: 0,
982
+ };
983
+ const encodedMessage = JSON.stringify(message, null, 0);
984
+ const proof = Intent.create(encodedMessage, inputs, []);
985
+ const signedProof = await this.identity.sign(proof);
986
+ return {
987
+ proof: base64.encode(signedProof.toPSBT()),
988
+ message: encodedMessage,
989
+ };
990
+ }
991
+ /**
992
+ * Finalizes pending transactions by retrieving them from the server and finalizing each one.
993
+ * @param vtxos - Optional list of VTXOs to use instead of retrieving them from the server
994
+ * @returns Array of transaction IDs that were finalized
995
+ */
996
+ async finalizePendingTxs(vtxos) {
997
+ const MAX_INPUTS_PER_INTENT = 20;
998
+ if (!vtxos || vtxos.length === 0) {
999
+ // get non-swept VTXOs, rely on the indexer only in case DB doesn't have the right state
1000
+ const scripts = [hex.encode(this.offchainTapscript.pkScript)];
1001
+ let { vtxos: fetchedVtxos } = await this.indexerProvider.getVtxos({
1002
+ scripts,
1003
+ });
1004
+ fetchedVtxos = fetchedVtxos.filter((vtxo) => vtxo.virtualStatus.state !== "swept" &&
1005
+ vtxo.virtualStatus.state !== "settled");
1006
+ if (fetchedVtxos.length === 0) {
1007
+ return { finalized: [], pending: [] };
1008
+ }
1009
+ vtxos = fetchedVtxos.map((v) => extendVirtualCoin(this, v));
1010
+ }
1011
+ const finalized = [];
1012
+ const pending = [];
1013
+ for (let i = 0; i < vtxos.length; i += MAX_INPUTS_PER_INTENT) {
1014
+ const batch = vtxos.slice(i, i + MAX_INPUTS_PER_INTENT);
1015
+ const intent = await this.makeGetPendingTxIntentSignature(batch);
1016
+ const pendingTxs = await this.arkProvider.getPendingTxs(intent);
1017
+ // finalize each transaction by signing the checkpoints
1018
+ for (const pendingTx of pendingTxs) {
1019
+ pending.push(pendingTx.arkTxid);
1020
+ try {
1021
+ // sign the checkpoints
1022
+ const finalCheckpoints = await Promise.all(pendingTx.signedCheckpointTxs.map(async (c) => {
1023
+ const tx = Transaction.fromPSBT(base64.decode(c));
1024
+ const signedCheckpoint = await this.identity.sign(tx);
1025
+ return base64.encode(signedCheckpoint.toPSBT());
1026
+ }));
1027
+ await this.arkProvider.finalizeTx(pendingTx.arkTxid, finalCheckpoints);
1028
+ finalized.push(pendingTx.arkTxid);
1029
+ }
1030
+ catch (error) {
1031
+ console.error(`Failed to finalize transaction ${pendingTx.arkTxid}:`, error);
1032
+ // continue with other transactions even if one fails
1033
+ }
1034
+ }
1035
+ }
1036
+ return { finalized, pending };
1037
+ }
977
1038
  prepareIntentProofInputs(coins) {
978
1039
  const inputs = [];
979
1040
  for (const input of coins) {
@@ -97,6 +97,16 @@ export declare class Wallet implements IWallet {
97
97
  safeRegisterIntent(intent: SignedIntent): Promise<string>;
98
98
  makeRegisterIntentSignature(coins: ExtendedCoin[], outputs: TransactionOutput[], onchainOutputsIndexes: number[], cosignerPubKeys: string[]): Promise<SignedIntent>;
99
99
  makeDeleteIntentSignature(coins: ExtendedCoin[]): Promise<SignedIntent>;
100
+ makeGetPendingTxIntentSignature(vtxos: ExtendedVirtualCoin[]): Promise<SignedIntent>;
101
+ /**
102
+ * Finalizes pending transactions by retrieving them from the server and finalizing each one.
103
+ * @param vtxos - Optional list of VTXOs to use instead of retrieving them from the server
104
+ * @returns Array of transaction IDs that were finalized
105
+ */
106
+ finalizePendingTxs(vtxos?: ExtendedVirtualCoin[]): Promise<{
107
+ finalized: string[];
108
+ pending: string[];
109
+ }>;
100
110
  private prepareIntentProofInputs;
101
111
  }
102
112
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkade-os/sdk",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "description": "Bitcoin wallet SDK with Taproot and Ark integration",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",