opnet 1.2.20 → 1.2.22

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.
@@ -25,10 +25,10 @@ export declare abstract class AbstractRpcProvider {
25
25
  readonly network: Network;
26
26
  private nextId;
27
27
  private chainId;
28
- protected constructor(network: Network);
29
- private _utxoManager;
30
28
  private gasCache;
31
29
  private lastFetchedGas;
30
+ protected constructor(network: Network);
31
+ private _utxoManager;
32
32
  get utxoManager(): UTXOsManager;
33
33
  getPublicKeyInfo(address: string): Promise<Address>;
34
34
  validateAddress(addr: string | Address, network: Network): AddressTypes | null;
@@ -37,6 +37,7 @@ export declare abstract class AbstractRpcProvider {
37
37
  getBlocks(blockNumbers: BlockTag[], prefetchTxs?: boolean): Promise<Block[]>;
38
38
  getBlockByHash(blockHash: string): Promise<Block>;
39
39
  getBalance(addressLike: string, filterOrdinals?: boolean): Promise<bigint>;
40
+ getBalances(addressesLike: string[], filterOrdinals?: boolean): Promise<Record<string, bigint>>;
40
41
  getTransaction(txHash: string): Promise<TransactionBase<OPNetTransactionTypes>>;
41
42
  getTransactionReceipt(txHash: string): Promise<TransactionReceipt>;
42
43
  getNetwork(): Network;
@@ -45,8 +46,8 @@ export declare abstract class AbstractRpcProvider {
45
46
  getStorageAt(address: string | Address, rawPointer: bigint | string, proofs?: boolean, height?: BigNumberish): Promise<StoredValue>;
46
47
  call(to: string | Address, data: Buffer | string, from?: Address, height?: BigNumberish, simulatedTransaction?: ParsedSimulatedTransaction, accessList?: IAccessList): Promise<CallResult | ICallRequestError>;
47
48
  gasParameters(): Promise<BlockGasParameters>;
48
- private _gasParameters;
49
49
  sendRawTransaction(tx: string, psbt: boolean): Promise<BroadcastedTransaction>;
50
+ sendRawTransactions(txs: string[]): Promise<BroadcastedTransaction[]>;
50
51
  getBlockWitness(height?: BigNumberish, trusted?: boolean, limit?: number, page?: number): Promise<BlockWitnesses>;
51
52
  getReorg(fromBlock?: BigNumberish, toBlock?: BigNumberish): Promise<ReorgInformation[]>;
52
53
  abstract _send(payload: JsonRpcPayload | JsonRpcPayload[]): Promise<JsonRpcCallResult>;
@@ -55,6 +56,7 @@ export declare abstract class AbstractRpcProvider {
55
56
  buildJsonRpcPayload<T extends JSONRpcMethods>(method: T, params: unknown[]): JsonRpcPayload;
56
57
  getPublicKeysInfo(addresses: string | string[] | Address | Address[]): Promise<AddressesInfo>;
57
58
  protected abstract providerUrl(url: string): string;
59
+ private _gasParameters;
58
60
  private parseSimulatedTransaction;
59
61
  private bufferToHex;
60
62
  private bigintToBase64;
@@ -12,6 +12,7 @@ export declare abstract class TransactionBase<T extends OPNetTransactionTypes> e
12
12
  readonly index: number;
13
13
  readonly burnedBitcoin: BigNumberish;
14
14
  readonly priorityFee: BigNumberish;
15
+ readonly maxGasSat: BigNumberish;
15
16
  readonly inputs: TransactionInput[];
16
17
  readonly outputs: TransactionOutput[];
17
18
  readonly OPNetType: T;
@@ -5,11 +5,16 @@ export declare class UTXOsManager {
5
5
  private readonly provider;
6
6
  private spentUTXOs;
7
7
  private pendingUTXOs;
8
+ private pendingUtxoDepth;
8
9
  private lastCleanup;
10
+ private lastFetchTimestamp;
11
+ private lastFetchedData;
9
12
  constructor(provider: AbstractRpcProvider);
10
13
  spentUTXO(spent: UTXOs, newUTXOs: UTXOs): void;
11
14
  clean(): void;
12
15
  getUTXOs({ address, optimize, mergePendingUTXOs, filterSpentUTXOs, }: RequestUTXOsParams): Promise<UTXOs>;
13
16
  getUTXOsForAmount({ address, amount, optimize, mergePendingUTXOs, filterSpentUTXOs, throwErrors, }: RequestUTXOsParamsWithAmount): Promise<UTXOs>;
17
+ private maybeFetchUTXOs;
14
18
  private fetchUTXOs;
19
+ private syncPendingDepthWithFetched;
15
20
  }
@@ -25,10 +25,10 @@ export declare abstract class AbstractRpcProvider {
25
25
  readonly network: Network;
26
26
  private nextId;
27
27
  private chainId;
28
- protected constructor(network: Network);
29
- private _utxoManager;
30
28
  private gasCache;
31
29
  private lastFetchedGas;
30
+ protected constructor(network: Network);
31
+ private _utxoManager;
32
32
  get utxoManager(): UTXOsManager;
33
33
  getPublicKeyInfo(address: string): Promise<Address>;
34
34
  validateAddress(addr: string | Address, network: Network): AddressTypes | null;
@@ -37,6 +37,7 @@ export declare abstract class AbstractRpcProvider {
37
37
  getBlocks(blockNumbers: BlockTag[], prefetchTxs?: boolean): Promise<Block[]>;
38
38
  getBlockByHash(blockHash: string): Promise<Block>;
39
39
  getBalance(addressLike: string, filterOrdinals?: boolean): Promise<bigint>;
40
+ getBalances(addressesLike: string[], filterOrdinals?: boolean): Promise<Record<string, bigint>>;
40
41
  getTransaction(txHash: string): Promise<TransactionBase<OPNetTransactionTypes>>;
41
42
  getTransactionReceipt(txHash: string): Promise<TransactionReceipt>;
42
43
  getNetwork(): Network;
@@ -45,8 +46,8 @@ export declare abstract class AbstractRpcProvider {
45
46
  getStorageAt(address: string | Address, rawPointer: bigint | string, proofs?: boolean, height?: BigNumberish): Promise<StoredValue>;
46
47
  call(to: string | Address, data: Buffer | string, from?: Address, height?: BigNumberish, simulatedTransaction?: ParsedSimulatedTransaction, accessList?: IAccessList): Promise<CallResult | ICallRequestError>;
47
48
  gasParameters(): Promise<BlockGasParameters>;
48
- private _gasParameters;
49
49
  sendRawTransaction(tx: string, psbt: boolean): Promise<BroadcastedTransaction>;
50
+ sendRawTransactions(txs: string[]): Promise<BroadcastedTransaction[]>;
50
51
  getBlockWitness(height?: BigNumberish, trusted?: boolean, limit?: number, page?: number): Promise<BlockWitnesses>;
51
52
  getReorg(fromBlock?: BigNumberish, toBlock?: BigNumberish): Promise<ReorgInformation[]>;
52
53
  abstract _send(payload: JsonRpcPayload | JsonRpcPayload[]): Promise<JsonRpcCallResult>;
@@ -55,6 +56,7 @@ export declare abstract class AbstractRpcProvider {
55
56
  buildJsonRpcPayload<T extends JSONRpcMethods>(method: T, params: unknown[]): JsonRpcPayload;
56
57
  getPublicKeysInfo(addresses: string | string[] | Address | Address[]): Promise<AddressesInfo>;
57
58
  protected abstract providerUrl(url: string): string;
59
+ private _gasParameters;
58
60
  private parseSimulatedTransaction;
59
61
  private bufferToHex;
60
62
  private bigintToBase64;
@@ -13,12 +13,12 @@ export class AbstractRpcProvider {
13
13
  network;
14
14
  nextId = 0;
15
15
  chainId;
16
+ gasCache;
17
+ lastFetchedGas = 0;
16
18
  constructor(network) {
17
19
  this.network = network;
18
20
  }
19
21
  _utxoManager = new UTXOsManager(this);
20
- gasCache;
21
- lastFetchedGas = 0;
22
22
  get utxoManager() {
23
23
  return this._utxoManager;
24
24
  }
@@ -101,6 +101,32 @@ export class AbstractRpcProvider {
101
101
  }
102
102
  return BigInt(result);
103
103
  }
104
+ async getBalances(addressesLike, filterOrdinals = true) {
105
+ const payloads = addressesLike.map((address) => {
106
+ return this.buildJsonRpcPayload(JSONRpcMethods.GET_BALANCE, [address, filterOrdinals]);
107
+ });
108
+ const balances = await this.callMultiplePayloads(payloads);
109
+ if ('error' in balances) {
110
+ const error = balances.error;
111
+ throw new Error(`Error fetching block: ${error.message}`);
112
+ }
113
+ const resultBalance = {};
114
+ for (let i = 0; i < balances.length; i++) {
115
+ const balance = balances[i];
116
+ const address = addressesLike[i];
117
+ if (!address)
118
+ throw new Error('Impossible index.');
119
+ if ('error' in balance) {
120
+ throw new Error(`Error fetching block: ${balance.error}`);
121
+ }
122
+ const result = balance.result;
123
+ if (!result || (result && !result.startsWith('0x'))) {
124
+ throw new Error(`Invalid balance returned from provider: ${result}`);
125
+ }
126
+ resultBalance[address] = BigInt(result);
127
+ }
128
+ return resultBalance;
129
+ }
104
130
  async getTransaction(txHash) {
105
131
  const payload = this.buildJsonRpcPayload(JSONRpcMethods.GET_TRANSACTION_BY_HASH, [txHash]);
106
132
  const rawTransaction = await this.callPayloadSingle(payload);
@@ -217,15 +243,6 @@ export class AbstractRpcProvider {
217
243
  }
218
244
  return this.gasCache;
219
245
  }
220
- async _gasParameters() {
221
- const payload = this.buildJsonRpcPayload(JSONRpcMethods.GAS, []);
222
- const rawCall = await this.callPayloadSingle(payload);
223
- if ('error' in rawCall) {
224
- throw new Error(`Error fetching gas parameters: ${rawCall.error}`);
225
- }
226
- const result = rawCall.result;
227
- return new BlockGasParameters(result);
228
- }
229
246
  async sendRawTransaction(tx, psbt) {
230
247
  if (!/^[0-9A-Fa-f]+$/.test(tx)) {
231
248
  throw new Error('sendRawTransaction: Invalid hex string');
@@ -241,6 +258,25 @@ export class AbstractRpcProvider {
241
258
  }
242
259
  return result;
243
260
  }
261
+ async sendRawTransactions(txs) {
262
+ const payloads = txs.map((tx) => {
263
+ return this.buildJsonRpcPayload(JSONRpcMethods.BROADCAST_TRANSACTION, [tx, false]);
264
+ });
265
+ const rawTxs = await this.callMultiplePayloads(payloads);
266
+ if ('error' in rawTxs) {
267
+ throw new Error(`Error sending transactions: ${rawTxs.error}`);
268
+ }
269
+ return rawTxs.map((rawTx) => {
270
+ const result = rawTx.result;
271
+ if (result && result.identifier) {
272
+ return {
273
+ ...result,
274
+ identifier: BigInt(result.identifier),
275
+ };
276
+ }
277
+ return result;
278
+ });
279
+ }
244
280
  async getBlockWitness(height = -1, trusted, limit, page) {
245
281
  const params = [height.toString()];
246
282
  if (trusted !== undefined && trusted !== null)
@@ -332,6 +368,15 @@ export class AbstractRpcProvider {
332
368
  }
333
369
  return response;
334
370
  }
371
+ async _gasParameters() {
372
+ const payload = this.buildJsonRpcPayload(JSONRpcMethods.GAS, []);
373
+ const rawCall = await this.callPayloadSingle(payload);
374
+ if ('error' in rawCall) {
375
+ throw new Error(`Error fetching gas parameters: ${rawCall.error}`);
376
+ }
377
+ const result = rawCall.result;
378
+ return new BlockGasParameters(result);
379
+ }
335
380
  parseSimulatedTransaction(transaction) {
336
381
  return {
337
382
  inputs: transaction.inputs.map((input) => {
@@ -12,6 +12,7 @@ export declare abstract class TransactionBase<T extends OPNetTransactionTypes> e
12
12
  readonly index: number;
13
13
  readonly burnedBitcoin: BigNumberish;
14
14
  readonly priorityFee: BigNumberish;
15
+ readonly maxGasSat: BigNumberish;
15
16
  readonly inputs: TransactionInput[];
16
17
  readonly outputs: TransactionOutput[];
17
18
  readonly OPNetType: T;
@@ -7,6 +7,7 @@ export class TransactionBase extends TransactionReceipt {
7
7
  index;
8
8
  burnedBitcoin;
9
9
  priorityFee;
10
+ maxGasSat;
10
11
  inputs;
11
12
  outputs;
12
13
  OPNetType;
@@ -31,6 +32,7 @@ export class TransactionBase extends TransactionReceipt {
31
32
  if (transaction.pow) {
32
33
  this.pow = this.decodeProofOfWorkChallenge(transaction.pow);
33
34
  }
35
+ this.maxGasSat = this.burnedBitcoin + (this.pow?.reward || 0n) - this.priorityFee;
34
36
  }
35
37
  decodeProofOfWorkChallenge(challenge) {
36
38
  return {
@@ -5,11 +5,16 @@ export declare class UTXOsManager {
5
5
  private readonly provider;
6
6
  private spentUTXOs;
7
7
  private pendingUTXOs;
8
+ private pendingUtxoDepth;
8
9
  private lastCleanup;
10
+ private lastFetchTimestamp;
11
+ private lastFetchedData;
9
12
  constructor(provider: AbstractRpcProvider);
10
13
  spentUTXO(spent: UTXOs, newUTXOs: UTXOs): void;
11
14
  clean(): void;
12
15
  getUTXOs({ address, optimize, mergePendingUTXOs, filterSpentUTXOs, }: RequestUTXOsParams): Promise<UTXOs>;
13
16
  getUTXOsForAmount({ address, amount, optimize, mergePendingUTXOs, filterSpentUTXOs, throwErrors, }: RequestUTXOsParamsWithAmount): Promise<UTXOs>;
17
+ private maybeFetchUTXOs;
14
18
  private fetchUTXOs;
19
+ private syncPendingDepthWithFetched;
15
20
  }
@@ -1,27 +1,57 @@
1
1
  import { UTXO } from '../bitcoin/UTXOs.js';
2
2
  import { JSONRpcMethods } from '../providers/interfaces/JSONRpcMethods.js';
3
3
  const AUTO_PURGE_AFTER = 1000 * 60;
4
+ const FETCH_COOLDOWN = 10000;
5
+ const MEMPOOL_CHAIN_LIMIT = 25;
4
6
  export class UTXOsManager {
5
7
  provider;
6
8
  spentUTXOs = [];
7
9
  pendingUTXOs = [];
10
+ pendingUtxoDepth = {};
8
11
  lastCleanup = Date.now();
12
+ lastFetchTimestamp = 0;
13
+ lastFetchedData = null;
9
14
  constructor(provider) {
10
15
  this.provider = provider;
11
16
  }
12
17
  spentUTXO(spent, newUTXOs) {
13
- this.pendingUTXOs = this.pendingUTXOs.filter((utxo) => !spent.some((spentUtxo) => spentUtxo.transactionId === utxo.transactionId &&
14
- spentUtxo.outputIndex === utxo.outputIndex));
18
+ const utxoKey = (u) => `${u.transactionId}:${u.outputIndex}`;
19
+ this.pendingUTXOs = this.pendingUTXOs.filter((utxo) => {
20
+ return !spent.some((spentUtxo) => spentUtxo.transactionId === utxo.transactionId &&
21
+ spentUtxo.outputIndex === utxo.outputIndex);
22
+ });
23
+ for (const spentUtxo of spent) {
24
+ const key = utxoKey(spentUtxo);
25
+ delete this.pendingUtxoDepth[key];
26
+ }
15
27
  this.spentUTXOs.push(...spent);
16
- this.pendingUTXOs.push(...newUTXOs);
28
+ let maxParentDepth = 0;
29
+ for (const spentUtxo of spent) {
30
+ const key = utxoKey(spentUtxo);
31
+ const parentDepth = this.pendingUtxoDepth[key] ?? 0;
32
+ if (parentDepth > maxParentDepth) {
33
+ maxParentDepth = parentDepth;
34
+ }
35
+ }
36
+ const newDepth = maxParentDepth + 1;
37
+ if (newDepth > MEMPOOL_CHAIN_LIMIT) {
38
+ throw new Error(`too-long-mempool-chain, too many descendants for tx ... [limit: ${MEMPOOL_CHAIN_LIMIT}]`);
39
+ }
40
+ for (const nu of newUTXOs) {
41
+ this.pendingUTXOs.push(nu);
42
+ this.pendingUtxoDepth[utxoKey(nu)] = newDepth;
43
+ }
17
44
  }
18
45
  clean() {
19
46
  this.spentUTXOs = [];
20
47
  this.pendingUTXOs = [];
48
+ this.pendingUtxoDepth = {};
21
49
  this.lastCleanup = Date.now();
50
+ this.lastFetchTimestamp = 0;
51
+ this.lastFetchedData = null;
22
52
  }
23
53
  async getUTXOs({ address, optimize = true, mergePendingUTXOs = true, filterSpentUTXOs = true, }) {
24
- const fetchedData = await this.fetchUTXOs(address, optimize);
54
+ const fetchedData = await this.maybeFetchUTXOs(address, optimize);
25
55
  const utxoKey = (utxo) => `${utxo.transactionId}:${utxo.outputIndex}`;
26
56
  const pendingUTXOKeys = new Set(this.pendingUTXOs.map(utxoKey));
27
57
  const spentUTXOKeys = new Set(this.spentUTXOs.map(utxoKey));
@@ -67,8 +97,8 @@ export class UTXOsManager {
67
97
  const utxoUntilAmount = [];
68
98
  let currentValue = 0n;
69
99
  for (const utxo of combinedUTXOs) {
70
- currentValue += utxo.value;
71
100
  utxoUntilAmount.push(utxo);
101
+ currentValue += utxo.value;
72
102
  if (currentValue >= amount) {
73
103
  break;
74
104
  }
@@ -78,31 +108,51 @@ export class UTXOsManager {
78
108
  }
79
109
  return utxoUntilAmount;
80
110
  }
81
- async fetchUTXOs(address, optimize = false) {
82
- if (Date.now() - this.lastCleanup > AUTO_PURGE_AFTER) {
111
+ async maybeFetchUTXOs(address, optimize) {
112
+ const now = Date.now();
113
+ const age = now - this.lastFetchTimestamp;
114
+ if (now - this.lastCleanup > AUTO_PURGE_AFTER) {
83
115
  this.clean();
84
116
  }
117
+ if (this.lastFetchedData && age < FETCH_COOLDOWN) {
118
+ return this.lastFetchedData;
119
+ }
120
+ this.lastFetchedData = await this.fetchUTXOs(address, optimize);
121
+ this.lastFetchTimestamp = now;
122
+ this.syncPendingDepthWithFetched();
123
+ return this.lastFetchedData;
124
+ }
125
+ async fetchUTXOs(address, optimize = false) {
85
126
  const addressStr = address.toString();
86
127
  const payload = this.provider.buildJsonRpcPayload(JSONRpcMethods.GET_UTXOS, [addressStr, optimize]);
87
- const rawUXTOs = await this.provider.callPayloadSingle(payload);
88
- if ('error' in rawUXTOs) {
89
- throw new Error(`Error fetching block: ${rawUXTOs.error}`);
128
+ const rawUTXOs = await this.provider.callPayloadSingle(payload);
129
+ if ('error' in rawUTXOs) {
130
+ throw new Error(`Error fetching block: ${rawUTXOs.error}`);
90
131
  }
91
- const result = rawUXTOs.result || {
132
+ const result = rawUTXOs.result || {
92
133
  confirmed: [],
93
134
  pending: [],
94
135
  spentTransactions: [],
95
136
  };
96
137
  return {
97
- confirmed: result.confirmed.map((utxo) => {
98
- return new UTXO(utxo);
99
- }),
100
- pending: result.pending.map((utxo) => {
101
- return new UTXO(utxo);
102
- }),
103
- spentTransactions: result.spentTransactions.map((utxo) => {
104
- return new UTXO(utxo);
105
- }),
138
+ confirmed: result.confirmed.map((utxo) => new UTXO(utxo)),
139
+ pending: result.pending.map((utxo) => new UTXO(utxo)),
140
+ spentTransactions: result.spentTransactions.map((utxo) => new UTXO(utxo)),
106
141
  };
107
142
  }
143
+ syncPendingDepthWithFetched() {
144
+ if (!this.lastFetchedData) {
145
+ return;
146
+ }
147
+ const confirmedKeys = new Set(this.lastFetchedData.confirmed.map((u) => `${u.transactionId}:${u.outputIndex}`));
148
+ const spentKeys = new Set(this.lastFetchedData.spentTransactions.map((u) => `${u.transactionId}:${u.outputIndex}`));
149
+ this.pendingUTXOs = this.pendingUTXOs.filter((u) => {
150
+ const key = `${u.transactionId}:${u.outputIndex}`;
151
+ if (confirmedKeys.has(key) || spentKeys.has(key)) {
152
+ delete this.pendingUtxoDepth[key];
153
+ return false;
154
+ }
155
+ return true;
156
+ });
157
+ }
108
158
  }
package/eslint.config.js CHANGED
@@ -30,6 +30,7 @@ export default tseslint.config(
30
30
  'prefer-spread': 'off',
31
31
  '@typescript-eslint/no-empty-object-type': 'off',
32
32
  '@typescript-eslint/no-base-to-string': 'off',
33
+ '@typescript-eslint/no-dynamic-delete': 'off',
33
34
  },
34
35
  },
35
36
  {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "opnet",
3
3
  "type": "module",
4
- "version": "1.2.20",
4
+ "version": "1.2.22",
5
5
  "author": "OP_NET",
6
6
  "description": "The perfect library for building Bitcoin-based applications.",
7
7
  "engines": {
@@ -100,9 +100,9 @@
100
100
  },
101
101
  "dependencies": {
102
102
  "@bitcoinerlab/secp256k1": "^1.2.0",
103
- "@btc-vision/bitcoin": "^6.3.4",
103
+ "@btc-vision/bitcoin": "^6.3.5",
104
104
  "@btc-vision/bitcoin-rpc": "^1.0.0",
105
- "@btc-vision/transaction": "^1.2.8",
105
+ "@btc-vision/transaction": "^1.2.9",
106
106
  "@noble/hashes": "^1.7.0",
107
107
  "bignumber.js": "^9.1.2",
108
108
  "buffer": "^6.0.3",
@@ -12,10 +12,7 @@ import { ContractData } from '../contracts/ContractData.js';
12
12
  import { IAccessList } from '../contracts/interfaces/IAccessList.js';
13
13
  import { ICallRequestError, ICallResult } from '../contracts/interfaces/ICallResult.js';
14
14
  import { IRawContract } from '../contracts/interfaces/IRawContract.js';
15
- import {
16
- ParsedSimulatedTransaction,
17
- SimulatedTransaction,
18
- } from '../contracts/interfaces/SimulatedTransaction.js';
15
+ import { ParsedSimulatedTransaction, SimulatedTransaction } from '../contracts/interfaces/SimulatedTransaction.js';
19
16
  import { OPNetTransactionTypes } from '../interfaces/opnet/OPNetTransactionTypes.js';
20
17
  import { IStorageValue } from '../storage/interfaces/IStorageValue.js';
21
18
  import { StoredValue } from '../storage/StoredValue.js';
@@ -45,14 +42,13 @@ import { ReorgInformation } from './interfaces/ReorgInformation.js';
45
42
  export abstract class AbstractRpcProvider {
46
43
  private nextId: number = 0;
47
44
  private chainId: bigint | undefined;
45
+ private gasCache: BlockGasParameters | undefined;
46
+ private lastFetchedGas: number = 0;
48
47
 
49
48
  protected constructor(public readonly network: Network) {}
50
49
 
51
50
  private _utxoManager: UTXOsManager = new UTXOsManager(this);
52
51
 
53
- private gasCache: BlockGasParameters | undefined;
54
- private lastFetchedGas: number = 0;
55
-
56
52
  /**
57
53
  * Get the UTXO manager.
58
54
  */
@@ -220,6 +216,51 @@ export abstract class AbstractRpcProvider {
220
216
  return BigInt(result);
221
217
  }
222
218
 
219
+ /**
220
+ * Get the bitcoin balances of multiple addresses.
221
+ * @param {string[]} addressesLike The addresses to get the balances of
222
+ * @param {boolean} filterOrdinals Whether to filter ordinals or not
223
+ * @description This method is used to get the balance of a bitcoin address.
224
+ * @returns {Record<string, bigint>} The balance of the address
225
+ * @example await getBalances(['bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq']);
226
+ */
227
+ public async getBalances(
228
+ addressesLike: string[],
229
+ filterOrdinals: boolean = true,
230
+ ): Promise<Record<string, bigint>> {
231
+ const payloads: JsonRpcPayload[] = addressesLike.map((address: string) => {
232
+ return this.buildJsonRpcPayload(JSONRpcMethods.GET_BALANCE, [address, filterOrdinals]);
233
+ });
234
+
235
+ const balances: JsonRpcCallResult = await this.callMultiplePayloads(payloads);
236
+ if ('error' in balances) {
237
+ const error = balances.error as JSONRpcResultError<JSONRpcMethods.GET_BALANCE>;
238
+
239
+ throw new Error(`Error fetching block: ${error.message}`);
240
+ }
241
+
242
+ const resultBalance: Record<string, bigint> = {};
243
+ for (let i = 0; i < balances.length; i++) {
244
+ const balance = balances[i];
245
+ const address = addressesLike[i];
246
+
247
+ if (!address) throw new Error('Impossible index.');
248
+
249
+ if ('error' in balance) {
250
+ throw new Error(`Error fetching block: ${balance.error}`);
251
+ }
252
+
253
+ const result = balance.result as string;
254
+ if (!result || (result && !result.startsWith('0x'))) {
255
+ throw new Error(`Invalid balance returned from provider: ${result}`);
256
+ }
257
+
258
+ resultBalance[address] = BigInt(result);
259
+ }
260
+
261
+ return resultBalance;
262
+ }
263
+
223
264
  /**
224
265
  * Get a transaction by its hash or hash id.
225
266
  * @description This method is used to get a transaction by its hash or hash id.
@@ -464,18 +505,6 @@ export abstract class AbstractRpcProvider {
464
505
  return this.gasCache;
465
506
  }
466
507
 
467
- private async _gasParameters(): Promise<BlockGasParameters> {
468
- const payload: JsonRpcPayload = this.buildJsonRpcPayload(JSONRpcMethods.GAS, []);
469
- const rawCall: JsonRpcResult = await this.callPayloadSingle(payload);
470
-
471
- if ('error' in rawCall) {
472
- throw new Error(`Error fetching gas parameters: ${rawCall.error}`);
473
- }
474
-
475
- const result: IBlockGasParametersInput = rawCall.result as IBlockGasParametersInput;
476
- return new BlockGasParameters(result);
477
- }
478
-
479
508
  /**
480
509
  * Send a raw transaction.
481
510
  * @description This method is used to send a raw transaction.
@@ -508,6 +537,36 @@ export abstract class AbstractRpcProvider {
508
537
  return result;
509
538
  }
510
539
 
540
+ /**
541
+ * Bulk send transactions.
542
+ * @description This method is used to send multiple transactions at the same time.
543
+ * @param {string[]} txs The raw transactions to send as hex string
544
+ * @returns {Promise<BroadcastedTransaction[]>} The result of the transaction
545
+ * @throws {Error} If something went wrong while sending the transaction
546
+ */
547
+ public async sendRawTransactions(txs: string[]): Promise<BroadcastedTransaction[]> {
548
+ const payloads: JsonRpcPayload[] = txs.map((tx) => {
549
+ return this.buildJsonRpcPayload(JSONRpcMethods.BROADCAST_TRANSACTION, [tx, false]);
550
+ });
551
+
552
+ const rawTxs: JsonRpcCallResult = await this.callMultiplePayloads(payloads);
553
+ if ('error' in rawTxs) {
554
+ throw new Error(`Error sending transactions: ${rawTxs.error}`);
555
+ }
556
+
557
+ return rawTxs.map((rawTx) => {
558
+ const result: BroadcastedTransaction = rawTx.result as BroadcastedTransaction;
559
+ if (result && result.identifier) {
560
+ return {
561
+ ...result,
562
+ identifier: BigInt(result.identifier),
563
+ };
564
+ }
565
+
566
+ return result;
567
+ });
568
+ }
569
+
511
570
  /**
512
571
  * Get block witnesses.
513
572
  * @description This method is used to get the witnesses of a block. This proves that the action 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.
@@ -587,6 +646,27 @@ export abstract class AbstractRpcProvider {
587
646
  */
588
647
  public abstract _send(payload: JsonRpcPayload | JsonRpcPayload[]): Promise<JsonRpcCallResult>;
589
648
 
649
+ /**
650
+ * Send a single payload. This method is used to send a single payload.
651
+ * @param {JsonRpcPayload} payload The payload to send
652
+ * @returns {Promise<JsonRpcResult>} The result of the payload
653
+ * @throws {Error} If no data is returned
654
+ * @private
655
+ */
656
+ public async callPayloadSingle(payload: JsonRpcPayload): Promise<JsonRpcResult> {
657
+ const rawData: JsonRpcCallResult = await this._send(payload);
658
+ if (!rawData.length) {
659
+ throw new Error('No data returned');
660
+ }
661
+
662
+ const data = rawData.shift();
663
+ if (!data) {
664
+ throw new Error('Block not found');
665
+ }
666
+
667
+ return data as JSONRpc2ResponseResult<JSONRpcMethods>;
668
+ }
669
+
590
670
  /*
591
671
  * Generate parameters needed to wrap bitcoin.
592
672
  * @description This method is used to generate the parameters needed to wrap bitcoin.
@@ -611,27 +691,6 @@ export abstract class AbstractRpcProvider {
611
691
  return new WrappedGeneration(result);
612
692
  }*/
613
693
 
614
- /**
615
- * Send a single payload. This method is used to send a single payload.
616
- * @param {JsonRpcPayload} payload The payload to send
617
- * @returns {Promise<JsonRpcResult>} The result of the payload
618
- * @throws {Error} If no data is returned
619
- * @private
620
- */
621
- public async callPayloadSingle(payload: JsonRpcPayload): Promise<JsonRpcResult> {
622
- const rawData: JsonRpcCallResult = await this._send(payload);
623
- if (!rawData.length) {
624
- throw new Error('No data returned');
625
- }
626
-
627
- const data = rawData.shift();
628
- if (!data) {
629
- throw new Error('Block not found');
630
- }
631
-
632
- return data as JSONRpc2ResponseResult<JSONRpcMethods>;
633
- }
634
-
635
694
  /**
636
695
  * Send multiple payloads. This method is used to send multiple payloads.
637
696
  * @param {JsonRpcPayload[]} payloads The payloads to send
@@ -719,6 +778,18 @@ export abstract class AbstractRpcProvider {
719
778
 
720
779
  protected abstract providerUrl(url: string): string;
721
780
 
781
+ private async _gasParameters(): Promise<BlockGasParameters> {
782
+ const payload: JsonRpcPayload = this.buildJsonRpcPayload(JSONRpcMethods.GAS, []);
783
+ const rawCall: JsonRpcResult = await this.callPayloadSingle(payload);
784
+
785
+ if ('error' in rawCall) {
786
+ throw new Error(`Error fetching gas parameters: ${rawCall.error}`);
787
+ }
788
+
789
+ const result: IBlockGasParametersInput = rawCall.result as IBlockGasParametersInput;
790
+ return new BlockGasParameters(result);
791
+ }
792
+
722
793
  private parseSimulatedTransaction(
723
794
  transaction: ParsedSimulatedTransaction,
724
795
  ): SimulatedTransaction {
@@ -44,6 +44,11 @@ export abstract class TransactionBase<T extends OPNetTransactionTypes>
44
44
  */
45
45
  public readonly priorityFee: BigNumberish;
46
46
 
47
+ /**
48
+ * @description The maximum amount of gas that can be spent by the transaction.
49
+ */
50
+ public readonly maxGasSat: BigNumberish;
51
+
47
52
  /**
48
53
  * @description The inputs of the transaction.
49
54
  */
@@ -98,6 +103,8 @@ export abstract class TransactionBase<T extends OPNetTransactionTypes>
98
103
  if (transaction.pow) {
99
104
  this.pow = this.decodeProofOfWorkChallenge(transaction.pow as RawProofOfWorkChallenge);
100
105
  }
106
+
107
+ this.maxGasSat = this.burnedBitcoin + (this.pow?.reward || 0n) - this.priorityFee;
101
108
  }
102
109
 
103
110
  private decodeProofOfWorkChallenge(challenge: RawProofOfWorkChallenge): ProofOfWorkChallenge {