mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
Swaps support for local testnet (#10658)
* Swaps support for local testnet * Create util method for comparison of token addresses/symbols to default swaps token * Get chainId from txMeta in _trackSwapsMetrics of transaction controller * Add comment to document purpose of getTransactionGroupRecipientAddressFilter * Use isSwapsDefaultTokenSymbol in place of repeated defaultTokenSymbol comparisons in build-quote.js
This commit is contained in:
parent
1c573ef852
commit
480512d14f
@ -8,12 +8,13 @@ import { calcTokenAmount } from '../../../ui/app/helpers/utils/token-util';
|
|||||||
import { calcGasTotal } from '../../../ui/app/pages/send/send.utils';
|
import { calcGasTotal } from '../../../ui/app/pages/send/send.utils';
|
||||||
import { conversionUtil } from '../../../ui/app/helpers/utils/conversion-util';
|
import { conversionUtil } from '../../../ui/app/helpers/utils/conversion-util';
|
||||||
import {
|
import {
|
||||||
ETH_SWAPS_TOKEN_OBJECT,
|
|
||||||
DEFAULT_ERC20_APPROVE_GAS,
|
DEFAULT_ERC20_APPROVE_GAS,
|
||||||
QUOTES_EXPIRED_ERROR,
|
QUOTES_EXPIRED_ERROR,
|
||||||
QUOTES_NOT_AVAILABLE_ERROR,
|
QUOTES_NOT_AVAILABLE_ERROR,
|
||||||
SWAPS_FETCH_ORDER_CONFLICT,
|
SWAPS_FETCH_ORDER_CONFLICT,
|
||||||
} from '../../../shared/constants/swaps';
|
} from '../../../shared/constants/swaps';
|
||||||
|
import { isSwapsDefaultTokenAddress } from '../../../shared/modules/swaps.utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
fetchTradesInfo as defaultFetchTradesInfo,
|
fetchTradesInfo as defaultFetchTradesInfo,
|
||||||
fetchSwapsFeatureLiveness as defaultFetchSwapsFeatureLiveness,
|
fetchSwapsFeatureLiveness as defaultFetchSwapsFeatureLiveness,
|
||||||
@ -85,6 +86,7 @@ export default class SwapsController {
|
|||||||
fetchTradesInfo = defaultFetchTradesInfo,
|
fetchTradesInfo = defaultFetchTradesInfo,
|
||||||
fetchSwapsFeatureLiveness = defaultFetchSwapsFeatureLiveness,
|
fetchSwapsFeatureLiveness = defaultFetchSwapsFeatureLiveness,
|
||||||
fetchSwapsQuoteRefreshTime = defaultFetchSwapsQuoteRefreshTime,
|
fetchSwapsQuoteRefreshTime = defaultFetchSwapsQuoteRefreshTime,
|
||||||
|
getCurrentChainId,
|
||||||
}) {
|
}) {
|
||||||
this.store = new ObservableStore({
|
this.store = new ObservableStore({
|
||||||
swapsState: { ...initialState.swapsState },
|
swapsState: { ...initialState.swapsState },
|
||||||
@ -93,6 +95,7 @@ export default class SwapsController {
|
|||||||
this._fetchTradesInfo = fetchTradesInfo;
|
this._fetchTradesInfo = fetchTradesInfo;
|
||||||
this._fetchSwapsFeatureLiveness = fetchSwapsFeatureLiveness;
|
this._fetchSwapsFeatureLiveness = fetchSwapsFeatureLiveness;
|
||||||
this._fetchSwapsQuoteRefreshTime = fetchSwapsQuoteRefreshTime;
|
this._fetchSwapsQuoteRefreshTime = fetchSwapsQuoteRefreshTime;
|
||||||
|
this._getCurrentChainId = getCurrentChainId;
|
||||||
|
|
||||||
this.getBufferedGasLimit = getBufferedGasLimit;
|
this.getBufferedGasLimit = getBufferedGasLimit;
|
||||||
this.tokenRatesStore = tokenRatesStore;
|
this.tokenRatesStore = tokenRatesStore;
|
||||||
@ -116,10 +119,11 @@ export default class SwapsController {
|
|||||||
|
|
||||||
// Sets the refresh rate for quote updates from the MetaSwap API
|
// Sets the refresh rate for quote updates from the MetaSwap API
|
||||||
async _setSwapsQuoteRefreshTime() {
|
async _setSwapsQuoteRefreshTime() {
|
||||||
|
const chainId = this._getCurrentChainId();
|
||||||
// Default to fallback time unless API returns valid response
|
// Default to fallback time unless API returns valid response
|
||||||
let swapsQuoteRefreshTime = FALLBACK_QUOTE_REFRESH_TIME;
|
let swapsQuoteRefreshTime = FALLBACK_QUOTE_REFRESH_TIME;
|
||||||
try {
|
try {
|
||||||
swapsQuoteRefreshTime = await this._fetchSwapsQuoteRefreshTime();
|
swapsQuoteRefreshTime = await this._fetchSwapsQuoteRefreshTime(chainId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Request for swaps quote refresh time failed: ', e);
|
console.error('Request for swaps quote refresh time failed: ', e);
|
||||||
}
|
}
|
||||||
@ -158,6 +162,8 @@ export default class SwapsController {
|
|||||||
fetchParamsMetaData = {},
|
fetchParamsMetaData = {},
|
||||||
isPolledRequest,
|
isPolledRequest,
|
||||||
) {
|
) {
|
||||||
|
const { chainId } = fetchParamsMetaData;
|
||||||
|
|
||||||
if (!fetchParams) {
|
if (!fetchParams) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -177,7 +183,7 @@ export default class SwapsController {
|
|||||||
this.indexOfNewestCallInFlight = indexOfCurrentCall;
|
this.indexOfNewestCallInFlight = indexOfCurrentCall;
|
||||||
|
|
||||||
let [newQuotes] = await Promise.all([
|
let [newQuotes] = await Promise.all([
|
||||||
this._fetchTradesInfo(fetchParams),
|
this._fetchTradesInfo(fetchParams, fetchParamsMetaData),
|
||||||
this._setSwapsQuoteRefreshTime(),
|
this._setSwapsQuoteRefreshTime(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -191,7 +197,7 @@ export default class SwapsController {
|
|||||||
|
|
||||||
let approvalRequired = false;
|
let approvalRequired = false;
|
||||||
if (
|
if (
|
||||||
fetchParams.sourceToken !== ETH_SWAPS_TOKEN_OBJECT.address &&
|
!isSwapsDefaultTokenAddress(fetchParams.sourceToken, chainId) &&
|
||||||
Object.values(newQuotes).length
|
Object.values(newQuotes).length
|
||||||
) {
|
) {
|
||||||
const allowance = await this._getERC20Allowance(
|
const allowance = await this._getERC20Allowance(
|
||||||
@ -490,6 +496,7 @@ export default class SwapsController {
|
|||||||
const {
|
const {
|
||||||
swapsState: { customGasPrice },
|
swapsState: { customGasPrice },
|
||||||
} = this.store.getState();
|
} = this.store.getState();
|
||||||
|
const chainId = this._getCurrentChainId();
|
||||||
|
|
||||||
const numQuotes = Object.keys(quotes).length;
|
const numQuotes = Object.keys(quotes).length;
|
||||||
if (!numQuotes) {
|
if (!numQuotes) {
|
||||||
@ -533,8 +540,8 @@ export default class SwapsController {
|
|||||||
|
|
||||||
// trade.value is a sum of different values depending on the transaction.
|
// trade.value is a sum of different values depending on the transaction.
|
||||||
// It always includes any external fees charged by the quote source. In
|
// It always includes any external fees charged by the quote source. In
|
||||||
// addition, if the source asset is ETH, trade.value includes the amount
|
// addition, if the source asset is the selected chain's default token, trade.value
|
||||||
// of swapped ETH.
|
// includes the amount of that token.
|
||||||
const totalWeiCost = new BigNumber(gasTotalInWeiHex, 16).plus(
|
const totalWeiCost = new BigNumber(gasTotalInWeiHex, 16).plus(
|
||||||
trade.value,
|
trade.value,
|
||||||
16,
|
16,
|
||||||
@ -549,21 +556,21 @@ export default class SwapsController {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// The total fee is aggregator/exchange fees plus gas fees.
|
// The total fee is aggregator/exchange fees plus gas fees.
|
||||||
// If the swap is from ETH, subtract the sourceAmount from the total cost.
|
// If the swap is from the selected chain's default token, subtract
|
||||||
// Otherwise, the total fee is simply trade.value plus gas fees.
|
// the sourceAmount from the total cost. Otherwise, the total fee
|
||||||
const ethFee =
|
// is simply trade.value plus gas fees.
|
||||||
sourceToken === ETH_SWAPS_TOKEN_OBJECT.address
|
const ethFee = isSwapsDefaultTokenAddress(sourceToken, chainId)
|
||||||
? conversionUtil(
|
? conversionUtil(
|
||||||
totalWeiCost.minus(sourceAmount, 10), // sourceAmount is in wei
|
totalWeiCost.minus(sourceAmount, 10), // sourceAmount is in wei
|
||||||
{
|
{
|
||||||
fromCurrency: 'ETH',
|
fromCurrency: 'ETH',
|
||||||
fromDenomination: 'WEI',
|
fromDenomination: 'WEI',
|
||||||
toDenomination: 'ETH',
|
toDenomination: 'ETH',
|
||||||
fromNumericBase: 'BN',
|
fromNumericBase: 'BN',
|
||||||
numberOfDecimals: 6,
|
numberOfDecimals: 6,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: totalEthCost;
|
: totalEthCost;
|
||||||
|
|
||||||
const decimalAdjustedDestinationAmount = calcTokenAmount(
|
const decimalAdjustedDestinationAmount = calcTokenAmount(
|
||||||
destinationAmount,
|
destinationAmount,
|
||||||
@ -588,10 +595,12 @@ export default class SwapsController {
|
|||||||
10,
|
10,
|
||||||
);
|
);
|
||||||
|
|
||||||
const conversionRateForCalculations =
|
const conversionRateForCalculations = isSwapsDefaultTokenAddress(
|
||||||
destinationToken === ETH_SWAPS_TOKEN_OBJECT.address
|
destinationToken,
|
||||||
? 1
|
chainId,
|
||||||
: tokenConversionRate;
|
)
|
||||||
|
? 1
|
||||||
|
: tokenConversionRate;
|
||||||
|
|
||||||
const overallValueOfQuoteForSorting =
|
const overallValueOfQuoteForSorting =
|
||||||
conversionRateForCalculations === undefined
|
conversionRateForCalculations === undefined
|
||||||
@ -618,8 +627,10 @@ export default class SwapsController {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const isBest =
|
const isBest =
|
||||||
newQuotes[topAggId].destinationToken === ETH_SWAPS_TOKEN_OBJECT.address ||
|
isSwapsDefaultTokenAddress(
|
||||||
Boolean(tokenConversionRates[newQuotes[topAggId]?.destinationToken]);
|
newQuotes[topAggId].destinationToken,
|
||||||
|
chainId,
|
||||||
|
) || Boolean(tokenConversionRates[newQuotes[topAggId]?.destinationToken]);
|
||||||
|
|
||||||
let savings = null;
|
let savings = null;
|
||||||
|
|
||||||
@ -726,13 +737,17 @@ export default class SwapsController {
|
|||||||
async _fetchAndSetSwapsLiveness() {
|
async _fetchAndSetSwapsLiveness() {
|
||||||
const { swapsState } = this.store.getState();
|
const { swapsState } = this.store.getState();
|
||||||
const { swapsFeatureIsLive: oldSwapsFeatureIsLive } = swapsState;
|
const { swapsFeatureIsLive: oldSwapsFeatureIsLive } = swapsState;
|
||||||
|
const chainId = this._getCurrentChainId();
|
||||||
|
|
||||||
let swapsFeatureIsLive = false;
|
let swapsFeatureIsLive = false;
|
||||||
let successfullyFetched = false;
|
let successfullyFetched = false;
|
||||||
let numAttempts = 0;
|
let numAttempts = 0;
|
||||||
|
|
||||||
const fetchAndIncrementNumAttempts = async () => {
|
const fetchAndIncrementNumAttempts = async () => {
|
||||||
try {
|
try {
|
||||||
swapsFeatureIsLive = Boolean(await this._fetchSwapsFeatureLiveness());
|
swapsFeatureIsLive = Boolean(
|
||||||
|
await this._fetchSwapsFeatureLiveness(chainId),
|
||||||
|
);
|
||||||
successfullyFetched = true;
|
successfullyFetched = true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log.error(err);
|
log.error(err);
|
||||||
|
@ -8,6 +8,7 @@ import { ObservableStore } from '@metamask/obs-store';
|
|||||||
import {
|
import {
|
||||||
ROPSTEN_NETWORK_ID,
|
ROPSTEN_NETWORK_ID,
|
||||||
MAINNET_NETWORK_ID,
|
MAINNET_NETWORK_ID,
|
||||||
|
MAINNET_CHAIN_ID,
|
||||||
} from '../../../shared/constants/network';
|
} from '../../../shared/constants/network';
|
||||||
import { ETH_SWAPS_TOKEN_OBJECT } from '../../../shared/constants/swaps';
|
import { ETH_SWAPS_TOKEN_OBJECT } from '../../../shared/constants/swaps';
|
||||||
import { createTestProviderTools } from '../../../test/stub/provider';
|
import { createTestProviderTools } from '../../../test/stub/provider';
|
||||||
@ -75,6 +76,7 @@ const MOCK_FETCH_METADATA = {
|
|||||||
symbol: 'FOO',
|
symbol: 'FOO',
|
||||||
decimals: 18,
|
decimals: 18,
|
||||||
},
|
},
|
||||||
|
chainId: MAINNET_CHAIN_ID,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MOCK_TOKEN_RATES_STORE = new ObservableStore({
|
const MOCK_TOKEN_RATES_STORE = new ObservableStore({
|
||||||
@ -131,6 +133,8 @@ const sandbox = sinon.createSandbox();
|
|||||||
const fetchTradesInfoStub = sandbox.stub();
|
const fetchTradesInfoStub = sandbox.stub();
|
||||||
const fetchSwapsFeatureLivenessStub = sandbox.stub();
|
const fetchSwapsFeatureLivenessStub = sandbox.stub();
|
||||||
const fetchSwapsQuoteRefreshTimeStub = sandbox.stub();
|
const fetchSwapsQuoteRefreshTimeStub = sandbox.stub();
|
||||||
|
const getCurrentChainIdStub = sandbox.stub();
|
||||||
|
getCurrentChainIdStub.returns(MAINNET_CHAIN_ID);
|
||||||
|
|
||||||
describe('SwapsController', function () {
|
describe('SwapsController', function () {
|
||||||
let provider;
|
let provider;
|
||||||
@ -145,6 +149,7 @@ describe('SwapsController', function () {
|
|||||||
fetchTradesInfo: fetchTradesInfoStub,
|
fetchTradesInfo: fetchTradesInfoStub,
|
||||||
fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub,
|
fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub,
|
||||||
fetchSwapsQuoteRefreshTime: fetchSwapsQuoteRefreshTimeStub,
|
fetchSwapsQuoteRefreshTime: fetchSwapsQuoteRefreshTimeStub,
|
||||||
|
getCurrentChainId: getCurrentChainIdStub,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -194,6 +199,7 @@ describe('SwapsController', function () {
|
|||||||
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
||||||
fetchTradesInfo: fetchTradesInfoStub,
|
fetchTradesInfo: fetchTradesInfoStub,
|
||||||
fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub,
|
fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub,
|
||||||
|
getCurrentChainId: getCurrentChainIdStub,
|
||||||
});
|
});
|
||||||
const currentEthersInstance = swapsController.ethersProvider;
|
const currentEthersInstance = swapsController.ethersProvider;
|
||||||
const onNetworkDidChange = networkController.on.getCall(0).args[1];
|
const onNetworkDidChange = networkController.on.getCall(0).args[1];
|
||||||
@ -218,6 +224,7 @@ describe('SwapsController', function () {
|
|||||||
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
||||||
fetchTradesInfo: fetchTradesInfoStub,
|
fetchTradesInfo: fetchTradesInfoStub,
|
||||||
fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub,
|
fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub,
|
||||||
|
getCurrentChainId: getCurrentChainIdStub,
|
||||||
});
|
});
|
||||||
const currentEthersInstance = swapsController.ethersProvider;
|
const currentEthersInstance = swapsController.ethersProvider;
|
||||||
const onNetworkDidChange = networkController.on.getCall(0).args[1];
|
const onNetworkDidChange = networkController.on.getCall(0).args[1];
|
||||||
@ -242,6 +249,7 @@ describe('SwapsController', function () {
|
|||||||
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
||||||
fetchTradesInfo: fetchTradesInfoStub,
|
fetchTradesInfo: fetchTradesInfoStub,
|
||||||
fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub,
|
fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub,
|
||||||
|
getCurrentChainId: getCurrentChainIdStub,
|
||||||
});
|
});
|
||||||
const currentEthersInstance = swapsController.ethersProvider;
|
const currentEthersInstance = swapsController.ethersProvider;
|
||||||
const onNetworkDidChange = networkController.on.getCall(0).args[1];
|
const onNetworkDidChange = networkController.on.getCall(0).args[1];
|
||||||
@ -686,7 +694,10 @@ describe('SwapsController', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
fetchTradesInfoStub.calledOnceWithExactly(MOCK_FETCH_PARAMS),
|
fetchTradesInfoStub.calledOnceWithExactly(
|
||||||
|
MOCK_FETCH_PARAMS,
|
||||||
|
MOCK_FETCH_METADATA,
|
||||||
|
),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -958,6 +958,7 @@ export default class TransactionController extends EventEmitter {
|
|||||||
txMeta.txParams.from,
|
txMeta.txParams.from,
|
||||||
txMeta.destinationTokenDecimals,
|
txMeta.destinationTokenDecimals,
|
||||||
approvalTxMeta,
|
approvalTxMeta,
|
||||||
|
txMeta.chainId,
|
||||||
);
|
);
|
||||||
|
|
||||||
const quoteVsExecutionRatio = `${new BigNumber(tokensReceived, 10)
|
const quoteVsExecutionRatio = `${new BigNumber(tokensReceived, 10)
|
||||||
|
@ -383,6 +383,9 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
this.networkController,
|
this.networkController,
|
||||||
),
|
),
|
||||||
tokenRatesStore: this.tokenRatesController.store,
|
tokenRatesStore: this.tokenRatesController.store,
|
||||||
|
getCurrentChainId: this.networkController.getCurrentChainId.bind(
|
||||||
|
this.networkController,
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
// ensure accountTracker updates balances after network change
|
// ensure accountTracker updates balances after network change
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
import { MAINNET_CHAIN_ID } from './network';
|
||||||
|
|
||||||
|
export const QUOTES_EXPIRED_ERROR = 'quotes-expired';
|
||||||
|
export const SWAP_FAILED_ERROR = 'swap-failed-error';
|
||||||
|
export const ERROR_FETCHING_QUOTES = 'error-fetching-quotes';
|
||||||
|
export const QUOTES_NOT_AVAILABLE_ERROR = 'quotes-not-avilable';
|
||||||
|
export const OFFLINE_FOR_MAINTENANCE = 'offline-for-maintenance';
|
||||||
|
export const SWAPS_FETCH_ORDER_CONFLICT = 'swaps-fetch-order-conflict';
|
||||||
|
|
||||||
// An address that the metaswap-api recognizes as ETH, in place of the token address that ERC-20 tokens have
|
// An address that the metaswap-api recognizes as ETH, in place of the token address that ERC-20 tokens have
|
||||||
const ETH_SWAPS_TOKEN_ADDRESS = '0x0000000000000000000000000000000000000000';
|
const ETH_SWAPS_TOKEN_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||||
|
|
||||||
@ -9,17 +18,42 @@ export const ETH_SWAPS_TOKEN_OBJECT = {
|
|||||||
iconUrl: 'images/black-eth-logo.svg',
|
iconUrl: 'images/black-eth-logo.svg',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const QUOTES_EXPIRED_ERROR = 'quotes-expired';
|
const TEST_ETH_SWAPS_TOKEN_OBJECT = {
|
||||||
export const SWAP_FAILED_ERROR = 'swap-failed-error';
|
symbol: 'TESTETH',
|
||||||
export const ERROR_FETCHING_QUOTES = 'error-fetching-quotes';
|
name: 'Test Ether',
|
||||||
export const QUOTES_NOT_AVAILABLE_ERROR = 'quotes-not-avilable';
|
address: ETH_SWAPS_TOKEN_ADDRESS,
|
||||||
export const OFFLINE_FOR_MAINTENANCE = 'offline-for-maintenance';
|
decimals: 18,
|
||||||
export const SWAPS_FETCH_ORDER_CONFLICT = 'swaps-fetch-order-conflict';
|
iconUrl: 'images/black-eth-logo.svg',
|
||||||
|
};
|
||||||
|
|
||||||
// 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';
|
||||||
|
|
||||||
export const SWAPS_CONTRACT_ADDRESS =
|
const MAINNET_CONTRACT_ADDRESS = '0x881d40237659c251811cec9c364ef91dc08d300c';
|
||||||
'0x881d40237659c251811cec9c364ef91dc08d300c';
|
|
||||||
|
|
||||||
export const METASWAP_API_HOST = 'https://api.metaswap.codefi.network';
|
const TESTNET_CONTRACT_ADDRESS = '0x881d40237659c251811cec9c364ef91dc08d300c';
|
||||||
|
|
||||||
|
const METASWAP_ETH_API_HOST = 'https://api.metaswap.codefi.network';
|
||||||
|
|
||||||
|
const SWAPS_TESTNET_CHAIN_ID = '0x539';
|
||||||
|
const SWAPS_TESTNET_HOST = 'https://metaswap-api.airswap-dev.codefi.network';
|
||||||
|
|
||||||
|
export const ALLOWED_SWAPS_CHAIN_IDS = {
|
||||||
|
[MAINNET_CHAIN_ID]: true,
|
||||||
|
[SWAPS_TESTNET_CHAIN_ID]: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const METASWAP_CHAINID_API_HOST_MAP = {
|
||||||
|
[MAINNET_CHAIN_ID]: METASWAP_ETH_API_HOST,
|
||||||
|
[SWAPS_TESTNET_CHAIN_ID]: SWAPS_TESTNET_HOST,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SWAPS_CHAINID_CONTRACT_ADDRESS_MAP = {
|
||||||
|
[MAINNET_CHAIN_ID]: MAINNET_CONTRACT_ADDRESS,
|
||||||
|
[SWAPS_TESTNET_CHAIN_ID]: TESTNET_CONTRACT_ADDRESS,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SWAPS_CHAINID_DEFAULT_TOKEN_MAP = {
|
||||||
|
[MAINNET_CHAIN_ID]: ETH_SWAPS_TOKEN_OBJECT,
|
||||||
|
[SWAPS_TESTNET_CHAIN_ID]: TEST_ETH_SWAPS_TOKEN_OBJECT,
|
||||||
|
};
|
||||||
|
33
shared/modules/swaps.utils.js
Normal file
33
shared/modules/swaps.utils.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { SWAPS_CHAINID_DEFAULT_TOKEN_MAP } from '../constants/swaps';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the provided address is strictly equal to the address for
|
||||||
|
* the default swaps token of the provided chain.
|
||||||
|
*
|
||||||
|
* @param {string} address - The string to compare to the default token address
|
||||||
|
* @param {string} chainId - The hex encoded chain ID of the default swaps token to check
|
||||||
|
* @returns {boolean} Whether the address is the provided chain's default token address
|
||||||
|
*/
|
||||||
|
export function isSwapsDefaultTokenAddress(address, chainId) {
|
||||||
|
if (!address || !chainId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return address === SWAPS_CHAINID_DEFAULT_TOKEN_MAP[chainId]?.address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the provided symbol is strictly equal to the symbol for
|
||||||
|
* the default swaps token of the provided chain.
|
||||||
|
*
|
||||||
|
* @param {string} symbol - The string to compare to the default token symbol
|
||||||
|
* @param {string} chainId - The hex encoded chain ID of the default swaps token to check
|
||||||
|
* @returns {boolean} Whether the symbl is the provided chain's default token symbol
|
||||||
|
*/
|
||||||
|
export function isSwapsDefaultTokenSymbol(symbol, chainId) {
|
||||||
|
if (!symbol || !chainId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return symbol === SWAPS_CHAINID_DEFAULT_TOKEN_MAP[chainId]?.symbol;
|
||||||
|
}
|
@ -5,20 +5,31 @@ import {
|
|||||||
nonceSortedCompletedTransactionsSelector,
|
nonceSortedCompletedTransactionsSelector,
|
||||||
nonceSortedPendingTransactionsSelector,
|
nonceSortedPendingTransactionsSelector,
|
||||||
} from '../../../selectors/transactions';
|
} from '../../../selectors/transactions';
|
||||||
|
import { getCurrentChainId } from '../../../selectors';
|
||||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||||
import TransactionListItem from '../transaction-list-item';
|
import TransactionListItem from '../transaction-list-item';
|
||||||
import Button from '../../ui/button';
|
import Button from '../../ui/button';
|
||||||
import { TOKEN_CATEGORY_HASH } from '../../../helpers/constants/transactions';
|
import { TOKEN_CATEGORY_HASH } from '../../../helpers/constants/transactions';
|
||||||
import { SWAPS_CONTRACT_ADDRESS } from '../../../../../shared/constants/swaps';
|
import { SWAPS_CHAINID_CONTRACT_ADDRESS_MAP } from '../../../../../shared/constants/swaps';
|
||||||
import { TRANSACTION_TYPES } from '../../../../../shared/constants/transaction';
|
import { TRANSACTION_TYPES } from '../../../../../shared/constants/transaction';
|
||||||
|
|
||||||
const PAGE_INCREMENT = 10;
|
const PAGE_INCREMENT = 10;
|
||||||
|
|
||||||
const getTransactionGroupRecipientAddressFilter = (recipientAddress) => {
|
// When we are on a token page, we only want to show transactions that involve that token.
|
||||||
|
// In the case of token transfers or approvals, these will be transactions sent to the
|
||||||
|
// token contract. In the case of swaps, these will be transactions sent to the swaps contract
|
||||||
|
// and which have the token address in the transaction data.
|
||||||
|
//
|
||||||
|
// getTransactionGroupRecipientAddressFilter is used to determine whether a transaction matches
|
||||||
|
// either of those criteria
|
||||||
|
const getTransactionGroupRecipientAddressFilter = (
|
||||||
|
recipientAddress,
|
||||||
|
chainId,
|
||||||
|
) => {
|
||||||
return ({ initialTransaction: { txParams } }) => {
|
return ({ initialTransaction: { txParams } }) => {
|
||||||
return (
|
return (
|
||||||
txParams?.to === recipientAddress ||
|
txParams?.to === recipientAddress ||
|
||||||
(txParams?.to === SWAPS_CONTRACT_ADDRESS &&
|
(txParams?.to === SWAPS_CHAINID_CONTRACT_ADDRESS_MAP[chainId] &&
|
||||||
txParams.data.match(recipientAddress.slice(2)))
|
txParams.data.match(recipientAddress.slice(2)))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -39,12 +50,13 @@ const getFilteredTransactionGroups = (
|
|||||||
transactionGroups,
|
transactionGroups,
|
||||||
hideTokenTransactions,
|
hideTokenTransactions,
|
||||||
tokenAddress,
|
tokenAddress,
|
||||||
|
chainId,
|
||||||
) => {
|
) => {
|
||||||
if (hideTokenTransactions) {
|
if (hideTokenTransactions) {
|
||||||
return transactionGroups.filter(tokenTransactionFilter);
|
return transactionGroups.filter(tokenTransactionFilter);
|
||||||
} else if (tokenAddress) {
|
} else if (tokenAddress) {
|
||||||
return transactionGroups.filter(
|
return transactionGroups.filter(
|
||||||
getTransactionGroupRecipientAddressFilter(tokenAddress),
|
getTransactionGroupRecipientAddressFilter(tokenAddress, chainId),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return transactionGroups;
|
return transactionGroups;
|
||||||
@ -63,6 +75,7 @@ export default function TransactionList({
|
|||||||
const unfilteredCompletedTransactions = useSelector(
|
const unfilteredCompletedTransactions = useSelector(
|
||||||
nonceSortedCompletedTransactionsSelector,
|
nonceSortedCompletedTransactionsSelector,
|
||||||
);
|
);
|
||||||
|
const chainId = useSelector(getCurrentChainId);
|
||||||
|
|
||||||
const pendingTransactions = useMemo(
|
const pendingTransactions = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@ -70,8 +83,14 @@ export default function TransactionList({
|
|||||||
unfilteredPendingTransactions,
|
unfilteredPendingTransactions,
|
||||||
hideTokenTransactions,
|
hideTokenTransactions,
|
||||||
tokenAddress,
|
tokenAddress,
|
||||||
|
chainId,
|
||||||
),
|
),
|
||||||
[hideTokenTransactions, tokenAddress, unfilteredPendingTransactions],
|
[
|
||||||
|
hideTokenTransactions,
|
||||||
|
tokenAddress,
|
||||||
|
unfilteredPendingTransactions,
|
||||||
|
chainId,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
const completedTransactions = useMemo(
|
const completedTransactions = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@ -79,8 +98,14 @@ export default function TransactionList({
|
|||||||
unfilteredCompletedTransactions,
|
unfilteredCompletedTransactions,
|
||||||
hideTokenTransactions,
|
hideTokenTransactions,
|
||||||
tokenAddress,
|
tokenAddress,
|
||||||
|
chainId,
|
||||||
),
|
),
|
||||||
[hideTokenTransactions, tokenAddress, unfilteredCompletedTransactions],
|
[
|
||||||
|
hideTokenTransactions,
|
||||||
|
tokenAddress,
|
||||||
|
unfilteredCompletedTransactions,
|
||||||
|
chainId,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const viewMore = useCallback(
|
const viewMore = useCallback(
|
||||||
|
@ -25,7 +25,8 @@ import {
|
|||||||
getIsMainnet,
|
getIsMainnet,
|
||||||
getIsTestnet,
|
getIsTestnet,
|
||||||
getCurrentKeyring,
|
getCurrentKeyring,
|
||||||
getSwapsEthToken,
|
getSwapsDefaultToken,
|
||||||
|
getIsSwapsChain,
|
||||||
} from '../../../selectors/selectors';
|
} from '../../../selectors/selectors';
|
||||||
import SwapIcon from '../../ui/icon/swap-icon.component';
|
import SwapIcon from '../../ui/icon/swap-icon.component';
|
||||||
import BuyIcon from '../../ui/icon/overview-buy-icon.component';
|
import BuyIcon from '../../ui/icon/overview-buy-icon.component';
|
||||||
@ -63,13 +64,14 @@ const EthOverview = ({ className }) => {
|
|||||||
const { balance } = selectedAccount;
|
const { balance } = selectedAccount;
|
||||||
const isMainnetChain = useSelector(getIsMainnet);
|
const isMainnetChain = useSelector(getIsMainnet);
|
||||||
const isTestnetChain = useSelector(getIsTestnet);
|
const isTestnetChain = useSelector(getIsTestnet);
|
||||||
|
const isSwapsChain = useSelector(getIsSwapsChain);
|
||||||
const enteredSwapsEvent = useNewMetricEvent({
|
const enteredSwapsEvent = useNewMetricEvent({
|
||||||
event: 'Swaps Opened',
|
event: 'Swaps Opened',
|
||||||
properties: { source: 'Main View', active_currency: 'ETH' },
|
properties: { source: 'Main View', active_currency: 'ETH' },
|
||||||
category: 'swaps',
|
category: 'swaps',
|
||||||
});
|
});
|
||||||
const swapsEnabled = useSelector(getSwapsFeatureLiveness);
|
const swapsEnabled = useSelector(getSwapsFeatureLiveness);
|
||||||
const swapsEthToken = useSelector(getSwapsEthToken);
|
const defaultSwapsToken = useSelector(getSwapsDefaultToken);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WalletOverview
|
<WalletOverview
|
||||||
@ -136,12 +138,12 @@ const EthOverview = ({ className }) => {
|
|||||||
{swapsEnabled ? (
|
{swapsEnabled ? (
|
||||||
<IconButton
|
<IconButton
|
||||||
className="eth-overview__button"
|
className="eth-overview__button"
|
||||||
disabled={!isMainnetChain}
|
disabled={!isSwapsChain}
|
||||||
Icon={SwapIcon}
|
Icon={SwapIcon}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (isMainnetChain) {
|
if (isSwapsChain) {
|
||||||
enteredSwapsEvent();
|
enteredSwapsEvent();
|
||||||
dispatch(setSwapsFromToken(swapsEthToken));
|
dispatch(setSwapsFromToken(defaultSwapsToken));
|
||||||
if (usingHardwareWallet) {
|
if (usingHardwareWallet) {
|
||||||
global.platform.openExtensionInBrowser(BUILD_QUOTE_ROUTE);
|
global.platform.openExtensionInBrowser(BUILD_QUOTE_ROUTE);
|
||||||
} else {
|
} else {
|
||||||
@ -154,7 +156,7 @@ const EthOverview = ({ className }) => {
|
|||||||
<Tooltip
|
<Tooltip
|
||||||
title={t('onlyAvailableOnMainnet')}
|
title={t('onlyAvailableOnMainnet')}
|
||||||
position="bottom"
|
position="bottom"
|
||||||
disabled={isMainnetChain}
|
disabled={isSwapsChain}
|
||||||
>
|
>
|
||||||
{contents}
|
{contents}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
@ -25,9 +25,8 @@ import {
|
|||||||
import {
|
import {
|
||||||
getAssetImages,
|
getAssetImages,
|
||||||
getCurrentKeyring,
|
getCurrentKeyring,
|
||||||
getCurrentChainId,
|
getIsSwapsChain,
|
||||||
} from '../../../selectors/selectors';
|
} from '../../../selectors/selectors';
|
||||||
import { MAINNET_CHAIN_ID } from '../../../../../shared/constants/network';
|
|
||||||
|
|
||||||
import SwapIcon from '../../ui/icon/swap-icon.component';
|
import SwapIcon from '../../ui/icon/swap-icon.component';
|
||||||
import SendIcon from '../../ui/icon/overview-send-icon.component';
|
import SendIcon from '../../ui/icon/overview-send-icon.component';
|
||||||
@ -58,7 +57,7 @@ const TokenOverview = ({ className, token }) => {
|
|||||||
balanceToRender,
|
balanceToRender,
|
||||||
token.symbol,
|
token.symbol,
|
||||||
);
|
);
|
||||||
const chainId = useSelector(getCurrentChainId);
|
const isSwapsChain = useSelector(getIsSwapsChain);
|
||||||
const enteredSwapsEvent = useNewMetricEvent({
|
const enteredSwapsEvent = useNewMetricEvent({
|
||||||
event: 'Swaps Opened',
|
event: 'Swaps Opened',
|
||||||
properties: { source: 'Token View', active_currency: token.symbol },
|
properties: { source: 'Token View', active_currency: token.symbol },
|
||||||
@ -100,10 +99,10 @@ const TokenOverview = ({ className, token }) => {
|
|||||||
{swapsEnabled ? (
|
{swapsEnabled ? (
|
||||||
<IconButton
|
<IconButton
|
||||||
className="token-overview__button"
|
className="token-overview__button"
|
||||||
disabled={chainId !== MAINNET_CHAIN_ID}
|
disabled={!isSwapsChain}
|
||||||
Icon={SwapIcon}
|
Icon={SwapIcon}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (chainId === MAINNET_CHAIN_ID) {
|
if (isSwapsChain) {
|
||||||
enteredSwapsEvent();
|
enteredSwapsEvent();
|
||||||
dispatch(
|
dispatch(
|
||||||
setSwapsFromToken({
|
setSwapsFromToken({
|
||||||
@ -125,7 +124,7 @@ const TokenOverview = ({ className, token }) => {
|
|||||||
<Tooltip
|
<Tooltip
|
||||||
title={t('onlyAvailableOnMainnet')}
|
title={t('onlyAvailableOnMainnet')}
|
||||||
position="bottom"
|
position="bottom"
|
||||||
disabled={chainId === MAINNET_CHAIN_ID}
|
disabled={isSwapsChain}
|
||||||
>
|
>
|
||||||
{contents}
|
{contents}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
@ -49,7 +49,8 @@ import {
|
|||||||
getSelectedAccount,
|
getSelectedAccount,
|
||||||
getTokenExchangeRates,
|
getTokenExchangeRates,
|
||||||
getUSDConversionRate,
|
getUSDConversionRate,
|
||||||
getSwapsEthToken,
|
getSwapsDefaultToken,
|
||||||
|
getCurrentChainId,
|
||||||
} from '../../selectors';
|
} from '../../selectors';
|
||||||
import {
|
import {
|
||||||
ERROR_FETCHING_QUOTES,
|
ERROR_FETCHING_QUOTES,
|
||||||
@ -376,9 +377,11 @@ export const fetchQuotesAndSetQuoteState = (
|
|||||||
metaMetricsEvent,
|
metaMetricsEvent,
|
||||||
) => {
|
) => {
|
||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
|
const state = getState();
|
||||||
|
const chainId = getCurrentChainId(state);
|
||||||
let swapsFeatureIsLive = false;
|
let swapsFeatureIsLive = false;
|
||||||
try {
|
try {
|
||||||
swapsFeatureIsLive = await fetchSwapsFeatureLiveness();
|
swapsFeatureIsLive = await fetchSwapsFeatureLiveness(chainId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error('Failed to fetch Swaps liveness, defaulting to false.', error);
|
log.error('Failed to fetch Swaps liveness, defaulting to false.', error);
|
||||||
}
|
}
|
||||||
@ -389,13 +392,14 @@ export const fetchQuotesAndSetQuoteState = (
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = getState();
|
|
||||||
const fetchParams = getFetchParams(state);
|
const fetchParams = getFetchParams(state);
|
||||||
const selectedAccount = getSelectedAccount(state);
|
const selectedAccount = getSelectedAccount(state);
|
||||||
const balanceError = getBalanceError(state);
|
const balanceError = getBalanceError(state);
|
||||||
|
const swapsDefaultToken = getSwapsDefaultToken(state);
|
||||||
const fetchParamsFromToken =
|
const fetchParamsFromToken =
|
||||||
fetchParams?.metaData?.sourceTokenInfo?.symbol === 'ETH'
|
fetchParams?.metaData?.sourceTokenInfo?.symbol ===
|
||||||
? getSwapsEthToken(state)
|
swapsDefaultToken.symbol
|
||||||
|
? swapsDefaultToken
|
||||||
: fetchParams?.metaData?.sourceTokenInfo;
|
: fetchParams?.metaData?.sourceTokenInfo;
|
||||||
const selectedFromToken = getFromToken(state) || fetchParamsFromToken || {};
|
const selectedFromToken = getFromToken(state) || fetchParamsFromToken || {};
|
||||||
const selectedToToken =
|
const selectedToToken =
|
||||||
@ -420,7 +424,10 @@ export const fetchQuotesAndSetQuoteState = (
|
|||||||
const contractExchangeRates = getTokenExchangeRates(state);
|
const contractExchangeRates = getTokenExchangeRates(state);
|
||||||
|
|
||||||
let destinationTokenAddedForSwap = false;
|
let destinationTokenAddedForSwap = false;
|
||||||
if (toTokenSymbol !== 'ETH' && !contractExchangeRates[toTokenAddress]) {
|
if (
|
||||||
|
toTokenSymbol !== swapsDefaultToken.symbol &&
|
||||||
|
!contractExchangeRates[toTokenAddress]
|
||||||
|
) {
|
||||||
destinationTokenAddedForSwap = true;
|
destinationTokenAddedForSwap = true;
|
||||||
await dispatch(
|
await dispatch(
|
||||||
addToken(
|
addToken(
|
||||||
@ -433,7 +440,7 @@ export const fetchQuotesAndSetQuoteState = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
fromTokenSymbol !== 'ETH' &&
|
fromTokenSymbol !== swapsDefaultToken.symbol &&
|
||||||
!contractExchangeRates[fromTokenAddress] &&
|
!contractExchangeRates[fromTokenAddress] &&
|
||||||
fromTokenBalance &&
|
fromTokenBalance &&
|
||||||
new BigNumber(fromTokenBalance, 16).gt(0)
|
new BigNumber(fromTokenBalance, 16).gt(0)
|
||||||
@ -494,6 +501,7 @@ export const fetchQuotesAndSetQuoteState = (
|
|||||||
sourceTokenInfo,
|
sourceTokenInfo,
|
||||||
destinationTokenInfo,
|
destinationTokenInfo,
|
||||||
accountBalance: selectedAccount.balance,
|
accountBalance: selectedAccount.balance,
|
||||||
|
chainId,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -563,9 +571,12 @@ export const fetchQuotesAndSetQuoteState = (
|
|||||||
|
|
||||||
export const signAndSendTransactions = (history, metaMetricsEvent) => {
|
export const signAndSendTransactions = (history, metaMetricsEvent) => {
|
||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
|
const state = getState();
|
||||||
|
const chainId = getCurrentChainId(state);
|
||||||
|
|
||||||
let swapsFeatureIsLive = false;
|
let swapsFeatureIsLive = false;
|
||||||
try {
|
try {
|
||||||
swapsFeatureIsLive = await fetchSwapsFeatureLiveness();
|
swapsFeatureIsLive = await fetchSwapsFeatureLiveness(chainId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error('Failed to fetch Swaps liveness, defaulting to false.', error);
|
log.error('Failed to fetch Swaps liveness, defaulting to false.', error);
|
||||||
}
|
}
|
||||||
@ -576,7 +587,6 @@ export const signAndSendTransactions = (history, metaMetricsEvent) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = getState();
|
|
||||||
const customSwapsGas = getCustomSwapsGas(state);
|
const customSwapsGas = getCustomSwapsGas(state);
|
||||||
const fetchParams = getFetchParams(state);
|
const fetchParams = getFetchParams(state);
|
||||||
const { metaData, value: swapTokenValue, slippage } = fetchParams;
|
const { metaData, value: swapTokenValue, slippage } = fetchParams;
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { useRouteMatch } from 'react-router-dom';
|
import { useRouteMatch } from 'react-router-dom';
|
||||||
import { getTokens } from '../ducks/metamask/metamask';
|
import { getTokens } from '../ducks/metamask/metamask';
|
||||||
|
import { getCurrentChainId } from '../selectors';
|
||||||
import { ASSET_ROUTE } from '../helpers/constants/routes';
|
import { ASSET_ROUTE } from '../helpers/constants/routes';
|
||||||
import { ETH_SWAPS_TOKEN_OBJECT } from '../../../shared/constants/swaps';
|
import {
|
||||||
|
SWAPS_CHAINID_DEFAULT_TOKEN_MAP,
|
||||||
|
ETH_SWAPS_TOKEN_OBJECT,
|
||||||
|
} from '../../../shared/constants/swaps';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a token object for the asset that is currently being viewed.
|
* Returns a token object for the asset that is currently being viewed.
|
||||||
* Will return the ETH_SWAPS_TOKEN_OBJECT when the user is viewing either
|
* Will return the default token object for the current chain when the
|
||||||
* the primary, unfiltered, activity list or the ETH asset page.
|
* user is viewing either the primary, unfiltered, activity list or the
|
||||||
|
* default token asset page.
|
||||||
* @returns {import('./useTokenDisplayValue').Token}
|
* @returns {import('./useTokenDisplayValue').Token}
|
||||||
*/
|
*/
|
||||||
export function useCurrentAsset() {
|
export function useCurrentAsset() {
|
||||||
@ -22,6 +27,10 @@ export function useCurrentAsset() {
|
|||||||
const knownTokens = useSelector(getTokens);
|
const knownTokens = useSelector(getTokens);
|
||||||
const token =
|
const token =
|
||||||
tokenAddress && knownTokens.find(({ address }) => address === tokenAddress);
|
tokenAddress && knownTokens.find(({ address }) => address === tokenAddress);
|
||||||
|
const chainId = useSelector(getCurrentChainId);
|
||||||
|
|
||||||
return token ?? ETH_SWAPS_TOKEN_OBJECT;
|
return (
|
||||||
|
token ??
|
||||||
|
(SWAPS_CHAINID_DEFAULT_TOKEN_MAP[chainId] || ETH_SWAPS_TOKEN_OBJECT)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
|
import { useSelector } from 'react-redux';
|
||||||
import { TRANSACTION_TYPES } from '../../../shared/constants/transaction';
|
import { TRANSACTION_TYPES } from '../../../shared/constants/transaction';
|
||||||
import { ETH_SWAPS_TOKEN_OBJECT } from '../../../shared/constants/swaps';
|
import {
|
||||||
|
isSwapsDefaultTokenAddress,
|
||||||
|
isSwapsDefaultTokenSymbol,
|
||||||
|
} from '../../../shared/modules/swaps.utils';
|
||||||
import { getSwapsTokensReceivedFromTxMeta } from '../pages/swaps/swaps.util';
|
import { getSwapsTokensReceivedFromTxMeta } from '../pages/swaps/swaps.util';
|
||||||
|
import { getCurrentChainId } from '../selectors';
|
||||||
import { useTokenFiatAmount } from './useTokenFiatAmount';
|
import { useTokenFiatAmount } from './useTokenFiatAmount';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -14,10 +19,11 @@ import { useTokenFiatAmount } from './useTokenFiatAmount';
|
|||||||
/**
|
/**
|
||||||
* A Swap transaction group's primaryTransaction contains details of the swap,
|
* A Swap transaction group's primaryTransaction contains details of the swap,
|
||||||
* including the source (from) and destination (to) token type (ETH, DAI, etc..)
|
* including the source (from) and destination (to) token type (ETH, DAI, etc..)
|
||||||
* When viewing a non ETH asset page, we need to determine if that asset is the
|
* When viewing an asset page that is not for the current chain's default token, we
|
||||||
* token that was received (destination) from the swap. In that circumstance we
|
* need to determine if that asset is the token that was received (destination) from
|
||||||
* would want to show the primaryCurrency in the activity list that is most relevant
|
* the swap. In that circumstance we would want to show the primaryCurrency in the
|
||||||
* for that token (- 1000 DAI, for example, when swapping DAI for ETH).
|
* activity list that is most relevant for that token (- 1000 DAI, for example, when
|
||||||
|
* swapping DAI for ETH).
|
||||||
* @param {import('../selectors').transactionGroup} transactionGroup - Group of transactions by nonce
|
* @param {import('../selectors').transactionGroup} transactionGroup - Group of transactions by nonce
|
||||||
* @param {import('./useTokenDisplayValue').Token} currentAsset - The current asset the user is looking at
|
* @param {import('./useTokenDisplayValue').Token} currentAsset - The current asset the user is looking at
|
||||||
* @returns {SwappedTokenValue}
|
* @returns {SwappedTokenValue}
|
||||||
@ -27,11 +33,15 @@ export function useSwappedTokenValue(transactionGroup, currentAsset) {
|
|||||||
const { primaryTransaction, initialTransaction } = transactionGroup;
|
const { primaryTransaction, initialTransaction } = transactionGroup;
|
||||||
const { type } = initialTransaction;
|
const { type } = initialTransaction;
|
||||||
const { from: senderAddress } = initialTransaction.txParams || {};
|
const { from: senderAddress } = initialTransaction.txParams || {};
|
||||||
|
const chainId = useSelector(getCurrentChainId);
|
||||||
|
|
||||||
const isViewingReceivedTokenFromSwap =
|
const isViewingReceivedTokenFromSwap =
|
||||||
currentAsset?.symbol === primaryTransaction.destinationTokenSymbol ||
|
currentAsset?.symbol === primaryTransaction.destinationTokenSymbol ||
|
||||||
(currentAsset.address === ETH_SWAPS_TOKEN_OBJECT.address &&
|
(isSwapsDefaultTokenAddress(currentAsset.address, chainId) &&
|
||||||
primaryTransaction.destinationTokenSymbol === 'ETH');
|
isSwapsDefaultTokenSymbol(
|
||||||
|
primaryTransaction.destinationTokenSymbol,
|
||||||
|
chainId,
|
||||||
|
));
|
||||||
|
|
||||||
const swapTokenValue =
|
const swapTokenValue =
|
||||||
type === TRANSACTION_TYPES.SWAP && isViewingReceivedTokenFromSwap
|
type === TRANSACTION_TYPES.SWAP && isViewingReceivedTokenFromSwap
|
||||||
@ -41,6 +51,8 @@ export function useSwappedTokenValue(transactionGroup, currentAsset) {
|
|||||||
address,
|
address,
|
||||||
senderAddress,
|
senderAddress,
|
||||||
decimals,
|
decimals,
|
||||||
|
null,
|
||||||
|
chainId,
|
||||||
)
|
)
|
||||||
: type === TRANSACTION_TYPES.SWAP && primaryTransaction.swapTokenValue;
|
: type === TRANSACTION_TYPES.SWAP && primaryTransaction.swapTokenValue;
|
||||||
|
|
||||||
|
@ -9,9 +9,11 @@ import {
|
|||||||
getTokenExchangeRates,
|
getTokenExchangeRates,
|
||||||
getConversionRate,
|
getConversionRate,
|
||||||
getCurrentCurrency,
|
getCurrentCurrency,
|
||||||
getSwapsEthToken,
|
getSwapsDefaultToken,
|
||||||
|
getCurrentChainId,
|
||||||
} from '../selectors';
|
} from '../selectors';
|
||||||
import { getSwapsTokens } from '../ducks/swaps/swaps';
|
import { getSwapsTokens } from '../ducks/swaps/swaps';
|
||||||
|
import { isSwapsDefaultTokenSymbol } from '../../../shared/modules/swaps.utils';
|
||||||
import { useEqualityCheck } from './useEqualityCheck';
|
import { useEqualityCheck } from './useEqualityCheck';
|
||||||
|
|
||||||
const tokenList = shuffle(
|
const tokenList = shuffle(
|
||||||
@ -28,12 +30,15 @@ export function getRenderableTokenData(
|
|||||||
contractExchangeRates,
|
contractExchangeRates,
|
||||||
conversionRate,
|
conversionRate,
|
||||||
currentCurrency,
|
currentCurrency,
|
||||||
|
chainId,
|
||||||
) {
|
) {
|
||||||
const { symbol, name, address, iconUrl, string, balance, decimals } = token;
|
const { symbol, name, address, iconUrl, string, balance, decimals } = token;
|
||||||
|
|
||||||
const formattedFiat =
|
const formattedFiat =
|
||||||
getTokenFiatAmount(
|
getTokenFiatAmount(
|
||||||
symbol === 'ETH' ? 1 : contractExchangeRates[address],
|
isSwapsDefaultTokenSymbol(symbol, chainId)
|
||||||
|
? 1
|
||||||
|
: contractExchangeRates[address],
|
||||||
conversionRate,
|
conversionRate,
|
||||||
currentCurrency,
|
currentCurrency,
|
||||||
string,
|
string,
|
||||||
@ -42,7 +47,9 @@ export function getRenderableTokenData(
|
|||||||
) || '';
|
) || '';
|
||||||
const rawFiat =
|
const rawFiat =
|
||||||
getTokenFiatAmount(
|
getTokenFiatAmount(
|
||||||
symbol === 'ETH' ? 1 : contractExchangeRates[address],
|
isSwapsDefaultTokenSymbol(symbol, chainId)
|
||||||
|
? 1
|
||||||
|
: contractExchangeRates[address],
|
||||||
conversionRate,
|
conversionRate,
|
||||||
currentCurrency,
|
currentCurrency,
|
||||||
string,
|
string,
|
||||||
@ -70,30 +77,32 @@ export function getRenderableTokenData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useTokensToSearch({ usersTokens = [], topTokens = {} }) {
|
export function useTokensToSearch({ usersTokens = [], topTokens = {} }) {
|
||||||
|
const chainId = useSelector(getCurrentChainId);
|
||||||
const tokenConversionRates = useSelector(getTokenExchangeRates, isEqual);
|
const tokenConversionRates = useSelector(getTokenExchangeRates, isEqual);
|
||||||
const conversionRate = useSelector(getConversionRate);
|
const conversionRate = useSelector(getConversionRate);
|
||||||
const currentCurrency = useSelector(getCurrentCurrency);
|
const currentCurrency = useSelector(getCurrentCurrency);
|
||||||
const swapsEthToken = useSelector(getSwapsEthToken);
|
const defaultSwapsToken = useSelector(getSwapsDefaultToken);
|
||||||
|
|
||||||
const memoizedTopTokens = useEqualityCheck(topTokens);
|
const memoizedTopTokens = useEqualityCheck(topTokens);
|
||||||
const memoizedUsersToken = useEqualityCheck(usersTokens);
|
const memoizedUsersToken = useEqualityCheck(usersTokens);
|
||||||
|
|
||||||
const ethToken = getRenderableTokenData(
|
const defaultToken = getRenderableTokenData(
|
||||||
swapsEthToken,
|
defaultSwapsToken,
|
||||||
tokenConversionRates,
|
tokenConversionRates,
|
||||||
conversionRate,
|
conversionRate,
|
||||||
currentCurrency,
|
currentCurrency,
|
||||||
|
chainId,
|
||||||
);
|
);
|
||||||
const memoizedEthToken = useEqualityCheck(ethToken);
|
const memoizedDefaultToken = useEqualityCheck(defaultToken);
|
||||||
|
|
||||||
const swapsTokens = useSelector(getSwapsTokens) || [];
|
const swapsTokens = useSelector(getSwapsTokens) || [];
|
||||||
|
|
||||||
const tokensToSearch = swapsTokens.length
|
const tokensToSearch = swapsTokens.length
|
||||||
? swapsTokens
|
? swapsTokens
|
||||||
: [
|
: [
|
||||||
memoizedEthToken,
|
memoizedDefaultToken,
|
||||||
...tokenList.filter(
|
...tokenList.filter(
|
||||||
(token) => token.symbol !== memoizedEthToken.symbol,
|
(token) => token.symbol !== memoizedDefaultToken.symbol,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -116,9 +125,10 @@ export function useTokensToSearch({ usersTokens = [], topTokens = {} }) {
|
|||||||
tokenConversionRates,
|
tokenConversionRates,
|
||||||
conversionRate,
|
conversionRate,
|
||||||
currentCurrency,
|
currentCurrency,
|
||||||
|
chainId,
|
||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
renderableDataToken.symbol === 'ETH' ||
|
isSwapsDefaultTokenSymbol(renderableDataToken.symbol, chainId) ||
|
||||||
(usersTokensAddressMap[token.address] &&
|
(usersTokensAddressMap[token.address] &&
|
||||||
Number(renderableDataToken.balance ?? 0) !== 0)
|
Number(renderableDataToken.balance ?? 0) !== 0)
|
||||||
) {
|
) {
|
||||||
@ -150,5 +160,6 @@ export function useTokensToSearch({ usersTokens = [], topTokens = {} }) {
|
|||||||
conversionRate,
|
conversionRate,
|
||||||
currentCurrency,
|
currentCurrency,
|
||||||
memoizedTopTokens,
|
memoizedTopTokens,
|
||||||
|
chainId,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,13 @@ import {
|
|||||||
getShouldShowFiat,
|
getShouldShowFiat,
|
||||||
getNativeCurrency,
|
getNativeCurrency,
|
||||||
getCurrentCurrency,
|
getCurrentCurrency,
|
||||||
|
getCurrentChainId,
|
||||||
} from '../selectors';
|
} from '../selectors';
|
||||||
import { getTokens } from '../ducks/metamask/metamask';
|
import { getTokens } from '../ducks/metamask/metamask';
|
||||||
import { getMessage } from '../helpers/utils/i18n-helper';
|
import { getMessage } from '../helpers/utils/i18n-helper';
|
||||||
import messages from '../../../app/_locales/en/messages.json';
|
import messages from '../../../app/_locales/en/messages.json';
|
||||||
import { ASSET_ROUTE, DEFAULT_ROUTE } from '../helpers/constants/routes';
|
import { ASSET_ROUTE, DEFAULT_ROUTE } from '../helpers/constants/routes';
|
||||||
|
import { MAINNET_CHAIN_ID } from '../../../shared/constants/network';
|
||||||
import {
|
import {
|
||||||
TRANSACTION_TYPES,
|
TRANSACTION_TYPES,
|
||||||
TRANSACTION_GROUP_CATEGORIES,
|
TRANSACTION_GROUP_CATEGORIES,
|
||||||
@ -164,6 +166,8 @@ describe('useTransactionDisplayData', function () {
|
|||||||
return 'ETH';
|
return 'ETH';
|
||||||
} else if (selector === getCurrentCurrency) {
|
} else if (selector === getCurrentCurrency) {
|
||||||
return 'ETH';
|
return 'ETH';
|
||||||
|
} else if (selector === getCurrentChainId) {
|
||||||
|
return MAINNET_CHAIN_ID;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
@ -6,12 +6,14 @@ import { useHistory } from 'react-router-dom';
|
|||||||
import { I18nContext } from '../../../contexts/i18n';
|
import { I18nContext } from '../../../contexts/i18n';
|
||||||
import { useNewMetricEvent } from '../../../hooks/useMetricEvent';
|
import { useNewMetricEvent } from '../../../hooks/useMetricEvent';
|
||||||
import { MetaMetricsContext } from '../../../contexts/metametrics.new';
|
import { MetaMetricsContext } from '../../../contexts/metametrics.new';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getCurrentChainId,
|
getCurrentChainId,
|
||||||
getCurrentCurrency,
|
getCurrentCurrency,
|
||||||
getRpcPrefsForCurrentProvider,
|
getRpcPrefsForCurrentProvider,
|
||||||
getUSDConversionRate,
|
getUSDConversionRate,
|
||||||
} from '../../../selectors';
|
} from '../../../selectors';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getUsedQuote,
|
getUsedQuote,
|
||||||
getFetchParams,
|
getFetchParams,
|
||||||
@ -23,7 +25,6 @@ import {
|
|||||||
prepareToLeaveSwaps,
|
prepareToLeaveSwaps,
|
||||||
} from '../../../ducks/swaps/swaps';
|
} from '../../../ducks/swaps/swaps';
|
||||||
import Mascot from '../../../components/ui/mascot';
|
import Mascot from '../../../components/ui/mascot';
|
||||||
import PulseLoader from '../../../components/ui/pulse-loader';
|
|
||||||
import {
|
import {
|
||||||
QUOTES_EXPIRED_ERROR,
|
QUOTES_EXPIRED_ERROR,
|
||||||
SWAP_FAILED_ERROR,
|
SWAP_FAILED_ERROR,
|
||||||
@ -31,6 +32,9 @@ import {
|
|||||||
QUOTES_NOT_AVAILABLE_ERROR,
|
QUOTES_NOT_AVAILABLE_ERROR,
|
||||||
OFFLINE_FOR_MAINTENANCE,
|
OFFLINE_FOR_MAINTENANCE,
|
||||||
} from '../../../../../shared/constants/swaps';
|
} from '../../../../../shared/constants/swaps';
|
||||||
|
import { isSwapsDefaultTokenSymbol } from '../../../../../shared/modules/swaps.utils';
|
||||||
|
import PulseLoader from '../../../components/ui/pulse-loader';
|
||||||
|
|
||||||
import { ASSET_ROUTE, DEFAULT_ROUTE } from '../../../helpers/constants/routes';
|
import { ASSET_ROUTE, DEFAULT_ROUTE } from '../../../helpers/constants/routes';
|
||||||
|
|
||||||
import { getRenderableNetworkFeesForQuote } from '../swaps.util';
|
import { getRenderableNetworkFeesForQuote } from '../swaps.util';
|
||||||
@ -73,16 +77,17 @@ export default function AwaitingSwap({
|
|||||||
let feeinUnformattedFiat;
|
let feeinUnformattedFiat;
|
||||||
|
|
||||||
if (usedQuote && swapsGasPrice) {
|
if (usedQuote && swapsGasPrice) {
|
||||||
const renderableNetworkFees = getRenderableNetworkFeesForQuote(
|
const renderableNetworkFees = getRenderableNetworkFeesForQuote({
|
||||||
usedQuote.gasEstimateWithRefund || usedQuote.averageGas,
|
tradeGas: usedQuote.gasEstimateWithRefund || usedQuote.averageGas,
|
||||||
approveTxParams?.gas || '0x0',
|
approveGas: approveTxParams?.gas || '0x0',
|
||||||
swapsGasPrice,
|
gasPrice: swapsGasPrice,
|
||||||
currentCurrency,
|
currentCurrency,
|
||||||
usdConversionRate,
|
conversionRate: usdConversionRate,
|
||||||
usedQuote?.trade?.value,
|
tradeValue: usedQuote?.trade?.value,
|
||||||
sourceTokenInfo?.symbol,
|
sourceSymbol: sourceTokenInfo?.symbol,
|
||||||
usedQuote.sourceAmount,
|
sourceAmount: usedQuote.sourceAmount,
|
||||||
);
|
chainId,
|
||||||
|
});
|
||||||
feeinUnformattedFiat = renderableNetworkFees.rawNetworkFees;
|
feeinUnformattedFiat = renderableNetworkFees.rawNetworkFees;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,7 +233,9 @@ export default function AwaitingSwap({
|
|||||||
);
|
);
|
||||||
} else if (errorKey) {
|
} else if (errorKey) {
|
||||||
await dispatch(navigateBackToBuildQuote(history));
|
await dispatch(navigateBackToBuildQuote(history));
|
||||||
} else if (destinationTokenInfo?.symbol === 'ETH') {
|
} else if (
|
||||||
|
isSwapsDefaultTokenSymbol(destinationTokenInfo?.symbol, chainId)
|
||||||
|
) {
|
||||||
history.push(DEFAULT_ROUTE);
|
history.push(DEFAULT_ROUTE);
|
||||||
} else {
|
} else {
|
||||||
history.push(`${ASSET_ROUTE}/${destinationTokenInfo?.address}`);
|
history.push(`${ASSET_ROUTE}/${destinationTokenInfo?.address}`);
|
||||||
|
@ -29,10 +29,11 @@ import {
|
|||||||
getFetchParams,
|
getFetchParams,
|
||||||
} from '../../../ducks/swaps/swaps';
|
} from '../../../ducks/swaps/swaps';
|
||||||
import {
|
import {
|
||||||
getSwapsEthToken,
|
getSwapsDefaultToken,
|
||||||
getTokenExchangeRates,
|
getTokenExchangeRates,
|
||||||
getConversionRate,
|
getConversionRate,
|
||||||
getCurrentCurrency,
|
getCurrentCurrency,
|
||||||
|
getCurrentChainId,
|
||||||
} from '../../../selectors';
|
} from '../../../selectors';
|
||||||
import {
|
import {
|
||||||
getValueFromWeiHex,
|
getValueFromWeiHex,
|
||||||
@ -44,7 +45,10 @@ import { useTokenTracker } from '../../../hooks/useTokenTracker';
|
|||||||
import { useTokenFiatAmount } from '../../../hooks/useTokenFiatAmount';
|
import { useTokenFiatAmount } from '../../../hooks/useTokenFiatAmount';
|
||||||
import { useEthFiatAmount } from '../../../hooks/useEthFiatAmount';
|
import { useEthFiatAmount } from '../../../hooks/useEthFiatAmount';
|
||||||
|
|
||||||
import { ETH_SWAPS_TOKEN_OBJECT } from '../../../../../shared/constants/swaps';
|
import {
|
||||||
|
isSwapsDefaultTokenAddress,
|
||||||
|
isSwapsDefaultTokenSymbol,
|
||||||
|
} from '../../../../../shared/modules/swaps.utils';
|
||||||
|
|
||||||
import { resetSwapsPostFetchState, removeToken } from '../../../store/actions';
|
import { resetSwapsPostFetchState, removeToken } from '../../../store/actions';
|
||||||
import { fetchTokenPrice, fetchTokenBalance } from '../swaps.util';
|
import { fetchTokenPrice, fetchTokenBalance } from '../swaps.util';
|
||||||
@ -84,21 +88,29 @@ export default function BuildQuote({
|
|||||||
const topAssets = useSelector(getTopAssets);
|
const topAssets = useSelector(getTopAssets);
|
||||||
const fromToken = useSelector(getFromToken);
|
const fromToken = useSelector(getFromToken);
|
||||||
const toToken = useSelector(getToToken) || destinationTokenInfo;
|
const toToken = useSelector(getToToken) || destinationTokenInfo;
|
||||||
const swapsEthToken = useSelector(getSwapsEthToken);
|
const defaultSwapsToken = useSelector(getSwapsDefaultToken);
|
||||||
const fetchParamsFromToken =
|
const chainId = useSelector(getCurrentChainId);
|
||||||
sourceTokenInfo?.symbol === 'ETH' ? swapsEthToken : sourceTokenInfo;
|
|
||||||
|
|
||||||
const tokenConversionRates = useSelector(getTokenExchangeRates, isEqual);
|
const tokenConversionRates = useSelector(getTokenExchangeRates, isEqual);
|
||||||
const conversionRate = useSelector(getConversionRate);
|
const conversionRate = useSelector(getConversionRate);
|
||||||
const currentCurrency = useSelector(getCurrentCurrency);
|
const currentCurrency = useSelector(getCurrentCurrency);
|
||||||
|
|
||||||
|
const fetchParamsFromToken = isSwapsDefaultTokenSymbol(
|
||||||
|
sourceTokenInfo?.symbol,
|
||||||
|
chainId,
|
||||||
|
)
|
||||||
|
? defaultSwapsToken
|
||||||
|
: sourceTokenInfo;
|
||||||
|
|
||||||
const { loading, tokensWithBalances } = useTokenTracker(tokens);
|
const { loading, tokensWithBalances } = useTokenTracker(tokens);
|
||||||
|
|
||||||
// If the fromToken was set in a call to `onFromSelect` (see below), and that from token has a balance
|
// If the fromToken was set in a call to `onFromSelect` (see below), and that from token has a balance
|
||||||
// but is not in tokensWithBalances or tokens, then we want to add it to the usersTokens array so that
|
// but is not in tokensWithBalances or tokens, then we want to add it to the usersTokens array so that
|
||||||
// the balance of the token can appear in the from token selection dropdown
|
// the balance of the token can appear in the from token selection dropdown
|
||||||
const fromTokenArray =
|
const fromTokenArray =
|
||||||
fromToken?.symbol !== 'ETH' && fromToken?.balance ? [fromToken] : [];
|
!isSwapsDefaultTokenSymbol(fromToken?.symbol, chainId) && fromToken?.balance
|
||||||
|
? [fromToken]
|
||||||
|
: [];
|
||||||
const usersTokens = uniqBy(
|
const usersTokens = uniqBy(
|
||||||
[...tokensWithBalances, ...tokens, ...fromTokenArray],
|
[...tokensWithBalances, ...tokens, ...fromTokenArray],
|
||||||
'address',
|
'address',
|
||||||
@ -110,6 +122,7 @@ export default function BuildQuote({
|
|||||||
tokenConversionRates,
|
tokenConversionRates,
|
||||||
conversionRate,
|
conversionRate,
|
||||||
currentCurrency,
|
currentCurrency,
|
||||||
|
chainId,
|
||||||
);
|
);
|
||||||
|
|
||||||
const tokensToSearch = useTokensToSearch({
|
const tokensToSearch = useTokensToSearch({
|
||||||
@ -119,9 +132,9 @@ export default function BuildQuote({
|
|||||||
const selectedToToken =
|
const selectedToToken =
|
||||||
tokensToSearch.find(({ address }) => address === toToken?.address) ||
|
tokensToSearch.find(({ address }) => address === toToken?.address) ||
|
||||||
toToken;
|
toToken;
|
||||||
const toTokenIsNotEth =
|
const toTokenIsNotDefault =
|
||||||
selectedToToken?.address &&
|
selectedToToken?.address &&
|
||||||
selectedToToken?.address !== ETH_SWAPS_TOKEN_OBJECT.address;
|
!isSwapsDefaultTokenAddress(selectedToToken?.address, chainId);
|
||||||
const occurances = Number(selectedToToken?.occurances || 0);
|
const occurances = Number(selectedToToken?.occurances || 0);
|
||||||
const {
|
const {
|
||||||
address: fromTokenAddress,
|
address: fromTokenAddress,
|
||||||
@ -151,8 +164,9 @@ export default function BuildQuote({
|
|||||||
{ showFiat: true },
|
{ showFiat: true },
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
const swapFromFiatValue =
|
const swapFromFiatValue = isSwapsDefaultTokenSymbol(fromTokenSymbol, chainId)
|
||||||
fromTokenSymbol === 'ETH' ? swapFromEthFiatValue : swapFromTokenFiatValue;
|
? swapFromEthFiatValue
|
||||||
|
: swapFromTokenFiatValue;
|
||||||
|
|
||||||
const onFromSelect = (token) => {
|
const onFromSelect = (token) => {
|
||||||
if (
|
if (
|
||||||
@ -227,15 +241,17 @@ export default function BuildQuote({
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const notEth =
|
const notDefault = !isSwapsDefaultTokenAddress(
|
||||||
tokensWithBalancesFromToken?.address !== ETH_SWAPS_TOKEN_OBJECT.address;
|
tokensWithBalancesFromToken?.address,
|
||||||
|
chainId,
|
||||||
|
);
|
||||||
const addressesAreTheSame =
|
const addressesAreTheSame =
|
||||||
tokensWithBalancesFromToken?.address ===
|
tokensWithBalancesFromToken?.address ===
|
||||||
previousTokensWithBalancesFromToken?.address;
|
previousTokensWithBalancesFromToken?.address;
|
||||||
const balanceHasChanged =
|
const balanceHasChanged =
|
||||||
tokensWithBalancesFromToken?.balance !==
|
tokensWithBalancesFromToken?.balance !==
|
||||||
previousTokensWithBalancesFromToken?.balance;
|
previousTokensWithBalancesFromToken?.balance;
|
||||||
if (notEth && addressesAreTheSame && balanceHasChanged) {
|
if (notDefault && addressesAreTheSame && balanceHasChanged) {
|
||||||
dispatch(
|
dispatch(
|
||||||
setSwapsFromToken({
|
setSwapsFromToken({
|
||||||
...fromToken,
|
...fromToken,
|
||||||
@ -249,12 +265,13 @@ export default function BuildQuote({
|
|||||||
tokensWithBalancesFromToken,
|
tokensWithBalancesFromToken,
|
||||||
previousTokensWithBalancesFromToken,
|
previousTokensWithBalancesFromToken,
|
||||||
fromToken,
|
fromToken,
|
||||||
|
chainId,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// If the eth balance changes while on build quote, we update the selected from token
|
// If the eth balance changes while on build quote, we update the selected from token
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
fromToken?.address === ETH_SWAPS_TOKEN_OBJECT.address &&
|
isSwapsDefaultTokenAddress(fromToken?.address, chainId) &&
|
||||||
fromToken?.balance !== hexToDecimal(ethBalance)
|
fromToken?.balance !== hexToDecimal(ethBalance)
|
||||||
) {
|
) {
|
||||||
dispatch(
|
dispatch(
|
||||||
@ -269,7 +286,7 @@ export default function BuildQuote({
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [dispatch, fromToken, ethBalance]);
|
}, [dispatch, fromToken, ethBalance, chainId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (prevFromTokenBalance !== fromTokenBalance) {
|
if (prevFromTokenBalance !== fromTokenBalance) {
|
||||||
@ -286,7 +303,7 @@ export default function BuildQuote({
|
|||||||
<div className="build-quote__content">
|
<div className="build-quote__content">
|
||||||
<div className="build-quote__dropdown-input-pair-header">
|
<div className="build-quote__dropdown-input-pair-header">
|
||||||
<div className="build-quote__input-label">{t('swapSwapFrom')}</div>
|
<div className="build-quote__input-label">{t('swapSwapFrom')}</div>
|
||||||
{fromTokenSymbol !== 'ETH' && (
|
{!isSwapsDefaultTokenSymbol(fromTokenSymbol, chainId) && (
|
||||||
<div
|
<div
|
||||||
className="build-quote__max-button"
|
className="build-quote__max-button"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
@ -384,7 +401,7 @@ export default function BuildQuote({
|
|||||||
defaultToAll
|
defaultToAll
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{toTokenIsNotEth &&
|
{toTokenIsNotDefault &&
|
||||||
(occurances < 2 ? (
|
(occurances < 2 ? (
|
||||||
<ActionableMessage
|
<ActionableMessage
|
||||||
message={
|
message={
|
||||||
@ -474,7 +491,7 @@ export default function BuildQuote({
|
|||||||
!selectedToToken?.address ||
|
!selectedToToken?.address ||
|
||||||
Number(maxSlippage) === 0 ||
|
Number(maxSlippage) === 0 ||
|
||||||
Number(maxSlippage) > MAX_ALLOWED_SLIPPAGE ||
|
Number(maxSlippage) > MAX_ALLOWED_SLIPPAGE ||
|
||||||
(toTokenIsNotEth && occurances < 2 && !verificationClicked)
|
(toTokenIsNotDefault && occurances < 2 && !verificationClicked)
|
||||||
}
|
}
|
||||||
hideCancel
|
hideCancel
|
||||||
showTermsOfService
|
showTermsOfService
|
||||||
|
@ -12,6 +12,7 @@ import { I18nContext } from '../../contexts/i18n';
|
|||||||
import {
|
import {
|
||||||
getSelectedAccount,
|
getSelectedAccount,
|
||||||
getCurrentChainId,
|
getCurrentChainId,
|
||||||
|
getIsSwapsChain,
|
||||||
} from '../../selectors/selectors';
|
} from '../../selectors/selectors';
|
||||||
import {
|
import {
|
||||||
getQuotes,
|
getQuotes,
|
||||||
@ -45,7 +46,6 @@ import {
|
|||||||
SWAP_FAILED_ERROR,
|
SWAP_FAILED_ERROR,
|
||||||
OFFLINE_FOR_MAINTENANCE,
|
OFFLINE_FOR_MAINTENANCE,
|
||||||
} from '../../../../shared/constants/swaps';
|
} from '../../../../shared/constants/swaps';
|
||||||
import { MAINNET_CHAIN_ID } from '../../../../shared/constants/network';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
resetBackgroundSwapsState,
|
resetBackgroundSwapsState,
|
||||||
@ -96,6 +96,8 @@ export default function Swap() {
|
|||||||
const fetchingQuotes = useSelector(getFetchingQuotes);
|
const fetchingQuotes = useSelector(getFetchingQuotes);
|
||||||
let swapsErrorKey = useSelector(getSwapsErrorKey);
|
let swapsErrorKey = useSelector(getSwapsErrorKey);
|
||||||
const swapsEnabled = useSelector(getSwapsFeatureLiveness);
|
const swapsEnabled = useSelector(getSwapsFeatureLiveness);
|
||||||
|
const chainId = useSelector(getCurrentChainId);
|
||||||
|
const isSwapsChain = useSelector(getIsSwapsChain);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
balance: ethBalance,
|
balance: ethBalance,
|
||||||
@ -116,6 +118,7 @@ export default function Swap() {
|
|||||||
selectedAccountAddress,
|
selectedAccountAddress,
|
||||||
destinationTokenInfo?.decimals,
|
destinationTokenInfo?.decimals,
|
||||||
approveTxData,
|
approveTxData,
|
||||||
|
chainId,
|
||||||
);
|
);
|
||||||
const tradeConfirmed = tradeTxData?.status === TRANSACTION_STATUSES.CONFIRMED;
|
const tradeConfirmed = tradeTxData?.status === TRANSACTION_STATUSES.CONFIRMED;
|
||||||
const approveError =
|
const approveError =
|
||||||
@ -155,26 +158,26 @@ export default function Swap() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchTokens()
|
fetchTokens(chainId)
|
||||||
.then((tokens) => {
|
.then((tokens) => {
|
||||||
dispatch(setSwapsTokens(tokens));
|
dispatch(setSwapsTokens(tokens));
|
||||||
})
|
})
|
||||||
.catch((error) => console.error(error));
|
.catch((error) => console.error(error));
|
||||||
|
|
||||||
fetchTopAssets().then((topAssets) => {
|
fetchTopAssets(chainId).then((topAssets) => {
|
||||||
dispatch(setTopAssets(topAssets));
|
dispatch(setTopAssets(topAssets));
|
||||||
});
|
});
|
||||||
|
|
||||||
fetchAggregatorMetadata().then((newAggregatorMetadata) => {
|
fetchAggregatorMetadata(chainId).then((newAggregatorMetadata) => {
|
||||||
dispatch(setAggregatorMetadata(newAggregatorMetadata));
|
dispatch(setAggregatorMetadata(newAggregatorMetadata));
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch(fetchAndSetSwapsGasPriceInfo());
|
dispatch(fetchAndSetSwapsGasPriceInfo(chainId));
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
dispatch(prepareToLeaveSwaps());
|
dispatch(prepareToLeaveSwaps());
|
||||||
};
|
};
|
||||||
}, [dispatch]);
|
}, [dispatch, chainId]);
|
||||||
|
|
||||||
const exitedSwapsEvent = useNewMetricEvent({
|
const exitedSwapsEvent = useNewMetricEvent({
|
||||||
event: 'Exited Swaps',
|
event: 'Exited Swaps',
|
||||||
@ -224,8 +227,7 @@ export default function Swap() {
|
|||||||
return () => window.removeEventListener('beforeunload', fn);
|
return () => window.removeEventListener('beforeunload', fn);
|
||||||
}, [dispatch, isLoadingQuotesRoute]);
|
}, [dispatch, isLoadingQuotesRoute]);
|
||||||
|
|
||||||
const chainId = useSelector(getCurrentChainId);
|
if (!isSwapsChain) {
|
||||||
if (chainId !== MAINNET_CHAIN_ID) {
|
|
||||||
return <Redirect to={{ pathname: DEFAULT_ROUTE }} />;
|
return <Redirect to={{ pathname: DEFAULT_ROUTE }} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import { setSwapsFromToken } from '../../../ducks/swaps/swaps';
|
|||||||
import { I18nContext } from '../../../contexts/i18n';
|
import { I18nContext } from '../../../contexts/i18n';
|
||||||
import { BUILD_QUOTE_ROUTE } from '../../../helpers/constants/routes';
|
import { BUILD_QUOTE_ROUTE } from '../../../helpers/constants/routes';
|
||||||
import { useNewMetricEvent } from '../../../hooks/useMetricEvent';
|
import { useNewMetricEvent } from '../../../hooks/useMetricEvent';
|
||||||
import { getSwapsEthToken } from '../../../selectors';
|
import { getSwapsDefaultToken } from '../../../selectors';
|
||||||
import Button from '../../../components/ui/button';
|
import Button from '../../../components/ui/button';
|
||||||
import Popover from '../../../components/ui/popover';
|
import Popover from '../../../components/ui/popover';
|
||||||
|
|
||||||
@ -14,9 +14,14 @@ export default function IntroPopup({ onClose }) {
|
|||||||
const dispatch = useDispatch(useDispatch);
|
const dispatch = useDispatch(useDispatch);
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const t = useContext(I18nContext);
|
const t = useContext(I18nContext);
|
||||||
|
|
||||||
|
const swapsDefaultToken = useSelector(getSwapsDefaultToken);
|
||||||
const enteredSwapsEvent = useNewMetricEvent({
|
const enteredSwapsEvent = useNewMetricEvent({
|
||||||
event: 'Swaps Opened',
|
event: 'Swaps Opened',
|
||||||
properties: { source: 'Intro popup', active_currency: 'ETH' },
|
properties: {
|
||||||
|
source: 'Intro popup',
|
||||||
|
active_currency: swapsDefaultToken.symbol,
|
||||||
|
},
|
||||||
category: 'swaps',
|
category: 'swaps',
|
||||||
});
|
});
|
||||||
const blogPostVisitedEvent = useNewMetricEvent({
|
const blogPostVisitedEvent = useNewMetricEvent({
|
||||||
@ -31,7 +36,6 @@ export default function IntroPopup({ onClose }) {
|
|||||||
event: 'Product Overview Dismissed',
|
event: 'Product Overview Dismissed',
|
||||||
category: 'swaps',
|
category: 'swaps',
|
||||||
});
|
});
|
||||||
const swapsEthToken = useSelector(getSwapsEthToken);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="intro-popup">
|
<div className="intro-popup">
|
||||||
@ -51,7 +55,7 @@ export default function IntroPopup({ onClose }) {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
onClose();
|
onClose();
|
||||||
enteredSwapsEvent();
|
enteredSwapsEvent();
|
||||||
dispatch(setSwapsFromToken(swapsEthToken));
|
dispatch(setSwapsFromToken(swapsDefaultToken));
|
||||||
history.push(BUILD_QUOTE_ROUTE);
|
history.push(BUILD_QUOTE_ROUTE);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -3,9 +3,15 @@ import BigNumber from 'bignumber.js';
|
|||||||
import abi from 'human-standard-token-abi';
|
import abi from 'human-standard-token-abi';
|
||||||
import { isValidAddress } from 'ethereumjs-util';
|
import { isValidAddress } from 'ethereumjs-util';
|
||||||
import {
|
import {
|
||||||
ETH_SWAPS_TOKEN_OBJECT,
|
SWAPS_CHAINID_DEFAULT_TOKEN_MAP,
|
||||||
METASWAP_API_HOST,
|
METASWAP_CHAINID_API_HOST_MAP,
|
||||||
} from '../../../../shared/constants/swaps';
|
} from '../../../../shared/constants/swaps';
|
||||||
|
import {
|
||||||
|
isSwapsDefaultTokenAddress,
|
||||||
|
isSwapsDefaultTokenSymbol,
|
||||||
|
} from '../../../../shared/modules/swaps.utils';
|
||||||
|
|
||||||
|
import { MAINNET_CHAIN_ID } from '../../../../shared/constants/network';
|
||||||
import {
|
import {
|
||||||
calcTokenValue,
|
calcTokenValue,
|
||||||
calcTokenAmount,
|
calcTokenAmount,
|
||||||
@ -30,22 +36,22 @@ const TOKEN_TRANSFER_LOG_TOPIC_HASH =
|
|||||||
|
|
||||||
const CACHE_REFRESH_ONE_HOUR = 3600000;
|
const CACHE_REFRESH_ONE_HOUR = 3600000;
|
||||||
|
|
||||||
const getBaseApi = function (type) {
|
const getBaseApi = function (type, chainId = MAINNET_CHAIN_ID) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'trade':
|
case 'trade':
|
||||||
return `${METASWAP_API_HOST}/trades?`;
|
return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/trades?`;
|
||||||
case 'tokens':
|
case 'tokens':
|
||||||
return `${METASWAP_API_HOST}/tokens`;
|
return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/tokens`;
|
||||||
case 'topAssets':
|
case 'topAssets':
|
||||||
return `${METASWAP_API_HOST}/topAssets`;
|
return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/topAssets`;
|
||||||
case 'featureFlag':
|
case 'featureFlag':
|
||||||
return `${METASWAP_API_HOST}/featureFlag`;
|
return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/featureFlag`;
|
||||||
case 'aggregatorMetadata':
|
case 'aggregatorMetadata':
|
||||||
return `${METASWAP_API_HOST}/aggregatorMetadata`;
|
return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/aggregatorMetadata`;
|
||||||
case 'gasPrices':
|
case 'gasPrices':
|
||||||
return `${METASWAP_API_HOST}/gasPrices`;
|
return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/gasPrices`;
|
||||||
case 'refreshTime':
|
case 'refreshTime':
|
||||||
return `${METASWAP_API_HOST}/quoteRefreshRate`;
|
return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/quoteRefreshRate`;
|
||||||
default:
|
default:
|
||||||
throw new Error('getBaseApi requires an api call type');
|
throw new Error('getBaseApi requires an api call type');
|
||||||
}
|
}
|
||||||
@ -205,15 +211,18 @@ function validateData(validators, object, urlUsed) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchTradesInfo({
|
export async function fetchTradesInfo(
|
||||||
slippage,
|
{
|
||||||
sourceToken,
|
slippage,
|
||||||
sourceDecimals,
|
sourceToken,
|
||||||
destinationToken,
|
sourceDecimals,
|
||||||
value,
|
destinationToken,
|
||||||
fromAddress,
|
value,
|
||||||
exchangeList,
|
fromAddress,
|
||||||
}) {
|
exchangeList,
|
||||||
|
},
|
||||||
|
{ chainId },
|
||||||
|
) {
|
||||||
const urlParams = {
|
const urlParams = {
|
||||||
destinationToken,
|
destinationToken,
|
||||||
sourceToken,
|
sourceToken,
|
||||||
@ -228,7 +237,7 @@ export async function fetchTradesInfo({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const queryString = new URLSearchParams(urlParams).toString();
|
const queryString = new URLSearchParams(urlParams).toString();
|
||||||
const tradeURL = `${getBaseApi('trade')}${queryString}`;
|
const tradeURL = `${getBaseApi('trade', chainId)}${queryString}`;
|
||||||
const tradesResponse = await fetchWithCache(
|
const tradesResponse = await fetchWithCache(
|
||||||
tradeURL,
|
tradeURL,
|
||||||
{ method: 'GET' },
|
{ method: 'GET' },
|
||||||
@ -272,21 +281,21 @@ export async function fetchTradesInfo({
|
|||||||
return newQuotes;
|
return newQuotes;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchTokens() {
|
export async function fetchTokens(chainId) {
|
||||||
const tokenUrl = getBaseApi('tokens');
|
const tokenUrl = getBaseApi('tokens', chainId);
|
||||||
const tokens = await fetchWithCache(
|
const tokens = await fetchWithCache(
|
||||||
tokenUrl,
|
tokenUrl,
|
||||||
{ method: 'GET' },
|
{ method: 'GET' },
|
||||||
{ cacheRefreshTime: CACHE_REFRESH_ONE_HOUR },
|
{ cacheRefreshTime: CACHE_REFRESH_ONE_HOUR },
|
||||||
);
|
);
|
||||||
const filteredTokens = [
|
const filteredTokens = [
|
||||||
ETH_SWAPS_TOKEN_OBJECT,
|
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[chainId],
|
||||||
...tokens.filter((token) => {
|
...tokens.filter((token) => {
|
||||||
return (
|
return (
|
||||||
validateData(TOKEN_VALIDATORS, token, tokenUrl) &&
|
validateData(TOKEN_VALIDATORS, token, tokenUrl) &&
|
||||||
!(
|
!(
|
||||||
token.symbol === ETH_SWAPS_TOKEN_OBJECT.symbol ||
|
isSwapsDefaultTokenSymbol(token.symbol, chainId) ||
|
||||||
token.address === ETH_SWAPS_TOKEN_OBJECT.address
|
isSwapsDefaultTokenAddress(token.address, chainId)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@ -294,8 +303,8 @@ export async function fetchTokens() {
|
|||||||
return filteredTokens;
|
return filteredTokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchAggregatorMetadata() {
|
export async function fetchAggregatorMetadata(chainId) {
|
||||||
const aggregatorMetadataUrl = getBaseApi('aggregatorMetadata');
|
const aggregatorMetadataUrl = getBaseApi('aggregatorMetadata', chainId);
|
||||||
const aggregators = await fetchWithCache(
|
const aggregators = await fetchWithCache(
|
||||||
aggregatorMetadataUrl,
|
aggregatorMetadataUrl,
|
||||||
{ method: 'GET' },
|
{ method: 'GET' },
|
||||||
@ -316,8 +325,8 @@ export async function fetchAggregatorMetadata() {
|
|||||||
return filteredAggregators;
|
return filteredAggregators;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchTopAssets() {
|
export async function fetchTopAssets(chainId) {
|
||||||
const topAssetsUrl = getBaseApi('topAssets');
|
const topAssetsUrl = getBaseApi('topAssets', chainId);
|
||||||
const response = await fetchWithCache(
|
const response = await fetchWithCache(
|
||||||
topAssetsUrl,
|
topAssetsUrl,
|
||||||
{ method: 'GET' },
|
{ method: 'GET' },
|
||||||
@ -332,18 +341,18 @@ export async function fetchTopAssets() {
|
|||||||
return topAssetsMap;
|
return topAssetsMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchSwapsFeatureLiveness() {
|
export async function fetchSwapsFeatureLiveness(chainId) {
|
||||||
const status = await fetchWithCache(
|
const status = await fetchWithCache(
|
||||||
getBaseApi('featureFlag'),
|
getBaseApi('featureFlag', chainId),
|
||||||
{ method: 'GET' },
|
{ method: 'GET' },
|
||||||
{ cacheRefreshTime: 600000 },
|
{ cacheRefreshTime: 600000 },
|
||||||
);
|
);
|
||||||
return status?.active;
|
return status?.active;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchSwapsQuoteRefreshTime() {
|
export async function fetchSwapsQuoteRefreshTime(chainId) {
|
||||||
const response = await fetchWithCache(
|
const response = await fetchWithCache(
|
||||||
getBaseApi('refreshTime'),
|
getBaseApi('refreshTime', chainId),
|
||||||
{ method: 'GET' },
|
{ method: 'GET' },
|
||||||
{ cacheRefreshTime: 600000 },
|
{ cacheRefreshTime: 600000 },
|
||||||
);
|
);
|
||||||
@ -378,8 +387,8 @@ export async function fetchTokenBalance(address, userAddress) {
|
|||||||
return usersToken;
|
return usersToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchSwapsGasPrices() {
|
export async function fetchSwapsGasPrices(chainId) {
|
||||||
const gasPricesUrl = getBaseApi('gasPrices');
|
const gasPricesUrl = getBaseApi('gasPrices', chainId);
|
||||||
const response = await fetchWithCache(
|
const response = await fetchWithCache(
|
||||||
gasPricesUrl,
|
gasPricesUrl,
|
||||||
{ method: 'GET' },
|
{ method: 'GET' },
|
||||||
@ -408,7 +417,7 @@ export async function fetchSwapsGasPrices() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRenderableNetworkFeesForQuote(
|
export function getRenderableNetworkFeesForQuote({
|
||||||
tradeGas,
|
tradeGas,
|
||||||
approveGas,
|
approveGas,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
@ -417,14 +426,18 @@ export function getRenderableNetworkFeesForQuote(
|
|||||||
tradeValue,
|
tradeValue,
|
||||||
sourceSymbol,
|
sourceSymbol,
|
||||||
sourceAmount,
|
sourceAmount,
|
||||||
) {
|
chainId,
|
||||||
|
}) {
|
||||||
const totalGasLimitForCalculation = new BigNumber(tradeGas || '0x0', 16)
|
const totalGasLimitForCalculation = new BigNumber(tradeGas || '0x0', 16)
|
||||||
.plus(approveGas || '0x0', 16)
|
.plus(approveGas || '0x0', 16)
|
||||||
.toString(16);
|
.toString(16);
|
||||||
const gasTotalInWeiHex = calcGasTotal(totalGasLimitForCalculation, gasPrice);
|
const gasTotalInWeiHex = calcGasTotal(totalGasLimitForCalculation, gasPrice);
|
||||||
|
|
||||||
const nonGasFee = new BigNumber(tradeValue, 16)
|
const nonGasFee = new BigNumber(tradeValue, 16)
|
||||||
.minus(sourceSymbol === 'ETH' ? sourceAmount : 0, 10)
|
.minus(
|
||||||
|
isSwapsDefaultTokenSymbol(sourceSymbol, chainId) ? sourceAmount : 0,
|
||||||
|
10,
|
||||||
|
)
|
||||||
.toString(16);
|
.toString(16);
|
||||||
|
|
||||||
const totalWeiCost = new BigNumber(gasTotalInWeiHex, 16)
|
const totalWeiCost = new BigNumber(gasTotalInWeiHex, 16)
|
||||||
@ -447,7 +460,7 @@ export function getRenderableNetworkFeesForQuote(
|
|||||||
rawNetworkFees,
|
rawNetworkFees,
|
||||||
rawEthFee: ethFee,
|
rawEthFee: ethFee,
|
||||||
feeInFiat: formattedNetworkFee,
|
feeInFiat: formattedNetworkFee,
|
||||||
feeInEth: `${ethFee} ETH`,
|
feeInEth: `${ethFee} ${SWAPS_CHAINID_DEFAULT_TOKEN_MAP[chainId].symbol}`,
|
||||||
nonGasFee,
|
nonGasFee,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -459,6 +472,7 @@ export function quotesToRenderableData(
|
|||||||
currentCurrency,
|
currentCurrency,
|
||||||
approveGas,
|
approveGas,
|
||||||
tokenConversionRates,
|
tokenConversionRates,
|
||||||
|
chainId,
|
||||||
) {
|
) {
|
||||||
return Object.values(quotes).map((quote) => {
|
return Object.values(quotes).map((quote) => {
|
||||||
const {
|
const {
|
||||||
@ -488,16 +502,17 @@ export function quotesToRenderableData(
|
|||||||
rawNetworkFees,
|
rawNetworkFees,
|
||||||
rawEthFee,
|
rawEthFee,
|
||||||
feeInEth,
|
feeInEth,
|
||||||
} = getRenderableNetworkFeesForQuote(
|
} = getRenderableNetworkFeesForQuote({
|
||||||
gasEstimateWithRefund || decimalToHex(averageGas || 800000),
|
tradeGas: gasEstimateWithRefund || decimalToHex(averageGas || 800000),
|
||||||
approveGas,
|
approveGas,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
currentCurrency,
|
currentCurrency,
|
||||||
conversionRate,
|
conversionRate,
|
||||||
trade.value,
|
tradeValue: trade.value,
|
||||||
sourceTokenInfo.symbol,
|
sourceSymbol: sourceTokenInfo.symbol,
|
||||||
sourceAmount,
|
sourceAmount,
|
||||||
);
|
chainId,
|
||||||
|
});
|
||||||
|
|
||||||
const slippageMultiplier = new BigNumber(100 - slippage).div(100);
|
const slippageMultiplier = new BigNumber(100 - slippage).div(100);
|
||||||
const minimumAmountReceived = new BigNumber(destinationValue)
|
const minimumAmountReceived = new BigNumber(destinationValue)
|
||||||
@ -506,18 +521,20 @@ export function quotesToRenderableData(
|
|||||||
|
|
||||||
const tokenConversionRate =
|
const tokenConversionRate =
|
||||||
tokenConversionRates[destinationTokenInfo.address];
|
tokenConversionRates[destinationTokenInfo.address];
|
||||||
const ethValueOfTrade =
|
const ethValueOfTrade = isSwapsDefaultTokenSymbol(
|
||||||
destinationTokenInfo.symbol === 'ETH'
|
destinationTokenInfo.symbol,
|
||||||
? calcTokenAmount(
|
chainId,
|
||||||
destinationAmount,
|
)
|
||||||
destinationTokenInfo.decimals,
|
? calcTokenAmount(destinationAmount, destinationTokenInfo.decimals).minus(
|
||||||
).minus(rawEthFee, 10)
|
rawEthFee,
|
||||||
: new BigNumber(tokenConversionRate || 0, 10)
|
10,
|
||||||
.times(
|
)
|
||||||
calcTokenAmount(destinationAmount, destinationTokenInfo.decimals),
|
: new BigNumber(tokenConversionRate || 0, 10)
|
||||||
10,
|
.times(
|
||||||
)
|
calcTokenAmount(destinationAmount, destinationTokenInfo.decimals),
|
||||||
.minus(rawEthFee, 10);
|
10,
|
||||||
|
)
|
||||||
|
.minus(rawEthFee, 10);
|
||||||
|
|
||||||
let liquiditySourceKey;
|
let liquiditySourceKey;
|
||||||
let renderedSlippage = slippage;
|
let renderedSlippage = slippage;
|
||||||
@ -566,9 +583,10 @@ export function getSwapsTokensReceivedFromTxMeta(
|
|||||||
accountAddress,
|
accountAddress,
|
||||||
tokenDecimals,
|
tokenDecimals,
|
||||||
approvalTxMeta,
|
approvalTxMeta,
|
||||||
|
chainId,
|
||||||
) {
|
) {
|
||||||
const txReceipt = txMeta?.txReceipt;
|
const txReceipt = txMeta?.txReceipt;
|
||||||
if (tokenSymbol === 'ETH') {
|
if (isSwapsDefaultTokenSymbol(tokenSymbol, chainId)) {
|
||||||
if (
|
if (
|
||||||
!txReceipt ||
|
!txReceipt ||
|
||||||
!txMeta ||
|
!txMeta ||
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
import proxyquire from 'proxyquire';
|
import proxyquire from 'proxyquire';
|
||||||
|
import { MAINNET_CHAIN_ID } from '../../../../shared/constants/network';
|
||||||
import {
|
import {
|
||||||
TRADES_BASE_PROD_URL,
|
TRADES_BASE_PROD_URL,
|
||||||
TOKENS_BASE_PROD_URL,
|
TOKENS_BASE_PROD_URL,
|
||||||
@ -89,42 +90,45 @@ describe('Swaps Util', function () {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
it('should fetch trade info on prod', async function () {
|
it('should fetch trade info on prod', async function () {
|
||||||
const result = await fetchTradesInfo({
|
const result = await fetchTradesInfo(
|
||||||
TOKENS,
|
{
|
||||||
slippage: '3',
|
TOKENS,
|
||||||
sourceToken: TOKENS[0].address,
|
slippage: '3',
|
||||||
destinationToken: TOKENS[1].address,
|
sourceToken: TOKENS[0].address,
|
||||||
value: '2000000000000000000',
|
destinationToken: TOKENS[1].address,
|
||||||
fromAddress: '0xmockAddress',
|
value: '2000000000000000000',
|
||||||
sourceSymbol: TOKENS[0].symbol,
|
fromAddress: '0xmockAddress',
|
||||||
sourceDecimals: TOKENS[0].decimals,
|
sourceSymbol: TOKENS[0].symbol,
|
||||||
sourceTokenInfo: { ...TOKENS[0] },
|
sourceDecimals: TOKENS[0].decimals,
|
||||||
destinationTokenInfo: { ...TOKENS[1] },
|
sourceTokenInfo: { ...TOKENS[0] },
|
||||||
});
|
destinationTokenInfo: { ...TOKENS[1] },
|
||||||
|
},
|
||||||
|
{ chainId: MAINNET_CHAIN_ID },
|
||||||
|
);
|
||||||
assert.deepStrictEqual(result, expectedResult2);
|
assert.deepStrictEqual(result, expectedResult2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('fetchTokens', function () {
|
describe('fetchTokens', function () {
|
||||||
it('should fetch tokens', async function () {
|
it('should fetch tokens', async function () {
|
||||||
const result = await fetchTokens(true);
|
const result = await fetchTokens(MAINNET_CHAIN_ID);
|
||||||
assert.deepStrictEqual(result, EXPECTED_TOKENS_RESULT);
|
assert.deepStrictEqual(result, EXPECTED_TOKENS_RESULT);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fetch tokens on prod', async function () {
|
it('should fetch tokens on prod', async function () {
|
||||||
const result = await fetchTokens(false);
|
const result = await fetchTokens(MAINNET_CHAIN_ID);
|
||||||
assert.deepStrictEqual(result, EXPECTED_TOKENS_RESULT);
|
assert.deepStrictEqual(result, EXPECTED_TOKENS_RESULT);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('fetchAggregatorMetadata', function () {
|
describe('fetchAggregatorMetadata', function () {
|
||||||
it('should fetch aggregator metadata', async function () {
|
it('should fetch aggregator metadata', async function () {
|
||||||
const result = await fetchAggregatorMetadata(true);
|
const result = await fetchAggregatorMetadata(MAINNET_CHAIN_ID);
|
||||||
assert.deepStrictEqual(result, AGGREGATOR_METADATA);
|
assert.deepStrictEqual(result, AGGREGATOR_METADATA);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fetch aggregator metadata on prod', async function () {
|
it('should fetch aggregator metadata on prod', async function () {
|
||||||
const result = await fetchAggregatorMetadata(false);
|
const result = await fetchAggregatorMetadata(MAINNET_CHAIN_ID);
|
||||||
assert.deepStrictEqual(result, AGGREGATOR_METADATA);
|
assert.deepStrictEqual(result, AGGREGATOR_METADATA);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -148,12 +152,12 @@ describe('Swaps Util', function () {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
it('should fetch top assets', async function () {
|
it('should fetch top assets', async function () {
|
||||||
const result = await fetchTopAssets(true);
|
const result = await fetchTopAssets(MAINNET_CHAIN_ID);
|
||||||
assert.deepStrictEqual(result, expectedResult);
|
assert.deepStrictEqual(result, expectedResult);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fetch top assets on prod', async function () {
|
it('should fetch top assets on prod', async function () {
|
||||||
const result = await fetchTopAssets(false);
|
const result = await fetchTopAssets(MAINNET_CHAIN_ID);
|
||||||
assert.deepStrictEqual(result, expectedResult);
|
assert.deepStrictEqual(result, expectedResult);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -36,7 +36,8 @@ import {
|
|||||||
getSelectedAccount,
|
getSelectedAccount,
|
||||||
getCurrentCurrency,
|
getCurrentCurrency,
|
||||||
getTokenExchangeRates,
|
getTokenExchangeRates,
|
||||||
getSwapsEthToken,
|
getSwapsDefaultToken,
|
||||||
|
getCurrentChainId,
|
||||||
} from '../../../selectors';
|
} from '../../../selectors';
|
||||||
import { toPrecisionWithoutTrailingZeros } from '../../../helpers/utils/util';
|
import { toPrecisionWithoutTrailingZeros } from '../../../helpers/utils/util';
|
||||||
import { getTokens } from '../../../ducks/metamask/metamask';
|
import { getTokens } from '../../../ducks/metamask/metamask';
|
||||||
@ -125,7 +126,8 @@ export default function ViewQuote() {
|
|||||||
const usedQuote = selectedQuote || topQuote;
|
const usedQuote = selectedQuote || topQuote;
|
||||||
const tradeValue = usedQuote?.trade?.value ?? '0x0';
|
const tradeValue = usedQuote?.trade?.value ?? '0x0';
|
||||||
const swapsQuoteRefreshTime = useSelector(getSwapsQuoteRefreshTime);
|
const swapsQuoteRefreshTime = useSelector(getSwapsQuoteRefreshTime);
|
||||||
const swapsEthToken = useSelector(getSwapsEthToken);
|
const defaultSwapsToken = useSelector(getSwapsDefaultToken);
|
||||||
|
const chainId = useSelector(getCurrentChainId);
|
||||||
|
|
||||||
const { isBestQuote } = usedQuote;
|
const { isBestQuote } = usedQuote;
|
||||||
|
|
||||||
@ -151,8 +153,8 @@ export default function ViewQuote() {
|
|||||||
|
|
||||||
const { tokensWithBalances } = useTokenTracker(swapsTokens, true);
|
const { tokensWithBalances } = useTokenTracker(swapsTokens, true);
|
||||||
const balanceToken =
|
const balanceToken =
|
||||||
fetchParamsSourceToken === swapsEthToken.address
|
fetchParamsSourceToken === defaultSwapsToken.address
|
||||||
? swapsEthToken
|
? defaultSwapsToken
|
||||||
: tokensWithBalances.find(
|
: tokensWithBalances.find(
|
||||||
({ address }) => address === fetchParamsSourceToken,
|
({ address }) => address === fetchParamsSourceToken,
|
||||||
);
|
);
|
||||||
@ -183,6 +185,7 @@ export default function ViewQuote() {
|
|||||||
currentCurrency,
|
currentCurrency,
|
||||||
approveGas,
|
approveGas,
|
||||||
memoizedTokenConversionRates,
|
memoizedTokenConversionRates,
|
||||||
|
chainId,
|
||||||
);
|
);
|
||||||
}, [
|
}, [
|
||||||
quotes,
|
quotes,
|
||||||
@ -191,6 +194,7 @@ export default function ViewQuote() {
|
|||||||
currentCurrency,
|
currentCurrency,
|
||||||
approveGas,
|
approveGas,
|
||||||
memoizedTokenConversionRates,
|
memoizedTokenConversionRates,
|
||||||
|
chainId,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const renderableDataForUsedQuote = renderablePopoverData.find(
|
const renderableDataForUsedQuote = renderablePopoverData.find(
|
||||||
@ -209,31 +213,33 @@ export default function ViewQuote() {
|
|||||||
sourceTokenIconUrl,
|
sourceTokenIconUrl,
|
||||||
} = renderableDataForUsedQuote;
|
} = renderableDataForUsedQuote;
|
||||||
|
|
||||||
const { feeInFiat, feeInEth } = getRenderableNetworkFeesForQuote(
|
const { feeInFiat, feeInEth } = getRenderableNetworkFeesForQuote({
|
||||||
usedGasLimit,
|
tradeGas: usedGasLimit,
|
||||||
approveGas,
|
approveGas,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
currentCurrency,
|
currentCurrency,
|
||||||
conversionRate,
|
conversionRate,
|
||||||
tradeValue,
|
tradeValue,
|
||||||
sourceTokenSymbol,
|
sourceSymbol: sourceTokenSymbol,
|
||||||
usedQuote.sourceAmount,
|
sourceAmount: usedQuote.sourceAmount,
|
||||||
);
|
chainId,
|
||||||
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
feeInFiat: maxFeeInFiat,
|
feeInFiat: maxFeeInFiat,
|
||||||
feeInEth: maxFeeInEth,
|
feeInEth: maxFeeInEth,
|
||||||
nonGasFee,
|
nonGasFee,
|
||||||
} = getRenderableNetworkFeesForQuote(
|
} = getRenderableNetworkFeesForQuote({
|
||||||
maxGasLimit,
|
tradeGas: maxGasLimit,
|
||||||
approveGas,
|
approveGas,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
currentCurrency,
|
currentCurrency,
|
||||||
conversionRate,
|
conversionRate,
|
||||||
tradeValue,
|
tradeValue,
|
||||||
sourceTokenSymbol,
|
sourceSymbol: sourceTokenSymbol,
|
||||||
usedQuote.sourceAmount,
|
sourceAmount: usedQuote.sourceAmount,
|
||||||
);
|
chainId,
|
||||||
|
});
|
||||||
|
|
||||||
const tokenCost = new BigNumber(usedQuote.sourceAmount);
|
const tokenCost = new BigNumber(usedQuote.sourceAmount);
|
||||||
const ethCost = new BigNumber(usedQuote.trade.value || 0, 10).plus(
|
const ethCost = new BigNumber(usedQuote.trade.value || 0, 10).plus(
|
||||||
@ -481,9 +487,9 @@ export default function ViewQuote() {
|
|||||||
<span key="swapApproveNeedMoreTokens-1" className="view-quote__bold">
|
<span key="swapApproveNeedMoreTokens-1" className="view-quote__bold">
|
||||||
{tokenBalanceNeeded || ethBalanceNeeded}
|
{tokenBalanceNeeded || ethBalanceNeeded}
|
||||||
</span>,
|
</span>,
|
||||||
tokenBalanceNeeded && !(sourceTokenSymbol === 'ETH')
|
tokenBalanceNeeded && !(sourceTokenSymbol === defaultSwapsToken.symbol)
|
||||||
? sourceTokenSymbol
|
? sourceTokenSymbol
|
||||||
: 'ETH',
|
: defaultSwapsToken.symbol,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Price difference warning
|
// Price difference warning
|
||||||
@ -643,7 +649,7 @@ export default function ViewQuote() {
|
|||||||
setSelectQuotePopoverShown(true);
|
setSelectQuotePopoverShown(true);
|
||||||
}}
|
}}
|
||||||
tokenConversionRate={
|
tokenConversionRate={
|
||||||
destinationTokenSymbol === 'ETH'
|
destinationTokenSymbol === defaultSwapsToken.symbol
|
||||||
? 1
|
? 1
|
||||||
: memoizedTokenConversionRates[destinationToken.address]
|
: memoizedTokenConversionRates[destinationToken.address]
|
||||||
}
|
}
|
||||||
@ -655,7 +661,7 @@ export default function ViewQuote() {
|
|||||||
setSubmitClicked(true);
|
setSubmitClicked(true);
|
||||||
if (!balanceError) {
|
if (!balanceError) {
|
||||||
dispatch(signAndSendTransactions(history, metaMetricsEvent));
|
dispatch(signAndSendTransactions(history, metaMetricsEvent));
|
||||||
} else if (destinationToken.symbol === 'ETH') {
|
} else if (destinationToken.symbol === defaultSwapsToken.symbol) {
|
||||||
history.push(DEFAULT_ROUTE);
|
history.push(DEFAULT_ROUTE);
|
||||||
} else {
|
} else {
|
||||||
history.push(`${ASSET_ROUTE}/${destinationToken.address}`);
|
history.push(`${ASSET_ROUTE}/${destinationToken.address}`);
|
||||||
|
@ -15,7 +15,10 @@ import {
|
|||||||
getValueFromWeiHex,
|
getValueFromWeiHex,
|
||||||
hexToDecimal,
|
hexToDecimal,
|
||||||
} from '../helpers/utils/conversions.util';
|
} from '../helpers/utils/conversions.util';
|
||||||
import { ETH_SWAPS_TOKEN_OBJECT } from '../../../shared/constants/swaps';
|
import {
|
||||||
|
SWAPS_CHAINID_DEFAULT_TOKEN_MAP,
|
||||||
|
ALLOWED_SWAPS_CHAIN_IDS,
|
||||||
|
} from '../../../shared/constants/swaps';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* One of the only remaining valid uses of selecting the network subkey of the
|
* One of the only remaining valid uses of selecting the network subkey of the
|
||||||
@ -431,22 +434,26 @@ export function getWeb3ShimUsageStateForOrigin(state, origin) {
|
|||||||
* minimal token units (according to its decimals).
|
* minimal token units (according to its decimals).
|
||||||
* `string` is the token balance in a readable format, ready for rendering.
|
* `string` is the token balance in a readable format, ready for rendering.
|
||||||
*
|
*
|
||||||
* Swaps treats ETH as a token, and we use the ETH_SWAPS_TOKEN_OBJECT constant
|
* Swaps treats the selected chain's currency as a token, and we use the token constants
|
||||||
* to set the standard properties for the token. The getSwapsEthToken selector
|
* in the SWAPS_CHAINID_DEFAULT_TOKEN_MAP to set the standard properties for
|
||||||
* extends that object with `balance` and `balance` values of the same type as
|
* the token. The getSwapsDefaultToken selector extends that object with
|
||||||
* in regular ERC-20 token objects, per the above description.
|
* `balance` and `string` values of the same type as in regular ERC-20 token
|
||||||
|
* objects, per the above description.
|
||||||
*
|
*
|
||||||
* @param {object} state - the redux state object
|
* @param {object} state - the redux state object
|
||||||
* @returns {SwapsEthToken} The token object representation of the currently
|
* @returns {SwapsEthToken} The token object representation of the currently
|
||||||
* selected account's ETH balance, as expected by the Swaps API.
|
* selected account's ETH balance, as expected by the Swaps API.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function getSwapsEthToken(state) {
|
export function getSwapsDefaultToken(state) {
|
||||||
const selectedAccount = getSelectedAccount(state);
|
const selectedAccount = getSelectedAccount(state);
|
||||||
const { balance } = selectedAccount;
|
const { balance } = selectedAccount;
|
||||||
|
const chainId = getCurrentChainId(state);
|
||||||
|
|
||||||
|
const defaultTokenObject = SWAPS_CHAINID_DEFAULT_TOKEN_MAP[chainId];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...ETH_SWAPS_TOKEN_OBJECT,
|
...defaultTokenObject,
|
||||||
balance: hexToDecimal(balance),
|
balance: hexToDecimal(balance),
|
||||||
string: getValueFromWeiHex({
|
string: getValueFromWeiHex({
|
||||||
value: balance,
|
value: balance,
|
||||||
@ -455,3 +462,8 @@ export function getSwapsEthToken(state) {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getIsSwapsChain(state) {
|
||||||
|
const chainId = getCurrentChainId(state);
|
||||||
|
return ALLOWED_SWAPS_CHAIN_IDS[chainId];
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user