@bsv/sdk 1.10.1 → 1.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/cjs/package.json +1 -1
  2. package/dist/cjs/src/identity/IdentityClient.js +124 -20
  3. package/dist/cjs/src/identity/IdentityClient.js.map +1 -1
  4. package/dist/cjs/src/primitives/ReaderUint8Array.js +180 -0
  5. package/dist/cjs/src/primitives/ReaderUint8Array.js.map +1 -0
  6. package/dist/cjs/src/primitives/WriterUint8Array.js +173 -0
  7. package/dist/cjs/src/primitives/WriterUint8Array.js.map +1 -0
  8. package/dist/cjs/src/primitives/utils.js +20 -2
  9. package/dist/cjs/src/primitives/utils.js.map +1 -1
  10. package/dist/cjs/src/transaction/Beef.js +85 -27
  11. package/dist/cjs/src/transaction/Beef.js.map +1 -1
  12. package/dist/cjs/src/transaction/BeefTx.js +32 -14
  13. package/dist/cjs/src/transaction/BeefTx.js.map +1 -1
  14. package/dist/cjs/src/transaction/MerklePath.js +25 -6
  15. package/dist/cjs/src/transaction/MerklePath.js.map +1 -1
  16. package/dist/cjs/src/transaction/Transaction.js +77 -26
  17. package/dist/cjs/src/transaction/Transaction.js.map +1 -1
  18. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  19. package/dist/esm/src/identity/IdentityClient.js +124 -20
  20. package/dist/esm/src/identity/IdentityClient.js.map +1 -1
  21. package/dist/esm/src/primitives/ReaderUint8Array.js +176 -0
  22. package/dist/esm/src/primitives/ReaderUint8Array.js.map +1 -0
  23. package/dist/esm/src/primitives/WriterUint8Array.js +169 -0
  24. package/dist/esm/src/primitives/WriterUint8Array.js.map +1 -0
  25. package/dist/esm/src/primitives/utils.js +18 -1
  26. package/dist/esm/src/primitives/utils.js.map +1 -1
  27. package/dist/esm/src/transaction/Beef.js +86 -28
  28. package/dist/esm/src/transaction/Beef.js.map +1 -1
  29. package/dist/esm/src/transaction/BeefTx.js +33 -15
  30. package/dist/esm/src/transaction/BeefTx.js.map +1 -1
  31. package/dist/esm/src/transaction/MerklePath.js +26 -7
  32. package/dist/esm/src/transaction/MerklePath.js.map +1 -1
  33. package/dist/esm/src/transaction/Transaction.js +78 -27
  34. package/dist/esm/src/transaction/Transaction.js.map +1 -1
  35. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  36. package/dist/types/src/identity/IdentityClient.d.ts +8 -0
  37. package/dist/types/src/identity/IdentityClient.d.ts.map +1 -1
  38. package/dist/types/src/primitives/ReaderUint8Array.d.ts +32 -0
  39. package/dist/types/src/primitives/ReaderUint8Array.d.ts.map +1 -0
  40. package/dist/types/src/primitives/WriterUint8Array.d.ts +54 -0
  41. package/dist/types/src/primitives/WriterUint8Array.d.ts.map +1 -0
  42. package/dist/types/src/primitives/utils.d.ts +15 -3
  43. package/dist/types/src/primitives/utils.d.ts.map +1 -1
  44. package/dist/types/src/transaction/Beef.d.ts +24 -7
  45. package/dist/types/src/transaction/Beef.d.ts.map +1 -1
  46. package/dist/types/src/transaction/BeefTx.d.ts +13 -6
  47. package/dist/types/src/transaction/BeefTx.d.ts.map +1 -1
  48. package/dist/types/src/transaction/MerklePath.d.ts +16 -3
  49. package/dist/types/src/transaction/MerklePath.d.ts.map +1 -1
  50. package/dist/types/src/transaction/Transaction.d.ts +44 -7
  51. package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
  52. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  53. package/dist/umd/bundle.js +3 -3
  54. package/dist/umd/bundle.js.map +1 -1
  55. package/docs/reference/primitives.md +167 -29
  56. package/docs/reference/script.md +1 -1
  57. package/docs/reference/transaction.md +177 -34
  58. package/package.json +1 -1
  59. package/src/identity/IdentityClient.ts +153 -29
  60. package/src/identity/__tests/IdentityClient.test.ts +289 -1
  61. package/src/overlay-tools/__tests/SHIPBroadcaster.test.ts +9 -0
  62. package/src/primitives/ReaderUint8Array.ts +196 -0
  63. package/src/primitives/WriterUint8Array.ts +195 -0
  64. package/src/primitives/__tests/ReaderUint8Array.test.ts +317 -0
  65. package/src/primitives/__tests/WriterUint8Array.test.ts +208 -0
  66. package/src/primitives/utils.ts +20 -2
  67. package/src/transaction/Beef.ts +103 -40
  68. package/src/transaction/BeefTx.ts +38 -19
  69. package/src/transaction/MerklePath.ts +30 -9
  70. package/src/transaction/Transaction.ts +91 -38
  71. package/src/transaction/__tests/Beef.test.ts +75 -0
