@bsv/sdk 1.0.31 → 1.0.33
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/dist/cjs/package.json +1 -1
- package/dist/cjs/src/primitives/PublicKey.js +36 -0
- package/dist/cjs/src/primitives/PublicKey.js.map +1 -1
- package/dist/cjs/src/primitives/Signature.js +6 -6
- package/dist/cjs/src/primitives/Signature.js.map +1 -1
- package/dist/cjs/src/script/Script.js.map +1 -1
- package/dist/cjs/src/transaction/MerklePath.js.map +1 -1
- package/dist/cjs/src/transaction/Transaction.js.map +1 -1
- package/dist/cjs/src/transaction/broadcasters/ARC.js +9 -39
- package/dist/cjs/src/transaction/broadcasters/ARC.js.map +1 -1
- package/dist/cjs/src/transaction/broadcasters/DefaultHttpClient.js +33 -0
- package/dist/cjs/src/transaction/broadcasters/DefaultHttpClient.js.map +1 -0
- package/dist/cjs/src/transaction/broadcasters/HttpClient.js +3 -0
- package/dist/cjs/src/transaction/broadcasters/HttpClient.js.map +1 -0
- package/dist/cjs/src/transaction/broadcasters/NodejsHttpClient.js +39 -0
- package/dist/cjs/src/transaction/broadcasters/NodejsHttpClient.js.map +1 -0
- package/dist/cjs/src/transaction/broadcasters/index.js +5 -1
- package/dist/cjs/src/transaction/broadcasters/index.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/primitives/PublicKey.js +36 -0
- package/dist/esm/src/primitives/PublicKey.js.map +1 -1
- package/dist/esm/src/primitives/Signature.js +6 -6
- package/dist/esm/src/primitives/Signature.js.map +1 -1
- package/dist/esm/src/script/Script.js.map +1 -1
- package/dist/esm/src/transaction/MerklePath.js.map +1 -1
- package/dist/esm/src/transaction/Transaction.js.map +1 -1
- package/dist/esm/src/transaction/broadcasters/ARC.js +7 -39
- package/dist/esm/src/transaction/broadcasters/ARC.js.map +1 -1
- package/dist/esm/src/transaction/broadcasters/DefaultHttpClient.js +30 -0
- package/dist/esm/src/transaction/broadcasters/DefaultHttpClient.js.map +1 -0
- package/dist/esm/src/transaction/broadcasters/HttpClient.js +2 -0
- package/dist/esm/src/transaction/broadcasters/HttpClient.js.map +1 -0
- package/dist/esm/src/transaction/broadcasters/NodejsHttpClient.js +36 -0
- package/dist/esm/src/transaction/broadcasters/NodejsHttpClient.js.map +1 -0
- package/dist/esm/src/transaction/broadcasters/index.js +2 -0
- package/dist/esm/src/transaction/broadcasters/index.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/primitives/PublicKey.d.ts +18 -0
- package/dist/types/src/primitives/PublicKey.d.ts.map +1 -1
- package/dist/types/src/primitives/Signature.d.ts +3 -3
- package/dist/types/src/script/Script.d.ts.map +1 -1
- package/dist/types/src/transaction/MerklePath.d.ts.map +1 -1
- package/dist/types/src/transaction/Transaction.d.ts +4 -4
- package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
- package/dist/types/src/transaction/broadcasters/ARC.d.ts +4 -3
- package/dist/types/src/transaction/broadcasters/ARC.d.ts.map +1 -1
- package/dist/types/src/transaction/broadcasters/DefaultHttpClient.d.ts +6 -0
- package/dist/types/src/transaction/broadcasters/DefaultHttpClient.d.ts.map +1 -0
- package/dist/types/src/transaction/broadcasters/HttpClient.d.ts +33 -0
- package/dist/types/src/transaction/broadcasters/HttpClient.d.ts.map +1 -0
- package/dist/types/src/transaction/broadcasters/NodejsHttpClient.d.ts +21 -0
- package/dist/types/src/transaction/broadcasters/NodejsHttpClient.d.ts.map +1 -0
- package/dist/types/src/transaction/broadcasters/index.d.ts +4 -0
- package/dist/types/src/transaction/broadcasters/index.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/docs/examples/EXAMPLE_SIMPLE_TX.md +43 -1
- package/docs/primitives.md +36 -3
- package/docs/transaction.md +48 -0
- package/package.json +1 -1
- package/src/primitives/PublicKey.ts +39 -0
- package/src/primitives/Signature.ts +6 -6
- package/src/script/Script.ts +19 -19
- package/src/transaction/MerklePath.ts +9 -9
- package/src/transaction/Transaction.ts +8 -10
- package/src/transaction/broadcasters/ARC.ts +8 -42
- package/src/transaction/broadcasters/DefaultHttpClient.ts +29 -0
- package/src/transaction/broadcasters/HttpClient.ts +34 -0
- package/src/transaction/broadcasters/NodejsHttpClient.ts +55 -0
- package/src/transaction/broadcasters/__tests/ARC.test.ts +76 -38
- package/src/transaction/broadcasters/index.ts +4 -0
package/src/script/Script.ts
CHANGED
|
@@ -21,7 +21,7 @@ export default class Script {
|
|
|
21
21
|
* @example
|
|
22
22
|
* const script = Script.fromASM("OP_DUP OP_HASH160 abcd... OP_EQUALVERIFY OP_CHECKSIG")
|
|
23
23
|
*/
|
|
24
|
-
static fromASM(asm: string): Script {
|
|
24
|
+
static fromASM (asm: string): Script {
|
|
25
25
|
const chunks: ScriptChunk[] = []
|
|
26
26
|
const tokens = asm.split(' ')
|
|
27
27
|
let i = 0
|
|
@@ -100,7 +100,7 @@ export default class Script {
|
|
|
100
100
|
* @example
|
|
101
101
|
* const script = Script.fromHex("76a9...");
|
|
102
102
|
*/
|
|
103
|
-
static fromHex(hex: string): Script {
|
|
103
|
+
static fromHex (hex: string): Script {
|
|
104
104
|
if (hex.length === 0) return Script.fromBinary([])
|
|
105
105
|
if (hex.length % 2 !== 0) throw new Error('There is an uneven number of characters in the string which suggests it is not hex encoded.')
|
|
106
106
|
if (!/^[0-9a-fA-F]+$/.test(hex)) throw new Error('Some elements in this string are not hex encoded.')
|
|
@@ -115,7 +115,7 @@ export default class Script {
|
|
|
115
115
|
* @example
|
|
116
116
|
* const script = Script.fromBinary([0x76, 0xa9, ...])
|
|
117
117
|
*/
|
|
118
|
-
static fromBinary(bin: number[]): Script {
|
|
118
|
+
static fromBinary (bin: number[]): Script {
|
|
119
119
|
bin = [...bin]
|
|
120
120
|
const chunks: ScriptChunk[] = []
|
|
121
121
|
|
|
@@ -179,7 +179,7 @@ export default class Script {
|
|
|
179
179
|
* Constructs a new Script object.
|
|
180
180
|
* @param chunks=[] - An array of script chunks to directly initialize the script.
|
|
181
181
|
*/
|
|
182
|
-
constructor(chunks: ScriptChunk[] = []) {
|
|
182
|
+
constructor (chunks: ScriptChunk[] = []) {
|
|
183
183
|
this.chunks = chunks
|
|
184
184
|
}
|
|
185
185
|
|
|
@@ -188,7 +188,7 @@ export default class Script {
|
|
|
188
188
|
* Serializes the script to an ASM formatted string.
|
|
189
189
|
* @returns The script in ASM string format.
|
|
190
190
|
*/
|
|
191
|
-
toASM(): string {
|
|
191
|
+
toASM (): string {
|
|
192
192
|
let str = ''
|
|
193
193
|
for (let i = 0; i < this.chunks.length; i++) {
|
|
194
194
|
const chunk = this.chunks[i]
|
|
@@ -203,7 +203,7 @@ export default class Script {
|
|
|
203
203
|
* Serializes the script to a hexadecimal string.
|
|
204
204
|
* @returns The script in hexadecimal format.
|
|
205
205
|
*/
|
|
206
|
-
toHex(): string {
|
|
206
|
+
toHex (): string {
|
|
207
207
|
return encode(this.toBinary(), 'hex') as string
|
|
208
208
|
}
|
|
209
209
|
|
|
@@ -212,7 +212,7 @@ export default class Script {
|
|
|
212
212
|
* Serializes the script to a binary array.
|
|
213
213
|
* @returns The script in binary array format.
|
|
214
214
|
*/
|
|
215
|
-
toBinary(): number[] {
|
|
215
|
+
toBinary (): number[] {
|
|
216
216
|
const writer = new Writer()
|
|
217
217
|
|
|
218
218
|
for (let i = 0; i < this.chunks.length; i++) {
|
|
@@ -244,7 +244,7 @@ export default class Script {
|
|
|
244
244
|
* @param script - The script to append.
|
|
245
245
|
* @returns This script instance for chaining.
|
|
246
246
|
*/
|
|
247
|
-
writeScript(script: Script): Script {
|
|
247
|
+
writeScript (script: Script): Script {
|
|
248
248
|
this.chunks = this.chunks.concat(script.chunks)
|
|
249
249
|
return this
|
|
250
250
|
}
|
|
@@ -255,7 +255,7 @@ export default class Script {
|
|
|
255
255
|
* @param op - The opcode to append.
|
|
256
256
|
* @returns This script instance for chaining.
|
|
257
257
|
*/
|
|
258
|
-
writeOpCode(op: number): Script {
|
|
258
|
+
writeOpCode (op: number): Script {
|
|
259
259
|
this.chunks.push({ op })
|
|
260
260
|
return this
|
|
261
261
|
}
|
|
@@ -267,7 +267,7 @@ export default class Script {
|
|
|
267
267
|
* @param op - The opcode to set.
|
|
268
268
|
* @returns This script instance for chaining.
|
|
269
269
|
*/
|
|
270
|
-
setChunkOpCode(i: number, op: number): Script {
|
|
270
|
+
setChunkOpCode (i: number, op: number): Script {
|
|
271
271
|
this.chunks[i] = { op }
|
|
272
272
|
return this
|
|
273
273
|
}
|
|
@@ -278,7 +278,7 @@ export default class Script {
|
|
|
278
278
|
* @param bn - The BigNumber to append.
|
|
279
279
|
* @returns This script instance for chaining.
|
|
280
280
|
*/
|
|
281
|
-
writeBn(bn: BigNumber): Script {
|
|
281
|
+
writeBn (bn: BigNumber): Script {
|
|
282
282
|
if (bn.cmpn(0) === OP.OP_0) {
|
|
283
283
|
this.chunks.push({
|
|
284
284
|
op: OP.OP_0
|
|
@@ -306,7 +306,7 @@ export default class Script {
|
|
|
306
306
|
* @returns This script instance for chaining.
|
|
307
307
|
* @throws {Error} Throws an error if the data is too large to be pushed.
|
|
308
308
|
*/
|
|
309
|
-
writeBin(bin: number[]): Script {
|
|
309
|
+
writeBin (bin: number[]): Script {
|
|
310
310
|
let op
|
|
311
311
|
if (bin.length > 0 && bin.length < OP.OP_PUSHDATA1) {
|
|
312
312
|
op = bin.length
|
|
@@ -334,7 +334,7 @@ export default class Script {
|
|
|
334
334
|
* @param num - The number to append.
|
|
335
335
|
* @returns This script instance for chaining.
|
|
336
336
|
*/
|
|
337
|
-
writeNumber(num: number): Script {
|
|
337
|
+
writeNumber (num: number): Script {
|
|
338
338
|
this.writeBn(new BigNumber(num))
|
|
339
339
|
return this
|
|
340
340
|
}
|
|
@@ -344,7 +344,7 @@ export default class Script {
|
|
|
344
344
|
* Removes all OP_CODESEPARATOR opcodes from the script.
|
|
345
345
|
* @returns This script instance for chaining.
|
|
346
346
|
*/
|
|
347
|
-
removeCodeseparators(): Script {
|
|
347
|
+
removeCodeseparators (): Script {
|
|
348
348
|
const chunks = []
|
|
349
349
|
for (let i = 0; i < this.chunks.length; i++) {
|
|
350
350
|
if (this.chunks[i].op !== OP.OP_CODESEPARATOR) {
|
|
@@ -362,7 +362,7 @@ export default class Script {
|
|
|
362
362
|
*
|
|
363
363
|
* @returns This script instance for chaining.
|
|
364
364
|
*/
|
|
365
|
-
findAndDelete(script: Script): Script {
|
|
365
|
+
findAndDelete (script: Script): Script {
|
|
366
366
|
const buf = script.toHex()
|
|
367
367
|
for (let i = 0; i < this.chunks.length; i++) {
|
|
368
368
|
const script2 = new Script([this.chunks[i]])
|
|
@@ -379,7 +379,7 @@ export default class Script {
|
|
|
379
379
|
* Checks if the script contains only push data operations.
|
|
380
380
|
* @returns True if the script is push-only, otherwise false.
|
|
381
381
|
*/
|
|
382
|
-
isPushOnly(): boolean {
|
|
382
|
+
isPushOnly (): boolean {
|
|
383
383
|
for (let i = 0; i < this.chunks.length; i++) {
|
|
384
384
|
const chunk = this.chunks[i]
|
|
385
385
|
const opCodeNum = chunk.op
|
|
@@ -395,7 +395,7 @@ export default class Script {
|
|
|
395
395
|
* Determines if the script is a locking script.
|
|
396
396
|
* @returns True if the script is a locking script, otherwise false.
|
|
397
397
|
*/
|
|
398
|
-
isLockingScript(): boolean {
|
|
398
|
+
isLockingScript (): boolean {
|
|
399
399
|
throw new Error('Not implemented')
|
|
400
400
|
}
|
|
401
401
|
|
|
@@ -404,7 +404,7 @@ export default class Script {
|
|
|
404
404
|
* Determines if the script is an unlocking script.
|
|
405
405
|
* @returns True if the script is an unlocking script, otherwise false.
|
|
406
406
|
*/
|
|
407
|
-
isUnlockingScript(): boolean {
|
|
407
|
+
isUnlockingScript (): boolean {
|
|
408
408
|
throw new Error('Not implemented')
|
|
409
409
|
}
|
|
410
410
|
|
|
@@ -415,7 +415,7 @@ export default class Script {
|
|
|
415
415
|
* @param chunk - The script chunk.
|
|
416
416
|
* @returns The string representation of the chunk.
|
|
417
417
|
*/
|
|
418
|
-
private _chunkToString(chunk: ScriptChunk): string {
|
|
418
|
+
private _chunkToString (chunk: ScriptChunk): string {
|
|
419
419
|
const op = chunk.op
|
|
420
420
|
let str = ''
|
|
421
421
|
if (typeof chunk.data === 'undefined') {
|
|
@@ -39,11 +39,11 @@ export default class MerklePath {
|
|
|
39
39
|
* @param {string} hex - The hexadecimal string representation of the Merkle Path.
|
|
40
40
|
* @returns {MerklePath} - A new MerklePath instance.
|
|
41
41
|
*/
|
|
42
|
-
static fromHex(hex: string): MerklePath {
|
|
42
|
+
static fromHex (hex: string): MerklePath {
|
|
43
43
|
return MerklePath.fromBinary(toArray(hex, 'hex'))
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
static fromReader(reader: Reader): MerklePath {
|
|
46
|
+
static fromReader (reader: Reader): MerklePath {
|
|
47
47
|
const blockHeight = reader.readVarIntNum()
|
|
48
48
|
const treeHeight = reader.readUInt8()
|
|
49
49
|
const path = Array(treeHeight).fill(0).map(() => ([]))
|
|
@@ -82,12 +82,12 @@ export default class MerklePath {
|
|
|
82
82
|
* @param {number[]} bump - The binary array representation of the Merkle Path.
|
|
83
83
|
* @returns {MerklePath} - A new MerklePath instance.
|
|
84
84
|
*/
|
|
85
|
-
static fromBinary(bump: number[]): MerklePath {
|
|
85
|
+
static fromBinary (bump: number[]): MerklePath {
|
|
86
86
|
const reader = new Reader(bump)
|
|
87
87
|
return MerklePath.fromReader(reader)
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
constructor(blockHeight: number, path: Array<Array<{
|
|
90
|
+
constructor (blockHeight: number, path: Array<Array<{
|
|
91
91
|
offset: number
|
|
92
92
|
hash?: string
|
|
93
93
|
txid?: boolean
|
|
@@ -135,7 +135,7 @@ export default class MerklePath {
|
|
|
135
135
|
*
|
|
136
136
|
* @returns {number[]} - The binary array representation of the Merkle Path.
|
|
137
137
|
*/
|
|
138
|
-
toBinary(): number[] {
|
|
138
|
+
toBinary (): number[] {
|
|
139
139
|
const writer = new Writer()
|
|
140
140
|
writer.writeVarIntNum(this.blockHeight)
|
|
141
141
|
const treeHeight = this.path.length
|
|
@@ -166,7 +166,7 @@ export default class MerklePath {
|
|
|
166
166
|
*
|
|
167
167
|
* @returns {string} - The hexadecimal string representation of the Merkle Path.
|
|
168
168
|
*/
|
|
169
|
-
toHex(): string {
|
|
169
|
+
toHex (): string {
|
|
170
170
|
return toHex(this.toBinary())
|
|
171
171
|
}
|
|
172
172
|
|
|
@@ -177,7 +177,7 @@ export default class MerklePath {
|
|
|
177
177
|
* @returns {string} - The computed Merkle root as a hexadecimal string.
|
|
178
178
|
* @throws {Error} - If the transaction ID is not part of the Merkle Path.
|
|
179
179
|
*/
|
|
180
|
-
computeRoot(txid?: string): string {
|
|
180
|
+
computeRoot (txid?: string): string {
|
|
181
181
|
if (typeof txid !== 'string') {
|
|
182
182
|
txid = this.path[0].find(leaf => Boolean(leaf?.hash)).hash
|
|
183
183
|
}
|
|
@@ -216,7 +216,7 @@ export default class MerklePath {
|
|
|
216
216
|
* @param {ChainTracker} chainTracker - The ChainTracker instance used to verify the Merkle root.
|
|
217
217
|
* @returns {boolean} - True if the transaction ID is valid within the Merkle Path at the specified block height.
|
|
218
218
|
*/
|
|
219
|
-
async verify(txid: string, chainTracker: ChainTracker): Promise<boolean> {
|
|
219
|
+
async verify (txid: string, chainTracker: ChainTracker): Promise<boolean> {
|
|
220
220
|
const root = this.computeRoot(txid)
|
|
221
221
|
// Use the chain tracker to determine whether this is a valid merkle root at the given block height
|
|
222
222
|
return await chainTracker.isValidRootForHeight(root, this.blockHeight)
|
|
@@ -228,7 +228,7 @@ export default class MerklePath {
|
|
|
228
228
|
* @param {MerklePath} other - Another MerklePath to combine with this path.
|
|
229
229
|
* @throws {Error} - If the paths have different block heights or roots.
|
|
230
230
|
*/
|
|
231
|
-
combine(other: MerklePath): void {
|
|
231
|
+
combine (other: MerklePath): void {
|
|
232
232
|
if (this.blockHeight !== other.blockHeight) {
|
|
233
233
|
throw new Error('You cannot combine paths which do not have the same block height.')
|
|
234
234
|
}
|
|
@@ -125,25 +125,23 @@ export default class Transaction {
|
|
|
125
125
|
* any application seeking to validate data in output scripts must store the entire transaction as well.
|
|
126
126
|
* Since the transaction data includes the output script data, saving a second copy of potentially
|
|
127
127
|
* large scripts can bloat application storage requirements.
|
|
128
|
-
*
|
|
128
|
+
*
|
|
129
129
|
* This function efficiently parses binary transaction data to determine the offsets and lengths of each script.
|
|
130
130
|
* This supports the efficient retreival of script data from transaction data.
|
|
131
|
-
*
|
|
131
|
+
*
|
|
132
132
|
* @param bin binary transaction data
|
|
133
133
|
* @returns {
|
|
134
134
|
* inputs: { vin: number, offset: number, length: number }[]
|
|
135
135
|
* outputs: { vout: number, offset: number, length: number }[]
|
|
136
136
|
* }
|
|
137
137
|
*/
|
|
138
|
-
static parseScriptOffsets(bin: number[])
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
{
|
|
138
|
+
static parseScriptOffsets (bin: number[]): {
|
|
139
|
+
inputs: Array<{ vin: number, offset: number, length: number }>
|
|
140
|
+
outputs: Array<{ vout: number, offset: number, length: number }>
|
|
141
|
+
} {
|
|
144
142
|
const br = new Reader(bin)
|
|
145
|
-
const inputs: { vin: number, offset: number, length: number }
|
|
146
|
-
const outputs: { vout: number, offset: number, length: number }
|
|
143
|
+
const inputs: Array<{ vin: number, offset: number, length: number }> = []
|
|
144
|
+
const outputs: Array<{ vout: number, offset: number, length: number }> = []
|
|
147
145
|
|
|
148
146
|
br.pos += 4 // version
|
|
149
147
|
const inputsLength = br.readVarIntNum()
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { BroadcastResponse, BroadcastFailure, Broadcaster } from '../Broadcaster.js'
|
|
2
2
|
import Transaction from '../Transaction.js'
|
|
3
|
+
import {HttpClient} from "./HttpClient.js";
|
|
4
|
+
import defaultHttpClient from "./DefaultHttpClient.js";
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Represents an ARC transaction broadcaster.
|
|
@@ -7,16 +9,19 @@ import Transaction from '../Transaction.js'
|
|
|
7
9
|
export default class ARC implements Broadcaster {
|
|
8
10
|
URL: string
|
|
9
11
|
apiKey: string
|
|
12
|
+
private httpClient: HttpClient;
|
|
10
13
|
|
|
11
14
|
/**
|
|
12
15
|
* Constructs an instance of the ARC broadcaster.
|
|
13
16
|
*
|
|
14
17
|
* @param {string} URL - The URL endpoint for the ARC API.
|
|
15
18
|
* @param {string} apiKey - The API key used for authorization with the ARC API.
|
|
19
|
+
* @param {HttpClient} httpClient - The HTTP client used to make requests to the ARC API.
|
|
16
20
|
*/
|
|
17
|
-
constructor (URL: string, apiKey: string) {
|
|
21
|
+
constructor (URL: string, apiKey: string, httpClient: HttpClient = defaultHttpClient()) {
|
|
18
22
|
this.URL = URL
|
|
19
23
|
this.apiKey = apiKey
|
|
24
|
+
this.httpClient = httpClient
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
/**
|
|
@@ -48,23 +53,8 @@ export default class ARC implements Broadcaster {
|
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
try {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (typeof window !== 'undefined' && typeof window.fetch === 'function') {
|
|
55
|
-
// Use fetch in a browser environment
|
|
56
|
-
response = await window.fetch(`${this.URL}/v1/tx`, requestOptions)
|
|
57
|
-
data = await response.json()
|
|
58
|
-
} else if (typeof require !== 'undefined') {
|
|
59
|
-
// Use Node.js https module
|
|
60
|
-
// eslint-disable-next-line
|
|
61
|
-
const https = require('https')
|
|
62
|
-
response = await this.nodeFetch(https, requestOptions)
|
|
63
|
-
data = JSON.parse(response)
|
|
64
|
-
} else {
|
|
65
|
-
throw new Error('No method available to perform HTTP request')
|
|
66
|
-
}
|
|
67
|
-
|
|
56
|
+
const response = await this.httpClient.fetch(`${this.URL}/v1/tx`, requestOptions)
|
|
57
|
+
const data = await response.json()
|
|
68
58
|
if (data.txid as boolean || response.ok as boolean || response.statusCode === 200) {
|
|
69
59
|
return {
|
|
70
60
|
status: 'success',
|
|
@@ -88,28 +78,4 @@ export default class ARC implements Broadcaster {
|
|
|
88
78
|
}
|
|
89
79
|
}
|
|
90
80
|
}
|
|
91
|
-
|
|
92
|
-
/** Helper function for Node.js HTTPS requests */
|
|
93
|
-
private async nodeFetch (https, requestOptions): Promise<any> {
|
|
94
|
-
return await new Promise((resolve, reject) => {
|
|
95
|
-
const req = https.request(`${this.URL}/v1/tx`, requestOptions, res => {
|
|
96
|
-
let data = ''
|
|
97
|
-
res.on('data', (chunk: string) => {
|
|
98
|
-
data += chunk
|
|
99
|
-
})
|
|
100
|
-
res.on('end', () => {
|
|
101
|
-
resolve(data)
|
|
102
|
-
})
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
req.on('error', error => {
|
|
106
|
-
reject(error)
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
if (requestOptions.body as boolean) {
|
|
110
|
-
req.write(requestOptions.body)
|
|
111
|
-
}
|
|
112
|
-
req.end()
|
|
113
|
-
})
|
|
114
|
-
}
|
|
115
81
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {HttpClient, HttpClientResponse} from "./HttpClient.js";
|
|
2
|
+
import {NodejsHttpClient} from "./NodejsHttpClient.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns a default HttpClient implementation based on the environment that it is run on.
|
|
6
|
+
*/
|
|
7
|
+
export default function defaultHttpClient(): HttpClient {
|
|
8
|
+
const noHttpClient:HttpClient = {
|
|
9
|
+
fetch(..._): Promise<HttpClientResponse> {
|
|
10
|
+
throw new Error('No method available to perform HTTP request')
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (typeof window !== 'undefined' && typeof window.fetch === 'function') {
|
|
15
|
+
// Use fetch in a browser environment
|
|
16
|
+
return window
|
|
17
|
+
} else if (typeof require !== 'undefined') {
|
|
18
|
+
// Use Node.js https module
|
|
19
|
+
// eslint-disable-next-line
|
|
20
|
+
try {
|
|
21
|
+
const https = require('https')
|
|
22
|
+
return new NodejsHttpClient(https)
|
|
23
|
+
} catch (e) {
|
|
24
|
+
return noHttpClient
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
return noHttpClient
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An interface for HTTP client used by ARC to make HTTP requests.
|
|
3
|
+
*/
|
|
4
|
+
export interface HttpClient {
|
|
5
|
+
/**
|
|
6
|
+
* Makes a request to the server.
|
|
7
|
+
* @param url The URL to make the request to.
|
|
8
|
+
* @param options The request configuration.
|
|
9
|
+
*/
|
|
10
|
+
fetch(url: string, options: HttpClientRequestOptions): Promise<HttpClientResponse>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* An interface for configuration of the request to be passed to the fetch method.
|
|
15
|
+
*/
|
|
16
|
+
export interface HttpClientRequestOptions {
|
|
17
|
+
/** A string to set request's method. */
|
|
18
|
+
method?: string;
|
|
19
|
+
/** An object literal set request's headers. */
|
|
20
|
+
headers?: Record<string, string>;
|
|
21
|
+
/** An object or null to set request's body. */
|
|
22
|
+
body?: string | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* An interface for the response returned by the fetch method.
|
|
27
|
+
*/
|
|
28
|
+
export interface HttpClientResponse {
|
|
29
|
+
/** The status code of the response. */
|
|
30
|
+
statusCode?: number;
|
|
31
|
+
/** A flag indicating whether the request ends with success status or not. */
|
|
32
|
+
ok?: boolean;
|
|
33
|
+
json(): Promise<any>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {HttpClient, HttpClientRequestOptions, HttpClientResponse} from "./HttpClient.js";
|
|
2
|
+
|
|
3
|
+
/** Node.js Https module interface limited to options needed by ts-sdk */
|
|
4
|
+
export interface HttpsNodejs {
|
|
5
|
+
request(url: string, options: HttpClientRequestOptions, callback: (res: any) => void): NodejsHttpClientRequest;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Nodejs result of the Node.js https.request call limited to options needed by ts-sdk */
|
|
9
|
+
export interface NodejsHttpClientRequest {
|
|
10
|
+
write(chunk: string): void;
|
|
11
|
+
|
|
12
|
+
end(): void;
|
|
13
|
+
|
|
14
|
+
on(event: string, callback: (data: any) => void): void;
|
|
15
|
+
|
|
16
|
+
end(): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Adapter for Node.js Https module to be used as HttpClient
|
|
21
|
+
*/
|
|
22
|
+
export class NodejsHttpClient implements HttpClient {
|
|
23
|
+
constructor(private https: HttpsNodejs) {
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async fetch(url: string, requestOptions: HttpClientRequestOptions): Promise<HttpClientResponse> {
|
|
27
|
+
return await new Promise((resolve, reject) => {
|
|
28
|
+
const req = this.https.request(url, requestOptions, res => {
|
|
29
|
+
let data = ''
|
|
30
|
+
res.on('data', (chunk: string) => {
|
|
31
|
+
data += chunk
|
|
32
|
+
})
|
|
33
|
+
res.on('end', () => {
|
|
34
|
+
resolve({
|
|
35
|
+
ok: res.statusCode >= 200 && res.statusCode <= 299,
|
|
36
|
+
statusCode: res.statusCode,
|
|
37
|
+
async json() {
|
|
38
|
+
return JSON.parse(data)
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
req.on('error', error => {
|
|
45
|
+
reject(error)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
if (!!requestOptions.body) {
|
|
49
|
+
req.write(requestOptions.body)
|
|
50
|
+
}
|
|
51
|
+
req.end()
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import ARC from '../../../../dist/cjs/src/transaction/broadcasters/ARC.js'
|
|
2
2
|
import Transaction from '../../../../dist/cjs/src/transaction/Transaction.js'
|
|
3
|
+
import {NodejsHttpClient} from "../NodejsHttpClient";
|
|
3
4
|
|
|
4
5
|
// Mock Transaction
|
|
5
6
|
jest.mock('../../Transaction', () => {
|
|
@@ -15,26 +16,24 @@ jest.mock('../../Transaction', () => {
|
|
|
15
16
|
describe('ARC Broadcaster', () => {
|
|
16
17
|
const URL = 'https://example.com'
|
|
17
18
|
const apiKey = 'test_api_key'
|
|
18
|
-
|
|
19
|
+
const successResponse = {
|
|
20
|
+
txid: 'mocked_txid',
|
|
21
|
+
txStatus: 'success',
|
|
22
|
+
extraInfo: 'received'
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
let transaction: Transaction
|
|
20
26
|
|
|
21
27
|
beforeEach(() => {
|
|
22
|
-
broadcaster = new ARC(URL, apiKey)
|
|
23
28
|
transaction = new Transaction()
|
|
24
29
|
})
|
|
25
30
|
|
|
26
31
|
it('should broadcast successfully using window.fetch', async () => {
|
|
27
32
|
// Mocking window.fetch
|
|
28
|
-
const mockFetch =
|
|
29
|
-
ok: true,
|
|
30
|
-
json: async () => await Promise.resolve({
|
|
31
|
-
txid: 'mocked_txid',
|
|
32
|
-
txStatus: 'success',
|
|
33
|
-
extraInfo: 'received'
|
|
34
|
-
})
|
|
35
|
-
})
|
|
33
|
+
const mockFetch = mockedFetch(successResponse)
|
|
36
34
|
global.window = { fetch: mockFetch } as any
|
|
37
35
|
|
|
36
|
+
const broadcaster = new ARC(URL, apiKey)
|
|
38
37
|
const response = await broadcaster.broadcast(transaction)
|
|
39
38
|
|
|
40
39
|
expect(mockFetch).toHaveBeenCalled()
|
|
@@ -47,29 +46,39 @@ describe('ARC Broadcaster', () => {
|
|
|
47
46
|
|
|
48
47
|
it('should broadcast successfully using Node.js https', async () => {
|
|
49
48
|
// Mocking Node.js https module
|
|
50
|
-
|
|
51
|
-
request: (url, options, callback) => {
|
|
52
|
-
// eslint-disable-next-line
|
|
53
|
-
callback({
|
|
54
|
-
statusCode: 200,
|
|
55
|
-
on: (event, handler) => {
|
|
56
|
-
if (event === 'data') handler(JSON.stringify({
|
|
57
|
-
txid: 'mocked_txid',
|
|
58
|
-
txStatus: 'success',
|
|
59
|
-
extraInfo: 'received'
|
|
60
|
-
}))
|
|
61
|
-
if (event === 'end') handler()
|
|
62
|
-
}
|
|
63
|
-
})
|
|
64
|
-
return {
|
|
65
|
-
on: jest.fn(),
|
|
66
|
-
write: jest.fn(),
|
|
67
|
-
end: jest.fn()
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}))
|
|
71
|
-
|
|
49
|
+
mockedHttps(successResponse)
|
|
72
50
|
delete global.window
|
|
51
|
+
|
|
52
|
+
const broadcaster = new ARC(URL, apiKey)
|
|
53
|
+
const response = await broadcaster.broadcast(transaction)
|
|
54
|
+
|
|
55
|
+
expect(response).toEqual({
|
|
56
|
+
status: 'success',
|
|
57
|
+
txid: 'mocked_txid',
|
|
58
|
+
message: 'success received'
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('should broadcast successfully using provided fetch', async () => {
|
|
63
|
+
|
|
64
|
+
const mockFetch = mockedFetch(successResponse)
|
|
65
|
+
|
|
66
|
+
const broadcaster = new ARC(URL, apiKey, { fetch: mockFetch })
|
|
67
|
+
const response = await broadcaster.broadcast(transaction)
|
|
68
|
+
|
|
69
|
+
expect(mockFetch).toHaveBeenCalled()
|
|
70
|
+
expect(response).toEqual({
|
|
71
|
+
status: 'success',
|
|
72
|
+
txid: 'mocked_txid',
|
|
73
|
+
message: 'success received'
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('should broadcast successfully using provided https', async () => {
|
|
78
|
+
|
|
79
|
+
const mockHttps = mockedHttps(successResponse)
|
|
80
|
+
const broadcaster = new ARC(URL, apiKey, new NodejsHttpClient(mockHttps))
|
|
81
|
+
|
|
73
82
|
const response = await broadcaster.broadcast(transaction)
|
|
74
83
|
|
|
75
84
|
expect(response).toEqual({
|
|
@@ -83,6 +92,7 @@ describe('ARC Broadcaster', () => {
|
|
|
83
92
|
const mockFetch = jest.fn().mockRejectedValue(new Error('Network error'))
|
|
84
93
|
global.window = { fetch: mockFetch } as any
|
|
85
94
|
|
|
95
|
+
const broadcaster = new ARC(URL, apiKey)
|
|
86
96
|
const response = await broadcaster.broadcast(transaction)
|
|
87
97
|
|
|
88
98
|
expect(mockFetch).toHaveBeenCalled()
|
|
@@ -94,15 +104,13 @@ describe('ARC Broadcaster', () => {
|
|
|
94
104
|
})
|
|
95
105
|
|
|
96
106
|
it('should handle non-200 responses', async () => {
|
|
97
|
-
const mockFetch =
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
status: '400',
|
|
101
|
-
detail: 'Bad request'
|
|
102
|
-
})
|
|
107
|
+
const mockFetch = mockedFetch({
|
|
108
|
+
status: '400',
|
|
109
|
+
detail: 'Bad request'
|
|
103
110
|
})
|
|
104
111
|
global.window = { fetch: mockFetch } as any
|
|
105
112
|
|
|
113
|
+
const broadcaster = new ARC(URL, apiKey)
|
|
106
114
|
const response = await broadcaster.broadcast(transaction)
|
|
107
115
|
|
|
108
116
|
expect(mockFetch).toHaveBeenCalled()
|
|
@@ -112,4 +120,34 @@ describe('ARC Broadcaster', () => {
|
|
|
112
120
|
description: 'Bad request'
|
|
113
121
|
})
|
|
114
122
|
})
|
|
123
|
+
|
|
124
|
+
function mockedFetch(response) {
|
|
125
|
+
return jest.fn().mockResolvedValue({
|
|
126
|
+
ok: response.status === '200',
|
|
127
|
+
json: async () => response
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function mockedHttps(response) {
|
|
132
|
+
const https = {
|
|
133
|
+
request: (url, options, callback) => {
|
|
134
|
+
// eslint-disable-next-line
|
|
135
|
+
callback({
|
|
136
|
+
statusCode: 200,
|
|
137
|
+
on: (event, handler) => {
|
|
138
|
+
if (event === 'data') handler(JSON.stringify(response))
|
|
139
|
+
if (event === 'end') handler()
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
return {
|
|
143
|
+
on: jest.fn(),
|
|
144
|
+
write: jest.fn(),
|
|
145
|
+
end: jest.fn()
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
jest.mock('https', () => https)
|
|
150
|
+
return https
|
|
151
|
+
}
|
|
115
152
|
})
|
|
153
|
+
|
|
@@ -1 +1,5 @@
|
|
|
1
1
|
export { default as ARC } from './ARC.js'
|
|
2
|
+
export type { HttpClient, HttpClientResponse, HttpClientRequestOptions } from './HttpClient.js'
|
|
3
|
+
export { default as defaultHttpClient } from './DefaultHttpClient.js'
|
|
4
|
+
export { NodejsHttpClient } from './NodejsHttpClient.js'
|
|
5
|
+
export type { HttpsNodejs, NodejsHttpClientRequest } from './NodejsHttpClient.js'
|