@exodus/web3-ethereum-utils 1.18.0 → 1.20.0

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.
@@ -5,13 +5,13 @@ const SIMULATION_API_DEFAULT = 'https://simulation.a.exodus.io/simulate';
5
5
  const SERVICE_PROVIDER_DEFAULT = 'blowfish';
6
6
  /* eslint-disable @typescript-eslint/no-unused-vars */
7
7
  export const createSimulateTransaction = ({ serviceProvider = SERVICE_PROVIDER_DEFAULT, apiEndpoint = SIMULATION_API_DEFAULT, }) => async ({ transaction, asset, shouldCallPreviewApi = true, }) => {
8
- if (!transaction.to) {
9
- return createEmptySimulationResult({ asset });
10
- }
11
8
  const simulationResult = createEmptySimulationResult({ asset });
12
9
  simulationResult.balanceChanges.willPayFee.push({
13
10
  balance: estimateFee({ asset, transaction }),
14
11
  });
12
+ if (!transaction.to) {
13
+ return simulationResult;
14
+ }
15
15
  if (!shouldCallPreviewApi) {
16
16
  tryEstimatingChangesLocally({ asset, simulationResult, transaction });
17
17
  return simulationResult;
@@ -1,10 +1,12 @@
1
1
  /*
2
- This utility determines a fee a transaction is going to pay based on
2
+ This utility determines a preliminary fee a transaction is going to pay based on
3
3
  its "gas", "gasPrice" and "maxFeePerGas" properties.
4
4
 
5
- Note: if EIP-1559 is enabled a transaction will have the "maxFeePerGas" field. In that case
6
- a final fee will be calculated on-chain (the utility returns a maximum possible value).
7
- If a transaction has just "gasPrice" it can be considered as a total fee.
5
+ Note: if EIP-1559 is enabled, a transaction will have the "maxFeePerGas" field. In that case,
6
+ a final gas price will be determined on-chain (the utility uses a maximum possible gas price value).
7
+ If a transaction has only "gasPrice", it can be considered as a final gas price.
8
+ However, a final fee for non-simple transfers is always calculated on-chain once the EVM executes a transaction
9
+ and has the number of gas consumed.
8
10
  */
9
11
  import { hexToBN } from '@exodus/web3-utils';
10
12
  export const estimateFee = ({ asset, transaction, }) => {
@@ -3,5 +3,5 @@ import type { AggregatedTransactionSimulationResult, CreateSimulateTransactionPa
3
3
  declare type SimulateTransactionParams = Required<CreateSimulateTransactionParams> & Pick<EthSimulateTransactionParams, 'transaction' | 'origin'> & {
4
4
  simulationResult: AggregatedTransactionSimulationResult;
5
5
  };
6
- export declare const simulateTransaction: ({ transaction, apiEndpoint, origin, }: SimulateTransactionParams) => Promise<void> | never;
6
+ export declare const simulateTransaction: ({ transaction, apiEndpoint, origin, simulationResult, }: SimulateTransactionParams) => Promise<void> | never;
7
7
  export {};
@@ -2,7 +2,7 @@
2
2
  This utility makes an API call to a simulation service, parses the response and mutates the supplied "simulationResult" object
3
3
  with the according balance changes.
4
4
  */
5
- import { makeSimulationAPICall } from '@exodus/web3-utils';
5
+ import { createCurrency, makeSimulationAPICall } from '@exodus/web3-utils';
6
6
  const convertTransactionToApiPayload = (transaction, origin) => ({
7
7
  txObject: {
8
8
  from: transaction.from,
@@ -15,12 +15,102 @@ const convertTransactionToApiPayload = (transaction, origin) => ({
15
15
  },
16
16
  userAccount: transaction.from,
17
17
  });
18
- export const simulateTransaction = async ({ transaction, apiEndpoint, origin, }) => {
18
+ const createBalanceAmount = (amount, { decimals, symbol }) => {
19
+ const beforeAmount = createCurrency({
20
+ amount: amount.before,
21
+ symbol: symbol,
22
+ denominator: decimals,
23
+ });
24
+ const afterAmount = createCurrency({
25
+ amount: amount.after,
26
+ symbol: symbol,
27
+ denominator: decimals,
28
+ });
29
+ return beforeAmount.sub(afterAmount);
30
+ };
31
+ const handleExpectedBalanceChange = (expectedBalanceChange, simulationResult) => {
32
+ const { willApprove, willSend, willReceive } = simulationResult.balanceChanges;
33
+ const { data, kind } = expectedBalanceChange.rawInfo;
34
+ const { asset } = data;
35
+ // ERC1155 is a multi-token standard (https://eips.ethereum.org/EIPS/eip-1155) and can be used for NFTs as well.
36
+ const isNftTransfer = kind === 'ERC721_TRANSFER' ||
37
+ (kind === 'ERC1155_TRANSFER' && !asset.decimals);
38
+ const symbol = asset.symbol;
39
+ const decimals = isNftTransfer ? 1 : asset.decimals;
40
+ if (kind === 'ERC20_APPROVAL' ||
41
+ kind === 'ERC721_APPROVAL_FOR_ALL' ||
42
+ kind === 'ERC1155_APPROVAL_FOR_ALL' ||
43
+ kind === 'ERC721_APPROVAL') {
44
+ willApprove.push({
45
+ spender: data.spender.address,
46
+ unitName: symbol,
47
+ balance: createCurrency({
48
+ amount: data.amount.after,
49
+ symbol: symbol,
50
+ denominator: decimals,
51
+ }),
52
+ });
53
+ return;
54
+ }
55
+ const { amount } = data;
56
+ const balance = createBalanceAmount(amount, { decimals, symbol });
57
+ if (balance.isZero) {
58
+ return;
59
+ }
60
+ const balanceChange = {
61
+ balance,
62
+ };
63
+ if (isNftTransfer) {
64
+ balanceChange.nft = {
65
+ title: asset.name,
66
+ };
67
+ }
68
+ if (balance.isNegative) {
69
+ willReceive.push(balanceChange);
70
+ }
71
+ else {
72
+ willSend.push(balanceChange);
73
+ }
74
+ };
75
+ const simulationResultHasAssets = (expectedStateChanges) => {
76
+ for (const expectedChange of expectedStateChanges) {
77
+ const { asset } = expectedChange.rawInfo
78
+ .data;
79
+ if (!asset) {
80
+ return false;
81
+ }
82
+ }
83
+ return true;
84
+ };
85
+ export const simulateTransaction = async ({ transaction, apiEndpoint, origin, simulationResult, }) => {
86
+ const userAddress = transaction.from;
19
87
  const payload = convertTransactionToApiPayload(transaction, origin);
20
- await makeSimulationAPICall({
88
+ const response = await makeSimulationAPICall({
21
89
  url: apiEndpoint,
22
90
  network: 'ethereum',
23
91
  payload,
24
92
  });
25
- // ToDo: parse the response and mutate the simulation result
93
+ const { expectedStateChanges = {}, error } = response.simulationResults.aggregated;
94
+ if (error) {
95
+ // ToDo: accept a logger dep and log the error
96
+ // ToDo: consider pushing an error to the "warnings" list
97
+ return;
98
+ }
99
+ const userExpectedChanges = Object.keys(expectedStateChanges).reduce((prev, address) => {
100
+ if (userAddress !== address) {
101
+ return prev;
102
+ }
103
+ const result = (Object.prototype.hasOwnProperty.call(expectedStateChanges, address) &&
104
+ expectedStateChanges[address]) ||
105
+ [];
106
+ return [...prev, ...result];
107
+ }, []);
108
+ // Return and log if asset is not present in the expectedStateChanges
109
+ if (!simulationResultHasAssets(userExpectedChanges)) {
110
+ // ToDo: accept a logger dep and log the error
111
+ return;
112
+ }
113
+ userExpectedChanges.forEach((expectedChange) => {
114
+ handleExpectedBalanceChange(expectedChange, simulationResult);
115
+ });
26
116
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@exodus/web3-ethereum-utils",
3
3
  "description": "Web3 Ethereum utils.",
4
- "version": "1.18.0",
4
+ "version": "1.20.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "module": "lib/index.js",
@@ -26,13 +26,13 @@
26
26
  "build": "yarn clean && tsc"
27
27
  },
28
28
  "devDependencies": {
29
- "@exodus/web3-types": "^1.23.0"
29
+ "@exodus/web3-types": "^1.25.0"
30
30
  },
31
31
  "dependencies": {
32
32
  "@exodus/ethereumjs-common": "^2.4.0-exodus.6",
33
33
  "@exodus/ethereumjs-tx": "^3.3.0-exodus.6",
34
- "@exodus/web3-utils": "^1.14.0",
34
+ "@exodus/web3-utils": "^1.16.0",
35
35
  "@metamask/eth-sig-util": "^4.0.1"
36
36
  },
37
- "gitHead": "e03ec3f4c16b10ac89ac42cbbc7bc9ea3409cb08"
37
+ "gitHead": "8f42c066e1d289271e5db5b859e090d06626ef0f"
38
38
  }