@bsv/sdk 1.0.38 → 1.1.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.
Files changed (65) hide show
  1. package/dist/cjs/package.json +1 -1
  2. package/dist/cjs/src/compat/Utxo.js +50 -0
  3. package/dist/cjs/src/compat/Utxo.js.map +1 -0
  4. package/dist/cjs/src/compat/index.js +3 -1
  5. package/dist/cjs/src/compat/index.js.map +1 -1
  6. package/dist/cjs/src/primitives/AESGCM.js +1 -1
  7. package/dist/cjs/src/primitives/AESGCM.js.map +1 -1
  8. package/dist/cjs/src/script/templates/P2PKH.js +0 -1
  9. package/dist/cjs/src/script/templates/P2PKH.js.map +1 -1
  10. package/dist/cjs/src/transaction/Broadcaster.js +15 -0
  11. package/dist/cjs/src/transaction/Broadcaster.js.map +1 -1
  12. package/dist/cjs/src/transaction/Transaction.js +73 -10
  13. package/dist/cjs/src/transaction/Transaction.js.map +1 -1
  14. package/dist/cjs/src/transaction/broadcasters/ARC.js +26 -5
  15. package/dist/cjs/src/transaction/broadcasters/ARC.js.map +1 -1
  16. package/dist/cjs/src/transaction/index.js +4 -1
  17. package/dist/cjs/src/transaction/index.js.map +1 -1
  18. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  19. package/dist/esm/src/compat/Utxo.js +44 -0
  20. package/dist/esm/src/compat/Utxo.js.map +1 -0
  21. package/dist/esm/src/compat/index.js +1 -0
  22. package/dist/esm/src/compat/index.js.map +1 -1
  23. package/dist/esm/src/primitives/AESGCM.js +1 -1
  24. package/dist/esm/src/primitives/AESGCM.js.map +1 -1
  25. package/dist/esm/src/script/templates/P2PKH.js +0 -1
  26. package/dist/esm/src/script/templates/P2PKH.js.map +1 -1
  27. package/dist/esm/src/transaction/Broadcaster.js +12 -1
  28. package/dist/esm/src/transaction/Broadcaster.js.map +1 -1
  29. package/dist/esm/src/transaction/Transaction.js +73 -10
  30. package/dist/esm/src/transaction/Transaction.js.map +1 -1
  31. package/dist/esm/src/transaction/broadcasters/ARC.js +27 -4
  32. package/dist/esm/src/transaction/broadcasters/ARC.js.map +1 -1
  33. package/dist/esm/src/transaction/index.js +1 -0
  34. package/dist/esm/src/transaction/index.js.map +1 -1
  35. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  36. package/dist/types/src/compat/Utxo.d.ts +42 -0
  37. package/dist/types/src/compat/Utxo.d.ts.map +1 -0
  38. package/dist/types/src/compat/index.d.ts +1 -0
  39. package/dist/types/src/compat/index.d.ts.map +1 -1
  40. package/dist/types/src/script/templates/P2PKH.d.ts.map +1 -1
  41. package/dist/types/src/transaction/Broadcaster.d.ts +8 -0
  42. package/dist/types/src/transaction/Broadcaster.d.ts.map +1 -1
  43. package/dist/types/src/transaction/Transaction.d.ts +14 -0
  44. package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
  45. package/dist/types/src/transaction/TransactionInput.d.ts +0 -4
  46. package/dist/types/src/transaction/TransactionInput.d.ts.map +1 -1
  47. package/dist/types/src/transaction/broadcasters/ARC.d.ts +9 -3
  48. package/dist/types/src/transaction/broadcasters/ARC.d.ts.map +1 -1
  49. package/dist/types/src/transaction/index.d.ts +2 -1
  50. package/dist/types/src/transaction/index.d.ts.map +1 -1
  51. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  52. package/docs/compat.md +52 -10
  53. package/docs/transaction.md +82 -3
  54. package/package.json +1 -1
  55. package/src/compat/Utxo.ts +55 -0
  56. package/src/compat/index.ts +1 -0
  57. package/src/primitives/AESGCM.ts +4 -4
  58. package/src/script/templates/P2PKH.ts +0 -1
  59. package/src/transaction/Broadcaster.ts +14 -0
  60. package/src/transaction/Transaction.ts +74 -10
  61. package/src/transaction/TransactionInput.ts +0 -4
  62. package/src/transaction/__tests/Transaction.test.ts +8 -8
  63. package/src/transaction/broadcasters/ARC.ts +35 -7
  64. package/src/transaction/broadcasters/__tests/ARC.test.ts +2 -2
  65. package/src/transaction/index.ts +2 -1
