@pioneer-platform/pioneer-sdk 4.20.0 → 4.20.1
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/dist/index.cjs +593 -445
- package/dist/index.es.js +594 -445
- package/dist/index.js +594 -445
- package/package.json +2 -2
- package/src/TransactionManager.ts +21 -4
- package/src/fees/index.ts +60 -57
- package/src/fees/index.ts.backup +510 -0
- package/src/getPubkey.ts +57 -26
- package/src/index.ts +391 -61
- package/src/txbuilder/createUnsignedEvmTx.ts +118 -63
- package/src/txbuilder/createUnsignedRippleTx.ts +12 -11
- package/src/txbuilder/createUnsignedStakingTx.ts +13 -11
- package/src/txbuilder/createUnsignedTendermintTx.ts +41 -17
- package/src/txbuilder/createUnsignedUxtoTx.ts +122 -57
- package/src/txbuilder/createUnsignedUxtoTx.ts.backup +384 -0
- package/src/txbuilder/templates/mayachain.ts +2 -0
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fee Management Module for Pioneer SDK
|
|
3
|
+
*
|
|
4
|
+
* Handles all fee-related complexity, normalization, and provides
|
|
5
|
+
* a clean, consistent interface for the frontend.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const TAG = ' | Pioneer-sdk | fees | ';
|
|
9
|
+
|
|
10
|
+
export interface FeeLevel {
|
|
11
|
+
label: string;
|
|
12
|
+
value: string;
|
|
13
|
+
unit: string;
|
|
14
|
+
description: string;
|
|
15
|
+
estimatedTime?: string;
|
|
16
|
+
priority: 'low' | 'medium' | 'high';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface NormalizedFeeRates {
|
|
20
|
+
slow: FeeLevel;
|
|
21
|
+
average: FeeLevel;
|
|
22
|
+
fastest: FeeLevel;
|
|
23
|
+
networkId: string;
|
|
24
|
+
networkType: 'UTXO' | 'EVM' | 'COSMOS' | 'RIPPLE' | 'OTHER';
|
|
25
|
+
raw: any; // Original API response for debugging
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface FeeEstimate {
|
|
29
|
+
amount: string;
|
|
30
|
+
unit: string;
|
|
31
|
+
usdValue?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Network type detection
|
|
35
|
+
function getNetworkType(networkId: string): 'UTXO' | 'EVM' | 'COSMOS' | 'RIPPLE' | 'OTHER' {
|
|
36
|
+
if (networkId.startsWith('bip122:')) return 'UTXO';
|
|
37
|
+
if (networkId.startsWith('eip155:')) return 'EVM';
|
|
38
|
+
if (networkId.startsWith('cosmos:')) return 'COSMOS';
|
|
39
|
+
if (networkId.startsWith('ripple:')) return 'RIPPLE';
|
|
40
|
+
return 'OTHER';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Get human-readable network name
|
|
44
|
+
function getNetworkName(networkId: string): string {
|
|
45
|
+
const networkNames: Record<string, string> = {
|
|
46
|
+
'bip122:000000000019d6689c085ae165831e93': 'Bitcoin',
|
|
47
|
+
'bip122:12a765e31ffd4059bada1e25190f6e98': 'Litecoin',
|
|
48
|
+
'bip122:00000000001a91e3dace36e2be3bf030': 'Dogecoin',
|
|
49
|
+
'bip122:000000000000000000651ef99cb9fcbe': 'Bitcoin Cash',
|
|
50
|
+
'bip122:000007d91d1254d60e2dd1ae58038307': 'Dash',
|
|
51
|
+
'eip155:1': 'Ethereum',
|
|
52
|
+
'eip155:56': 'BNB Smart Chain',
|
|
53
|
+
'eip155:137': 'Polygon',
|
|
54
|
+
'eip155:43114': 'Avalanche',
|
|
55
|
+
'eip155:8453': 'Base',
|
|
56
|
+
'eip155:10': 'Optimism',
|
|
57
|
+
'cosmos:cosmoshub-4': 'Cosmos Hub',
|
|
58
|
+
'cosmos:osmosis-1': 'Osmosis',
|
|
59
|
+
'cosmos:thorchain-mainnet-v1': 'THORChain',
|
|
60
|
+
'cosmos:mayachain-mainnet-v1': 'Maya',
|
|
61
|
+
'ripple:4109c6f2045fc7eff4cde8f9905d19c2': 'Ripple',
|
|
62
|
+
};
|
|
63
|
+
return networkNames[networkId] || networkId;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Main fee fetching and normalization function
|
|
68
|
+
* Handles all the complexity of different API formats and returns
|
|
69
|
+
* a clean, normalized structure for the UI
|
|
70
|
+
*/
|
|
71
|
+
export async function getFees(
|
|
72
|
+
pioneer: any,
|
|
73
|
+
networkId: string
|
|
74
|
+
): Promise<NormalizedFeeRates> {
|
|
75
|
+
const tag = TAG + ' | getFees | ';
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
console.log(tag, `Fetching fees for network: ${networkId}`);
|
|
79
|
+
|
|
80
|
+
// For Cosmos chains, always use hardcoded fees
|
|
81
|
+
const networkType = getNetworkType(networkId);
|
|
82
|
+
if (networkType === 'COSMOS') {
|
|
83
|
+
console.log(tag, 'Using hardcoded fees for Cosmos network:', networkId);
|
|
84
|
+
return getCosmosFees(networkId);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Get raw fee data from API
|
|
88
|
+
const feeResponse = await (pioneer.GetFeeRateByNetwork
|
|
89
|
+
? pioneer.GetFeeRateByNetwork({ networkId })
|
|
90
|
+
: pioneer.GetFeeRate({ networkId }));
|
|
91
|
+
|
|
92
|
+
if (!feeResponse || !feeResponse.data) {
|
|
93
|
+
throw new Error(`No fee data returned for ${networkId}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const feeData = feeResponse.data;
|
|
97
|
+
console.log(tag, 'Raw fee data:', feeData);
|
|
98
|
+
|
|
99
|
+
// Network type already detected above, just get network name
|
|
100
|
+
const networkName = getNetworkName(networkId);
|
|
101
|
+
|
|
102
|
+
// Normalize the fee data based on format (pass networkId for sanity checks)
|
|
103
|
+
let normalizedFees = normalizeFeeData(feeData, networkType, networkName, networkId);
|
|
104
|
+
|
|
105
|
+
// Ensure fees are differentiated for better UX
|
|
106
|
+
normalizedFees = ensureFeeDifferentiation(normalizedFees, networkType);
|
|
107
|
+
|
|
108
|
+
// Add network metadata
|
|
109
|
+
normalizedFees.networkId = networkId;
|
|
110
|
+
normalizedFees.networkType = networkType;
|
|
111
|
+
normalizedFees.raw = feeData;
|
|
112
|
+
|
|
113
|
+
console.log(tag, 'Normalized fees:', normalizedFees);
|
|
114
|
+
return normalizedFees;
|
|
115
|
+
|
|
116
|
+
} catch (error: any) {
|
|
117
|
+
console.error(tag, 'Failed to fetch fees:', error);
|
|
118
|
+
|
|
119
|
+
// Return sensible defaults on error
|
|
120
|
+
return getFallbackFees(networkId);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Normalize fee data from various API formats to consistent UI format
|
|
126
|
+
*/
|
|
127
|
+
function normalizeFeeData(
|
|
128
|
+
feeData: any,
|
|
129
|
+
networkType: string,
|
|
130
|
+
networkName: string,
|
|
131
|
+
networkId?: string
|
|
132
|
+
): NormalizedFeeRates {
|
|
133
|
+
// Check which format the API returned
|
|
134
|
+
const hasSlowAverageFastest = feeData.slow !== undefined &&
|
|
135
|
+
feeData.average !== undefined &&
|
|
136
|
+
feeData.fastest !== undefined;
|
|
137
|
+
|
|
138
|
+
const hasAverageFastFastest = feeData.average !== undefined &&
|
|
139
|
+
feeData.fast !== undefined &&
|
|
140
|
+
feeData.fastest !== undefined;
|
|
141
|
+
|
|
142
|
+
let slowValue: string, averageValue: string, fastestValue: string;
|
|
143
|
+
|
|
144
|
+
if (hasSlowAverageFastest) {
|
|
145
|
+
// Already in UI format
|
|
146
|
+
slowValue = feeData.slow.toString();
|
|
147
|
+
averageValue = feeData.average.toString();
|
|
148
|
+
fastestValue = feeData.fastest.toString();
|
|
149
|
+
} else if (hasAverageFastFastest) {
|
|
150
|
+
// Map API format to UI format
|
|
151
|
+
slowValue = feeData.average.toString();
|
|
152
|
+
averageValue = feeData.fast.toString();
|
|
153
|
+
fastestValue = feeData.fastest.toString();
|
|
154
|
+
} else {
|
|
155
|
+
throw new Error('Unknown fee data format');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Apply sanity checks for UTXO networks to prevent absurdly high fees
|
|
159
|
+
// The API sometimes returns stale or incorrect data (e.g., DOGE returning 50000 sat/byte instead of 50)
|
|
160
|
+
if (networkType === 'UTXO') {
|
|
161
|
+
const sanityLimits: Record<string, number> = {
|
|
162
|
+
'bip122:00000000001a91e3dace36e2be3bf030': 100, // DOGE max 100 sat/byte (typical: 1-10)
|
|
163
|
+
'bip122:12a765e31ffd4059bada1e25190f6e98': 500, // LTC max 500 sat/byte
|
|
164
|
+
'bip122:000000000000000000651ef99cb9fcbe': 50, // BCH max 50 sat/byte (low fee chain)
|
|
165
|
+
'bip122:000007d91d1254d60e2dd1ae58038307': 50, // DASH max 50 sat/byte (low fee chain)
|
|
166
|
+
'bip122:000000000019d6689c085ae165831e93': 5000, // BTC max 5000 sat/byte (can spike during congestion)
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const matchedNetworkId = networkId && sanityLimits[networkId] ? networkId :
|
|
170
|
+
Object.keys(sanityLimits).find(id => networkName.toLowerCase().includes(id.split(':')[1]?.substring(0, 8)));
|
|
171
|
+
|
|
172
|
+
if (matchedNetworkId && sanityLimits[matchedNetworkId]) {
|
|
173
|
+
const limit = sanityLimits[matchedNetworkId];
|
|
174
|
+
const slowNum = parseFloat(slowValue);
|
|
175
|
+
const avgNum = parseFloat(averageValue);
|
|
176
|
+
const fastestNum = parseFloat(fastestValue);
|
|
177
|
+
|
|
178
|
+
if (slowNum > limit || avgNum > limit || fastestNum > limit) {
|
|
179
|
+
console.warn(`[FEES] Detected absurdly high fees for ${networkName}: slow=${slowNum}, avg=${avgNum}, fastest=${fastestNum}`);
|
|
180
|
+
console.warn(`[FEES] Capping fees to reasonable limits (max: ${limit} sat/byte)`);
|
|
181
|
+
|
|
182
|
+
// Cap to reasonable values - use 10% of limit as conservative default
|
|
183
|
+
const safeFee = (limit * 0.1).toFixed(2);
|
|
184
|
+
const mediumFee = (limit * 0.15).toFixed(2);
|
|
185
|
+
const fastFee = (limit * 0.2).toFixed(2);
|
|
186
|
+
|
|
187
|
+
slowValue = safeFee;
|
|
188
|
+
averageValue = mediumFee;
|
|
189
|
+
fastestValue = fastFee;
|
|
190
|
+
|
|
191
|
+
console.warn(`[FEES] Adjusted to: slow=${slowValue}, avg=${averageValue}, fastest=${fastestValue}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Get unit and descriptions based on network type
|
|
197
|
+
const unit = feeData.unit || getDefaultUnit(networkType);
|
|
198
|
+
const baseDescription = feeData.description || getDefaultDescription(networkType, networkName);
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
slow: {
|
|
202
|
+
label: 'Economy',
|
|
203
|
+
value: slowValue,
|
|
204
|
+
unit,
|
|
205
|
+
description: `${baseDescription} - Lower priority, may take longer to confirm.`,
|
|
206
|
+
estimatedTime: getEstimatedTime(networkType, 'low'),
|
|
207
|
+
priority: 'low',
|
|
208
|
+
},
|
|
209
|
+
average: {
|
|
210
|
+
label: 'Standard',
|
|
211
|
+
value: averageValue,
|
|
212
|
+
unit,
|
|
213
|
+
description: `${baseDescription} - Normal priority, typical confirmation time.`,
|
|
214
|
+
estimatedTime: getEstimatedTime(networkType, 'medium'),
|
|
215
|
+
priority: 'medium',
|
|
216
|
+
},
|
|
217
|
+
fastest: {
|
|
218
|
+
label: 'Priority',
|
|
219
|
+
value: fastestValue,
|
|
220
|
+
unit,
|
|
221
|
+
description: `${baseDescription} - High priority, fastest confirmation.`,
|
|
222
|
+
estimatedTime: getEstimatedTime(networkType, 'high'),
|
|
223
|
+
priority: 'high',
|
|
224
|
+
},
|
|
225
|
+
networkId: '',
|
|
226
|
+
networkType: networkType as any,
|
|
227
|
+
raw: feeData,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Ensure fees are differentiated for better UX
|
|
233
|
+
*/
|
|
234
|
+
function ensureFeeDifferentiation(
|
|
235
|
+
fees: NormalizedFeeRates,
|
|
236
|
+
networkType: string
|
|
237
|
+
): NormalizedFeeRates {
|
|
238
|
+
const slowVal = parseFloat(fees.slow.value) || 0;
|
|
239
|
+
const avgVal = parseFloat(fees.average.value) || 0;
|
|
240
|
+
const fastestVal = parseFloat(fees.fastest.value) || 0;
|
|
241
|
+
|
|
242
|
+
// Check if all values are zero
|
|
243
|
+
if (slowVal === 0 && avgVal === 0 && fastestVal === 0) {
|
|
244
|
+
console.warn('All fee values are 0 - using fallback values');
|
|
245
|
+
// Return sensible defaults based on network type
|
|
246
|
+
if (networkType === 'UTXO') {
|
|
247
|
+
return {
|
|
248
|
+
...fees,
|
|
249
|
+
slow: { ...fees.slow, value: '1' },
|
|
250
|
+
average: { ...fees.average, value: '2' },
|
|
251
|
+
fastest: { ...fees.fastest, value: '3' },
|
|
252
|
+
};
|
|
253
|
+
} else {
|
|
254
|
+
return {
|
|
255
|
+
...fees,
|
|
256
|
+
slow: { ...fees.slow, value: '1' },
|
|
257
|
+
average: { ...fees.average, value: '1.5' },
|
|
258
|
+
fastest: { ...fees.fastest, value: '2' },
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// For UTXO networks with very similar values (like 1, 1, 1.01)
|
|
264
|
+
if (networkType === 'UTXO') {
|
|
265
|
+
const diff = fastestVal - slowVal;
|
|
266
|
+
if (diff < 0.5) {
|
|
267
|
+
console.warn('UTXO fees too similar, adjusting for better UX');
|
|
268
|
+
return {
|
|
269
|
+
...fees,
|
|
270
|
+
slow: { ...fees.slow, value: slowVal.toString() },
|
|
271
|
+
average: { ...fees.average, value: (slowVal + 1).toString() },
|
|
272
|
+
fastest: { ...fees.fastest, value: (slowVal + 2).toString() },
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// For EVM networks, check if values are already well differentiated
|
|
278
|
+
// Don't adjust if there's already good separation
|
|
279
|
+
const slowToAvgRatio = avgVal / slowVal;
|
|
280
|
+
const avgToFastRatio = fastestVal / avgVal;
|
|
281
|
+
|
|
282
|
+
// If ratios show good differentiation (at least 10% difference), keep original
|
|
283
|
+
if (slowToAvgRatio >= 1.1 && avgToFastRatio >= 1.1) {
|
|
284
|
+
return fees; // Already well differentiated
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Only adjust if fees are too similar
|
|
288
|
+
console.warn('Fees not well differentiated, adjusting slightly');
|
|
289
|
+
return {
|
|
290
|
+
...fees,
|
|
291
|
+
slow: { ...fees.slow, value: slowVal.toString() },
|
|
292
|
+
average: { ...fees.average, value: (slowVal * 1.2).toFixed(6) },
|
|
293
|
+
fastest: { ...fees.fastest, value: (slowVal * 1.5).toFixed(6) },
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Get default unit based on network type
|
|
299
|
+
*/
|
|
300
|
+
function getDefaultUnit(networkType: string): string {
|
|
301
|
+
switch (networkType) {
|
|
302
|
+
case 'UTXO':
|
|
303
|
+
return 'sat/vB';
|
|
304
|
+
case 'EVM':
|
|
305
|
+
return 'gwei';
|
|
306
|
+
case 'COSMOS':
|
|
307
|
+
return 'uatom';
|
|
308
|
+
case 'RIPPLE':
|
|
309
|
+
return 'XRP';
|
|
310
|
+
default:
|
|
311
|
+
return 'units';
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Get default description based on network type
|
|
317
|
+
*/
|
|
318
|
+
function getDefaultDescription(networkType: string, networkName: string): string {
|
|
319
|
+
switch (networkType) {
|
|
320
|
+
case 'UTXO':
|
|
321
|
+
return `Fee rate in satoshis per virtual byte for ${networkName}`;
|
|
322
|
+
case 'EVM':
|
|
323
|
+
return `Gas price in Gwei for ${networkName} (1 Gwei = 0.000000001 ETH)`;
|
|
324
|
+
case 'COSMOS':
|
|
325
|
+
return `Transaction fee for ${networkName}`;
|
|
326
|
+
case 'RIPPLE':
|
|
327
|
+
return `Fixed transaction fee for ${networkName}`;
|
|
328
|
+
default:
|
|
329
|
+
return `Transaction fee for ${networkName}`;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Get estimated confirmation time
|
|
335
|
+
*/
|
|
336
|
+
function getEstimatedTime(networkType: string, priority: string): string {
|
|
337
|
+
const times: Record<string, Record<string, string>> = {
|
|
338
|
+
UTXO: {
|
|
339
|
+
low: '~60+ minutes',
|
|
340
|
+
medium: '~30 minutes',
|
|
341
|
+
high: '~10 minutes',
|
|
342
|
+
},
|
|
343
|
+
EVM: {
|
|
344
|
+
low: '~5 minutes',
|
|
345
|
+
medium: '~2 minutes',
|
|
346
|
+
high: '~30 seconds',
|
|
347
|
+
},
|
|
348
|
+
COSMOS: {
|
|
349
|
+
low: '~10 seconds',
|
|
350
|
+
medium: '~7 seconds',
|
|
351
|
+
high: '~5 seconds',
|
|
352
|
+
},
|
|
353
|
+
RIPPLE: {
|
|
354
|
+
low: '~4 seconds',
|
|
355
|
+
medium: '~4 seconds',
|
|
356
|
+
high: '~4 seconds',
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
return times[networkType]?.[priority] || '~varies';
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Get hardcoded Cosmos fees based on network
|
|
365
|
+
*/
|
|
366
|
+
function getCosmosFees(networkId: string): NormalizedFeeRates {
|
|
367
|
+
const networkName = getNetworkName(networkId);
|
|
368
|
+
|
|
369
|
+
// These match the fees in txbuilder/createUnsignedTendermintTx.ts
|
|
370
|
+
const cosmosFeesMap: Record<string, { base: number; unit: string; denom: string }> = {
|
|
371
|
+
'cosmos:thorchain-mainnet-v1': { base: 0.02, unit: 'RUNE', denom: 'rune' },
|
|
372
|
+
'cosmos:mayachain-mainnet-v1': { base: 0.2, unit: 'MAYA', denom: 'maya' },
|
|
373
|
+
'cosmos:cosmoshub-4': { base: 0.005, unit: 'ATOM', denom: 'uatom' },
|
|
374
|
+
'cosmos:osmosis-1': { base: 0.035, unit: 'OSMO', denom: 'uosmo' },
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
const feeConfig = cosmosFeesMap[networkId] || { base: 0.025, unit: 'units', denom: 'units' };
|
|
378
|
+
|
|
379
|
+
// For Cosmos, we provide the base fee with different priority multipliers
|
|
380
|
+
const slowFee = feeConfig.base.toString();
|
|
381
|
+
const avgFee = (feeConfig.base * 1.5).toFixed(4);
|
|
382
|
+
const fastFee = (feeConfig.base * 2).toFixed(4);
|
|
383
|
+
|
|
384
|
+
return {
|
|
385
|
+
slow: {
|
|
386
|
+
label: 'Economy',
|
|
387
|
+
value: slowFee,
|
|
388
|
+
unit: feeConfig.unit,
|
|
389
|
+
description: `Standard fee for ${networkName}. Gas is automatically calculated.`,
|
|
390
|
+
estimatedTime: '~10 seconds',
|
|
391
|
+
priority: 'low',
|
|
392
|
+
},
|
|
393
|
+
average: {
|
|
394
|
+
label: 'Standard',
|
|
395
|
+
value: avgFee,
|
|
396
|
+
unit: feeConfig.unit,
|
|
397
|
+
description: `Priority fee for ${networkName}. Slightly higher for faster processing.`,
|
|
398
|
+
estimatedTime: '~7 seconds',
|
|
399
|
+
priority: 'medium',
|
|
400
|
+
},
|
|
401
|
+
fastest: {
|
|
402
|
+
label: 'Priority',
|
|
403
|
+
value: fastFee,
|
|
404
|
+
unit: feeConfig.unit,
|
|
405
|
+
description: `Maximum priority for ${networkName}. Fastest possible confirmation.`,
|
|
406
|
+
estimatedTime: '~5 seconds',
|
|
407
|
+
priority: 'high',
|
|
408
|
+
},
|
|
409
|
+
networkId,
|
|
410
|
+
networkType: 'COSMOS',
|
|
411
|
+
raw: { hardcoded: true, base: feeConfig.base, unit: feeConfig.unit, denom: feeConfig.denom },
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Get fallback fees when API fails
|
|
417
|
+
*/
|
|
418
|
+
function getFallbackFees(networkId: string): NormalizedFeeRates {
|
|
419
|
+
const networkType = getNetworkType(networkId);
|
|
420
|
+
const networkName = getNetworkName(networkId);
|
|
421
|
+
|
|
422
|
+
// For Cosmos chains, use hardcoded fees
|
|
423
|
+
if (networkType === 'COSMOS') {
|
|
424
|
+
return getCosmosFees(networkId);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Default fallback values by network type
|
|
428
|
+
const fallbacks: Record<string, { slow: string; average: string; fastest: string; unit: string }> = {
|
|
429
|
+
UTXO: { slow: '1', average: '2', fastest: '3', unit: 'sat/vB' },
|
|
430
|
+
EVM: { slow: '1', average: '1.5', fastest: '2', unit: 'gwei' },
|
|
431
|
+
RIPPLE: { slow: '0.00001', average: '0.00001', fastest: '0.00001', unit: 'XRP' },
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
const fallback = fallbacks[networkType] || fallbacks.UTXO;
|
|
435
|
+
|
|
436
|
+
return {
|
|
437
|
+
slow: {
|
|
438
|
+
label: 'Economy',
|
|
439
|
+
value: fallback.slow,
|
|
440
|
+
unit: fallback.unit,
|
|
441
|
+
description: `Default fee for ${networkName} (API unavailable)`,
|
|
442
|
+
estimatedTime: getEstimatedTime(networkType, 'low'),
|
|
443
|
+
priority: 'low',
|
|
444
|
+
},
|
|
445
|
+
average: {
|
|
446
|
+
label: 'Standard',
|
|
447
|
+
value: fallback.average,
|
|
448
|
+
unit: fallback.unit,
|
|
449
|
+
description: `Default fee for ${networkName} (API unavailable)`,
|
|
450
|
+
estimatedTime: getEstimatedTime(networkType, 'medium'),
|
|
451
|
+
priority: 'medium',
|
|
452
|
+
},
|
|
453
|
+
fastest: {
|
|
454
|
+
label: 'Priority',
|
|
455
|
+
value: fallback.fastest,
|
|
456
|
+
unit: fallback.unit,
|
|
457
|
+
description: `Default fee for ${networkName} (API unavailable)`,
|
|
458
|
+
estimatedTime: getEstimatedTime(networkType, 'high'),
|
|
459
|
+
priority: 'high',
|
|
460
|
+
},
|
|
461
|
+
networkId,
|
|
462
|
+
networkType: networkType as any,
|
|
463
|
+
raw: null,
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Calculate estimated transaction fee based on fee rate and transaction size
|
|
469
|
+
*/
|
|
470
|
+
export function estimateTransactionFee(
|
|
471
|
+
feeRate: string,
|
|
472
|
+
unit: string,
|
|
473
|
+
networkType: string,
|
|
474
|
+
txSize?: number
|
|
475
|
+
): FeeEstimate {
|
|
476
|
+
switch (networkType) {
|
|
477
|
+
case 'UTXO':
|
|
478
|
+
// For UTXO chains, multiply fee rate by transaction size
|
|
479
|
+
const sizeInBytes = txSize || 250; // Default estimate
|
|
480
|
+
const feeInSatoshis = parseFloat(feeRate) * sizeInBytes;
|
|
481
|
+
const feeInBTC = feeInSatoshis / 100000000;
|
|
482
|
+
return {
|
|
483
|
+
amount: feeInBTC.toFixed(8),
|
|
484
|
+
unit: 'BTC',
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
case 'EVM':
|
|
488
|
+
// For EVM chains, multiply gas price by gas limit
|
|
489
|
+
const gasLimit = 21000; // Standard transfer
|
|
490
|
+
const feeInGwei = parseFloat(feeRate) * gasLimit;
|
|
491
|
+
const feeInEth = feeInGwei / 1000000000;
|
|
492
|
+
return {
|
|
493
|
+
amount: feeInEth.toFixed(9),
|
|
494
|
+
unit: 'ETH',
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
case 'RIPPLE':
|
|
498
|
+
// Ripple has fixed fees
|
|
499
|
+
return {
|
|
500
|
+
amount: feeRate,
|
|
501
|
+
unit: 'XRP',
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
default:
|
|
505
|
+
return {
|
|
506
|
+
amount: feeRate,
|
|
507
|
+
unit: unit,
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
}
|
package/src/getPubkey.ts
CHANGED
|
@@ -47,45 +47,31 @@ export const getPubkey = async (networkId: string, path: any, sdk: any, context:
|
|
|
47
47
|
}, 30000);
|
|
48
48
|
|
|
49
49
|
try {
|
|
50
|
-
let result;
|
|
51
50
|
switch (networkType) {
|
|
52
51
|
case 'UTXO':
|
|
53
|
-
|
|
52
|
+
({ address } = await sdk.address.utxoGetAddress(addressInfo));
|
|
54
53
|
break;
|
|
55
54
|
case 'EVM':
|
|
56
|
-
|
|
55
|
+
({ address } = await sdk.address.ethereumGetAddress(addressInfo));
|
|
57
56
|
break;
|
|
58
57
|
case 'OSMOSIS':
|
|
59
|
-
|
|
58
|
+
({ address } = await sdk.address.osmosisGetAddress(addressInfo));
|
|
60
59
|
break;
|
|
61
60
|
case 'COSMOS':
|
|
62
|
-
|
|
61
|
+
({ address } = await sdk.address.cosmosGetAddress(addressInfo));
|
|
63
62
|
break;
|
|
64
63
|
case 'MAYACHAIN':
|
|
65
|
-
|
|
64
|
+
({ address } = await sdk.address.mayachainGetAddress(addressInfo));
|
|
66
65
|
break;
|
|
67
66
|
case 'THORCHAIN':
|
|
68
|
-
|
|
67
|
+
({ address } = await sdk.address.thorchainGetAddress(addressInfo));
|
|
69
68
|
break;
|
|
70
69
|
case 'XRP':
|
|
71
|
-
|
|
70
|
+
({ address } = await sdk.address.xrpGetAddress(addressInfo));
|
|
72
71
|
break;
|
|
73
72
|
default:
|
|
74
73
|
throw new Error(`Unsupported network type for networkId: ${networkId}`);
|
|
75
74
|
}
|
|
76
|
-
|
|
77
|
-
if (!result) {
|
|
78
|
-
throw new Error(`Address call returned null/undefined for ${networkType} (${networkId})`);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (typeof result === 'object' && result.address) {
|
|
82
|
-
address = result.address;
|
|
83
|
-
} else if (typeof result === 'string') {
|
|
84
|
-
address = result;
|
|
85
|
-
} else {
|
|
86
|
-
throw new Error(`Invalid address response format for ${networkType} (${networkId}): ${JSON.stringify(result)}`);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
75
|
clearTimeout(addressTimeout);
|
|
90
76
|
} catch (addressError) {
|
|
91
77
|
clearTimeout(addressTimeout);
|
|
@@ -99,6 +85,14 @@ export const getPubkey = async (networkId: string, path: any, sdk: any, context:
|
|
|
99
85
|
pubkey.master = address;
|
|
100
86
|
pubkey.address = address;
|
|
101
87
|
if (['xpub', 'ypub', 'zpub'].includes(path.type)) {
|
|
88
|
+
// ========================================
|
|
89
|
+
// XPUB RETRIEVAL - CRITICAL FOR UTXO CHAINS
|
|
90
|
+
// ========================================
|
|
91
|
+
console.log('🔑 [XPUB] Starting xpub retrieval for path type:', path.type);
|
|
92
|
+
console.log('🔑 [XPUB] Network:', networkId);
|
|
93
|
+
console.log('🔑 [XPUB] Path:', addressNListToBIP32(path.addressNList));
|
|
94
|
+
console.log('🔑 [XPUB] Script type:', path.script_type);
|
|
95
|
+
|
|
102
96
|
const pathQuery = {
|
|
103
97
|
symbol: 'BTC',
|
|
104
98
|
coin: 'Bitcoin',
|
|
@@ -106,30 +100,67 @@ export const getPubkey = async (networkId: string, path: any, sdk: any, context:
|
|
|
106
100
|
address_n: path.addressNList,
|
|
107
101
|
showDisplay: false,
|
|
108
102
|
};
|
|
109
|
-
|
|
103
|
+
|
|
104
|
+
console.log('🔑 [XPUB] Calling sdk.system.info.getPublicKey with:', JSON.stringify(pathQuery, null, 2));
|
|
105
|
+
|
|
110
106
|
const xpubTimeout = setTimeout(() => {
|
|
111
|
-
console.error('getPublicKey timeout after 20 seconds');
|
|
107
|
+
console.error('⏰ [XPUB TIMEOUT] getPublicKey timeout after 20 seconds');
|
|
108
|
+
console.error('⏰ [XPUB TIMEOUT] Path:', addressNListToBIP32(path.addressNList));
|
|
109
|
+
console.error('⏰ [XPUB TIMEOUT] This is a CRITICAL FAILURE - UTXO balances require xpubs');
|
|
112
110
|
}, 20000);
|
|
113
|
-
|
|
111
|
+
|
|
114
112
|
try {
|
|
113
|
+
console.log('🔑 [XPUB] Calling getPublicKey...');
|
|
115
114
|
const responsePubkey = await sdk.system.info.getPublicKey(pathQuery);
|
|
116
115
|
clearTimeout(xpubTimeout);
|
|
117
116
|
|
|
117
|
+
console.log('✅ [XPUB] getPublicKey SUCCESS');
|
|
118
|
+
console.log('✅ [XPUB] Response:', JSON.stringify(responsePubkey, null, 2));
|
|
119
|
+
|
|
120
|
+
if (!responsePubkey || !responsePubkey.xpub) {
|
|
121
|
+
const error = new Error('FAIL FAST: getPublicKey returned null or missing xpub');
|
|
122
|
+
console.error('❌ [XPUB] CRITICAL:', error.message);
|
|
123
|
+
console.error('❌ [XPUB] Response was:', responsePubkey);
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
|
|
118
127
|
if (path.script_type === 'p2wpkh') {
|
|
128
|
+
console.log('🔑 [XPUB] Converting xpub to zpub for native segwit');
|
|
119
129
|
responsePubkey.xpub = xpubConvert(responsePubkey.xpub, 'zpub');
|
|
120
130
|
} else if (path.script_type === 'p2sh-p2wpkh') {
|
|
131
|
+
console.log('🔑 [XPUB] Converting xpub to ypub for wrapped segwit');
|
|
121
132
|
responsePubkey.xpub = xpubConvert(responsePubkey.xpub, 'ypub');
|
|
122
133
|
}
|
|
123
134
|
|
|
135
|
+
console.log('✅ [XPUB] Final xpub:', responsePubkey.xpub.substring(0, 20) + '...');
|
|
136
|
+
|
|
124
137
|
pubkey.pubkey = responsePubkey.xpub;
|
|
125
138
|
pubkey.path = addressNListToBIP32(path.addressNList);
|
|
126
139
|
pubkey.pathMaster = addressNListToBIP32(path.addressNListMaster);
|
|
140
|
+
|
|
141
|
+
console.log('✅ [XPUB] Xpub retrieval COMPLETE for', path.note || path.type);
|
|
127
142
|
} catch (xpubError) {
|
|
128
143
|
clearTimeout(xpubTimeout);
|
|
129
|
-
console.error('getPublicKey failed
|
|
130
|
-
|
|
144
|
+
console.error('❌ [XPUB] CRITICAL FAILURE - getPublicKey failed');
|
|
145
|
+
console.error('❌ [XPUB] Error:', xpubError);
|
|
146
|
+
console.error('❌ [XPUB] Error message:', xpubError.message);
|
|
147
|
+
console.error('❌ [XPUB] Error stack:', xpubError.stack);
|
|
148
|
+
console.error('❌ [XPUB] Path:', addressNListToBIP32(path.addressNList));
|
|
149
|
+
console.error('❌ [XPUB] Network:', networkId);
|
|
150
|
+
console.error('❌ [XPUB] Query:', JSON.stringify(pathQuery, null, 2));
|
|
151
|
+
console.error('❌ [XPUB] SDK available:', !!sdk);
|
|
152
|
+
console.error('❌ [XPUB] SDK.system available:', !!sdk?.system);
|
|
153
|
+
console.error('❌ [XPUB] SDK.system.info available:', !!sdk?.system?.info);
|
|
154
|
+
console.error('❌ [XPUB] SDK.system.info.getPublicKey available:', !!sdk?.system?.info?.getPublicKey);
|
|
155
|
+
console.error('');
|
|
156
|
+
console.error('🚨 FAIL FAST: Cannot proceed without xpub for UTXO chains');
|
|
157
|
+
console.error('🚨 UTXO balance queries REQUIRE extended public keys (xpubs)');
|
|
158
|
+
console.error('🚨 This is a device communication or SDK issue that must be resolved');
|
|
159
|
+
console.error('');
|
|
160
|
+
throw new Error(`FAIL FAST - xpub retrieval failed for ${networkId} at ${addressNListToBIP32(path.addressNList)}: ${xpubError.message}`);
|
|
131
161
|
}
|
|
132
162
|
} else {
|
|
163
|
+
console.log('🔑 [PUBKEY] Non-xpub path (address-based), using address as pubkey');
|
|
133
164
|
pubkey.pubkey = address;
|
|
134
165
|
pubkey.path = addressNListToBIP32(path.addressNList);
|
|
135
166
|
pubkey.pathMaster = addressNListToBIP32(path.addressNListMaster);
|