@exodus/web3-ethereum-utils 4.7.5 → 4.7.6

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,8 +1,8 @@
1
1
  import type { EthSimulateMessageParams } from './types.js';
2
2
  import type { MessageSimulationResult } from '@exodus/web3-types';
3
- export declare const createSimulateMessage: ({ apiEndpoint, headers, }?: {
4
- apiEndpoint?: string | undefined;
5
- headers?: {
6
- 'X-Api-Version': string;
7
- } | undefined;
8
- }) => ({ message, url, asset, address, }: EthSimulateMessageParams) => Promise<MessageSimulationResult>;
3
+ type CreateSimulateMessage = (params?: {
4
+ apiEndpoint?: string;
5
+ headers?: Record<string, string>;
6
+ }) => (params: EthSimulateMessageParams) => Promise<MessageSimulationResult>;
7
+ export declare const createSimulateMessage: CreateSimulateMessage;
8
+ export {};
@@ -1,37 +1,3 @@
1
- import { tryParsingSIWE } from '@exodus/web3-utils';
2
- import assert from 'minimalistic-assert';
3
- import { getMessageType } from './simulation/getMessageType.js';
4
- import { simulateMessage } from './simulation/simulateMessage.js';
5
- export const createSimulateMessage = ({ apiEndpoint = 'https://simulation.a.exodus.io/simulateMessage', headers = {
6
- 'X-Api-Version': '2023-06-05',
7
- }, } = {}) => async ({ message, url, asset, address, }) => {
8
- assert(url instanceof URL, "'url' should be an instance of the URL object.");
9
- const simulationResult = {
10
- baseAssetName: asset.baseAssetName,
11
- action: 'NONE',
12
- };
13
- const messageType = getMessageType(message);
14
- if (messageType === 2 /* MessageTypeEnum.Unknown */) {
15
- return simulationResult;
16
- }
17
- if (messageType === 0 /* MessageTypeEnum.RawMessage */) {
18
- tryParsingSIWE({
19
- address,
20
- message,
21
- url,
22
- simulationResult,
23
- });
24
- }
25
- await simulateMessage({
26
- address,
27
- message: {
28
- message,
29
- messageType,
30
- },
31
- url,
32
- apiEndpoint,
33
- headers,
34
- simulationResult,
35
- });
36
- return simulationResult;
37
- };
1
+ // @ts-expect-error @exodus/ethereum-api is a JavaScript package without deep declarations.
2
+ import { createSimulateMessage as createEthereumApiSimulateMessage } from '@exodus/ethereum-api/src/simulation/create-simulate-message.js';
3
+ export const createSimulateMessage = createEthereumApiSimulateMessage;
@@ -1,3 +1,5 @@
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: headersFromDeps }: Required<CreateSimulateTransactionsParams>) => ({ transactions, asset, origin, blockNumber, headers: headersFromParams, overrideApiEndpoint, }: EthSimulateTransactionParams) => Promise<EthAggregatedTransactionSimulationResult>;
3
+ type CreateSimulateTransactions = (params: Required<CreateSimulateTransactionsParams>) => (params: EthSimulateTransactionParams) => Promise<EthAggregatedTransactionSimulationResult>;
4
+ export declare const createSimulateTransactions: CreateSimulateTransactions;
5
+ export {};
@@ -1,95 +1,3 @@
1
- import { createEmptySimulationResult } from '@exodus/web3-utils';
2
- import { estimateFee } from './simulation/estimateFee.js';
3
- import { simulateTransactions } from './simulation/simulateTransactions.js';
4
- import { tryEstimatingChangesLocally } from './simulation/tryEstimatingChangesLocally.js';
5
- import { decodeRecipientAddresses, getDisplayDetails, getTxFeeDetails } from './transactions.js';
6
- export const createSimulateTransactions = ({ apiEndpoint, headers: headersFromDeps }) => async ({ transactions, asset, origin, blockNumber, headers: headersFromParams, overrideApiEndpoint, }) => {
7
- const headers = { ...headersFromDeps, ...headersFromParams };
8
- const simulationResult = createEmptySimulationResult({
9
- asset,
10
- });
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;
36
- return simulationResult;
37
- }
38
- // Fees are added only after a successful simulation (remote or local)
39
- simulationResult.recipientAddresses = transactions.map((tx) => decodeRecipientAddresses(tx));
40
- // Mutates the simulationResult, simulationTransactionInternalResult is only for checking the kind of response
41
- const simulationTransactionInternalResult = await simulateTransactions({
42
- asset,
43
- transactions,
44
- apiEndpoint: overrideApiEndpoint || apiEndpoint,
45
- origin,
46
- blockNumber,
47
- simulationResult,
48
- headers,
49
- });
50
- // Blowfish simulation failed, try local simulation
51
- if (simulationTransactionInternalResult.kind === 'error') {
52
- if (transactions.length > 1) {
53
- // For batched transactions, don't attempt local simulation
54
- const message = 'Cannot locally simulate batched transactions.';
55
- simulationResult.warnings.push({ kind: 'INTERNAL_ERROR', severity: 'HIGH', message });
56
- simulationResult.metadata.humanReadableError = message;
57
- }
58
- else {
59
- const transaction = transactions[0];
60
- // Local simulation also mutates the simulationResult, so we need to check if it was successful
61
- const simulatedLocally = tryEstimatingChangesLocally({
62
- asset,
63
- simulationResult,
64
- transaction,
65
- });
66
- // If local simulation succeeded, add fee for the (single) transaction now
67
- if (simulatedLocally) {
68
- simulationResult.metadata.simulatedLocally = true;
69
- simulationResult.balanceChanges.willPayFee.push({
70
- balance: estimateFee({ asset, transaction }),
71
- feeDetails: getTxFeeDetails(transaction),
72
- });
73
- }
74
- else {
75
- // Local Simulation also failed.
76
- const message = 'Balance changes cannot be estimated.';
77
- simulationResult.warnings.push({ kind: 'INTERNAL_ERROR', severity: 'HIGH', message });
78
- simulationResult.metadata.humanReadableError = message;
79
- }
80
- }
81
- if (simulationTransactionInternalResult.errorMessage) {
82
- simulationResult.metadata.humanReadableError = simulationTransactionInternalResult.errorMessage;
83
- }
84
- return simulationResult;
85
- }
86
- // Remote simulation succeeded; add fees for all transactions now
87
- for (const transaction of transactions) {
88
- simulationResult.balanceChanges.willPayFee.push({
89
- balance: estimateFee({ asset, transaction }),
90
- feeDetails: getTxFeeDetails(transaction),
91
- });
92
- }
93
- simulationResult.displayDetails = getDisplayDetails(simulationResult.balanceChanges.willApprove);
94
- return simulationResult;
95
- };
1
+ // @ts-expect-error @exodus/ethereum-api is a JavaScript package without deep declarations.
2
+ import { createSimulateTransactions as createEthereumApiSimulateTransactions } from '@exodus/ethereum-api/src/simulation/create-simulate-transactions.js';
3
+ export const createSimulateTransactions = createEthereumApiSimulateTransactions;
package/lib/types.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import type { MessageTypeEnum } from './simulation/getMessageType.js';
2
- import type { AggregatedTransactionSimulationResult, Asset, BN, MessageSimulationResult, SimulateTransactionParams } from '@exodus/web3-types';
1
+ import type { AggregatedTransactionSimulationResult, Asset, BN, SimulateTransactionParams } from '@exodus/web3-types';
3
2
  export interface EthTransactionParams {
4
3
  chainId: number;
5
4
  from: string;
@@ -61,14 +60,3 @@ export interface EthSimulateMessageParams {
61
60
  url: URL;
62
61
  address: string;
63
62
  }
64
- export interface SimulateMessageParams {
65
- address: EthSimulateMessageParams['address'];
66
- message: {
67
- message: string;
68
- messageType: MessageTypeEnum.RawMessage | MessageTypeEnum.TypedData;
69
- };
70
- url: EthSimulateMessageParams['url'];
71
- apiEndpoint: string;
72
- headers: Record<string, string>;
73
- simulationResult: MessageSimulationResult;
74
- }
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.7.5",
4
+ "version": "4.7.6",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "module": "lib/index.js",
@@ -35,16 +35,17 @@
35
35
  "test": "run -T exodus-test --jest --esbuild"
36
36
  },
