gn-provider 1.2.5 → 1.2.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.
package/README.md CHANGED
@@ -122,7 +122,7 @@ Creates a new provider instance.
122
122
  | `listUnspent(address: string): Promise<UTXO[]>` | Lists UTXOs for an address |
123
123
  | `getBalance(address: string): Promise<{confirmed: number, unconfirmed: number}>` | Gets address balance |
124
124
  | `getTransaction(txHash: string): Promise<Transaction>` | Retrieves transaction details |
125
- | `getFeePerKb(): Promise<number>` | Estimates current fee rate |
125
+ | `getFeePerKb(): Promise<number>` | Enhanced estimation of the fee rate based on current block stats |
126
126
 
127
127
  ## Features
128
128
 
@@ -87,32 +87,28 @@ class GNProvider extends abstract_provider_1.Provider {
87
87
  yield this._ready();
88
88
  const headers = this._getHeaders();
89
89
  try {
90
- // Paso 1: Obtener la información de la cadena para obtener la altura actual
91
- const chainInfoRes = yield superagent.get(`${this.apiPrefix()}/chain/info`)
92
- .set(headers);
90
+ const chainInfoRes = yield superagent.get(`${this.apiPrefix()}/chain/info`).set(headers);
93
91
  const currentHeight = chainInfoRes.body.blocks;
94
- // Paso 2: Obtener las estadísticas del bloque actual
95
- const blockStatsRes = yield superagent.get(`${this.apiPrefix()}/block/height/${currentHeight}/stats`)
96
- .set(headers);
97
- const blockStats = blockStatsRes.body;
98
- const totalFee = blockStats.total_fee;
99
- const size = blockStats.size;
100
- // Si el tamaño es 0, evitar división por cero
101
- if (size === 0) {
102
- throw new Error('Block size is zero');
103
- }
104
- // Calcular tarifa por kilobyte: (total_fee * 1024) / size
105
- const feePerKb = (totalFee * 1024) / size;
106
- // Aplicar un multiplicador para asegurar una tarifa competitiva (1.5x)
92
+ const blockStatsRes = yield superagent.get(`${this.apiPrefix()}/block/height/${currentHeight}/stats`).set(headers);
93
+ const stats = blockStatsRes.body;
94
+ // 1. Extraer mediana de fee y tamaño medio de transacción
95
+ const medianFeePerTx = stats.median_fee; // Satoshis por transacción
96
+ const medianSizePerTx = stats.median_tx_size; // Bytes por transacción
97
+ if (!medianSizePerTx || medianSizePerTx === 0)
98
+ throw new Error('Invalid block stats');
99
+ // 2. Calcular la tasa: (Satoshis / Bytes) * 1000 para obtener sat/kb
100
+ const feePerKb = (medianFeePerTx / medianSizePerTx) * 1000;
101
+ // 3. Aplicar multiplicador competitivo (1.5x es seguro en BSV)
107
102
  const competitiveFee = feePerKb * 1.5;
108
- // Establecer un mínimo de 50 sat/kb y un máximo de 5000 sat/kb para evitar valores extremos
109
- const safeFee = Math.max(50, Math.min(competitiveFee, 5000));
110
- console.log(`Calculated fee rate: ${safeFee.toFixed(2)} sat/kb from block ${currentHeight}`);
103
+ // 4. SafeFee con tus nuevos límites
104
+ // Nota: Subimos el mínimo a 1.1 para asegurar prioridad sobre el polvo de la red
105
+ const safeFee = Math.max(1.1, Math.min(competitiveFee, 500));
106
+ console.log(`Calculated MEDIAN fee rate: ${safeFee.toFixed(2)} sat/kb`);
111
107
  return Math.round(safeFee * 100) / 100;
112
108
  }
113
109
  catch (error) {
114
- console.warn('Fee estimation from block stats failed, using fallback');
115
- return 500; // Fallback a 500 sat/kb
110
+ console.warn('Fee estimation failed, using fallback');
111
+ return 1.1; // Fallback mucho más realista para BSV que 500
116
112
  }
117
113
  });
118
114
  this.isConnected = () => this._isConnected;
