@avalabs/evm-module 0.0.16 → 0.0.17

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 (30) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/.turbo/turbo-test.log +25 -24
  4. package/CHANGELOG.md +8 -0
  5. package/dist/index.cjs +25 -22
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.cts +9 -1
  8. package/dist/index.d.ts +9 -1
  9. package/dist/index.js +22 -21
  10. package/dist/index.js.map +1 -1
  11. package/package.json +4 -3
  12. package/src/constants.ts +1 -0
  13. package/src/handlers/eth-send-transaction/eth-send-transaction.test.ts +232 -1
  14. package/src/handlers/eth-send-transaction/eth-send-transaction.ts +24 -5
  15. package/src/handlers/eth-sign/eth-sign.test.ts +138 -35
  16. package/src/handlers/eth-sign/eth-sign.ts +29 -8
  17. package/src/handlers/get-balances/evm-balance-service/get-erc20-balances.test.ts +2 -2
  18. package/src/handlers/get-balances/evm-balance-service/get-erc20-balances.ts +4 -6
  19. package/src/handlers/get-balances/get-balances.test.ts +0 -5
  20. package/src/handlers/get-balances/get-balances.ts +14 -3
  21. package/src/handlers/get-balances/glacier-balance-service/get-erc20-balances.test.ts +0 -1
  22. package/src/handlers/get-balances/glacier-balance-service/get-erc20-balances.ts +10 -7
  23. package/src/handlers/get-tokens/get-tokens.test.ts +6 -6
  24. package/src/module.ts +2 -0
  25. package/src/types.ts +9 -0
  26. package/src/utils/parse-erc20-transaction-type.ts +35 -0
  27. package/src/utils/process-transaction-simulation.test.ts +105 -0
  28. package/src/utils/process-transaction-simulation.ts +294 -0
  29. package/src/utils/scan-transaction.ts +63 -0
  30. package/src/handlers/eth-sign/schemas/parse-request-params.ts +0 -90
package/package.json CHANGED
@@ -1,18 +1,19 @@
1
1
  {
2
2
  "name": "@avalabs/evm-module",
3
- "version": "0.0.16",
3
+ "version": "0.0.17",
4
4
  "main": "dist/index.cjs",
5
5
  "module": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
7
7
  "type": "module",
8
8
  "dependencies": {
9
- "@avalabs/vm-module-types": "0.0.16",
9
+ "@avalabs/vm-module-types": "0.0.17",
10
10
  "@zodios/core": "10.9.6",
11
11
  "@avalabs/coingecko-sdk": "v2.8.0-alpha.193",
12
12
  "@avalabs/utils-sdk": "2.8.0-alpha.193",
13
13
  "@avalabs/etherscan-sdk": "v2.8.0-alpha.193",
14
14
  "@avalabs/glacier-sdk": "v2.8.0-alpha.193",
15
15
  "@avalabs/wallets-sdk": "v2.8.0-alpha.193",
16
+ "@blockaid/client": "0.11.0",
16
17
  "bn.js": "5.2.1",
17
18
  "lodash.startcase": "4.4.0",
18
19
  "@metamask/rpc-errors": "6.3.0",
@@ -31,7 +32,7 @@
31
32
  "ethers": "6.8.1",
32
33
  "@internal/tsup-config": "0.0.1",
33
34
  "eslint-config-custom": "0.0.1",
34
- "@internal/utils": "0.0.2"
35
+ "@internal/utils": "0.0.3"
35
36
  },
36
37
  "peerDependencies": {
37
38
  "ethers": "^6.8.1"
@@ -0,0 +1 @@
1
+ export const DEFAULT_DECIMALS = 18;
@@ -3,16 +3,33 @@ import { parseRequestParams } from './schema';
3
3
  import { estimateGasLimit } from '../../utils/estimate-gas-limit';
4
4
  import { getNonce } from '../../utils/get-nonce';
5
5
  import { rpcErrors } from '@metamask/rpc-errors';
6
- import { RpcMethod, type ApprovalController, type Network } from '@avalabs/vm-module-types';
6
+ import { AlertType, RpcMethod, TokenType, type ApprovalController, type Network } from '@avalabs/vm-module-types';
7
7
  import { ZodError } from 'zod';
8
8
  import { getProvider } from '../../utils/get-provider';
9
+ import Blockaid from '@blockaid/client';
9
10
 
10
11
  const mockGetProvider = getProvider as jest.MockedFunction<typeof getProvider>;
11
12
 
13
+ const PROXY_API_URL = 'https://proxy-api.avax.network';
14
+
12
15
  jest.mock('./schema');
13
16
  jest.mock('../../utils/estimate-gas-limit');
14
17
  jest.mock('../../utils/get-nonce');
15
18
  jest.mock('../../utils/get-provider');
19
+ jest.mock('@blockaid/client', () => {
20
+ return jest.fn().mockImplementation(() => {
21
+ return {
22
+ evm: {
23
+ transaction: {
24
+ scan: jest.fn().mockResolvedValue({ validation: { result_type: 'Benign' } }),
25
+ },
26
+ jsonRpc: {
27
+ scan: jest.fn(),
28
+ },
29
+ },
30
+ };
31
+ });
32
+ });
16
33
 
17
34
  const mockOnTransactionConfirmed = jest.fn();
18
35
  const mockOnTransactionReverted = jest.fn();
@@ -64,6 +81,7 @@ const testRequestParams = () => ({
64
81
  },
65
82
  network: testNetwork,
66
83
  approvalController: mockApprovalController,
84
+ proxyApiUrl: PROXY_API_URL,
67
85
  });
68
86
 
69
87
  const displayData = {
@@ -80,6 +98,9 @@ const displayData = {
80
98
  data: '0xdata',
81
99
  },
82
100
  networkFeeSelector: true,
101
+ alert: undefined,
102
+ tokenApprovals: undefined,
103
+ balanceChange: undefined,
83
104
  };
84
105
 
85
106
  const signingData = {
@@ -221,6 +242,149 @@ describe('eth_sendTransaction handler', () => {
221
242
  });
222
243
  });
