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 +1 -1
- package/dist/gn-provider.js +59 -32
- package/package.json +1 -1
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>` |
|
|
125
|
+
| `getFeePerKb(): Promise<number>` | Enhanced estimation of the fee rate based on current block stats |
|
|
126
126
|
|
|
127
127
|
## Features
|
|
128
128
|
|
package/dist/gn-provider.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
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
|
|
115
|
-
return
|
|
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 =
|
|
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 (!
|
|
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 (
|
|
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 });
|