@avalabs/evm-module 0.0.17 → 0.0.19

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/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "@avalabs/evm-module",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
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.17",
10
- "@zodios/core": "10.9.6",
9
+ "@avalabs/vm-module-types": "0.0.19",
11
10
  "@avalabs/coingecko-sdk": "v2.8.0-alpha.193",
12
11
  "@avalabs/utils-sdk": "2.8.0-alpha.193",
13
12
  "@avalabs/etherscan-sdk": "v2.8.0-alpha.193",
@@ -17,7 +16,8 @@
17
16
  "bn.js": "5.2.1",
18
17
  "lodash.startcase": "4.4.0",
19
18
  "@metamask/rpc-errors": "6.3.0",
20
- "zod": "3.23.8"
19
+ "zod": "3.23.8",
20
+ "@zodios/core": "10.9.6"
21
21
  },
22
22
  "devDependencies": {
23
23
  "typechain": "8.3.1",
@@ -3,7 +3,14 @@ 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 { AlertType, RpcMethod, TokenType, type ApprovalController, type Network } from '@avalabs/vm-module-types';
6
+ import {
7
+ AlertType,
8
+ RpcMethod,
9
+ TokenType,
10
+ type ApprovalController,
11
+ type Network,
12
+ NetworkVMType,
13
+ } from '@avalabs/vm-module-types';
7
14
  import { ZodError } from 'zod';
8
15
  import { getProvider } from '../../utils/get-provider';
9
16
  import Blockaid from '@blockaid/client';
@@ -66,6 +73,7 @@ const testNetwork: Network = {
66
73
  description: 'Ethereum Token',
67
74
  logoUri: 'some logo uri',
68
75
  },
76
+ vmName: NetworkVMType.EVM,
69
77
  };
70
78
 
71
79
  const testParams = { from: '0xfrom', to: '0xto', data: '0xdata', value: '0xvalue', nonce: '12', gas: '0x5208' };
@@ -1,5 +1,5 @@
1
1
  import { ethSign } from './eth-sign';
2
- import { AlertType, RpcMethod } from '@avalabs/vm-module-types';
2
+ import { AlertType, NetworkVMType, RpcMethod } from '@avalabs/vm-module-types';
3
3
  import { rpcErrors } from '@metamask/rpc-errors';
4
4
  import Blockaid from '@blockaid/client';
5
5
 
@@ -72,6 +72,7 @@ const mockNetwork = {
72
72
  symbol: 'ETH',
73
73
  decimals: 18,
74
74
  },
75
+ vmName: NetworkVMType.EVM,
75
76
  };
76
77
 
