@avalabs/evm-module 0.0.13 → 0.0.16

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 (57) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/.turbo/turbo-test.log +32 -8
  4. package/CHANGELOG.md +27 -0
  5. package/dist/index.cjs +29 -7
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.cts +10 -6
  8. package/dist/index.d.ts +10 -6
  9. package/dist/index.js +27 -8
  10. package/dist/index.js.map +1 -1
  11. package/manifest.json +1 -1
  12. package/package.json +16 -9
  13. package/src/contracts/openzeppelin/ERC1155.ts +440 -0
  14. package/src/contracts/openzeppelin/ERC20.ts +330 -0
  15. package/src/contracts/openzeppelin/ERC721.ts +420 -0
  16. package/src/contracts/openzeppelin/common.ts +131 -0
  17. package/src/contracts/openzeppelin/factories/ERC1155__factory.ts +388 -0
  18. package/src/contracts/openzeppelin/factories/ERC20__factory.ts +353 -0
  19. package/src/contracts/openzeppelin/factories/ERC721__factory.ts +413 -0
  20. package/src/contracts/openzeppelin/factories/index.ts +6 -0
  21. package/src/contracts/openzeppelin/index.ts +10 -0
  22. package/src/handlers/eth-send-transaction/eth-send-transaction.test.ts +3 -3
  23. package/src/handlers/eth-send-transaction/eth-send-transaction.ts +4 -4
  24. package/src/handlers/eth-sign/eth-sign.test.ts +217 -0
  25. package/src/handlers/eth-sign/eth-sign.ts +137 -0
  26. package/src/handlers/eth-sign/schemas/eth-sign-typed-data.ts +65 -0
  27. package/src/handlers/eth-sign/schemas/eth-sign.ts +9 -0
  28. package/src/handlers/eth-sign/schemas/parse-request-params/fixture.ts +47 -0
  29. package/src/handlers/eth-sign/schemas/parse-request-params/parse-request-params.test.ts +284 -0
  30. package/src/handlers/eth-sign/schemas/parse-request-params/parse-request-params.ts +94 -0
  31. package/src/handlers/eth-sign/schemas/parse-request-params.ts +90 -0
  32. package/src/handlers/eth-sign/schemas/personal-sign.ts +12 -0
  33. package/src/handlers/eth-sign/schemas/shared.ts +5 -0
  34. package/src/handlers/eth-sign/utils/beautify-message/beautify-message.test.ts +29 -0
  35. package/src/handlers/eth-sign/utils/beautify-message/beautify-message.ts +134 -0
  36. package/src/handlers/eth-sign/utils/is-typed-data-valid.ts +26 -0
  37. package/src/handlers/eth-sign/utils/typeguards.ts +10 -0
  38. package/src/handlers/get-balances/evm-balance-service/get-erc20-balances.test.ts +78 -0
  39. package/src/handlers/get-balances/evm-balance-service/get-erc20-balances.ts +85 -0
  40. package/src/handlers/get-balances/evm-balance-service/get-native-token-balances.test.ts +102 -0
  41. package/src/handlers/get-balances/evm-balance-service/get-native-token-balances.ts +54 -0
  42. package/src/handlers/get-balances/get-balances.test.ts +252 -0
  43. package/src/handlers/get-balances/get-balances.ts +109 -0
  44. package/src/handlers/get-balances/glacier-balance-service/get-erc20-balances.test.ts +76 -0
  45. package/src/handlers/get-balances/glacier-balance-service/get-erc20-balances.ts +107 -0
  46. package/src/handlers/get-balances/glacier-balance-service/get-native-token-balances.test.ts +61 -0
  47. package/src/handlers/get-balances/glacier-balance-service/get-native-token-balances.ts +46 -0
  48. package/src/handlers/get-network-fee/get-network-fee.test.ts +0 -1
  49. package/src/handlers/get-network-fee/get-network-fee.ts +0 -1
  50. package/src/handlers/get-transaction-history/converters/evm-transaction-converter/get-transaction-from-glacier.test.ts +72 -59
  51. package/src/handlers/get-transaction-history/converters/evm-transaction-converter/get-transactions-from-glacier.ts +4 -11
  52. package/src/handlers/get-transaction-history/get-transaction-history.test.ts +7 -2
  53. package/src/handlers/get-transaction-history/get-transaction-history.ts +13 -3
  54. package/src/index.ts +1 -0
  55. package/src/module.ts +29 -7
  56. package/src/services/glacier-service/glacier-service.ts +259 -0
  57. package/tsconfig.json +7 -1