@@ -47,7 +47,6 @@ export default interface TransactionInput {
47
47
  sourceTransaction?: Transaction;
48
48
  sourceTXID?: string;
49
49
  sourceOutputIndex: number;
50
- sourceSatoshis?: number;
51
50
  unlockingScript?: UnlockingScript;
52
51
  unlockingScriptTemplate?: {
53
52
  sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>;
@@ -348,8 +347,10 @@ Configuration options for the ARC broadcaster.
348
347
  ```ts
349
348
  export interface ArcConfig {
350
349
  apiKey?: string;
351
- deploymentId?: string;
352
350
  httpClient?: HttpClient;
351
+ deploymentId?: string;
352
+ callbackUrl?: string;
353
+ callbackToken?: string;
353
354
  headers?: Record<string, string>;
354
355
  }
355
356
  ```
@@ -366,6 +367,22 @@ Authentication token for the ARC API
366
367
  apiKey?: string
367
368
  ```
368
369
 
370
+ #### Property callbackToken
371
+
372
+ default access token for notification callback endpoint. It will be used as a Authorization header for the http callback
373
+
374
+ ```ts
375
+ callbackToken?: string
376
+ ```
377
+
378
+ #### Property callbackUrl
379
+
380
+ notification callback endpoint for proofs and double spend notification
381
+
382
+ ```ts
383
+ callbackUrl?: string
384
+ ```
385
+
369
386
  #### Property deploymentId
370
387
 
371
388
  Deployment id used annotating api calls in XDeployment-ID header - this value will be randomly generated if not set
@@ -376,7 +393,7 @@ deploymentId?: string
376
393
 
377
394
  #### Property headers
378
395
 
379
- The headers to be attached to all tx submissions.
396
+ additional headers to be attached to all tx submissions.
380
397
 
381
398
  ```ts
382
399
  headers?: Record<string, string>
@@ -699,6 +716,8 @@ export default class ARC implements Broadcaster {
699
716
  readonly URL: string;
700
717
  readonly apiKey: string | undefined;
701
718
  readonly deploymentId: string;
719
+ readonly callbackUrl: string | undefined;
720
+ readonly callbackToken: string | undefined;
702
721
  readonly headers: Record<string, string> | undefined;
703
722
  constructor(URL: string, config?: ArcConfig);
704
723
  constructor(URL: string, apiKey?: string);
@@ -827,6 +846,7 @@ export default class Transaction {
827
846
  metadata: Record<string, any>;
828
847
  merklePath?: MerklePath;
829
848
  static fromBEEF(beef: number[]): Transaction
849
+ static fromEF(ef: number[]): Transaction
830
850
  static parseScriptOffsets(bin: number[]): {
831
851
  inputs: Array<{
832
852
  vin: number;
@@ -841,6 +861,7 @@ export default class Transaction {
841
861
  }
842
862
  static fromBinary(bin: number[]): Transaction
843
863
  static fromHex(hex: string): Transaction
864
+ static fromHexEF(hex: string): Transaction
844
865
  static fromHexBEEF(hex: string): Transaction
845
866
  constructor(version: number = 1, inputs: TransactionInput[] = [], outputs: TransactionOutput[] = [], lockTime: number = 0, metadata: Record<string, any> = {}, merklePath?: MerklePath)
846
867
  addInput(input: TransactionInput): void
@@ -969,6 +990,23 @@ Argument Details
969
990
  + **bin**
970
991
  + The binary array representation of the transaction.
971
992
 
993
+ #### Method fromEF
994
+
995
+ Creates a new transaction, linked to its inputs and their associated merkle paths, from a EF (BRC-30) structure.
996
+
997
+ ```ts
998
+ static fromEF(ef: number[]): Transaction
999
+ ```
1000
+
1001
+ Returns
1002
+
1003
+ An extended transaction, linked to its associated inputs by locking script and satoshis amounts only.
1004
+
1005
+ Argument Details
1006
+
1007
+ + **ef**
1008
+ + A binary representation of a transaction in EF format.
1009
+
972
1010
  #### Method fromHex
973
1011
 
974
1012
  Creates a Transaction instance from a hexadecimal string.
@@ -1003,6 +1041,23 @@ Argument Details
1003
1041
  + **hex**
1004
1042
  + The hexadecimal string representation of the transaction BEEF.
1005
1043
 
1044
+ #### Method fromHexEF
1045
+
1046
+ Creates a Transaction instance from a hexadecimal string encoded EF.
1047
+
1048
+ ```ts
1049
+ static fromHexEF(hex: string): Transaction
1050
+ ```
1051
+
1052
+ Returns
1053
+
1054
+ - A new Transaction instance.
1055
+
1056
+ Argument Details
1057
+
1058
+ + **hex**
1059
+ + The hexadecimal string representation of the transaction EF.
1060
+
1006
1061
  #### Method getFee
1007
1062
 
1008
1063
  Utility method that returns the current fee based on inputs and outputs
@@ -1236,11 +1291,35 @@ Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](
1236
1291
  | [defaultBroadcaster](#function-defaultbroadcaster) |
1237
1292
  | [defaultChainTracker](#function-defaultchaintracker) |
1238
1293
  | [defaultHttpClient](#function-defaulthttpclient) |
1294
+ | [isBroadcastFailure](#function-isbroadcastfailure) |
1295
+ | [isBroadcastResponse](#function-isbroadcastresponse) |
1296
+
1297
+ Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
1298
+
1299
+ ---
1300
+
1301
+ ### Function: isBroadcastResponse
1302
+
1303
+ Convenience type guard for response from `Broadcaster.broadcast`
1304
+
1305
+ ```ts
1306
+ export function isBroadcastResponse(r: BroadcastResponse | BroadcastFailure): r is BroadcastResponse
1307
+ ```
1239
1308
 
1240
1309
  Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
1241
1310
 
1242
1311
  ---
1312
+ ### Function: isBroadcastFailure
1313
+
1314
+ Convenience type guard for response from `Broadcaster.broadcast`
1243
1315
 
1316
+ ```ts
1317
+ export function isBroadcastFailure(r: BroadcastResponse | BroadcastFailure): r is BroadcastFailure
1318
+ ```
1319
+
1320
+ Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
1321
+
1322
+ ---
1244
1323
  ### Function: defaultHttpClient
1245
1324
 
1246
1325
  Returns a default HttpClient implementation based on the environment that it is run on.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/sdk",
3
- "version": "1.0.38",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "BSV Blockchain Software Development Kit",
6
6
  "main": "dist/cjs/mod.js",
@@ -0,0 +1,55 @@
1
+ import Transaction from '../transaction/Transaction.js'
2
+ import TransactionInput from '../transaction/TransactionInput.js'
3
+ import LockingScript from '../script/LockingScript.js'
4
+ import UnlockingScript from '../script/UnlockingScript.js'
5
+
6
+ type jsonUtxo = {
7
+ txid: string
8
+ vout: number
9
+ satoshis: number
10
+ script: string
11
+ }
12
+ /**
13
+ * @method fromUtxo
14
+ *
15
+ * @description
16
+ * This function creates a transaction input from a utxo json object
17
+ * The idea being old code that uses utxos rather than sourceTranactions can convert using this.
18
+ *
19
+ * @deprecated
20
+ * This approach is made available for compatibility only. It is deprecated in favor of using sourceTransactions
21
+ * directly. It's recommended that wallets general keep transactions which store unspent outputs in their entirety,
22
+ * along with corresonding Merkle paths. The reason you would keep the whole transaction is such that you can prove
23
+ * the txid, and therefore its inclusion within a specific block.
24
+ *
25
+ * @example
26
+ * const i = fromUtxo({
27
+ * txid: '434555433eaca96dff6e71a4d02febd0dd3832e5ca4e5734623ca914522e17d5',
28
+ * vout: 0,
29
+ * script: '51',
30
+ * satoshis: 1234
31
+ * }, new P2PKH().unlock(p))
32
+ *
33
+ * tx.addInput(i)
34
+ *
35
+ * @param utxo: jsonUtxo
36
+ * @param unlockingScriptTemplate: { sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>, estimateLength: (tx: Transaction, inputIndex: number) => Promise<number> }
37
+ * @returns
38
+ */
39
+ export default function fromUtxo(utxo: jsonUtxo, unlockingScriptTemplate: {
40
+ sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>
41
+ estimateLength: (tx: Transaction, inputIndex: number) => Promise<number>
42
+ }): TransactionInput {
43
+ const sourceTransaction = new Transaction(0, [], [], 0)
44
+ sourceTransaction.outputs = Array(utxo.vout + 1).fill(null)
45
+ sourceTransaction.outputs[utxo.vout] = {
46
+ satoshis: utxo.satoshis,
47
+ lockingScript: LockingScript.fromHex(utxo.script)
48
+ }
49
+ return {
50
+ sourceTransaction,
51
+ sourceOutputIndex: utxo.vout,
52
+ unlockingScriptTemplate,
53
+ sequence: 0xFFFFFFFF
54
+ }
55
+ }
@@ -2,3 +2,4 @@ export * as BSM from './BSM.js'
2
2
  export { default as HD } from './HD.js'
3
3
  export { default as Mnemonic } from './Mnemonic.js'
4
4
  export { default as ECIES } from './ECIES.js'
5
+ export { default as fromUtxo } from './Utxo.js'
@@ -180,15 +180,15 @@ export function AES (input: number[], key: number[]): number[] {
180
180
 
181
181
  // Since the BigNumber representation of keys ignores big endian zeroes,
182
182
  // extend incoming key arrays with zeros to the smallest standard key size.
183
- let ekey = Array.from(key)
183
+ const ekey = Array.from(key)
184
184
  if (ekey.length <= 16) {
185
- while (ekey.length < 16) ekey.unshift(0)
185
+ while (ekey.length < 16) ekey.unshift(0)
186
186
  roundLimit = 11
187
187
  } else if (ekey.length <= 24) {
188
- while (ekey.length < 24) ekey.unshift(0)
188
+ while (ekey.length < 24) ekey.unshift(0)
189
189
  roundLimit = 13
190
190
  } else if (key.length <= 32) {
191
- while (ekey.length < 32) ekey.unshift(0)
191
+ while (ekey.length < 32) ekey.unshift(0)
192
192
  roundLimit = 15
193
193
  } else {
194
194
  throw new Error('Illegal key length: ' + String(key.length))
@@ -91,7 +91,6 @@ export default class P2PKH implements ScriptTemplate {
91
91
  )
92
92
  }
93
93
  sourceSatoshis ||= input.sourceTransaction?.outputs[input.sourceOutputIndex].satoshis
94
- sourceSatoshis ||= input.sourceSatoshis
95
94
  if (!sourceSatoshis) {
96
95
  throw new Error(
97
96
  'The sourceSatoshis or input sourceTransaction is required for transaction signing.'
@@ -40,3 +40,17 @@ export interface Broadcaster {
40
40
  broadcast: (transaction: Transaction) =>
41
41
  Promise<BroadcastResponse | BroadcastFailure>
42
42
  }
43
+
44
+ /**
45
+ * Convenience type guard for response from `Broadcaster.broadcast`
46
+ */
47
+ export function isBroadcastResponse (r: BroadcastResponse | BroadcastFailure): r is BroadcastResponse {
48
+ return r.status === 'success'
49
+ }
50
+
51
+ /**
52
+ * Convenience type guard for response from `Broadcaster.broadcast`
53
+ */
54
+ export function isBroadcastFailure (r: BroadcastResponse | BroadcastFailure): r is BroadcastFailure {
55
+ return r.status === 'error'
56
+ }
@@ -122,6 +122,59 @@ export default class Transaction {
122
122
  return transactions[lastTXID].tx
123
123
  }
124
124
 
125
+
126
+ /**
127
+ * Creates a new transaction, linked to its inputs and their associated merkle paths, from a EF (BRC-30) structure.
128
+ * @param ef A binary representation of a transaction in EF format.
129
+ * @returns An extended transaction, linked to its associated inputs by locking script and satoshis amounts only.
130
+ */
131
+ static fromEF (ef: number[]): Transaction {
132
+ const br = new Reader(ef)
133
+ const version = br.readUInt32LE()
134
+ if (toHex(br.read(6)) !== '0000000000ef') throw new Error('Invalid EF marker')
135
+ const inputsLength = br.readVarIntNum()
136
+ const inputs: TransactionInput[] = []
137
+ for (let i = 0; i < inputsLength; i++) {
138
+ const sourceTXID = toHex(br.readReverse(32))
139
+ const sourceOutputIndex = br.readUInt32LE()
140
+ const scriptLength = br.readVarIntNum()
141
+ const scriptBin = br.read(scriptLength)
142
+ const unlockingScript = UnlockingScript.fromBinary(scriptBin)
143
+ const sequence = br.readUInt32LE()
144
+ const satoshis = br.readUInt64LEBn().toNumber()
145
+ const lockingScriptLength = br.readVarIntNum()
146
+ const lockingScriptBin = br.read(lockingScriptLength)
147
+ const lockingScript = LockingScript.fromBinary(lockingScriptBin)
148
+ const sourceTransaction = new Transaction(null, [], [], null)
149
+ sourceTransaction.outputs = Array(sourceOutputIndex + 1).fill(null)
150
+ sourceTransaction.outputs[sourceOutputIndex] = {
151
+ satoshis,
152
+ lockingScript
153
+ }
154
+ inputs.push({
155
+ sourceTransaction,
156
+ sourceTXID,
157
+ sourceOutputIndex,
158
+ unlockingScript,
159
+ sequence
160
+ })
161
+ }
162
+ const outputsLength = br.readVarIntNum()
163
+ const outputs: TransactionOutput[] = []
164
+ for (let i = 0; i < outputsLength; i++) {
165
+ const satoshis = br.readUInt64LEBn().toNumber()
166
+ const scriptLength = br.readVarIntNum()
167
+ const scriptBin = br.read(scriptLength)
168
+ const lockingScript = LockingScript.fromBinary(scriptBin)
169
+ outputs.push({
170
+ satoshis,
171
+ lockingScript
172
+ })
173
+ }
174
+ const lockTime = br.readUInt32LE()
175
+ return new Transaction(version, inputs, outputs, lockTime)
176
+ }
177
+
125
178
  /**
126
179
  * Since the validation of blockchain data is atomically transaction data validation,
127
180
  * any application seeking to validate data in output scripts must store the entire transaction as well.
@@ -220,6 +273,17 @@ export default class Transaction {
220
273
  return Transaction.fromBinary(toArray(hex, 'hex'))
221
274
  }
222
275
 
276
+ /**
277
+ * Creates a Transaction instance from a hexadecimal string encoded EF.
278
+ *
279
+ * @static
280
+ * @param {string} hex - The hexadecimal string representation of the transaction EF.
281
+ * @returns {Transaction} - A new Transaction instance.
282
+ */
283
+ static fromHexEF (hex: string): Transaction {
284
+ return Transaction.fromEF(toArray(hex, 'hex'))
285
+ }
286
+
223
287
  /**
224
288
  * Creates a Transaction instance from a hexadecimal string encoded BEEF.
225
289
  *
@@ -316,12 +380,10 @@ export default class Transaction {
316
380
  // change = inputs - fee - non-change outputs
317
381
  let change = 0
318
382
  for (const input of this.inputs) {
319
- if (typeof input.sourceTransaction !== 'object' && typeof input.sourceSatoshis !== 'number') {
320
- throw new Error('Source transactions or sourceSatoshis are required for all inputs during fee computation')
383
+ if (typeof input.sourceTransaction !== 'object') {
384
+ throw new Error('Source transactions are required for all inputs during fee computation')
321
385
  }
322
- change += input.sourceTransaction
323
- ? input.sourceTransaction.outputs[input.sourceOutputIndex].satoshis
324
- : input.sourceSatoshis
386
+ change += input.sourceTransaction.outputs[input.sourceOutputIndex].satoshis
325
387
  }
326
388
  change -= fee
327
389
  let changeCount = 0
@@ -367,12 +429,10 @@ export default class Transaction {
367
429
  getFee (): number {
368
430
  let totalIn = 0
369
431
  for (const input of this.inputs) {
370
- if (typeof input.sourceTransaction !== 'object' && typeof input.sourceSatoshis !== 'number') {
432
+ if (typeof input.sourceTransaction !== 'object') {
371
433
  throw new Error('Source transactions or sourceSatoshis are required for all inputs to calculate fee')
372
434
  }
373
- totalIn += input.sourceTransaction
374
- ? input.sourceTransaction.outputs[input.sourceOutputIndex].satoshis
375
- : input.sourceSatoshis || 0
435
+ totalIn += input.sourceTransaction.outputs[input.sourceOutputIndex].satoshis
376
436
  }
377
437
  let totalOut = 0
378
438
  for (const output of this.outputs) {
@@ -465,7 +525,11 @@ export default class Transaction {
465
525
  if (typeof i.sourceTransaction === 'undefined') {
466
526
  throw new Error('All inputs must have source transactions when serializing to EF format')
467
527
  }
468
- writer.write(i.sourceTransaction.hash() as number[])
528
+ if (typeof i.sourceTXID === 'undefined') {
529
+ writer.write(i.sourceTransaction.hash() as number[])
530
+ } else {
531
+ writer.write(toArray(i.sourceTXID, 'hex').reverse() as number[])
532
+ }
469
533
  writer.writeUInt32LE(i.sourceOutputIndex)
470
534
  const scriptBin = i.unlockingScript.toBinary()
471
535
  writer.writeVarIntNum(scriptBin.length)
@@ -16,9 +16,6 @@ import Transaction from './Transaction.js'
16
16
  * @property {number} sourceOutputIndex - The index of the output in the source transaction
17
17
  * that this input is spending. It is zero-based, indicating the position of the
18
18
  * output in the array of outputs of the source transaction.
19
- * @property {number} [sourceSatoshis] - The amount of satoshis of the source transaction
20
- * output that this input is spending, used for fee calculation and signing when
21
- * source transaction is not present
22
19
  * @property {UnlockingScript} [unlockingScript] - Optional. The script that 'unlocks' the
23
20
  * source output for spending. This script typically contains signatures and
24
21
  * public keys that evidence the ownership of the output.
@@ -57,7 +54,6 @@ export default interface TransactionInput {
57
54
  sourceTransaction?: Transaction
58
55
  sourceTXID?: string
59
56
  sourceOutputIndex: number
60
- sourceSatoshis?: number
61
57
  unlockingScript?: UnlockingScript
62
58
  unlockingScriptTemplate?: {
63
59
  sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>
@@ -9,6 +9,7 @@ import { hash256, hash160 } from '../../../dist/cjs/src/primitives/Hash'
9
9
  import PrivateKey from '../../../dist/cjs/src/primitives/PrivateKey'
10
10
  import Curve from '../../../dist/cjs/src/primitives/Curve'
11
11
  import P2PKH from '../../../dist/cjs/src/script/templates/P2PKH'
12
+ import fromUtxo from '../../../dist/cjs/src/compat/Utxo'
12
13
 
13
14
  import sighashVectors from '../../primitives/__tests/sighash.vectors'
14
15
  import invalidTransactions from './tx.invalid.vectors'
@@ -396,14 +397,13 @@ describe('Transaction', () => {
396
397
  const priv = PrivateKey.fromRandom()
397
398
  const tx = new Transaction()
398
399
  utxos.forEach(utxo => {
399
- const script = new P2PKH().lock(priv.toPublicKey().toHash())
400
- tx.addInput({
401
- sourceTXID: utxo.tx_hash,
402
- sourceOutputIndex: utxo.tx_pos,
403
- sourceSatoshis: utxo.value,
404
- unlockingScriptTemplate: new P2PKH()
405
- .unlock(priv, 'all', false, utxo.value, script)
406
- })
400
+ const u = {
401
+ txid: utxo.tx_hash,
402
+ vout: utxo.tx_pos,
403
+ script: new P2PKH().lock(priv.toPublicKey().toHash()).toHex(),
404
+ satoshis: utxo.value
405
+ }
406
+ tx.addInput(fromUtxo(u, new P2PKH().unlock(priv)))
407
407
  })
408
408
  tx.addOutput({
409
409
  lockingScript: new P2PKH().lock(priv.toAddress()),
@@ -9,11 +9,15 @@ import {toHex} from "../../primitives/utils.js";
9
9
  export interface ArcConfig {
10
10
  /** Authentication token for the ARC API */
11
11
  apiKey?: string
12
- /** Deployment id used annotating api calls in XDeployment-ID header - this value will be randomly generated if not set */
13
- deploymentId?: string
14
12
  /** The HTTP client used to make requests to the ARC API. */
15
13
  httpClient?: HttpClient
16
- /** The headers to be attached to all tx submissions. */
14
+ /** Deployment id used annotating api calls in XDeployment-ID header - this value will be randomly generated if not set */
15
+ deploymentId?: string
16
+ /** notification callback endpoint for proofs and double spend notification */
17
+ callbackUrl?: string
18
+ /** default access token for notification callback endpoint. It will be used as a Authorization header for the http callback */
19
+ callbackToken?: string
20
+ /** additional headers to be attached to all tx submissions. */
17
21
  headers?: Record<string, string>
18
22
  }
19
23
 
@@ -29,6 +33,8 @@ export default class ARC implements Broadcaster {
29
33
  readonly URL: string
30
34
  readonly apiKey: string | undefined
31
35
  readonly deploymentId: string
36
+ readonly callbackUrl: string | undefined
37
+ readonly callbackToken: string | undefined
32
38
  readonly headers: Record<string, string> | undefined
33
39
  private readonly httpClient: HttpClient;
34
40
 
@@ -52,11 +58,16 @@ export default class ARC implements Broadcaster {
52
58
  if (typeof config === 'string') {
53
59
  this.apiKey = config
54
60
  this.httpClient = defaultHttpClient()
61
+ this.deploymentId = defaultDeploymentId()
62
+ this.callbackToken = undefined
63
+ this.callbackUrl = undefined
55
64
  } else {
56
- const {apiKey, deploymentId, headers, httpClient} = config ?? {} as ArcConfig
65
+ const {apiKey, deploymentId, httpClient, callbackToken, callbackUrl, headers } = config ?? {} as ArcConfig
66
+ this.apiKey = apiKey
57
67
  this.httpClient = httpClient ?? defaultHttpClient()
58
68
  this.deploymentId = deploymentId ?? defaultDeploymentId()
59
- this.apiKey = apiKey
69
+ this.callbackToken = callbackToken
70
+ this.callbackUrl = callbackUrl
60
71
  this.headers = headers
61
72
  }
62
73
  }
@@ -95,11 +106,20 @@ export default class ARC implements Broadcaster {
95
106
  message: `${txStatus} ${extraInfo}`
96
107
  }
97
108
  } else {
98
- return {
109
+ const r: BroadcastFailure = {
99
110
  status: 'error',
100
111
  code: response.status.toString() ?? 'ERR_UNKNOWN',
101
- description: response.data?.detail ?? 'Unknown error'
112
+ description: 'Unknown error'
113
+ }
114
+ if (typeof response.data === 'string') {
115
+ try {
116
+ const data = JSON.parse(response.data)
117
+ if (typeof data.detail === 'string') {
118
+ r.description = data.detail
119
+ }
120
+ } catch {}
102
121
  }
122
+ return r
103
123
  }
104
124
  } catch (error) {
105
125
  return {
@@ -122,6 +142,14 @@ export default class ARC implements Broadcaster {
122
142
  headers['Authorization'] = `Bearer ${this.apiKey}`
123
143
  }
124
144
 
145
+ if (this.callbackUrl) {
146
+ headers['X-CallbackUrl'] = this.callbackUrl
147
+ }
148
+
149
+ if (this.callbackToken) {
150
+ headers['X-CallbackToken'] = this.callbackToken
151
+ }
152
+
125
153
  if (!!this.headers) {
126
154
  for (const key in this.headers) {
127
155
  headers[key] = this.headers[key]
@@ -165,9 +165,9 @@ describe('ARC Broadcaster', () => {
165
165
  it('should handle non-200 responses', async () => {
166
166
  const mockFetch = mockedFetch({
167
167
  status: '400',
168
- data: {
168
+ data: JSON.stringify({
169
169
  detail: 'Bad request'
170
- }
170
+ })
171
171
  })
172
172
 
173
173
  const broadcaster = new ARC(URL, {httpClient: new FetchHttpClient(mockFetch)})
@@ -2,5 +2,6 @@ export { default as Transaction } from './Transaction.js'
2
2
  export { default as MerklePath } from './MerklePath.js'
3
3
  export type { default as TransactionInput } from './TransactionInput.js'
4
4
  export type { default as TransactionOutput } from './TransactionOutput.js'
5
- export type * as Broadcaster from './Broadcaster.js'
5
+ export type { Broadcaster, BroadcastFailure, BroadcastResponse } from './Broadcaster.js'
6
+ export { isBroadcastResponse, isBroadcastFailure } from './Broadcaster.js'
6
7
  export type { default as ChainTracker } from './ChainTracker.js'