223
244
 
245
+ it('should add alert object with Warning type to displayData when validation result is Warning', async () => {
246
+ testWithValidationResultType('Warning');
247
+ });
248
+
249
+ it('should add alert object with Warning type to displayData when validation result is Error', async () => {
250
+ testWithValidationResultType('Error');
251
+ });
252
+
253
+ it('should add alert object with Danger type to displayData when validation result is Malicious', async () => {
254
+ testWithValidationResultType('Malicious');
255
+ });
256
+
257
+ it('should process transaction and add token approvals and balance changes to displayData', async () => {
258
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
259
+ (Blockaid as any).mockImplementation(() => ({
260
+ evm: {
261
+ transaction: {
262
+ scan: jest.fn().mockResolvedValue({
263
+ validation: { result_type: 'Benign' },
264
+ simulation: {
265
+ status: 'Success',
266
+ account_summary: {
267
+ exposures: [
268
+ {
269
+ asset: {
270
+ type: TokenType.ERC20,
271
+ address: '0xTokenAddress',
272
+ name: 'TokenName',
273
+ symbol: 'TKN',
274
+ decimals: 18,
275
+ logo_url: 'logo_url',
276
+ },
277
+ spenders: {
278
+ '0xSpenderAddress': {
279
+ exposure: [{ raw_value: '1', usd_price: '1' }],
280
+ },
281
+ },
282
+ },
283
+ ],
284
+ assets_diffs: [
285
+ {
286
+ asset: {
287
+ name: 'TokenName',
288
+ symbol: 'TKN',
289
+ decimals: 18,
290
+ logo_url: 'logo_url',
291
+ type: TokenType.ERC20,
292
+ address: '0xTokenAddress',
293
+ },
294
+ in: [{ value: '1', usd_price: '1' }],
295
+ out: [{ value: '1', usd_price: '1' }],
296
+ },
297
+ ],
298
+ },
299
+ },
300
+ }),
301
+ },
302
+ },
303
+ }));
304
+
305
+ mockParseRequestParams.mockReturnValue({
306
+ success: true,
307
+ data: [{ from: '0xfrom', to: '0xto', data: '0xdata', value: '0xvalue', nonce: '12', gas: '0x5208' }],
308
+ });
309
+
310
+ const requestParams = testRequestParams();
311
+
312
+ await ethSendTransaction(requestParams);
313
+
314
+ expect(mockGetProvider).toHaveBeenCalledWith({
315
+ chainId: 1,
316
+ chainName: 'chainName',
317
+ rpcUrl: 'rpcUrl',
318
+ multiContractAddress: 'multiContractAddress',
319
+ pollingInterval: 1000,
320
+ });
321
+
322
+ expect(mockApprovalController.requestApproval).toHaveBeenCalledWith({
323
+ request: requestParams.request,
324
+ displayData: {
325
+ ...displayData,
326
+ tokenApprovals: {
327
+ isEditable: true,
328
+ approvals: [
329
+ {
330
+ token: {
331
+ type: TokenType.ERC20,
332
+ address: '0xTokenAddress',
333
+ name: 'TokenName',
334
+ symbol: 'TKN',
335
+ decimals: 18,
336
+ logoUri: 'logo_url',
337
+ },
338
+ spenderAddress: '0xSpenderAddress',
339
+ value: '1',
340
+ usdPrice: '1',
341
+ logoUri: 'logo_url',
342
+ },
343
+ ],
344
+ },
345
+ balanceChange: {
346
+ ins: [
347
+ {
348
+ token: {
349
+ type: TokenType.ERC20,
350
+ address: '0xTokenAddress',
351
+ name: 'TokenName',
352
+ symbol: 'TKN',
353
+ decimals: 18,
354
+ logoUri: 'logo_url',
355
+ },
356
+ items: [
357
+ {
358
+ displayValue: '1',
359
+ usdPrice: '1',
360
+ },
361
+ ],
362
+ },
363
+ ],
364
+ outs: [
365
+ {
366
+ token: {
367
+ type: TokenType.ERC20,
368
+ address: '0xTokenAddress',
369
+ name: 'TokenName',
370
+ symbol: 'TKN',
371
+ decimals: 18,
372
+ logoUri: 'logo_url',
373
+ },
374
+ items: [
375
+ {
376
+ displayValue: '1',
377
+ usdPrice: '1',
378
+ },
379
+ ],
380
+ },
381
+ ],
382
+ },
383
+ },
384
+ signingData,
385
+ });
386
+ });
387
+
224
388
  it('should return error if gas limit calculation fails', async () => {
225
389
  mockParseRequestParams.mockReturnValue({
226
390
  success: true,
@@ -331,3 +495,70 @@ describe('eth_sendTransaction handler', () => {
331
495
  });
332
496
  });
333
497
  });