@@ -0,0 +1,78 @@
1
+ import { BN } from 'bn.js';
2
+ import { getErc20Balances } from './get-erc20-balances';
3
+ import { ethers } from 'ethers';
4
+
5
+ describe('get-erc20-balances', () => {
6
+ it('should return erc20 token balances', async () => {
7
+ jest.spyOn(ethers, 'Contract').mockImplementation(() => {
8
+ return {
9
+ balanceOf: jest.fn().mockResolvedValue(new BN('1000000000000000000')),
10
+ } as never;
11
+ });
12
+
13
+ const balance = getErc20Balances({
14
+ tokens: [
15
+ {
16
+ address: '0x123',
17
+ name: 'Ethereum',
18
+ symbol: 'ETH',
19
+ decimals: 18,
20
+ logoUri: 'https://example.com/logo.png',
21
+ contractType: 'ERC-20',
22
+ },
23
+ ],
24
+ provider: {
25
+ getBalance: jest.fn().mockResolvedValue(new BN('1000000000000000000')),
26
+ } as never,
27
+ tokenService: {
28
+ getPricesByAddresses: jest.fn().mockResolvedValue({
29
+ '123': {
30
+ USD: {
31
+ price: 1000,
32
+ marketCap: 0,
33
+ vol24: 0,
34
+ change24: 0,
35
+ },
36
+ },
37
+ }),
38
+ } as never,
39
+ address: '0x123',
40
+ currency: 'USD',
41
+ network: {
42
+ pricingProviders: {
43
+ coingecko: {
44
+ nativeTokenId: '123',
45
+ assetPlatformId: '123',
46
+ },
47
+ },
48
+ networkToken: {
49
+ address: '0x123',
50
+ name: 'Ethereum',
51
+ symbol: 'ETH',
52
+ decimals: 18,
53
+ logoUri: 'https://example.com/logo.png',
54
+ },
55
+ } as never,
56
+ });
57
+
58
+ expect(balance).resolves.toEqual({
59
+ '0x123': {
60
+ address: '0x123',
61
+ name: 'Ethereum',
62
+ symbol: 'ETH',
63
+ decimals: 18,
64
+ logoUri: 'https://example.com/logo.png',
65
+ balance: new BN('1000000000000000000'),
66
+ balanceCurrencyDisplayValue: '1000.00',
67
+ balanceDisplayValue: '1',
68
+ balanceInCurrency: 1000,
69
+ priceInCurrency: 1000,
70
+ contractType: 'ERC-20',
71
+ type: 'ERC20',
72
+ change24: 0,
73
+ marketCap: 0,
74
+ vol24: 0,
75
+ },
76
+ });
77
+ });
78
+ });
@@ -0,0 +1,85 @@
1
+ import { numberToBN, bnToBig, balanceToDisplayValue } from '@avalabs/utils-sdk';
2
+ import { TokenType, type Network, type NetworkContractToken, type TokenWithBalance } from '@avalabs/vm-module-types';
3
+ import { ethers, type Provider } from 'ethers';
4
+ import ERC20 from '@openzeppelin/contracts/build/contracts/ERC20.json';
5
+ import type { TokenService } from '@internal/utils';
6
+ import { VsCurrencyType } from '@avalabs/coingecko-sdk';
7
+ import BN from 'bn.js';
8
+
9
+ const DEFAULT_DECIMALS = 18;
10
+
11
+ export const getErc20Balances = async ({
12
+ provider,
13
+ tokenService,
14
+ address: userAddress,
15
+ currency,
16
+ tokens,
17
+ network,
18
+ }: {
19
+ provider: Provider;
20
+ tokenService: TokenService;
21
+ address: string;
22
+ currency: string;
23
+ tokens: NetworkContractToken[];
24
+ network: Network;
25
+ }): Promise<Record<string, TokenWithBalance>> => {
26
+ const coingeckoPlatformId = network.pricingProviders?.coingecko.assetPlatformId;
27
+ const coingeckoTokenId = network.pricingProviders?.coingecko.nativeTokenId;
28
+ const tokenAddresses = tokens.map((token) => token.address);
29
+
30
+ const tokensBalances = await Promise.allSettled(
31
+ tokens.map(async (token) => {
32
+ const contract = new ethers.Contract(token.address, ERC20.abi, provider);
33
+ const balanceBig = await contract.balanceOf?.(userAddress);
34
+ const balance = new BN(balanceBig) || numberToBN(0, token.decimals || DEFAULT_DECIMALS);
35
+
36
+ const tokenWithBalance = {
37
+ ...token,
38
+ balance,
39
+ };
40
+
41
+ return tokenWithBalance;
42
+ }),
43
+ ).then((res) => {
44
+ return res.reduce<(NetworkContractToken & { balance: BN })[]>((acc, result) => {
45
+ return result.status === 'fulfilled' && !result.value.balance.isZero() ? [...acc, result.value] : acc;
46
+ }, []);
47
+ });
48
+
49
+ if (!tokensBalances.length) return {};
50
+
51
+ const simplePriceResponse =
52
+ (coingeckoPlatformId &&
53
+ (await tokenService.getPricesByAddresses(tokenAddresses, coingeckoPlatformId, currency as VsCurrencyType))) ||
54
+ {};
55
+
56
+ return tokensBalances.reduce(
57
+ (acc, token) => {
58
+ const priceInCurrency = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.price ?? 0;
59
+ const marketCap = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.marketCap ?? 0;
60
+ const vol24 = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.vol24 ?? 0;
61
+ const change24 = simplePriceResponse?.[coingeckoTokenId ?? '']?.[currency]?.change24 ?? 0;
62
+
63
+ const balanceInCurrency = bnToBig(token.balance, token.decimals).mul(priceInCurrency).toNumber();
64
+ const balanceDisplayValue = balanceToDisplayValue(token.balance, token.decimals);
65
+ const balanceCurrencyDisplayValue = balanceInCurrency.toFixed(2);
66
+
67
+ return {
68
+ ...acc,
69
+ [token.address.toLowerCase()]: {
70
+ ...token,
71
+ type: TokenType.ERC20,
72
+ balance: token.balance,
73
+ balanceDisplayValue,
74
+ balanceInCurrency,
75
+ balanceCurrencyDisplayValue,
76
+ priceInCurrency,
77
+ marketCap,
78
+ change24,
79
+ vol24,
80
+ },
81
+ };
82
+ },
83
+ {} as Record<string, TokenWithBalance>,
84
+ );
85
+ };
@@ -0,0 +1,102 @@
1
+ import { BN } from 'bn.js';
2
+ import { getNativeTokenBalances } from './get-native-token-balances';
3
+
4
+ describe('get-native-token-balances', () => {
5
+ it('should return native token balances', async () => {
6
+ const balance = getNativeTokenBalances({
7
+ provider: {
8
+ getBalance: jest.fn().mockResolvedValue(new BN('1000000000000000000')),
9
+ } as never,
10
+ tokenService: {
11
+ getSimplePrice: jest.fn().mockResolvedValue({
12
+ '123': {
13
+ USD: {
14
+ price: 1000,
15
+ marketCap: 0,
16
+ vol24: 0,
17
+ change24: 0,
18
+ },
19
+ },
20
+ }),
21
+ } as never,
22
+ address: '0x123',
23
+ currency: 'USD',
24
+ network: {
25
+ pricingProviders: {
26
+ coingecko: {
27
+ nativeTokenId: '123',
28
+ },
29
+ },
30
+ networkToken: {
31
+ address: '0x123',
32
+ name: 'Ethereum',
33
+ symbol: 'ETH',
34
+ decimals: 18,
35
+ logoUri: 'https://example.com/logo.png',
36
+ },
37
+ } as never,
38
+ });
39
+
40
+ expect(balance).resolves.toEqual({
41
+ address: '0x123',
42
+ name: 'Ethereum',
43
+ symbol: 'ETH',
44
+ decimals: 18,
45
+ logoUri: 'https://example.com/logo.png',
46
+ balance: new BN('1000000000000000000'),
47
+ balanceCurrencyDisplayValue: '1000.00',
48
+ balanceDisplayValue: '1',
49
+ balanceInCurrency: 1000,
50
+ priceInCurrency: 1000,
51
+ coingeckoId: '123',
52
+ type: 'NATIVE',
53
+ marketCap: 0,
54
+ vol24: 0,
55
+ change24: 0,
56
+ });
57
+ });
58
+
59
+ it('should return native token object without balance data', async () => {
60
+ const balance = getNativeTokenBalances({
61
+ provider: {
62
+ getBalance: jest.fn().mockResolvedValue(new BN('0')),
63
+ } as never,
64
+ tokenService: {
65
+ getSimplePrice: jest.fn().mockResolvedValue({}),
66
+ } as never,
67
+ address: '0x123',
68
+ currency: 'USD',
69
+ network: {
70
+ pricingProviders: {
71
+ coingecko: {
72
+ nativeTokenId: '123',
73
+ },
74
+ },
75
+ networkToken: {
76
+ address: '0x123',
77
+ name: 'Ethereum',
78
+ symbol: 'ETH',
79
+ decimals: 18,
80
+ logoUri: 'https://example.com/logo.png',
81
+ },
82
+ } as never,
83
+ });
84
+ expect(balance).resolves.toEqual({
85
+ address: '0x123',
86
+ name: 'Ethereum',
87
+ symbol: 'ETH',
88
+ decimals: 18,
89
+ logoUri: 'https://example.com/logo.png',
90
+ balance: new BN('0'),
91
+ balanceCurrencyDisplayValue: '0.00',
92
+ balanceDisplayValue: '0',
93
+ balanceInCurrency: 0,
94
+ priceInCurrency: 0,
95
+ coingeckoId: '123',
96
+ type: 'NATIVE',
97
+ marketCap: 0,
98
+ vol24: 0,
99
+ change24: 0,
100
+ });
101
+ });
102
+ });
@@ -0,0 +1,54 @@
1
+ import { balanceToDisplayValue, bigToBN, bigintToBig, bnToBig } from '@avalabs/utils-sdk';
2
+ import { TokenType, type Network, type NetworkTokenWithBalance } from '@avalabs/vm-module-types';
3
+ import type { VsCurrencyType } from '@avalabs/coingecko-sdk';
4
+ import { TokenService } from '@internal/utils';
5
+ import type { Provider } from 'ethers';
6
+
7
+ export const getNativeTokenBalances = async ({
8
+ provider,
9
+ tokenService,
10
+ address,
11
+ currency,
12
+ network,
13
+ }: {
14
+ provider: Provider;
15
+ tokenService: TokenService;
16
+ address: string;
17
+ currency: string;
18
+ network: Network;
19
+ }): Promise<NetworkTokenWithBalance> => {
20
+ const coingeckoTokenId = network.pricingProviders?.coingecko.nativeTokenId;
21
+ const networkToken = network.networkToken;
22
+ const simplePriceResponse = coingeckoTokenId
23
+ ? await tokenService.getSimplePrice({
24
+ coinIds: [coingeckoTokenId],
25
+ currencies: [currency] as VsCurrencyType[],
26
+ })
27
+ : {};
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;
33
+
34
+ const balanceBigint = await provider.getBalance(address);
35
+ const balaceBig = bigintToBig(balanceBigint, networkToken.decimals);
36
+ const balance = bigToBN(balaceBig, networkToken.decimals);
37
+ const balanceDisplayValue = balanceToDisplayValue(balance, networkToken.decimals);
38
+ const balanceInCurrency = bnToBig(balance, networkToken.decimals).mul(priceInCurrency).toNumber();
39
+ const balanceCurrencyDisplayValue = balanceInCurrency.toFixed(2);
40
+
41
+ return {
42
+ ...networkToken,
43
+ coingeckoId: coingeckoTokenId ?? '',
44
+ type: TokenType.NATIVE,
45
+ balance,
46
+ balanceDisplayValue,
47
+ balanceInCurrency,
48
+ balanceCurrencyDisplayValue,
49
+ priceInCurrency,
50
+ marketCap,
51
+ vol24,
52
+ change24,
53
+ };
54
+ };
@@ -0,0 +1,252 @@
1
+ import { TokenType } from '@avalabs/vm-module-types';
2
+ import { getBalances } from './get-balances';
3
+ import * as GlacierNativeToken from './glacier-balance-service/get-native-token-balances';
4
+ import { BN } from 'bn.js';
5
+ import * as GlacierERC20Token from './glacier-balance-service/get-erc20-balances';
6
+ import * as EvmNativeToken from './evm-balance-service/get-native-token-balances';
7
+ import * as EvmERC20Token from './evm-balance-service/get-erc20-balances';
8
+ import type { EvmGlacierService } from '../../services/glacier-service/glacier-service';
9
+
10
+ describe('get-balances', () => {
11
+ it('should get balances from glacier', async () => {
12
+ const mockGlacierService: EvmGlacierService = {
13
+ ...expect.any(Object),
14
+ isNetworkSupported: () => true,
15
+ isHealthy: () => true,
16
+ };
17
+ jest.spyOn(GlacierNativeToken, 'getNativeTokenBalances').mockImplementationOnce(async () => {
18
+ return {
19
+ name: 'Ether',
20
+ symbol: 'ETH',
21
+ decimals: 18,
22
+ type: TokenType.NATIVE,
23
+ logoUri:
24
+ 'https://images.ctfassets.net/gcj8jwzm6086/6l56QLVZmvacuBfjHBTThP/791d743dd2c526692562780c2325fedf/eth-circle__1_.svg',
25
+ balance: new BN(1),
26
+ balanceDisplayValue: '1',
27
+ balanceInCurrency: 1,
28
+ balanceCurrencyDisplayValue: '1',
29
+ priceInCurrency: 1,
30
+ marketCap: 0,
31
+ vol24: 0,
32
+ change24: 0,
33
+ coingeckoId: '',
34
+ };
35
+ });
36
+
37
+ jest.spyOn(GlacierERC20Token, 'getErc20Balances').mockImplementationOnce(async () => {
38
+ return {
39
+ '0x123': {
40
+ chainId: 1,
41
+ address: '0x123',
42
+ name: 'DAI',
43
+ symbol: 'DAI',
44
+ decimals: 18,
45
+ logoUri: 'https://s3.us-east-2.amazonaws.com/nomics-api/static/images/currencies/dai.svg',
46
+ balance: new BN(1),
47
+ balanceCurrencyDisplayValue: '1',
48
+ balanceDisplayValue: '1',
49
+ balanceInCurrency: 1,
50
+ priceInCurrency: 1,
51
+ contractType: 'ERC-20',
52
+ type: TokenType.ERC20,
53
+ change24: 0,
54
+ marketCap: 0,
55
+ vol24: 0,
56
+ },
57
+ };
58
+ });
59
+ const balances = await getBalances({
60
+ addresses: ['0x123'],
61
+ currency: 'USD',
62
+ network: {
63
+ chainId: 1,
64
+ chainName: 'Ethereum',
65
+ isTestnet: false,
66
+ networkToken: {
67
+ name: 'Ether',
68
+ decimals: 18,
69
+ symbol: 'ETH',
70
+ description:
71
+ 'Ether is used to pay for transaction fees and computational services on Etherum. Users can send Ether to other users, and developers can write smart contracts that receive, hold, and send Ether.',
72
+ logoUri:
73
+ 'https://images.ctfassets.net/gcj8jwzm6086/6l56QLVZmvacuBfjHBTThP/791d743dd2c526692562780c2325fedf/eth-circle__1_.svg',
74
+ },
75
+ pricingProviders: { coingecko: { nativeTokenId: 'ethereum', assetPlatformId: 'ethereum' } },
76
+ rpcUrl: 'https://proxy-api.avax.network/proxy/infura/mainnet',
77
+ utilityAddresses: { multicall: '0x5ba1e12693dc8f9c48aad8770482f4739beed696' },
78
+ },
79
+ proxyApiUrl: 'proxyApiUrl',
80
+ glacierService: mockGlacierService,
81
+ });
82
+ expect(balances).toEqual({
83
+ '0x123': {
84
+ '0x123': {
85
+ address: '0x123',
86
+ balance: new BN(1),
87
+ balanceCurrencyDisplayValue: '1',
88
+ balanceDisplayValue: '1',
89
+ balanceInCurrency: 1,
90
+ chainId: 1,
91
+ change24: 0,
92
+ contractType: 'ERC-20',
93
+ decimals: 18,
94
+ logoUri: 'https://s3.us-east-2.amazonaws.com/nomics-api/static/images/currencies/dai.svg',
95
+ marketCap: 0,
96
+ name: 'DAI',
97
+ priceInCurrency: 1,
98
+ symbol: 'DAI',
99
+ type: 'ERC20',
100
+ vol24: 0,
101
+ },
102
+ ETH: {
103
+ balance: new BN(1),
104
+ balanceCurrencyDisplayValue: '1',
105
+ balanceDisplayValue: '1',
106
+ balanceInCurrency: 1,
107
+ change24: 0,
108
+ coingeckoId: '',
109
+ decimals: 18,
110
+ logoUri:
111
+ 'https://images.ctfassets.net/gcj8jwzm6086/6l56QLVZmvacuBfjHBTThP/791d743dd2c526692562780c2325fedf/eth-circle__1_.svg',
112
+ marketCap: 0,
113
+ name: 'Ether',
114
+ priceInCurrency: 1,
115
+ symbol: 'ETH',
116
+ type: 'NATIVE',
117
+ vol24: 0,
118
+ },
119
+ },
120
+ });
121
+ });
122
+
123
+ it('should get balances from evm', async () => {
124
+ const mockGlacierService: EvmGlacierService = {
125
+ ...expect.any(Object),
126
+ isNetworkSupported: () => false,
127
+ isHealthy: () => false,
128
+ };
129
+ global.fetch = jest.fn(() =>
130
+ Promise.resolve({
131
+ ok: true,
132
+ json: () =>
133
+ Promise.resolve([
134
+ {
135
+ chainId: 2,
136
+ address: '0x456',
137
+ name: 'DAI2',
138
+ symbol: 'DAI2',
139
+ decimals: 18,
140
+ logoUri: 'https://s3.us-east-2.amazonaws.com/nomics-api/static/images/currencies/dai.svg',
141
+ contractType: 'ERC-20',
142
+ type: TokenType.ERC20,
143
+ },
144
+ ]),
145
+ }),
146
+ ) as jest.Mock;
147
+ jest.spyOn(EvmNativeToken, 'getNativeTokenBalances').mockImplementationOnce(async () => {
148
+ return {
149
+ name: 'Ether2',
150
+ symbol: 'ETH2',
151
+ decimals: 18,
152
+ type: TokenType.NATIVE,
153
+ logoUri:
154
+ 'https://images.ctfassets.net/gcj8jwzm6086/6l56QLVZmvacuBfjHBTThP/791d743dd2c526692562780c2325fedf/eth-circle__1_.svg',
155
+ balance: new BN(1),
156
+ balanceDisplayValue: '1',
157
+ balanceInCurrency: 1,
158
+ balanceCurrencyDisplayValue: '1',
159
+ priceInCurrency: 1,
160
+ marketCap: 0,
161
+ vol24: 0,
162
+ change24: 0,
163
+ coingeckoId: '',
164
+ };
165
+ });
166
+
167
+ jest.spyOn(EvmERC20Token, 'getErc20Balances').mockImplementationOnce(async () => {
168
+ return {
169
+ '0x456': {
170
+ chainId: 2,
171
+ address: '0x456',
172
+ name: 'DAI2',
173
+ symbol: 'DAI2',
174
+ decimals: 18,
175
+ logoUri: 'https://s3.us-east-2.amazonaws.com/nomics-api/static/images/currencies/dai.svg',
176
+ balance: new BN(1),
177
+ balanceCurrencyDisplayValue: '1',
178
+ balanceDisplayValue: '1',
179
+ balanceInCurrency: 1,
180
+ priceInCurrency: 1,
181
+ contractType: 'ERC-20',
182
+ type: TokenType.ERC20,
183
+ change24: 0,
184
+ marketCap: 0,
185
+ vol24: 0,
186
+ },
187
+ };
188
+ });
189
+ const balances = await getBalances({
190
+ addresses: ['0x456'],
191
+ currency: 'USD',
192
+ network: {
193
+ chainId: 2,
194
+ chainName: '2',
195
+ isTestnet: false,
196
+ networkToken: {
197
+ name: 'Ether2',
198
+ decimals: 18,
199
+ symbol: 'ETH2',
200
+ description:
201
+ 'Ether is used to pay for transaction fees and computational services on Etherum. Users can send Ether to other users, and developers can write smart contracts that receive, hold, and send Ether.',
202
+ logoUri:
203
+ 'https://images.ctfassets.net/gcj8jwzm6086/6l56QLVZmvacuBfjHBTThP/791d743dd2c526692562780c2325fedf/eth-circle__1_.svg',
204
+ },
205
+ pricingProviders: { coingecko: { nativeTokenId: 'ethereum', assetPlatformId: 'ethereum' } },
206
+ rpcUrl: 'https://proxy-api.avax.network/proxy/infura/mainnet',
207
+ utilityAddresses: { multicall: '0x5ba1e12693dc8f9c48aad8770482f4739beed696' },
208
+ },
209
+ proxyApiUrl: 'proxyApiUrl',
210
+ glacierService: mockGlacierService,
211
+ });
212
+ expect(balances).toEqual({
213
+ '0x456': {
214
+ '0x456': {
215
+ address: '0x456',
216
+ balance: new BN(1),
217
+ balanceCurrencyDisplayValue: '1',
218
+ balanceDisplayValue: '1',
219
+ balanceInCurrency: 1,
220
+ chainId: 2,
221
+ change24: 0,
222
+ contractType: 'ERC-20',
223
+ decimals: 18,
224
+ logoUri: 'https://s3.us-east-2.amazonaws.com/nomics-api/static/images/currencies/dai.svg',
225
+ marketCap: 0,
226
+ name: 'DAI2',
227
+ priceInCurrency: 1,
228
+ symbol: 'DAI2',
229
+ type: 'ERC20',
230
+ vol24: 0,
231
+ },
232
+ ETH2: {
233
+ balance: new BN(1),
234
+ balanceCurrencyDisplayValue: '1',
235
+ balanceDisplayValue: '1',
236
+ balanceInCurrency: 1,
237
+ change24: 0,
238
+ coingeckoId: '',
239
+ decimals: 18,
240
+ logoUri:
241
+ 'https://images.ctfassets.net/gcj8jwzm6086/6l56QLVZmvacuBfjHBTThP/791d743dd2c526692562780c2325fedf/eth-circle__1_.svg',
242
+ marketCap: 0,
243
+ name: 'Ether2',
244
+ priceInCurrency: 1,
245
+ symbol: 'ETH2',
246
+ type: 'NATIVE',
247
+ vol24: 0,
248
+ },
249
+ },
250
+ });
251
+ });
252
+ });
@@ -0,0 +1,109 @@
1
+ import type { GetBalancesResponse, GetBalancesParams, Storage } from '@avalabs/vm-module-types';
2
+ import { getErc20Balances } from './evm-balance-service/get-erc20-balances';
3
+ import { TokenService } from '@internal/utils';
4
+ import { getProvider } from '../../utils/get-provider';
5
+ import { getTokens } from '../get-tokens/get-tokens';
6
+ import { getNativeTokenBalances } from './evm-balance-service/get-native-token-balances';
7
+ import { getNativeTokenBalances as getNativeTokenBalancesFromGlacier } from './glacier-balance-service/get-native-token-balances';
8
+ import { getErc20Balances as getErc20BalancesFromGlacier } from './glacier-balance-service/get-erc20-balances';
9
+ import type { EvmGlacierService } from '../../services/glacier-service/glacier-service';
10
+
11
+ export const getBalances = async ({
12
+ addresses,
13
+ currency,
14
+ network,
15
+ proxyApiUrl,
16
+ customTokens = [],
17
+ storage,
18
+ glacierService,
19
+ }: GetBalancesParams & {
20
+ proxyApiUrl: string;
21
+ glacierService: EvmGlacierService;
22
+ storage?: Storage;
23
+ }): Promise<GetBalancesResponse> => {
24
+ const chainId = network.chainId;
25
+ const isNetworkSupported = await glacierService.isNetworkSupported(network.chainId);
26
+ const isHealthy = glacierService.isHealthy();
27
+
28
+ let balances = [];
29
+ if (isHealthy && isNetworkSupported) {
30
+ balances = await Promise.allSettled(
31
+ addresses.map(async (address) => {
32
+ const nativeToken = await getNativeTokenBalancesFromGlacier({
33
+ address,
34
+ currency,
35
+ chainId,
36
+ glacierService,
37
+ });
38
+
39
+ const erc20Tokens = await getErc20BalancesFromGlacier({
40
+ customTokens,
41
+ glacierService,
42
+ currency,
43
+ chainId,
44
+ address,
45
+ });
46
+
47
+ return {
48
+ address,
49
+ balances: {
50
+ [nativeToken.symbol]: nativeToken,
51
+ ...erc20Tokens,
52
+ },
53
+ };
54
+ }),
55
+ );
56
+ } else {
57
+ const tokenService = new TokenService({ storage, proxyApiUrl });
58
+ const tokens = await getTokens({ chainId: Number(chainId), proxyApiUrl });
59
+ const allTokens = [...tokens, ...customTokens];
60
+ const provider = getProvider({
61
+ chainId,
62
+ chainName: network.chainName,
63
+ rpcUrl: network.rpcUrl,
64
+ multiContractAddress: network.utilityAddresses?.multicall,
65
+ });
66
+
67
+ balances = await Promise.allSettled(
68
+ addresses.map(async (address) => {
69
+ const nativeToken = await getNativeTokenBalances({
70
+ network,
71
+ tokenService,
72
+ address,
73
+ currency,
74
+ provider,
75
+ });
76
+
77
+ const erc20Tokens = await getErc20Balances({
78
+ provider,
79
+ network,
80
+ tokenService,
81
+ address,
82
+ currency,
83
+ tokens: allTokens,
84
+ });
85
+
86
+ return {
87
+ address,
88
+ balances: {
89
+ [nativeToken.symbol]: nativeToken,
90
+ ...erc20Tokens,
91
+ },
92
+ };
93
+ }),
94
+ );
95
+ }
96
+
97
+ const filterBalances = balances.reduce((acc, result) => {
98
+ if (result.status === 'rejected') {
99
+ return acc;
100
+ }
101
+
102
+ return {
103
+ ...acc,
104
+ [result.value.address]: result.value.balances,
105
+ };
106
+ }, {} as GetBalancesResponse);
107
+
108
+ return filterBalances;
109
+ };