@ -5,7 +5,7 @@ import { ethers } from 'ethers'
import BigNumber from 'bignumber.js'
import ObservableStore from 'obs-store'
import { createTestProviderTools } from '../../../stub/provider'
import SwapsController from '../../../../app/scripts/controllers/swaps'
import SwapsController , { utils } from '../../../../app/scripts/controllers/swaps'
const MOCK _FETCH _PARAMS = {
slippage : 3 ,
@ -17,32 +17,11 @@ const MOCK_FETCH_PARAMS = {
exchangeList : 'zeroExV1' ,
}
const TEST _AGG _ID = 'zeroExV1'
const MOCK _QUOTES = {
[ TEST _AGG _ID ] : {
trade : {
data : '0x00' ,
from : '0x7F18BB4Dd92CF2404C54CBa1A9BE4A1153bdb078' ,
value : '0x17647444f166000' ,
gas : '0xe09c0' ,
gasPrice : undefined ,
to : '0x881d40237659c251811cec9c364ef91dc08d300c' ,
} ,
sourceAmount : '1000000000000000000000000000000000000' ,
destinationAmount : '396493201125465' ,
error : null ,
sourceToken : '0x6b175474e89094c44da98b954eedeac495271d0f' ,
destinationToken : '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' ,
approvalNeeded : null ,
maxGas : 920000 ,
averageGas : 312510 ,
estimatedRefund : 343090 ,
fetchTime : 559 ,
aggregator : TEST _AGG _ID ,
aggType : 'AGG' ,
slippage : 3 ,
} ,
}
const TEST _AGG _ID _1 = 'TEST_AGG_1'
const TEST _AGG _ID _2 = 'TEST_AGG_2'
const TEST _AGG _ID _BEST = 'TEST_AGG_BEST'
const TEST _AGG _ID _APPROVAL = 'TEST_AGG_APPROVAL'
const MOCK _APPROVAL _NEEDED = {
'data' : '0x095ea7b300000000000000000000000095e6f48254609a6ee006f7d493c8e5fb97094cef0000000000000000000000000000000000000000004a817c7ffffffdabf41c00' ,
'to' : '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' ,
@ -51,8 +30,9 @@ const MOCK_APPROVAL_NEEDED = {
'gas' : '12' ,
'gasPrice' : '34' ,
}
const MOCK _QUOTES _APPROVAL _REQUIRED = {
[ TEST _AGG _ID ] : {
[ TEST _AGG _ID _APPROVAL ] : {
trade : {
data : '0x00' ,
from : '0x7F18BB4Dd92CF2404C54CBa1A9BE4A1153bdb078' ,
@ -70,12 +50,13 @@ const MOCK_QUOTES_APPROVAL_REQUIRED = {
averageGas : 312510 ,
estimatedRefund : 343090 ,
fetchTime : 559 ,
aggregator : TEST _AGG _ID ,
aggregator : TEST _AGG _ID _APPROVAL ,
aggType : 'AGG' ,
slippage : 3 ,
approvalNeeded : MOCK _APPROVAL _NEEDED ,
} ,
}
const MOCK _FETCH _METADATA = {
destinationTokenInfo : {
symbol : 'FOO' ,
@ -233,14 +214,14 @@ describe('SwapsController', function () {
} )
it ( 'should set initial gas estimate' , async function ( ) {
const initialAggId = TEST _AGG _ID
const initialAggId = TEST _AGG _ID _1
const baseGasEstimate = 10
const { maxGas , estimatedRefund } = MOCK _QUOTES [ TEST _AGG _ID ]
const { maxGas , estimatedRefund } = getMockQuotes ( ) [ TEST _AGG _ID _1 ]
const { swapsState } = swapsController . store . getState ( )
// Set mock quotes in order to have data for the test agg
swapsController . store . updateState ( {
swapsState : { ... swapsState , quotes : MOCK _QUOTES } ,
swapsState : { ... swapsState , quotes : getMockQuotes ( ) } ,
} )
await swapsController . setInitialGasEstimate (
@ -272,6 +253,19 @@ describe('SwapsController', function () {
} )
} )
describe ( '_findTopQuoteAndCalculateSavings' , function ( ) {
it ( 'returns empty object if passed undefined or empty object' , async function ( ) {
assert . deepStrictEqual (
await swapsController . _findTopQuoteAndCalculateSavings ( ) ,
{ } ,
)
assert . deepStrictEqual (
await swapsController . _findTopQuoteAndCalculateSavings ( { } ) ,
{ } ,
)
} )
} )
describe ( 'fetchAndSetQuotes' , function ( ) {
it ( 'returns null if fetchParams is not provided' , async function ( ) {
const quotes = await swapsController . fetchAndSetQuotes ( undefined )
@ -279,7 +273,7 @@ describe('SwapsController', function () {
} )
it ( 'calls fetchTradesInfo with the given fetchParams and returns the correct quotes' , async function ( ) {
fetchTradesInfoStub . resolves ( MOCK _QUOTES )
fetchTradesInfoStub . resolves ( getMockQuotes ( ) )
// Make it so approval is not required
sandbox
@ -291,8 +285,8 @@ describe('SwapsController', function () {
MOCK _FETCH _METADATA ,
)
assert . deepStrictEqual ( newQuotes [ TEST _AGG _ID ] , {
... MOCK _QUOTES [ TEST _AGG _ID ] ,
assert . deepStrictEqual ( newQuotes [ TEST _AGG _ID _BEST ] , {
... getMockQuotes ( ) [ TEST _AGG _ID _BEST ] ,
sourceTokenInfo : undefined ,
destinationTokenInfo : {
symbol : 'FOO' ,
@ -301,7 +295,12 @@ describe('SwapsController', function () {
isBestQuote : true ,
// TODO: find a way to calculate these values dynamically
gasEstimate : 2000000 ,
gasEstimateWithRefund : '8cd8e' ,
gasEstimateWithRefund : 'b8cae' ,
savings : {
fee : '0' ,
performance : '6' ,
total : '6' ,
} ,
} )
assert . strictEqual (
@ -311,7 +310,7 @@ describe('SwapsController', function () {
} )
it ( 'performs the allowance check' , async function ( ) {
fetchTradesInfoStub . resolves ( MOCK _QUOTES )
fetchTradesInfoStub . resolves ( getMockQuotes ( ) )
// Make it so approval is not required
const allowanceStub = sandbox
@ -358,7 +357,7 @@ describe('SwapsController', function () {
} )
it ( 'marks the best quote' , async function ( ) {
fetchTradesInfoStub . resolves ( MOCK _QUOTES )
fetchTradesInfoStub . resolves ( getMockQuotes ( ) )
// Make it so approval is not required
sandbox
@ -370,7 +369,7 @@ describe('SwapsController', function () {
MOCK _FETCH _METADATA ,
)
assert . strictEqual ( topAggId , TEST _AGG _ID )
assert . strictEqual ( topAggId , TEST _AGG _ID _BEST )
assert . strictEqual ( newQuotes [ topAggId ] . isBestQuote , true )
} )
@ -379,15 +378,15 @@ describe('SwapsController', function () {
// Clone the existing mock quote and increase destination amount
const bestQuote = {
... MOCK _QUOTES [ TEST _AGG _ID ] ,
... getMockQuotes ( ) [ TEST _AGG _ID _1 ] ,
aggregator : bestAggId ,
destinationAmount : ethers . BigNumber . from (
MOCK _QUOTES [ TEST _AGG _ID ] . destinationAmount ,
getMockQuotes ( ) [ TEST _AGG _ID _1 ] . destinationAmount ,
)
. add ( 1 )
. add ( ( 100e18 ) . toString ( ) )
. toString ( ) ,
}
const quotes = { ... MOCK _QUOTES , [ bestAggId ] : bestQuote }
const quotes = { ... getMockQuotes ( ) , [ bestAggId ] : bestQuote }
fetchTradesInfoStub . resolves ( quotes )
// Make it so approval is not required
@ -405,7 +404,7 @@ describe('SwapsController', function () {
} )
it ( 'does not mark as best quote if no conversion rate exists for destination token' , async function ( ) {
fetchTradesInfoStub . resolves ( MOCK _QUOTES )
fetchTradesInfoStub . resolves ( getMockQuotes ( ) )
// Make it so approval is not required
sandbox
@ -762,4 +761,150 @@ describe('SwapsController', function () {
} )
} )
} )
describe ( 'utils' , function ( ) {
describe ( 'getMedian' , function ( ) {
const { getMedian } = utils
it ( 'calculates median correctly with uneven sample' , function ( ) {
const values = [ 3 , 2 , 6 ] . map ( ( value ) => new BigNumber ( value ) )
const median = getMedian ( values )
assert . strictEqual (
median . toNumber ( ) , 3 ,
'should have returned correct median' ,
)
} )
it ( 'calculates median correctly with even sample' , function ( ) {
const values = [ 3 , 2 , 2 , 6 ] . map ( ( value ) => new BigNumber ( value ) )
const median = getMedian ( values )
assert . strictEqual (
median . toNumber ( ) , 2.5 ,
'should have returned correct median' ,
)
} )
it ( 'throws on empty or non-array sample' , function ( ) {
assert . throws (
( ) => getMedian ( [ ] ) ,
'should throw on empty array' ,
)
assert . throws (
( ) => getMedian ( ) ,
'should throw on non-array param' ,
)
assert . throws (
( ) => getMedian ( { } ) ,
'should throw on non-array param' ,
)
} )
} )
} )
} )
function getMockQuotes ( ) {
return {
[ TEST _AGG _ID _1 ] : {
'trade' : {
'from' : '0xe18035bf8712672935fdb4e5e431b1a0183d2dfc' ,
'value' : '0x0' ,
'gas' : '0x61a80' , // 4e5
'to' : '0x881D40237659C251811CEC9c364ef91dC08D300C' ,
} ,
'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' ,
} ,
'destinationTokenInfo' : {
'address' : '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' ,
'symbol' : 'USDC' ,
'decimals' : 18 ,
} ,
} ,
[ TEST _AGG _ID _BEST ] : {
'trade' : {
'from' : '0xe18035bf8712672935fdb4e5e431b1a0183d2dfc' ,
'value' : '0x0' ,
'gas' : '0x61a80' ,
'to' : '0x881D40237659C251811CEC9c364ef91dC08D300C' ,
} ,
'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' ,
} ,
'destinationTokenInfo' : {
'address' : '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' ,
'symbol' : 'USDC' ,
'decimals' : 18 ,
} ,
} ,
[ TEST _AGG _ID _2 ] : {
'trade' : {
'from' : '0xe18035bf8712672935fdb4e5e431b1a0183d2dfc' ,
'value' : '0x0' ,
'gas' : '0x61a80' ,
'to' : '0x881D40237659C251811CEC9c364ef91dC08D300C' ,
} ,
'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' ,
} ,
'destinationTokenInfo' : {
'address' : '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' ,
'symbol' : 'USDC' ,
'decimals' : 18 ,
} ,
} ,
}
}