498
+
499
+ const testWithValidationResultType = async (resultType: 'Warning' | 'Error' | 'Malicious') => {
500
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
501
+ (Blockaid as any).mockImplementation(() => ({
502
+ evm: {
503
+ transaction: {
504
+ scan: jest.fn().mockResolvedValue({
505
+ validation: { result_type: resultType },
506
+ simulation: { status: 'Success', account_summary: { exposures: [], assets_diffs: [] } },
507
+ }),
508
+ },
509
+ },
510
+ }));
511
+
512
+ mockParseRequestParams.mockReturnValue({
513
+ success: true,
514
+ data: [{ from: '0xfrom', to: '0xto', data: '0xdata', value: '0xvalue', nonce: '12', gas: '0x5208' }],
515
+ });
516
+
517
+ const requestParams = testRequestParams();
518
+
519
+ await ethSendTransaction(requestParams);
520
+
521
+ expect(mockGetProvider).toHaveBeenCalledWith({
522
+ chainId: 1,
523
+ chainName: 'chainName',
524
+ rpcUrl: 'rpcUrl',
525
+ multiContractAddress: 'multiContractAddress',
526
+ pollingInterval: 1000,
527
+ });
528
+
529
+ if (resultType === 'Malicious') {
530
+ expect(mockApprovalController.requestApproval).toHaveBeenCalledWith({
531
+ request: requestParams.request,
532
+ displayData: {
533
+ ...displayData,
534
+ alert: {
535
+ type: AlertType.DANGER,
536
+ details: {
537
+ title: 'Scam Transaction',
538
+ description: 'This transaction is malicious, do not proceed.',
539
+ actionTitles: {
540
+ reject: 'Reject Transaction',
541
+ proceed: 'Proceed Anyway',
542
+ },
543
+ },
544
+ },
545
+ },
546
+ signingData,
547
+ });
548
+ } else {
549
+ expect(mockApprovalController.requestApproval).toHaveBeenCalledWith({
550
+ request: requestParams.request,
551
+ displayData: {
552
+ ...displayData,
553
+ alert: {
554
+ type: AlertType.WARNING,
555
+ details: {
556
+ title: 'Suspicious Transaction',
557
+ description: 'Use caution, this transaction may be malicious.',
558
+ },
559
+ },
560
+ },
561
+ signingData,
562
+ });
563
+ }
564
+ };
@@ -13,15 +13,20 @@ import { getNonce } from '../../utils/get-nonce';
13
13
  import { rpcErrors } from '@metamask/rpc-errors';