37
37
  "devDependencies": {
38
- "@exodus/assets": "^11.9.0",
38
+ "@exodus/assets": "^11.10.0",
39
39
  "@exodus/ethereum-meta": "^2.12.0",
40
- "@exodus/web3-types": "1.59.2",
41
40
  "lodash": "^4.17.21"
42
41
  },
43
42
  "dependencies": {
44
43
  "@exodus/basic-utils": "^3.0.1",
44
+ "@exodus/ethereum-api": "^8.76.4",
45
45
  "@exodus/ethereum-lib": "^5.20.0",
46
46
  "@exodus/ethereumjs": "^1.5.0",
47
47
  "@exodus/solidity-contract": "^1.2.1",
48
+ "@exodus/web3-types": "^1.62.0",
48
49
  "@exodus/web3-utils": "^1.50.1",
49
50
  "bn.js": "^5.2.0",
50
51
  "minimalistic-assert": "^1.0.1"
@@ -54,5 +55,5 @@
54
55
  "url": "https://github.com/ExodusMovement/assets/issues?q=is%3Aissue+is%3Aopen+label%3Aweb3-ethereum-utils"
55
56
  },
56
57
  "homepage": "https://github.com/ExodusMovement/assets/tree/master/web3/web3-ethereum-utils",
57
- "gitHead": "38608063e5eafb78384c129e0b24b2da2d3ab238"
58
+ "gitHead": "6defbed4f39a326cb0c2d5fcdadf3f2abfb83a46"
58
59
  }
