@exodus/web3-ethereum-utils 4.5.1 → 4.7.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.
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { EthAggregatedTransactionSimulationResult, EthSimulateTransactionParams } from './types.js';
|
|
2
2
|
import type { CreateSimulateTransactionsParams } from '@exodus/web3-types';
|
|
3
|
-
export declare const createSimulateTransactions: ({ apiEndpoint, headers }: Required<CreateSimulateTransactionsParams>) => ({ transactions, asset, origin, overrideApiEndpoint, }: EthSimulateTransactionParams) => Promise<EthAggregatedTransactionSimulationResult>;
|
|
3
|
+
export declare const createSimulateTransactions: ({ apiEndpoint, headers: headersFromDeps }: Required<CreateSimulateTransactionsParams>) => ({ transactions, asset, origin, headers: headersFromParams, overrideApiEndpoint, }: EthSimulateTransactionParams) => Promise<EthAggregatedTransactionSimulationResult>;
|
|
@@ -3,20 +3,41 @@ import { estimateFee } from './simulation/estimateFee.js';
|
|
|
3
3
|
import { simulateTransactions } from './simulation/simulateTransactions.js';
|
|
4
4
|
import { tryEstimatingChangesLocally } from './simulation/tryEstimatingChangesLocally.js';
|
|
5
5
|
import { decodeRecipientAddresses, getDisplayDetails, getTxFeeDetails } from './transactions.js';
|
|
6
|
-
export const createSimulateTransactions = ({ apiEndpoint, headers }) => async ({ transactions, asset, origin, overrideApiEndpoint, }) => {
|
|
7
|
-
|
|
8
|
-
const transaction = transactions[0];
|
|
6
|
+
export const createSimulateTransactions = ({ apiEndpoint, headers: headersFromDeps }) => async ({ transactions, asset, origin, headers: headersFromParams, overrideApiEndpoint, }) => {
|
|
7
|
+
const headers = { ...headersFromDeps, ...headersFromParams };
|
|
9
8
|
const simulationResult = createEmptySimulationResult({
|
|
10
9
|
asset,
|
|
11
10
|
});
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
if (transactions.length === 0) {
|
|
12
|
+
const message = 'No transactions provided. Cannot simulate transaction batch.';
|
|
13
|
+
simulationResult.warnings.push({ kind: 'INTERNAL_ERROR', severity: 'HIGH', message });
|
|
14
|
+
simulationResult.metadata.humanReadableError = message;
|
|
15
|
+
return simulationResult;
|
|
16
|
+
}
|
|
17
|
+
// Ensure all transactions share the same "from" address
|
|
18
|
+
// Added this because simulateTransactions makes this assumption and would give us invalid results if it's not the case
|
|
19
|
+
// If we need this (which is unlikely) we can remove this check and update simulateTransactions to handle multiple from addresses
|
|
20
|
+
const firstFrom = transactions[0].from.toLowerCase();
|
|
21
|
+
const hasMismatchedFrom = transactions.some((tx) => tx.from.toLowerCase() !== firstFrom);
|
|
22
|
+
if (hasMismatchedFrom) {
|
|
23
|
+
const message = 'All transactions in a batch must have the same "from" address. Cannot simulate transaction batch.';
|
|
24
|
+
simulationResult.warnings.push({ kind: 'INTERNAL_ERROR', severity: 'HIGH', message });
|
|
25
|
+
// add human readable error
|
|
26
|
+
simulationResult.metadata.humanReadableError = message;
|
|
27
|
+
return simulationResult;
|
|
28
|
+
}
|
|
29
|
+
// Check if ALL transactions have a 'to' address - if any are missing, return early
|
|
30
|
+
// Transactions are linked, so if one is invalid, the entire batch is invalid
|
|
31
|
+
const hasInvalidTransaction = transactions.some(tx => !tx.to);
|
|
32
|
+
if (hasInvalidTransaction) {
|
|
33
|
+
const message = 'One or more transactions are missing required "to" address. Cannot simulate transaction batch.';
|
|
34
|
+
simulationResult.warnings.push({ kind: 'INTERNAL_ERROR', severity: 'HIGH', message });
|
|
35
|
+
simulationResult.metadata.humanReadableError = message;
|
|
17
36
|
return simulationResult;
|
|
18
37
|
}
|
|
38
|
+
// Fees are added only after a successful simulation (remote or local)
|
|
19
39
|
simulationResult.recipientAddresses = transactions.map((tx) => decodeRecipientAddresses(tx));
|
|
40
|
+
// Mutates the simulationResult, simulationTransactionInternalResult is only for checking the kind of response
|
|
20
41
|
const simulationTransactionInternalResult = await simulateTransactions({
|
|
21
42
|
asset,
|
|
22
43
|
transactions,
|
|
@@ -25,24 +46,48 @@ export const createSimulateTransactions = ({ apiEndpoint, headers }) => async ({
|
|
|
25
46
|
simulationResult,
|
|
26
47
|
headers,
|
|
27
48
|
});
|
|
49
|
+
// Blowfish simulation failed, try local simulation
|
|
28
50
|
if (simulationTransactionInternalResult.kind === 'error') {
|
|
51
|
+
if (transactions.length > 1) {
|
|
52
|
+
// For batched transactions, don't attempt local simulation
|
|
53
|
+
const message = 'Cannot locally simulate batched transactions.';
|
|
54
|
+
simulationResult.warnings.push({ kind: 'INTERNAL_ERROR', severity: 'HIGH', message });
|
|
55
|
+
simulationResult.metadata.humanReadableError = message;
|
|
56
|
+
return simulationResult;
|
|
57
|
+
}
|
|
58
|
+
// Local simulation also mutates the simulationResult, so we need to check if it was successful
|
|
29
59
|
const simulatedLocally = tryEstimatingChangesLocally({
|
|
30
60
|
asset,
|
|
31
61
|
simulationResult,
|
|
32
|
-
transaction,
|
|
62
|
+
transaction: transactions[0],
|
|
33
63
|
});
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
64
|
+
// If local simulation succeeded, add fee for the (single) transaction now
|
|
65
|
+
if (simulatedLocally) {
|
|
66
|
+
simulationResult.metadata.simulatedLocally = true;
|
|
67
|
+
const transaction = transactions[0];
|
|
68
|
+
simulationResult.balanceChanges.willPayFee.push({
|
|
69
|
+
balance: estimateFee({ asset, transaction }),
|
|
70
|
+
feeDetails: getTxFeeDetails(transaction),
|
|
39
71
|
});
|
|
40
72
|
}
|
|
73
|
+
else {
|
|
74
|
+
// Local Simulation also failed.
|
|
75
|
+
const message = 'Balance changes cannot be estimated.';
|
|
76
|
+
simulationResult.warnings.push({ kind: 'INTERNAL_ERROR', severity: 'HIGH', message });
|
|
77
|
+
simulationResult.metadata.humanReadableError = message;
|
|
78
|
+
}
|
|
41
79
|
if (simulationTransactionInternalResult.errorMessage) {
|
|
42
80
|
simulationResult.metadata.humanReadableError = simulationTransactionInternalResult.errorMessage;
|
|
43
81
|
}
|
|
44
82
|
return simulationResult;
|
|
45
83
|
}
|
|
84
|
+
// Remote simulation succeeded; add fees for all transactions now
|
|
85
|
+
for (const transaction of transactions) {
|
|
86
|
+
simulationResult.balanceChanges.willPayFee.push({
|
|
87
|
+
balance: estimateFee({ asset, transaction }),
|
|
88
|
+
feeDetails: getTxFeeDetails(transaction),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
46
91
|
simulationResult.displayDetails = getDisplayDetails(simulationResult.balanceChanges.willApprove);
|
|
47
92
|
return simulationResult;
|
|
48
93
|
};
|
|
@@ -45,7 +45,7 @@ const handleExpectedBalanceChange = (expectedBalanceChange, simulationResult, {
|
|
|
45
45
|
const isNftTransferApproval = kind === 'ERC721_APPROVAL' ||
|
|
46
46
|
(kind === 'ERC721_APPROVAL_FOR_ALL' && !asset.decimals) ||
|
|
47
47
|
(kind === 'ERC1155_APPROVAL_FOR_ALL' && !asset.decimals);
|
|
48
|
-
const decimals = isNftTransfer || isNftTransferApproval ? 0 : asset.decimals;
|
|
48
|
+
const decimals = isNftTransfer || isNftTransferApproval || !Number.isFinite(asset.decimals) ? 0 : asset.decimals;
|
|
49
49
|
const asset_ = pick(asset, [
|
|
50
50
|
'address',
|
|
51
51
|
'imageUrl',
|
|
@@ -130,8 +130,11 @@ export const simulateTransactions = async ({ transactions, apiEndpoint, origin,
|
|
|
130
130
|
return {
|
|
131
131
|
kind: 'error',
|
|
132
132
|
error: 2 /* SimulationErrorMessages.SimulationNotSupported */,
|
|
133
|
+
errorMessage: 'Simulation is not supported for this asset.',
|
|
133
134
|
};
|
|
134
135
|
}
|
|
136
|
+
//Only one user address is supported
|
|
137
|
+
//Sending multiple transactions with different from addresses would lead to invalid results
|
|
135
138
|
const userAddress = transactions[0].from;
|
|
136
139
|
const payload = convertTransactionToApiPayload(transactions, origin);
|
|
137
140
|
if (!Object.hasOwnProperty.call(BLOWFISH_EVM_CHAINS, asset.name)) {
|
|
@@ -150,6 +153,7 @@ export const simulateTransactions = async ({ transactions, apiEndpoint, origin,
|
|
|
150
153
|
return {
|
|
151
154
|
kind: 'error',
|
|
152
155
|
error: 0 /* SimulationErrorMessages.APICallFailed */,
|
|
156
|
+
errorMessage: 'Simulation API call failed.',
|
|
153
157
|
};
|
|
154
158
|
}
|
|
155
159
|
simulationResult.metadata.simulatedLocally = false;
|
package/lib/types.d.ts
CHANGED
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": "4.
|
|
4
|
+
"version": "4.7.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"module": "lib/index.js",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"lib/**/*.js"
|
|
19
19
|
],
|
|
20
20
|
"publishConfig": {
|
|
21
|
-
"access": "public"
|
|
21
|
+
"access": "public",
|
|
22
|
+
"provenance": false
|
|
22
23
|
},
|
|
23
24
|
"repository": {
|
|
24
25
|
"type": "git",
|
|
@@ -38,7 +39,7 @@
|
|
|
38
39
|
},
|
|
39
40
|
"dependencies": {
|
|
40
41
|
"@exodus/basic-utils": "^3.0.1",
|
|
41
|
-
"@exodus/ethereum-lib": "^5.
|
|
42
|
+
"@exodus/ethereum-lib": "^5.20.0",
|
|
42
43
|
"@exodus/ethereumjs": "^1.5.0",
|
|
43
44
|
"@exodus/solidity-contract": "^1.2.1",
|
|
44
45
|
"@exodus/web3-utils": "^1.50.1",
|
|
@@ -49,5 +50,5 @@
|
|
|
49
50
|
"url": "https://github.com/ExodusMovement/assets/issues?q=is%3Aissue+is%3Aopen+label%3Aweb3-ethereum-utils"
|
|
50
51
|
},
|
|
51
52
|
"homepage": "https://github.com/ExodusMovement/assets/tree/master/web3/web3-ethereum-utils",
|
|
52
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "600b3f3f510e30333080011e85958f71835e020c"
|
|
53
54
|
}
|