@injectivelabs/wallet-core 0.0.2

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 (76) hide show
  1. package/README.md +77 -0
  2. package/dist/cjs/broadcaster/MsgBroadcaster.d.ts +125 -0
  3. package/dist/cjs/broadcaster/MsgBroadcaster.d.ts.map +1 -0
  4. package/dist/cjs/broadcaster/MsgBroadcaster.js +703 -0
  5. package/dist/cjs/broadcaster/MsgBroadcaster.js.map +1 -0
  6. package/dist/cjs/broadcaster/Web3Broadcaster.d.ts +30 -0
  7. package/dist/cjs/broadcaster/Web3Broadcaster.d.ts.map +1 -0
  8. package/dist/cjs/broadcaster/Web3Broadcaster.js +41 -0
  9. package/dist/cjs/broadcaster/Web3Broadcaster.js.map +1 -0
  10. package/dist/cjs/broadcaster/index.d.ts +4 -0
  11. package/dist/cjs/broadcaster/index.d.ts.map +1 -0
  12. package/dist/cjs/broadcaster/index.js +20 -0
  13. package/dist/cjs/broadcaster/index.js.map +1 -0
  14. package/dist/cjs/broadcaster/types.d.ts +33 -0
  15. package/dist/cjs/broadcaster/types.d.ts.map +1 -0
  16. package/dist/cjs/broadcaster/types.js +3 -0
  17. package/dist/cjs/broadcaster/types.js.map +1 -0
  18. package/dist/cjs/index.d.ts +4 -0
  19. package/dist/cjs/index.d.ts.map +1 -0
  20. package/dist/cjs/index.js +20 -0
  21. package/dist/cjs/index.js.map +1 -0
  22. package/dist/cjs/package.json +3 -0
  23. package/dist/cjs/strategy/BaseWalletStrategy.d.ts +45 -0
  24. package/dist/cjs/strategy/BaseWalletStrategy.d.ts.map +1 -0
  25. package/dist/cjs/strategy/BaseWalletStrategy.js +154 -0
  26. package/dist/cjs/strategy/BaseWalletStrategy.js.map +1 -0
  27. package/dist/cjs/strategy/index.d.ts +3 -0
  28. package/dist/cjs/strategy/index.d.ts.map +1 -0
  29. package/dist/cjs/strategy/index.js +9 -0
  30. package/dist/cjs/strategy/index.js.map +1 -0
  31. package/dist/cjs/utils/index.d.ts +2 -0
  32. package/dist/cjs/utils/index.d.ts.map +1 -0
  33. package/dist/cjs/utils/index.js +18 -0
  34. package/dist/cjs/utils/index.js.map +1 -0
  35. package/dist/cjs/utils/tx.d.ts +2 -0
  36. package/dist/cjs/utils/tx.d.ts.map +1 -0
  37. package/dist/cjs/utils/tx.js +12 -0
  38. package/dist/cjs/utils/tx.js.map +1 -0
  39. package/dist/esm/broadcaster/MsgBroadcaster.d.ts +125 -0
  40. package/dist/esm/broadcaster/MsgBroadcaster.d.ts.map +1 -0
  41. package/dist/esm/broadcaster/MsgBroadcaster.js +704 -0
  42. package/dist/esm/broadcaster/MsgBroadcaster.js.map +1 -0
  43. package/dist/esm/broadcaster/Web3Broadcaster.d.ts +30 -0
  44. package/dist/esm/broadcaster/Web3Broadcaster.d.ts.map +1 -0
  45. package/dist/esm/broadcaster/Web3Broadcaster.js +28 -0
  46. package/dist/esm/broadcaster/Web3Broadcaster.js.map +1 -0
  47. package/dist/esm/broadcaster/index.d.ts +4 -0
  48. package/dist/esm/broadcaster/index.d.ts.map +1 -0
  49. package/dist/esm/broadcaster/index.js +4 -0
  50. package/dist/esm/broadcaster/index.js.map +1 -0
  51. package/dist/esm/broadcaster/types.d.ts +33 -0
  52. package/dist/esm/broadcaster/types.d.ts.map +1 -0
  53. package/dist/esm/broadcaster/types.js +2 -0
  54. package/dist/esm/broadcaster/types.js.map +1 -0
  55. package/dist/esm/index.d.ts +4 -0
  56. package/dist/esm/index.d.ts.map +1 -0
  57. package/dist/esm/index.js +4 -0
  58. package/dist/esm/index.js.map +1 -0
  59. package/dist/esm/package.json +3 -0
  60. package/dist/esm/strategy/BaseWalletStrategy.d.ts +45 -0
  61. package/dist/esm/strategy/BaseWalletStrategy.d.ts.map +1 -0
  62. package/dist/esm/strategy/BaseWalletStrategy.js +121 -0
  63. package/dist/esm/strategy/BaseWalletStrategy.js.map +1 -0
  64. package/dist/esm/strategy/index.d.ts +3 -0
  65. package/dist/esm/strategy/index.d.ts.map +1 -0
  66. package/dist/esm/strategy/index.js +3 -0
  67. package/dist/esm/strategy/index.js.map +1 -0
  68. package/dist/esm/utils/index.d.ts +2 -0
  69. package/dist/esm/utils/index.d.ts.map +1 -0
  70. package/dist/esm/utils/index.js +2 -0
  71. package/dist/esm/utils/index.js.map +1 -0
  72. package/dist/esm/utils/tx.d.ts +2 -0
  73. package/dist/esm/utils/tx.d.ts.map +1 -0
  74. package/dist/esm/utils/tx.js +8 -0
  75. package/dist/esm/utils/tx.js.map +1 -0
  76. package/package.json +57 -0