14
14
  import { getProvider } from '../../utils/get-provider';
15
15
  import type { JsonRpcBatchInternal } from '@avalabs/wallets-sdk';
16
+ import { processTransactionSimulation } from '../../utils/process-transaction-simulation';
17
+ import { parseERC20TransactionType } from '../../utils/parse-erc20-transaction-type';
18
+ import { ERC20TransactionType } from '../../types';
16
19
 
17
20
  export const ethSendTransaction = async ({
18
21
  request,
19
22
  network,
20
23
  approvalController,
24
+ proxyApiUrl,
21
25
  }: {
22
26
  request: RpcRequest;
23
27
  network: Network;
24
28
  approvalController: ApprovalController;
29
+ proxyApiUrl: string;
25
30
  }) => {
26
31
  const { dappInfo, params } = request;
27
32
 
@@ -87,14 +92,24 @@ export const ethSendTransaction = async ({
87
92
  }
88
93
  }
89
94
 
90
- // TODO: validate + simulate transaction
91
- // https://ava-labs.atlassian.net/browse/CP-8870
95
+ const transactionType = parseERC20TransactionType(transaction);
96
+
97
+ const { alert, balanceChange, tokenApprovals } = await processTransactionSimulation({
98
+ request,
99
+ proxyApiUrl,
100
+ chainId: network.chainId,
101
+ params: transaction,
102
+ dAppUrl: request.dappInfo.url,
103
+ });
92
104
 
93
105
  // generate display and signing data
94
- // TODO adjust title for different transaction types
95
- // https://ava-labs.atlassian.net/browse/CP-8870
106
+ let title = 'Approve Transaction';
107
+ if (transactionType === ERC20TransactionType.APPROVE) {
108
+ title = 'Token Spend Approval';
109
+ }
110
+
96
111
  const displayData: DisplayData = {
97
- title: 'Approve Transaction',
112
+ title,
98
113
  network: {
99
114
  chainId: network.chainId,
100
115
  name: network.chainName,
@@ -105,8 +120,12 @@ export const ethSendTransaction = async ({
105
120
  from: transaction.from,
106
121
  to: transaction.to,
107
122
  data: transaction.data,
123
+ type: transactionType,
108
124
  },
109
125
  networkFeeSelector: true,
126
+ alert,
127
+ balanceChange,
128
+ tokenApprovals,
110
129
  };
111
130
 
112
131
  const signingData: SigningData = {
@@ -1,6 +1,24 @@
1
1
  import { ethSign } from './eth-sign';
2
- import { RpcMethod, BannerType } from '@avalabs/vm-module-types';
2
+ import { AlertType, RpcMethod } from '@avalabs/vm-module-types';
3
3
  import { rpcErrors } from '@metamask/rpc-errors';
4
+ import Blockaid from '@blockaid/client';
5
+
6
+ const PROXY_API_URL = 'https://proxy-api.avax.network';
7
+
8
+ jest.mock('@blockaid/client', () => {
9
+ return jest.fn().mockImplementation(() => {
10
+ return {
11
+ evm: {
12
+ transaction: {
13
+ scan: jest.fn().mockResolvedValue({ validation: { result_type: 'Benign' } }),
14
+ },
15
+ jsonRpc: {
16
+ scan: jest.fn().mockResolvedValue({ validation: { result_type: 'Benign' } }),
17
+ },
18
+ },
19
+ };
20
+ });
21
+ });
4
22
 
5
23
  jest.mock('./schemas/parse-request-params/parse-request-params', () => ({
6
24
  parseRequestParams: jest.fn(),
@@ -31,37 +49,38 @@ const mockBeautifySimpleMessage = require('./utils/beautify-message/beautify-mes
31
49
  const mockBeautifyComplexMessage = require('./utils/beautify-message/beautify-message').beautifyComplexMessage;
32
50
  const mockIsTypedDataV1 = require('./utils/typeguards').isTypedDataV1;
33
51
 
34
- describe('ethSign', () => {
35
- const mockRequest = {
36
- method: RpcMethod.ETH_SIGN,
37
- params: ['0x123'],
38
- dappInfo: {
39
- name: 'Test DApp',
40
- url: 'test-url',
41
- icon: 'test-icon-uri',
42
- },
43
- requestId: 'requestId',
44
- sessionId: 'sessionId',
45
- chainId: 'eip155:1',
46
- };
47
- const mockNetwork = {
48
- chainId: 1,
49
- chainName: 'Ethereum',
50
- logoUri: 'test-logo-uri',
51
- rpcUrl: 'rpcUrl',
52
- networkToken: {
53
- name: 'ethereum',
54
- symbol: 'ETH',
55
- decimals: 18,
56
- },
57
- };
52
+ const mockRequest = {
53
+ method: RpcMethod.ETH_SIGN,
54
+ params: ['0x123'],
55
+ dappInfo: {
56
+ name: 'Test DApp',
57
+ url: 'test-url',
58
+ icon: 'test-icon-uri',
59
+ },
60
+ requestId: 'requestId',
61
+ sessionId: 'sessionId',
62
+ chainId: 'eip155:1',
63
+ };
64
+
65
+ const mockNetwork = {
66
+ chainId: 1,
67
+ chainName: 'Ethereum',
68
+ logoUri: 'test-logo-uri',
69
+ rpcUrl: 'rpcUrl',
70
+ networkToken: {
71
+ name: 'ethereum',
72
+ symbol: 'ETH',
73
+ decimals: 18,
74
+ },
75
+ };
58
76
 
59
- const mockApprovalController = {
60
- requestApproval: jest.fn(),
61
- onTransactionConfirmed: jest.fn(),
62
- onTransactionReverted: jest.fn(),
63
- };
77
+ const mockApprovalController = {
78
+ requestApproval: jest.fn(),
79
+ onTransactionConfirmed: jest.fn(),
80
+ onTransactionReverted: jest.fn(),
81
+ };
64
82
 
83
+ describe('ethSign', () => {
65
84
  beforeEach(() => {
66
85
  mockApprovalController.requestApproval.mockResolvedValue({ result: '0x1234' });
67
86
  mockBeautifySimpleMessage.mockReturnValue('beautified simple message');
@@ -75,6 +94,7 @@ describe('ethSign', () => {
75
94
  request: mockRequest,
76
95
  network: mockNetwork,
77
96
  approvalController: mockApprovalController,
97
+ proxyApiUrl: PROXY_API_URL,
78
98
  });
79
99
 
80
100
  expect(result).toEqual({
@@ -96,16 +116,19 @@ describe('ethSign', () => {
96
116
  request: { ...mockRequest, method },
97
117
  network: mockNetwork,
98
118
  approvalController: mockApprovalController,
119
+ proxyApiUrl: PROXY_API_URL,
99
120
  });
100
121
 
101
122
  expect(mockApprovalController.requestApproval).toHaveBeenCalledWith(
102
123
  expect.objectContaining({
103
124
  displayData: expect.objectContaining({
104
- banner: {
105
- type: BannerType.WARNING,
106
- title: 'Warning: Verify Message Content',
107
- description: 'This message contains non-standard elements.',
108
- detailedDescription: 'Invalid typed data',
125
+ alert: {
126
+ type: AlertType.INFO,
127
+ details: {
128
+ title: 'Warning: Verify Message Content',
129
+ description: 'This message contains non-standard elements.',
130
+ detailedDescription: 'Invalid typed data',
131
+ },
109
132
  },
110
133
  }),
111
134
  }),
@@ -153,6 +176,7 @@ describe('ethSign', () => {
153
176
  request: { ...mockRequest, method },
154
177
  network: mockNetwork,
155
178
  approvalController: mockApprovalController,
179
+ proxyApiUrl: PROXY_API_URL,
156
180
  });
157
181
 
158
182
  expect(mockApprovalController.requestApproval).toHaveBeenCalledWith({
@@ -172,6 +196,9 @@ describe('ethSign', () => {
172
196
  logoUri: 'test-logo-uri',
173
197
  name: 'Ethereum',
174
198
  },
199
+ alert: undefined,
200
+ balanceChange: undefined,
201
+ tokenApprovals: undefined,
175
202
  },
176
203
  request: { ...mockRequest, method },
177
204
  signingData: {
@@ -184,6 +211,18 @@ describe('ethSign', () => {
184
211
  },
185
212
  );
186
213
 
214
+ it('should add alert object with Warning type to displayData when validation result is Warning', async () => {
215
+ testWithValidationResultType('Warning');
216
+ });
217
+
218
+ it('should add alert object with Warning type to displayData when validation result is Error', async () => {
219
+ testWithValidationResultType('Error');
220
+ });
221
+
222
+ it('should add alert object with Danger type to displayData when validation result is Malicious', async () => {
223
+ testWithValidationResultType('Malicious');
224
+ });
225
+
187
226
  it('should handle success case for approvalController.requestApproval', async () => {
188
227
  mockParseRequestParams.mockReturnValueOnce({
189
228
  success: true,
@@ -194,6 +233,7 @@ describe('ethSign', () => {
194
233
  request: mockRequest,
195
234
  network: mockNetwork,
196
235
  approvalController: mockApprovalController,
236
+ proxyApiUrl: PROXY_API_URL,
197
237
  });
198
238
 
199
239
  expect(result).toEqual({ result: '0x1234' });
@@ -210,8 +250,71 @@ describe('ethSign', () => {
210
250
  request: mockRequest,
211
251
  network: mockNetwork,
212
252
  approvalController: mockApprovalController,
253
+ proxyApiUrl: PROXY_API_URL,
213
254
  });
214
255
 
215
256
  expect(result).toEqual({ error: 'User denied message signature' });
216
257
  });
217
258
  });
259
+
260
+ const testWithValidationResultType = async (resultType: 'Warning' | 'Error' | 'Malicious') => {
261
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
262
+ (Blockaid as any).mockImplementation(() => ({
263
+ evm: {
264
+ jsonRpc: {
265
+ scan: jest.fn().mockResolvedValue({
266
+ validation: { result_type: resultType },
267
+ simulation: { status: 'Success', account_summary: { exposures: [], assets_diffs: [] } },
268
+ }),
269
+ },
270
+ },
271
+ }));
272
+
273
+ mockParseRequestParams.mockReturnValueOnce({
274
+ success: true,
275
+ data: { method: RpcMethod.ETH_SIGN, data: 'data', address: '0xabc' },
276
+ });
277
+
278
+ const result = await ethSign({
279
+ request: mockRequest,
280
+ network: mockNetwork,
281
+ approvalController: mockApprovalController,
282
+ proxyApiUrl: PROXY_API_URL,
283
+ });
284
+
285
+ expect(result).toEqual({ result: '0x1234' });
286
+
287
+ if (resultType === 'Malicious') {
288
+ expect(mockApprovalController.requestApproval).toHaveBeenCalledWith(
289
+ expect.objectContaining({
290
+ displayData: expect.objectContaining({
291
+ alert: {
292
+ type: AlertType.DANGER,
293
+ details: {
294
+ title: 'Scam Transaction',
295
+ description: 'This transaction is malicious, do not proceed.',
296
+ actionTitles: {
297
+ reject: 'Reject Transaction',
298
+ proceed: 'Proceed Anyway',
299
+ },
300
+ },
301
+ },
302
+ }),
303
+ }),
304
+ );
305
+ } else {
306
+ expect(mockApprovalController.requestApproval).toHaveBeenCalledWith(
307
+ expect.objectContaining({
308
+ displayData: expect.objectContaining({
309
+ alert: {
310
+ type: AlertType.WARNING,
311
+ details: {
312
+ title: 'Suspicious Transaction',
313
+ description: 'Use caution, this transaction may be malicious.',
314
+ },
315
+ },
316
+ }),
317
+ }),
318
+ );
319
+ }
320
+ };
@@ -5,7 +5,8 @@ import {
5
5
  type DisplayData,
6
6
  type RpcRequest,
7
7
  RpcMethod,
8
- BannerType,
8
+ type Alert,
9
+ AlertType,
9
10
  } from '@avalabs/vm-module-types';
10
11
  import { rpcErrors } from '@metamask/rpc-errors';
11
12
  import { toUtf8String } from 'ethers';
@@ -13,15 +14,18 @@ import { beautifySimpleMessage, beautifyComplexMessage } from './utils/beautify-
13
14
  import { parseRequestParams } from './schemas/parse-request-params/parse-request-params';
14
15
  import { isTypedDataV1 } from './utils/typeguards';
15
16
  import { isTypedDataValid } from './utils/is-typed-data-valid';
17
+ import { processJsonRpcSimulation } from '../../utils/process-transaction-simulation';
16
18
 
17
19
  export const ethSign = async ({
18
20
  request,
19
21
  network,
20
22
  approvalController,
23
+ proxyApiUrl,
21
24
  }: {
22
25
  request: RpcRequest;
23
26
  network: Network;
24
27
  approvalController: ApprovalController;
28
+ proxyApiUrl: string;
25
29
  }) => {
26
30
  const result = parseRequestParams({ method: request.method, params: request.params });
27
31
 
@@ -46,14 +50,16 @@ export const ethSign = async ({
46
50
  let signingData: SigningData | undefined;
47
51
  let messageDetails: string | undefined;
48
52
  let disclaimer: string | undefined;
49
- let banner: DisplayData['banner'] | undefined;
53
+ let alert: Alert | undefined;
50
54
 
51
55
  if (typedDataValidationResult && !typedDataValidationResult.isValid) {
52
- banner = {
53
- type: BannerType.WARNING,
54
- title: 'Warning: Verify Message Content',
55
- description: 'This message contains non-standard elements.',
56
- detailedDescription: (typedDataValidationResult.error as Error).toString(),
56
+ alert = {
57
+ type: AlertType.INFO,
58
+ details: {
59
+ title: 'Warning: Verify Message Content',
60
+ description: 'This message contains non-standard elements.',
61
+ detailedDescription: (typedDataValidationResult.error as Error).toString(),
62
+ },
57
63
  };
58
64
  }
59
65
 
@@ -106,8 +112,20 @@ export const ethSign = async ({
106
112
  };
107
113
  }
108
114
 
115
+ const {
116
+ alert: prioritizedAlert,
117
+ balanceChange,
118
+ tokenApprovals,
119
+ } = await processJsonRpcSimulation({
120
+ request,
121
+ proxyApiUrl,
122
+ accountAddress: address,
123
+ chainId: network.chainId,
124
+ data: { method, params: request.params },
125
+ dAppUrl: request.dappInfo.url,
126
+ });
127
+
109
128
  const displayData: DisplayData = {
110
- banner,
111
129
  title: 'Sign Message',
112
130
  dAppInfo: {
113
131
  name: request.dappInfo.name,
@@ -122,6 +140,9 @@ export const ethSign = async ({
122
140
  account: address,
123
141
  messageDetails,
124
142
  disclaimer,
143
+ alert: prioritizedAlert ?? alert,
144
+ balanceChange,
145
+ tokenApprovals,
125
146
  };
126
147
 
127
148
  // prompt user for approval