@keplr-wallet/hooks-evm 0.13.15-rc.0
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/.eslintrc.json +14 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +18 -0
- package/build/index.js.map +1 -0
- package/build/tx/amount.d.ts +27 -0
- package/build/tx/amount.js +238 -0
- package/build/tx/amount.js.map +1 -0
- package/build/tx/chain.d.ts +10 -0
- package/build/tx/chain.js +37 -0
- package/build/tx/chain.js.map +1 -0
- package/build/tx/errors.d.ts +30 -0
- package/build/tx/errors.js +84 -0
- package/build/tx/errors.js.map +1 -0
- package/build/tx/evm-fee-utils.d.ts +28 -0
- package/build/tx/evm-fee-utils.js +133 -0
- package/build/tx/evm-fee-utils.js.map +1 -0
- package/build/tx/fee.d.ts +61 -0
- package/build/tx/fee.js +523 -0
- package/build/tx/fee.js.map +1 -0
- package/build/tx/gas-simulator.d.ts +89 -0
- package/build/tx/gas-simulator.js +465 -0
- package/build/tx/gas-simulator.js.map +1 -0
- package/build/tx/gas.d.ts +12 -0
- package/build/tx/gas.js +84 -0
- package/build/tx/gas.js.map +1 -0
- package/build/tx/index.d.ts +13 -0
- package/build/tx/index.js +30 -0
- package/build/tx/index.js.map +1 -0
- package/build/tx/internal.d.ts +3 -0
- package/build/tx/internal.js +3 -0
- package/build/tx/internal.js.map +1 -0
- package/build/tx/name-service-ens.d.ts +40 -0
- package/build/tx/name-service-ens.js +189 -0
- package/build/tx/name-service-ens.js.map +1 -0
- package/build/tx/name-service.d.ts +20 -0
- package/build/tx/name-service.js +20 -0
- package/build/tx/name-service.js.map +1 -0
- package/build/tx/recipient.d.ts +35 -0
- package/build/tx/recipient.js +131 -0
- package/build/tx/recipient.js.map +1 -0
- package/build/tx/send-tx.d.ts +13 -0
- package/build/tx/send-tx.js +24 -0
- package/build/tx/send-tx.js.map +1 -0
- package/build/tx/sender.d.ts +12 -0
- package/build/tx/sender.js +73 -0
- package/build/tx/sender.js.map +1 -0
- package/build/tx/types.d.ts +102 -0
- package/build/tx/types.js +3 -0
- package/build/tx/types.js.map +1 -0
- package/build/tx/validate.d.ts +11 -0
- package/build/tx/validate.js +37 -0
- package/build/tx/validate.js.map +1 -0
- package/package.json +40 -0
- package/src/index.ts +1 -0
- package/src/tx/amount.ts +273 -0
- package/src/tx/chain.ts +31 -0
- package/src/tx/errors.ts +79 -0
- package/src/tx/evm-fee-utils.ts +217 -0
- package/src/tx/fee.ts +622 -0
- package/src/tx/gas-simulator.ts +567 -0
- package/src/tx/gas.ts +93 -0
- package/src/tx/index.ts +13 -0
- package/src/tx/internal.ts +4 -0
- package/src/tx/name-service-ens.ts +207 -0
- package/src/tx/name-service.ts +39 -0
- package/src/tx/recipient.ts +166 -0
- package/src/tx/send-tx.ts +55 -0
- package/src/tx/sender.ts +82 -0
- package/src/tx/types.ts +153 -0
- package/src/tx/validate.ts +55 -0
- package/tsconfig.check.json +90 -0
- package/tsconfig.json +12 -0
package/src/tx/fee.ts
ADDED
|
@@ -0,0 +1,622 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EVMFeeType,
|
|
3
|
+
IAmountConfig,
|
|
4
|
+
IFeeConfig,
|
|
5
|
+
IGasConfig,
|
|
6
|
+
ISenderConfig,
|
|
7
|
+
UIProperties,
|
|
8
|
+
} from "./types";
|
|
9
|
+
import { TxChainSetter } from "./chain";
|
|
10
|
+
import { ChainGetter } from "@keplr-wallet/stores";
|
|
11
|
+
import { action, computed, makeObservable, observable } from "mobx";
|
|
12
|
+
import { CoinPretty, Dec, Int } from "@keplr-wallet/unit";
|
|
13
|
+
import { useState } from "react";
|
|
14
|
+
import { QueriesStore } from "./internal";
|
|
15
|
+
import { InsufficientFeeError } from "./errors";
|
|
16
|
+
import { computedFn } from "mobx-utils";
|
|
17
|
+
import { EthereumQueriesImpl } from "@keplr-wallet/stores-eth";
|
|
18
|
+
import {
|
|
19
|
+
calculateOptimalMaxPriorityFeePerGas,
|
|
20
|
+
computeEIP1559TxFees,
|
|
21
|
+
ETH_FEE_HISTORY_BLOCK_COUNT,
|
|
22
|
+
ETH_FEE_HISTORY_NEWEST_BLOCK,
|
|
23
|
+
ETH_FEE_HISTORY_REWARD_PERCENTILES,
|
|
24
|
+
getEIP1559QueryUIState,
|
|
25
|
+
getL1DataFeeToAdd,
|
|
26
|
+
GWEI,
|
|
27
|
+
} from "./evm-fee-utils";
|
|
28
|
+
|
|
29
|
+
export class FeeConfig extends TxChainSetter implements IFeeConfig {
|
|
30
|
+
@observable
|
|
31
|
+
protected _type: EVMFeeType = "average";
|
|
32
|
+
|
|
33
|
+
@observable
|
|
34
|
+
protected _customPriorityFee: string = "";
|
|
35
|
+
|
|
36
|
+
@observable
|
|
37
|
+
protected _customPriorityFeeInput: string = "";
|
|
38
|
+
|
|
39
|
+
@observable
|
|
40
|
+
protected _customGasPrice: string = "";
|
|
41
|
+
|
|
42
|
+
@observable
|
|
43
|
+
protected _customGasPriceInput: string = "";
|
|
44
|
+
|
|
45
|
+
@observable
|
|
46
|
+
protected _forceLegacyFeeMode: boolean | undefined = undefined;
|
|
47
|
+
|
|
48
|
+
@observable
|
|
49
|
+
protected _disableBalanceCheck: boolean = false;
|
|
50
|
+
|
|
51
|
+
@observable
|
|
52
|
+
protected additionAmountToNeedFee: boolean = true;
|
|
53
|
+
|
|
54
|
+
@observable
|
|
55
|
+
protected _l1DataFee: Dec | undefined = undefined;
|
|
56
|
+
|
|
57
|
+
constructor(
|
|
58
|
+
chainGetter: ChainGetter,
|
|
59
|
+
protected readonly queriesStore: QueriesStore,
|
|
60
|
+
initialChainId: string,
|
|
61
|
+
protected readonly senderConfig: ISenderConfig,
|
|
62
|
+
protected readonly amountConfig: IAmountConfig,
|
|
63
|
+
protected readonly gasConfig: IGasConfig,
|
|
64
|
+
additionAmountToNeedFee: boolean = true
|
|
65
|
+
) {
|
|
66
|
+
super(chainGetter, initialChainId);
|
|
67
|
+
|
|
68
|
+
this.additionAmountToNeedFee = additionAmountToNeedFee;
|
|
69
|
+
makeObservable(this);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@action
|
|
73
|
+
setAdditionAmountToNeedFee(additionAmountToNeedFee: boolean) {
|
|
74
|
+
this.additionAmountToNeedFee = additionAmountToNeedFee;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@action
|
|
78
|
+
setDisableBalanceCheck(bool: boolean) {
|
|
79
|
+
this._disableBalanceCheck = bool;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get type(): EVMFeeType {
|
|
83
|
+
return this._type;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@action
|
|
87
|
+
setType(type: EVMFeeType): void {
|
|
88
|
+
if (type === "custom" && this._customPriorityFee === "") {
|
|
89
|
+
const currentFees = this.getEIP1559TxFees(this._type);
|
|
90
|
+
const priorityFee = currentFees.maxPriorityFeePerGas;
|
|
91
|
+
if (priorityFee) {
|
|
92
|
+
this._customPriorityFee = priorityFee.truncate().toString();
|
|
93
|
+
this._customPriorityFeeInput = priorityFee.quo(GWEI).toString(9);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (this.isLegacyFeeMode) {
|
|
97
|
+
const fees = this.getEIP1559TxFees("average");
|
|
98
|
+
const currentGasPrice = fees.gasPrice ?? new Dec(0);
|
|
99
|
+
this._customGasPrice = currentGasPrice.truncate().toString();
|
|
100
|
+
this._customGasPriceInput = currentGasPrice.quo(GWEI).toString(9);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (type !== "custom") {
|
|
104
|
+
this._forceLegacyFeeMode = undefined;
|
|
105
|
+
}
|
|
106
|
+
this._type = type;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@action
|
|
110
|
+
setForceLegacyFeeMode(value: boolean | undefined): void {
|
|
111
|
+
this._forceLegacyFeeMode = value;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
get customPriorityFee(): string {
|
|
115
|
+
return this._customPriorityFee;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
get customPriorityFeeInput(): string {
|
|
119
|
+
return this._customPriorityFeeInput;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@action
|
|
123
|
+
setCustomPriorityFee(gweiValue: string): void {
|
|
124
|
+
if (gweiValue.startsWith(".")) {
|
|
125
|
+
gweiValue = "0" + gweiValue;
|
|
126
|
+
}
|
|
127
|
+
this._customPriorityFeeInput = gweiValue;
|
|
128
|
+
|
|
129
|
+
if (gweiValue.trim() === "") {
|
|
130
|
+
this._customPriorityFee = "";
|
|
131
|
+
} else {
|
|
132
|
+
try {
|
|
133
|
+
this._customPriorityFee = new Dec(gweiValue)
|
|
134
|
+
.mul(GWEI)
|
|
135
|
+
.truncate()
|
|
136
|
+
.toString();
|
|
137
|
+
} catch {
|
|
138
|
+
// keep previous value on invalid input
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@computed
|
|
144
|
+
get isLegacyFeeMode(): boolean {
|
|
145
|
+
if (this._forceLegacyFeeMode !== undefined) {
|
|
146
|
+
return this._forceLegacyFeeMode;
|
|
147
|
+
}
|
|
148
|
+
const ethereumQueries = this.getEthereumQueries();
|
|
149
|
+
if (!ethereumQueries || !this.canEIP1559TxFeesAndReady()) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
const block = ethereumQueries.queryEthereumBlock.getQueryByBlockNumberOrTag(
|
|
153
|
+
ETH_FEE_HISTORY_NEWEST_BLOCK
|
|
154
|
+
).block;
|
|
155
|
+
if (!block || block.baseFeePerGas == null) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
return parseInt(block.baseFeePerGas) === 0;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
get customGasPrice(): string {
|
|
162
|
+
return this._customGasPrice;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
get customGasPriceInput(): string {
|
|
166
|
+
return this._customGasPriceInput;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
@action
|
|
170
|
+
setCustomGasPrice(gweiValue: string): void {
|
|
171
|
+
if (gweiValue.startsWith(".")) {
|
|
172
|
+
gweiValue = "0" + gweiValue;
|
|
173
|
+
}
|
|
174
|
+
this._customGasPriceInput = gweiValue;
|
|
175
|
+
|
|
176
|
+
if (gweiValue.trim() === "") {
|
|
177
|
+
this._customGasPrice = "";
|
|
178
|
+
} else {
|
|
179
|
+
try {
|
|
180
|
+
this._customGasPrice = new Dec(gweiValue)
|
|
181
|
+
.mul(GWEI)
|
|
182
|
+
.truncate()
|
|
183
|
+
.toString();
|
|
184
|
+
} catch {
|
|
185
|
+
// keep previous value on invalid input
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
protected get currentPriorityFee(): Dec {
|
|
191
|
+
const ethereumQueries = this.queriesStore.get(this.chainId).ethereum;
|
|
192
|
+
if (!ethereumQueries || !this.canEIP1559TxFeesAndReady()) {
|
|
193
|
+
return new Dec(0);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const feeType = this.type === "custom" ? "average" : this.type;
|
|
197
|
+
const priorityFee = calculateOptimalMaxPriorityFeePerGas(
|
|
198
|
+
ethereumQueries,
|
|
199
|
+
feeType,
|
|
200
|
+
this.chainId
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
return priorityFee;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
get l1DataFee(): Dec | undefined {
|
|
207
|
+
return this._l1DataFee;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
@action
|
|
211
|
+
setL1DataFee(fee: Dec) {
|
|
212
|
+
this._l1DataFee = fee;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
protected getEthereumQueries(): EthereumQueriesImpl | undefined {
|
|
216
|
+
return this.queriesStore.get(this.chainId).ethereum;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
protected canEIP1559TxFeesAndReady(isRefresh?: boolean): boolean {
|
|
220
|
+
if (!this.senderConfig.sender.startsWith("0x")) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const ethereumQueries = this.getEthereumQueries();
|
|
225
|
+
if (!ethereumQueries) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const blockQuery =
|
|
230
|
+
ethereumQueries.queryEthereumBlock.getQueryByBlockNumberOrTag(
|
|
231
|
+
ETH_FEE_HISTORY_NEWEST_BLOCK
|
|
232
|
+
);
|
|
233
|
+
if (blockQuery.block != null) {
|
|
234
|
+
if (isRefresh) {
|
|
235
|
+
blockQuery.waitFreshResponse();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const feeHistoryQuery =
|
|
239
|
+
ethereumQueries.queryEthereumFeeHistory.getQueryByFeeHistoryParams(
|
|
240
|
+
ETH_FEE_HISTORY_BLOCK_COUNT,
|
|
241
|
+
ETH_FEE_HISTORY_NEWEST_BLOCK,
|
|
242
|
+
ETH_FEE_HISTORY_REWARD_PERCENTILES
|
|
243
|
+
);
|
|
244
|
+
if (feeHistoryQuery.feeHistory != null) {
|
|
245
|
+
if (isRefresh) {
|
|
246
|
+
feeHistoryQuery.waitFreshResponse();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const maxPriorityFeePerGasQuery =
|
|
251
|
+
ethereumQueries.queryEthereumMaxPriorityFee;
|
|
252
|
+
if (maxPriorityFeePerGasQuery.maxPriorityFeePerGas != null) {
|
|
253
|
+
if (isRefresh) {
|
|
254
|
+
maxPriorityFeePerGasQuery.waitFreshResponse();
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const gasPriceQuery = ethereumQueries.queryEthereumGasPrice;
|
|
262
|
+
if (gasPriceQuery.gasPrice != null) {
|
|
263
|
+
if (isRefresh) {
|
|
264
|
+
gasPriceQuery.waitFreshResponse();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
refreshEIP1559TxFees() {
|
|
274
|
+
this.canEIP1559TxFeesAndReady(true);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
readonly getEIP1559TxFees = computedFn(
|
|
278
|
+
(feeTypeOrManual: EVMFeeType | "manual") => {
|
|
279
|
+
const isCustom = feeTypeOrManual === "custom";
|
|
280
|
+
const feeType =
|
|
281
|
+
feeTypeOrManual === "manual" || feeTypeOrManual === "custom"
|
|
282
|
+
? "average"
|
|
283
|
+
: feeTypeOrManual;
|
|
284
|
+
|
|
285
|
+
const ethereumQueries = this.getEthereumQueries();
|
|
286
|
+
if (ethereumQueries && this.canEIP1559TxFeesAndReady()) {
|
|
287
|
+
if (feeTypeOrManual === "custom") {
|
|
288
|
+
// Legacy mode: use custom gas price if available
|
|
289
|
+
if (this.isLegacyFeeMode) {
|
|
290
|
+
if (isCustom && this._customGasPrice.trim() !== "") {
|
|
291
|
+
try {
|
|
292
|
+
return { gasPrice: new Dec(this._customGasPrice) };
|
|
293
|
+
} catch {
|
|
294
|
+
// fall through to default
|
|
295
|
+
}
|
|
296
|
+
} else {
|
|
297
|
+
return computeEIP1559TxFees(
|
|
298
|
+
ethereumQueries,
|
|
299
|
+
feeType,
|
|
300
|
+
this.chainId
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// EIP-1559 mode: use custom priority fee
|
|
305
|
+
if (isCustom && this._customPriorityFee.trim() !== "") {
|
|
306
|
+
try {
|
|
307
|
+
const customWei = new Dec(this._customPriorityFee);
|
|
308
|
+
// 여기서 feeType이 average인 이유는 customWei값이 존재 하면 실제로 계산 하지 않고 설정된 값으로 전달이 되기 때문에
|
|
309
|
+
// fallback feeType이다.
|
|
310
|
+
return computeEIP1559TxFees(
|
|
311
|
+
ethereumQueries,
|
|
312
|
+
"average",
|
|
313
|
+
this.chainId,
|
|
314
|
+
customWei
|
|
315
|
+
);
|
|
316
|
+
} catch {
|
|
317
|
+
// fall through to default
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return computeEIP1559TxFees(ethereumQueries, "average", this.chainId);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return computeEIP1559TxFees(ethereumQueries, feeType, this.chainId);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return {
|
|
327
|
+
gasPrice: new Dec(0),
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
protected getFeeCurrency(): AppCurrency {
|
|
333
|
+
const u = this.modularChainInfo.unwrapped;
|
|
334
|
+
if (u.type !== "evm" && u.type !== "ethermint") {
|
|
335
|
+
throw new Error("Chain is not an EVM chain");
|
|
336
|
+
}
|
|
337
|
+
return u.evm.nativeCurrency;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
@computed
|
|
341
|
+
get maxFeePerGas(): Dec | undefined {
|
|
342
|
+
const fees = this.getEIP1559TxFees(this.type);
|
|
343
|
+
return fees.maxFeePerGas;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
@computed
|
|
347
|
+
get maxPriorityFeePerGas(): Dec | undefined {
|
|
348
|
+
const fees = this.getEIP1559TxFees(this.type);
|
|
349
|
+
return fees.maxPriorityFeePerGas;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
@computed
|
|
353
|
+
get gasPrice(): Dec | undefined {
|
|
354
|
+
const fees = this.getEIP1559TxFees(this.type);
|
|
355
|
+
return fees.gasPrice ?? fees.maxFeePerGas;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
protected getL1DataFeeToAdd(): Dec {
|
|
359
|
+
return getL1DataFeeToAdd(
|
|
360
|
+
this.modularChainInfo.hasFeature("op-stack-l1-data-fee"),
|
|
361
|
+
this._l1DataFee
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
@computed
|
|
366
|
+
get fee(): CoinPretty | undefined {
|
|
367
|
+
const price = this.gasPrice;
|
|
368
|
+
if (!price || price.equals(new Dec(0))) {
|
|
369
|
+
return undefined;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const feeCurrency = this.getFeeCurrency();
|
|
373
|
+
const gasDec = new Dec(this.gasConfig.gas);
|
|
374
|
+
const l1DataFeeToAdd = this.getL1DataFeeToAdd();
|
|
375
|
+
return new CoinPretty(
|
|
376
|
+
feeCurrency,
|
|
377
|
+
price.mul(gasDec).add(l1DataFeeToAdd).roundUp()
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
@computed
|
|
382
|
+
get maxFee(): CoinPretty | undefined {
|
|
383
|
+
const price = this.gasPrice;
|
|
384
|
+
if (!price || price.equals(new Dec(0))) {
|
|
385
|
+
return undefined;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const feeCurrency = this.getFeeCurrency();
|
|
389
|
+
const gasDec = new Dec(this.gasConfig.gas);
|
|
390
|
+
const l1DataFeeToAdd = this.getL1DataFeeToAdd();
|
|
391
|
+
// For maxFee, use a higher multiplier to be safe
|
|
392
|
+
return new CoinPretty(
|
|
393
|
+
feeCurrency,
|
|
394
|
+
price.mul(new Dec(1.2)).mul(gasDec).add(l1DataFeeToAdd).roundUp()
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// --- hooks IFeeConfig 구조 호환용 ---
|
|
399
|
+
|
|
400
|
+
@computed
|
|
401
|
+
get fees(): CoinPretty[] {
|
|
402
|
+
return this.fee ? [this.fee] : [];
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
@computed
|
|
406
|
+
get selectableFeeCurrencies(): FeeCurrency[] {
|
|
407
|
+
return [this.getFeeCurrency()];
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
@action
|
|
411
|
+
setFee(
|
|
412
|
+
fee:
|
|
413
|
+
| { type: EVMFeeType; currency: FeeCurrency }
|
|
414
|
+
| CoinPretty
|
|
415
|
+
| CoinPretty[]
|
|
416
|
+
| undefined
|
|
417
|
+
): void {
|
|
418
|
+
if (!fee) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
if ("type" in fee && "currency" in fee) {
|
|
422
|
+
this.setType(fee.type);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
readonly getFeeTypePrettyForFeeCurrency = computedFn(
|
|
427
|
+
(_currency: FeeCurrency, feeType: EVMFeeType): CoinPretty => {
|
|
428
|
+
const fees = this.getEIP1559TxFees(feeType);
|
|
429
|
+
const price = fees.gasPrice ?? fees.maxFeePerGas ?? new Dec(0);
|
|
430
|
+
const feeCurrency = this.getFeeCurrency();
|
|
431
|
+
const gasDec = new Dec(this.gasConfig.gas);
|
|
432
|
+
const l1DataFeeToAdd = this.getL1DataFeeToAdd();
|
|
433
|
+
return new CoinPretty(
|
|
434
|
+
feeCurrency,
|
|
435
|
+
price.mul(gasDec).add(l1DataFeeToAdd).roundUp()
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
toStdFee(): StdFee {
|
|
441
|
+
const fee = this.fee;
|
|
442
|
+
if (!fee) {
|
|
443
|
+
return { gas: "0", amount: [] };
|
|
444
|
+
}
|
|
445
|
+
return {
|
|
446
|
+
gas: this.gasConfig.gas.toString(),
|
|
447
|
+
amount: [fee.toCoin()],
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// --- end hooks IFeeConfig 구조 호환용 ---
|
|
452
|
+
|
|
453
|
+
@computed
|
|
454
|
+
get uiProperties(): UIProperties {
|
|
455
|
+
if (this._disableBalanceCheck) {
|
|
456
|
+
return {};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (!this.canEIP1559TxFeesAndReady()) {
|
|
460
|
+
return {
|
|
461
|
+
loadingState: "loading-block",
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (this._type === "custom") {
|
|
466
|
+
const rawValue = this.isLegacyFeeMode
|
|
467
|
+
? this._customGasPrice
|
|
468
|
+
: this._customPriorityFee;
|
|
469
|
+
if (rawValue.trim() !== "") {
|
|
470
|
+
try {
|
|
471
|
+
const dec = new Dec(rawValue);
|
|
472
|
+
if (dec.lt(new Dec(0))) {
|
|
473
|
+
return {
|
|
474
|
+
error: new Error("Enter a positive number"),
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
} catch {
|
|
478
|
+
return {
|
|
479
|
+
error: new Error("Enter a valid number"),
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
let priorWarning: Error | undefined = undefined;
|
|
486
|
+
let priorIsLoadingState = false;
|
|
487
|
+
const makeReturn = (uiProperties: UIProperties): UIProperties => {
|
|
488
|
+
return {
|
|
489
|
+
...uiProperties,
|
|
490
|
+
...(() => {
|
|
491
|
+
if (priorIsLoadingState) {
|
|
492
|
+
if (uiProperties.loadingState === "loading-block") {
|
|
493
|
+
return {
|
|
494
|
+
loadingState: "loading-block" as const,
|
|
495
|
+
};
|
|
496
|
+
} else {
|
|
497
|
+
return {
|
|
498
|
+
loadingState: "loading" as const,
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return {};
|
|
503
|
+
})(),
|
|
504
|
+
...(() => {
|
|
505
|
+
if (priorWarning) {
|
|
506
|
+
if (uiProperties.error) {
|
|
507
|
+
return {};
|
|
508
|
+
} else {
|
|
509
|
+
return {
|
|
510
|
+
warning: priorWarning,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
return {};
|
|
515
|
+
})(),
|
|
516
|
+
};
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
const ethereumQueries = this.getEthereumQueries();
|
|
520
|
+
if (ethereumQueries) {
|
|
521
|
+
const eipState = getEIP1559QueryUIState(ethereumQueries, this.chainId);
|
|
522
|
+
if (eipState.warning) {
|
|
523
|
+
priorWarning = eipState.warning;
|
|
524
|
+
}
|
|
525
|
+
if (eipState.isLoading) {
|
|
526
|
+
priorIsLoadingState = true;
|
|
527
|
+
}
|
|
528
|
+
if (eipState.isBlocked) {
|
|
529
|
+
return makeReturn({
|
|
530
|
+
loadingState: "loading-block",
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const fee = this.fee;
|
|
536
|
+
if (!fee) {
|
|
537
|
+
return makeReturn({
|
|
538
|
+
error: new Error("Fee is not set"),
|
|
539
|
+
loadingState: "loading-block",
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const feeCurrency = this.getFeeCurrency();
|
|
544
|
+
let needAmount = new Int(fee.toCoin().amount);
|
|
545
|
+
|
|
546
|
+
if (this.additionAmountToNeedFee) {
|
|
547
|
+
for (const amt of this.amountConfig.amount) {
|
|
548
|
+
if (amt.currency.coinMinimalDenom === feeCurrency.coinMinimalDenom) {
|
|
549
|
+
needAmount = needAmount.add(new Int(amt.toCoin().amount));
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const bal = this.queriesStore
|
|
555
|
+
.get(this.chainId)
|
|
556
|
+
.queryBalances.getQueryEthereumHexAddress(this.senderConfig.sender)
|
|
557
|
+
.getBalance(feeCurrency);
|
|
558
|
+
|
|
559
|
+
if (!bal) {
|
|
560
|
+
priorWarning = new Error(
|
|
561
|
+
`Can't parse the balance for ${feeCurrency.coinMinimalDenom}`
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (bal) {
|
|
566
|
+
bal.waitResponse();
|
|
567
|
+
|
|
568
|
+
if (bal.error) {
|
|
569
|
+
priorWarning = new Error("Failed to fetch balance");
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (!bal.response) {
|
|
573
|
+
return makeReturn({
|
|
574
|
+
loadingState: "loading-block",
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if (new Int(bal.balance.toCoin().amount).lt(needAmount)) {
|
|
579
|
+
if (bal.isFetching) {
|
|
580
|
+
priorIsLoadingState = true;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
return makeReturn({
|
|
584
|
+
error: new InsufficientFeeError("Insufficient fee"),
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
return makeReturn({});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
import { AppCurrency, FeeCurrency, StdFee } from "@keplr-wallet/types";
|
|
594
|
+
|
|
595
|
+
export const useFeeConfig = (
|
|
596
|
+
chainGetter: ChainGetter,
|
|
597
|
+
queriesStore: QueriesStore,
|
|
598
|
+
chainId: string,
|
|
599
|
+
senderConfig: ISenderConfig,
|
|
600
|
+
amountConfig: IAmountConfig,
|
|
601
|
+
gasConfig: IGasConfig,
|
|
602
|
+
opts: {
|
|
603
|
+
additionAmountToNeedFee?: boolean;
|
|
604
|
+
} = {}
|
|
605
|
+
) => {
|
|
606
|
+
const [config] = useState(
|
|
607
|
+
() =>
|
|
608
|
+
new FeeConfig(
|
|
609
|
+
chainGetter,
|
|
610
|
+
queriesStore,
|
|
611
|
+
chainId,
|
|
612
|
+
senderConfig,
|
|
613
|
+
amountConfig,
|
|
614
|
+
gasConfig,
|
|
615
|
+
opts.additionAmountToNeedFee ?? true
|
|
616
|
+
)
|
|
617
|
+
);
|
|
618
|
+
config.setChain(chainId);
|
|
619
|
+
config.setAdditionAmountToNeedFee(opts.additionAmountToNeedFee ?? true);
|
|
620
|
+
|
|
621
|
+
return config;
|
|
622
|
+
};
|