gn-provider 1.2.6 → 1.2.8
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.d.ts +5 -1
- package/dist/gn-provider.js +68 -99
- 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.d.ts
CHANGED
|
@@ -5,6 +5,9 @@ declare enum ProviderEvent {
|
|
|
5
5
|
Connected = "connected",
|
|
6
6
|
NetworkChange = "networkChange"
|
|
7
7
|
}
|
|
8
|
+
export interface GNProviderOptions {
|
|
9
|
+
bridgeUrl?: string;
|
|
10
|
+
}
|
|
8
11
|
export declare class GNProvider extends Provider {
|
|
9
12
|
emit: (event: ProviderEvent, ...args: any[]) => boolean;
|
|
10
13
|
private _network;
|
|
@@ -13,10 +16,11 @@ export declare class GNProvider extends Provider {
|
|
|
13
16
|
private _gorillaPoolApiKey;
|
|
14
17
|
private apiPrefix;
|
|
15
18
|
private _mapiURL;
|
|
19
|
+
private _bridgeUrl?;
|
|
16
20
|
private _getHeaders;
|
|
17
21
|
connect: () => Promise<this>;
|
|
18
22
|
getFeePerKb: () => Promise<number>;
|
|
19
|
-
constructor(network: scryptlib.bsv.Networks.Network, wocApiKey?: string, gpApiKey?: string);
|
|
23
|
+
constructor(network: scryptlib.bsv.Networks.Network, wocApiKey?: string, gpApiKey?: string, options?: GNProviderOptions);
|
|
20
24
|
isConnected: () => boolean;
|
|
21
25
|
updateNetwork: (network: scryptlib.bsv.Networks.Network) => void;
|
|
22
26
|
getNetwork: () => scryptlib.bsv.Networks.Network;
|
package/dist/gn-provider.js
CHANGED
|
@@ -53,11 +53,8 @@ var ProviderEvent;
|
|
|
53
53
|
ProviderEvent["Connected"] = "connected";
|
|
54
54
|
ProviderEvent["NetworkChange"] = "networkChange";
|
|
55
55
|
})(ProviderEvent || (ProviderEvent = {}));
|
|
56
|
-
/*export type UTXOWithHeight = UTXO & {
|
|
57
|
-
height: number;
|
|
58
|
-
};*/
|
|
59
56
|
class GNProvider extends abstract_provider_1.Provider {
|
|
60
|
-
constructor(network, wocApiKey = '', gpApiKey = '') {
|
|
57
|
+
constructor(network, wocApiKey = '', gpApiKey = '', options) {
|
|
61
58
|
super();
|
|
62
59
|
this._isConnected = false;
|
|
63
60
|
this._getHeaders = () => {
|
|
@@ -87,32 +84,28 @@ class GNProvider extends abstract_provider_1.Provider {
|
|
|
87
84
|
yield this._ready();
|
|
88
85
|
const headers = this._getHeaders();
|
|
89
86
|
try {
|
|
90
|
-
|
|
91
|
-
const chainInfoRes = yield superagent.get(`${this.apiPrefix()}/chain/info`)
|
|
92
|
-
.set(headers);
|
|
87
|
+
const chainInfoRes = yield superagent.get(`${this.apiPrefix()}/chain/info`).set(headers);
|
|
93
88
|
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)
|
|
89
|
+
const blockStatsRes = yield superagent.get(`${this.apiPrefix()}/block/height/${currentHeight}/stats`).set(headers);
|
|
90
|
+
const stats = blockStatsRes.body;
|
|
91
|
+
// 1. Extraer mediana de fee y tamaño medio de transacción
|
|
92
|
+
const medianFeePerTx = stats.median_fee; // Satoshis por transacción
|
|
93
|
+
const medianSizePerTx = stats.median_tx_size; // Bytes por transacción
|
|
94
|
+
if (!medianSizePerTx || medianSizePerTx === 0)
|
|
95
|
+
throw new Error('Invalid block stats');
|
|
96
|
+
// 2. Calcular la tasa: (Satoshis / Bytes) * 1000 para obtener sat/kb
|
|
97
|
+
const feePerKb = (medianFeePerTx / medianSizePerTx) * 1000;
|
|
98
|
+
// 3. Aplicar multiplicador competitivo (1.5x es seguro en BSV)
|
|
107
99
|
const competitiveFee = feePerKb * 1.5;
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
100
|
+
// 4. SafeFee con tus nuevos límites
|
|
101
|
+
// Nota: Subimos el mínimo a 1.1 para asegurar prioridad sobre el polvo de la red
|
|
102
|
+
const safeFee = Math.max(1.1, Math.min(competitiveFee, 500));
|
|
103
|
+
console.log(`Calculated MEDIAN fee rate: ${safeFee.toFixed(2)} sat/kb`);
|
|
111
104
|
return Math.round(safeFee * 100) / 100;
|
|
112
105
|
}
|
|
113
106
|
catch (error) {
|
|
114
|
-
console.warn('Fee estimation
|
|
115
|
-
return
|
|
107
|
+
console.warn('Fee estimation failed, using fallback');
|
|
108
|
+
return 1.1; // Fallback mucho más realista para BSV que 500
|
|
116
109
|
}
|
|
117
110
|
});
|
|
118
111
|
this.isConnected = () => this._isConnected;
|
|
@@ -225,24 +218,25 @@ class GNProvider extends abstract_provider_1.Provider {
|
|
|
225
218
|
throw new Error(`GNProvider ERROR: ${error.message}`);
|
|
226
219
|
}
|
|
227
220
|
}*/
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
221
|
+
/*EN USO
|
|
222
|
+
sendRawTransaction = async (rawTxHex: string): Promise<TxHash> => {
|
|
223
|
+
await this._ready();
|
|
231
224
|
const headers = this._getHeaders();
|
|
232
225
|
const size = Math.max(1, rawTxHex.length / 2 / 1024);
|
|
233
226
|
const timeout = Math.max(10000, 1000 * size);
|
|
227
|
+
|
|
234
228
|
try {
|
|
235
|
-
const res =
|
|
229
|
+
const res = await superagent.post(`${this.apiPrefix()}/tx/raw`)
|
|
236
230
|
.timeout({
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
231
|
+
response: timeout,
|
|
232
|
+
deadline: 60000
|
|
233
|
+
})
|
|
240
234
|
.set(headers)
|
|
241
235
|
.send({ txhex: rawTxHex });
|
|
236
|
+
|
|
242
237
|
return res.body;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if ((_a = error.response) === null || _a === void 0 ? void 0 : _a.text) {
|
|
238
|
+
} catch (error: any) {
|
|
239
|
+
if (error.response?.text) {
|
|
246
240
|
if (this.needIgnoreError(error.response.text)) {
|
|
247
241
|
return new scryptlib.bsv.Transaction(rawTxHex).id;
|
|
248
242
|
}
|
|
@@ -250,74 +244,48 @@ class GNProvider extends abstract_provider_1.Provider {
|
|
|
250
244
|
}
|
|
251
245
|
throw new Error(`GNProvider ERROR: ${error.message}`);
|
|
252
246
|
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
const
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
.timeout({ response: timeout, deadline: 60000 })
|
|
262
|
-
.set(headers)
|
|
263
|
-
.send({ txhex: rawTxHex });
|
|
264
|
-
|
|
265
|
-
const gpRequest = superagent.post(this._mapiURL + 'tx')
|
|
266
|
-
.timeout({ response: timeout, deadline: 60000 })
|
|
267
|
-
.set('Content-Type', 'application/octet-stream')
|
|
268
|
-
.send(Buffer.from(rawTxHex, 'hex'));
|
|
269
|
-
|
|
270
|
-
if (this._gorillaPoolApiKey) {
|
|
271
|
-
gpRequest.set('Authorization', `Bearer ${this._gorillaPoolApiKey}`);
|
|
272
|
-
}
|
|
273
|
-
|
|
247
|
+
}*/
|
|
248
|
+
this.sendRawTransaction = (rawTxHex) => __awaiter(this, void 0, void 0, function* () {
|
|
249
|
+
var _a, _b;
|
|
250
|
+
yield this._ready();
|
|
251
|
+
const isUsingBridge = !!this._bridgeUrl;
|
|
252
|
+
const url = isUsingBridge
|
|
253
|
+
? `${this._bridgeUrl}/broadcast`
|
|
254
|
+
: `${this.apiPrefix()}/tx/raw`;
|
|
274
255
|
try {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
const message = parseError instanceof Error ? parseError.message : String(parseError);
|
|
292
|
-
throw new Error(`GorillaPool: Error parsing response - ${message}`);
|
|
293
|
-
}
|
|
294
|
-
})
|
|
295
|
-
]);
|
|
296
|
-
|
|
297
|
-
for (const response of responses) {
|
|
298
|
-
if (response.status === 'fulfilled') {
|
|
299
|
-
console.log(`✅ Transacción aceptada por: ${response.value.source}`);
|
|
300
|
-
return response.value.result;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
const firstRejection = responses.find(r => r.status === 'rejected');
|
|
305
|
-
if (firstRejection) {
|
|
306
|
-
throw new Error(firstRejection.reason.message || firstRejection.reason);
|
|
256
|
+
console.log(`🚀 [GNProvider] Broadcasting via ${isUsingBridge ? 'GoldenNotes Bridge' : 'WhatsOnChain'}...`);
|
|
257
|
+
const res = yield superagent
|
|
258
|
+
.post(url)
|
|
259
|
+
.send({
|
|
260
|
+
txhex: rawTxHex,
|
|
261
|
+
network: this._network.name === scryptlib.bsv.Networks.mainnet.name ? 'main' : 'test'
|
|
262
|
+
})
|
|
263
|
+
.timeout(35000); // 35s para dar margen al polling del bridge
|
|
264
|
+
// Tu bridge devuelve el TXID como texto (res.text)
|
|
265
|
+
//const txid = typeof res.body === 'string' ? res.body : (res.text || res.body?.txid);
|
|
266
|
+
//const txid = res.text || (res.body && res.body.txid) || res.body;
|
|
267
|
+
const txid = isUsingBridge
|
|
268
|
+
? (res.text || (res.body && res.body.txid) || res.body)
|
|
269
|
+
: res.body;
|
|
270
|
+
if (typeof txid !== 'string' || txid.length !== 64) {
|
|
271
|
+
return new scryptlib.bsv.Transaction(rawTxHex).id;
|
|
307
272
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
273
|
+
console.log(`✅ [GNProvider] Éxito: ${txid}`);
|
|
274
|
+
return txid;
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
const errorMsg = ((_b = (_a = error.response) === null || _a === void 0 ? void 0 : _a.body) === null || _b === void 0 ? void 0 : _b.error) || error.message || "";
|
|
278
|
+
// --- LOGICA DE TOLERANCIA ---
|
|
279
|
+
// Si el bridge falla porque la TX ya existe, no detenemos el contrato.
|
|
280
|
+
if (this.needIgnoreError(errorMsg)) {
|
|
281
|
+
const recoveredTxid = new scryptlib.bsv.Transaction(rawTxHex).id;
|
|
282
|
+
console.log(`ℹ️ [GNProvider] La TX ya estaba en red. ID recuperado: ${recoveredTxid}`);
|
|
283
|
+
return recoveredTxid;
|
|
317
284
|
}
|
|
318
|
-
|
|
285
|
+
console.error(`❌ [GNProvider] Error crítico: ${errorMsg}`);
|
|
286
|
+
throw new Error(`GNProvider ERROR: ${this.friendlyBIP22RejectionMsg(errorMsg)}`);
|
|
319
287
|
}
|
|
320
|
-
}
|
|
288
|
+
});
|
|
321
289
|
this.sendTransaction = (signedTx) => __awaiter(this, void 0, void 0, function* () {
|
|
322
290
|
try {
|
|
323
291
|
const txHex = signedTx.serialize({ disableIsFullySigned: true });
|
|
@@ -438,6 +406,7 @@ class GNProvider extends abstract_provider_1.Provider {
|
|
|
438
406
|
const networkStr = this._network.name === scryptlib.bsv.Networks.mainnet.name ? 'main' : 'test';
|
|
439
407
|
return `https://api.whatsonchain.com/v1/bsv/${networkStr}`;
|
|
440
408
|
};
|
|
409
|
+
this._bridgeUrl = options === null || options === void 0 ? void 0 : options.bridgeUrl;
|
|
441
410
|
Object.setPrototypeOf(this, events_1.EventEmitter.prototype);
|
|
442
411
|
this.connect().catch(console.error);
|
|
443
412
|
}
|