77
78
  const mockApprovalController = {
@@ -223,6 +224,44 @@ describe('ethSign', () => {
223
224
  testWithValidationResultType('Malicious');
224
225
  });
225
226
 
227
+ it('should add alert object with Warning type to displayData when schema validation error occurs in jsonRpc scan', async () => {
228
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
229
+ (Blockaid as any).mockImplementation(() => ({
230
+ evm: {
231
+ jsonRpc: {
232
+ scan: jest.fn().mockRejectedValue({ message: 'schema validation error' }),
233
+ },
234
+ },
235
+ }));
236
+
237
+ mockParseRequestParams.mockReturnValueOnce({
238
+ success: true,
239
+ data: { method: RpcMethod.ETH_SIGN, data: 'data', address: '0xabc' },
240
+ });
241
+
242
+ const result = await ethSign({
243
+ request: mockRequest,
244
+ network: mockNetwork,
245
+ approvalController: mockApprovalController,
246
+ proxyApiUrl: PROXY_API_URL,
247
+ });
248
+
249
+ expect(result).toEqual({ result: '0x1234' });
250
+ expect(mockApprovalController.requestApproval).toHaveBeenCalledWith(
251
+ expect.objectContaining({
252
+ displayData: expect.objectContaining({
253
+ alert: {
254
+ type: AlertType.WARNING,
255
+ details: {
256
+ title: 'Suspicious Transaction',
257
+ description: 'Use caution, this transaction may be malicious.',
258
+ },
259
+ },
260
+ }),
261
+ }),
262
+ );
263
+ });
264
+
226
265
  it('should handle success case for approvalController.requestApproval', async () => {
227
266
  mockParseRequestParams.mockReturnValueOnce({
228
267
  success: true,
@@ -53,14 +53,16 @@ export const getErc20Balances = async ({
53
53
 
54
54
  return tokensBalances.reduce(
55
55
  (acc, token) => {
56
- const priceInCurrency = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.price ?? 0;
57
- const marketCap = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.marketCap ?? 0;
58
- const vol24 = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.vol24 ?? 0;
59
- const change24 = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.change24 ?? 0;
56
+ const priceInCurrency = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.price ?? undefined;
57
+ const marketCap = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.marketCap ?? undefined;
58
+ const vol24 = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.vol24 ?? undefined;
59
+ const change24 = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.change24 ?? undefined;
60
60
 
61
- const balanceInCurrency = bnToBig(token.balance, token.decimals).mul(priceInCurrency).toNumber();
61
+ const balanceInCurrency = priceInCurrency
62
+ ? bnToBig(token.balance, token.decimals).mul(priceInCurrency).toNumber()
63
+ : undefined;
62
64
  const balanceDisplayValue = balanceToDisplayValue(token.balance, token.decimals);
63
- const balanceCurrencyDisplayValue = balanceInCurrency.toFixed(2);
65
+ const balanceCurrencyDisplayValue = balanceInCurrency ? balanceInCurrency.toFixed(2) : '';
64
66
 
65
67
  return {
66
68
  ...acc,
@@ -88,15 +88,10 @@ describe('get-native-token-balances', () => {
88
88
  decimals: 18,
89
89
  logoUri: 'https://example.com/logo.png',
90
90
  balance: new BN('0'),
91
- balanceCurrencyDisplayValue: '0.00',
91
+ balanceCurrencyDisplayValue: '',
92
92
  balanceDisplayValue: '0',
93
- balanceInCurrency: 0,
94
- priceInCurrency: 0,
95
93
  coingeckoId: '123',
96
94
  type: 'NATIVE',
97
- marketCap: 0,
98
- vol24: 0,
99
- change24: 0,
100
95
  });
101
96
  });
102
97
  });
@@ -26,17 +26,19 @@ export const getNativeTokenBalances = async ({
26
26
  })
27
27
  : {};
28
28
 
29
- const priceInCurrency = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.price ?? 0;
30
- const marketCap = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.marketCap ?? 0;
31
- const vol24 = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.vol24 ?? 0;
32
- const change24 = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.change24 ?? 0;
29
+ const priceInCurrency = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.price ?? undefined;
30
+ const marketCap = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.marketCap ?? undefined;
31
+ const vol24 = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.vol24 ?? undefined;
32
+ const change24 = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.change24 ?? undefined;
33
33
 
34
34
  const balanceBigint = await provider.getBalance(address);
35
35
  const balaceBig = bigintToBig(balanceBigint, networkToken.decimals);
36
36
  const balance = bigToBN(balaceBig, networkToken.decimals);
37
37
  const balanceDisplayValue = balanceToDisplayValue(balance, networkToken.decimals);
38
- const balanceInCurrency = bnToBig(balance, networkToken.decimals).mul(priceInCurrency).toNumber();
39
- const balanceCurrencyDisplayValue = balanceInCurrency.toFixed(2);
38
+ const balanceInCurrency = priceInCurrency
39
+ ? bnToBig(balance, networkToken.decimals).mul(priceInCurrency).toNumber()
40
+ : undefined;
41
+ const balanceCurrencyDisplayValue = balanceInCurrency ? balanceInCurrency.toFixed(2) : '';
40
42
 
41
43
  return {
42
44
  ...networkToken,
@@ -1,4 +1,4 @@
1
- import { TokenType } from '@avalabs/vm-module-types';
1
+ import { NetworkVMType, TokenType } from '@avalabs/vm-module-types';
2
2
  import { getBalances } from './get-balances';
3
3
  import * as GlacierNativeToken from './glacier-balance-service/get-native-token-balances';
4
4
  import { BN } from 'bn.js';
@@ -74,6 +74,7 @@ describe('get-balances', () => {
74
74
  pricingProviders: { coingecko: { nativeTokenId: 'ethereum', assetPlatformId: 'ethereum' } },
75
75
  rpcUrl: 'https://proxy-api.avax.network/proxy/infura/mainnet',
76
76
  utilityAddresses: { multicall: '0x5ba1e12693dc8f9c48aad8770482f4739beed696' },
77
+ vmName: NetworkVMType.EVM,
77
78
  },
78
79
  proxyApiUrl: 'proxyApiUrl',
79
80
  glacierService: mockGlacierService,
@@ -201,6 +202,7 @@ describe('get-balances', () => {
201
202
  pricingProviders: { coingecko: { nativeTokenId: 'ethereum', assetPlatformId: 'ethereum' } },
202
203
  rpcUrl: 'https://proxy-api.avax.network/proxy/infura/mainnet',
203
204
  utilityAddresses: { multicall: '0x5ba1e12693dc8f9c48aad8770482f4739beed696' },
205
+ vmName: NetworkVMType.EVM,
204
206
  },
205
207
  proxyApiUrl: 'proxyApiUrl',
206
208
  glacierService: mockGlacierService,
@@ -41,6 +41,7 @@ export const getBalances = async ({
41
41
  currency,
42
42
  chainId,
43
43
  glacierService,
44
+ coingeckoId: network.pricingProviders?.coingecko.nativeTokenId ?? '',
44
45
  });
45
46
 
46
47
  const erc20Tokens = await getErc20BalancesFromGlacier({
@@ -51,9 +51,6 @@ describe('get-erc20-balances', () => {
51
51
  balanceInCurrency: 1000,
52
52
  priceInCurrency: 1000,
53
53
  type: 'ERC20',
54
- change24: 0,
55
- marketCap: 0,
56
- vol24: 0,
57
54
  },
58
55
  });
59
56
  });
@@ -85,9 +85,11 @@ const convertErc20TokenWithBalanceToTokenWithBalance = (
85
85
  return tokenBalances.map((token: Erc20TokenBalance): TokenWithBalanceERC20 => {
86
86
  const balance = new BN(token.balance);
87
87
  const balanceDisplayValue = balanceToDisplayValue(balance, token.decimals);
88
- const balanceCurrencyDisplayValue = token.balanceValue?.value.toString() ?? '0';
89
- const priceInCurrency = token.price?.value ?? 0;
90
- const balanceInCurrency = bnToBig(balance, token.decimals).mul(priceInCurrency).toNumber();
88
+ const balanceCurrencyDisplayValue = token.balanceValue?.value.toString() ?? '';
89
+ const priceInCurrency = token.price?.value;
90
+ const balanceInCurrency = priceInCurrency
91
+ ? bnToBig(balance, token.decimals).mul(priceInCurrency).toNumber()
92
+ : undefined;
91
93
 
92
94
  return {
93
95
  chainId,
@@ -102,9 +104,6 @@ const convertErc20TokenWithBalanceToTokenWithBalance = (
102
104
  balanceInCurrency,
103
105
  priceInCurrency,
104
106
  type: TokenType.ERC20,
105
- change24: 0,
106
- marketCap: 0,
107
- vol24: 0,
108
107
  };
109
108
  });
110
109
  };
@@ -25,6 +25,7 @@ describe('get-native-token-balances', () => {
25
25
  currency: 'USD',
26
26
  chainId: 123,
27
27
  glacierService: mockGlacierService,
28
+ coingeckoId: '',
28
29
  });
29
30
 
30
31
  expect(balance).resolves.toEqual({
@@ -38,9 +39,6 @@ describe('get-native-token-balances', () => {
38
39
  balanceInCurrency: 1000,
39
40
  balanceCurrencyDisplayValue: '1000.00',
40
41
  priceInCurrency: 1000,
41
- marketCap: 0,
42
- vol24: 0,
43
- change24: 0,
44
42
  coingeckoId: '',
45
43
  });
46
44
  });
@@ -55,6 +53,7 @@ describe('get-native-token-balances', () => {
55
53
  currency: 'USD',
56
54
  chainId: 123,
57
55
  glacierService: mockGlacierService,
56
+ coingeckoId: '',
58
57
  });
59
58
  expect(balance).rejects.toEqual(new Error('Failed to get native balance'));
60
59
  });
@@ -9,10 +9,12 @@ export const getNativeTokenBalances = async ({
9
9
  currency,
10
10
  chainId,
11
11
  glacierService,
12
+ coingeckoId,
12
13
  }: {
13
14
  chainId: number;
14
15
  address: string;
15
16
  currency: string;
17
+ coingeckoId: string;
16
18
  glacierService: EvmGlacierService;
17
19
  }): Promise<NetworkTokenWithBalance> => {
18
20
  const nativeBalance = await glacierService.getNativeBalance({
@@ -23,9 +25,11 @@ export const getNativeTokenBalances = async ({
23
25
  const nativeTokenBalance = nativeBalance.nativeTokenBalance;
24
26
  const balance = new BN(nativeTokenBalance.balance);
25
27
  const balanceDisplayValue = balanceToDisplayValue(balance, nativeTokenBalance.decimals);
26
- const priceInCurrency = nativeTokenBalance.price?.value ?? 0;
27
- const balanceInCurrency = bnToBig(balance, nativeTokenBalance.decimals).mul(priceInCurrency).toNumber();
28
- const balanceCurrencyDisplayValue = balanceInCurrency.toFixed(2);
28
+ const priceInCurrency = nativeTokenBalance.price?.value;
29
+ const balanceInCurrency = priceInCurrency
30
+ ? bnToBig(balance, nativeTokenBalance.decimals).mul(priceInCurrency).toNumber()
31
+ : undefined;
32
+ const balanceCurrencyDisplayValue = balanceInCurrency ? balanceInCurrency.toFixed(2) : '';
29
33
 
30
34
  return {
31
35
  name: nativeTokenBalance.name,
@@ -38,9 +42,6 @@ export const getNativeTokenBalances = async ({
38
42
  balanceInCurrency,
39
43
  balanceCurrencyDisplayValue,
40
44
  priceInCurrency,
41
- marketCap: 0,
42
- vol24: 0,
43
- change24: 0,
44
- coingeckoId: '',
45
+ coingeckoId,
45
46
  };
46
47
  };
package/src/module.ts CHANGED
@@ -44,7 +44,13 @@ export class EvmModule implements Module {
44
44
  return Promise.resolve('EVM address');
45
45
  }
46
46
 
47
- getBalances({ addresses, network, currency, customTokens }: GetBalancesParams): Promise<GetBalancesResponse> {
47
+ getBalances({
48
+ addresses,
49
+ network,
50
+ currency,
51
+ customTokens,
52
+ storage,
53
+ }: GetBalancesParams): Promise<GetBalancesResponse> {
48
54
  return getBalances({
49
55
  addresses,
50
56
  currency,
@@ -52,6 +58,7 @@ export class EvmModule implements Module {
52
58
  proxyApiUrl: this.#proxyApiUrl,
53
59
  customTokens,
54
60
  glacierService: this.#glacierService,
61
+ storage,
55
62
  });
56
63
  }
57
64
 
@@ -1,17 +1,12 @@
1
1
  import {
2
- BlockchainId,
3
2
  CurrencyCode,
4
3
  Erc1155Token,
5
4
  Erc721Token,
6
5
  type GetNativeBalanceResponse,
7
6
  Glacier,
8
- type ListCChainAtomicBalancesResponse,
9
7
  type ListErc1155BalancesResponse,
10
8
  type ListErc20BalancesResponse,
11
9
  type ListErc721BalancesResponse,
12
- type ListPChainBalancesResponse,
13
- type ListXChainBalancesResponse,
14
- Network,
15
10
  } from '@avalabs/glacier-sdk';
16
11
 
17
12
  class GlacierUnhealthyError extends Error {
@@ -111,22 +106,6 @@ export class EvmGlacierService {
111
106
  }
112
107
  }
113
108
 
114
- async getChainBalance(params: {
115
- blockchainId: BlockchainId;
116
- network: Network;
117
- blockTimestamp?: number;
118
- addresses?: string;
119
- }): Promise<ListPChainBalancesResponse | ListXChainBalancesResponse | ListCChainAtomicBalancesResponse> {
120
- try {
121
- return this.glacierSdk.primaryNetworkBalances.getBalancesByAddresses(params);
122
- } catch (error) {
123
- if (error instanceof GlacierUnhealthyError) {
124
- this.setGlacierToUnhealthy();
125
- }
126
- throw error;
127
- }
128
- }
129
-
130
109
  async getNativeBalance({
131
110
  chainId,
132
111
  address,
@@ -31,42 +31,31 @@ export const processTransactionSimulation = async ({
31
31
  chainId: number;
32
32
  proxyApiUrl: string;
33
33
  }) => {
34
- const { validation, simulation } = await scanTransaction({
35
- proxyApiUrl,
36
- chainId,
37
- params,
38
- domain: dAppUrl,
39
- });
40
-
41
34
  let alert: Alert | undefined;
42
- if (!validation || validation.result_type === 'Error' || validation.result_type === 'Warning') {
43
- alert = {
44
- type: AlertType.WARNING,
45
- details: {
46
- title: 'Suspicious Transaction',
47
- description: 'Use caution, this transaction may be malicious.',
48
- },
49
- };
50
- } else if (validation.result_type === 'Malicious') {
51
- alert = {
52
- type: AlertType.DANGER,
53
- details: {
54
- title: 'Scam Transaction',
55
- description: 'This transaction is malicious, do not proceed.',
56
- actionTitles: {
57
- reject: 'Reject Transaction',
58
- proceed: 'Proceed Anyway',
59
- },
60
- },
61
- };
62
- }
63
-
64
35
  let balanceChange: BalanceChange | undefined;
65
36
  let tokenApprovals: TokenApprovals | undefined;
66
37
 
67
- if (simulation?.status === 'Success') {
68
- tokenApprovals = processTokenApprovals(request, simulation.account_summary.exposures);
69
- balanceChange = processBalanceChange(simulation.account_summary.assets_diffs);
38
+ try {
39
+ const { validation, simulation } = await scanTransaction({
40
+ proxyApiUrl,
41
+ chainId,
42
+ params,
43
+ domain: dAppUrl,
44
+ });
45
+
46
+ if (!validation || validation.result_type === 'Error' || validation.result_type === 'Warning') {
47
+ alert = transactionAlerts[AlertType.WARNING];
48
+ } else if (validation.result_type === 'Malicious') {
49
+ alert = transactionAlerts[AlertType.DANGER];
50
+ }
51
+
52
+ if (simulation?.status === 'Success') {
53
+ tokenApprovals = processTokenApprovals(request, simulation.account_summary.exposures);
54
+ balanceChange = processBalanceChange(simulation.account_summary.assets_diffs);
55
+ }
56
+ } catch (error) {
57
+ console.error('processTransactionSimulation error', error);
58
+ alert = transactionAlerts[AlertType.WARNING];
70
59
  }
71
60
 
72
61
  return { alert, balanceChange, tokenApprovals };
@@ -251,44 +240,54 @@ export const processJsonRpcSimulation = async ({
251
240
  chainId: number;
252
241
  proxyApiUrl: string;
253
242
  }) => {
254
- const { validation, simulation } = await scanJsonRpc({
255
- proxyApiUrl,
256
- chainId,
257
- accountAddress,
258
- data: data as Blockaid.Evm.JsonRpcScanParams.Data,
259
- domain: dAppUrl,
260
- });
261
-
262
243
  let alert: Alert | undefined;
263
- if (!validation || validation.result_type === 'Error' || validation.result_type === 'Warning') {
264
- alert = {
265
- type: AlertType.WARNING,
266
- details: {
267
- title: 'Suspicious Transaction',
268
- description: 'Use caution, this transaction may be malicious.',
269
- },
270
- };
271
- } else if (validation.result_type === 'Malicious') {
272
- alert = {
273
- type: AlertType.DANGER,
274
- details: {
275
- title: 'Scam Transaction',
276
- description: 'This transaction is malicious, do not proceed.',
277
- actionTitles: {
278
- reject: 'Reject Transaction',
279
- proceed: 'Proceed Anyway',
280
- },
281
- },
282
- };
283
- }
284
-
285
244
  let balanceChange: BalanceChange | undefined;
286
245
  let tokenApprovals: TokenApprovals | undefined;
287
246
 
288
- if (simulation?.status === 'Success') {
289
- tokenApprovals = processTokenApprovals(request, simulation.account_summary.exposures);
290
- balanceChange = processBalanceChange(simulation.account_summary.assets_diffs);
247
+ try {
248
+ const { validation, simulation } = await scanJsonRpc({
249
+ proxyApiUrl,
250
+ chainId,
251
+ accountAddress,
252
+ data: data as Blockaid.Evm.JsonRpcScanParams.Data,
253
+ domain: dAppUrl,
254
+ });
255
+
256
+ if (!validation || validation.result_type === 'Error' || validation.result_type === 'Warning') {
257
+ alert = transactionAlerts[AlertType.WARNING];
258
+ } else if (validation.result_type === 'Malicious') {
259
+ alert = transactionAlerts[AlertType.DANGER];
260
+ }
261
+
262
+ if (simulation?.status === 'Success') {
263
+ tokenApprovals = processTokenApprovals(request, simulation.account_summary.exposures);
264
+ balanceChange = processBalanceChange(simulation.account_summary.assets_diffs);
265
+ }
266
+ } catch (error) {
267
+ console.error('processJsonRpcSimulation error', error);
268
+ alert = transactionAlerts[AlertType.WARNING];
291
269
  }
292
270
 
293
271
  return { alert, balanceChange, tokenApprovals };
294
272
  };
273
+
274
+ const transactionAlerts = {
275
+ [AlertType.WARNING]: {
276
+ type: AlertType.WARNING,
277
+ details: {
278
+ title: 'Suspicious Transaction',
279
+ description: 'Use caution, this transaction may be malicious.',
280
+ },
281
+ },
282
+ [AlertType.DANGER]: {
283
+ type: AlertType.DANGER,
284
+ details: {
285
+ title: 'Scam Transaction',
286
+ description: 'This transaction is malicious, do not proceed.',
287
+ actionTitles: {
288
+ reject: 'Reject Transaction',
289
+ proceed: 'Proceed Anyway',
290
+ },
291
+ },
292
+ },
293
+ };