2021-05-07 21:38:24 +02:00
import { strict as assert } from 'assert' ;
2021-02-04 19:15:23 +01:00
import sinon from 'sinon' ;
2020-10-06 20:28:38 +02:00
2023-01-24 15:10:36 +01:00
import { BigNumber } from '@ethersproject/bignumber' ;
2021-02-04 19:15:23 +01:00
import { mapValues } from 'lodash' ;
2023-01-24 15:10:36 +01:00
import BigNumberjs from 'bignumber.js' ;
2023-08-03 19:31:35 +02:00
import { NetworkType } from '@metamask/controller-utils' ;
NetworkController: Split `network` into `networkId` and `networkStatus` (#17556)
The `network` store of the network controller crams two types of data
into one place. It roughly tracks whether we have enough information to
make requests to the network and whether the network is capable of
receiving requests, but it also stores the ID of the network (as
obtained via `net_version`).
Generally we shouldn't be using the network ID for anything, as it has
been completely replaced by chain ID, which all custom RPC endpoints
have been required to support for over a year now. However, as the
network ID is used in various places within the extension codebase,
removing it entirely would be a non-trivial effort. So, minimally, this
commit splits `network` into two stores: `networkId` and
`networkStatus`. But it also expands the concept of network status.
Previously, the network was in one of two states: "loading" and
"not-loading". But now it can be in one of four states:
- `available`: The network is able to receive and respond to requests.
- `unavailable`: The network is not able to receive and respond to
requests for unknown reasons.
- `blocked`: The network is actively blocking requests based on the
user's geolocation. (This is specific to Infura.)
- `unknown`: We don't know whether the network can receive and respond
to requests, either because we haven't checked or we tried to check
and were unsuccessful.
This commit also changes how the network status is determined —
specifically, how many requests are used to determine that status, when
they occur, and whether they are awaited. Previously, the network
controller would make 2 to 3 requests during the course of running
`lookupNetwork`.
* First, if it was an Infura network, it would make a request for
`eth_blockNumber` to determine whether Infura was blocking requests or
not, then emit an appropriate event. This operation was not awaited.
* Then, regardless of the network, it would fetch the network ID via
`net_version`. This operation was awaited.
* Finally, regardless of the network, it would fetch the latest block
via `eth_getBlockByNumber`, then use the result to determine whether
the network supported EIP-1559. This operation was awaited.
Now:
* One fewer request is made, specifically `eth_blockNumber`, as we don't
need to make an extra request to determine whether Infura is blocking
requests; we can reuse `eth_getBlockByNumber`;
* All requests are awaited, which makes `lookupNetwork` run fully
in-band instead of partially out-of-band; and
* Both requests for `net_version` and `eth_getBlockByNumber` are
performed in parallel to make `lookupNetwork` run slightly faster.
2023-03-31 00:49:12 +02:00
import {
CHAIN _IDS ,
NETWORK _IDS ,
NetworkStatus ,
} from '../../../shared/constants/network' ;
2021-03-16 22:00:08 +01:00
import { ETH _SWAPS _TOKEN _OBJECT } from '../../../shared/constants/swaps' ;
import { createTestProviderTools } from '../../../test/stub/provider' ;
2021-06-10 21:27:03 +02:00
import { SECOND } from '../../../shared/constants/time' ;
2023-01-27 19:28:03 +01:00
import { GasEstimateTypes } from '../../../shared/constants/gas' ;
2022-05-09 18:48:14 +02:00
import {
FALLBACK _SMART _TRANSACTIONS _REFRESH _TIME ,
FALLBACK _SMART _TRANSACTIONS _MAX _FEE _MULTIPLIER ,
} from '../../../shared/constants/smartTransactions' ;
2021-03-16 22:00:08 +01:00
import SwapsController , { utils } from './swaps' ;
2020-10-06 20:28:38 +02:00
const MOCK _FETCH _PARAMS = {
slippage : 3 ,
sourceToken : '0x6b175474e89094c44da98b954eedeac495271d0f' ,
sourceDecimals : 18 ,
destinationToken : '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' ,
value : '1000000000000000000' ,
fromAddress : '0x7F18BB4Dd92CF2404C54CBa1A9BE4A1153bdb078' ,
exchangeList : 'zeroExV1' ,
2021-02-04 19:15:23 +01:00
} ;
2020-10-06 20:28:38 +02:00
2021-02-04 19:15:23 +01:00
const TEST _AGG _ID _1 = 'TEST_AGG_1' ;
const TEST _AGG _ID _2 = 'TEST_AGG_2' ;
const TEST _AGG _ID _3 = 'TEST_AGG_3' ;
const TEST _AGG _ID _4 = 'TEST_AGG_4' ;
const TEST _AGG _ID _5 = 'TEST_AGG_5' ;
const TEST _AGG _ID _6 = 'TEST_AGG_6' ;
const TEST _AGG _ID _BEST = 'TEST_AGG_BEST' ;
const TEST _AGG _ID _APPROVAL = 'TEST_AGG_APPROVAL' ;
2020-10-19 23:52:47 +02:00
2021-06-10 21:27:03 +02:00
const POLLING _TIMEOUT = SECOND * 1000 ;
2020-10-07 21:00:17 +02:00
const MOCK _APPROVAL _NEEDED = {
2022-07-31 20:26:40 +02:00
data : '0x095ea7b300000000000000000000000095e6f48254609a6ee006f7d493c8e5fb97094cef0000000000000000000000000000000000000000004a817c7ffffffdabf41c00' ,
2020-11-03 00:41:28 +01:00
to : '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' ,
amount : '0' ,
from : '0x2369267687A84ac7B494daE2f1542C40E37f4455' ,
gas : '12' ,
gasPrice : '34' ,
2021-02-04 19:15:23 +01:00
} ;
2020-10-19 23:52:47 +02:00
2020-10-07 21:00:17 +02:00
const MOCK _QUOTES _APPROVAL _REQUIRED = {
2020-10-19 23:52:47 +02:00
[ TEST _AGG _ID _APPROVAL ] : {
2020-10-07 21:00:17 +02:00
trade : {
data : '0x00' ,
from : '0x7F18BB4Dd92CF2404C54CBa1A9BE4A1153bdb078' ,
value : '0x17647444f166000' ,
gas : '0xe09c0' ,
gasPrice : undefined ,
2020-10-12 19:20:37 +02:00
to : '0x881d40237659c251811cec9c364ef91dc08d300c' ,
2020-10-07 21:00:17 +02:00
} ,
sourceAmount : '1000000000000000000000000000000000000' ,
destinationAmount : '396493201125465' ,
error : null ,
sourceToken : '0x6b175474e89094c44da98b954eedeac495271d0f' ,
destinationToken : '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' ,
maxGas : 920000 ,
averageGas : 312510 ,
estimatedRefund : 343090 ,
fetchTime : 559 ,
2020-10-19 23:52:47 +02:00
aggregator : TEST _AGG _ID _APPROVAL ,
2020-10-07 21:00:17 +02:00
aggType : 'AGG' ,
slippage : 3 ,
approvalNeeded : MOCK _APPROVAL _NEEDED ,
2020-11-09 18:09:38 +01:00
fee : 1 ,
2020-10-07 21:00:17 +02:00
} ,
2021-02-04 19:15:23 +01:00
} ;
2020-10-19 23:52:47 +02:00
2020-10-06 20:28:38 +02:00
const MOCK _FETCH _METADATA = {
destinationTokenInfo : {
symbol : 'FOO' ,
decimals : 18 ,
} ,
2022-09-14 16:55:31 +02:00
chainId : CHAIN _IDS . MAINNET ,
2021-02-04 19:15:23 +01:00
} ;
2020-10-06 20:28:38 +02:00
2021-11-22 13:04:31 +01:00
const MOCK _TOKEN _RATES _STORE = ( ) => ( {
2020-11-09 18:09:38 +01:00
contractExchangeRates : {
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' : 2 ,
'0x1111111111111111111111111111111111111111' : 0.1 ,
} ,
2021-11-22 13:04:31 +01:00
} ) ;
2020-10-06 20:28:38 +02:00
2021-02-04 19:15:23 +01:00
const MOCK _GET _PROVIDER _CONFIG = ( ) => ( { type : 'FAKE_NETWORK' } ) ;
2020-10-06 20:28:38 +02:00
const MOCK _GET _BUFFERED _GAS _LIMIT = async ( ) => ( {
gasLimit : 2000000 ,
simulationFails : undefined ,
2021-02-04 19:15:23 +01:00
} ) ;
2020-10-06 20:28:38 +02:00
2020-11-03 00:41:28 +01:00
function getMockNetworkController ( ) {
2020-10-28 19:47:32 +01:00
return {
2023-06-22 20:46:09 +02:00
state : {
networkId : NETWORK _IDS . GOERLI ,
2023-08-03 19:31:35 +02:00
selectedNetworkClientId : NetworkType . goerli ,
networksMetadata : {
[ NetworkType . goerli ] : {
EIPS : { } ,
status : NetworkStatus . Available ,
} ,
} ,
2020-10-28 19:47:32 +01:00
} ,
2021-02-04 19:15:23 +01:00
} ;
2020-10-28 19:47:32 +01:00
}
2020-10-06 20:28:38 +02:00
const EMPTY _INIT _STATE = {
swapsState : {
quotes : { } ,
2021-09-15 15:13:18 +02:00
quotesPollingLimitEnabled : false ,
2020-10-06 20:28:38 +02:00
fetchParams : null ,
tokens : null ,
tradeTxId : null ,
approveTxId : null ,
quotesLastFetched : null ,
2021-07-30 13:35:30 +02:00
customMaxFeePerGas : null ,
2020-10-06 20:28:38 +02:00
customMaxGas : '' ,
2021-07-30 13:35:30 +02:00
customMaxPriorityFeePerGas : null ,
2020-10-06 20:28:38 +02:00
customGasPrice : null ,
selectedAggId : null ,
customApproveTxData : '' ,
errorKey : '' ,
topAggId : null ,
routeState : '' ,
2021-04-14 09:16:27 +02:00
swapsFeatureIsLive : true ,
2022-02-18 17:48:38 +01:00
swapsFeatureFlags : { } ,
2020-12-15 21:24:22 +01:00
swapsQuoteRefreshTime : 60000 ,
2021-09-15 15:13:18 +02:00
swapsQuotePrefetchingRefreshTime : 60000 ,
2022-05-09 18:48:14 +02:00
swapsStxBatchStatusRefreshTime : FALLBACK _SMART _TRANSACTIONS _REFRESH _TIME ,
2022-07-31 20:26:40 +02:00
swapsStxGetTransactionsRefreshTime :
FALLBACK _SMART _TRANSACTIONS _REFRESH _TIME ,
2022-05-09 18:48:14 +02:00
swapsStxMaxFeeMultiplier : FALLBACK _SMART _TRANSACTIONS _MAX _FEE _MULTIPLIER ,
2021-08-06 21:31:30 +02:00
swapsUserFeeLevel : '' ,
2021-10-28 08:53:26 +02:00
saveFetchedQuotes : false ,
2020-10-06 20:28:38 +02:00
} ,
2021-02-04 19:15:23 +01:00
} ;
2020-10-06 20:28:38 +02:00
2021-02-04 19:15:23 +01:00
const sandbox = sinon . createSandbox ( ) ;
const fetchTradesInfoStub = sandbox . stub ( ) ;
2021-03-18 11:20:06 +01:00
const getCurrentChainIdStub = sandbox . stub ( ) ;
2022-09-14 16:55:31 +02:00
getCurrentChainIdStub . returns ( CHAIN _IDS . MAINNET ) ;
2021-07-30 13:35:30 +02:00
const getEIP1559GasFeeEstimatesStub = sandbox . stub ( ( ) => {
return {
gasFeeEstimates : {
high : '150' ,
} ,
2023-01-27 19:28:03 +01:00
gasEstimateType : GasEstimateTypes . legacy ,
2021-07-30 13:35:30 +02:00
} ;
} ) ;
2020-10-06 20:28:38 +02:00
describe ( 'SwapsController' , function ( ) {
2021-02-04 19:15:23 +01:00
let provider ;
2020-10-06 20:28:38 +02:00
2023-04-18 16:45:18 +02:00
const getSwapsController = ( _provider = provider ) => {
2020-10-06 20:28:38 +02:00
return new SwapsController ( {
getBufferedGasLimit : MOCK _GET _BUFFERED _GAS _LIMIT ,
2020-10-28 19:47:32 +01:00
networkController : getMockNetworkController ( ) ,
2023-04-19 22:45:26 +02:00
onNetworkStateChange : sinon . stub ( ) ,
2023-04-18 16:45:18 +02:00
provider : _provider ,
2020-10-06 20:28:38 +02:00
getProviderConfig : MOCK _GET _PROVIDER _CONFIG ,
2021-11-22 13:04:31 +01:00
getTokenRatesState : MOCK _TOKEN _RATES _STORE ,
2020-10-06 20:28:38 +02:00
fetchTradesInfo : fetchTradesInfoStub ,
2021-03-18 11:20:06 +01:00
getCurrentChainId : getCurrentChainIdStub ,
2021-07-30 13:35:30 +02:00
getEIP1559GasFeeEstimates : getEIP1559GasFeeEstimatesStub ,
2021-02-04 19:15:23 +01:00
} ) ;
} ;
2020-10-06 20:28:38 +02:00
before ( function ( ) {
const providerResultStub = {
// 1 gwei
eth _gasPrice : '0x0de0b6b3a7640000' ,
// by default, all accounts are external accounts (not contracts)
eth _getCode : '0x' ,
2021-02-04 19:15:23 +01:00
} ;
2020-11-03 00:41:28 +01:00
provider = createTestProviderTools ( {
scaffold : providerResultStub ,
networkId : 1 ,
chainId : 1 ,
2021-02-04 19:15:23 +01:00
} ) . provider ;
} ) ;
2020-10-06 20:28:38 +02:00
afterEach ( function ( ) {
2021-02-04 19:15:23 +01:00
sandbox . restore ( ) ;
} ) ;
2020-10-06 20:28:38 +02:00
describe ( 'constructor' , function ( ) {
it ( 'should setup correctly' , function ( ) {
2021-02-04 19:15:23 +01:00
const swapsController = getSwapsController ( ) ;
assert . deepStrictEqual (
swapsController . store . getState ( ) ,
EMPTY _INIT _STATE ,
) ;
2020-10-06 20:28:38 +02:00
assert . deepStrictEqual (
swapsController . getBufferedGasLimit ,
MOCK _GET _BUFFERED _GAS _LIMIT ,
2021-02-04 19:15:23 +01:00
) ;
assert . strictEqual ( swapsController . pollCount , 0 ) ;
2020-10-06 20:28:38 +02:00
assert . deepStrictEqual (
swapsController . getProviderConfig ,
MOCK _GET _PROVIDER _CONFIG ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2020-10-28 19:47:32 +01:00
it ( 'should replace ethers instance when network changes' , function ( ) {
2021-02-04 19:15:23 +01:00
const networkController = getMockNetworkController ( ) ;
2023-06-22 20:46:09 +02:00
let networkStateChangeListener ;
const onNetworkStateChange = ( listener ) => {
networkStateChangeListener = listener ;
} ;
2020-10-28 19:47:32 +01:00
const swapsController = new SwapsController ( {
getBufferedGasLimit : MOCK _GET _BUFFERED _GAS _LIMIT ,
networkController ,
2023-04-19 22:45:26 +02:00
onNetworkStateChange ,
2020-10-28 19:47:32 +01:00
provider ,
getProviderConfig : MOCK _GET _PROVIDER _CONFIG ,
2021-11-22 13:04:31 +01:00
getTokenRatesState : MOCK _TOKEN _RATES _STORE ,
2020-10-28 19:47:32 +01:00
fetchTradesInfo : fetchTradesInfoStub ,
2021-03-18 11:20:06 +01:00
getCurrentChainId : getCurrentChainIdStub ,
2021-02-04 19:15:23 +01:00
} ) ;
const currentEthersInstance = swapsController . ethersProvider ;
2020-10-28 19:47:32 +01:00
2023-06-22 20:46:09 +02:00
networkController . state = {
NetworkController: Split `network` into `networkId` and `networkStatus` (#17556)
The `network` store of the network controller crams two types of data
into one place. It roughly tracks whether we have enough information to
make requests to the network and whether the network is capable of
receiving requests, but it also stores the ID of the network (as
obtained via `net_version`).
Generally we shouldn't be using the network ID for anything, as it has
been completely replaced by chain ID, which all custom RPC endpoints
have been required to support for over a year now. However, as the
network ID is used in various places within the extension codebase,
removing it entirely would be a non-trivial effort. So, minimally, this
commit splits `network` into two stores: `networkId` and
`networkStatus`. But it also expands the concept of network status.
Previously, the network was in one of two states: "loading" and
"not-loading". But now it can be in one of four states:
- `available`: The network is able to receive and respond to requests.
- `unavailable`: The network is not able to receive and respond to
requests for unknown reasons.
- `blocked`: The network is actively blocking requests based on the
user's geolocation. (This is specific to Infura.)
- `unknown`: We don't know whether the network can receive and respond
to requests, either because we haven't checked or we tried to check
and were unsuccessful.
This commit also changes how the network status is determined —
specifically, how many requests are used to determine that status, when
they occur, and whether they are awaited. Previously, the network
controller would make 2 to 3 requests during the course of running
`lookupNetwork`.
* First, if it was an Infura network, it would make a request for
`eth_blockNumber` to determine whether Infura was blocking requests or
not, then emit an appropriate event. This operation was not awaited.
* Then, regardless of the network, it would fetch the network ID via
`net_version`. This operation was awaited.
* Finally, regardless of the network, it would fetch the latest block
via `eth_getBlockByNumber`, then use the result to determine whether
the network supported EIP-1559. This operation was awaited.
Now:
* One fewer request is made, specifically `eth_blockNumber`, as we don't
need to make an extra request to determine whether Infura is blocking
requests; we can reuse `eth_getBlockByNumber`;
* All requests are awaited, which makes `lookupNetwork` run fully
in-band instead of partially out-of-band; and
* Both requests for `net_version` and `eth_getBlockByNumber` are
performed in parallel to make `lookupNetwork` run slightly faster.
2023-03-31 00:49:12 +02:00
networkId : NETWORK _IDS . MAINNET ,
2023-08-03 19:31:35 +02:00
selectedNetworkClientId : NetworkType . mainnet ,
networksMetadata : {
[ NetworkType . mainnet ] : {
EIPS : { } ,
status : NetworkStatus . Available ,
} ,
} ,
2023-06-22 20:46:09 +02:00
} ;
networkStateChangeListener ( ) ;
2020-10-28 19:47:32 +01:00
2021-02-04 19:15:23 +01:00
const newEthersInstance = swapsController . ethersProvider ;
2020-10-28 19:47:32 +01:00
assert . notStrictEqual (
currentEthersInstance ,
newEthersInstance ,
'Ethers provider should be replaced' ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2020-10-28 19:47:32 +01:00
it ( 'should not replace ethers instance when network changes to loading' , function ( ) {
2021-02-04 19:15:23 +01:00
const networkController = getMockNetworkController ( ) ;
2023-06-22 20:46:09 +02:00
let networkStateChangeListener ;
const onNetworkStateChange = ( listener ) => {
networkStateChangeListener = listener ;
} ;
2020-10-28 19:47:32 +01:00
const swapsController = new SwapsController ( {
getBufferedGasLimit : MOCK _GET _BUFFERED _GAS _LIMIT ,
networkController ,
2023-04-19 22:45:26 +02:00
onNetworkStateChange ,
2020-10-28 19:47:32 +01:00
provider ,
getProviderConfig : MOCK _GET _PROVIDER _CONFIG ,
2021-11-22 13:04:31 +01:00
getTokenRatesState : MOCK _TOKEN _RATES _STORE ,
2020-10-28 19:47:32 +01:00
fetchTradesInfo : fetchTradesInfoStub ,
2021-03-18 11:20:06 +01:00
getCurrentChainId : getCurrentChainIdStub ,
2021-02-04 19:15:23 +01:00
} ) ;
const currentEthersInstance = swapsController . ethersProvider ;
2020-10-28 19:47:32 +01:00
2023-06-22 20:46:09 +02:00
networkController . state = {
NetworkController: Split `network` into `networkId` and `networkStatus` (#17556)
The `network` store of the network controller crams two types of data
into one place. It roughly tracks whether we have enough information to
make requests to the network and whether the network is capable of
receiving requests, but it also stores the ID of the network (as
obtained via `net_version`).
Generally we shouldn't be using the network ID for anything, as it has
been completely replaced by chain ID, which all custom RPC endpoints
have been required to support for over a year now. However, as the
network ID is used in various places within the extension codebase,
removing it entirely would be a non-trivial effort. So, minimally, this
commit splits `network` into two stores: `networkId` and
`networkStatus`. But it also expands the concept of network status.
Previously, the network was in one of two states: "loading" and
"not-loading". But now it can be in one of four states:
- `available`: The network is able to receive and respond to requests.
- `unavailable`: The network is not able to receive and respond to
requests for unknown reasons.
- `blocked`: The network is actively blocking requests based on the
user's geolocation. (This is specific to Infura.)
- `unknown`: We don't know whether the network can receive and respond
to requests, either because we haven't checked or we tried to check
and were unsuccessful.
This commit also changes how the network status is determined —
specifically, how many requests are used to determine that status, when
they occur, and whether they are awaited. Previously, the network
controller would make 2 to 3 requests during the course of running
`lookupNetwork`.
* First, if it was an Infura network, it would make a request for
`eth_blockNumber` to determine whether Infura was blocking requests or
not, then emit an appropriate event. This operation was not awaited.
* Then, regardless of the network, it would fetch the network ID via
`net_version`. This operation was awaited.
* Finally, regardless of the network, it would fetch the latest block
via `eth_getBlockByNumber`, then use the result to determine whether
the network supported EIP-1559. This operation was awaited.
Now:
* One fewer request is made, specifically `eth_blockNumber`, as we don't
need to make an extra request to determine whether Infura is blocking
requests; we can reuse `eth_getBlockByNumber`;
* All requests are awaited, which makes `lookupNetwork` run fully
in-band instead of partially out-of-band; and
* Both requests for `net_version` and `eth_getBlockByNumber` are
performed in parallel to make `lookupNetwork` run slightly faster.
2023-03-31 00:49:12 +02:00
networkId : null ,
2023-08-03 19:31:35 +02:00
selectedNetworkClientId : NetworkType . goerli ,
networksMetadata : {
[ NetworkType . goerli ] : {
EIPS : { } ,
status : NetworkStatus . Unknown ,
} ,
} ,
2023-06-22 20:46:09 +02:00
} ;
networkStateChangeListener ( ) ;
2020-10-28 19:47:32 +01:00
2021-02-04 19:15:23 +01:00
const newEthersInstance = swapsController . ethersProvider ;
2020-10-28 19:47:32 +01:00
assert . strictEqual (
currentEthersInstance ,
newEthersInstance ,
'Ethers provider should not be replaced' ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2020-10-28 19:47:32 +01:00
it ( 'should not replace ethers instance when network changes to the same network' , function ( ) {
2021-02-04 19:15:23 +01:00
const networkController = getMockNetworkController ( ) ;
2023-06-22 20:46:09 +02:00
let networkStateChangeListener ;
const onNetworkStateChange = ( listener ) => {
networkStateChangeListener = listener ;
} ;
2020-10-28 19:47:32 +01:00
const swapsController = new SwapsController ( {
getBufferedGasLimit : MOCK _GET _BUFFERED _GAS _LIMIT ,
networkController ,
2023-04-19 22:45:26 +02:00
onNetworkStateChange ,
2020-10-28 19:47:32 +01:00
provider ,
getProviderConfig : MOCK _GET _PROVIDER _CONFIG ,
2021-11-22 13:04:31 +01:00
getTokenRatesState : MOCK _TOKEN _RATES _STORE ,
2020-10-28 19:47:32 +01:00
fetchTradesInfo : fetchTradesInfoStub ,
2021-03-18 11:20:06 +01:00
getCurrentChainId : getCurrentChainIdStub ,
2021-02-04 19:15:23 +01:00
} ) ;
const currentEthersInstance = swapsController . ethersProvider ;
2020-10-28 19:47:32 +01:00
2023-06-22 20:46:09 +02:00
networkController . state = {
NetworkController: Split `network` into `networkId` and `networkStatus` (#17556)
The `network` store of the network controller crams two types of data
into one place. It roughly tracks whether we have enough information to
make requests to the network and whether the network is capable of
receiving requests, but it also stores the ID of the network (as
obtained via `net_version`).
Generally we shouldn't be using the network ID for anything, as it has
been completely replaced by chain ID, which all custom RPC endpoints
have been required to support for over a year now. However, as the
network ID is used in various places within the extension codebase,
removing it entirely would be a non-trivial effort. So, minimally, this
commit splits `network` into two stores: `networkId` and
`networkStatus`. But it also expands the concept of network status.
Previously, the network was in one of two states: "loading" and
"not-loading". But now it can be in one of four states:
- `available`: The network is able to receive and respond to requests.
- `unavailable`: The network is not able to receive and respond to
requests for unknown reasons.
- `blocked`: The network is actively blocking requests based on the
user's geolocation. (This is specific to Infura.)
- `unknown`: We don't know whether the network can receive and respond
to requests, either because we haven't checked or we tried to check
and were unsuccessful.
This commit also changes how the network status is determined —
specifically, how many requests are used to determine that status, when
they occur, and whether they are awaited. Previously, the network
controller would make 2 to 3 requests during the course of running
`lookupNetwork`.
* First, if it was an Infura network, it would make a request for
`eth_blockNumber` to determine whether Infura was blocking requests or
not, then emit an appropriate event. This operation was not awaited.
* Then, regardless of the network, it would fetch the network ID via
`net_version`. This operation was awaited.
* Finally, regardless of the network, it would fetch the latest block
via `eth_getBlockByNumber`, then use the result to determine whether
the network supported EIP-1559. This operation was awaited.
Now:
* One fewer request is made, specifically `eth_blockNumber`, as we don't
need to make an extra request to determine whether Infura is blocking
requests; we can reuse `eth_getBlockByNumber`;
* All requests are awaited, which makes `lookupNetwork` run fully
in-band instead of partially out-of-band; and
* Both requests for `net_version` and `eth_getBlockByNumber` are
performed in parallel to make `lookupNetwork` run slightly faster.
2023-03-31 00:49:12 +02:00
networkId : NETWORK _IDS . GOERLI ,
2023-08-03 19:31:35 +02:00
selectedNetworkClientId : NetworkType . goerli ,
networksMetadata : {
[ NetworkType . goerli ] : {
EIPS : { } ,
status : NetworkStatus . Available ,
} ,
} ,
2023-06-22 20:46:09 +02:00
} ;
networkStateChangeListener ( ) ;
2020-10-28 19:47:32 +01:00
2021-02-04 19:15:23 +01:00
const newEthersInstance = swapsController . ethersProvider ;
2020-10-28 19:47:32 +01:00
assert . strictEqual (
currentEthersInstance ,
newEthersInstance ,
'Ethers provider should not be replaced' ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
} ) ;
2020-10-06 20:28:38 +02:00
describe ( 'API' , function ( ) {
2021-02-04 19:15:23 +01:00
let swapsController ;
2020-10-06 20:28:38 +02:00
beforeEach ( function ( ) {
2021-02-04 19:15:23 +01:00
swapsController = getSwapsController ( ) ;
} ) ;
2020-10-06 20:28:38 +02:00
describe ( 'setters' , function ( ) {
it ( 'should set selected quote agg id' , function ( ) {
2021-02-04 19:15:23 +01:00
const selectedAggId = 'test' ;
swapsController . setSelectedQuoteAggId ( selectedAggId ) ;
2020-10-06 20:28:38 +02:00
assert . deepStrictEqual (
swapsController . store . getState ( ) . swapsState . selectedAggId ,
selectedAggId ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2020-10-06 20:28:38 +02:00
it ( 'should set swaps tokens' , function ( ) {
2021-02-04 19:15:23 +01:00
const tokens = [ ] ;
swapsController . setSwapsTokens ( tokens ) ;
2020-10-06 20:28:38 +02:00
assert . deepStrictEqual (
swapsController . store . getState ( ) . swapsState . tokens ,
tokens ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2020-10-06 20:28:38 +02:00
it ( 'should set trade tx id' , function ( ) {
2021-02-04 19:15:23 +01:00
const tradeTxId = 'test' ;
swapsController . setTradeTxId ( tradeTxId ) ;
2020-10-06 20:28:38 +02:00
assert . strictEqual (
swapsController . store . getState ( ) . swapsState . tradeTxId ,
tradeTxId ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2020-10-06 20:28:38 +02:00
it ( 'should set swaps tx gas price' , function ( ) {
2021-02-04 19:15:23 +01:00
const gasPrice = 1 ;
swapsController . setSwapsTxGasPrice ( gasPrice ) ;
2020-10-06 20:28:38 +02:00
assert . deepStrictEqual (
swapsController . store . getState ( ) . swapsState . customGasPrice ,
gasPrice ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2020-10-06 20:28:38 +02:00
it ( 'should set swaps tx gas limit' , function ( ) {
2021-02-04 19:15:23 +01:00
const gasLimit = '1' ;
swapsController . setSwapsTxGasLimit ( gasLimit ) ;
2020-10-06 20:28:38 +02:00
assert . deepStrictEqual (
swapsController . store . getState ( ) . swapsState . customMaxGas ,
gasLimit ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2020-10-06 20:28:38 +02:00
it ( 'should set background swap route state' , function ( ) {
2021-02-04 19:15:23 +01:00
const routeState = 'test' ;
swapsController . setBackgroundSwapRouteState ( routeState ) ;
2020-10-06 20:28:38 +02:00
assert . deepStrictEqual (
swapsController . store . getState ( ) . swapsState . routeState ,
routeState ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2020-10-06 20:28:38 +02:00
it ( 'should set swaps error key' , function ( ) {
2021-02-04 19:15:23 +01:00
const errorKey = 'test' ;
swapsController . setSwapsErrorKey ( errorKey ) ;
2020-10-06 20:28:38 +02:00
assert . deepStrictEqual (
swapsController . store . getState ( ) . swapsState . errorKey ,
errorKey ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2020-10-06 20:28:38 +02:00
it ( 'should set initial gas estimate' , async function ( ) {
2021-02-04 19:15:23 +01:00
const initialAggId = TEST _AGG _ID _1 ;
const baseGasEstimate = 10 ;
const { maxGas , estimatedRefund } = getMockQuotes ( ) [ TEST _AGG _ID _1 ] ;
2020-10-06 20:28:38 +02:00
2021-02-04 19:15:23 +01:00
const { swapsState } = swapsController . store . getState ( ) ;
2020-10-06 20:28:38 +02:00
// Set mock quotes in order to have data for the test agg
swapsController . store . updateState ( {
2020-10-19 23:52:47 +02:00
swapsState : { ... swapsState , quotes : getMockQuotes ( ) } ,
2021-02-04 19:15:23 +01:00
} ) ;
2020-10-06 20:28:38 +02:00
await swapsController . setInitialGasEstimate (
initialAggId ,
baseGasEstimate ,
2021-02-04 19:15:23 +01:00
) ;
2020-10-06 20:28:38 +02:00
2022-07-31 20:26:40 +02:00
const { gasLimit : bufferedGasLimit } =
await swapsController . getBufferedGasLimit ( ) ;
const { gasEstimate , gasEstimateWithRefund } =
swapsController . store . getState ( ) . swapsState . quotes [ initialAggId ] ;
2021-02-04 19:15:23 +01:00
assert . strictEqual ( gasEstimate , bufferedGasLimit ) ;
2020-10-06 20:28:38 +02:00
assert . strictEqual (
gasEstimateWithRefund ,
2023-01-24 15:10:36 +01:00
` 0x ${ new BigNumberjs ( maxGas , 10 )
2022-02-08 19:14:50 +01:00
. minus ( estimatedRefund , 10 )
. toString ( 16 ) } ` ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2020-10-06 20:28:38 +02:00
it ( 'should set custom approve tx data' , function ( ) {
2021-02-04 19:15:23 +01:00
const data = 'test' ;
swapsController . setCustomApproveTxData ( data ) ;
2020-10-06 20:28:38 +02:00
assert . deepStrictEqual (
swapsController . store . getState ( ) . swapsState . customApproveTxData ,
data ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
} ) ;
2020-10-06 20:28:38 +02:00
2020-10-19 23:52:47 +02:00
describe ( '_findTopQuoteAndCalculateSavings' , function ( ) {
2020-11-09 18:09:38 +01:00
beforeEach ( function ( ) {
2021-02-04 19:15:23 +01:00
const { swapsState } = swapsController . store . getState ( ) ;
2020-11-09 18:09:38 +01:00
swapsController . store . updateState ( {
swapsState : { ... swapsState , customGasPrice : '0x174876e800' } ,
2021-02-04 19:15:23 +01:00
} ) ;
} ) ;
2020-11-09 18:09:38 +01:00
2020-10-19 23:52:47 +02:00
it ( 'returns empty object if passed undefined or empty object' , async function ( ) {
assert . deepStrictEqual (
await swapsController . _findTopQuoteAndCalculateSavings ( ) ,
{ } ,
2021-02-04 19:15:23 +01:00
) ;
2020-10-19 23:52:47 +02:00
assert . deepStrictEqual (
await swapsController . _findTopQuoteAndCalculateSavings ( { } ) ,
{ } ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2020-11-09 18:09:38 +01:00
it ( 'returns the top aggId and quotes with savings and fee values if passed necessary data and an even number of quotes' , async function ( ) {
2022-07-31 20:26:40 +02:00
const [ topAggId , resultQuotes ] =
await swapsController . _findTopQuoteAndCalculateSavings (
getTopQuoteAndSavingsMockQuotes ( ) ,
) ;
2021-02-04 19:15:23 +01:00
assert . equal ( topAggId , TEST _AGG _ID _1 ) ;
2020-11-09 18:09:38 +01:00
assert . deepStrictEqual (
resultQuotes ,
getTopQuoteAndSavingsBaseExpectedResults ( ) ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2020-11-09 18:09:38 +01:00
it ( 'returns the top aggId and quotes with savings and fee values if passed necessary data and an odd number of quotes' , async function ( ) {
2021-02-04 19:15:23 +01:00
const testInput = getTopQuoteAndSavingsMockQuotes ( ) ;
delete testInput [ TEST _AGG _ID _6 ] ;
const expectedResultQuotes = getTopQuoteAndSavingsBaseExpectedResults ( ) ;
delete expectedResultQuotes [ TEST _AGG _ID _6 ] ;
2020-11-09 18:09:38 +01:00
expectedResultQuotes [ TEST _AGG _ID _1 ] . savings = {
2022-09-26 18:30:17 +02:00
total : '0.0092' ,
2020-11-09 18:09:38 +01:00
performance : '0.0297' ,
2022-09-26 18:30:17 +02:00
fee : '0' ,
2020-11-09 18:09:38 +01:00
metaMaskFee : '0.0205' ,
medianMetaMaskFee : '0.0202' ,
2021-02-04 19:15:23 +01:00
} ;
2020-11-09 18:09:38 +01:00
2022-07-31 20:26:40 +02:00
const [ topAggId , resultQuotes ] =
await swapsController . _findTopQuoteAndCalculateSavings ( testInput ) ;
2021-02-04 19:15:23 +01:00
assert . equal ( topAggId , TEST _AGG _ID _1 ) ;
assert . deepStrictEqual ( resultQuotes , expectedResultQuotes ) ;
} ) ;
2020-11-09 18:09:38 +01:00
it ( 'returns the top aggId, without best quote flagged, and quotes with fee values if passed necessary data but no custom convert rate exists' , async function ( ) {
const testInput = mapValues (
getTopQuoteAndSavingsMockQuotes ( ) ,
( quote ) => ( {
... quote ,
destinationToken : '0xnoConversionRateExists' ,
} ) ,
2021-02-04 19:15:23 +01:00
) ;
2020-11-09 18:09:38 +01:00
const expectedResultQuotes = {
[ TEST _AGG _ID _1 ] : {
... testInput [ TEST _AGG _ID _1 ] ,
2022-09-26 18:30:17 +02:00
ethFee : '0.25' ,
2020-11-09 18:09:38 +01:00
} ,
[ TEST _AGG _ID _2 ] : {
... testInput [ TEST _AGG _ID _2 ] ,
2022-09-26 18:30:17 +02:00
ethFee : '0.25' ,
2020-11-09 18:09:38 +01:00
} ,
[ TEST _AGG _ID _3 ] : {
... testInput [ TEST _AGG _ID _3 ] ,
2022-09-26 18:30:17 +02:00
ethFee : '0.25' ,
2020-11-09 18:09:38 +01:00
} ,
[ TEST _AGG _ID _4 ] : {
... testInput [ TEST _AGG _ID _4 ] ,
2022-09-26 18:30:17 +02:00
ethFee : '0.25' ,
2020-11-09 18:09:38 +01:00
} ,
[ TEST _AGG _ID _5 ] : {
... testInput [ TEST _AGG _ID _5 ] ,
2022-09-26 18:30:17 +02:00
ethFee : '0.25' ,
2020-11-09 18:09:38 +01:00
} ,
[ TEST _AGG _ID _6 ] : {
... testInput [ TEST _AGG _ID _6 ] ,
2022-09-26 18:30:17 +02:00
ethFee : '0.25' ,
2020-11-09 18:09:38 +01:00
} ,
2021-02-04 19:15:23 +01:00
} ;
2020-11-09 18:09:38 +01:00
2022-07-31 20:26:40 +02:00
const [ topAggId , resultQuotes ] =
await swapsController . _findTopQuoteAndCalculateSavings ( testInput ) ;
2021-02-04 19:15:23 +01:00
assert . equal ( topAggId , TEST _AGG _ID _1 ) ;
assert . deepStrictEqual ( resultQuotes , expectedResultQuotes ) ;
} ) ;
2020-11-09 18:09:38 +01:00
it ( 'returns the top aggId and quotes with savings and fee values if passed necessary data and the source token is ETH' , async function ( ) {
const testInput = mapValues (
getTopQuoteAndSavingsMockQuotes ( ) ,
( quote ) => ( {
... quote ,
2021-03-09 16:59:35 +01:00
sourceToken : ETH _SWAPS _TOKEN _OBJECT . address ,
2020-11-09 18:09:38 +01:00
destinationToken : '0x1111111111111111111111111111111111111111' ,
trade : { value : '0x8ac7230489e80000' } ,
} ) ,
2021-02-04 19:15:23 +01:00
) ;
2022-07-31 20:26:40 +02:00
const baseExpectedResultQuotes =
getTopQuoteAndSavingsBaseExpectedResults ( ) ;
2020-11-09 18:09:38 +01:00
const expectedResultQuotes = {
[ TEST _AGG _ID _1 ] : {
... baseExpectedResultQuotes [ TEST _AGG _ID _1 ] ,
2021-03-09 16:59:35 +01:00
sourceToken : ETH _SWAPS _TOKEN _OBJECT . address ,
2020-11-09 18:09:38 +01:00
destinationToken : '0x1111111111111111111111111111111111111111' ,
trade : { value : '0x8ac7230489e80000' } ,
2022-09-26 18:30:17 +02:00
overallValueOfQuote : '1.7795' ,
2020-11-09 18:09:38 +01:00
} ,
[ TEST _AGG _ID _2 ] : {
... baseExpectedResultQuotes [ TEST _AGG _ID _2 ] ,
2021-03-09 16:59:35 +01:00
sourceToken : ETH _SWAPS _TOKEN _OBJECT . address ,
2020-11-09 18:09:38 +01:00
destinationToken : '0x1111111111111111111111111111111111111111' ,
trade : { value : '0x8ac7230489e80000' } ,
2022-09-26 18:30:17 +02:00
overallValueOfQuote : '1.7696' ,
2020-11-09 18:09:38 +01:00
} ,
[ TEST _AGG _ID _3 ] : {
... baseExpectedResultQuotes [ TEST _AGG _ID _3 ] ,
2021-03-09 16:59:35 +01:00
sourceToken : ETH _SWAPS _TOKEN _OBJECT . address ,
2020-11-09 18:09:38 +01:00
destinationToken : '0x1111111111111111111111111111111111111111' ,
trade : { value : '0x8ac7230489e80000' } ,
2022-09-26 18:30:17 +02:00
overallValueOfQuote : '1.7498' ,
2020-11-09 18:09:38 +01:00
} ,
[ TEST _AGG _ID _4 ] : {
... baseExpectedResultQuotes [ TEST _AGG _ID _4 ] ,
2021-03-09 16:59:35 +01:00
sourceToken : ETH _SWAPS _TOKEN _OBJECT . address ,
2020-11-09 18:09:38 +01:00
destinationToken : '0x1111111111111111111111111111111111111111' ,
trade : { value : '0x8ac7230489e80000' } ,
2022-09-26 18:30:17 +02:00
overallValueOfQuote : '1.73' ,
2020-11-09 18:09:38 +01:00
} ,
[ TEST _AGG _ID _5 ] : {
... baseExpectedResultQuotes [ TEST _AGG _ID _5 ] ,
2021-03-09 16:59:35 +01:00
sourceToken : ETH _SWAPS _TOKEN _OBJECT . address ,
2020-11-09 18:09:38 +01:00
destinationToken : '0x1111111111111111111111111111111111111111' ,
trade : { value : '0x8ac7230489e80000' } ,
2022-09-26 18:30:17 +02:00
overallValueOfQuote : '1.7102' ,
2020-11-09 18:09:38 +01:00
} ,
[ TEST _AGG _ID _6 ] : {
... baseExpectedResultQuotes [ TEST _AGG _ID _6 ] ,
2021-03-09 16:59:35 +01:00
sourceToken : ETH _SWAPS _TOKEN _OBJECT . address ,
2020-11-09 18:09:38 +01:00
destinationToken : '0x1111111111111111111111111111111111111111' ,
trade : { value : '0x8ac7230489e80000' } ,
2022-09-26 18:30:17 +02:00
overallValueOfQuote : '1.6805' ,
2020-11-09 18:09:38 +01:00
} ,
2021-02-04 19:15:23 +01:00
} ;
2020-11-09 18:09:38 +01:00
2022-07-31 20:26:40 +02:00
const [ topAggId , resultQuotes ] =
await swapsController . _findTopQuoteAndCalculateSavings ( testInput ) ;
2021-02-04 19:15:23 +01:00
assert . equal ( topAggId , TEST _AGG _ID _1 ) ;
assert . deepStrictEqual ( resultQuotes , expectedResultQuotes ) ;
} ) ;
2020-11-09 18:09:38 +01:00
it ( 'returns the top aggId and quotes with savings and fee values if passed necessary data and the source token is ETH and an ETH fee is included in the trade value of what would be the best quote' , async function ( ) {
const testInput = mapValues (
getTopQuoteAndSavingsMockQuotes ( ) ,
( quote ) => ( {
... quote ,
2021-03-09 16:59:35 +01:00
sourceToken : ETH _SWAPS _TOKEN _OBJECT . address ,
2020-11-09 18:09:38 +01:00
destinationToken : '0x1111111111111111111111111111111111111111' ,
trade : { value : '0x8ac7230489e80000' } ,
} ) ,
2021-02-04 19:15:23 +01:00
) ;
2020-11-09 18:09:38 +01:00
// 0.04 ETH fee included in trade value
2021-02-04 19:15:23 +01:00
testInput [ TEST _AGG _ID _1 ] . trade . value = '0x8b553ece48ec0000' ;
2022-07-31 20:26:40 +02:00
const baseExpectedResultQuotes =
getTopQuoteAndSavingsBaseExpectedResults ( ) ;
2020-11-09 18:09:38 +01:00
const expectedResultQuotes = {
[ TEST _AGG _ID _1 ] : {
... baseExpectedResultQuotes [ TEST _AGG _ID _1 ] ,
2021-03-09 16:59:35 +01:00
sourceToken : ETH _SWAPS _TOKEN _OBJECT . address ,
2020-11-09 18:09:38 +01:00
destinationToken : '0x1111111111111111111111111111111111111111' ,
trade : { value : '0x8b553ece48ec0000' } ,
2022-09-26 18:30:17 +02:00
overallValueOfQuote : '1.7395' ,
ethFee : '0.29' ,
2020-11-09 18:09:38 +01:00
} ,
[ TEST _AGG _ID _2 ] : {
... baseExpectedResultQuotes [ TEST _AGG _ID _2 ] ,
2021-03-09 16:59:35 +01:00
sourceToken : ETH _SWAPS _TOKEN _OBJECT . address ,
2020-11-09 18:09:38 +01:00
destinationToken : '0x1111111111111111111111111111111111111111' ,
trade : { value : '0x8ac7230489e80000' } ,
2022-09-26 18:30:17 +02:00
overallValueOfQuote : '1.7696' ,
2020-11-09 18:09:38 +01:00
isBestQuote : true ,
savings : {
2022-09-26 18:30:17 +02:00
total : '0.01445' ,
performance : '0.01485' ,
fee : '0.02' ,
2020-11-09 18:09:38 +01:00
metaMaskFee : '0.0204' ,
2022-09-26 18:30:17 +02:00
medianMetaMaskFee : '0.02025' ,
2020-11-09 18:09:38 +01:00
} ,
} ,
[ TEST _AGG _ID _3 ] : {
... baseExpectedResultQuotes [ TEST _AGG _ID _3 ] ,
2021-03-09 16:59:35 +01:00
sourceToken : ETH _SWAPS _TOKEN _OBJECT . address ,
2020-11-09 18:09:38 +01:00
destinationToken : '0x1111111111111111111111111111111111111111' ,
trade : { value : '0x8ac7230489e80000' } ,
2022-09-26 18:30:17 +02:00
overallValueOfQuote : '1.7498' ,
2020-11-09 18:09:38 +01:00
} ,
[ TEST _AGG _ID _4 ] : {
... baseExpectedResultQuotes [ TEST _AGG _ID _4 ] ,
2021-03-09 16:59:35 +01:00
sourceToken : ETH _SWAPS _TOKEN _OBJECT . address ,
2020-11-09 18:09:38 +01:00
destinationToken : '0x1111111111111111111111111111111111111111' ,
trade : { value : '0x8ac7230489e80000' } ,
2022-09-26 18:30:17 +02:00
overallValueOfQuote : '1.73' ,
2020-11-09 18:09:38 +01:00
} ,
[ TEST _AGG _ID _5 ] : {
... baseExpectedResultQuotes [ TEST _AGG _ID _5 ] ,
2021-03-09 16:59:35 +01:00
sourceToken : ETH _SWAPS _TOKEN _OBJECT . address ,
2020-11-09 18:09:38 +01:00
destinationToken : '0x1111111111111111111111111111111111111111' ,
trade : { value : '0x8ac7230489e80000' } ,
2022-09-26 18:30:17 +02:00
overallValueOfQuote : '1.7102' ,
2020-11-09 18:09:38 +01:00
} ,
[ TEST _AGG _ID _6 ] : {
... baseExpectedResultQuotes [ TEST _AGG _ID _6 ] ,
2021-03-09 16:59:35 +01:00
sourceToken : ETH _SWAPS _TOKEN _OBJECT . address ,
2020-11-09 18:09:38 +01:00
destinationToken : '0x1111111111111111111111111111111111111111' ,
trade : { value : '0x8ac7230489e80000' } ,
2022-09-26 18:30:17 +02:00
overallValueOfQuote : '1.6805' ,
2020-11-09 18:09:38 +01:00
} ,
2021-02-04 19:15:23 +01:00
} ;
delete expectedResultQuotes [ TEST _AGG _ID _1 ] . isBestQuote ;
delete expectedResultQuotes [ TEST _AGG _ID _1 ] . savings ;
2020-11-09 18:09:38 +01:00
2022-07-31 20:26:40 +02:00
const [ topAggId , resultQuotes ] =
await swapsController . _findTopQuoteAndCalculateSavings ( testInput ) ;
2021-02-04 19:15:23 +01:00
assert . equal ( topAggId , TEST _AGG _ID _2 ) ;
assert . deepStrictEqual ( resultQuotes , expectedResultQuotes ) ;
} ) ;
2020-11-09 18:09:38 +01:00
it ( 'returns the top aggId and quotes with savings and fee values if passed necessary data and the source token is not ETH and an ETH fee is included in the trade value of what would be the best quote' , async function ( ) {
2021-02-04 19:15:23 +01:00
const testInput = getTopQuoteAndSavingsMockQuotes ( ) ;
2020-11-09 18:09:38 +01:00
// 0.04 ETH fee included in trade value
2021-02-04 19:15:23 +01:00
testInput [ TEST _AGG _ID _1 ] . trade . value = '0x8e1bc9bf040000' ;
2022-07-31 20:26:40 +02:00
const baseExpectedResultQuotes =
getTopQuoteAndSavingsBaseExpectedResults ( ) ;
2020-11-09 18:09:38 +01:00
const expectedResultQuotes = {
... baseExpectedResultQuotes ,
[ TEST _AGG _ID _1 ] : {
... baseExpectedResultQuotes [ TEST _AGG _ID _1 ] ,
trade : { value : '0x8e1bc9bf040000' } ,
2022-09-26 18:30:17 +02:00
overallValueOfQuote : '1.7395' ,
ethFee : '0.29' ,
2020-11-09 18:09:38 +01:00
} ,
[ TEST _AGG _ID _2 ] : {
... baseExpectedResultQuotes [ TEST _AGG _ID _2 ] ,
isBestQuote : true ,
savings : {
2022-09-26 18:30:17 +02:00
total : '0.01445' ,
performance : '0.01485' ,
fee : '0.02' ,
2020-11-09 18:09:38 +01:00
metaMaskFee : '0.0204' ,
2022-09-26 18:30:17 +02:00
medianMetaMaskFee : '0.02025' ,
2020-11-09 18:09:38 +01:00
} ,
} ,
2021-02-04 19:15:23 +01:00
} ;
delete expectedResultQuotes [ TEST _AGG _ID _1 ] . isBestQuote ;
delete expectedResultQuotes [ TEST _AGG _ID _1 ] . savings ;
2020-11-09 18:09:38 +01:00
2022-07-31 20:26:40 +02:00
const [ topAggId , resultQuotes ] =
await swapsController . _findTopQuoteAndCalculateSavings ( testInput ) ;
2021-05-07 21:38:24 +02:00
assert . equal ( topAggId , TEST _AGG _ID _2 ) ;
2021-02-04 19:15:23 +01:00
assert . deepStrictEqual ( resultQuotes , expectedResultQuotes ) ;
} ) ;
} ) ;
2020-10-19 23:52:47 +02:00
2020-10-06 20:28:38 +02:00
describe ( 'fetchAndSetQuotes' , function ( ) {
it ( 'returns null if fetchParams is not provided' , async function ( ) {
2021-02-04 19:15:23 +01:00
const quotes = await swapsController . fetchAndSetQuotes ( undefined ) ;
assert . strictEqual ( quotes , null ) ;
} ) ;
2021-07-09 17:24:00 +02:00
2020-10-06 20:28:38 +02:00
it ( 'calls fetchTradesInfo with the given fetchParams and returns the correct quotes' , async function ( ) {
2021-02-04 19:15:23 +01:00
fetchTradesInfoStub . resolves ( getMockQuotes ( ) ) ;
2020-10-06 20:28:38 +02:00
// Make it so approval is not required
sandbox
. stub ( swapsController , '_getERC20Allowance' )
2023-01-24 15:10:36 +01:00
. resolves ( BigNumber . from ( 1 ) ) ;
2020-10-06 20:28:38 +02:00
const [ newQuotes ] = await swapsController . fetchAndSetQuotes (
MOCK _FETCH _PARAMS ,
MOCK _FETCH _METADATA ,
2021-02-04 19:15:23 +01:00
) ;
2020-10-06 20:28:38 +02:00
2020-10-19 23:52:47 +02:00
assert . deepStrictEqual ( newQuotes [ TEST _AGG _ID _BEST ] , {
... getMockQuotes ( ) [ TEST _AGG _ID _BEST ] ,
2020-10-06 20:28:38 +02:00
sourceTokenInfo : undefined ,
destinationTokenInfo : {
symbol : 'FOO' ,
decimals : 18 ,
} ,
isBestQuote : true ,
// TODO: find a way to calculate these values dynamically
gasEstimate : 2000000 ,
2022-02-08 19:14:50 +01:00
gasEstimateWithRefund : '0xb8cae' ,
2020-10-19 23:52:47 +02:00
savings : {
2022-09-26 18:30:17 +02:00
fee : '-0.061067' ,
2020-11-09 18:09:38 +01:00
metaMaskFee : '0.5050505050505050505' ,
2020-10-19 23:52:47 +02:00
performance : '6' ,
2022-09-26 18:30:17 +02:00
total : '5.4338824949494949495' ,
2020-11-09 18:09:38 +01:00
medianMetaMaskFee : '0.44444444444444444444' ,
2020-10-19 23:52:47 +02:00
} ,
2022-09-26 18:30:17 +02:00
ethFee : '0.113536' ,
overallValueOfQuote : '49.886464' ,
2020-11-09 18:09:38 +01:00
metaMaskFeeInEth : '0.5050505050505050505' ,
ethValueOfTokens : '50' ,
2021-02-04 19:15:23 +01:00
} ) ;
2020-10-06 20:28:38 +02:00
assert . strictEqual (
2021-07-09 17:24:00 +02:00
fetchTradesInfoStub . calledOnceWithExactly ( MOCK _FETCH _PARAMS , {
... MOCK _FETCH _METADATA ,
} ) ,
2020-10-06 20:28:38 +02:00
true ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2021-07-09 17:24:00 +02:00
2023-04-18 16:45:18 +02:00
it ( 'calls returns the correct quotes on the optimism chain' , async function ( ) {
fetchTradesInfoStub . resetHistory ( ) ;
const OPTIMISM _MOCK _FETCH _METADATA = {
... MOCK _FETCH _METADATA ,
chainId : CHAIN _IDS . OPTIMISM ,
} ;
const optimismProviderResultStub = {
// 1 gwei
eth _gasPrice : '0x0de0b6b3a7640000' ,
// by default, all accounts are external accounts (not contracts)
eth _getCode : '0x' ,
eth _call :
'0x000000000000000000000000000000000000000000000000000103c18816d4e8' ,
} ;
const optimismProvider = createTestProviderTools ( {
scaffold : optimismProviderResultStub ,
networkId : 10 ,
chainId : 10 ,
} ) . provider ;
swapsController = getSwapsController ( optimismProvider ) ;
fetchTradesInfoStub . resolves ( getMockQuotes ( ) ) ;
// Make it so approval is not required
sandbox
. stub ( swapsController , '_getERC20Allowance' )
. resolves ( BigNumber . from ( 1 ) ) ;
const [ newQuotes ] = await swapsController . fetchAndSetQuotes (
MOCK _FETCH _PARAMS ,
OPTIMISM _MOCK _FETCH _METADATA ,
) ;
assert . deepStrictEqual ( newQuotes [ TEST _AGG _ID _BEST ] , {
... getMockQuotes ( ) [ TEST _AGG _ID _BEST ] ,
sourceTokenInfo : undefined ,
destinationTokenInfo : {
symbol : 'FOO' ,
decimals : 18 ,
} ,
isBestQuote : true ,
// TODO: find a way to calculate these values dynamically
gasEstimate : 2000000 ,
gasEstimateWithRefund : '0xb8cae' ,
savings : {
fee : '-0.061067' ,
metaMaskFee : '0.5050505050505050505' ,
performance : '6' ,
total : '5.4338824949494949495' ,
medianMetaMaskFee : '0.44444444444444444444' ,
} ,
ethFee : '0.113822' ,
multiLayerL1TradeFeeTotal : '0x0103c18816d4e8' ,
overallValueOfQuote : '49.886178' ,
metaMaskFeeInEth : '0.5050505050505050505' ,
ethValueOfTokens : '50' ,
} ) ;
assert . strictEqual (
fetchTradesInfoStub . calledOnceWithExactly ( MOCK _FETCH _PARAMS , {
... OPTIMISM _MOCK _FETCH _METADATA ,
} ) ,
true ,
) ;
} ) ;
2020-10-06 20:28:38 +02:00
it ( 'performs the allowance check' , async function ( ) {
2021-02-04 19:15:23 +01:00
fetchTradesInfoStub . resolves ( getMockQuotes ( ) ) ;
2020-10-06 20:28:38 +02:00
// Make it so approval is not required
const allowanceStub = sandbox
. stub ( swapsController , '_getERC20Allowance' )
2023-01-24 15:10:36 +01:00
. resolves ( BigNumber . from ( 1 ) ) ;
2020-10-06 20:28:38 +02:00
await swapsController . fetchAndSetQuotes (
MOCK _FETCH _PARAMS ,
MOCK _FETCH _METADATA ,
2021-02-04 19:15:23 +01:00
) ;
2020-10-06 20:28:38 +02:00
assert . strictEqual (
allowanceStub . calledOnceWithExactly (
MOCK _FETCH _PARAMS . sourceToken ,
MOCK _FETCH _PARAMS . fromAddress ,
2022-09-14 16:55:31 +02:00
CHAIN _IDS . MAINNET ,
2020-10-06 20:28:38 +02:00
) ,
true ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2020-10-06 20:28:38 +02:00
it ( 'gets the gas limit if approval is required' , async function ( ) {
2021-02-04 19:15:23 +01:00
fetchTradesInfoStub . resolves ( MOCK _QUOTES _APPROVAL _REQUIRED ) ;
2020-10-06 20:28:38 +02:00
// Ensure approval is required
sandbox
. stub ( swapsController , '_getERC20Allowance' )
2023-01-24 15:10:36 +01:00
. resolves ( BigNumber . from ( 0 ) ) ;
2020-10-06 20:28:38 +02:00
2021-02-04 19:15:23 +01:00
const timedoutGasReturnResult = { gasLimit : 1000000 } ;
2020-10-06 20:28:38 +02:00
const timedoutGasReturnStub = sandbox
. stub ( swapsController , 'timedoutGasReturn' )
2021-02-04 19:15:23 +01:00
. resolves ( timedoutGasReturnResult ) ;
2020-10-06 20:28:38 +02:00
await swapsController . fetchAndSetQuotes (
MOCK _FETCH _PARAMS ,
MOCK _FETCH _METADATA ,
2021-02-04 19:15:23 +01:00
) ;
2020-10-06 20:28:38 +02:00
// Mocked quotes approvalNeeded is null, so it will only be called with the gas
assert . strictEqual (
2020-10-07 21:00:17 +02:00
timedoutGasReturnStub . calledOnceWithExactly ( MOCK _APPROVAL _NEEDED ) ,
2020-10-06 20:28:38 +02:00
true ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2020-10-06 20:28:38 +02:00
it ( 'marks the best quote' , async function ( ) {
2021-02-04 19:15:23 +01:00
fetchTradesInfoStub . resolves ( getMockQuotes ( ) ) ;
2020-10-06 20:28:38 +02:00
// Make it so approval is not required
sandbox
. stub ( swapsController , '_getERC20Allowance' )
2023-01-24 15:10:36 +01:00
. resolves ( BigNumber . from ( 1 ) ) ;
2020-10-06 20:28:38 +02:00
const [ newQuotes , topAggId ] = await swapsController . fetchAndSetQuotes (
MOCK _FETCH _PARAMS ,
MOCK _FETCH _METADATA ,
2021-02-04 19:15:23 +01:00
) ;
2020-10-06 20:28:38 +02:00
2021-02-04 19:15:23 +01:00
assert . strictEqual ( topAggId , TEST _AGG _ID _BEST ) ;
assert . strictEqual ( newQuotes [ topAggId ] . isBestQuote , true ) ;
} ) ;
2020-10-06 20:28:38 +02:00
it ( 'selects the best quote' , async function ( ) {
2021-02-04 19:15:23 +01:00
const bestAggId = 'bestAggId' ;
2020-10-06 20:28:38 +02:00
// Clone the existing mock quote and increase destination amount
const bestQuote = {
2020-10-19 23:52:47 +02:00
... getMockQuotes ( ) [ TEST _AGG _ID _1 ] ,
2020-10-06 20:28:38 +02:00
aggregator : bestAggId ,
2023-01-24 15:10:36 +01:00
destinationAmount : BigNumber . from (
2020-10-19 23:52:47 +02:00
getMockQuotes ( ) [ TEST _AGG _ID _1 ] . destinationAmount ,
2020-10-06 20:28:38 +02:00
)
2020-10-19 23:52:47 +02:00
. add ( ( 100e18 ) . toString ( ) )
2020-10-06 20:28:38 +02:00
. toString ( ) ,
2021-02-04 19:15:23 +01:00
} ;
const quotes = { ... getMockQuotes ( ) , [ bestAggId ] : bestQuote } ;
fetchTradesInfoStub . resolves ( quotes ) ;
2020-10-06 20:28:38 +02:00
// Make it so approval is not required
sandbox
. stub ( swapsController , '_getERC20Allowance' )
2023-01-24 15:10:36 +01:00
. resolves ( BigNumber . from ( 1 ) ) ;
2020-10-06 20:28:38 +02:00
const [ newQuotes , topAggId ] = await swapsController . fetchAndSetQuotes (
MOCK _FETCH _PARAMS ,
MOCK _FETCH _METADATA ,
2021-02-04 19:15:23 +01:00
) ;
2020-10-06 20:28:38 +02:00
2021-02-04 19:15:23 +01:00
assert . strictEqual ( topAggId , bestAggId ) ;
assert . strictEqual ( newQuotes [ topAggId ] . isBestQuote , true ) ;
} ) ;
2020-10-06 20:28:38 +02:00
2020-10-09 20:00:20 +02:00
it ( 'does not mark as best quote if no conversion rate exists for destination token' , async function ( ) {
2021-02-04 19:15:23 +01:00
fetchTradesInfoStub . resolves ( getMockQuotes ( ) ) ;
2020-10-06 20:28:38 +02:00
// Make it so approval is not required
sandbox
. stub ( swapsController , '_getERC20Allowance' )
2023-01-24 15:10:36 +01:00
. resolves ( BigNumber . from ( 1 ) ) ;
2020-10-06 20:28:38 +02:00
2021-11-22 13:04:31 +01:00
swapsController . getTokenRatesState = ( ) => ( {
2020-10-06 20:28:38 +02:00
contractExchangeRates : { } ,
2021-11-22 13:04:31 +01:00
} ) ;
2020-10-06 20:28:38 +02:00
const [ newQuotes , topAggId ] = await swapsController . fetchAndSetQuotes (
MOCK _FETCH _PARAMS ,
MOCK _FETCH _METADATA ,
2021-02-04 19:15:23 +01:00
) ;
2020-10-06 20:28:38 +02:00
2021-02-04 19:15:23 +01:00
assert . strictEqual ( newQuotes [ topAggId ] . isBestQuote , undefined ) ;
} ) ;
} ) ;
2020-10-06 20:28:38 +02:00
describe ( 'resetSwapsState' , function ( ) {
it ( 'resets the swaps state correctly' , function ( ) {
2021-02-04 19:15:23 +01:00
const { swapsState : old } = swapsController . store . getState ( ) ;
swapsController . resetSwapsState ( ) ;
const { swapsState } = swapsController . store . getState ( ) ;
2020-10-06 20:28:38 +02:00
assert . deepStrictEqual ( swapsState , {
... EMPTY _INIT _STATE . swapsState ,
tokens : old . tokens ,
2020-12-15 21:24:22 +01:00
swapsQuoteRefreshTime : old . swapsQuoteRefreshTime ,
2021-09-15 15:13:18 +02:00
swapsQuotePrefetchingRefreshTime :
old . swapsQuotePrefetchingRefreshTime ,
2022-02-18 17:48:38 +01:00
swapsStxGetTransactionsRefreshTime :
old . swapsStxGetTransactionsRefreshTime ,
swapsStxBatchStatusRefreshTime : old . swapsStxBatchStatusRefreshTime ,
2021-02-04 19:15:23 +01:00
} ) ;
} ) ;
2020-10-06 20:28:38 +02:00
it ( 'clears polling timeout' , function ( ) {
swapsController . pollingTimeout = setTimeout (
( ) => assert . fail ( ) ,
2021-06-10 21:27:03 +02:00
POLLING _TIMEOUT ,
2021-02-04 19:15:23 +01:00
) ;
swapsController . resetSwapsState ( ) ;
assert . strictEqual ( swapsController . pollingTimeout . _idleTimeout , - 1 ) ;
} ) ;
} ) ;
2020-10-06 20:28:38 +02:00
describe ( 'stopPollingForQuotes' , function ( ) {
it ( 'clears polling timeout' , function ( ) {
swapsController . pollingTimeout = setTimeout (
( ) => assert . fail ( ) ,
2021-06-10 21:27:03 +02:00
POLLING _TIMEOUT ,
2021-02-04 19:15:23 +01:00
) ;
swapsController . stopPollingForQuotes ( ) ;
assert . strictEqual ( swapsController . pollingTimeout . _idleTimeout , - 1 ) ;
} ) ;
2020-10-06 20:28:38 +02:00
it ( 'resets quotes state correctly' , function ( ) {
2021-02-04 19:15:23 +01:00
swapsController . stopPollingForQuotes ( ) ;
const { swapsState } = swapsController . store . getState ( ) ;
assert . deepStrictEqual ( swapsState . quotes , { } ) ;
assert . strictEqual ( swapsState . quotesLastFetched , null ) ;
} ) ;
} ) ;
2020-10-06 20:28:38 +02:00
describe ( 'resetPostFetchState' , function ( ) {
it ( 'clears polling timeout' , function ( ) {
swapsController . pollingTimeout = setTimeout (
( ) => assert . fail ( ) ,
2021-06-10 21:27:03 +02:00
POLLING _TIMEOUT ,
2021-02-04 19:15:23 +01:00
) ;
swapsController . resetPostFetchState ( ) ;
assert . strictEqual ( swapsController . pollingTimeout . _idleTimeout , - 1 ) ;
} ) ;
2020-10-06 20:28:38 +02:00
it ( 'updates state correctly' , function ( ) {
2021-02-04 19:15:23 +01:00
const tokens = 'test' ;
const fetchParams = 'test' ;
const swapsFeatureIsLive = false ;
2022-02-18 17:48:38 +01:00
const swapsFeatureFlags = { } ;
2021-02-04 19:15:23 +01:00
const swapsQuoteRefreshTime = 0 ;
2021-09-15 15:13:18 +02:00
const swapsQuotePrefetchingRefreshTime = 0 ;
2022-02-18 17:48:38 +01:00
const swapsStxBatchStatusRefreshTime = 0 ;
const swapsStxGetTransactionsRefreshTime = 0 ;
2020-10-06 20:28:38 +02:00
swapsController . store . updateState ( {
2020-12-15 21:24:22 +01:00
swapsState : {
tokens ,
fetchParams ,
swapsFeatureIsLive ,
2022-02-18 17:48:38 +01:00
swapsFeatureFlags ,
2020-12-15 21:24:22 +01:00
swapsQuoteRefreshTime ,
2021-09-15 15:13:18 +02:00
swapsQuotePrefetchingRefreshTime ,
2022-02-18 17:48:38 +01:00
swapsStxBatchStatusRefreshTime ,
swapsStxGetTransactionsRefreshTime ,
2020-12-15 21:24:22 +01:00
} ,
2021-02-04 19:15:23 +01:00
} ) ;
2020-10-06 20:28:38 +02:00
2021-02-04 19:15:23 +01:00
swapsController . resetPostFetchState ( ) ;
2020-10-06 20:28:38 +02:00
2021-02-04 19:15:23 +01:00
const { swapsState } = swapsController . store . getState ( ) ;
2020-10-06 20:28:38 +02:00
assert . deepStrictEqual ( swapsState , {
... EMPTY _INIT _STATE . swapsState ,
tokens ,
fetchParams ,
swapsFeatureIsLive ,
2020-12-15 21:24:22 +01:00
swapsQuoteRefreshTime ,
2021-09-15 15:13:18 +02:00
swapsQuotePrefetchingRefreshTime ,
2021-02-04 19:15:23 +01:00
} ) ;
} ) ;
} ) ;
} ) ;
2020-10-19 23:52:47 +02:00
describe ( 'utils' , function ( ) {
2020-11-09 18:09:38 +01:00
describe ( 'getMedianEthValueQuote' , function ( ) {
2021-02-04 19:15:23 +01:00
const { getMedianEthValueQuote } = utils ;
2020-10-19 23:52:47 +02:00
it ( 'calculates median correctly with uneven sample' , function ( ) {
2020-11-09 18:09:38 +01:00
const expectedResult = {
ethFee : '10' ,
metaMaskFeeInEth : '5' ,
ethValueOfTokens : '0.3' ,
2021-02-04 19:15:23 +01:00
} ;
2020-11-09 18:09:38 +01:00
const values = [
{
overallValueOfQuote : '3' ,
ethFee : '10' ,
metaMaskFeeInEth : '5' ,
ethValueOfTokens : '0.3' ,
} ,
{
overallValueOfQuote : '2' ,
ethFee : '20' ,
metaMaskFeeInEth : '3' ,
ethValueOfTokens : '0.2' ,
} ,
{
overallValueOfQuote : '6' ,
ethFee : '40' ,
metaMaskFeeInEth : '6' ,
ethValueOfTokens : '0.6' ,
} ,
2021-02-04 19:15:23 +01:00
] ;
2020-10-19 23:52:47 +02:00
2021-02-04 19:15:23 +01:00
const median = getMedianEthValueQuote ( values ) ;
2020-11-09 18:09:38 +01:00
assert . deepEqual (
median ,
expectedResult ,
'should have returned correct median quote object' ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2020-10-19 23:52:47 +02:00
it ( 'calculates median correctly with even sample' , function ( ) {
2020-11-09 18:09:38 +01:00
const expectedResult = {
ethFee : '20' ,
metaMaskFeeInEth : '6.5' ,
ethValueOfTokens : '0.25' ,
2021-02-04 19:15:23 +01:00
} ;
2020-11-09 18:09:38 +01:00
const values = [
{
overallValueOfQuote : '3' ,
ethFee : '10' ,
metaMaskFeeInEth : '5' ,
ethValueOfTokens : '0.3' ,
} ,
{
overallValueOfQuote : '1' ,
ethFee : '20' ,
metaMaskFeeInEth : '3' ,
ethValueOfTokens : '0.2' ,
} ,
{
overallValueOfQuote : '2' ,
ethFee : '30' ,
metaMaskFeeInEth : '8' ,
ethValueOfTokens : '0.2' ,
} ,
{
overallValueOfQuote : '6' ,
ethFee : '40' ,
metaMaskFeeInEth : '6' ,
ethValueOfTokens : '0.6' ,
} ,
2021-02-04 19:15:23 +01:00
] ;
const median = getMedianEthValueQuote ( values ) ;
2020-10-19 23:52:47 +02:00
2020-11-09 18:09:38 +01:00
assert . deepEqual (
median ,
expectedResult ,
'should have returned correct median quote object' ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2020-11-09 18:09:38 +01:00
it ( 'calculates median correctly with an uneven sample where multiple quotes have the median overall value' , function ( ) {
const expectedResult = {
ethFee : '2' ,
metaMaskFeeInEth : '0.5' ,
ethValueOfTokens : '5' ,
2021-02-04 19:15:23 +01:00
} ;
2020-11-09 18:09:38 +01:00
const values = [
{
overallValueOfQuote : '1' ,
ethValueOfTokens : '2' ,
ethFee : '1' ,
metaMaskFeeInEth : '0.2' ,
} ,
{
overallValueOfQuote : '3' ,
ethValueOfTokens : '4' ,
ethFee : '1' ,
metaMaskFeeInEth : '0.4' ,
} ,
{
overallValueOfQuote : '3' ,
ethValueOfTokens : '5' ,
ethFee : '2' ,
metaMaskFeeInEth : '0.5' ,
} ,
{
overallValueOfQuote : '3' ,
ethValueOfTokens : '6' ,
ethFee : '3' ,
metaMaskFeeInEth : '0.6' ,
} ,
{
overallValueOfQuote : '4' ,
ethValueOfTokens : '6' ,
ethFee : '2' ,
metaMaskFeeInEth : '0.6' ,
} ,
{
overallValueOfQuote : '4' ,
ethValueOfTokens : '7' ,
ethFee : '3' ,
metaMaskFeeInEth : '0.7' ,
} ,
{
overallValueOfQuote : '6' ,
ethValueOfTokens : '8' ,
ethFee : '2' ,
metaMaskFeeInEth : '0.8' ,
} ,
2021-02-04 19:15:23 +01:00
] ;
const median = getMedianEthValueQuote ( values ) ;
2020-11-09 18:09:38 +01:00
assert . deepEqual (
median ,
expectedResult ,
'should have returned correct median quote object' ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2020-11-09 18:09:38 +01:00
it ( 'calculates median correctly with an even sample where multiple quotes have the same overall value as either of the two middle values' , function ( ) {
const expectedResult = {
ethFee : '2' ,
metaMaskFeeInEth : '0.55' ,
ethValueOfTokens : '5.5' ,
2021-02-04 19:15:23 +01:00
} ;
2020-11-09 18:09:38 +01:00
const values = [
{
overallValueOfQuote : '1' ,
ethValueOfTokens : '2' ,
ethFee : '1' ,
metaMaskFeeInEth : '0.2' ,
} ,
{
overallValueOfQuote : '3' ,
ethValueOfTokens : '4' ,
ethFee : '1' ,
metaMaskFeeInEth : '0.4' ,
} ,
{
overallValueOfQuote : '3' ,
ethValueOfTokens : '5' ,
ethFee : '2' ,
metaMaskFeeInEth : '0.5' ,
} ,
{
overallValueOfQuote : '4' ,
ethValueOfTokens : '6' ,
ethFee : '2' ,
metaMaskFeeInEth : '0.6' ,
} ,
{
overallValueOfQuote : '4' ,
ethValueOfTokens : '7' ,
ethFee : '3' ,
metaMaskFeeInEth : '0.7' ,
} ,
{
overallValueOfQuote : '6' ,
ethValueOfTokens : '8' ,
ethFee : '2' ,
metaMaskFeeInEth : '0.8' ,
} ,
2021-02-04 19:15:23 +01:00
] ;
const median = getMedianEthValueQuote ( values ) ;
2020-11-09 18:09:38 +01:00
assert . deepEqual (
median ,
expectedResult ,
'should have returned correct median quote object' ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
2020-10-19 23:52:47 +02:00
it ( 'throws on empty or non-array sample' , function ( ) {
2020-11-09 18:09:38 +01:00
assert . throws (
( ) => getMedianEthValueQuote ( [ ] ) ,
'should throw on empty array' ,
2021-02-04 19:15:23 +01:00
) ;
2020-10-19 23:52:47 +02:00
2020-11-09 18:09:38 +01:00
assert . throws (
( ) => getMedianEthValueQuote ( ) ,
'should throw on non-array param' ,
2021-02-04 19:15:23 +01:00
) ;
2020-10-19 23:52:47 +02:00
2020-11-09 18:09:38 +01:00
assert . throws (
( ) => getMedianEthValueQuote ( { } ) ,
'should throw on non-array param' ,
2021-02-04 19:15:23 +01:00
) ;
} ) ;
} ) ;
} ) ;
} ) ;
2020-10-19 23:52:47 +02:00
2020-11-03 00:41:28 +01:00
function getMockQuotes ( ) {
2020-10-19 23:52:47 +02:00
return {
[ TEST _AGG _ID _1 ] : {
2020-11-03 00:41:28 +01:00
trade : {
from : '0xe18035bf8712672935fdb4e5e431b1a0183d2dfc' ,
value : '0x0' ,
gas : '0x61a80' , // 4e5
to : '0x881D40237659C251811CEC9c364ef91dC08D300C' ,
2020-10-19 23:52:47 +02:00
} ,
2020-11-03 00:41:28 +01:00
sourceAmount : '10000000000000000000' , // 10e18
destinationAmount : '20000000000000000000' , // 20e18
error : null ,
sourceToken : '0x6b175474e89094c44da98b954eedeac495271d0f' ,
destinationToken : '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' ,
approvalNeeded : null ,
maxGas : 600000 ,
averageGas : 120000 ,
estimatedRefund : 80000 ,
fetchTime : 607 ,
aggregator : TEST _AGG _ID _1 ,
aggType : 'AGG' ,
slippage : 2 ,
sourceTokenInfo : {
address : '0x6b175474e89094c44da98b954eedeac495271d0f' ,
symbol : 'DAI' ,
decimals : 18 ,
iconUrl : 'https://foo.bar/logo.png' ,
2020-10-19 23:52:47 +02:00
} ,
2020-11-03 00:41:28 +01:00
destinationTokenInfo : {
address : '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' ,
symbol : 'USDC' ,
decimals : 18 ,
2020-10-19 23:52:47 +02:00
} ,
2020-11-09 18:09:38 +01:00
fee : 1 ,
2020-10-19 23:52:47 +02:00
} ,
[ TEST _AGG _ID _BEST ] : {
2020-11-03 00:41:28 +01:00
trade : {
from : '0xe18035bf8712672935fdb4e5e431b1a0183d2dfc' ,
value : '0x0' ,
gas : '0x61a80' ,
to : '0x881D40237659C251811CEC9c364ef91dC08D300C' ,
2020-10-19 23:52:47 +02:00
} ,
2020-11-03 00:41:28 +01:00
sourceAmount : '10000000000000000000' ,
destinationAmount : '25000000000000000000' , // 25e18
error : null ,
sourceToken : '0x6b175474e89094c44da98b954eedeac495271d0f' ,
destinationToken : '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' ,
approvalNeeded : null ,
maxGas : 1100000 ,
averageGas : 411000 ,
estimatedRefund : 343090 ,
fetchTime : 1003 ,
aggregator : TEST _AGG _ID _BEST ,
aggType : 'AGG' ,
slippage : 2 ,
sourceTokenInfo : {
address : '0x6b175474e89094c44da98b954eedeac495271d0f' ,
symbol : 'DAI' ,
decimals : 18 ,
iconUrl : 'https://foo.bar/logo.png' ,
2020-10-19 23:52:47 +02:00
} ,
2020-11-03 00:41:28 +01:00
destinationTokenInfo : {
address : '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' ,
symbol : 'USDC' ,
decimals : 18 ,
2020-10-19 23:52:47 +02:00
} ,
2020-11-09 18:09:38 +01:00
fee : 1 ,
2020-10-19 23:52:47 +02:00
} ,
[ TEST _AGG _ID _2 ] : {
2020-11-03 00:41:28 +01:00
trade : {
from : '0xe18035bf8712672935fdb4e5e431b1a0183d2dfc' ,
value : '0x0' ,
gas : '0x61a80' ,
to : '0x881D40237659C251811CEC9c364ef91dC08D300C' ,
2020-10-19 23:52:47 +02:00
} ,
2020-11-03 00:41:28 +01:00
sourceAmount : '10000000000000000000' ,
destinationAmount : '22000000000000000000' , // 22e18
error : null ,
sourceToken : '0x6b175474e89094c44da98b954eedeac495271d0f' ,
destinationToken : '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' ,
approvalNeeded : null ,
maxGas : 368000 ,
averageGas : 197000 ,
estimatedRefund : 18205 ,
fetchTime : 1354 ,
aggregator : TEST _AGG _ID _2 ,
aggType : 'AGG' ,
slippage : 2 ,
sourceTokenInfo : {
address : '0x6b175474e89094c44da98b954eedeac495271d0f' ,
symbol : 'DAI' ,
decimals : 18 ,
iconUrl : 'https://foo.bar/logo.png' ,
2020-10-19 23:52:47 +02:00
} ,
2020-11-03 00:41:28 +01:00
destinationTokenInfo : {
address : '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' ,
symbol : 'USDC' ,
decimals : 18 ,
2020-10-19 23:52:47 +02:00
} ,
2020-11-09 18:09:38 +01:00
fee : 1 ,
} ,
2021-02-04 19:15:23 +01:00
} ;
2020-11-09 18:09:38 +01:00
}
function getTopQuoteAndSavingsMockQuotes ( ) {
// These destination amounts are calculated using the following "pre-fee" amounts
// TEST_AGG_ID_1: 20.5
// TEST_AGG_ID_2: 20.4
// TEST_AGG_ID_3: 20.2
// TEST_AGG_ID_4: 20
// TEST_AGG_ID_5: 19.8
// TEST_AGG_ID_6: 19.5
return {
[ TEST _AGG _ID _1 ] : {
aggregator : TEST _AGG _ID _1 ,
approvalNeeded : null ,
gasEstimate : '0x186a0' ,
destinationAmount : '20295000000000000000' ,
destinationToken : '0x1111111111111111111111111111111111111111' ,
destinationTokenInfo : { decimals : 18 } ,
sourceAmount : '10000000000000000000' ,
sourceToken : '0xsomeERC20TokenAddress' ,
trade : {
value : '0x0' ,
} ,
fee : 1 ,
} ,
[ TEST _AGG _ID _2 ] : {
aggregator : TEST _AGG _ID _2 ,
approvalNeeded : null ,
gasEstimate : '0x30d40' ,
destinationAmount : '20196000000000000000' ,
destinationToken : '0x1111111111111111111111111111111111111111' ,
destinationTokenInfo : { decimals : 18 } ,
sourceAmount : '10000000000000000000' ,
sourceToken : '0xsomeERC20TokenAddress' ,
trade : {
value : '0x0' ,
} ,
fee : 1 ,
} ,
[ TEST _AGG _ID _3 ] : {
aggregator : TEST _AGG _ID _3 ,
approvalNeeded : null ,
gasEstimate : '0x493e0' ,
destinationAmount : '19998000000000000000' ,
destinationToken : '0x1111111111111111111111111111111111111111' ,
destinationTokenInfo : { decimals : 18 } ,
sourceAmount : '10000000000000000000' ,
sourceToken : '0xsomeERC20TokenAddress' ,
trade : {
value : '0x0' ,
} ,
fee : 1 ,
} ,
[ TEST _AGG _ID _4 ] : {
aggregator : TEST _AGG _ID _4 ,
approvalNeeded : null ,
gasEstimate : '0x61a80' ,
destinationAmount : '19800000000000000000' ,
destinationToken : '0x1111111111111111111111111111111111111111' ,
destinationTokenInfo : { decimals : 18 } ,
sourceAmount : '10000000000000000000' ,
sourceToken : '0xsomeERC20TokenAddress' ,
trade : {
value : '0x0' ,
} ,
fee : 1 ,
} ,
[ TEST _AGG _ID _5 ] : {
aggregator : TEST _AGG _ID _5 ,
approvalNeeded : null ,
gasEstimate : '0x7a120' ,
destinationAmount : '19602000000000000000' ,
destinationToken : '0x1111111111111111111111111111111111111111' ,
destinationTokenInfo : { decimals : 18 } ,
sourceAmount : '10000000000000000000' ,
sourceToken : '0xsomeERC20TokenAddress' ,
trade : {
value : '0x0' ,
} ,
fee : 1 ,
} ,
[ TEST _AGG _ID _6 ] : {
aggregator : TEST _AGG _ID _6 ,
approvalNeeded : null ,
gasEstimate : '0x927c0' ,
destinationAmount : '19305000000000000000' ,
destinationToken : '0x1111111111111111111111111111111111111111' ,
destinationTokenInfo : { decimals : 18 } ,
sourceAmount : '10000000000000000000' ,
sourceToken : '0xsomeERC20TokenAddress' ,
trade : {
value : '0x0' ,
} ,
fee : 1 ,
} ,
2021-02-04 19:15:23 +01:00
} ;
2020-11-09 18:09:38 +01:00
}
function getTopQuoteAndSavingsBaseExpectedResults ( ) {
2021-02-04 19:15:23 +01:00
const baseTestInput = getTopQuoteAndSavingsMockQuotes ( ) ;
2020-11-09 18:09:38 +01:00
return {
[ TEST _AGG _ID _1 ] : {
... baseTestInput [ TEST _AGG _ID _1 ] ,
isBestQuote : true ,
2022-09-26 18:30:17 +02:00
ethFee : '0.25' ,
overallValueOfQuote : '1.7795' ,
2020-11-09 18:09:38 +01:00
metaMaskFeeInEth : '0.0205' ,
ethValueOfTokens : '2.0295' ,
savings : {
2022-09-26 18:30:17 +02:00
total : '0.0191' ,
2020-11-09 18:09:38 +01:00
performance : '0.0396' ,
2022-09-26 18:30:17 +02:00
fee : '0' ,
2020-11-09 18:09:38 +01:00
metaMaskFee : '0.0205' ,
medianMetaMaskFee : '0.0201' ,
} ,
} ,
[ TEST _AGG _ID _2 ] : {
... baseTestInput [ TEST _AGG _ID _2 ] ,
2022-09-26 18:30:17 +02:00
ethFee : '0.25' ,
overallValueOfQuote : '1.7696' ,
2020-11-09 18:09:38 +01:00
metaMaskFeeInEth : '0.0204' ,
ethValueOfTokens : '2.0196' ,
} ,
[ TEST _AGG _ID _3 ] : {
... baseTestInput [ TEST _AGG _ID _3 ] ,
2022-09-26 18:30:17 +02:00
ethFee : '0.25' ,
overallValueOfQuote : '1.7498' ,
2020-11-09 18:09:38 +01:00
metaMaskFeeInEth : '0.0202' ,
ethValueOfTokens : '1.9998' ,
} ,
[ TEST _AGG _ID _4 ] : {
... baseTestInput [ TEST _AGG _ID _4 ] ,
2022-09-26 18:30:17 +02:00
ethFee : '0.25' ,
overallValueOfQuote : '1.73' ,
2020-11-09 18:09:38 +01:00
metaMaskFeeInEth : '0.02' ,
ethValueOfTokens : '1.98' ,
} ,
[ TEST _AGG _ID _5 ] : {
... baseTestInput [ TEST _AGG _ID _5 ] ,
2022-09-26 18:30:17 +02:00
ethFee : '0.25' ,
overallValueOfQuote : '1.7102' ,
2020-11-09 18:09:38 +01:00
metaMaskFeeInEth : '0.0198' ,
ethValueOfTokens : '1.9602' ,
} ,
[ TEST _AGG _ID _6 ] : {
... baseTestInput [ TEST _AGG _ID _6 ] ,
2022-09-26 18:30:17 +02:00
ethFee : '0.25' ,
overallValueOfQuote : '1.6805' ,
2020-11-09 18:09:38 +01:00
metaMaskFeeInEth : '0.0195' ,
ethValueOfTokens : '1.9305' ,
2020-10-19 23:52:47 +02:00
} ,
2021-02-04 19:15:23 +01:00
} ;
2020-10-19 23:52:47 +02:00
}