@keplr-wallet/background 0.12.313 → 0.13.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.
Files changed (83) hide show
  1. package/build/index.d.ts +1 -0
  2. package/build/index.js +7 -1
  3. package/build/index.js.map +1 -1
  4. package/build/keyring-cosmos/service.d.ts +10 -0
  5. package/build/keyring-cosmos/service.js +100 -0
  6. package/build/keyring-cosmos/service.js.map +1 -1
  7. package/build/keyring-ethereum/service.d.ts +5 -0
  8. package/build/keyring-ethereum/service.js +66 -0
  9. package/build/keyring-ethereum/service.js.map +1 -1
  10. package/build/recent-send-history/api.d.ts +31 -0
  11. package/build/recent-send-history/api.js +97 -0
  12. package/build/recent-send-history/api.js.map +1 -0
  13. package/build/recent-send-history/handler.js +36 -0
  14. package/build/recent-send-history/handler.js.map +1 -1
  15. package/build/recent-send-history/init.js +5 -0
  16. package/build/recent-send-history/init.js.map +1 -1
  17. package/build/recent-send-history/messages.d.ts +76 -1
  18. package/build/recent-send-history/messages.js +121 -1
  19. package/build/recent-send-history/messages.js.map +1 -1
  20. package/build/recent-send-history/service.d.ts +262 -9
  21. package/build/recent-send-history/service.js +2103 -812
  22. package/build/recent-send-history/service.js.map +1 -1
  23. package/build/recent-send-history/types.d.ts +214 -22
  24. package/build/recent-send-history/types.js +21 -0
  25. package/build/recent-send-history/types.js.map +1 -1
  26. package/build/tx/service.d.ts +2 -0
  27. package/build/tx/service.js +35 -0
  28. package/build/tx/service.js.map +1 -1
  29. package/build/tx-ethereum/service.d.ts +2 -0
  30. package/build/tx-ethereum/service.js +42 -0
  31. package/build/tx-ethereum/service.js.map +1 -1
  32. package/build/tx-executor/constants.d.ts +1 -0
  33. package/build/tx-executor/constants.js +5 -0
  34. package/build/tx-executor/constants.js.map +1 -0
  35. package/build/tx-executor/handler.d.ts +3 -0
  36. package/build/tx-executor/handler.js +45 -0
  37. package/build/tx-executor/handler.js.map +1 -0
  38. package/build/tx-executor/index.d.ts +3 -0
  39. package/build/tx-executor/index.js +20 -0
  40. package/build/tx-executor/index.js.map +1 -0
  41. package/build/tx-executor/init.d.ts +3 -0
  42. package/build/tx-executor/init.js +14 -0
  43. package/build/tx-executor/init.js.map +1 -0
  44. package/build/tx-executor/internal.d.ts +4 -0
  45. package/build/tx-executor/internal.js +24 -0
  46. package/build/tx-executor/internal.js.map +1 -0
  47. package/build/tx-executor/messages.d.ts +53 -0
  48. package/build/tx-executor/messages.js +116 -0
  49. package/build/tx-executor/messages.js.map +1 -0
  50. package/build/tx-executor/service.d.ts +67 -0
  51. package/build/tx-executor/service.js +715 -0
  52. package/build/tx-executor/service.js.map +1 -0
  53. package/build/tx-executor/types.d.ts +105 -0
  54. package/build/tx-executor/types.js +33 -0
  55. package/build/tx-executor/types.js.map +1 -0
  56. package/build/tx-executor/utils/cosmos.d.ts +59 -0
  57. package/build/tx-executor/utils/cosmos.js +526 -0
  58. package/build/tx-executor/utils/cosmos.js.map +1 -0
  59. package/build/tx-executor/utils/evm.d.ts +4 -0
  60. package/build/tx-executor/utils/evm.js +236 -0
  61. package/build/tx-executor/utils/evm.js.map +1 -0
  62. package/package.json +13 -13
  63. package/src/index.ts +24 -1
  64. package/src/keyring-cosmos/service.ts +151 -0
  65. package/src/keyring-ethereum/service.ts +103 -6
  66. package/src/recent-send-history/api.ts +119 -0
  67. package/src/recent-send-history/handler.ts +84 -0
  68. package/src/recent-send-history/init.ts +10 -0
  69. package/src/recent-send-history/messages.ts +163 -1
  70. package/src/recent-send-history/service.ts +3042 -1153
  71. package/src/recent-send-history/types.ts +268 -31
  72. package/src/tx/service.ts +41 -0
  73. package/src/tx-ethereum/service.ts +57 -0
  74. package/src/tx-executor/constants.ts +1 -0
  75. package/src/tx-executor/handler.ts +71 -0
  76. package/src/tx-executor/index.ts +3 -0
  77. package/src/tx-executor/init.ts +20 -0
  78. package/src/tx-executor/internal.ts +9 -0
  79. package/src/tx-executor/messages.ts +157 -0
  80. package/src/tx-executor/service.ts +1025 -0
  81. package/src/tx-executor/types.ts +161 -0
  82. package/src/tx-executor/utils/cosmos.ts +771 -0
  83. package/src/tx-executor/utils/evm.ts +310 -0