@@ -0,0 +1,208 @@
1
+ import BigNumber from '../BigNumber'
2
+ import { Reader, encode, WriterUint8Array } from '../utils'
3
+
4
+ describe('WriterUint8Array', () => {
5
+ it('should create a new buffer writer', () => {
6
+ const bw = new WriterUint8Array()
7
+ expect(bw).toBeDefined()
8
+ })
9
+
10
+ describe('#getLength', () => {
11
+ it('should compute length correctly of two 2-byte buffers', () => {
12
+ const buf1 = Buffer.from('0000', 'hex')
13
+ const buf2 = Buffer.from('0000', 'hex')
14
+ const bw = new WriterUint8Array().write([...buf1]).write([...buf2])
15
+ expect(bw.getLength()).toEqual(4)
16
+ })
17
+ })
18
+
19
+ describe('#toArray', () => {
20
+ it('should concat these two bufs', () => {
21
+ const buf1 = [0]
22
+ const buf2 = [1]
23
+ const bw = new WriterUint8Array([buf1, buf2])
24
+ expect(encode(bw.toArray(), 'hex')).toEqual('0001')
25
+ })
26
+ })
27
+
28
+ describe('#write', () => {
29
+ it('should write a buffer', () => {
30
+ const buf = [0]
31
+ const bw = new WriterUint8Array()
32
+ bw.write(buf)
33
+ expect(encode(bw.toArray(), 'hex')).toEqual('00')
34
+ })
35
+ })
36
+
37
+ describe('#writeReverse', () => {
38
+ it('should write a buffer in reverse', () => {
39
+ const buf = [0, 1]
40
+ const bw = new WriterUint8Array()
41
+ bw.writeReverse(buf)
42
+ expect(encode(bw.toArray(), 'hex')).toEqual('0100')
43
+ })
44
+ })
45
+
46
+ describe('#writeUInt8', () => {
47
+ it('should write 1', () => {
48
+ const bw = new WriterUint8Array()
49
+ expect(encode(bw.writeUInt8(1).toArray(), 'hex')).toEqual('01')
50
+ })
51
+ })
52
+
53
+ describe('#writeInt8', () => {
54
+ it('should write 1', () => {
55
+ const bw = new WriterUint8Array()
56
+ expect(encode(bw.writeInt8(1).toArray(), 'hex')).toEqual('01')
57
+ expect(encode(new WriterUint8Array().writeInt8(-1).toArray(), 'hex')).toEqual('ff')
58
+ })
59
+ })
60
+
61
+ describe('#writeUInt16BE', () => {
62
+ it('should write 1', () => {
63
+ const bw = new WriterUint8Array()
64
+ expect(encode(bw.writeUInt16BE(1).toArray(), 'hex')).toEqual('0001')
65
+ })
66
+ })
67
+
68
+ describe('#writeInt16BE', () => {
69
+ it('should write 1', () => {
70
+ const bw = new WriterUint8Array()
71
+ expect(encode(bw.writeInt16BE(1).toArray(), 'hex')).toEqual('0001')
72
+ expect(encode(new WriterUint8Array().writeInt16BE(-1).toArray(), 'hex')).toEqual(
73
+ 'ffff'
74
+ )
75
+ })
76
+ })
77
+
78
+ describe('#writeUInt16LE', () => {
79
+ it('should write 1', () => {
80
+ const bw = new WriterUint8Array()
81
+ expect(encode(bw.writeUInt16LE(1).toArray(), 'hex')).toEqual('0100')
82
+ })
83
+ })
84
+
85
+ describe('#writeInt16LE', () => {
86
+ it('should write 1', () => {
87
+ const bw = new WriterUint8Array()
88
+ expect(encode(bw.writeInt16LE(1).toArray(), 'hex')).toEqual('0100')
89
+ expect(encode(new WriterUint8Array().writeInt16LE(-1).toArray(), 'hex')).toEqual(
90
+ 'ffff'
91
+ )
92
+ })
93
+ })
94
+
95
+ describe('#writeUInt32BE', () => {
96
+ it('should write 1', () => {
97
+ const bw = new WriterUint8Array()
98
+ expect(encode(bw.writeUInt32BE(1).toArray(), 'hex')).toEqual('00000001')
99
+ })
100
+ })
101
+
102
+ describe('#writeInt32BE', () => {
103
+ it('should write 1', () => {
104
+ const bw = new WriterUint8Array()
105
+ expect(encode(bw.writeInt32BE(1).toArray(), 'hex')).toEqual('00000001')
106
+ expect(encode(new WriterUint8Array().writeInt32BE(-1).toArray(), 'hex')).toEqual(
107
+ 'ffffffff'
108
+ )
109
+ })
110
+ })
111
+
112
+ describe('#writeUInt32LE', () => {
113
+ it('should write 1', () => {
114
+ const bw = new WriterUint8Array()
115
+ expect(encode(bw.writeUInt32LE(1).toArray(), 'hex')).toEqual('01000000')
116
+ })
117
+ })
118
+
119
+ describe('#writeInt32LE', () => {
120
+ it('should write 1', () => {
121
+ const bw = new WriterUint8Array()
122
+ expect(encode(bw.writeInt32LE(1).toArray(), 'hex')).toEqual('01000000')
123
+ expect(encode(new WriterUint8Array().writeInt32LE(-1).toArray(), 'hex')).toEqual(
124
+ 'ffffffff'
125
+ )
126
+ })
127
+ })
128
+
129
+ describe('#writeUInt64BEBn', () => {
130
+ it('should write 1', () => {
131
+ const bw = new WriterUint8Array()
132
+ expect(
133
+ encode(bw.writeUInt64BEBn(new BigNumber(1)).toArray(), 'hex')
134
+ ).toEqual('0000000000000001')
135
+ })
136
+ })
137
+
138
+ describe('#writeUInt64LEBn', () => {
139
+ it('should write 1', () => {
140
+ const bw = new WriterUint8Array()
141
+ expect(
142
+ encode(bw.writeUInt64LEBn(new BigNumber(1)).toArray(), 'hex')
143
+ ).toEqual('0100000000000000')
144
+ })
145
+ })
146
+
147
+ describe('#writeVarInt', () => {
148
+ it('should write a 1 byte varInt', () => {
149
+ const bw = new WriterUint8Array()
150
+ bw.writeVarIntNum(1)
151
+ expect(bw.toArray().length).toEqual(1)
152
+ })
153
+
154
+ it('should write a 3 byte varInt', () => {
155
+ const bw = new WriterUint8Array()
156
+ bw.writeVarIntNum(1000)
157
+ expect(bw.toArray().length).toEqual(3)
158
+ })
159
+
160
+ it('should write a 5 byte varInt', () => {
161
+ const bw = new WriterUint8Array()
162
+ bw.writeVarIntNum(Math.pow(2, 16 + 1))
163
+ expect(bw.toArray().length).toEqual(5)
164
+ })
165
+
166
+ it('should write a 9 byte varInt', () => {
167
+ const bw = new WriterUint8Array()
168
+ bw.writeVarIntNum(Math.pow(2, 32 + 1))
169
+ expect(bw.toArray().length).toEqual(9)
170
+ })
171
+
172
+ it('should read back the same value it wrote for a 9 byte varInt', () => {
173
+ const bw = new WriterUint8Array()
174
+ const n = Math.pow(2, 53)
175
+ expect(n).toEqual(n + 1) // javascript number precision limit
176
+ bw.writeVarIntNum(n)
177
+ const br = new Reader(bw.toArray())
178
+ expect(br.readVarIntBn().toHex()).toEqual('20000000000000')
179
+ })
180
+ })
181
+
182
+ describe('#writeVarIntBn', () => {
183
+ it('should write a 1 byte varInt', () => {
184
+ const bw = new WriterUint8Array()
185
+ bw.writeVarIntBn(new BigNumber(1))
186
+ expect(bw.toArray().length).toEqual(1)
187
+ })
188
+
189
+ it('should write a 3 byte varInt', () => {
190
+ const bw = new WriterUint8Array()
191
+ bw.writeVarIntBn(new BigNumber(1000))
192
+ expect(bw.toArray().length).toEqual(3)
193
+ })
194
+
195
+ it('should write a 5 byte varInt', () => {
196
+ const bw = new WriterUint8Array()
197
+ const bn = new BigNumber(Math.pow(2, 16 + 1))
198
+ bw.writeVarIntBn(bn)
199
+ expect(bw.toArray().length).toEqual(5)
200
+ })
201
+
202
+ it('should write a 9 byte varInt', () => {
203
+ const bw = new WriterUint8Array()
204
+ bw.writeVarIntBn(new BigNumber(Math.pow(2, 32 + 1)))
205
+ expect(bw.toArray().length).toEqual(9)
206
+ })
207
+ })
208
+ })
@@ -1,6 +1,11 @@
1
1
  import BigNumber from './BigNumber.js'