@@ -230,55 +226,86 @@ class GNProvider extends abstract_provider_1.Provider {
230
226
  yield this._ready();
231
227
  const headers = this._getHeaders();
232
228
  const size = Math.max(1, rawTxHex.length / 2 / 1024);
229
+ const timeout = Math.max(10000, 1000 * size);
230
+ try {
231
+ const res = yield superagent.post(`${this.apiPrefix()}/tx/raw`)
232
+ .timeout({
233
+ response: timeout,
234
+ deadline: 60000
235
+ })
236
+ .set(headers)
237
+ .send({ txhex: rawTxHex });
238
+ return res.body;
239
+ }
240
+ catch (error) {
241
+ if ((_a = error.response) === null || _a === void 0 ? void 0 : _a.text) {
242
+ if (this.needIgnoreError(error.response.text)) {
243
+ return new scryptlib.bsv.Transaction(rawTxHex).id;
244
+ }
245
+ throw new Error(`GNProvider ERROR: ${this.friendlyBIP22RejectionMsg(error.response.text)}`);
246
+ }
247
+ throw new Error(`GNProvider ERROR: ${error.message}`);
248
+ }
249
+ });
250
+ /*sendRawTransaction = async (rawTxHex: string): Promise<TxHash> => {
251
+ await this._ready();
252
+ const headers = this._getHeaders();
253
+ const size = Math.max(1, rawTxHex.length / 2 / 1024);
233
254
  const timeout = Math.max(15000, 1000 * size);
255
+
234
256
  const wocRequest = superagent.post(`${this.apiPrefix()}/tx/raw`)
235
257
  .timeout({ response: timeout, deadline: 60000 })
236
258
  .set(headers)
237
259
  .send({ txhex: rawTxHex });
260
+
238
261
  const gpRequest = superagent.post(this._mapiURL + 'tx')
239
262
  .timeout({ response: timeout, deadline: 60000 })
240
263
  .set('Content-Type', 'application/octet-stream')
241
264
  .send(Buffer.from(rawTxHex, 'hex'));
265
+
242
266
  if (this._gorillaPoolApiKey) {
243
267
  gpRequest.set('Authorization', `Bearer ${this._gorillaPoolApiKey}`);
244
268
  }
269
+
245
270
  try {
246
- const responses = yield Promise.allSettled([
271
+ const responses = await Promise.allSettled([
247
272
  wocRequest.then(res => ({ source: 'WhatsOnChain', result: res.body })),
248
273
  gpRequest.then(res => {
249
- var _a;
250
274
  try {
251
- if (!((_a = res.body) === null || _a === void 0 ? void 0 : _a.payload)) {
275
+ if (!res.body?.payload) {
252
276
  throw new Error('Respuesta inválida de GorillaPool: falta payload');
253
277
  }
278
+
254
279
  const payload = JSON.parse(res.body.payload);
280
+
255
281
  if (payload.returnResult === 'success') {
256
282
  return { source: 'GorillaPool', result: payload.txid };
257
- }
258
- else {
283
+ } else {
259
284
  throw new Error(`GorillaPool [${payload.returnResult || 'unknown'}]: ${payload.resultDescription || 'Sin descripción'}`);
260
285
  }
261
- }
262
- catch (parseError) {
286
+ } catch (parseError) {
263
287
  const message = parseError instanceof Error ? parseError.message : String(parseError);
264
288
  throw new Error(`GorillaPool: Error parsing response - ${message}`);
265
289
  }
266
290
  })
267
291
  ]);
292
+
268
293
  for (const response of responses) {
269
294
  if (response.status === 'fulfilled') {
270
295
  console.log(`✅ Transacción aceptada por: ${response.value.source}`);
271
296
  return response.value.result;
272
297
  }
273
298
  }
299
+
274
300
  const firstRejection = responses.find(r => r.status === 'rejected');
275
301
  if (firstRejection) {
276
302
  throw new Error(firstRejection.reason.message || firstRejection.reason);
277
303
  }
304
+
278
305
  throw new Error('Todos los intentos de envío fallaron');
279
- }
280
- catch (error) {
281
- if ((_a = error.response) === null || _a === void 0 ? void 0 : _a.text) {
306
+
307
+ } catch (error: any) {
308
+ if (error.response?.text) {
282
309
  if (this.needIgnoreError(error.response.text)) {
283
310
  return new scryptlib.bsv.Transaction(rawTxHex).id;
284
311
  }
@@ -286,7 +313,7 @@ class GNProvider extends abstract_provider_1.Provider {
286
313
  }
287
314
  throw new Error(`GNProvider ERROR: ${error.message}`);
288
315
  }
289
- });
316
+ }*/
290
317
  this.sendTransaction = (signedTx) => __awaiter(this, void 0, void 0, function* () {
291
318
  try {
292
319
  const txHex = signedTx.serialize({ disableIsFullySigned: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gn-provider",
3
- "version": "1.2.5",
3
+ "version": "1.2.7",
4
4
  "files": [
5
5
  "dist",
6
6
  "scripts",