@@ -0,0 +1,704 @@
1
+ import { TxGrpcApi, hexToBuff, PublicKey, SIGN_DIRECT, hexToBase64, ofacWallets, SIGN_EIP712_V2, SIGN_EIP712, ChainGrpcAuthApi, createTxRawEIP712, createTransaction, getAminoStdSignDoc, getEip712TypedData, createWeb3Extension, getEip712TypedDataV2, ChainGrpcTendermintApi, createTransactionWithSigners, createTxRawFromSigResponse, IndexerGrpcWeb3GwApi, getGasPriceBasedOnMessage, recoverTypedSignaturePubKey, } from '@injectivelabs/sdk-ts';
2
+ import { getStdFee, BigNumberInBase, DEFAULT_BLOCK_TIMEOUT_HEIGHT, } from '@injectivelabs/utils';
3
+ import { GeneralException, TransactionException, UnspecifiedErrorCode, } from '@injectivelabs/exceptions';
4
+ import { getNetworkInfo, getNetworkEndpoints, } from '@injectivelabs/networks';
5
+ import { checkIfTxRunOutOfGas } from './../utils/tx';
6
+ import { Wallet, isCosmosWallet, WalletDeviceType, isEip712V2OnlyWallet, createEip712StdSignDoc, isCosmosAminoOnlyWallet, getEthereumSignerAddress, getInjectiveSignerAddress, } from '@injectivelabs/wallet-base';
7
+ const getEthereumWalletPubKey = ({ pubKey, eip712TypedData, signature, }) => {
8
+ if (pubKey) {
9
+ return pubKey;
10
+ }
11
+ return hexToBase64(recoverTypedSignaturePubKey(eip712TypedData, signature));
12
+ };
13
+ /**
14
+ * This class is used to broadcast transactions
15
+ * using the WalletStrategy as a handler
16
+ * for the sign/broadcast flow of the transactions
17
+ *
18
+ * Mainly used for building UI products
19
+ */
20
+ export class MsgBroadcaster {
21
+ options;
22
+ walletStrategy;
23
+ endpoints;
24
+ chainId;
25
+ txTimeout = DEFAULT_BLOCK_TIMEOUT_HEIGHT;
26
+ simulateTx = true;
27
+ txTimeoutOnFeeDelegation = false;
28
+ ethereumChainId;
29
+ gasBufferCoefficient = 1.2;
30
+ constructor(options) {
31
+ const networkInfo = getNetworkInfo(options.network);
32
+ this.options = options;
33
+ this.simulateTx =
34
+ options.simulateTx !== undefined ? options.simulateTx : true;
35
+ this.txTimeout = options.txTimeout || DEFAULT_BLOCK_TIMEOUT_HEIGHT;
36
+ this.txTimeoutOnFeeDelegation =
37
+ options.txTimeoutOnFeeDelegation !== undefined
38
+ ? options.txTimeoutOnFeeDelegation
39
+ : true;
40
+ this.gasBufferCoefficient = options.gasBufferCoefficient || 1.2;
41
+ this.chainId = options.chainId || networkInfo.chainId;
42
+ this.ethereumChainId =
43
+ options.ethereumChainId || networkInfo.ethereumChainId;
44
+ this.endpoints = options.endpoints || getNetworkEndpoints(options.network);
45
+ this.walletStrategy = options.walletStrategy;
46
+ }
47
+ setOptions(options) {
48
+ this.simulateTx = options.simulateTx || this.simulateTx;
49
+ this.txTimeout = options.txTimeout || this.txTimeout;
50
+ this.txTimeoutOnFeeDelegation =
51
+ options.txTimeoutOnFeeDelegation || this.txTimeoutOnFeeDelegation;
52
+ }
53
+ /**
54
+ * Broadcasting the transaction using the client
55
+ * side approach for both cosmos and ethereum native wallets
56
+ *
57
+ * @param tx
58
+ * @returns {string} transaction hash
59
+ */
60
+ async broadcast(tx) {
61
+ const { walletStrategy } = this;
62
+ const txWithAddresses = {
63
+ ...tx,
64
+ ethereumAddress: getEthereumSignerAddress(tx.injectiveAddress),
65
+ injectiveAddress: getInjectiveSignerAddress(tx.injectiveAddress),
66
+ };
67
+ if (ofacWallets.includes(txWithAddresses.ethereumAddress)) {
68
+ throw new GeneralException(new Error('You cannot execute this transaction'));
69
+ }
70
+ return isCosmosWallet(walletStrategy.wallet)
71
+ ? this.broadcastCosmos(txWithAddresses)
72
+ : isEip712V2OnlyWallet(walletStrategy.wallet)
73
+ ? this.broadcastWeb3V2(txWithAddresses)
74
+ : this.broadcastWeb3(txWithAddresses);
75
+ }
76
+ /**
77
+ * Broadcasting the transaction using the client
78
+ * side approach for both cosmos and ethereum native wallets
79
+ * Note: using EIP712_V2 for Ethereum wallets
80
+ *
81
+ * @param tx
82
+ * @returns {string} transaction hash
83
+ */
84
+ async broadcastV2(tx) {
85
+ const { walletStrategy } = this;
86
+ const txWithAddresses = {
87
+ ...tx,
88
+ ethereumAddress: getEthereumSignerAddress(tx.injectiveAddress),
89
+ injectiveAddress: getInjectiveSignerAddress(tx.injectiveAddress),
90
+ };
91
+ if (ofacWallets.includes(txWithAddresses.ethereumAddress)) {
92
+ throw new GeneralException(new Error('You cannot execute this transaction'));
93
+ }
94
+ return isCosmosWallet(walletStrategy.wallet)
95
+ ? this.broadcastCosmos(txWithAddresses)
96
+ : this.broadcastWeb3V2(txWithAddresses);
97
+ }
98
+ /**
99
+ * Broadcasting the transaction using the feeDelegation
100
+ * support approach for both cosmos and ethereum native wallets
101
+ *
102
+ * @param tx
103
+ * @returns {string} transaction hash
104
+ */
105
+ async broadcastWithFeeDelegation(tx) {
106
+ const { walletStrategy } = this;
107
+ const txWithAddresses = {
108
+ ...tx,
109
+ ethereumAddress: getEthereumSignerAddress(tx.injectiveAddress),
110
+ injectiveAddress: getInjectiveSignerAddress(tx.injectiveAddress),
111
+ };
112
+ if (ofacWallets.includes(txWithAddresses.ethereumAddress)) {
113
+ throw new GeneralException(new Error('You cannot execute this transaction'));
114
+ }
115
+ return isCosmosWallet(walletStrategy.wallet)
116
+ ? this.broadcastCosmosWithFeeDelegation(txWithAddresses)
117
+ : this.broadcastWeb3WithFeeDelegation(txWithAddresses);
118
+ }
119
+ /**
120
+ * Prepare/sign/broadcast transaction using
121
+ * Ethereum native wallets on the client side.
122
+ *
123
+ * Note: Gas estimation not available
124
+ *
125
+ * @param tx The transaction that needs to be broadcasted
126
+ * @returns transaction hash
127
+ */
128
+ async broadcastWeb3(tx) {
129
+ const { chainId, txTimeout, endpoints, ethereumChainId, walletStrategy } = this;
130
+ const msgs = Array.isArray(tx.msgs) ? tx.msgs : [tx.msgs];
131
+ if (!ethereumChainId) {
132
+ throw new GeneralException(new Error('Please provide ethereumChainId'));
133
+ }
134
+ /** Account Details * */
135
+ const accountDetails = await new ChainGrpcAuthApi(endpoints.grpc).fetchAccount(tx.injectiveAddress);
136
+ const { baseAccount } = accountDetails;
137
+ /** Block Details */
138
+ const latestBlock = await new ChainGrpcTendermintApi(endpoints.grpc).fetchLatestBlock();
139
+ const latestHeight = latestBlock.header.height;
140
+ const timeoutHeight = new BigNumberInBase(latestHeight).plus(txTimeout);
141
+ const gas = (tx.gas?.gas || getGasPriceBasedOnMessage(msgs)).toString();
142
+ let stdFee = getStdFee({ ...tx.gas, gas });
143
+ /**
144
+ * Account has been created on chain
145
+ * and we can simulate the transaction
146
+ * to estimate the gas
147
+ **/
148
+ if (baseAccount.pubKey) {
149
+ const { stdFee: simulatedStdFee } = await this.getTxWithSignersAndStdFee({
150
+ chainId,
151
+ signMode: SIGN_EIP712,
152
+ memo: tx.memo,
153
+ message: msgs,
154
+ timeoutHeight: timeoutHeight.toNumber(),
155
+ signers: {
156
+ pubKey: baseAccount.pubKey.key,
157
+ accountNumber: baseAccount.accountNumber,
158
+ sequence: baseAccount.sequence,
159
+ },
160
+ fee: stdFee,
161
+ });
162
+ stdFee = simulatedStdFee;
163
+ }
164
+ /** EIP712 for signing on Ethereum wallets */
165
+ const eip712TypedData = getEip712TypedData({
166
+ msgs,
167
+ fee: stdFee,
168
+ tx: {
169
+ memo: tx.memo,
170
+ accountNumber: baseAccount.accountNumber.toString(),
171
+ sequence: baseAccount.sequence.toString(),
172
+ timeoutHeight: timeoutHeight.toFixed(),
173
+ chainId,
174
+ },
175
+ ethereumChainId,
176
+ });
177
+ /** Signing on Ethereum */
178
+ const signature = await walletStrategy.signEip712TypedData(JSON.stringify(eip712TypedData), tx.ethereumAddress);
179
+ const pubKeyOrSignatureDerivedPubKey = getEthereumWalletPubKey({
180
+ pubKey: baseAccount.pubKey?.key,
181
+ eip712TypedData,
182
+ signature,
183
+ });
184
+ /** Preparing the transaction for client broadcasting */
185
+ const { txRaw } = createTransaction({
186
+ message: msgs,
187
+ memo: tx.memo,
188
+ signMode: SIGN_EIP712,
189
+ fee: stdFee,
190
+ pubKey: pubKeyOrSignatureDerivedPubKey,
191
+ sequence: baseAccount.sequence,
192
+ timeoutHeight: timeoutHeight.toNumber(),
193
+ accountNumber: baseAccount.accountNumber,
194
+ chainId,
195
+ });
196
+ const web3Extension = createWeb3Extension({
197
+ ethereumChainId,
198
+ });
199
+ const txRawEip712 = createTxRawEIP712(txRaw, web3Extension);
200
+ /** Append Signatures */
201
+ txRawEip712.signatures = [hexToBuff(signature)];
202
+ return walletStrategy.sendTransaction(txRawEip712, {
203
+ chainId,
204
+ endpoints,
205
+ txTimeout,
206
+ address: tx.injectiveAddress,
207
+ });
208
+ }
209
+ /**
210
+ * Prepare/sign/broadcast transaction using
211
+ * Ethereum native wallets on the client side.
212
+ *
213
+ * Note: Gas estimation not available
214
+ *
215
+ * @param tx The transaction that needs to be broadcasted
216
+ * @returns transaction hash
217
+ */
218
+ async broadcastWeb3V2(tx) {
219
+ const { walletStrategy, chainId, txTimeout, endpoints, ethereumChainId } = this;
220
+ const msgs = Array.isArray(tx.msgs) ? tx.msgs : [tx.msgs];
221
+ if (!ethereumChainId) {
222
+ throw new GeneralException(new Error('Please provide ethereumChainId'));
223
+ }
224
+ /** Account Details * */
225
+ const accountDetails = await new ChainGrpcAuthApi(endpoints.grpc).fetchAccount(tx.injectiveAddress);
226
+ const { baseAccount } = accountDetails;
227
+ /** Block Details */
228
+ const latestBlock = await new ChainGrpcTendermintApi(endpoints.grpc).fetchLatestBlock();
229
+ const latestHeight = latestBlock.header.height;
230
+ const timeoutHeight = new BigNumberInBase(latestHeight).plus(txTimeout);
231
+ const gas = (tx.gas?.gas || getGasPriceBasedOnMessage(msgs)).toString();
232
+ let stdFee = getStdFee({ ...tx.gas, gas });
233
+ /**
234
+ * Account has been created on chain
235
+ * and we can simulate the transaction
236
+ * to estimate the gas
237
+ **/
238
+ if (baseAccount.pubKey) {
239
+ const { stdFee: simulatedStdFee } = await this.getTxWithSignersAndStdFee({
240
+ chainId,
241
+ signMode: SIGN_EIP712_V2,
242
+ memo: tx.memo,
243
+ message: msgs,
244
+ timeoutHeight: timeoutHeight.toNumber(),
245
+ signers: {
246
+ pubKey: baseAccount.pubKey.key,
247
+ accountNumber: baseAccount.accountNumber,
248
+ sequence: baseAccount.sequence,
249
+ },
250
+ fee: stdFee,
251
+ });
252
+ stdFee = simulatedStdFee;
253
+ }
254
+ /** EIP712 for signing on Ethereum wallets */
255
+ const eip712TypedData = getEip712TypedDataV2({
256
+ msgs,
257
+ fee: stdFee,
258
+ tx: {
259
+ memo: tx.memo,
260
+ accountNumber: baseAccount.accountNumber.toString(),
261
+ sequence: baseAccount.sequence.toString(),
262
+ timeoutHeight: timeoutHeight.toFixed(),
263
+ chainId,
264
+ },
265
+ ethereumChainId,
266
+ });
267
+ /** Signing on Ethereum */
268
+ const signature = await walletStrategy.signEip712TypedData(JSON.stringify(eip712TypedData), tx.ethereumAddress);
269
+ const pubKeyOrSignatureDerivedPubKey = getEthereumWalletPubKey({
270
+ pubKey: baseAccount.pubKey?.key,
271
+ eip712TypedData,
272
+ signature,
273
+ });
274
+ const { txRaw } = createTransaction({
275
+ message: msgs,
276
+ memo: tx.memo,
277
+ signMode: SIGN_EIP712_V2,
278
+ fee: stdFee,
279
+ pubKey: pubKeyOrSignatureDerivedPubKey,
280
+ sequence: baseAccount.sequence,
281
+ timeoutHeight: timeoutHeight.toNumber(),
282
+ accountNumber: baseAccount.accountNumber,
283
+ chainId,
284
+ });
285
+ const web3Extension = createWeb3Extension({
286
+ ethereumChainId,
287
+ });
288
+ const txRawEip712 = createTxRawEIP712(txRaw, web3Extension);
289
+ /** Append Signatures */
290
+ txRawEip712.signatures = [hexToBuff(signature)];
291
+ return walletStrategy.sendTransaction(txRawEip712, {
292
+ chainId,
293
+ endpoints,
294
+ txTimeout,
295
+ address: tx.injectiveAddress,
296
+ });
297
+ }
298
+ /**
299
+ * Prepare/sign/broadcast transaction using
300
+ * Ethereum native wallets using the Web3Gateway.
301
+ *
302
+ * @param tx The transaction that needs to be broadcasted
303
+ * @returns transaction hash
304
+ */
305
+ async broadcastWeb3WithFeeDelegation(tx) {
306
+ const { txTimeout, endpoints, simulateTx, walletStrategy, ethereumChainId, txTimeoutOnFeeDelegation, } = this;
307
+ const msgs = Array.isArray(tx.msgs) ? tx.msgs : [tx.msgs];
308
+ const web3Msgs = msgs.map((msg) => msg.toWeb3());
309
+ if (!ethereumChainId) {
310
+ throw new GeneralException(new Error('Please provide ethereumChainId'));
311
+ }
312
+ const transactionApi = new IndexerGrpcWeb3GwApi(endpoints.web3gw || endpoints.indexer);
313
+ let timeoutHeight = undefined;
314
+ if (txTimeoutOnFeeDelegation) {
315
+ const latestBlock = await new ChainGrpcTendermintApi(endpoints.grpc).fetchLatestBlock();
316
+ const latestHeight = latestBlock.header.height;
317
+ timeoutHeight = new BigNumberInBase(latestHeight)
318
+ .plus(txTimeout)
319
+ .toNumber();
320
+ }
321
+ const txResponse = await transactionApi.prepareTxRequest({
322
+ timeoutHeight,
323
+ memo: tx.memo,
324
+ message: web3Msgs,
325
+ address: tx.ethereumAddress,
326
+ chainId: ethereumChainId,
327
+ gasLimit: getGasPriceBasedOnMessage(msgs),
328
+ estimateGas: simulateTx,
329
+ });
330
+ const signature = await walletStrategy.signEip712TypedData(txResponse.data, tx.ethereumAddress);
331
+ const response = await transactionApi.broadcastTxRequest({
332
+ signature,
333
+ txResponse,
334
+ message: web3Msgs,
335
+ chainId: ethereumChainId,
336
+ });
337
+ try {
338
+ const txResponse = await new TxGrpcApi(endpoints.grpc).fetchTxPoll(response.txHash);
339
+ return txResponse;
340
+ }
341
+ catch (e) {
342
+ /**
343
+ * First MsgExec transaction with a PrivateKey wallet
344
+ * always runs out of gas for some reason, temporary solution
345
+ * to just broadcast the transaction twice
346
+ **/
347
+ if (walletStrategy.wallet === Wallet.PrivateKey &&
348
+ checkIfTxRunOutOfGas(e)) {
349
+ /** Account Details * */
350
+ const accountDetails = await new ChainGrpcAuthApi(endpoints.grpc).fetchAccount(tx.injectiveAddress);
351
+ const { baseAccount } = accountDetails;
352
+ /** We only do it on the first account tx fail */
353
+ if (baseAccount.sequence > 1) {
354
+ throw e;
355
+ }
356
+ return await this.broadcastWeb3WithFeeDelegation(tx);
357
+ }
358
+ throw e;
359
+ }
360
+ }
361
+ /**
362
+ * Prepare/sign/broadcast transaction using
363
+ * Cosmos native wallets on the client side.
364
+ *
365
+ * @param tx The transaction that needs to be broadcasted
366
+ * @returns transaction hash
367
+ */
368
+ async broadcastCosmos(tx) {
369
+ const { walletStrategy, txTimeout, endpoints, chainId } = this;
370
+ const msgs = Array.isArray(tx.msgs) ? tx.msgs : [tx.msgs];
371
+ /**
372
+ * When using Ledger with Keplr/Leap we have
373
+ * to send EIP712 to sign on Keplr/Leap
374
+ */
375
+ if ([Wallet.Keplr, Wallet.Leap].includes(walletStrategy.getWallet())) {
376
+ const walletDeviceType = await walletStrategy.getWalletDeviceType();
377
+ const isLedgerConnected = walletDeviceType === WalletDeviceType.Hardware;
378
+ if (isLedgerConnected) {
379
+ return this.experimentalBroadcastWalletThroughLedger(tx);
380
+ }
381
+ }
382
+ /** Account Details * */
383
+ const accountDetails = await new ChainGrpcAuthApi(endpoints.grpc).fetchAccount(tx.injectiveAddress);
384
+ const { baseAccount } = accountDetails;
385
+ /** Block Details */
386
+ const latestBlock = await new ChainGrpcTendermintApi(endpoints.grpc).fetchLatestBlock();
387
+ const latestHeight = latestBlock.header.height;
388
+ const timeoutHeight = new BigNumberInBase(latestHeight).plus(txTimeout);
389
+ const signMode = isCosmosAminoOnlyWallet(walletStrategy.wallet)
390
+ ? SIGN_EIP712
391
+ : SIGN_DIRECT;
392
+ const pubKey = await walletStrategy.getPubKey(tx.injectiveAddress);
393
+ const gas = (tx.gas?.gas || getGasPriceBasedOnMessage(msgs)).toString();
394
+ /** Prepare the Transaction * */
395
+ const { txRaw } = await this.getTxWithSignersAndStdFee({
396
+ chainId,
397
+ signMode,
398
+ memo: tx.memo,
399
+ message: msgs,
400
+ timeoutHeight: timeoutHeight.toNumber(),
401
+ signers: {
402
+ pubKey,
403
+ accountNumber: baseAccount.accountNumber,
404
+ sequence: baseAccount.sequence,
405
+ },
406
+ fee: getStdFee({ ...tx.gas, gas }),
407
+ });
408
+ /** Ledger using Cosmos app only allows signing amino docs */
409
+ if (isCosmosAminoOnlyWallet(walletStrategy.wallet)) {
410
+ const aminoSignDoc = getAminoStdSignDoc({
411
+ ...tx,
412
+ ...baseAccount,
413
+ msgs,
414
+ chainId,
415
+ gas: gas || tx.gas?.gas?.toString(),
416
+ timeoutHeight: timeoutHeight.toFixed(),
417
+ });
418
+ const signResponse = await walletStrategy.signAminoCosmosTransaction({
419
+ signDoc: aminoSignDoc,
420
+ address: tx.injectiveAddress,
421
+ });
422
+ txRaw.signatures = [
423
+ Buffer.from(signResponse.signature.signature, 'base64'),
424
+ ];
425
+ return walletStrategy.sendTransaction(txRaw, {
426
+ chainId,
427
+ endpoints,
428
+ txTimeout,
429
+ address: tx.injectiveAddress,
430
+ });
431
+ }
432
+ const directSignResponse = (await walletStrategy.signCosmosTransaction({
433
+ txRaw,
434
+ chainId,
435
+ address: tx.injectiveAddress,
436
+ accountNumber: baseAccount.accountNumber,
437
+ }));
438
+ return walletStrategy.sendTransaction(directSignResponse, {
439
+ chainId,
440
+ endpoints,
441
+ txTimeout,
442
+ address: tx.injectiveAddress,
443
+ });
444
+ }
445
+ /**
446
+ * We use this method only when we want to broadcast a transaction using Ledger on Keplr/Leap for Injective
447
+ *
448
+ * Note: Gas estimation not available
449
+ * @param tx the transaction that needs to be broadcasted
450
+ */
451
+ async experimentalBroadcastWalletThroughLedger(tx) {
452
+ const { chainId, txTimeout, endpoints, simulateTx, walletStrategy, ethereumChainId, } = this;
453
+ const msgs = Array.isArray(tx.msgs) ? tx.msgs : [tx.msgs];
454
+ /**
455
+ * We can only use this method
456
+ * when Ledger is connected through Keplr
457
+ */
458
+ if ([Wallet.Keplr, Wallet.Leap].includes(walletStrategy.getWallet())) {
459
+ const walletDeviceType = await walletStrategy.getWalletDeviceType();
460
+ const isLedgerConnected = walletDeviceType === WalletDeviceType.Hardware;
461
+ if (!isLedgerConnected) {
462
+ throw new GeneralException(new Error(`This method can only be used when Ledger is connected through ${walletStrategy.getWallet()}`));
463
+ }
464
+ }
465
+ if (!ethereumChainId) {
466
+ throw new GeneralException(new Error('Please provide ethereumChainId'));
467
+ }
468
+ const cosmosWallet = walletStrategy.getCosmosWallet(chainId);
469
+ /** Account Details * */
470
+ const accountDetails = await new ChainGrpcAuthApi(endpoints.grpc).fetchAccount(tx.injectiveAddress);
471
+ const { baseAccount } = accountDetails;
472
+ /** Block Details */
473
+ const latestBlock = await new ChainGrpcTendermintApi(endpoints.grpc).fetchLatestBlock();
474
+ const latestHeight = latestBlock.header.height;
475
+ const timeoutHeight = new BigNumberInBase(latestHeight).plus(txTimeout);
476
+ const pubKey = await walletStrategy.getPubKey();
477
+ const gas = (tx.gas?.gas || getGasPriceBasedOnMessage(msgs)).toString();
478
+ /** EIP712 for signing on Ethereum wallets */
479
+ const eip712TypedData = getEip712TypedData({
480
+ msgs,
481
+ fee: getStdFee({ ...tx.gas, gas }),
482
+ tx: {
483
+ memo: tx.memo,
484
+ accountNumber: baseAccount.accountNumber.toString(),
485
+ sequence: baseAccount.sequence.toString(),
486
+ timeoutHeight: timeoutHeight.toFixed(),
487
+ chainId,
488
+ },
489
+ ethereumChainId,
490
+ });
491
+ const aminoSignResponse = await cosmosWallet.signEIP712CosmosTx({
492
+ eip712: eip712TypedData,
493
+ signDoc: createEip712StdSignDoc({
494
+ ...tx,
495
+ ...baseAccount,
496
+ msgs,
497
+ chainId,
498
+ gas: gas || tx.gas?.gas?.toString(),
499
+ timeoutHeight: timeoutHeight.toFixed(),
500
+ }),
501
+ });
502
+ /**
503
+ * Create TxRaw from the signed tx that we
504
+ * get as a response in case the user changed the fee/memo
505
+ * on the Keplr popup
506
+ */
507
+ const { txRaw } = createTransaction({
508
+ pubKey,
509
+ message: msgs,
510
+ memo: aminoSignResponse.signed.memo,
511
+ signMode: SIGN_EIP712,
512
+ fee: aminoSignResponse.signed.fee,
513
+ sequence: parseInt(aminoSignResponse.signed.sequence, 10),
514
+ timeoutHeight: parseInt(aminoSignResponse.signed.timeout_height, 10),
515
+ accountNumber: parseInt(aminoSignResponse.signed.account_number, 10),
516
+ chainId,
517
+ });
518
+ /** Preparing the transaction for client broadcasting */
519
+ const web3Extension = createWeb3Extension({
520
+ ethereumChainId,
521
+ });
522
+ const txRawEip712 = createTxRawEIP712(txRaw, web3Extension);
523
+ if (simulateTx) {
524
+ await this.simulateTxRaw(txRawEip712);
525
+ }
526
+ /** Append Signatures */
527
+ const signatureBuff = Buffer.from(aminoSignResponse.signature.signature, 'base64');
528
+ txRawEip712.signatures = [signatureBuff];
529
+ /** Broadcast the transaction */
530
+ const response = await new TxGrpcApi(endpoints.grpc).broadcast(txRawEip712, { txTimeout });
531
+ if (response.code !== 0) {
532
+ throw new TransactionException(new Error(response.rawLog), {
533
+ code: UnspecifiedErrorCode,
534
+ contextCode: response.code,
535
+ contextModule: response.codespace,
536
+ });
537
+ }
538
+ return response;
539
+ }
540
+ /**
541
+ * Prepare/sign/broadcast transaction using
542
+ * Cosmos native wallets using the Web3Gateway.
543
+ *
544
+ * @param tx The transaction that needs to be broadcasted
545
+ * @returns transaction hash
546
+ */
547
+ async broadcastCosmosWithFeeDelegation(tx) {
548
+ const { options, chainId, txTimeout, endpoints, walletStrategy, txTimeoutOnFeeDelegation, } = this;
549
+ const msgs = Array.isArray(tx.msgs) ? tx.msgs : [tx.msgs];
550
+ /**
551
+ * We can only use this method when Keplr is connected
552
+ * with ledger
553
+ */
554
+ if (walletStrategy.getWallet() === Wallet.Keplr) {
555
+ const walletDeviceType = await walletStrategy.getWalletDeviceType();
556
+ const isLedgerConnectedOnKeplr = walletDeviceType === WalletDeviceType.Hardware;
557
+ if (isLedgerConnectedOnKeplr) {
558
+ throw new GeneralException(new Error('Keplr + Ledger is not available with fee delegation. Connect with Ledger directly.'));
559
+ }
560
+ }
561
+ const cosmosWallet = walletStrategy.getCosmosWallet(chainId);
562
+ const feePayerPubKey = await this.fetchFeePayerPubKey(options.feePayerPubKey);
563
+ const feePayerPublicKey = PublicKey.fromBase64(feePayerPubKey);
564
+ const feePayer = feePayerPublicKey.toAddress().address;
565
+ /** Account Details * */
566
+ const chainGrpcAuthApi = new ChainGrpcAuthApi(endpoints.grpc);
567
+ const accountDetails = await chainGrpcAuthApi.fetchAccount(tx.injectiveAddress);
568
+ const feePayerAccountDetails = await chainGrpcAuthApi.fetchAccount(feePayer);
569
+ const { baseAccount } = accountDetails;
570
+ const { baseAccount: feePayerBaseAccount } = feePayerAccountDetails;
571
+ /** Block Details */
572
+ const latestBlock = await new ChainGrpcTendermintApi(endpoints.grpc).fetchLatestBlock();
573
+ const latestHeight = latestBlock.header.height;
574
+ const timeoutHeight = new BigNumberInBase(latestHeight).plus(txTimeoutOnFeeDelegation ? txTimeout : DEFAULT_BLOCK_TIMEOUT_HEIGHT);
575
+ const pubKey = await walletStrategy.getPubKey();
576
+ const gas = (tx.gas?.gas || getGasPriceBasedOnMessage(msgs)).toString();
577
+ /** Prepare the Transaction * */
578
+ const { txRaw } = await this.getTxWithSignersAndStdFee({
579
+ chainId,
580
+ memo: tx.memo,
581
+ message: msgs,
582
+ timeoutHeight: timeoutHeight.toNumber(),
583
+ signers: [
584
+ {
585
+ pubKey,
586
+ accountNumber: baseAccount.accountNumber,
587
+ sequence: baseAccount.sequence,
588
+ },
589
+ {
590
+ pubKey: feePayerPublicKey.toBase64(),
591
+ accountNumber: feePayerBaseAccount.accountNumber,
592
+ sequence: feePayerBaseAccount.sequence,
593
+ },
594
+ ],
595
+ fee: getStdFee({ ...tx.gas, gas, payer: feePayer }),
596
+ });
597
+ // Temporary remove tx gas check because Keplr doesn't recognize feePayer
598
+ if (walletStrategy.wallet === Wallet.Keplr &&
599
+ cosmosWallet.disableGasCheck) {
600
+ cosmosWallet.disableGasCheck(chainId);
601
+ }
602
+ const directSignResponse = (await walletStrategy.signCosmosTransaction({
603
+ txRaw,
604
+ chainId,
605
+ address: tx.injectiveAddress,
606
+ accountNumber: baseAccount.accountNumber,
607
+ }));
608
+ const transactionApi = new IndexerGrpcWeb3GwApi(endpoints.web3gw || endpoints.indexer);
609
+ const response = await transactionApi.broadcastCosmosTxRequest({
610
+ address: tx.injectiveAddress,
611
+ txRaw: createTxRawFromSigResponse(directSignResponse),
612
+ signature: directSignResponse.signature.signature,
613
+ pubKey: directSignResponse.signature.pub_key || {
614
+ value: pubKey,
615
+ type: '/injective.crypto.v1beta1.ethsecp256k1.PubKey',
616
+ },
617
+ });
618
+ // Re-enable tx gas check removed above
619
+ if (walletStrategy.wallet === Wallet.Keplr && cosmosWallet.enableGasCheck) {
620
+ cosmosWallet.enableGasCheck(chainId);
621
+ }
622
+ return await new TxGrpcApi(endpoints.grpc).fetchTxPoll(response.txHash);
623
+ }
624
+ /**
625
+ * Fetch the fee payer's pub key from the web3 gateway
626
+ *
627
+ * Returns a base64 version of it
628
+ */
629
+ async fetchFeePayerPubKey(existingFeePayerPubKey) {
630
+ if (existingFeePayerPubKey) {
631
+ return existingFeePayerPubKey;
632
+ }
633
+ const { endpoints } = this;
634
+ const transactionApi = new IndexerGrpcWeb3GwApi(endpoints.web3gw || endpoints.indexer);
635
+ const response = await transactionApi.fetchFeePayer();
636
+ if (!response.feePayerPubKey) {
637
+ throw new GeneralException(new Error('Please provide a feePayerPubKey'));
638
+ }
639
+ if (response.feePayerPubKey.key.startsWith('0x') ||
640
+ response.feePayerPubKey.key.length === 66) {
641
+ return Buffer.from(response.feePayerPubKey.key, 'hex').toString('base64');
642
+ }
643
+ return response.feePayerPubKey.key;
644
+ }
645
+ /**
646
+ * In case we don't want to simulate the transaction
647
+ * we get the gas limit based on the message type.
648
+ *
649
+ * If we want to simulate the transaction we set the
650
+ * gas limit based on the simulation and add a small multiplier
651
+ * to be safe (factor of 1.2 as default)
652
+ */
653
+ async getTxWithSignersAndStdFee(args) {
654
+ const { simulateTx } = this;
655
+ if (!simulateTx) {
656
+ return {
657
+ ...createTransactionWithSigners(args),
658
+ stdFee: getStdFee(args.fee),
659
+ };
660
+ }
661
+ const result = await this.simulateTxWithSigners(args);
662
+ if (!result.gasInfo?.gasUsed) {
663
+ return {
664
+ ...createTransactionWithSigners(args),
665
+ stdFee: getStdFee(args.fee),
666
+ };
667
+ }
668
+ const stdGasFee = {
669
+ ...getStdFee({
670
+ ...getStdFee(args.fee),
671
+ gas: new BigNumberInBase(result.gasInfo.gasUsed)
672
+ .times(this.gasBufferCoefficient)
673
+ .toFixed(),
674
+ }),
675
+ };
676
+ return {
677
+ ...createTransactionWithSigners({
678
+ ...args,
679
+ fee: stdGasFee,
680
+ }),
681
+ stdFee: stdGasFee,
682
+ };
683
+ }
684
+ /**
685
+ * Create TxRaw and simulate it
686
+ */
687
+ async simulateTxRaw(txRaw) {
688
+ const { endpoints } = this;
689
+ txRaw.signatures = [new Uint8Array(0)];
690
+ const simulationResponse = await new TxGrpcApi(endpoints.grpc).simulate(txRaw);
691
+ return simulationResponse;
692
+ }
693
+ /**
694
+ * Create TxRaw and simulate it
695
+ */
696
+ async simulateTxWithSigners(args) {
697
+ const { endpoints } = this;
698
+ const { txRaw } = createTransactionWithSigners(args);
699
+ txRaw.signatures = Array(Array.isArray(args.signers) ? args.signers.length : 1).fill(new Uint8Array(0));
700
+ const simulationResponse = await new TxGrpcApi(endpoints.grpc).simulate(txRaw);
701
+ return simulationResponse;
702
+ }
703
+ }
704
+ //# sourceMappingURL=MsgBroadcaster.js.map