@@ -1,10 +0,0 @@
1
- import BN from 'bn.js';
2
- interface BlowfishEvmChain {
3
- [key: string]: {
4
- network: string;
5
- chain: string;
6
- };
7
- }
8
- export declare const BLOWFISH_EVM_CHAINS: BlowfishEvmChain;
9
- export declare const MAX_INT256_SOLIDITY: BN;
10
- export {};
@@ -1,33 +0,0 @@
1
- import BN from 'bn.js';
2
- export const BLOWFISH_EVM_CHAINS = {
3
- avalanchec: {
4
- network: 'avalanche',
5
- chain: 'mainnet',
6
- },
7
- basemainnet: {
8
- network: 'base',
9
- chain: 'mainnet',
10
- },
11
- bsc: {
12
- network: 'bnb',
13
- chain: 'mainnet',
14
- },
15
- ethereum: {
16
- network: 'ethereum',
17
- chain: 'mainnet',
18
- },
19
- ethereumarbone: {
20
- network: 'arbitrum',
21
- chain: 'one',
22
- },
23
- matic: {
24
- network: 'polygon',
25
- chain: 'mainnet',
26
- },
27
- optimism: {
28
- network: 'optimism',
29
- chain: 'mainnet',
30
- },
31
- };
32
- // The maximum signed integer 2\^255 - 1 represented in BN.
33
- export const MAX_INT256_SOLIDITY = new BN(2).pow(new BN(255)).subn(1);
@@ -1,6 +0,0 @@
1
- import type { EthTransaction } from '../types.js';
2
- import type { Asset, NumberUnit } from '@exodus/web3-types';
3
- export declare const estimateFee: ({ asset, transaction, }: {
4
- asset: Asset;
5
- transaction: EthTransaction;
6
- }) => NumberUnit;
@@ -1,21 +0,0 @@
1
- /*
2
- This utility determines a preliminary fee a transaction is going to pay based on
3
- its "gas", "gasPrice" and "maxFeePerGas" properties.
4
-
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.
10
- */
11
- import { hexToBN } from '@exodus/web3-utils';
12
- export const estimateFee = ({ asset, transaction, }) => {
13
- const gas = hexToBN(transaction.gas);
14
- if (transaction.maxFeePerGas) {
15
- const maxFeePerGas = hexToBN(transaction.maxFeePerGas);
16
- const maximumFee = gas.mul(maxFeePerGas);
17
- return asset.currency.baseUnit(maximumFee.toString());
18
- }
19
- const gasPrice = hexToBN(transaction.gasPrice);
20
- return asset.currency.baseUnit(gas.mul(gasPrice).toString());
21
- };
@@ -1,9 +0,0 @@
1
- import type { EthTransaction } from '../types.js';
2
- import type { Asset, NumberUnit } from '@exodus/web3-types';
3
- export declare class NotSimpleTransferError extends Error {
4
- constructor(msg: string);
5
- }
6
- export declare const estimateSimpleTransfer: ({ asset, transaction, }: {
7
- asset: Asset;
8
- transaction: EthTransaction;
9
- }) => NumberUnit | never;
@@ -1,22 +0,0 @@
1
- /*
2
- This utility determines whether the provided transaction is a simple native token transfer
3
- and returns the value it's sending if that's true. Throws an error otherwise.
4
-
5
- Note: only simple transfers (non-smart contract calls) can be reliably evaluated locally.
6
- Even empty `data` field transactions ("0x") can cause unexpected state changes if sent to a smart contract
7
- with a gas limit exceeding 21000 units.
8
- */
9
- import { hexToBN } from '@exodus/web3-utils';
10
- import { isSimpleTransfer } from '../transactions.js';
11
- export class NotSimpleTransferError extends Error {
12
- constructor(msg) {
13
- super(msg);
14
- }
15
- }
16
- export const estimateSimpleTransfer = ({ asset, transaction, }) => {
17
- if (!isSimpleTransfer(transaction)) {
18
- throw new NotSimpleTransferError('The transaction input should be empty ("0x").');
19
- }
20
- const txValue = hexToBN(transaction.value || '0x0').toString();
21
- return asset.currency.baseUnit(txValue);
22
- };
@@ -1,6 +0,0 @@
1
- export declare const enum MessageTypeEnum {
2
- 'RawMessage' = 0,// References to the `eth_sign` RPC method.
3
- 'TypedData' = 1,// References to the `eth_signTypedData` RPC method.
4
- 'Unknown' = 2
5
- }
6
- export declare const getMessageType: (message: string) => MessageTypeEnum;
@@ -1,21 +0,0 @@
1
- /*
2
- This utility figures out if the supplied message is plaint text to be signed or a structured typed data.
3
- See for differences:
4
- - https://docs.metamask.io/wallet/reference/personal_sign/
5
- - https://docs.metamask.io/wallet/reference/eth_signtypeddata_v4/
6
-
7
- The utility does not make any security checks (e.g. schema validation), it's supposed to be used only for choosing
8
- a proper payload type for the simulation API.
9
- */
10
- export const getMessageType = (message) => {
11
- if (typeof message !== 'string') {
12
- return 2 /* MessageTypeEnum.Unknown */;
13
- }
14
- try {
15
- JSON.parse(message);
16
- return 1 /* MessageTypeEnum.TypedData */;
17
- }
18
- catch (err) {
19
- return 0 /* MessageTypeEnum.RawMessage */;
20
- }
21
- };
@@ -1,7 +0,0 @@
1
- import type { SimulateMessageParams } from '../types.js';
2
- import type { MessageSimulationResult, ScanMessageEvm200Response } from '@exodus/web3-types';
3
- export declare const handleActionResponse: ({ simulationResult, response, }: {
4
- simulationResult: MessageSimulationResult;
5
- response: ScanMessageEvm200Response;
6
- }) => void;
7
- export declare const simulateMessage: ({ address, apiEndpoint, message, url, headers, simulationResult, }: SimulateMessageParams) => Promise<void>;
@@ -1,46 +0,0 @@
1
- import { makeSimulationAPICall } from '@exodus/web3-utils';
2
- import { BLOWFISH_EVM_CHAINS } from './common.js';
3
- const convertToAPIPayload = ({ address, message, url, }) => {
4
- const restParameters = {
5
- metadata: { origin: url.href },
6
- userAccount: address,
7
- };
8
- if (message.messageType === 1 /* MessageTypeEnum.TypedData */) {
9
- const kind = 'SIGN_TYPED_DATA';
10
- return {
11
- message: {
12
- kind,
13
- data: JSON.parse(message.message),
14
- },
15
- ...restParameters,
16
- };
17
- }
18
- const kind = 'SIGN_MESSAGE';
19
- return {
20
- message: {
21
- kind,
22
- rawMessage: message.message,
23
- },
24
- ...restParameters,
25
- };
26
- };
27
- export const handleActionResponse = ({ simulationResult, response, }) => {
28
- if (['BLOCK', 'WARN'].includes(response.action)) {
29
- simulationResult.action = response.action;
30
- }
31
- };
32
- export const simulateMessage = async ({ address, apiEndpoint, message, url, headers, simulationResult, }) => {
33
- const { network, chain } = BLOWFISH_EVM_CHAINS[simulationResult.baseAssetName];
34
- const payload = convertToAPIPayload({ address, message, url });
35
- const response = await makeSimulationAPICall({
36
- url: apiEndpoint,
37
- chain,
38
- network,
39
- payload,
40
- headers,
41
- });
42
- if (!response) {
43
- return;
44
- }
45
- handleActionResponse({ simulationResult, response });
46
- };
@@ -1,14 +0,0 @@
1
- import type { EthAggregatedTransactionSimulationResult, EthSimulateTransactionParams } from '../types.js';
2
- import type { CreateSimulateTransactionsParams, SimulateTransactionsInternalResult } from '@exodus/web3-types';
3
- type SimulateTransactionParams = Required<Pick<CreateSimulateTransactionsParams, 'apiEndpoint'>> & Required<Pick<EthSimulateTransactionParams, 'asset' | 'transactions' | 'origin'>> & {
4
- blockNumber?: EthSimulateTransactionParams['blockNumber'];
5
- simulationResult: EthAggregatedTransactionSimulationResult;
6
- headers?: Record<string, string>;
7
- };
8
- export declare const enum SimulationErrorMessages {
9
- 'APICallFailed' = 0,
10
- 'SimulationFailed' = 1,
11
- 'SimulationNotSupported' = 2
12
- }
13
- export declare const simulateTransactions: ({ transactions, apiEndpoint, origin, blockNumber, simulationResult, headers, asset, }: SimulateTransactionParams) => SimulateTransactionsInternalResult<undefined, SimulationErrorMessages>;
14
- export {};
@@ -1,225 +0,0 @@
1
- /*
2
- This utility makes an API call to a simulation service, parses the response and mutates the supplied "simulationResult" object
3
- with the according balance changes.
4
- */
5
- import { pick } from '@exodus/basic-utils';
6
- import { createCurrency, makeSimulationAPICall } from '@exodus/web3-utils';
7
- import { BLOWFISH_EVM_CHAINS, MAX_INT256_SOLIDITY } from './common.js';
8
- const convertTransactionToApiPayload = (transactions, origin, blockNumber) => {
9
- const payload = {
10
- txObjects: transactions.map((transaction) => ({
11
- from: transaction.from,
12
- to: transaction.to,
13
- data: transaction.data,
14
- value: transaction.value,
15
- gas: transaction.gas,
16
- gas_price: transaction.gasPrice,
17
- })),
18
- metadata: {
19
- origin,
20
- },
21
- userAccount: transactions[0].from,
22
- };
23
- if (blockNumber !== undefined) {
24
- payload.blockNumber = blockNumber;
25
- }
26
- return payload;
27
- };
28
- const createBalanceAmount = (amount, { decimals, symbol }) => {
29
- const beforeAmount = createCurrency({
30
- amount: amount.before,
31
- symbol: symbol,
32
- denominator: decimals,
33
- });
34
- const afterAmount = createCurrency({
35
- amount: amount.after,
36
- symbol: symbol,
37
- denominator: decimals,
38
- });
39
- return beforeAmount.sub(afterAmount);
40
- };
41
- const handleExpectedBalanceChange = (expectedBalanceChange, simulationResult, { network }) => {
42
- const { willApprove, willSend, willReceive } = simulationResult.balanceChanges;
43
- const { data, kind } = expectedBalanceChange.rawInfo;
44
- if (kind === 'FARCASTER_CHANGE_RECOVERY_ADDRESS' || kind === 'ERC20_PERMIT') {
45
- return;
46
- }
47
- const { asset } = data;
48
- // ERC1155 is a multi-token standard (https://eips.ethereum.org/EIPS/eip-1155) and can be used for NFTs as well.
49
- const isNftTransfer = kind === 'ERC721_TRANSFER' ||
50
- (kind === 'ERC1155_TRANSFER' && !asset.decimals);
51
- const isNftTransferApproval = kind === 'ERC721_APPROVAL' ||
52
- (kind === 'ERC721_APPROVAL_FOR_ALL' && !asset.decimals) ||
53
- (kind === 'ERC1155_APPROVAL_FOR_ALL' && !asset.decimals);
54
- const decimals = isNftTransfer || isNftTransferApproval || !Number.isFinite(asset.decimals) ? 0 : asset.decimals;
55
- const asset_ = pick(asset, [
56
- 'address',
57
- 'imageUrl',
58
- 'name',
59
- 'symbol',
60
- 'verified',
61
- ]);
62
- // According to the Blowfish docs, `symbol` and `name` can be null as they are not required by the standard.
63
- // Using `NFT` as a fallback ensures a user sees a meaningful UI in that case.
64
- let symbol = asset.symbol || asset.name || 'NFT';
65
- // TODO: remove this once Blowfish completes their migration from Matic to POL.
66
- if (network === 'polygon' && asset.symbol === 'MATIC') {
67
- symbol = 'POL';
68
- }
69
- if (isNftTransfer || isNftTransferApproval) {
70
- asset_.imageUrl = data?.metadata?.rawImageUrl;
71
- }
72
- if (kind === 'ERC20_APPROVAL' ||
73
- kind === 'ERC721_APPROVAL_FOR_ALL' ||
74
- kind === 'ERC1155_APPROVAL_FOR_ALL' ||
75
- kind === 'ERC721_APPROVAL') {
76
- const unitsToUse = symbol || asset.name || 'Units.';
77
- const approvingBalance = createCurrency({
78
- amount: data.amount.after,
79
- symbol: unitsToUse,
80
- denominator: decimals,
81
- });
82
- willApprove.push({
83
- asset: asset_,
84
- spender: data.spender.address,
85
- unitName: unitsToUse,
86
- balance: approvingBalance,
87
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
88
- // @ts-ignore _number exists on NumberUnit, but deprecated
89
- isMaxApproval: approvingBalance._number.gte(MAX_INT256_SOLIDITY),
90
- });
91
- return;
92
- }
93
- const { amount } = data;
94
- const balance = createBalanceAmount(amount, { decimals, symbol });
95
- if (balance.isZero) {
96
- return;
97
- }
98
- const balanceChange = {
99
- asset: asset_,
100
- balance,
101
- };
102
- if (isNftTransfer || isNftTransferApproval) {
103
- const tokenId = data.tokenId;
104
- const compositeId = `${asset.address}/${tokenId}`;
105
- balanceChange.nft = {
106
- id: `${network}:${compositeId}`,
107
- compositeId,
108
- title: asset.name,
109
- };
110
- }
111
- if (balance.isNegative) {
112
- // Convert to non-negative value.
113
- balanceChange.balance = balanceChange.balance.negate();
114
- willReceive.push(balanceChange);
115
- }
116
- else {
117
- willSend.push(balanceChange);
118
- }
119
- };
120
- const simulationResultHasAssets = (expectedStateChanges) => {
121
- for (const expectedChange of expectedStateChanges) {
122
- const { asset } = expectedChange.rawInfo
123
- .data;
124
- if (!asset) {
125
- return false;
126
- }
127
- }
128
- return true;
129
- };
130
- export const simulateTransactions = async ({ transactions, apiEndpoint, origin, blockNumber, simulationResult, headers, asset, }) => {
131
- const successDefaultResult = {
132
- kind: 'success',
133
- value: undefined,
134
- };
135
- if (!BLOWFISH_EVM_CHAINS[asset.name]) {
136
- return {
137
- kind: 'error',
138
- error: 2 /* SimulationErrorMessages.SimulationNotSupported */,
139
- errorMessage: 'Simulation is not supported for this asset.',
140
- };
141
- }
142
- //Only one user address is supported
143
- //Sending multiple transactions with different from addresses would lead to invalid results
144
- const userAddress = transactions[0].from;
145
- const payload = convertTransactionToApiPayload(transactions, origin, blockNumber);
146
- if (!Object.hasOwnProperty.call(BLOWFISH_EVM_CHAINS, asset.name)) {
147
- // ToDo: accept a logger dep and log the error
148
- return successDefaultResult;
149
- }
150
- const { network, chain } = BLOWFISH_EVM_CHAINS[asset.name];
151
- const response = await makeSimulationAPICall({
152
- url: apiEndpoint,
153
- chain,
154
- network,
155
- payload,
156
- headers,
157
- });
158
- if (!response) {
159
- return {
160
- kind: 'error',
161
- error: 0 /* SimulationErrorMessages.APICallFailed */,
162
- errorMessage: 'Simulation API call failed.',
163
- };
164
- }
165
- simulationResult.metadata.simulatedLocally = false;
166
- const { expectedStateChanges = {}, error } = response.simulationResults.aggregated;
167
- if (error) {
168
- // ToDo: accept a logger dep and log the error
169
- // ToDo: consider pushing an error to the "warnings" list
170
- return {
171
- kind: 'error',
172
- error: 1 /* SimulationErrorMessages.SimulationFailed */,
173
- errorMessage: error?.humanReadableError,
174
- };
175
- }
176
- const { action } = response;
177
- if (action === 'BLOCK') {
178
- const warning = {
179
- kind: 'MALICIOUS_ACTION',
180
- severity: 'CRITICAL',
181
- // We use a generic default message unless we find a known warning type below
182
- // (TODO: add known warning types)
183
- message: 'This dApp could be malicious. Do not proceed unless you are certain this is safe.',
184
- };
185
- simulationResult.warnings.push(warning);
186
- }
187
- else if (action === 'WARN') {
188
- const warning = {
189
- kind: 'SUSPICIOUS_ACTION',
190
- severity: 'MEDIUM',
191
- message: 'This transaction may be suspicious. Proceed with caution.',
192
- };
193
- simulationResult.warnings.push(warning);
194
- }
195
- const noExpectedStateChangesDetected = Object.keys(expectedStateChanges).length === 0;
196
- // This is a self-send transaction, adding a zero "transfer" so that the UI can display it.
197
- if (noExpectedStateChangesDetected) {
198
- simulationResult.balanceChanges.willSend.push({
199
- balance: createCurrency({
200
- amount: 0,
201
- symbol: asset.displayTicker,
202
- denominator: asset.currency.defaultUnit.power,
203
- }),
204
- });
205
- return successDefaultResult;
206
- }
207
- const userExpectedChanges = Object.keys(expectedStateChanges).reduce((prev, address) => {
208
- if (userAddress.toLowerCase() !== address.toLowerCase()) {
209
- return prev;
210
- }
211
- const result = (Object.prototype.hasOwnProperty.call(expectedStateChanges, address) &&
212
- expectedStateChanges[address]) ||
213
- [];
214
- return [...prev, ...result];
215
- }, []);
216
- // Return and log if asset is not present in the expectedStateChanges
217
- if (!simulationResultHasAssets(userExpectedChanges)) {
218
- // ToDo: accept a logger dep and log the error
219
- return successDefaultResult;
220
- }
221
- userExpectedChanges.forEach((expectedChange) => {
222
- handleExpectedBalanceChange(expectedChange, simulationResult, { network });
223
- });
224
- return successDefaultResult;
225
- };
@@ -1,7 +0,0 @@
1
- import type { EthAggregatedTransactionSimulationResult, EthTransaction } from '../types.js';
2
- import type { Asset } from '@exodus/web3-types';
3
- export declare const tryEstimatingChangesLocally: ({ asset, simulationResult, transaction, }: {
4
- asset: Asset;
5
- simulationResult: EthAggregatedTransactionSimulationResult;
6
- transaction: EthTransaction;
7
- }) => boolean;
@@ -1,24 +0,0 @@
1
- import { estimateSimpleTransfer, NotSimpleTransferError, } from './estimateSimpleTransfer.js';
2
- /*
3
- Mutates the provided 'simulationResult' adding a 'willSend' entry if the transaction is a simple transfer.
4
- The function also returns a boolean indicating whether the local simulation was possible (e.g., if any balance changes were made).
5
- */
6
- export const tryEstimatingChangesLocally = ({ asset, simulationResult, transaction, }) => {
7
- try {
8
- const estimatedValue = estimateSimpleTransfer({ asset, transaction });
9
- const actualTransferValue = transaction.from.toLowerCase() === transaction.to.toLowerCase()
10
- ? asset.currency.ZERO // A self-send does not transfer any funds.
11
- : estimatedValue;
12
- simulationResult.balanceChanges.willSend.push({
13
- balance: actualTransferValue,
14
- });
15
- return true;
16
- }
17
- catch (err) {
18
- const expectedError = err instanceof NotSimpleTransferError;
19
- if (!expectedError) {
20
- // ToDo: accept a logger dep and log an error here
21
- }
22
- return false;
23
- }
24
- };