@avalabs/evm-module 0.0.16 → 0.0.18
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/.turbo/turbo-build.log +10 -10
- package/.turbo/turbo-lint.log +1 -1
- package/.turbo/turbo-test.log +70 -24
- package/CHANGELOG.md +15 -0
- package/dist/index.cjs +23 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +20 -19
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/src/constants.ts +1 -0
- package/src/handlers/eth-send-transaction/eth-send-transaction.test.ts +232 -1
- package/src/handlers/eth-send-transaction/eth-send-transaction.ts +24 -5
- package/src/handlers/eth-sign/eth-sign.test.ts +176 -35
- package/src/handlers/eth-sign/eth-sign.ts +29 -8
- package/src/handlers/get-balances/evm-balance-service/get-erc20-balances.test.ts +2 -2
- package/src/handlers/get-balances/evm-balance-service/get-erc20-balances.ts +4 -6
- package/src/handlers/get-balances/get-balances.test.ts +0 -5
- package/src/handlers/get-balances/get-balances.ts +14 -3
- package/src/handlers/get-balances/glacier-balance-service/get-erc20-balances.test.ts +0 -1
- package/src/handlers/get-balances/glacier-balance-service/get-erc20-balances.ts +10 -7
- package/src/handlers/get-tokens/get-tokens.test.ts +6 -6
- package/src/module.ts +2 -0
- package/src/types.ts +9 -0
- package/src/utils/parse-erc20-transaction-type.ts +35 -0
- package/src/utils/process-transaction-simulation.test.ts +105 -0
- package/src/utils/process-transaction-simulation.ts +293 -0
- package/src/utils/scan-transaction.ts +63 -0
- package/src/handlers/eth-sign/schemas/parse-request-params.ts +0 -90
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
type DisplayData,
|
|
6
6
|
type RpcRequest,
|
|
7
7
|
RpcMethod,
|
|
8
|
-
|
|
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
|
|
53
|
+
let alert: Alert | undefined;
|
|
50
54
|
|
|
51
55
|
if (typedDataValidationResult && !typedDataValidationResult.isValid) {
|
|
52
|
-
|
|
53
|
-
type:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BN } from 'bn.js';
|
|
2
2
|
import { getErc20Balances } from './get-erc20-balances';
|
|
3
3
|
import { ethers } from 'ethers';
|
|
4
|
+
import { TokenType } from '@avalabs/vm-module-types';
|
|
4
5
|
|
|
5
6
|
describe('get-erc20-balances', () => {
|
|
6
7
|
it('should return erc20 token balances', async () => {
|
|
@@ -18,7 +19,7 @@ describe('get-erc20-balances', () => {
|
|
|
18
19
|
symbol: 'ETH',
|
|
19
20
|
decimals: 18,
|
|
20
21
|
logoUri: 'https://example.com/logo.png',
|
|
21
|
-
|
|
22
|
+
type: TokenType.ERC20,
|
|
22
23
|
},
|
|
23
24
|
],
|
|
24
25
|
provider: {
|
|
@@ -67,7 +68,6 @@ describe('get-erc20-balances', () => {
|
|
|
67
68
|
balanceDisplayValue: '1',
|
|
68
69
|
balanceInCurrency: 1000,
|
|
69
70
|
priceInCurrency: 1000,
|
|
70
|
-
contractType: 'ERC-20',
|
|
71
71
|
type: 'ERC20',
|
|
72
72
|
change24: 0,
|
|
73
73
|
marketCap: 0,
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { numberToBN, bnToBig, balanceToDisplayValue } from '@avalabs/utils-sdk';
|
|
2
|
-
import { TokenType, type
|
|
2
|
+
import { TokenType, type ERC20Token, type Network, type TokenWithBalance } from '@avalabs/vm-module-types';
|
|
3
3
|
import { ethers, type Provider } from 'ethers';
|
|
4
4
|
import ERC20 from '@openzeppelin/contracts/build/contracts/ERC20.json';
|
|
5
5
|
import type { TokenService } from '@internal/utils';
|
|
6
6
|
import { VsCurrencyType } from '@avalabs/coingecko-sdk';
|
|
7
7
|
import BN from 'bn.js';
|
|
8
8
|
|
|
9
|
-
const DEFAULT_DECIMALS = 18;
|
|
10
|
-
|
|
11
9
|
export const getErc20Balances = async ({
|
|
12
10
|
provider,
|
|
13
11
|
tokenService,
|
|
@@ -20,7 +18,7 @@ export const getErc20Balances = async ({
|
|
|
20
18
|
tokenService: TokenService;
|
|
21
19
|
address: string;
|
|
22
20
|
currency: string;
|
|
23
|
-
tokens:
|
|
21
|
+
tokens: ERC20Token[];
|
|
24
22
|
network: Network;
|
|
25
23
|
}): Promise<Record<string, TokenWithBalance>> => {
|
|
26
24
|
const coingeckoPlatformId = network.pricingProviders?.coingecko.assetPlatformId;
|
|
@@ -31,7 +29,7 @@ export const getErc20Balances = async ({
|
|
|
31
29
|
tokens.map(async (token) => {
|
|
32
30
|
const contract = new ethers.Contract(token.address, ERC20.abi, provider);
|
|
33
31
|
const balanceBig = await contract.balanceOf?.(userAddress);
|
|
34
|
-
const balance = new BN(balanceBig) || numberToBN(0, token.decimals
|
|
32
|
+
const balance = new BN(balanceBig) || numberToBN(0, token.decimals);
|
|
35
33
|
|
|
36
34
|
const tokenWithBalance = {
|
|
37
35
|
...token,
|
|
@@ -41,7 +39,7 @@ export const getErc20Balances = async ({
|
|
|
41
39
|
return tokenWithBalance;
|
|
42
40
|
}),
|
|
43
41
|
).then((res) => {
|
|
44
|
-
return res.reduce<(
|
|
42
|
+
return res.reduce<(ERC20Token & { balance: BN })[]>((acc, result) => {
|
|
45
43
|
return result.status === 'fulfilled' && !result.value.balance.isZero() ? [...acc, result.value] : acc;
|
|
46
44
|
}, []);
|
|
47
45
|
});
|
|
@@ -48,7 +48,6 @@ describe('get-balances', () => {
|
|
|
48
48
|
balanceDisplayValue: '1',
|
|
49
49
|
balanceInCurrency: 1,
|
|
50
50
|
priceInCurrency: 1,
|
|
51
|
-
contractType: 'ERC-20',
|
|
52
51
|
type: TokenType.ERC20,
|
|
53
52
|
change24: 0,
|
|
54
53
|
marketCap: 0,
|
|
@@ -89,7 +88,6 @@ describe('get-balances', () => {
|
|
|
89
88
|
balanceInCurrency: 1,
|
|
90
89
|
chainId: 1,
|
|
91
90
|
change24: 0,
|
|
92
|
-
contractType: 'ERC-20',
|
|
93
91
|
decimals: 18,
|
|
94
92
|
logoUri: 'https://s3.us-east-2.amazonaws.com/nomics-api/static/images/currencies/dai.svg',
|
|
95
93
|
marketCap: 0,
|
|
@@ -138,7 +136,6 @@ describe('get-balances', () => {
|
|
|
138
136
|
symbol: 'DAI2',
|
|
139
137
|
decimals: 18,
|
|
140
138
|
logoUri: 'https://s3.us-east-2.amazonaws.com/nomics-api/static/images/currencies/dai.svg',
|
|
141
|
-
contractType: 'ERC-20',
|
|
142
139
|
type: TokenType.ERC20,
|
|
143
140
|
},
|
|
144
141
|
]),
|
|
@@ -178,7 +175,6 @@ describe('get-balances', () => {
|
|
|
178
175
|
balanceDisplayValue: '1',
|
|
179
176
|
balanceInCurrency: 1,
|
|
180
177
|
priceInCurrency: 1,
|
|
181
|
-
contractType: 'ERC-20',
|
|
182
178
|
type: TokenType.ERC20,
|
|
183
179
|
change24: 0,
|
|
184
180
|
marketCap: 0,
|
|
@@ -219,7 +215,6 @@ describe('get-balances', () => {
|
|
|
219
215
|
balanceInCurrency: 1,
|
|
220
216
|
chainId: 2,
|
|
221
217
|
change24: 0,
|
|
222
|
-
contractType: 'ERC-20',
|
|
223
218
|
decimals: 18,
|
|
224
219
|
logoUri: 'https://s3.us-east-2.amazonaws.com/nomics-api/static/images/currencies/dai.svg',
|
|
225
220
|
marketCap: 0,
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
type GetBalancesResponse,
|
|
3
|
+
type GetBalancesParams,
|
|
4
|
+
type Storage,
|
|
5
|
+
TokenType,
|
|
6
|
+
type NetworkContractToken,
|
|
7
|
+
type ERC20Token,
|
|
8
|
+
} from '@avalabs/vm-module-types';
|
|
2
9
|
import { getErc20Balances } from './evm-balance-service/get-erc20-balances';
|
|
3
10
|
import { TokenService } from '@internal/utils';
|
|
4
11
|
import { getProvider } from '../../utils/get-provider';
|
|
@@ -37,7 +44,7 @@ export const getBalances = async ({
|
|
|
37
44
|
});
|
|
38
45
|
|
|
39
46
|
const erc20Tokens = await getErc20BalancesFromGlacier({
|
|
40
|
-
customTokens,
|
|
47
|
+
customTokens: customTokens.filter(isERC20Token),
|
|
41
48
|
glacierService,
|
|
42
49
|
currency,
|
|
43
50
|
chainId,
|
|
@@ -80,7 +87,7 @@ export const getBalances = async ({
|
|
|
80
87
|
tokenService,
|
|
81
88
|
address,
|
|
82
89
|
currency,
|
|
83
|
-
tokens: allTokens,
|
|
90
|
+
tokens: allTokens.filter(isERC20Token),
|
|
84
91
|
});
|
|
85
92
|
|
|
86
93
|
return {
|
|
@@ -107,3 +114,7 @@ export const getBalances = async ({
|
|
|
107
114
|
|
|
108
115
|
return filterBalances;
|
|
109
116
|
};
|
|
117
|
+
|
|
118
|
+
function isERC20Token(token: NetworkContractToken): token is ERC20Token {
|
|
119
|
+
return token.type === TokenType.ERC20;
|
|
120
|
+
}
|
|
@@ -2,12 +2,13 @@ import { balanceToDisplayValue, bnToBig } from '@avalabs/utils-sdk';
|
|
|
2
2
|
import {
|
|
3
3
|
TokenType,
|
|
4
4
|
type TokenWithBalanceERC20,
|
|
5
|
-
type NetworkContractToken,
|
|
6
5
|
type TokenWithBalance,
|
|
6
|
+
type ERC20Token,
|
|
7
7
|
} from '@avalabs/vm-module-types';
|
|
8
8
|
import { CurrencyCode, Erc20TokenBalance } from '@avalabs/glacier-sdk';
|
|
9
9
|
import BN from 'bn.js';
|
|
10
10
|
import type { EvmGlacierService } from '../../../services/glacier-service/glacier-service';
|
|
11
|
+
import { DEFAULT_DECIMALS } from '../../../constants';
|
|
11
12
|
|
|
12
13
|
export const getErc20Balances = async ({
|
|
13
14
|
glacierService,
|
|
@@ -20,7 +21,7 @@ export const getErc20Balances = async ({
|
|
|
20
21
|
address: string;
|
|
21
22
|
currency: string;
|
|
22
23
|
chainId: number;
|
|
23
|
-
customTokens:
|
|
24
|
+
customTokens: ERC20Token[];
|
|
24
25
|
}): Promise<Record<string, TokenWithBalance>> => {
|
|
25
26
|
const tokensWithBalance: TokenWithBalanceERC20[] = [];
|
|
26
27
|
/**
|
|
@@ -37,7 +38,9 @@ export const getErc20Balances = async ({
|
|
|
37
38
|
pageToken: nextPageToken,
|
|
38
39
|
});
|
|
39
40
|
|
|
40
|
-
tokensWithBalance.push(
|
|
41
|
+
tokensWithBalance.push(
|
|
42
|
+
...convertErc20TokenWithBalanceToTokenWithBalance(response.erc20TokenBalances, Number(chainId)),
|
|
43
|
+
);
|
|
41
44
|
nextPageToken = response.nextPageToken;
|
|
42
45
|
} while (nextPageToken);
|
|
43
46
|
|
|
@@ -47,7 +50,7 @@ export const getErc20Balances = async ({
|
|
|
47
50
|
* tokens are only used for swap, bridge and tx parsing.
|
|
48
51
|
*/
|
|
49
52
|
return [
|
|
50
|
-
...
|
|
53
|
+
...convertErc20TokenToTokenWithBalance(customTokens),
|
|
51
54
|
...tokensWithBalance, // this needs to be second in the list so it overwrites its zero balance counterpart if there is one
|
|
52
55
|
].reduce(
|
|
53
56
|
(acc, token) => {
|
|
@@ -57,10 +60,11 @@ export const getErc20Balances = async ({
|
|
|
57
60
|
);
|
|
58
61
|
};
|
|
59
62
|
|
|
60
|
-
const
|
|
63
|
+
const convertErc20TokenToTokenWithBalance = (tokens: ERC20Token[]): TokenWithBalanceERC20[] => {
|
|
61
64
|
return tokens.map((token) => {
|
|
62
65
|
return {
|
|
63
66
|
...token,
|
|
67
|
+
decimals: token.decimals ?? DEFAULT_DECIMALS,
|
|
64
68
|
type: TokenType.ERC20,
|
|
65
69
|
balance: new BN(0),
|
|
66
70
|
balanceInCurrency: 0,
|
|
@@ -74,7 +78,7 @@ const convertNetworkTokenToTokenWithBalance = (tokens: NetworkContractToken[]):
|
|
|
74
78
|
});
|
|
75
79
|
};
|
|
76
80
|
|
|
77
|
-
const
|
|
81
|
+
const convertErc20TokenWithBalanceToTokenWithBalance = (
|
|
78
82
|
tokenBalances: Erc20TokenBalance[],
|
|
79
83
|
chainId: number,
|
|
80
84
|
): TokenWithBalanceERC20[] => {
|
|
@@ -97,7 +101,6 @@ const convertErc20ToTokenWithBalance = (
|
|
|
97
101
|
balanceDisplayValue,
|
|
98
102
|
balanceInCurrency,
|
|
99
103
|
priceInCurrency,
|
|
100
|
-
contractType: 'ERC-20',
|
|
101
104
|
type: TokenType.ERC20,
|
|
102
105
|
change24: 0,
|
|
103
106
|
marketCap: 0,
|
|
@@ -28,7 +28,7 @@ describe('get-tokens', () => {
|
|
|
28
28
|
address: '0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7',
|
|
29
29
|
name: 'Wrapped AVAX',
|
|
30
30
|
symbol: 'WAVAX',
|
|
31
|
-
|
|
31
|
+
type: 'ERC20',
|
|
32
32
|
chainId: 43114,
|
|
33
33
|
decimals: 18,
|
|
34
34
|
},
|
|
@@ -36,7 +36,7 @@ describe('get-tokens', () => {
|
|
|
36
36
|
address: '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E',
|
|
37
37
|
name: 'USD Coin',
|
|
38
38
|
symbol: 'USDC',
|
|
39
|
-
|
|
39
|
+
type: 'ERC20',
|
|
40
40
|
chainId: 43114,
|
|
41
41
|
decimals: 6,
|
|
42
42
|
},
|
|
@@ -58,7 +58,7 @@ describe('get-tokens', () => {
|
|
|
58
58
|
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
59
59
|
name: 'Tether USD',
|
|
60
60
|
symbol: 'USDT',
|
|
61
|
-
|
|
61
|
+
type: 'ERC20',
|
|
62
62
|
chainId: 1,
|
|
63
63
|
decimals: 6,
|
|
64
64
|
},
|
|
@@ -66,7 +66,7 @@ describe('get-tokens', () => {
|
|
|
66
66
|
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
|
67
67
|
name: 'USDCoin',
|
|
68
68
|
symbol: 'USDC',
|
|
69
|
-
|
|
69
|
+
type: 'ERC20',
|
|
70
70
|
chainId: 1,
|
|
71
71
|
decimals: 6,
|
|
72
72
|
},
|
|
@@ -74,7 +74,7 @@ describe('get-tokens', () => {
|
|
|
74
74
|
address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
|
|
75
75
|
name: 'Wrapped BTC',
|
|
76
76
|
symbol: 'WBTC',
|
|
77
|
-
|
|
77
|
+
type: 'ERC20',
|
|
78
78
|
chainId: 1,
|
|
79
79
|
decimals: 8,
|
|
80
80
|
},
|
|
@@ -82,7 +82,7 @@ describe('get-tokens', () => {
|
|
|
82
82
|
address: '0x514910771AF9Ca656af840dff83E8264EcF986CA',
|
|
83
83
|
name: 'ChainLink Token',
|
|
84
84
|
symbol: 'LINK',
|
|
85
|
-
|
|
85
|
+
type: 'ERC20',
|
|
86
86
|
chainId: 1,
|
|
87
87
|
decimals: 18,
|
|
88
88
|
},
|
package/src/module.ts
CHANGED
|
@@ -98,6 +98,7 @@ export class EvmModule implements Module {
|
|
|
98
98
|
request,
|
|
99
99
|
network,
|
|
100
100
|
approvalController: this.#approvalController,
|
|
101
|
+
proxyApiUrl: this.#proxyApiUrl,
|
|
101
102
|
});
|
|
102
103
|
case RpcMethod.PERSONAL_SIGN:
|
|
103
104
|
case RpcMethod.ETH_SIGN:
|
|
@@ -109,6 +110,7 @@ export class EvmModule implements Module {
|
|
|
109
110
|
request,
|
|
110
111
|
network,
|
|
111
112
|
approvalController: this.#approvalController,
|
|
113
|
+
proxyApiUrl: this.#proxyApiUrl,
|
|
112
114
|
});
|
|
113
115
|
default:
|
|
114
116
|
return { error: rpcErrors.methodNotSupported(`Method ${request.method} not supported`) };
|
package/src/types.ts
CHANGED
|
@@ -14,3 +14,12 @@ export type TransactionParams = {
|
|
|
14
14
|
nonce?: string;
|
|
15
15
|
chainId?: string;
|
|
16
16
|
};
|
|
17
|
+
|
|
18
|
+
export enum ERC20TransactionType {
|
|
19
|
+
TOTAL_SUPPLY = 'totalSupply',
|
|
20
|
+
BALANCE_OF = 'balanceOf',
|
|
21
|
+
TRANSFER = 'transfer',
|
|
22
|
+
TRANSFER_FROM = 'transferFrom',
|
|
23
|
+
APPROVE = 'approve',
|
|
24
|
+
ALLOWANCE = 'allowance',
|
|
25
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Interface } from 'ethers';
|
|
2
|
+
import ERC20 from '@openzeppelin/contracts/build/contracts/ERC20.json';
|
|
3
|
+
import { ERC20TransactionType } from '../types';
|
|
4
|
+
|
|
5
|
+
export const parseERC20TransactionType = (transaction: {
|
|
6
|
+
data?: string;
|
|
7
|
+
value?: string;
|
|
8
|
+
}): ERC20TransactionType | undefined => {
|
|
9
|
+
if (!transaction.data) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const contractInterface = new Interface(ERC20.abi);
|
|
15
|
+
|
|
16
|
+
const description = contractInterface.parseTransaction({
|
|
17
|
+
data: transaction.data,
|
|
18
|
+
value: transaction.value,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const functionName = description?.name ?? description?.fragment?.name;
|
|
22
|
+
|
|
23
|
+
if (functionName && isERC20TransactionType(functionName)) {
|
|
24
|
+
return functionName;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return undefined;
|
|
28
|
+
} catch (e) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function isERC20TransactionType(value: string): value is ERC20TransactionType {
|
|
34
|
+
return Object.values(ERC20TransactionType).includes(value as ERC20TransactionType);
|
|
35
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import Blockaid from '@blockaid/client';
|
|
2
|
+
import { processBalanceChange } from './process-transaction-simulation';
|
|
3
|
+
|
|
4
|
+
jest.mock('@blockaid/client', () => {
|
|
5
|
+
return jest.fn().mockImplementation(() => {
|
|
6
|
+
return {};
|
|
7
|
+
});
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
describe('processBalanceChange', () => {
|
|
11
|
+
it('should sort asset diffs correctly within ins and outs', () => {
|
|
12
|
+
const assetDiffs: Blockaid.AssetDiff[] = [
|
|
13
|
+
{
|
|
14
|
+
asset: {
|
|
15
|
+
type: 'ERC20',
|
|
16
|
+
address: '0xTokenAddress1',
|
|
17
|
+
name: 'TokenName1',
|
|
18
|
+
symbol: 'TKN1',
|
|
19
|
+
decimals: 18,
|
|
20
|
+
logo_url: 'logo_url1',
|
|
21
|
+
},
|
|
22
|
+
in: [
|
|
23
|
+
{ value: '1', usd_price: '1', raw_value: '0x1' },
|
|
24
|
+
{ value: '2', usd_price: '2', raw_value: '0x2' },
|
|
25
|
+
],
|
|
26
|
+
out: [
|
|
27
|
+
{ value: '3', usd_price: '3', raw_value: '0x3' },
|
|
28
|
+
{ value: '1', usd_price: '1', raw_value: '0x1' },
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
asset: {
|
|
33
|
+
type: 'ERC20',
|
|
34
|
+
address: '0xTokenAddress2',
|
|
35
|
+
name: 'TokenName2',
|
|
36
|
+
symbol: 'TKN2',
|
|
37
|
+
decimals: 18,
|
|
38
|
+
logo_url: 'logo_url2',
|
|
39
|
+
},
|
|
40
|
+
in: [{ value: '4', usd_price: '4', raw_value: '0x4' }],
|
|
41
|
+
out: [{ value: '5', usd_price: '5', raw_value: '0x5' }],
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
const result = processBalanceChange(assetDiffs);
|
|
46
|
+
|
|
47
|
+
// Verify sorting logic for 'ins'
|
|
48
|
+
expect(result?.ins).toEqual([
|
|
49
|
+
{
|
|
50
|
+
token: {
|
|
51
|
+
type: 'ERC20',
|
|
52
|
+
address: '0xTokenAddress2',
|
|
53
|
+
name: 'TokenName2',
|
|
54
|
+
symbol: 'TKN2',
|
|
55
|
+
decimals: 18,
|
|
56
|
+
logoUri: 'logo_url2',
|
|
57
|
+
},
|
|
58
|
+
items: [{ displayValue: '4', usdPrice: '4' }],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
token: {
|
|
62
|
+
type: 'ERC20',
|
|
63
|
+
address: '0xTokenAddress1',
|
|
64
|
+
name: 'TokenName1',
|
|
65
|
+
symbol: 'TKN1',
|
|
66
|
+
decimals: 18,
|
|
67
|
+
logoUri: 'logo_url1',
|
|
68
|
+
},
|
|
69
|
+
items: [
|
|
70
|
+
{ displayValue: '1', usdPrice: '1' },
|
|
71
|
+
{ displayValue: '2', usdPrice: '2' },
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
// Verify sorting logic for 'outs'
|
|
77
|
+
expect(result?.outs).toEqual([
|
|
78
|
+
{
|
|
79
|
+
token: {
|
|
80
|
+
type: 'ERC20',
|
|
81
|
+
address: '0xTokenAddress2',
|
|
82
|
+
name: 'TokenName2',
|
|
83
|
+
symbol: 'TKN2',
|
|
84
|
+
decimals: 18,
|
|
85
|
+
logoUri: 'logo_url2',
|
|
86
|
+
},
|
|
87
|
+
items: [{ displayValue: '5', usdPrice: '5' }],
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
token: {
|
|
91
|
+
type: 'ERC20',
|
|
92
|
+
address: '0xTokenAddress1',
|
|
93
|
+
name: 'TokenName1',
|
|
94
|
+
symbol: 'TKN1',
|
|
95
|
+
decimals: 18,
|
|
96
|
+
logoUri: 'logo_url1',
|
|
97
|
+
},
|
|
98
|
+
items: [
|
|
99
|
+
{ displayValue: '3', usdPrice: '3' },
|
|
100
|
+
{ displayValue: '1', usdPrice: '1' },
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
]);
|
|
104
|
+
});
|
|
105
|
+
});
|