opnet 1.8.5 → 1.8.7

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.
@@ -7,7 +7,7 @@ export declare class UTXOsManager {
7
7
  constructor(provider: IProviderForUTXO);
8
8
  spentUTXO(address: string, spent: UTXOs, newUTXOs: UTXOs): void;
9
9
  getPendingUTXOs(address: string): UTXOs;
10
- clean(address?: string): void;
10
+ clean(address?: string, threshold?: bigint): void;
11
11
  getUTXOs({ address, isCSV, optimize, mergePendingUTXOs, filterSpentUTXOs, olderThan, }: RequestUTXOsParams): Promise<UTXOs>;
12
12
  getUTXOsForAmount({ address, amount, csvAddress, optimize, mergePendingUTXOs, filterSpentUTXOs, throwErrors, olderThan, maxUTXOs, throwIfUTXOsLimitReached, }: RequestUTXOsParamsWithAmount): Promise<UTXOs>;
13
13
  getMultipleUTXOs({ requests, mergePendingUTXOs, filterSpentUTXOs, }: RequestMultipleUTXOsParams): Promise<Record<string, UTXOs>>;
@@ -42,9 +42,9 @@ export class UTXOsManager {
42
42
  const addressData = this.getAddressData(address);
43
43
  return addressData.pendingUTXOs;
44
44
  }
45
- clean(address) {
45
+ clean(address, threshold) {
46
46
  if (address) {
47
- const addressData = this.getAddressData(address);
47
+ const addressData = this.getAddressData(address, threshold);
48
48
  addressData.spentUTXOs = [];
49
49
  addressData.pendingUTXOs = [];
50
50
  addressData.pendingUtxoDepth = {};
@@ -57,7 +57,7 @@ export class UTXOsManager {
57
57
  }
58
58
  }
59
59
  async getUTXOs({ address, isCSV = false, optimize = true, mergePendingUTXOs = true, filterSpentUTXOs = true, olderThan, }) {
60
- const addressData = this.getAddressData(address);
60
+ const addressData = this.getAddressData(address, olderThan);
61
61
  const fetchedData = await this.maybeFetchUTXOs(address, optimize, olderThan, isCSV);
62
62
  const utxoKey = (utxo) => `${utxo.transactionId}:${utxo.outputIndex}`;
63
63
  const spentRefKey = (ref) => `${ref.transactionId}:${ref.outputIndex}`;
@@ -244,9 +244,10 @@ export class UTXOsManager {
244
244
  }
245
245
  return result;
246
246
  }
247
- getAddressData(address) {
248
- if (!this.dataByAddress[address]) {
249
- this.dataByAddress[address] = {
247
+ getAddressData(address, threshold) {
248
+ const addressWithThreshold = threshold ? `${address}_${threshold}` : address;
249
+ if (!this.dataByAddress[addressWithThreshold]) {
250
+ this.dataByAddress[addressWithThreshold] = {
250
251
  spentUTXOs: [],
251
252
  pendingUTXOs: [],
252
253
  pendingUtxoDepth: {},
@@ -255,21 +256,23 @@ export class UTXOsManager {
255
256
  lastFetchedData: null,
256
257
  };
257
258
  }
258
- return this.dataByAddress[address];
259
+ return this.dataByAddress[addressWithThreshold];
259
260
  }
260
261
  async maybeFetchUTXOs(address, optimize, olderThan, isCSV = false) {
261
- const addressData = this.getAddressData(address);
262
+ const addressData = this.getAddressData(address, olderThan);
262
263
  const now = Date.now();
263
264
  const age = now - addressData.lastFetchTimestamp;
264
265
  if (now - addressData.lastCleanup > AUTO_PURGE_AFTER) {
265
- this.clean(address);
266
+ this.clean(address, olderThan);
266
267
  }
267
268
  if (addressData.lastFetchedData && age < FETCH_COOLDOWN) {
268
269
  return addressData.lastFetchedData;
269
270
  }
270
271
  addressData.lastFetchedData = await this.fetchUTXOs(address, optimize, olderThan, isCSV);
271
272
  addressData.lastFetchTimestamp = now;
272
- this.syncPendingDepthWithFetched(address);
273
+ if (!olderThan) {
274
+ this.syncPendingDepthWithFetched(address);
275
+ }
273
276
  return addressData.lastFetchedData;
274
277
  }
275
278
  async fetchUTXOs(address, optimize = false, olderThan, isCSV = false) {
@@ -30,7 +30,6 @@ import {
30
30
  | `name()` | - | `string` | Collection name |
31
31
  | `symbol()` | - | `string` | Collection symbol |
32
32
  | `maxSupply()` | - | `bigint` | Maximum supply |
33
- | `collectionInfo()` | - | `{ icon, banner, description, website }` | Collection metadata |
34
33
  | `tokenURI(tokenId)` | `bigint` | `string` | Token metadata URI |
35
34
  | `metadata()` | - | Full metadata object | All metadata in one call |
36
35
 
@@ -79,7 +78,7 @@ import {
79
78
  | `changeMetadata()` | - | - | Trigger metadata change |
80
79
  | `setBaseURI(baseURI)` | `string` | - | Set base URI for tokens |
81
80
  | `domainSeparator()` | - | `Uint8Array` | EIP-712 domain separator |
82
- | `getApproveNonce(owner)` | `Address` | `bigint` | Get nonce for signatures |
81
+ | `nonceOf(owner)` | `Address` | `bigint` | Get nonce for signatures |
83
82
 
84
83
  ---
85
84
 
@@ -94,7 +93,7 @@ interface TransferredEventNFT {
94
93
  operator: Address; // Who initiated the transfer
95
94
  from: Address; // Sender
96
95
  to: Address; // Recipient
97
- amount: bigint; // Amount (1 for single NFT)
96
+ tokenId: bigint; // Token ID
98
97
  }
99
98
  ```
100
99
 
@@ -105,8 +104,8 @@ Emitted when single token approval changes.
105
104
  ```typescript
106
105
  interface ApprovedEventNFT {
107
106
  owner: Address; // Token owner
108
- spender: Address; // Approved spender
109
- amount: bigint; // Token ID as amount
107
+ operator: Address; // Approved operator
108
+ tokenId: bigint; // Token ID
110
109
  }
111
110
  ```
112
111
 
@@ -122,6 +121,28 @@ interface ApprovedForAllEventNFT {
122
121
  }
123
122
  ```
124
123
 
124
+ ### Burned
125
+
126
+ Emitted when a token is burned.
127
+
128
+ ```typescript
129
+ interface BurnedEventNFT {
130
+ from: Address; // Token owner
131
+ tokenId: bigint; // Token ID
132
+ }
133
+ ```
134
+
135
+ ### Minted
136
+
137
+ Emitted when a new token is minted.
138
+
139
+ ```typescript
140
+ interface MintedEventNFT {
141
+ to: Address; // Recipient
142
+ tokenId: bigint; // Token ID
143
+ }
144
+ ```
145
+
125
146
  ### URI
126
147
 
127
148
  Emitted when token URI changes.
@@ -165,10 +186,10 @@ console.log('Collection:', name.properties.name);
165
186
  console.log('Symbol:', symbol.properties.symbol);
166
187
  console.log('Max Supply:', maxSupply.properties.maxSupply);
167
188
 
168
- // Get full collection metadata
169
- const info = await nft.collectionInfo();
170
- console.log('Description:', info.properties.description);
171
- console.log('Website:', info.properties.website);
189
+ // Get full collection metadata in one call
190
+ const metadata = await nft.metadata();
191
+ console.log('Description:', metadata.properties.description);
192
+ console.log('Website:', metadata.properties.website);
172
193
  ```
173
194
 
174
195
  ### Check Ownership
@@ -363,18 +384,6 @@ export const OP_721_ABI: BitcoinInterfaceAbi = [
363
384
  outputs: [{ name: 'maxSupply', type: ABIDataTypes.UINT256 }],
364
385
  type: BitcoinAbiTypes.Function,
365
386
  },
366
- {
367
- name: 'collectionInfo',
368
- constant: true,
369
- inputs: [],
370
- outputs: [
371
- { name: 'icon', type: ABIDataTypes.STRING },
372
- { name: 'banner', type: ABIDataTypes.STRING },
373
- { name: 'description', type: ABIDataTypes.STRING },
374
- { name: 'website', type: ABIDataTypes.STRING },
375
- ],
376
- type: BitcoinAbiTypes.Function,
377
- },
378
387
  {
379
388
  name: 'tokenURI',
380
389
  constant: true,
@@ -549,7 +558,7 @@ export const OP_721_ABI: BitcoinInterfaceAbi = [
549
558
  type: BitcoinAbiTypes.Function,
550
559
  },
551
560
  {
552
- name: 'getApproveNonce',
561
+ name: 'nonceOf',
553
562
  constant: true,
554
563
  inputs: [{ name: 'owner', type: ABIDataTypes.ADDRESS }],
555
564
  outputs: [{ name: 'nonce', type: ABIDataTypes.UINT256 }],
@@ -563,7 +572,7 @@ export const OP_721_ABI: BitcoinInterfaceAbi = [
563
572
  { name: 'operator', type: ABIDataTypes.ADDRESS },
564
573
  { name: 'from', type: ABIDataTypes.ADDRESS },
565
574
  { name: 'to', type: ABIDataTypes.ADDRESS },
566
- { name: 'amount', type: ABIDataTypes.UINT256 },
575
+ { name: 'tokenId', type: ABIDataTypes.UINT256 },
567
576
  ],
568
577
  type: BitcoinAbiTypes.Event,
569
578
  },
@@ -571,8 +580,8 @@ export const OP_721_ABI: BitcoinInterfaceAbi = [
571
580
  name: 'Approved',
572
581
  values: [
573
582
  { name: 'owner', type: ABIDataTypes.ADDRESS },
574
- { name: 'spender', type: ABIDataTypes.ADDRESS },
575
- { name: 'amount', type: ABIDataTypes.UINT256 },
583
+ { name: 'operator', type: ABIDataTypes.ADDRESS },
584
+ { name: 'tokenId', type: ABIDataTypes.UINT256 },
576
585
  ],
577
586
  type: BitcoinAbiTypes.Event,
578
587
  },
@@ -585,6 +594,22 @@ export const OP_721_ABI: BitcoinInterfaceAbi = [
585
594
  ],
586
595
  type: BitcoinAbiTypes.Event,
587
596
  },
597
+ {
598
+ name: 'Burned',
599
+ values: [
600
+ { name: 'from', type: ABIDataTypes.ADDRESS },
601
+ { name: 'tokenId', type: ABIDataTypes.UINT256 },
602
+ ],
603
+ type: BitcoinAbiTypes.Event,
604
+ },
605
+ {
606
+ name: 'Minted',
607
+ values: [
608
+ { name: 'to', type: ABIDataTypes.ADDRESS },
609
+ { name: 'tokenId', type: ABIDataTypes.UINT256 },
610
+ ],
611
+ type: BitcoinAbiTypes.Event,
612
+ },
588
613
  {
589
614
  name: 'URI',
590
615
  values: [
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "opnet",
3
3
  "type": "module",
4
- "version": "1.8.5",
4
+ "version": "1.8.7",
5
5
  "author": "OP_NET",
6
6
  "description": "The perfect library for building Bitcoin-based applications.",
7
7
  "engines": {
@@ -101,7 +101,7 @@
101
101
  },
102
102
  "devDependencies": {
103
103
  "@babel/core": "^7.29.0",
104
- "@babel/preset-env": "^7.29.0",
104
+ "@babel/preset-env": "^7.29.2",
105
105
  "@babel/preset-typescript": "^7.28.5",
106
106
  "@eslint/js": "^10.0.1",
107
107
  "@microsoft/api-extractor": "7.57.7",
@@ -116,7 +116,7 @@
116
116
  "stream-browserify": "^3.0.0",
117
117
  "typedoc": "^0.28.17",
118
118
  "typescript": "^5.9.3",
119
- "typescript-eslint": "^8.57.0",
119
+ "typescript-eslint": "^8.57.1",
120
120
  "vite": "^8.0.0",
121
121
  "vite-plugin-dts": "4.5.4",
122
122
  "vite-plugin-node-polyfills": "^0.25.0",
@@ -143,7 +143,7 @@
143
143
  "p-limit": "^7.3.0",
144
144
  "pako": "^2.1.0",
145
145
  "protobufjs": "^8.0.0",
146
- "undici": "^7.24.0"
146
+ "undici": "^7.24.4"
147
147
  },
148
148
  "overrides": {
149
149
  "vite-plugin-node-polyfills": {
package/src/_version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '1.8.5';
1
+ export const version = '1.8.7';
@@ -23,7 +23,7 @@ export const OP721Events: BitcoinInterfaceAbi = [
23
23
  type: ABIDataTypes.ADDRESS,
24
24
  },
25
25
  {
26
- name: 'amount',
26
+ name: 'tokenId',
27
27
  type: ABIDataTypes.UINT256,
28
28
  },
29
29
  ],
@@ -37,11 +37,11 @@ export const OP721Events: BitcoinInterfaceAbi = [
37
37
  type: ABIDataTypes.ADDRESS,
38
38
  },
39
39
  {
40
- name: 'spender',
40
+ name: 'operator',
41
41
  type: ABIDataTypes.ADDRESS,
42
42
  },
43
43
  {
44
- name: 'amount',
44
+ name: 'tokenId',
45
45
  type: ABIDataTypes.UINT256,
46
46
  },
47
47
  ],
@@ -65,6 +65,34 @@ export const OP721Events: BitcoinInterfaceAbi = [
65
65
  ],
66
66
  type: BitcoinAbiTypes.Event,
67
67
  },
68
+ {
69
+ name: 'Burned',
70
+ values: [
71
+ {
72
+ name: 'from',
73
+ type: ABIDataTypes.ADDRESS
74
+ },
75
+ {
76
+ name: 'tokenId',
77
+ type: ABIDataTypes.UINT256
78
+ },
79
+ ],
80
+ type: BitcoinAbiTypes.Event,
81
+ },
82
+ {
83
+ name: 'Minted',
84
+ values: [
85
+ {
86
+ name: 'to',
87
+ type: ABIDataTypes.ADDRESS,
88
+ },
89
+ {
90
+ name: 'tokenId',
91
+ type: ABIDataTypes.UINT256,
92
+ },
93
+ ],
94
+ type: BitcoinAbiTypes.Event,
95
+ },
68
96
  {
69
97
  name: 'URI',
70
98
  values: [
@@ -121,30 +149,6 @@ export const OP_721_ABI: BitcoinInterfaceAbi = [
121
149
  },
122
150
  ],
123
151
  },
124
- {
125
- name: 'collectionInfo',
126
- type: BitcoinAbiTypes.Function,
127
- constant: true,
128
- inputs: [],
129
- outputs: [
130
- {
131
- name: 'icon',
132
- type: ABIDataTypes.STRING,
133
- },
134
- {
135
- name: 'banner',
136
- type: ABIDataTypes.STRING,
137
- },
138
- {
139
- name: 'description',
140
- type: ABIDataTypes.STRING,
141
- },
142
- {
143
- name: 'website',
144
- type: ABIDataTypes.STRING,
145
- },
146
- ],
147
- },
148
152
  {
149
153
  name: 'tokenURI',
150
154
  type: BitcoinAbiTypes.Function,
@@ -426,7 +430,7 @@ export const OP_721_ABI: BitcoinInterfaceAbi = [
426
430
  ],
427
431
  },
428
432
  {
429
- name: 'getApproveNonce',
433
+ name: 'nonceOf',
430
434
  type: BitcoinAbiTypes.Function,
431
435
  constant: true,
432
436
  inputs: [
@@ -8,7 +8,6 @@ import {
8
8
  } from './interfaces/IBlockWitness.js';
9
9
 
10
10
  export class BlockWitnessAPI implements IBlockWitnessAPI {
11
- public readonly trusted: boolean;
12
11
  public readonly signature: Uint8Array;
13
12
  public readonly timestamp: number;
14
13
  public readonly proofs: readonly Uint8Array[];
@@ -16,7 +15,6 @@ export class BlockWitnessAPI implements IBlockWitnessAPI {
16
15
  public readonly publicKey?: Address;
17
16
 
18
17
  constructor(data: RawBlockWitnessAPI) {
19
- this.trusted = data.trusted;
20
18
  this.signature = stringBase64ToBuffer(data.signature);
21
19
  this.timestamp = data.timestamp;
22
20
  this.proofs = Object.freeze(data.proofs.map((proof) => stringBase64ToBuffer(proof)));
@@ -1,7 +1,6 @@
1
1
  import { Address } from '@btc-vision/transaction';
2
2
 
3
3
  export interface IBlockWitnessAPI {
4
- readonly trusted: boolean;
5
4
  readonly signature: Uint8Array;
6
5
  readonly timestamp: number;
7
6
 
@@ -12,7 +11,6 @@ export interface IBlockWitnessAPI {
12
11
  }
13
12
 
14
13
  export interface RawBlockWitnessAPI {
15
- readonly trusted: boolean;
16
14
  readonly signature: string;
17
15
  readonly timestamp: number;
18
16
 
@@ -759,7 +759,6 @@ export abstract class AbstractRpcProvider {
759
759
  * Get block witnesses.
760
760
  * @description This method is used to get the witnesses of a block. This proves that the actions executed inside a block are valid and confirmed by the network. If the minimum number of witnesses are not met, the block is considered as potentially invalid.
761
761
  * @param {BlockTag} height The block number or hash, use -1 for latest block
762
- * @param {boolean} [trusted] Whether to trust the witnesses or not
763
762
  * @param {number} [limit] The maximum number of witnesses to return
764
763
  * @param {number} [page] The page number of the witnesses
765
764
  * @returns {Promise<BlockWitnesses>} The witnesses of the block
@@ -768,13 +767,11 @@ export abstract class AbstractRpcProvider {
768
767
  */
769
768
  public async getBlockWitness(
770
769
  height: BigNumberish = -1,
771
- trusted?: boolean,
772
770
  limit?: number,
773
771
  page?: number,
774
772
  ): Promise<BlockWitnesses> {
775
- const params: [BigNumberish, boolean?, number?, number?] = [height.toString()];
773
+ const params: [BigNumberish, number?, number?] = [height.toString()];
776
774
 
777
- if (trusted !== undefined && trusted !== null) params.push(trusted);
778
775
  if (limit !== undefined && limit !== null) params.push(limit);
779
776
  if (page !== undefined && page !== null) params.push(page);
780
777
 
@@ -398,12 +398,11 @@ export class WebSocketRpcProvider extends AbstractRpcProvider {
398
398
  };
399
399
 
400
400
  case JSONRpcMethods.BLOCK_WITNESS:
401
- // GetBlockWitnessRequest: requestId=1, height=2, trusted=3, limit=4, page=5
401
+ // GetBlockWitnessRequest: requestId=1, height=2, limit=3, page=4
402
402
  return {
403
403
  2: Long.fromString(String(params[0] ?? -1)),
404
404
  3: params[1],
405
405
  4: params[2],
406
- 5: params[3],
407
406
  };
408
407
 
409
408
  case JSONRpcMethods.GAS:
@@ -116,10 +116,10 @@ export class UTXOsManager {
116
116
  /**
117
117
  * Clean (reset) the data for a particular address or for all addresses if none is passed.
118
118
  */
119
- public clean(address?: string): void {
119
+ public clean(address?: string, threshold?: bigint): void {
120
120
  if (address) {
121
121
  // Reset a single address
122
- const addressData = this.getAddressData(address);
122
+ const addressData = this.getAddressData(address, threshold);
123
123
  addressData.spentUTXOs = [];
124
124
  addressData.pendingUTXOs = [];
125
125
  addressData.pendingUtxoDepth = {};
@@ -156,7 +156,7 @@ export class UTXOsManager {
156
156
  filterSpentUTXOs = true,
157
157
  olderThan,
158
158
  }: RequestUTXOsParams): Promise<UTXOs> {
159
- const addressData = this.getAddressData(address);
159
+ const addressData = this.getAddressData(address, olderThan);
160
160
  const fetchedData = await this.maybeFetchUTXOs(address, optimize, olderThan, isCSV);
161
161
 
162
162
  const utxoKey = (utxo: UTXO) => `${utxo.transactionId}:${utxo.outputIndex}`;
@@ -493,9 +493,10 @@ export class UTXOsManager {
493
493
  /**
494
494
  * Return the AddressData object for a given address. Initializes it if nonexistent.
495
495
  */
496
- private getAddressData(address: string): AddressData {
497
- if (!this.dataByAddress[address]) {
498
- this.dataByAddress[address] = {
496
+ private getAddressData(address: string, threshold?: bigint): AddressData {
497
+ const addressWithThreshold = threshold ? `${address}_${threshold}` : address;
498
+ if (!this.dataByAddress[addressWithThreshold]) {
499
+ this.dataByAddress[addressWithThreshold] = {
499
500
  spentUTXOs: [],
500
501
  pendingUTXOs: [],
501
502
  pendingUtxoDepth: {},
@@ -504,7 +505,7 @@ export class UTXOsManager {
504
505
  lastFetchedData: null,
505
506
  };
506
507
  }
507
- return this.dataByAddress[address];
508
+ return this.dataByAddress[addressWithThreshold];
508
509
  }
509
510
 
510
511
  /**
@@ -516,13 +517,13 @@ export class UTXOsManager {
516
517
  olderThan: bigint | undefined,
517
518
  isCSV: boolean = false,
518
519
  ): Promise<IUTXOsData> {
519
- const addressData = this.getAddressData(address);
520
+ const addressData = this.getAddressData(address, olderThan);
520
521
  const now = Date.now();
521
522
  const age = now - addressData.lastFetchTimestamp;
522
523
 
523
524
  // Purge if it's been too long for this address
524
525
  if (now - addressData.lastCleanup > AUTO_PURGE_AFTER) {
525
- this.clean(address); // Clean only this address data
526
+ this.clean(address, olderThan); // Clean only this address data
526
527
  }
527
528
 
528
529
  // If it's been less than FETCH_COOLDOWN ms, return cached data if available
@@ -535,7 +536,9 @@ export class UTXOsManager {
535
536
  addressData.lastFetchTimestamp = now;
536
537
 
537
538
  // Remove any pending UTXOs that have become confirmed or known spent
538
- this.syncPendingDepthWithFetched(address);
539
+ if (!olderThan) {
540
+ this.syncPendingDepthWithFetched(address);
541
+ }
539
542
 
540
543
  return addressData.lastFetchedData;
541
544
  }
@@ -5,7 +5,7 @@ import { AddressTypes } from '@btc-vision/transaction';
5
5
  import type { JsonRpcPayload } from '../src/providers/interfaces/JSONRpc.js';
6
6
  import type { JsonRpcCallResult } from '../src/providers/interfaces/JSONRpcResult.js';
7
7
  import type { MempoolInfo } from '../src/providers/interfaces/mempool/MempoolInfo.js';
8
- import type { IMempoolTransactionData } from '../src/providers/interfaces/mempool/MempoolTransactionData.js';
8
+ import type { IMempoolTransactionData, IMempoolOPNetTransactionData } from '../src/providers/interfaces/mempool/MempoolTransactionData.js';
9
9
 
10
10
  // ============================================================================
11
11
  // Mock provider that intercepts _send
@@ -146,7 +146,7 @@ describe('Mempool API - Unit Tests', () => {
146
146
  // ========================================================================
147
147
 
148
148
  describe('getPendingTransaction', () => {
149
- const mockTx: IMempoolTransactionData = {
149
+ const mockTx: IMempoolOPNetTransactionData = {
150
150
  id: 'abc123def456abc123def456abc123def456abc123def456abc123def456abc1',
151
151
  firstSeen: '2023-11-14T22:13:20.000Z',
152
152
  blockHeight: '0xcf080',
@@ -270,13 +270,14 @@ describe('Mempool API - Unit Tests', () => {
270
270
 
271
271
  it('should handle Generic transaction', async () => {
272
272
  const nonOPNetTx: IMempoolTransactionData = {
273
- ...mockTx,
273
+ id: mockTx.id,
274
+ firstSeen: mockTx.firstSeen,
275
+ blockHeight: mockTx.blockHeight,
274
276
  transactionType: 'Generic',
275
- theoreticalGasLimit: undefined,
276
- priorityFee: undefined,
277
- from: undefined,
278
- contractAddress: undefined,
279
- calldata: undefined,
277
+ psbt: mockTx.psbt,
278
+ inputs: mockTx.inputs,
279
+ outputs: mockTx.outputs,
280
+ raw: mockTx.raw,
280
281
  };
281
282
 
282
283
  provider.mockSend.mockResolvedValue([
@@ -350,7 +351,7 @@ describe('Mempool API - Unit Tests', () => {
350
351
  // ========================================================================
351
352
 
352
353
  describe('getLatestPendingTransactions', () => {
353
- const mockTx1: IMempoolTransactionData = {
354
+ const mockTx1: IMempoolOPNetTransactionData = {
354
355
  id: 'a000000000000000000000000000000000000000000000000000000000000001',
355
356
  firstSeen: '2023-11-14T22:13:20.000Z',
356
357
  blockHeight: '0xcf080',
@@ -603,7 +604,7 @@ describe('Mempool API - Unit Tests', () => {
603
604
  // ========================================================================
604
605
 
605
606
  describe('getLatestPendingTransactionsByAddresses', () => {
606
- const mockTx1: IMempoolTransactionData = {
607
+ const mockTx1: IMempoolOPNetTransactionData = {
607
608
  id: 'a000000000000000000000000000000000000000000000000000000000000001',
608
609
  firstSeen: '2023-11-14T22:13:20.000Z',
609
610
  blockHeight: '0xcf080',
@@ -710,12 +711,12 @@ describe('Mempool API - Unit Tests', () => {
710
711
  // Integration Tests - Real Regtest Network
711
712
  // ============================================================================
712
713
 
713
- describe('Mempool API - Integration Tests (regtest.opnet.org)', () => {
714
- const REGTEST_URL = 'https://regtest.opnet.org';
714
+ describe('Mempool API - Integration Tests (testnet.opnet.org)', () => {
715
+ const TESTNET_URL = 'https://testnet.opnet.org';
715
716
  let provider: JSONRpcProvider;
716
717
 
717
718
  beforeEach(() => {
718
- provider = new JSONRpcProvider({ url: REGTEST_URL, network: networks.regtest });
719
+ provider = new JSONRpcProvider({ url: TESTNET_URL, network: networks.opnetTestnet });
719
720
  });
720
721
 
721
722
  describe('getMempoolInfo', () => {
@@ -745,8 +746,8 @@ describe('Mempool API - Integration Tests (regtest.opnet.org)', () => {
745
746
  if (txs.length > 0) {
746
747
  const tx = txs[0];
747
748
  expect(typeof tx.id).toBe('string');
748
- expect(typeof tx.firstSeen).toBe('string');
749
- expect(typeof tx.blockHeight).toBe('string');
749
+ expect(tx.firstSeen).toBeDefined();
750
+ expect(tx.blockHeight).toBeDefined();
750
751
  expect(typeof tx.transactionType).toBe('string');
751
752
  expect(typeof tx.psbt).toBe('boolean');
752
753
  expect(Array.isArray(tx.inputs)).toBe(true);
@@ -83,7 +83,7 @@ function createMockProvider(): IProviderForUTXO & {
83
83
  } {
84
84
  const mockCallPayloadSingle = vi.fn<(payload: JsonRpcPayload) => Promise<JsonRpcResult>>();
85
85
  const mockCallMultiplePayloads = vi.fn<(payloads: JsonRpcPayload[]) => Promise<JsonRpcCallResult>>();
86
- const mockBuildJsonRpcPayload = vi.fn((method, params) => ({
86
+ const mockBuildJsonRpcPayload = vi.fn((method: unknown, params: unknown) => ({
87
87
  method,
88
88
  params,
89
89
  id: 1,
@@ -91,6 +91,7 @@ function createMockProvider(): IProviderForUTXO & {
91
91
  }));
92
92
 
93
93
  return {
94
+ // @ts-expect-error - This is a mockup for mockBuildJsonRpcPayload
94
95
  buildJsonRpcPayload: mockBuildJsonRpcPayload,
95
96
  callPayloadSingle: mockCallPayloadSingle,
96
97
  callMultiplePayloads: mockCallMultiplePayloads,
@@ -1159,7 +1160,7 @@ describe('UTXOsManager - Ultra Complex Tests', () => {
1159
1160
  expect(manager.getPendingUTXOs(address)).toHaveLength(1);
1160
1161
  });
1161
1162
 
1162
- it('should handle no fetched data gracefully', async () => {
1163
+ it('should handle no fetched data gracefully', () => {
1163
1164
  const address = 'bc1qtest...';
1164
1165
 
1165
1166
  // Add pending UTXO
@@ -1488,6 +1489,58 @@ describe('UTXOsManager - Ultra Complex Tests', () => {
1488
1489
  // Note: First call fetches, subsequent use cache
1489
1490
  expect(callCount).toBeGreaterThanOrEqual(1);
1490
1491
  });
1492
+
1493
+ it('should do real getUTXOs fetch for calls with different threshold', async () => {
1494
+ const address = 'bc1qconcurrent...';
1495
+
1496
+ let callCount = 0;
1497
+ mockProvider.mockCallPayloadSingle.mockImplementation(async (data: unknown) => {
1498
+ ++callCount;
1499
+
1500
+ const mockData = createMockRawUTXOsData([
1501
+ { txId: `tx${callCount}`, index: 0, value: '1000' },
1502
+ ]);
1503
+
1504
+ // Simulate network delay
1505
+ await new Promise(resolve => setTimeout(resolve, 10));
1506
+ return { result: mockData, jsonrpc: '2.0', id: callCount };
1507
+ });
1508
+
1509
+ // First call without threshold
1510
+ const promises = [
1511
+ manager.getUTXOs({ address })
1512
+ ];
1513
+ vi.advanceTimersByTime(50);
1514
+ const first = await Promise.all(promises);
1515
+ expect(first).toHaveLength(1);
1516
+ expect(first[0][0]).toHaveProperty('transactionId', 'tx1');
1517
+
1518
+ // Fire multiple concurrent requests
1519
+ const promises2 = [
1520
+ manager.getUTXOs({ address, olderThan: 1n }),
1521
+ manager.getUTXOs({ address }),
1522
+ manager.getUTXOs({ address, olderThan: 2n }),
1523
+ ];
1524
+
1525
+ // Advance timers to complete all requests
1526
+ vi.advanceTimersByTime(50);
1527
+
1528
+ const results = await Promise.all(promises2);
1529
+ const [csv1, all, csv2] = results;
1530
+
1531
+ // All should return same data
1532
+ for (const result of results) {
1533
+ expect(result).toHaveLength(1);
1534
+ }
1535
+
1536
+ expect(csv1[0]).toHaveProperty('transactionId', 'tx2');
1537
+ expect(all[0]).toHaveProperty('transactionId', 'tx1'); // Fetched from cache
1538
+ expect(csv2[0]).toHaveProperty('transactionId', 'tx3');
1539
+
1540
+ // But only 1 actual fetch should have happened (cache)
1541
+ // Note: First call fetches, subsequent use cache
1542
+ expect(callCount).toBe(3); // Only one call cached
1543
+ });
1491
1544
  });
1492
1545
 
1493
1546
  // ==========================================