@@ -0,0 +1,310 @@
1
+ import { EVMInfo } from "@keplr-wallet/types";
2
+ import { simpleFetch } from "@keplr-wallet/simple-fetch";
3
+ import { UnsignedTransaction } from "@ethersproject/transactions";
4
+ import { Dec } from "@keplr-wallet/unit";
5
+ import { BackgroundTxFeeType } from "../types";
6
+ import { JsonRpcResponse } from "@keplr-wallet/types";
7
+
8
+ const ETH_FEE_HISTORY_REWARD_PERCENTILES = [25, 50, 75];
9
+ const ETH_FEE_SETTINGS_BY_FEE_TYPE: Record<
10
+ BackgroundTxFeeType,
11
+ {
12
+ percentile: number;
13
+ }
14
+ > = {
15
+ low: {
16
+ percentile: ETH_FEE_HISTORY_REWARD_PERCENTILES[0],
17
+ },
18
+ average: {
19
+ percentile: ETH_FEE_HISTORY_REWARD_PERCENTILES[1],
20
+ },
21
+ high: {
22
+ percentile: ETH_FEE_HISTORY_REWARD_PERCENTILES[2],
23
+ },
24
+ };
25
+
26
+ const FEE_MULTIPLIERS: Record<BackgroundTxFeeType, number> = {
27
+ low: 1.1,
28
+ average: 1.25,
29
+ high: 1.5,
30
+ };
31
+ const GAS_ADJUSTMENT_NUM = BigInt(13);
32
+ const GAS_ADJUSTMENT_DEN = BigInt(10);
33
+
34
+ const TX_COUNT_ID = 1;
35
+ const LATEST_BLOCK_ID = 2;
36
+ const FEE_HISTORY_ID = 3;
37
+ const ESTIMATE_GAS_ID = 4;
38
+ const MAX_PRIORITY_FEE_ID = 5;
39
+
40
+ type BigNumberishLike = string | number | bigint | { toString(): string };
41
+
42
+ const toBigIntFromTxField = (value: BigNumberishLike): bigint => {
43
+ if (
44
+ typeof value === "string" ||
45
+ typeof value === "number" ||
46
+ typeof value === "bigint"
47
+ ) {
48
+ return BigInt(value);
49
+ }
50
+
51
+ if (value && typeof value === "object" && "toString" in value) {
52
+ return BigInt((value as { toString(): string }).toString());
53
+ }
54
+
55
+ throw new Error("Unsupported numeric value in unsigned transaction");
56
+ };
57
+
58
+ export async function fillUnsignedEVMTx(
59
+ origin: string,
60
+ evmInfo: EVMInfo,
61
+ signer: string,
62
+ tx: UnsignedTransaction,
63
+ feeType: BackgroundTxFeeType = "average"
64
+ ): Promise<UnsignedTransaction> {
65
+ const hasProvidedPriorityFee = tx.maxPriorityFeePerGas != null;
66
+ const hasProvidedGasLimit = tx.gasLimit != null;
67
+
68
+ const getTransactionCountRequest = {
69
+ jsonrpc: "2.0",
70
+ method: "eth_getTransactionCount",
71
+ params: [signer, "pending"],
72
+ id: TX_COUNT_ID,
73
+ };
74
+
75
+ const getBlockRequest = {
76
+ jsonrpc: "2.0",
77
+ method: "eth_getBlockByNumber",
78
+ params: ["latest", false],
79
+ id: LATEST_BLOCK_ID,
80
+ };
81
+
82
+ const getFeeHistoryRequest = hasProvidedPriorityFee
83
+ ? null
84
+ : {
85
+ jsonrpc: "2.0",
86
+ method: "eth_feeHistory",
87
+ params: [20, "latest", ETH_FEE_HISTORY_REWARD_PERCENTILES],
88
+ id: FEE_HISTORY_ID,
89
+ };
90
+
91
+ const estimateGasRequest = hasProvidedGasLimit
92
+ ? null
93
+ : {
94
+ jsonrpc: "2.0",
95
+ method: "eth_estimateGas",
96
+ params: [
97
+ {
98
+ from: signer,
99
+ to: tx.to,
100
+ value: tx.value,
101
+ data: tx.data,
102
+ },
103
+ ],
104
+ id: ESTIMATE_GAS_ID,
105
+ };
106
+
107
+ const getMaxPriorityFeePerGasRequest = hasProvidedPriorityFee
108
+ ? null
109
+ : {
110
+ jsonrpc: "2.0",
111
+ method: "eth_maxPriorityFeePerGas",
112
+ params: [],
113
+ id: MAX_PRIORITY_FEE_ID,
114
+ };
115
+
116
+ // rpc request in batch (as 2.0 jsonrpc supports batch requests)
117
+ const batchRequest = [
118
+ getTransactionCountRequest,
119
+ getBlockRequest,
120
+ ...(getFeeHistoryRequest ? [getFeeHistoryRequest] : []),
121
+ ...(estimateGasRequest ? [estimateGasRequest] : []),
122
+ ...(getMaxPriorityFeePerGasRequest ? [getMaxPriorityFeePerGasRequest] : []),
123
+ ];
124
+
125
+ const { data: rpcResponses } = await simpleFetch<
126
+ Array<JsonRpcResponse<unknown>>
127
+ >(evmInfo.rpc, {
128
+ method: "POST",
129
+ headers: {
130
+ "content-type": "application/json",
131
+ "request-source": origin,
132
+ },
133
+ body: JSON.stringify(batchRequest),
134
+ });
135
+
136
+ if (
137
+ !Array.isArray(rpcResponses) ||
138
+ rpcResponses.length !== batchRequest.length
139
+ ) {
140
+ throw new Error("Invalid batch response format");
141
+ }
142
+
143
+ const getResult = <T = any>(id: number, optional = false): T | undefined => {
144
+ const res = rpcResponses.find((r) => r.id === id);
145
+ if (!res) {
146
+ if (optional) {
147
+ return undefined;
148
+ }
149
+ throw new Error(`No response for id=${id}`);
150
+ }
151
+ if (res.error) {
152
+ throw new Error(
153
+ `RPC error (id=${id}): ${res.error.code} ${res.error.message}`
154
+ );
155
+ }
156
+ return res.result as T;
157
+ };
158
+
159
+ // find responses by id
160
+ const nonceHex = getResult<string>(TX_COUNT_ID);
161
+ if (!nonceHex) {
162
+ throw new Error("Failed to get nonce to fill unsigned transaction");
163
+ }
164
+
165
+ const latestBlock = getResult<{ baseFeePerGas?: string }>(LATEST_BLOCK_ID);
166
+ if (!latestBlock) {
167
+ throw new Error("Failed to get latest block to fill unsigned transaction");
168
+ }
169
+ const feeHistory = hasProvidedPriorityFee
170
+ ? undefined
171
+ : getResult<{
172
+ baseFeePerGas?: string[];
173
+ gasUsedRatio: number[];
174
+ oldestBlock: string;
175
+ reward?: string[][];
176
+ }>(FEE_HISTORY_ID);
177
+ const gasLimitHex = hasProvidedGasLimit
178
+ ? undefined
179
+ : getResult<string>(ESTIMATE_GAS_ID, true);
180
+ const networkMaxPriorityFeePerGasHex = hasProvidedPriorityFee
181
+ ? undefined
182
+ : getResult<string>(MAX_PRIORITY_FEE_ID, true);
183
+
184
+ let maxPriorityFeePerGasDec: Dec | undefined;
185
+
186
+ if (hasProvidedPriorityFee) {
187
+ if (tx.maxPriorityFeePerGas == null) {
188
+ throw new Error("maxPriorityFeePerGas is required but missing");
189
+ }
190
+ maxPriorityFeePerGasDec = new Dec(
191
+ toBigIntFromTxField(tx.maxPriorityFeePerGas)
192
+ );
193
+ } else if (feeHistory?.reward && feeHistory.reward.length > 0) {
194
+ const percentile =
195
+ ETH_FEE_SETTINGS_BY_FEE_TYPE[feeType].percentile ??
196
+ ETH_FEE_HISTORY_REWARD_PERCENTILES[1];
197
+ const percentileIndex =
198
+ ETH_FEE_HISTORY_REWARD_PERCENTILES.indexOf(percentile);
199
+
200
+ if (percentileIndex >= 0) {
201
+ const rewards = feeHistory.reward
202
+ .map((block) => block[percentileIndex])
203
+ .filter((v) => v != null)
204
+ .map((v) => BigInt(v));
205
+
206
+ if (rewards.length > 0) {
207
+ const sum = rewards.reduce((acc, x) => acc + x, BigInt(0));
208
+ const mean = sum / BigInt(rewards.length);
209
+
210
+ const sortedRewards = [...rewards].sort((a, b) =>
211
+ a < b ? -1 : a > b ? 1 : 0
212
+ );
213
+ const median = sortedRewards[Math.floor(sortedRewards.length / 2)];
214
+
215
+ // use 1 Gwei deviation threshold to decide between mean and median
216
+ const deviationThreshold = BigInt(1 * 10 ** 9); // 1 Gwei
217
+ const deviation = mean > median ? mean - median : median - mean;
218
+ const pick =
219
+ deviation > deviationThreshold
220
+ ? mean > median
221
+ ? mean
222
+ : median
223
+ : mean;
224
+
225
+ maxPriorityFeePerGasDec = new Dec(pick);
226
+ }
227
+ }
228
+ }
229
+
230
+ if (networkMaxPriorityFeePerGasHex) {
231
+ const networkMaxPriorityFeePerGasDec = new Dec(
232
+ BigInt(networkMaxPriorityFeePerGasHex)
233
+ );
234
+
235
+ if (
236
+ !maxPriorityFeePerGasDec ||
237
+ (maxPriorityFeePerGasDec &&
238
+ networkMaxPriorityFeePerGasDec.gt(maxPriorityFeePerGasDec))
239
+ ) {
240
+ maxPriorityFeePerGasDec = networkMaxPriorityFeePerGasDec;
241
+ }
242
+ }
243
+
244
+ if (!maxPriorityFeePerGasDec) {
245
+ throw new Error(
246
+ "Failed to calculate maxPriorityFeePerGas to fill unsigned transaction"
247
+ );
248
+ }
249
+
250
+ if (!latestBlock.baseFeePerGas) {
251
+ throw new Error("Failed to get baseFeePerGas to fill unsigned transaction");
252
+ }
253
+
254
+ const multiplier = new Dec(FEE_MULTIPLIERS[feeType]);
255
+
256
+ // Calculate maxFeePerGas = baseFeePerGas + maxPriorityFeePerGas
257
+ const baseFeePerGasDec = new Dec(BigInt(latestBlock.baseFeePerGas));
258
+ const suggestedFeeFromBase = baseFeePerGasDec.mul(multiplier);
259
+
260
+ const providedMaxFeePerGasDec = tx.maxFeePerGas
261
+ ? new Dec(toBigIntFromTxField(tx.maxFeePerGas))
262
+ : undefined;
263
+
264
+ const maxFeePerGasDec =
265
+ providedMaxFeePerGasDec && hasProvidedPriorityFee
266
+ ? providedMaxFeePerGasDec.gte(
267
+ suggestedFeeFromBase.add(maxPriorityFeePerGasDec)
268
+ )
269
+ ? providedMaxFeePerGasDec
270
+ : suggestedFeeFromBase.add(maxPriorityFeePerGasDec)
271
+ : suggestedFeeFromBase.add(maxPriorityFeePerGasDec);
272
+
273
+ const maxFeePerGasHex = `0x${maxFeePerGasDec
274
+ .truncate()
275
+ .toBigNumber()
276
+ .toString(16)}`;
277
+
278
+ const maxPriorityFeePerGasHex = `0x${maxPriorityFeePerGasDec
279
+ .truncate()
280
+ .toBigNumber()
281
+ .toString(16)}`;
282
+
283
+ const finalNonce =
284
+ tx.nonce != null
285
+ ? Math.max(Number(tx.nonce), parseInt(nonceHex, 16))
286
+ : parseInt(nonceHex, 16);
287
+
288
+ let finalGasLimit: UnsignedTransaction["gasLimit"];
289
+ if (tx.gasLimit != null) {
290
+ finalGasLimit = tx.gasLimit;
291
+ } else if (gasLimitHex) {
292
+ const estimatedGas = toBigIntFromTxField(gasLimitHex);
293
+ const adjustedGas =
294
+ (estimatedGas * GAS_ADJUSTMENT_NUM + (GAS_ADJUSTMENT_DEN - BigInt(1))) /
295
+ GAS_ADJUSTMENT_DEN;
296
+ finalGasLimit = `0x${adjustedGas.toString(16)}`;
297
+ } else {
298
+ throw new Error("Failed to estimate gas to fill unsigned transaction");
299
+ }
300
+
301
+ const newUnsignedTx: UnsignedTransaction = {
302
+ ...tx,
303
+ nonce: finalNonce,
304
+ maxFeePerGas: maxFeePerGasHex,
305
+ maxPriorityFeePerGas: maxPriorityFeePerGasHex,
306
+ gasLimit: finalGasLimit,
307
+ };
308
+
309
+ return newUnsignedTx;
310
+ }