2
2
  import { hash256 } from './Hash.js'
3
3
  import { assertValidHex } from './hex.js'
4
+ import { WriterUint8Array } from './WriterUint8Array.js'
5
+ import { ReaderUint8Array } from './ReaderUint8Array.js'
6
+
7
+ export { WriterUint8Array }
8
+ export { ReaderUint8Array }
4
9
 
5
10
  const BufferCtor =
6
11
  typeof globalThis !== 'undefined' ? (globalThis as any).Buffer : undefined
@@ -32,7 +37,7 @@ for (let i = 0; i < 256; i++) {
32
37
  HEX_DIGITS[(i >> 4) & 0xf] + HEX_DIGITS[i & 0xf]
33
38
  }
34
39
 
35
- export const toHex = (msg: number[]): string => {
40
+ export const toHex = (msg: number[] | Uint8Array): string => {
36
41
  if (CAN_USE_BUFFER) {
37
42
  return BufferCtor.from(msg).toString('hex')
38
43
  }
@@ -44,11 +49,24 @@ export const toHex = (msg: number[]): string => {
44
49
  return out.join('')
45
50
  }
46
51
 
52
+ /**
53
+ * Converts various message formats into a Uint8Array.
54
+ * Supports Uint8Array, arrays of bytes, hexadecimal strings, base64 strings, and UTF-8 strings.
55
+ *
56
+ * @param {any} msg - The input message (Uint8Array, number[], Array or string).
57
+ * @param {('hex' | 'utf8' | 'base64')} enc - Specifies the string encoding, if applicable.
58
+ * @returns {Uint8Array}
59
+ */
60
+ export const toUint8Array = (msg: any, enc?: 'hex' | 'utf8' | 'base64'): Uint8Array => {
61
+ if (msg instanceof Uint8Array) return msg
62
+ return new Uint8Array(toArray(msg, enc))
63
+ }
64
+
47
65
  /**
48
66
  * Converts various message formats into an array of numbers.
49
67
  * Supports arrays, hexadecimal strings, base64 strings, and UTF-8 strings.
50
68
  *
51
- * @param {any} msg - The input message (array or string).
69
+ * @param {any} msg - The input message (Uint8Array, number[], Array or string).
52
70
  * @param {('hex' | 'utf8' | 'base64')} enc - Specifies the string encoding, if applicable.
53
71
  * @returns {any[]} - Array representation of the input.
54
72
  */
@@ -2,12 +2,9 @@ import MerklePath from './MerklePath.js'
2
2
  import Transaction from './Transaction.js'
3
3
  import ChainTracker from './ChainTracker.js'
4
4
  import BeefTx from './BeefTx.js'
5
- import { Reader, Writer, toHex, toArray, verifyNotNull } from '../primitives/utils.js'
5
+ import { Reader, Writer, toHex, toArray, verifyNotNull, ReaderUint8Array, WriterUint8Array, toUint8Array } from '../primitives/utils.js'
6
6
  import { hash256 } from '../primitives/Hash.js'
7
7
 
8
- const BufferCtor =
9
- typeof globalThis !== 'undefined' ? (globalThis as any).Buffer : undefined
10
-
11
8
  export const BEEF_V1 = 4022206465 // 0100BEEF in LE order
12
9
  export const BEEF_V2 = 4022206466 // 0200BEEF in LE order
13
10
  export const ATOMIC_BEEF = 0x01010101 // 01010101
@@ -112,13 +109,37 @@ export class Beef {
112
109
  this.ensureSerializableState()
113
110
  if (this.rawBytesCache == null) {
114
111
  this.ensureSortedForSerialization()
115
- const writer = new Writer()
112
+ const writer = new WriterUint8Array()
116
113
  this.toWriter(writer)
117
114
  this.rawBytesCache = writer.toUint8Array()
118
115
  }
119
116
  return this.rawBytesCache
120
117
  }
121
118
 
119
+ private getBeefForAtomic (txid: string): { beef: Beef, writer: WriterUint8Array } {
120
+ if (this.needsSort) {
121
+ this.sortTxs()
122
+ }
123
+ const tx = this.findTxid(txid)
124
+ if (tx == null) {
125
+ throw new Error(`${txid} does not exist in this Beef`)
126
+ }
127
+
128
+ // If the transaction is not the last one, clone and modify
129
+ const beef = (this.txs[this.txs.length - 1] === tx) ? this : this.clone()
130
+
131
+ if (beef !== this) {
132
+ const i = this.txs.findIndex((t) => t.txid === txid)
133
+ beef.txs.splice(i + 1)
134
+ }
135
+
136
+ const writer = new WriterUint8Array()
137
+ writer.writeUInt32LE(ATOMIC_BEEF)
138
+ writer.writeReverse(toArray(txid, 'hex'))
139
+
140
+ return { beef, writer }
141
+ }
142
+
122
143
  /**
123
144
  * @param txid of `beefTx` to find
124
145
  * @returns `BeefTx` in `txs` with `txid`.
@@ -308,7 +329,7 @@ export class Beef {
308
329
  * @param bumpIndex Optional. If a number, must be valid index into bumps array.
309
330
  * @returns txid of rawTx
310
331
  */
311
- mergeRawTx (rawTx: number[], bumpIndex?: number): BeefTx {
332
+ mergeRawTx (rawTx: number[] | Uint8Array, bumpIndex?: number): BeefTx {
312
333
  this.markMutated(true)
313
334
  const newTx: BeefTx = new BeefTx(rawTx, bumpIndex)
314
335
  this.removeExistingTxid(newTx.txid)
@@ -394,8 +415,8 @@ export class Beef {
394
415
  return beefTx
395
416
  }
396
417
 
397
- mergeBeef (beef: number[] | Beef): void {
398
- const b: Beef = Array.isArray(beef) ? Beef.fromBinary(beef) : beef
418
+ mergeBeef (beef: Beef | number[] | Uint8Array): void {
419
+ const b: Beef = (beef instanceof Beef) ? beef : Beef.fromBinary(beef)
399
420
 
400
421
  for (const bump of b.bumps) {
401
422
  this.mergeBump(bump)
@@ -549,7 +570,7 @@ export class Beef {
549
570
  * Serializes this data to `writer`
550
571
  * @param writer
551
572
  */
552
- toWriter (writer: Writer): void {
573
+ toWriter (writer: Writer | WriterUint8Array): void {
553
574
  writer.writeUInt32LE(this.version)
554
575
 
555
576
  writer.writeVarIntNum(this.bumps.length)
@@ -566,11 +587,16 @@ export class Beef {
566
587
  /**
567
588
  * Returns a binary array representing the serialized BEEF
568
589
  * @returns A binary array representing the BEEF
590
+ * @returns An array of byte values containing binary serialization of the BEEF
569
591
  */
570
592
  toBinary (): number[] {
571
593
  return Array.from(this.getSerializedBytes())
572
594
  }
573
595
 
596
+ /**
597
+ * Returns a binary array representing the serialized BEEF
598
+ * @returns A Uint8Array containing binary serialization of the BEEF
599
+ */
574
600
  toUint8Array (): Uint8Array {
575
601
  return this.getSerializedBytes()
576
602
  }
@@ -586,30 +612,31 @@ export class Beef {
586
612
  * @returns serialized contents of this Beef with AtomicBEEF prefix.
587
613
  */
588
614
  toBinaryAtomic (txid: string): number[] {
589
- if (this.needsSort) {
590
- this.sortTxs()
591
- }
592
- const tx = this.findTxid(txid)
593
- if (tx == null) {
594
- throw new Error(`${txid} does not exist in this Beef`)
595
- }
596
-
597
- // If the transaction is not the last one, clone and modify
598
- const beef = (this.txs[this.txs.length - 1] === tx) ? this : this.clone()
599
-
600
- if (beef !== this) {
601
- const i = this.txs.findIndex((t) => t.txid === txid)
602
- beef.txs.splice(i + 1)
603
- }
604
-
605
- const writer = new Writer()
606
- writer.writeUInt32LE(ATOMIC_BEEF)
607
- writer.writeReverse(toArray(txid, 'hex'))
615
+ const { beef, writer } = this.getBeefForAtomic(txid)
608
616
  beef.toWriter(writer)
609
-
610
617
  return writer.toArray()
611
618
  }
612
619
 
620
+ /**
621
+ * Serialize this Beef as AtomicBEEF.
622
+ *
623
+ * `txid` must exist
624
+ *
625
+ * after sorting, if txid is not last txid, creates a clone and removes newer txs
626
+ *
627
+ * @param txid
628
+ * @returns serialized contents of this Beef with AtomicBEEF prefix.
629
+ */
630
+ toUint8ArrayAtomic (txid: string): Uint8Array {
631
+ const { beef, writer } = this.getBeefForAtomic(txid)
632
+ const beefUint8 = beef.getSerializedBytes()
633
+ const prefix = writer.toUint8Array()
634
+ const atomic = new Uint8Array(prefix.length + beefUint8.length)
635
+ atomic.set(prefix, 0)
636
+ atomic.set(beefUint8, prefix.length)
637
+ return atomic
638
+ }
639
+
613
640
  /**
614
641
  * Returns a hex string representing the serialized BEEF
615
642
  * @returns A hex string representing the BEEF
@@ -619,15 +646,12 @@ export class Beef {
619
646
  return this.hexCache
620
647
  }
621
648
  const bytes = this.getSerializedBytes()
622
- const hex =
623
- BufferCtor != null
624
- ? BufferCtor.from(bytes).toString('hex')
625
- : toHex(Array.from(bytes))
649
+ const hex = toHex(bytes)
626
650
  this.hexCache = hex
627
651
  return hex
628
652
  }
629
653
 
630
- static fromReader (br: Reader): Beef {
654
+ static fromReader (br: Reader | ReaderUint8Array): Beef {
631
655
  let version = br.readUInt32LE()
632
656
  let atomicTxid: string | undefined
633
657
  if (version === ATOMIC_BEEF) {
@@ -657,11 +681,11 @@ export class Beef {
657
681
 
658
682
  /**
659
683
  * Constructs an instance of the Beef class based on the provided binary array
660
- * @param bin The binary array from which to construct BEEF
684
+ * @param bin The binary array or Uint8Array from which to construct BEEF
661
685
  * @returns An instance of the Beef class constructed from the binary data
662
686
  */
663
- static fromBinary (bin: number[]): Beef {
664
- const br = new Reader(bin)
687
+ static fromBinary (bin: number[] | Uint8Array): Beef {
688
+ const br = ReaderUint8Array.makeReader(bin)
665
689
  return Beef.fromReader(br)
666
690
  }
667
691
 
@@ -672,8 +696,8 @@ export class Beef {
672
696
  * @returns An instance of the Beef class constructed from the string
673
697
  */
674
698
  static fromString (s: string, enc: 'hex' | 'utf8' | 'base64' = 'hex'): Beef {
675
- const bin = toArray(s, enc)
676
- const br = new Reader(bin)
699
+ const bin = toUint8Array(s, enc)
700
+ const br = new ReaderUint8Array(bin)
677
701
  return Beef.fromReader(br)
678
702
  }
679
703
 
@@ -825,6 +849,8 @@ export class Beef {
825
849
  c.txs = Array.from(this.txs)
826
850
  c.txidIndex = undefined
827
851
  c.needsSort = this.needsSort
852
+ c.hexCache = this.hexCache
853
+ c.rawBytesCache = this.rawBytesCache
828
854
  return c
829
855
  }
830
856
 
@@ -844,7 +870,44 @@ export class Beef {
844
870
  i++
845
871
  }
846
872
  }
847
- // TODO: bumps could be trimmed to eliminate unreferenced proofs.
873
+
874
+ // Trim unreferenced bumps after removing known txids
875
+ const referencedBumpIndices = new Set<number>()
876
+ for (const tx of this.txs) {
877
+ if (tx.bumpIndex !== undefined) {
878
+ referencedBumpIndices.add(tx.bumpIndex)
879
+ }
880
+ }
881
+
882
+ // Check if there are any unreferenced bumps to remove
883
+ if (referencedBumpIndices.size < this.bumps.length) {
884
+ // Build mapping of old indices to new indices after removal
885
+ const indexMap = new Map<number, number>()
886
+ let newIndex = 0
887
+ for (let i = 0; i < this.bumps.length; i++) {
888
+ if (referencedBumpIndices.has(i)) {
889
+ indexMap.set(i, newIndex)
890
+ newIndex++
891
+ }
892
+ }
893
+
894
+ // Remove unreferenced bumps
895
+ this.bumps = this.bumps.filter((_, i) => referencedBumpIndices.has(i))
896
+
897
+ // Update all transaction bumpIndex references
898
+ for (const tx of this.txs) {
899
+ if (tx.bumpIndex !== undefined) {
900
+ const newIndex = indexMap.get(tx.bumpIndex)
901
+ if (newIndex === undefined) {
902
+ throw new Error(`Internal error: bumpIndex ${tx.bumpIndex} not found in indexMap`)
903
+ }
904
+ tx.bumpIndex = newIndex
905
+ }
906
+ }
907
+
908
+ mutated = true
909
+ }
910
+
848
911
  if (mutated) {
849
912
  this.markMutated(true)
850
913
  }
@@ -1,5 +1,5 @@
1
1
  import { hash256 } from '../primitives/Hash.js'
2
- import { Reader, Writer, toHex, toArray } from '../primitives/utils.js'
2
+ import { Reader, Writer, toHex, toArray, ReaderUint8Array, WriterUint8Array } from '../primitives/utils.js'
3
3
  import Transaction from './Transaction.js'
4
4
  import { BEEF_V2, TX_DATA_FORMAT } from './Beef.js'
5
5
 
@@ -15,7 +15,7 @@ import { BEEF_V2, TX_DATA_FORMAT } from './Beef.js'
15
15
  export default class BeefTx {
16
16
  _bumpIndex?: number
17
17
  _tx?: Transaction
18
- _rawTx?: number[]
18
+ _rawTx?: Uint8Array // ← changed to Uint8Array internally
19
19
  _txid?: string
20
20
  inputTxids: string[] = []
21
21
  /**
@@ -64,10 +64,28 @@ export default class BeefTx {
64
64
  return undefined
65
65
  }
66
66
 
67
+ /**
68
+ * Legacy compatibility getter — returns number[] (Byte[])
69
+ */
67
70
  get rawTx (): number[] | undefined {
71
+ if (this._rawTx != null) {
72
+ return Array.from(this._rawTx)
73
+ }
74
+ if (this._tx != null) {
75
+ const bytes = this._tx.toUint8Array()
76
+ this._rawTx = bytes // cache
77
+ return Array.from(bytes)
78
+ }
79
+ return undefined
80
+ }
81
+
82
+ /**
83
+ * Preferred modern getter — returns Uint8Array (zero-copy where possible)
84
+ */
85
+ get rawTxUint8Array (): Uint8Array | undefined {
68
86
  if (this._rawTx != null) return this._rawTx
69
87
  if (this._tx != null) {
70
- this._rawTx = this._tx.toBinary()
88
+ this._rawTx = this._tx.toUint8Array()
71
89
  return this._rawTx
72
90
  }
73
91
  return undefined
@@ -77,13 +95,17 @@ export default class BeefTx {
77
95
  * @param tx If string, must be a valid txid. If `number[]` must be a valid serialized transaction.
78
96
  * @param bumpIndex If transaction already has a proof in the beef to which it will be added.
79
97
  */
80
- constructor (tx: Transaction | number[] | string, bumpIndex?: number) {
98
+ constructor (tx: Transaction | Uint8Array | number[] | string, bumpIndex?: number) {
81
99
  if (typeof tx === 'string') {
82
100
  this._txid = tx
83
- } else if (Array.isArray(tx)) {
101
+ } else if (tx instanceof Uint8Array) {
84
102
  this._rawTx = tx
85
- } else {
103
+ } else if (Array.isArray(tx)) {
104
+ this._rawTx = new Uint8Array(tx)
105
+ } else if (tx instanceof Transaction) {
86
106
  this._tx = tx
107
+ } else {
108
+ throw new Error('Invalid transaction data type')
87
109
  }
88
110
  this.bumpIndex = bumpIndex
89
111
  this.updateInputTxids()
@@ -93,7 +115,7 @@ export default class BeefTx {
93
115
  return new BeefTx(tx, bumpIndex)
94
116
  }
95
117
 
96
- static fromRawTx (rawTx: number[], bumpIndex?: number): BeefTx {
118
+ static fromRawTx (rawTx: Uint8Array | number[], bumpIndex?: number): BeefTx {
97
119
  return new BeefTx(rawTx, bumpIndex)
98
120
  }
99
121
 
@@ -106,18 +128,17 @@ export default class BeefTx {
106
128
  // If we have a proof, or don't have a parsed transaction
107
129
  this.inputTxids = []
108
130
  } else {
109
- const inputTxids: Record<string, boolean> = {} // Explicit object type
131
+ const inputTxids: Set<string> = new Set() // minor perf improvement
110
132
  for (const input of this.tx.inputs) {
111
133
  if (input.sourceTXID !== undefined && input.sourceTXID !== null && input.sourceTXID !== '') {
112
- // ✅ Ensure sourceTXID is defined
113
- inputTxids[input.sourceTXID] = true
134
+ inputTxids.add(input.sourceTXID)
114
135
  }
115
136
  }
116
- this.inputTxids = Object.keys(inputTxids)
137
+ this.inputTxids = Array.from(inputTxids)
117
138
  }
118
139
  }
119
140
 
120
- toWriter (writer: Writer, version: number): void {
141
+ toWriter (writer: Writer | WriterUint8Array, version: number): void {
121
142
  const writeByte = (bb: number): void => {
122
143
  writer.writeUInt8(bb)
123
144
  }
@@ -130,13 +151,11 @@ export default class BeefTx {
130
151
  }
131
152
 
132
153
  const writeTx = (): void => {
133
- if (this._rawTx != null) {
134
- writer.write(this._rawTx)
135
- } else if (this._tx != null) {
136
- writer.write(this._tx.toUint8Array())
137
- } else {
154
+ const bytes = this.rawTxUint8Array
155
+ if (bytes == null) {
138
156
  throw new Error('a valid serialized Transaction is expected')
139
157
  }
158
+ writer.write(bytes)
140
159
  }
141
160
 
142
161
  const writeBumpIndex = (): void => {
@@ -166,8 +185,8 @@ export default class BeefTx {
166
185
  }
167
186
  }
168
187
 
169
- static fromReader (br: Reader, version: number): BeefTx {
170
- let data: Transaction | number[] | string | undefined
188
+ static fromReader (br: Reader | ReaderUint8Array, version: number): BeefTx {
189
+ let data: Transaction | undefined
171
190
  let bumpIndex: number | undefined
172
191
  let beefTx: BeefTx | undefined
173
192
  if (version === BEEF_V2) {