@arkade-os/sdk 0.3.0-alpha.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -14
- package/dist/cjs/arknote/index.js +3 -3
- package/dist/cjs/forfeit.js +2 -2
- package/dist/cjs/identity/singleKey.js +8 -8
- package/dist/cjs/index.js +13 -5
- package/dist/cjs/{bip322 → intent}/index.js +38 -61
- package/dist/cjs/musig2/index.js +2 -1
- package/dist/cjs/musig2/nonces.js +4 -0
- package/dist/cjs/providers/ark.js +76 -45
- package/dist/cjs/providers/errors.js +59 -0
- package/dist/cjs/providers/expoArk.js +15 -170
- package/dist/cjs/providers/expoIndexer.js +22 -111
- package/dist/cjs/providers/expoUtils.js +124 -0
- package/dist/cjs/providers/onchain.js +19 -20
- package/dist/cjs/repositories/walletRepository.js +64 -28
- package/dist/cjs/script/base.js +15 -7
- package/dist/cjs/script/tapscript.js +20 -21
- package/dist/cjs/script/vhtlc.js +2 -2
- package/dist/cjs/tree/signingSession.js +44 -11
- package/dist/cjs/tree/txTree.js +3 -4
- package/dist/cjs/tree/validation.js +2 -3
- package/dist/cjs/utils/arkTransaction.js +105 -15
- package/dist/cjs/utils/transaction.js +28 -0
- package/dist/cjs/utils/unknownFields.js +7 -7
- package/dist/cjs/wallet/onchain.js +6 -7
- package/dist/cjs/wallet/serviceWorker/response.js +32 -0
- package/dist/cjs/wallet/serviceWorker/utils.js +2 -0
- package/dist/cjs/wallet/serviceWorker/wallet.js +7 -8
- package/dist/cjs/wallet/serviceWorker/worker.js +46 -27
- package/dist/cjs/wallet/unroll.js +7 -9
- package/dist/cjs/wallet/utils.js +9 -0
- package/dist/cjs/wallet/vtxo-manager.js +323 -0
- package/dist/cjs/wallet/wallet.js +98 -125
- package/dist/esm/arknote/index.js +2 -2
- package/dist/esm/forfeit.js +1 -1
- package/dist/esm/identity/singleKey.js +9 -9
- package/dist/esm/index.js +14 -10
- package/dist/esm/{bip322 → intent}/index.js +32 -54
- package/dist/esm/musig2/index.js +1 -1
- package/dist/esm/musig2/nonces.js +3 -0
- package/dist/esm/providers/ark.js +76 -45
- package/dist/esm/providers/errors.js +54 -0
- package/dist/esm/providers/expoArk.js +15 -137
- package/dist/esm/providers/expoIndexer.js +22 -78
- package/dist/esm/providers/expoUtils.js +87 -0
- package/dist/esm/providers/onchain.js +19 -20
- package/dist/esm/repositories/walletRepository.js +64 -28
- package/dist/esm/script/base.js +12 -4
- package/dist/esm/script/tapscript.js +1 -2
- package/dist/esm/script/vhtlc.js +1 -1
- package/dist/esm/tree/signingSession.js +45 -12
- package/dist/esm/tree/txTree.js +3 -4
- package/dist/esm/tree/validation.js +2 -3
- package/dist/esm/utils/arkTransaction.js +97 -8
- package/dist/esm/utils/transaction.js +24 -0
- package/dist/esm/utils/unknownFields.js +3 -3
- package/dist/esm/wallet/onchain.js +3 -4
- package/dist/esm/wallet/serviceWorker/response.js +32 -0
- package/dist/esm/wallet/serviceWorker/utils.js +1 -0
- package/dist/esm/wallet/serviceWorker/wallet.js +8 -9
- package/dist/esm/wallet/serviceWorker/worker.js +48 -29
- package/dist/esm/wallet/unroll.js +5 -7
- package/dist/esm/wallet/utils.js +8 -0
- package/dist/esm/wallet/vtxo-manager.js +317 -0
- package/dist/esm/wallet/wallet.js +92 -119
- package/dist/types/arknote/index.d.ts +1 -1
- package/dist/types/forfeit.d.ts +2 -2
- package/dist/types/identity/index.d.ts +2 -2
- package/dist/types/identity/singleKey.d.ts +2 -2
- package/dist/types/index.d.ts +9 -7
- package/dist/types/intent/index.d.ts +41 -0
- package/dist/types/musig2/index.d.ts +1 -1
- package/dist/types/musig2/nonces.d.ts +1 -0
- package/dist/types/providers/ark.d.ts +62 -26
- package/dist/types/providers/errors.d.ts +13 -0
- package/dist/types/providers/expoIndexer.d.ts +2 -10
- package/dist/types/providers/expoUtils.d.ts +18 -0
- package/dist/types/providers/indexer.d.ts +1 -9
- package/dist/types/providers/onchain.d.ts +6 -2
- package/dist/types/repositories/walletRepository.d.ts +9 -5
- package/dist/types/script/base.d.ts +5 -2
- package/dist/types/tree/signingSession.d.ts +16 -11
- package/dist/types/utils/anchor.d.ts +2 -2
- package/dist/types/utils/arkTransaction.d.ts +12 -4
- package/dist/types/utils/transaction.d.ts +13 -0
- package/dist/types/utils/unknownFields.d.ts +4 -4
- package/dist/types/wallet/index.d.ts +6 -4
- package/dist/types/wallet/onchain.d.ts +1 -1
- package/dist/types/wallet/serviceWorker/response.d.ts +16 -2
- package/dist/types/wallet/serviceWorker/utils.d.ts +1 -0
- package/dist/types/wallet/serviceWorker/wallet.d.ts +2 -2
- package/dist/types/wallet/serviceWorker/worker.d.ts +7 -1
- package/dist/types/wallet/unroll.d.ts +1 -1
- package/dist/types/wallet/utils.d.ts +2 -1
- package/dist/types/wallet/vtxo-manager.d.ts +179 -0
- package/dist/types/wallet/wallet.d.ts +8 -4
- package/package.json +1 -2
- package/dist/cjs/bip322/errors.js +0 -13
- package/dist/esm/bip322/errors.js +0 -9
- package/dist/types/bip322/errors.d.ts +0 -6
- package/dist/types/bip322/index.d.ts +0 -57
|
@@ -1,22 +1,20 @@
|
|
|
1
|
-
import { OP } from "@scure/btc-signer
|
|
2
|
-
import { Transaction, SigHash } from "@scure/btc-signer/transaction.js";
|
|
3
|
-
import { Script } from "@scure/btc-signer/script.js";
|
|
4
|
-
import { ErrMissingData, ErrMissingInputs, ErrMissingWitnessUtxo, } from './errors.js';
|
|
1
|
+
import { OP, Script, SigHash } from "@scure/btc-signer";
|
|
5
2
|
import { schnorr } from "@noble/curves/secp256k1.js";
|
|
6
|
-
import {
|
|
3
|
+
import { Transaction } from '../utils/transaction.js';
|
|
7
4
|
/**
|
|
8
|
-
*
|
|
5
|
+
* Intent proof implementation for Bitcoin message signing.
|
|
9
6
|
*
|
|
10
|
-
*
|
|
7
|
+
* Intent proof defines a standard for signing Bitcoin messages as well as proving
|
|
11
8
|
* ownership of coins. This namespace provides utilities for creating and
|
|
12
|
-
* validating
|
|
9
|
+
* validating Intent proof.
|
|
13
10
|
*
|
|
11
|
+
* it is greatly inspired by BIP322.
|
|
14
12
|
* @see https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki
|
|
15
13
|
*
|
|
16
14
|
* @example
|
|
17
15
|
* ```typescript
|
|
18
|
-
* // Create a
|
|
19
|
-
* const proof =
|
|
16
|
+
* // Create a Intent proof
|
|
17
|
+
* const proof = Intent.create(
|
|
20
18
|
* "Hello Bitcoin!",
|
|
21
19
|
* [input],
|
|
22
20
|
* [output]
|
|
@@ -25,65 +23,46 @@ import { base64 } from "@scure/base";
|
|
|
25
23
|
* // Sign the proof
|
|
26
24
|
* const signedProof = await identity.sign(proof);
|
|
27
25
|
*
|
|
28
|
-
* // Extract the signature
|
|
29
|
-
* const signature = BIP322.signature(signedProof);
|
|
30
|
-
* ```
|
|
31
26
|
*/
|
|
32
|
-
export var
|
|
33
|
-
(function (
|
|
27
|
+
export var Intent;
|
|
28
|
+
(function (Intent) {
|
|
34
29
|
/**
|
|
35
|
-
* Creates a new
|
|
30
|
+
* Creates a new Intent proof unsigned transaction.
|
|
36
31
|
*
|
|
37
32
|
* This function constructs a special transaction that can be signed to prove
|
|
38
33
|
* ownership of VTXOs and UTXOs. The proof includes the message to be
|
|
39
34
|
* signed and the inputs/outputs that demonstrate ownership.
|
|
40
35
|
*
|
|
41
|
-
* @param message - The
|
|
36
|
+
* @param message - The Intent message to be signed
|
|
42
37
|
* @param inputs - Array of transaction inputs to prove ownership of
|
|
43
38
|
* @param outputs - Optional array of transaction outputs
|
|
44
|
-
* @returns An unsigned
|
|
39
|
+
* @returns An unsigned Intent proof transaction
|
|
45
40
|
*/
|
|
46
41
|
function create(message, inputs, outputs = []) {
|
|
47
42
|
if (inputs.length == 0)
|
|
48
|
-
throw
|
|
43
|
+
throw new Error("intent proof requires at least one input");
|
|
49
44
|
if (!validateInputs(inputs))
|
|
50
|
-
throw
|
|
45
|
+
throw new Error("invalid inputs");
|
|
51
46
|
if (!validateOutputs(outputs))
|
|
52
|
-
throw
|
|
47
|
+
throw new Error("invalid outputs");
|
|
53
48
|
// create the initial transaction to spend
|
|
54
49
|
const toSpend = craftToSpendTx(message, inputs[0].witnessUtxo.script);
|
|
55
50
|
// create the transaction to sign
|
|
56
51
|
return craftToSignTx(toSpend, inputs, outputs);
|
|
57
52
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
* Finalizes and extracts the FullProof transaction into a BIP-322 signature.
|
|
61
|
-
*
|
|
62
|
-
* This function takes a signed proof transaction and converts it into a
|
|
63
|
-
* base64-encoded signature string. If the proof's inputs have special
|
|
64
|
-
* spending conditions, a custom finalizer can be provided.
|
|
65
|
-
*
|
|
66
|
-
* @param signedProof - The signed BIP-322 proof transaction
|
|
67
|
-
* @param finalizer - Optional custom finalizer function
|
|
68
|
-
* @returns Base64-encoded BIP-322 signature
|
|
69
|
-
*/
|
|
70
|
-
function signature(signedProof, finalizer = (tx) => tx.finalize()) {
|
|
71
|
-
finalizer(signedProof);
|
|
72
|
-
return base64.encode(signedProof.extract());
|
|
73
|
-
}
|
|
74
|
-
BIP322.signature = signature;
|
|
75
|
-
})(BIP322 || (BIP322 = {}));
|
|
53
|
+
Intent.create = create;
|
|
54
|
+
})(Intent || (Intent = {}));
|
|
76
55
|
const OP_RETURN_EMPTY_PKSCRIPT = new Uint8Array([OP.RETURN]);
|
|
77
56
|
const ZERO_32 = new Uint8Array(32).fill(0);
|
|
78
57
|
const MAX_INDEX = 0xffffffff;
|
|
79
|
-
const
|
|
58
|
+
const TAG_INTENT_PROOF = "ark-intent-proof-message";
|
|
80
59
|
function validateInput(input) {
|
|
81
60
|
if (input.index === undefined)
|
|
82
|
-
throw
|
|
61
|
+
throw new Error("intent proof input requires index");
|
|
83
62
|
if (input.txid === undefined)
|
|
84
|
-
throw
|
|
63
|
+
throw new Error("intent proof input requires txid");
|
|
85
64
|
if (input.witnessUtxo === undefined)
|
|
86
|
-
throw
|
|
65
|
+
throw new Error("intent proof input requires witness utxo");
|
|
87
66
|
return true;
|
|
88
67
|
}
|
|
89
68
|
function validateInputs(inputs) {
|
|
@@ -92,9 +71,9 @@ function validateInputs(inputs) {
|
|
|
92
71
|
}
|
|
93
72
|
function validateOutput(output) {
|
|
94
73
|
if (output.amount === undefined)
|
|
95
|
-
throw
|
|
74
|
+
throw new Error("intent proof output requires amount");
|
|
96
75
|
if (output.script === undefined)
|
|
97
|
-
throw
|
|
76
|
+
throw new Error("intent proof output requires script");
|
|
98
77
|
return true;
|
|
99
78
|
}
|
|
100
79
|
function validateOutputs(outputs) {
|
|
@@ -102,13 +81,10 @@ function validateOutputs(outputs) {
|
|
|
102
81
|
return true;
|
|
103
82
|
}
|
|
104
83
|
// craftToSpendTx creates the initial transaction that will be spent in the proof
|
|
105
|
-
|
|
84
|
+
function craftToSpendTx(message, pkScript) {
|
|
106
85
|
const messageHash = hashMessage(message);
|
|
107
86
|
const tx = new Transaction({
|
|
108
87
|
version: 0,
|
|
109
|
-
allowUnknownOutputs: true,
|
|
110
|
-
allowUnknown: true,
|
|
111
|
-
allowUnknownInputs: true,
|
|
112
88
|
});
|
|
113
89
|
// add input with zero hash and max index
|
|
114
90
|
tx.addInput({
|
|
@@ -131,9 +107,6 @@ function craftToSignTx(toSpend, inputs, outputs) {
|
|
|
131
107
|
const firstInput = inputs[0];
|
|
132
108
|
const tx = new Transaction({
|
|
133
109
|
version: 2,
|
|
134
|
-
allowUnknownOutputs: outputs.length === 0,
|
|
135
|
-
allowUnknown: true,
|
|
136
|
-
allowUnknownInputs: true,
|
|
137
110
|
lockTime: 0,
|
|
138
111
|
});
|
|
139
112
|
// add the first "toSpend" input
|
|
@@ -148,11 +121,16 @@ function craftToSignTx(toSpend, inputs, outputs) {
|
|
|
148
121
|
sighashType: SigHash.ALL,
|
|
149
122
|
});
|
|
150
123
|
// add other inputs
|
|
151
|
-
for (const input of inputs) {
|
|
124
|
+
for (const [i, input] of inputs.entries()) {
|
|
152
125
|
tx.addInput({
|
|
153
126
|
...input,
|
|
154
127
|
sighashType: SigHash.ALL,
|
|
155
128
|
});
|
|
129
|
+
if (input.unknown?.length) {
|
|
130
|
+
tx.updateInput(i + 1, {
|
|
131
|
+
unknown: input.unknown,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
156
134
|
}
|
|
157
135
|
// add the special OP_RETURN output if no outputs are provided
|
|
158
136
|
if (outputs.length === 0) {
|
|
@@ -172,5 +150,5 @@ function craftToSignTx(toSpend, inputs, outputs) {
|
|
|
172
150
|
return tx;
|
|
173
151
|
}
|
|
174
152
|
function hashMessage(message) {
|
|
175
|
-
return schnorr.utils.taggedHash(
|
|
153
|
+
return schnorr.utils.taggedHash(TAG_INTENT_PROOF, new TextEncoder().encode(message));
|
|
176
154
|
}
|
package/dist/esm/musig2/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { hex } from "@scure/base";
|
|
2
2
|
import { eventSourceIterator } from './utils.js';
|
|
3
|
+
import { maybeArkError } from './errors.js';
|
|
3
4
|
export var SettlementEventType;
|
|
4
5
|
(function (SettlementEventType) {
|
|
5
6
|
SettlementEventType["BatchStarted"] = "batch_started";
|
|
@@ -7,7 +8,7 @@ export var SettlementEventType;
|
|
|
7
8
|
SettlementEventType["BatchFinalized"] = "batch_finalized";
|
|
8
9
|
SettlementEventType["BatchFailed"] = "batch_failed";
|
|
9
10
|
SettlementEventType["TreeSigningStarted"] = "tree_signing_started";
|
|
10
|
-
SettlementEventType["
|
|
11
|
+
SettlementEventType["TreeNonces"] = "tree_nonces";
|
|
11
12
|
SettlementEventType["TreeTx"] = "tree_tx";
|
|
12
13
|
SettlementEventType["TreeSignature"] = "tree_signature";
|
|
13
14
|
})(SettlementEventType || (SettlementEventType = {}));
|
|
@@ -28,29 +29,41 @@ export class RestArkProvider {
|
|
|
28
29
|
const url = `${this.serverUrl}/v1/info`;
|
|
29
30
|
const response = await fetch(url);
|
|
30
31
|
if (!response.ok) {
|
|
31
|
-
|
|
32
|
+
const errorText = await response.text();
|
|
33
|
+
handleError(errorText, `Failed to get server info: ${response.statusText}`);
|
|
32
34
|
}
|
|
33
35
|
const fromServer = await response.json();
|
|
34
36
|
return {
|
|
35
|
-
...fromServer,
|
|
36
|
-
vtxoTreeExpiry: BigInt(fromServer.vtxoTreeExpiry ?? 0),
|
|
37
|
-
unilateralExitDelay: BigInt(fromServer.unilateralExitDelay ?? 0),
|
|
38
|
-
roundInterval: BigInt(fromServer.roundInterval ?? 0),
|
|
39
|
-
dust: BigInt(fromServer.dust ?? 0),
|
|
40
|
-
utxoMinAmount: BigInt(fromServer.utxoMinAmount ?? 0),
|
|
41
|
-
utxoMaxAmount: BigInt(fromServer.utxoMaxAmount ?? -1),
|
|
42
|
-
vtxoMinAmount: BigInt(fromServer.vtxoMinAmount ?? 0),
|
|
43
|
-
vtxoMaxAmount: BigInt(fromServer.vtxoMaxAmount ?? -1),
|
|
44
37
|
boardingExitDelay: BigInt(fromServer.boardingExitDelay ?? 0),
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
checkpointTapscript: fromServer.checkpointTapscript ?? "",
|
|
39
|
+
deprecatedSigners: fromServer.deprecatedSigners?.map((signer) => ({
|
|
40
|
+
cutoffDate: BigInt(signer.cutoffDate ?? 0),
|
|
41
|
+
pubkey: signer.pubkey ?? "",
|
|
42
|
+
})) ?? [],
|
|
43
|
+
digest: fromServer.digest ?? "",
|
|
44
|
+
dust: BigInt(fromServer.dust ?? 0),
|
|
45
|
+
fees: fromServer.fees,
|
|
46
|
+
forfeitAddress: fromServer.forfeitAddress ?? "",
|
|
47
|
+
forfeitPubkey: fromServer.forfeitPubkey ?? "",
|
|
48
|
+
network: fromServer.network ?? "",
|
|
49
|
+
scheduledSession: "scheduledSession" in fromServer &&
|
|
50
|
+
fromServer.scheduledSession != null
|
|
47
51
|
? {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
duration: BigInt(fromServer.scheduledSession.duration ?? 0),
|
|
53
|
+
nextStartTime: BigInt(fromServer.scheduledSession.nextStartTime ?? 0),
|
|
54
|
+
nextEndTime: BigInt(fromServer.scheduledSession.nextEndTime ?? 0),
|
|
55
|
+
period: BigInt(fromServer.scheduledSession.period ?? 0),
|
|
52
56
|
}
|
|
53
57
|
: undefined,
|
|
58
|
+
serviceStatus: fromServer.serviceStatus ?? {},
|
|
59
|
+
sessionDuration: BigInt(fromServer.sessionDuration ?? 0),
|
|
60
|
+
signerPubkey: fromServer.signerPubkey ?? "",
|
|
61
|
+
unilateralExitDelay: BigInt(fromServer.unilateralExitDelay ?? 0),
|
|
62
|
+
utxoMaxAmount: BigInt(fromServer.utxoMaxAmount ?? -1),
|
|
63
|
+
utxoMinAmount: BigInt(fromServer.utxoMinAmount ?? 0),
|
|
64
|
+
version: fromServer.version ?? "",
|
|
65
|
+
vtxoMaxAmount: BigInt(fromServer.vtxoMaxAmount ?? -1),
|
|
66
|
+
vtxoMinAmount: BigInt(fromServer.vtxoMinAmount ?? 0),
|
|
54
67
|
};
|
|
55
68
|
}
|
|
56
69
|
async submitTx(signedArkTx, checkpointTxs) {
|
|
@@ -61,22 +74,13 @@ export class RestArkProvider {
|
|
|
61
74
|
"Content-Type": "application/json",
|
|
62
75
|
},
|
|
63
76
|
body: JSON.stringify({
|
|
64
|
-
signedArkTx
|
|
65
|
-
checkpointTxs
|
|
77
|
+
signedArkTx,
|
|
78
|
+
checkpointTxs,
|
|
66
79
|
}),
|
|
67
80
|
});
|
|
68
81
|
if (!response.ok) {
|
|
69
82
|
const errorText = await response.text();
|
|
70
|
-
|
|
71
|
-
const grpcError = JSON.parse(errorText);
|
|
72
|
-
// gRPC errors usually have a message and code field
|
|
73
|
-
throw new Error(`Failed to submit virtual transaction: ${grpcError.message || grpcError.error || errorText}`);
|
|
74
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
75
|
-
}
|
|
76
|
-
catch (_) {
|
|
77
|
-
// If JSON parse fails, use the raw error text
|
|
78
|
-
throw new Error(`Failed to submit virtual transaction: ${errorText}`);
|
|
79
|
-
}
|
|
83
|
+
handleError(errorText, `Failed to submit virtual transaction: ${errorText}`);
|
|
80
84
|
}
|
|
81
85
|
const data = await response.json();
|
|
82
86
|
return {
|
|
@@ -99,7 +103,7 @@ export class RestArkProvider {
|
|
|
99
103
|
});
|
|
100
104
|
if (!response.ok) {
|
|
101
105
|
const errorText = await response.text();
|
|
102
|
-
|
|
106
|
+
handleError(errorText, `Failed to finalize offchain transaction: ${errorText}`);
|
|
103
107
|
}
|
|
104
108
|
}
|
|
105
109
|
async registerIntent(intent) {
|
|
@@ -111,14 +115,14 @@ export class RestArkProvider {
|
|
|
111
115
|
},
|
|
112
116
|
body: JSON.stringify({
|
|
113
117
|
intent: {
|
|
114
|
-
|
|
118
|
+
proof: intent.proof,
|
|
115
119
|
message: intent.message,
|
|
116
120
|
},
|
|
117
121
|
}),
|
|
118
122
|
});
|
|
119
123
|
if (!response.ok) {
|
|
120
124
|
const errorText = await response.text();
|
|
121
|
-
|
|
125
|
+
handleError(errorText, `Failed to register intent: ${errorText}`);
|
|
122
126
|
}
|
|
123
127
|
const data = await response.json();
|
|
124
128
|
return data.intentId;
|
|
@@ -132,14 +136,14 @@ export class RestArkProvider {
|
|
|
132
136
|
},
|
|
133
137
|
body: JSON.stringify({
|
|
134
138
|
proof: {
|
|
135
|
-
|
|
139
|
+
proof: intent.proof,
|
|
136
140
|
message: intent.message,
|
|
137
141
|
},
|
|
138
142
|
}),
|
|
139
143
|
});
|
|
140
144
|
if (!response.ok) {
|
|
141
145
|
const errorText = await response.text();
|
|
142
|
-
|
|
146
|
+
handleError(errorText, `Failed to delete intent: ${errorText}`);
|
|
143
147
|
}
|
|
144
148
|
}
|
|
145
149
|
async confirmRegistration(intentId) {
|
|
@@ -155,7 +159,7 @@ export class RestArkProvider {
|
|
|
155
159
|
});
|
|
156
160
|
if (!response.ok) {
|
|
157
161
|
const errorText = await response.text();
|
|
158
|
-
|
|
162
|
+
handleError(errorText, `Failed to confirm registration: ${errorText}`);
|
|
159
163
|
}
|
|
160
164
|
}
|
|
161
165
|
async submitTreeNonces(batchId, pubkey, nonces) {
|
|
@@ -173,7 +177,7 @@ export class RestArkProvider {
|
|
|
173
177
|
});
|
|
174
178
|
if (!response.ok) {
|
|
175
179
|
const errorText = await response.text();
|
|
176
|
-
|
|
180
|
+
handleError(errorText, `Failed to submit tree nonces: ${errorText}`);
|
|
177
181
|
}
|
|
178
182
|
}
|
|
179
183
|
async submitTreeSignatures(batchId, pubkey, signatures) {
|
|
@@ -191,7 +195,7 @@ export class RestArkProvider {
|
|
|
191
195
|
});
|
|
192
196
|
if (!response.ok) {
|
|
193
197
|
const errorText = await response.text();
|
|
194
|
-
|
|
198
|
+
handleError(errorText, `Failed to submit tree signatures: ${errorText}`);
|
|
195
199
|
}
|
|
196
200
|
}
|
|
197
201
|
async submitSignedForfeitTxs(signedForfeitTxs, signedCommitmentTx) {
|
|
@@ -207,7 +211,8 @@ export class RestArkProvider {
|
|
|
207
211
|
}),
|
|
208
212
|
});
|
|
209
213
|
if (!response.ok) {
|
|
210
|
-
|
|
214
|
+
const errorText = await response.text();
|
|
215
|
+
handleError(errorText, `Failed to submit forfeit transactions: ${response.statusText}`);
|
|
211
216
|
}
|
|
212
217
|
}
|
|
213
218
|
async *getEventStream(signal, topics) {
|
|
@@ -305,6 +310,22 @@ export class RestArkProvider {
|
|
|
305
310
|
}
|
|
306
311
|
}
|
|
307
312
|
}
|
|
313
|
+
async getPendingTxs(intent) {
|
|
314
|
+
const url = `${this.serverUrl}/v1/tx/pending`;
|
|
315
|
+
const response = await fetch(url, {
|
|
316
|
+
method: "POST",
|
|
317
|
+
headers: {
|
|
318
|
+
"Content-Type": "application/json",
|
|
319
|
+
},
|
|
320
|
+
body: JSON.stringify({ intent }),
|
|
321
|
+
});
|
|
322
|
+
if (!response.ok) {
|
|
323
|
+
const errorText = await response.text();
|
|
324
|
+
handleError(errorText, `Failed to get pending transactions: ${errorText}`);
|
|
325
|
+
}
|
|
326
|
+
const data = await response.json();
|
|
327
|
+
return data.pendingTxs;
|
|
328
|
+
}
|
|
308
329
|
parseSettlementEvent(data) {
|
|
309
330
|
// Check for BatchStarted event
|
|
310
331
|
if (data.batchStarted) {
|
|
@@ -350,10 +371,16 @@ export class RestArkProvider {
|
|
|
350
371
|
}
|
|
351
372
|
// Check for TreeNoncesAggregated event
|
|
352
373
|
if (data.treeNoncesAggregated) {
|
|
374
|
+
// skip treeNoncesAggregated event, deprecated
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
if (data.treeNonces) {
|
|
353
378
|
return {
|
|
354
|
-
type: SettlementEventType.
|
|
355
|
-
id: data.
|
|
356
|
-
|
|
379
|
+
type: SettlementEventType.TreeNonces,
|
|
380
|
+
id: data.treeNonces.id,
|
|
381
|
+
topic: data.treeNonces.topic,
|
|
382
|
+
txid: data.treeNonces.txid,
|
|
383
|
+
nonces: decodeMusig2Nonces(data.treeNonces.nonces), // pubkey -> public nonce
|
|
357
384
|
};
|
|
358
385
|
}
|
|
359
386
|
// Check for TreeTx event
|
|
@@ -426,17 +453,16 @@ function encodeMusig2Nonces(nonces) {
|
|
|
426
453
|
for (const [txid, nonce] of nonces) {
|
|
427
454
|
noncesObject[txid] = hex.encode(nonce.pubNonce);
|
|
428
455
|
}
|
|
429
|
-
return
|
|
456
|
+
return noncesObject;
|
|
430
457
|
}
|
|
431
458
|
function encodeMusig2Signatures(signatures) {
|
|
432
459
|
const sigObject = {};
|
|
433
460
|
for (const [txid, sig] of signatures) {
|
|
434
461
|
sigObject[txid] = hex.encode(sig.encode());
|
|
435
462
|
}
|
|
436
|
-
return
|
|
463
|
+
return sigObject;
|
|
437
464
|
}
|
|
438
|
-
function decodeMusig2Nonces(
|
|
439
|
-
const noncesObject = JSON.parse(str);
|
|
465
|
+
function decodeMusig2Nonces(noncesObject) {
|
|
440
466
|
return new Map(Object.entries(noncesObject).map(([txid, nonce]) => {
|
|
441
467
|
if (typeof nonce !== "string") {
|
|
442
468
|
throw new Error("invalid nonce");
|
|
@@ -478,3 +504,8 @@ function mapVtxo(vtxo) {
|
|
|
478
504
|
arkTxid: vtxo.arkTxid,
|
|
479
505
|
};
|
|
480
506
|
}
|
|
507
|
+
function handleError(errorText, defaultMessage) {
|
|
508
|
+
const error = new Error(errorText);
|
|
509
|
+
const arkError = maybeArkError(error);
|
|
510
|
+
throw arkError ?? new Error(defaultMessage);
|
|
511
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export class ArkError extends Error {
|
|
2
|
+
constructor(code, message, name, metadata) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.code = code;
|
|
5
|
+
this.message = message;
|
|
6
|
+
this.name = name;
|
|
7
|
+
this.metadata = metadata;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Try to convert an error to an ArkError class, returning undefined if the error is not an ArkError
|
|
12
|
+
* @param error - The error to parse
|
|
13
|
+
* @returns The parsed ArkError, or undefined if the error is not an ArkError
|
|
14
|
+
*/
|
|
15
|
+
export function maybeArkError(error) {
|
|
16
|
+
try {
|
|
17
|
+
if (!(error instanceof Error))
|
|
18
|
+
return undefined;
|
|
19
|
+
const decoded = JSON.parse(error.message);
|
|
20
|
+
if (!("details" in decoded))
|
|
21
|
+
return undefined;
|
|
22
|
+
if (!Array.isArray(decoded.details))
|
|
23
|
+
return undefined;
|
|
24
|
+
// search for a valid details object with the correct type
|
|
25
|
+
for (const details of decoded.details) {
|
|
26
|
+
if (!("@type" in details))
|
|
27
|
+
continue;
|
|
28
|
+
const type = details["@type"];
|
|
29
|
+
if (type !== "type.googleapis.com/ark.v1.ErrorDetails")
|
|
30
|
+
continue;
|
|
31
|
+
if (!("code" in details))
|
|
32
|
+
continue;
|
|
33
|
+
const code = details.code;
|
|
34
|
+
if (!("message" in details))
|
|
35
|
+
continue;
|
|
36
|
+
const message = details.message;
|
|
37
|
+
if (!("name" in details))
|
|
38
|
+
continue;
|
|
39
|
+
const name = details.name;
|
|
40
|
+
let metadata;
|
|
41
|
+
if ("metadata" in details && isMetadata(details.metadata)) {
|
|
42
|
+
metadata = details.metadata;
|
|
43
|
+
}
|
|
44
|
+
return new ArkError(code, message, name, metadata);
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function isMetadata(value) {
|
|
53
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
54
|
+
}
|