mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-24 19:10:22 +01:00
Ensure 0% slippage on Arbitrum for wrapping / unwrapping (#16778)
* Contract addresses to lowercase * Use case insensitive checks in the shouldEnableDirectWrapping fn * Remove duplicated functions, move unit tests into the right location
This commit is contained in:
parent
0487de6e30
commit
7f50febfcd
@ -74,17 +74,14 @@ export const OPTIMISM_SWAPS_TOKEN_OBJECT = { ...ETH_SWAPS_TOKEN_OBJECT };
|
|||||||
// A gas value for ERC20 approve calls that should be sufficient for all ERC20 approve implementations
|
// A gas value for ERC20 approve calls that should be sufficient for all ERC20 approve implementations
|
||||||
export const DEFAULT_ERC20_APPROVE_GAS = '0x1d4c0';
|
export const DEFAULT_ERC20_APPROVE_GAS = '0x1d4c0';
|
||||||
|
|
||||||
|
// Contract addresses below should be in lowercase.
|
||||||
const MAINNET_CONTRACT_ADDRESS = '0x881d40237659c251811cec9c364ef91dc08d300c';
|
const MAINNET_CONTRACT_ADDRESS = '0x881d40237659c251811cec9c364ef91dc08d300c';
|
||||||
|
|
||||||
const TESTNET_CONTRACT_ADDRESS = '0x881d40237659c251811cec9c364ef91dc08d300c';
|
const TESTNET_CONTRACT_ADDRESS = '0x881d40237659c251811cec9c364ef91dc08d300c';
|
||||||
|
|
||||||
const BSC_CONTRACT_ADDRESS = '0x1a1ec25dc08e98e5e93f1104b5e5cdd298707d31';
|
const BSC_CONTRACT_ADDRESS = '0x1a1ec25dc08e98e5e93f1104b5e5cdd298707d31';
|
||||||
|
|
||||||
// It's the same as we use for BSC.
|
|
||||||
const POLYGON_CONTRACT_ADDRESS = '0x1a1ec25dc08e98e5e93f1104b5e5cdd298707d31';
|
const POLYGON_CONTRACT_ADDRESS = '0x1a1ec25dc08e98e5e93f1104b5e5cdd298707d31';
|
||||||
const AVALANCHE_CONTRACT_ADDRESS = '0x1a1ec25dc08e98e5e93f1104b5e5cdd298707d31';
|
const AVALANCHE_CONTRACT_ADDRESS = '0x1a1ec25dc08e98e5e93f1104b5e5cdd298707d31';
|
||||||
const OPTIMISM_CONTRACT_ADDRESS = '0x9dDA6Ef3D919c9bC8885D5560999A3640431e8e6';
|
const OPTIMISM_CONTRACT_ADDRESS = '0x9dda6ef3d919c9bc8885d5560999a3640431e8e6';
|
||||||
const ARBITRUM_CONTRACT_ADDRESS = '0x9dDA6Ef3D919c9bC8885D5560999A3640431e8e6';
|
const ARBITRUM_CONTRACT_ADDRESS = '0x9dda6ef3d919c9bc8885d5560999a3640431e8e6';
|
||||||
|
|
||||||
export const WETH_CONTRACT_ADDRESS =
|
export const WETH_CONTRACT_ADDRESS =
|
||||||
'0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
|
'0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
|
||||||
@ -100,7 +97,7 @@ export const WAVAX_CONTRACT_ADDRESS =
|
|||||||
export const WETH_OPTIMISM_CONTRACT_ADDRESS =
|
export const WETH_OPTIMISM_CONTRACT_ADDRESS =
|
||||||
'0x4200000000000000000000000000000000000006';
|
'0x4200000000000000000000000000000000000006';
|
||||||
export const WETH_ARBITRUM_CONTRACT_ADDRESS =
|
export const WETH_ARBITRUM_CONTRACT_ADDRESS =
|
||||||
'0x82aF49447D8a07e3bd95BD0d56f35241523fBab1';
|
'0x82af49447d8a07e3bd95bd0d56f35241523fbab1';
|
||||||
|
|
||||||
const SWAPS_TESTNET_CHAIN_ID = '0x539';
|
const SWAPS_TESTNET_CHAIN_ID = '0x539';
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import {
|
|||||||
} from '../constants/swaps';
|
} from '../constants/swaps';
|
||||||
import { SECOND } from '../constants/time';
|
import { SECOND } from '../constants/time';
|
||||||
import { isValidHexAddress } from '../modules/hexstring-utils';
|
import { isValidHexAddress } from '../modules/hexstring-utils';
|
||||||
|
import { isEqualCaseInsensitive } from '../modules/string-utils';
|
||||||
import { addHexPrefix } from '../../app/scripts/lib/util';
|
import { addHexPrefix } from '../../app/scripts/lib/util';
|
||||||
import fetchWithCache from './fetch-with-cache';
|
import fetchWithCache from './fetch-with-cache';
|
||||||
import { decimalToHex } from './transactions-controller-utils';
|
import { decimalToHex } from './transactions-controller-utils';
|
||||||
@ -184,13 +185,11 @@ export const shouldEnableDirectWrapping = (
|
|||||||
}
|
}
|
||||||
const wrappedToken = SWAPS_WRAPPED_TOKENS_ADDRESSES[chainId];
|
const wrappedToken = SWAPS_WRAPPED_TOKENS_ADDRESSES[chainId];
|
||||||
const nativeToken = SWAPS_CHAINID_DEFAULT_TOKEN_MAP[chainId]?.address;
|
const nativeToken = SWAPS_CHAINID_DEFAULT_TOKEN_MAP[chainId]?.address;
|
||||||
const sourceTokenLowerCase = sourceToken.toLowerCase();
|
|
||||||
const destinationTokenLowerCase = destinationToken.toLowerCase();
|
|
||||||
return (
|
return (
|
||||||
(sourceTokenLowerCase === wrappedToken &&
|
(isEqualCaseInsensitive(sourceToken, wrappedToken) &&
|
||||||
destinationTokenLowerCase === nativeToken) ||
|
isEqualCaseInsensitive(destinationToken, nativeToken)) ||
|
||||||
(sourceTokenLowerCase === nativeToken &&
|
(isEqualCaseInsensitive(sourceToken, nativeToken) &&
|
||||||
destinationTokenLowerCase === wrappedToken)
|
isEqualCaseInsensitive(destinationToken, wrappedToken))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
227
shared/lib/swaps-utils.test.js
Normal file
227
shared/lib/swaps-utils.test.js
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
import nock from 'nock';
|
||||||
|
import { CHAIN_IDS } from '../constants/network';
|
||||||
|
import {
|
||||||
|
SWAPS_CHAINID_DEFAULT_TOKEN_MAP,
|
||||||
|
WETH_CONTRACT_ADDRESS,
|
||||||
|
WBNB_CONTRACT_ADDRESS,
|
||||||
|
WMATIC_CONTRACT_ADDRESS,
|
||||||
|
} from '../constants/swaps';
|
||||||
|
import {
|
||||||
|
TOKENS,
|
||||||
|
MOCK_TRADE_RESPONSE_2,
|
||||||
|
} from '../../ui/pages/swaps/swaps-util-test-constants';
|
||||||
|
import { fetchTradesInfo, shouldEnableDirectWrapping } from './swaps-utils';
|
||||||
|
|
||||||
|
jest.mock('./storage-helpers', () => ({
|
||||||
|
getStorageItem: jest.fn(),
|
||||||
|
setStorageItem: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('Swaps Utils', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
nock.cleanAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('fetchTradesInfo', () => {
|
||||||
|
const expectedResult1 = {
|
||||||
|
zeroEx: {
|
||||||
|
trade: {
|
||||||
|
// the ethereum transaction data for the swap
|
||||||
|
data: '0xa6c3bf330000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000004e0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000005591360f8c7640fea5771c9682d6b5ecb776e1f8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021486a000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005efe3c3b5dfc3a75ffc8add04bbdbac1e42fa234bf4549d8dab1bc44c8056eaf0e1dfe8600000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000001c4dc1600f3000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000005591360f8c7640fea5771c9682d6b5ecb776e1f800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000140000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000036691c4f426eb8f42f150ebde43069a31cb080ad000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000000000000021486a00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010400000000000000000000000000000000000000000000000000000000000000869584cd0000000000000000000000001000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000005efe201b',
|
||||||
|
from: '0x2369267687A84ac7B494daE2f1542C40E37f4455',
|
||||||
|
value: '0x14401eab384000',
|
||||||
|
to: '0x61935cbdd02287b511119ddb11aeb42f1593b7ef',
|
||||||
|
gas: '0xa',
|
||||||
|
gasPrice: undefined,
|
||||||
|
},
|
||||||
|
sourceAmount: '10000000000000000',
|
||||||
|
destinationAmount: '2248687',
|
||||||
|
error: null,
|
||||||
|
fee: 0.875,
|
||||||
|
sourceToken: TOKENS[0].address,
|
||||||
|
destinationToken: TOKENS[1].address,
|
||||||
|
fetchTime: 553,
|
||||||
|
aggregator: 'zeroEx',
|
||||||
|
aggType: 'AGG',
|
||||||
|
approvalNeeded: {
|
||||||
|
data: '0x095ea7b300000000000000000000000095e6f48254609a6ee006f7d493c8e5fb97094cef0000000000000000000000000000000000000000004a817c7ffffffdabf41c00',
|
||||||
|
to: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
|
||||||
|
value: '0x0',
|
||||||
|
from: '0x2369267687A84ac7B494daE2f1542C40E37f4455',
|
||||||
|
gas: '0x12',
|
||||||
|
gasPrice: '0x34',
|
||||||
|
},
|
||||||
|
maxGas: 10,
|
||||||
|
averageGas: 1,
|
||||||
|
slippage: '3',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const expectedResult2 = {
|
||||||
|
zeroEx: {
|
||||||
|
...expectedResult1.zeroEx,
|
||||||
|
sourceAmount: '20000000000000000',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
it('should fetch trade info on prod', async () => {
|
||||||
|
nock('https://swap.metaswap.codefi.network')
|
||||||
|
.get('/networks/1/trades')
|
||||||
|
.query(true)
|
||||||
|
.reply(200, MOCK_TRADE_RESPONSE_2);
|
||||||
|
|
||||||
|
const result = await fetchTradesInfo(
|
||||||
|
{
|
||||||
|
TOKENS,
|
||||||
|
slippage: '3',
|
||||||
|
sourceToken: TOKENS[0].address,
|
||||||
|
destinationToken: TOKENS[1].address,
|
||||||
|
value: '2000000000000000000',
|
||||||
|
fromAddress: '0xmockAddress',
|
||||||
|
sourceSymbol: TOKENS[0].symbol,
|
||||||
|
sourceDecimals: TOKENS[0].decimals,
|
||||||
|
sourceTokenInfo: { ...TOKENS[0] },
|
||||||
|
destinationTokenInfo: { ...TOKENS[1] },
|
||||||
|
},
|
||||||
|
{ chainId: CHAIN_IDS.MAINNET },
|
||||||
|
);
|
||||||
|
expect(result).toStrictEqual(expectedResult2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('shouldEnableDirectWrapping', () => {
|
||||||
|
const randomTokenAddress = '0x881d40237659c251811cec9c364ef91234567890';
|
||||||
|
|
||||||
|
it('returns true if swapping from ETH to WETH', () => {
|
||||||
|
expect(
|
||||||
|
shouldEnableDirectWrapping(
|
||||||
|
CHAIN_IDS.MAINNET,
|
||||||
|
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.MAINNET]?.address,
|
||||||
|
WETH_CONTRACT_ADDRESS,
|
||||||
|
),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true if swapping from ETH with uppercase chars to WETH', () => {
|
||||||
|
const ethAddressWithUpperCaseChars =
|
||||||
|
'0X0000000000000000000000000000000000000000';
|
||||||
|
expect(
|
||||||
|
shouldEnableDirectWrapping(
|
||||||
|
CHAIN_IDS.MAINNET,
|
||||||
|
ethAddressWithUpperCaseChars,
|
||||||
|
WETH_CONTRACT_ADDRESS,
|
||||||
|
),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true if swapping from WETH to ETH', () => {
|
||||||
|
expect(
|
||||||
|
shouldEnableDirectWrapping(
|
||||||
|
CHAIN_IDS.MAINNET,
|
||||||
|
WETH_CONTRACT_ADDRESS,
|
||||||
|
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.MAINNET]?.address,
|
||||||
|
),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true if swapping from WETH with uppercase chars to ETH', () => {
|
||||||
|
const wethContractAddressWithUpperCaseChars =
|
||||||
|
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
|
||||||
|
expect(
|
||||||
|
shouldEnableDirectWrapping(
|
||||||
|
CHAIN_IDS.MAINNET,
|
||||||
|
wethContractAddressWithUpperCaseChars,
|
||||||
|
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.MAINNET]?.address,
|
||||||
|
),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false if swapping from ETH to a non-WETH token', () => {
|
||||||
|
expect(
|
||||||
|
shouldEnableDirectWrapping(
|
||||||
|
CHAIN_IDS.MAINNET,
|
||||||
|
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.MAINNET]?.address,
|
||||||
|
randomTokenAddress,
|
||||||
|
),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true if swapping from BNB to WBNB', () => {
|
||||||
|
expect(
|
||||||
|
shouldEnableDirectWrapping(
|
||||||
|
CHAIN_IDS.BSC,
|
||||||
|
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.BSC]?.address,
|
||||||
|
WBNB_CONTRACT_ADDRESS,
|
||||||
|
),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true if swapping from WBNB to BNB', () => {
|
||||||
|
expect(
|
||||||
|
shouldEnableDirectWrapping(
|
||||||
|
CHAIN_IDS.BSC,
|
||||||
|
WBNB_CONTRACT_ADDRESS,
|
||||||
|
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.BSC]?.address,
|
||||||
|
),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false if swapping from BNB to a non-WBNB token', () => {
|
||||||
|
expect(
|
||||||
|
shouldEnableDirectWrapping(
|
||||||
|
CHAIN_IDS.BSC,
|
||||||
|
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.BSC]?.address,
|
||||||
|
randomTokenAddress,
|
||||||
|
),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true if swapping from MATIC to WMATIC', () => {
|
||||||
|
expect(
|
||||||
|
shouldEnableDirectWrapping(
|
||||||
|
CHAIN_IDS.POLYGON,
|
||||||
|
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.POLYGON]?.address,
|
||||||
|
WMATIC_CONTRACT_ADDRESS,
|
||||||
|
),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true if swapping from WMATIC to MATIC', () => {
|
||||||
|
expect(
|
||||||
|
shouldEnableDirectWrapping(
|
||||||
|
CHAIN_IDS.POLYGON,
|
||||||
|
WMATIC_CONTRACT_ADDRESS,
|
||||||
|
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.POLYGON]?.address,
|
||||||
|
),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false if swapping from MATIC to a non-WMATIC token', () => {
|
||||||
|
expect(
|
||||||
|
shouldEnableDirectWrapping(
|
||||||
|
CHAIN_IDS.POLYGON,
|
||||||
|
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.POLYGON]?.address,
|
||||||
|
randomTokenAddress,
|
||||||
|
),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false if a source token is undefined', () => {
|
||||||
|
expect(
|
||||||
|
shouldEnableDirectWrapping(
|
||||||
|
CHAIN_IDS.MAINNET,
|
||||||
|
undefined,
|
||||||
|
WETH_CONTRACT_ADDRESS,
|
||||||
|
),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false if a destination token is undefined', () => {
|
||||||
|
expect(
|
||||||
|
shouldEnableDirectWrapping(CHAIN_IDS.MAINNET, WETH_CONTRACT_ADDRESS),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false if source and destination tokens are undefined', () => {
|
||||||
|
expect(shouldEnableDirectWrapping(CHAIN_IDS.MAINNET)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -13,7 +13,6 @@ import {
|
|||||||
SWAPS_API_V2_BASE_URL,
|
SWAPS_API_V2_BASE_URL,
|
||||||
SWAPS_DEV_API_V2_BASE_URL,
|
SWAPS_DEV_API_V2_BASE_URL,
|
||||||
SWAPS_CLIENT_ID,
|
SWAPS_CLIENT_ID,
|
||||||
SWAPS_WRAPPED_TOKENS_ADDRESSES,
|
|
||||||
} from '../../../shared/constants/swaps';
|
} from '../../../shared/constants/swaps';
|
||||||
import {
|
import {
|
||||||
isSwapsDefaultTokenAddress,
|
isSwapsDefaultTokenAddress,
|
||||||
@ -32,14 +31,10 @@ import {
|
|||||||
toPrecisionWithoutTrailingZeros,
|
toPrecisionWithoutTrailingZeros,
|
||||||
} from '../../../shared/lib/transactions-controller-utils';
|
} from '../../../shared/lib/transactions-controller-utils';
|
||||||
import {
|
import {
|
||||||
calcTokenValue,
|
|
||||||
constructTxParams,
|
|
||||||
getBaseApi,
|
getBaseApi,
|
||||||
QUOTE_VALIDATORS,
|
|
||||||
truthyString,
|
truthyString,
|
||||||
validateData,
|
validateData,
|
||||||
} from '../../../shared/lib/swaps-utils';
|
} from '../../../shared/lib/swaps-utils';
|
||||||
import { SECOND } from '../../../shared/constants/time';
|
|
||||||
import { sumHexes } from '../../helpers/utils/transactions.util';
|
import { sumHexes } from '../../helpers/utils/transactions.util';
|
||||||
|
|
||||||
const CACHE_REFRESH_FIVE_MINUTES = 300000;
|
const CACHE_REFRESH_FIVE_MINUTES = 300000;
|
||||||
@ -106,99 +101,6 @@ const SWAP_GAS_PRICE_VALIDATOR = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const shouldEnableDirectWrapping = (
|
|
||||||
chainId,
|
|
||||||
sourceToken,
|
|
||||||
destinationToken,
|
|
||||||
) => {
|
|
||||||
if (!sourceToken || !destinationToken) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const wrappedToken = SWAPS_WRAPPED_TOKENS_ADDRESSES[chainId];
|
|
||||||
const nativeToken = SWAPS_CHAINID_DEFAULT_TOKEN_MAP[chainId]?.address;
|
|
||||||
const sourceTokenLowerCase = sourceToken.toLowerCase();
|
|
||||||
const destinationTokenLowerCase = destinationToken.toLowerCase();
|
|
||||||
return (
|
|
||||||
(sourceTokenLowerCase === wrappedToken &&
|
|
||||||
destinationTokenLowerCase === nativeToken) ||
|
|
||||||
(sourceTokenLowerCase === nativeToken &&
|
|
||||||
destinationTokenLowerCase === wrappedToken)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function fetchTradesInfo(
|
|
||||||
{
|
|
||||||
slippage,
|
|
||||||
sourceToken,
|
|
||||||
sourceDecimals,
|
|
||||||
destinationToken,
|
|
||||||
value,
|
|
||||||
fromAddress,
|
|
||||||
exchangeList,
|
|
||||||
},
|
|
||||||
{ chainId },
|
|
||||||
) {
|
|
||||||
const urlParams = {
|
|
||||||
destinationToken,
|
|
||||||
sourceToken,
|
|
||||||
sourceAmount: calcTokenValue(value, sourceDecimals).toString(10),
|
|
||||||
slippage,
|
|
||||||
timeout: SECOND * 10,
|
|
||||||
walletAddress: fromAddress,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (exchangeList) {
|
|
||||||
urlParams.exchangeList = exchangeList;
|
|
||||||
}
|
|
||||||
if (shouldEnableDirectWrapping(chainId, sourceToken, destinationToken)) {
|
|
||||||
urlParams.enableDirectWrapping = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const queryString = new URLSearchParams(urlParams).toString();
|
|
||||||
const tradeURL = `${getBaseApi('trade', chainId)}${queryString}`;
|
|
||||||
const tradesResponse = await fetchWithCache(
|
|
||||||
tradeURL,
|
|
||||||
{ method: 'GET', headers: clientIdHeader },
|
|
||||||
{ cacheRefreshTime: 0, timeout: SECOND * 15 },
|
|
||||||
);
|
|
||||||
const newQuotes = tradesResponse.reduce((aggIdTradeMap, quote) => {
|
|
||||||
if (
|
|
||||||
quote.trade &&
|
|
||||||
!quote.error &&
|
|
||||||
validateData(QUOTE_VALIDATORS, quote, tradeURL)
|
|
||||||
) {
|
|
||||||
const constructedTrade = constructTxParams({
|
|
||||||
to: quote.trade.to,
|
|
||||||
from: quote.trade.from,
|
|
||||||
data: quote.trade.data,
|
|
||||||
amount: decimalToHex(quote.trade.value),
|
|
||||||
gas: decimalToHex(quote.maxGas),
|
|
||||||
});
|
|
||||||
|
|
||||||
let { approvalNeeded } = quote;
|
|
||||||
|
|
||||||
if (approvalNeeded) {
|
|
||||||
approvalNeeded = constructTxParams({
|
|
||||||
...approvalNeeded,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...aggIdTradeMap,
|
|
||||||
[quote.aggregator]: {
|
|
||||||
...quote,
|
|
||||||
slippage,
|
|
||||||
trade: constructedTrade,
|
|
||||||
approvalNeeded,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return aggIdTradeMap;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
return newQuotes;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function fetchToken(contractAddress, chainId) {
|
export async function fetchToken(contractAddress, chainId) {
|
||||||
const tokenUrl = getBaseApi('token', chainId);
|
const tokenUrl = getBaseApi('token', chainId);
|
||||||
const token = await fetchWithCache(
|
const token = await fetchWithCache(
|
||||||
|
@ -4,7 +4,6 @@ import { CHAIN_IDS, CURRENCY_SYMBOLS } from '../../../shared/constants/network';
|
|||||||
import { getSwapsTokensReceivedFromTxMeta } from '../../../shared/lib/transactions-controller-utils';
|
import { getSwapsTokensReceivedFromTxMeta } from '../../../shared/lib/transactions-controller-utils';
|
||||||
import {
|
import {
|
||||||
SWAPS_CHAINID_CONTRACT_ADDRESS_MAP,
|
SWAPS_CHAINID_CONTRACT_ADDRESS_MAP,
|
||||||
SWAPS_CHAINID_DEFAULT_TOKEN_MAP,
|
|
||||||
WETH_CONTRACT_ADDRESS,
|
WETH_CONTRACT_ADDRESS,
|
||||||
WBNB_CONTRACT_ADDRESS,
|
WBNB_CONTRACT_ADDRESS,
|
||||||
WMATIC_CONTRACT_ADDRESS,
|
WMATIC_CONTRACT_ADDRESS,
|
||||||
@ -16,14 +15,9 @@ import {
|
|||||||
OPTIMISM,
|
OPTIMISM,
|
||||||
ARBITRUM,
|
ARBITRUM,
|
||||||
} from '../../../shared/constants/swaps';
|
} from '../../../shared/constants/swaps';
|
||||||
import {
|
|
||||||
fetchTradesInfo,
|
|
||||||
shouldEnableDirectWrapping,
|
|
||||||
} from '../../../shared/lib/swaps-utils';
|
|
||||||
import {
|
import {
|
||||||
TOKENS,
|
TOKENS,
|
||||||
EXPECTED_TOKENS_RESULT,
|
EXPECTED_TOKENS_RESULT,
|
||||||
MOCK_TRADE_RESPONSE_2,
|
|
||||||
AGGREGATOR_METADATA,
|
AGGREGATOR_METADATA,
|
||||||
TOP_ASSETS,
|
TOP_ASSETS,
|
||||||
} from './swaps-util-test-constants';
|
} from './swaps-util-test-constants';
|
||||||
@ -50,71 +44,6 @@ describe('Swaps Util', () => {
|
|||||||
nock.cleanAll();
|
nock.cleanAll();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('fetchTradesInfo', () => {
|
|
||||||
const expectedResult1 = {
|
|
||||||
zeroEx: {
|
|
||||||
trade: {
|
|
||||||
// the ethereum transaction data for the swap
|
|
||||||
data: '0xa6c3bf330000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000004e0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000005591360f8c7640fea5771c9682d6b5ecb776e1f8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021486a000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005efe3c3b5dfc3a75ffc8add04bbdbac1e42fa234bf4549d8dab1bc44c8056eaf0e1dfe8600000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000001c4dc1600f3000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000005591360f8c7640fea5771c9682d6b5ecb776e1f800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000140000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000036691c4f426eb8f42f150ebde43069a31cb080ad000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000000000000021486a00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010400000000000000000000000000000000000000000000000000000000000000869584cd0000000000000000000000001000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000005efe201b',
|
|
||||||
from: '0x2369267687A84ac7B494daE2f1542C40E37f4455',
|
|
||||||
value: '0x14401eab384000',
|
|
||||||
to: '0x61935cbdd02287b511119ddb11aeb42f1593b7ef',
|
|
||||||
gas: '0xa',
|
|
||||||
gasPrice: undefined,
|
|
||||||
},
|
|
||||||
sourceAmount: '10000000000000000',
|
|
||||||
destinationAmount: '2248687',
|
|
||||||
error: null,
|
|
||||||
fee: 0.875,
|
|
||||||
sourceToken: TOKENS[0].address,
|
|
||||||
destinationToken: TOKENS[1].address,
|
|
||||||
fetchTime: 553,
|
|
||||||
aggregator: 'zeroEx',
|
|
||||||
aggType: 'AGG',
|
|
||||||
approvalNeeded: {
|
|
||||||
data: '0x095ea7b300000000000000000000000095e6f48254609a6ee006f7d493c8e5fb97094cef0000000000000000000000000000000000000000004a817c7ffffffdabf41c00',
|
|
||||||
to: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
|
|
||||||
value: '0x0',
|
|
||||||
from: '0x2369267687A84ac7B494daE2f1542C40E37f4455',
|
|
||||||
gas: '0x12',
|
|
||||||
gasPrice: '0x34',
|
|
||||||
},
|
|
||||||
maxGas: 10,
|
|
||||||
averageGas: 1,
|
|
||||||
slippage: '3',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const expectedResult2 = {
|
|
||||||
zeroEx: {
|
|
||||||
...expectedResult1.zeroEx,
|
|
||||||
sourceAmount: '20000000000000000',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
it('should fetch trade info on prod', async () => {
|
|
||||||
nock('https://swap.metaswap.codefi.network')
|
|
||||||
.get('/networks/1/trades')
|
|
||||||
.query(true)
|
|
||||||
.reply(200, MOCK_TRADE_RESPONSE_2);
|
|
||||||
|
|
||||||
const result = await fetchTradesInfo(
|
|
||||||
{
|
|
||||||
TOKENS,
|
|
||||||
slippage: '3',
|
|
||||||
sourceToken: TOKENS[0].address,
|
|
||||||
destinationToken: TOKENS[1].address,
|
|
||||||
value: '2000000000000000000',
|
|
||||||
fromAddress: '0xmockAddress',
|
|
||||||
sourceSymbol: TOKENS[0].symbol,
|
|
||||||
sourceDecimals: TOKENS[0].decimals,
|
|
||||||
sourceTokenInfo: { ...TOKENS[0] },
|
|
||||||
destinationTokenInfo: { ...TOKENS[1] },
|
|
||||||
},
|
|
||||||
{ chainId: CHAIN_IDS.MAINNET },
|
|
||||||
);
|
|
||||||
expect(result).toStrictEqual(expectedResult2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('fetchTokens', () => {
|
describe('fetchTokens', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
nock('https://swap.metaswap.codefi.network')
|
nock('https://swap.metaswap.codefi.network')
|
||||||
@ -414,144 +343,6 @@ describe('Swaps Util', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('shouldEnableDirectWrapping', () => {
|
|
||||||
const randomTokenAddress = '0x881d40237659c251811cec9c364ef91234567890';
|
|
||||||
|
|
||||||
it('returns true if swapping from ETH to WETH', () => {
|
|
||||||
expect(
|
|
||||||
shouldEnableDirectWrapping(
|
|
||||||
CHAIN_IDS.MAINNET,
|
|
||||||
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.MAINNET]?.address,
|
|
||||||
WETH_CONTRACT_ADDRESS,
|
|
||||||
),
|
|
||||||
).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns true if swapping from ETH with uppercase chars to WETH', () => {
|
|
||||||
const ethAddressWithUpperCaseChars =
|
|
||||||
'0X0000000000000000000000000000000000000000';
|
|
||||||
expect(
|
|
||||||
shouldEnableDirectWrapping(
|
|
||||||
CHAIN_IDS.MAINNET,
|
|
||||||
ethAddressWithUpperCaseChars,
|
|
||||||
WETH_CONTRACT_ADDRESS,
|
|
||||||
),
|
|
||||||
).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns true if swapping from WETH to ETH', () => {
|
|
||||||
expect(
|
|
||||||
shouldEnableDirectWrapping(
|
|
||||||
CHAIN_IDS.MAINNET,
|
|
||||||
WETH_CONTRACT_ADDRESS,
|
|
||||||
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.MAINNET]?.address,
|
|
||||||
),
|
|
||||||
).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns true if swapping from WETH with uppercase chars to ETH', () => {
|
|
||||||
const wethContractAddressWithUpperCaseChars =
|
|
||||||
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
|
|
||||||
expect(
|
|
||||||
shouldEnableDirectWrapping(
|
|
||||||
CHAIN_IDS.MAINNET,
|
|
||||||
wethContractAddressWithUpperCaseChars,
|
|
||||||
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.MAINNET]?.address,
|
|
||||||
),
|
|
||||||
).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns false if swapping from ETH to a non-WETH token', () => {
|
|
||||||
expect(
|
|
||||||
shouldEnableDirectWrapping(
|
|
||||||
CHAIN_IDS.MAINNET,
|
|
||||||
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.MAINNET]?.address,
|
|
||||||
randomTokenAddress,
|
|
||||||
),
|
|
||||||
).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns true if swapping from BNB to WBNB', () => {
|
|
||||||
expect(
|
|
||||||
shouldEnableDirectWrapping(
|
|
||||||
CHAIN_IDS.BSC,
|
|
||||||
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.BSC]?.address,
|
|
||||||
WBNB_CONTRACT_ADDRESS,
|
|
||||||
),
|
|
||||||
).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns true if swapping from WBNB to BNB', () => {
|
|
||||||
expect(
|
|
||||||
shouldEnableDirectWrapping(
|
|
||||||
CHAIN_IDS.BSC,
|
|
||||||
WBNB_CONTRACT_ADDRESS,
|
|
||||||
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.BSC]?.address,
|
|
||||||
),
|
|
||||||
).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns false if swapping from BNB to a non-WBNB token', () => {
|
|
||||||
expect(
|
|
||||||
shouldEnableDirectWrapping(
|
|
||||||
CHAIN_IDS.BSC,
|
|
||||||
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.BSC]?.address,
|
|
||||||
randomTokenAddress,
|
|
||||||
),
|
|
||||||
).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns true if swapping from MATIC to WMATIC', () => {
|
|
||||||
expect(
|
|
||||||
shouldEnableDirectWrapping(
|
|
||||||
CHAIN_IDS.POLYGON,
|
|
||||||
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.POLYGON]?.address,
|
|
||||||
WMATIC_CONTRACT_ADDRESS,
|
|
||||||
),
|
|
||||||
).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns true if swapping from WMATIC to MATIC', () => {
|
|
||||||
expect(
|
|
||||||
shouldEnableDirectWrapping(
|
|
||||||
CHAIN_IDS.POLYGON,
|
|
||||||
WMATIC_CONTRACT_ADDRESS,
|
|
||||||
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.POLYGON]?.address,
|
|
||||||
),
|
|
||||||
).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns false if swapping from MATIC to a non-WMATIC token', () => {
|
|
||||||
expect(
|
|
||||||
shouldEnableDirectWrapping(
|
|
||||||
CHAIN_IDS.POLYGON,
|
|
||||||
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[CHAIN_IDS.POLYGON]?.address,
|
|
||||||
randomTokenAddress,
|
|
||||||
),
|
|
||||||
).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns false if a source token is undefined', () => {
|
|
||||||
expect(
|
|
||||||
shouldEnableDirectWrapping(
|
|
||||||
CHAIN_IDS.MAINNET,
|
|
||||||
undefined,
|
|
||||||
WETH_CONTRACT_ADDRESS,
|
|
||||||
),
|
|
||||||
).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns false if a destination token is undefined', () => {
|
|
||||||
expect(
|
|
||||||
shouldEnableDirectWrapping(CHAIN_IDS.MAINNET, WETH_CONTRACT_ADDRESS),
|
|
||||||
).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns false if source and destination tokens are undefined', () => {
|
|
||||||
expect(shouldEnableDirectWrapping(CHAIN_IDS.MAINNET)).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('showRemainingTimeInMinAndSec', () => {
|
describe('showRemainingTimeInMinAndSec', () => {
|
||||||
it('returns 0:00 if we do not pass an integer', () => {
|
it('returns 0:00 if we do not pass an integer', () => {
|
||||||
expect(showRemainingTimeInMinAndSec('5')).toBe('0:00');
|
expect(showRemainingTimeInMinAndSec('5')).toBe('0:00');
|
||||||
|
Loading…
Reference in New Issue
Block a user