import chai , { expect } from 'chai' ; import { Contract } from 'ethers' ; import { MaxUint256 } from 'ethers/constants' ; import { BigNumber , bigNumberify , defaultAbiCoder , formatEther } from 'ethers/utils' ; import { solidity , MockProvider , createFixtureLoader , deployContract } from 'ethereum-waffle' ; import { expandTo18Decimals } from './shared/utilities' ; import { v2Fixture } from './shared/fixtures' ; import ExampleFlashSwap from '../build/ExampleFlashSwap.json' ; chai . use ( solidity ) ; const overrides = { gasLimit : 9999999 , gasPrice : 0 } ; describe ( 'ExampleFlashSwap' , ( ) => { const provider = new MockProvider ( { hardfork : 'istanbul' , mnemonic : 'horn horn horn horn horn horn horn horn horn horn horn horn' , gasLimit : 9999999 } ) ; const [ wallet ] = provider . getWallets ( ) ; const loadFixture = createFixtureLoader ( provider , [ wallet ] ) ; let WETH ; let WETHPartner ; let WETHExchangeV1 ; let WETHPair ; let flashSwapExample ; beforeEach ( async function ( ) { const fixture = await loadFixture ( v2Fixture ) ; WETH = fixture . WETH ; WETHPartner = fixture . WETHPartner ; WETHExchangeV1 = fixture . WETHExchangeV1 ; WETHPair = fixture . WETHPair ; flashSwapExample = await deployContract ( wallet , ExampleFlashSwap , [ fixture . factoryV2 . address , fixture . factoryV1 . address , fixture . router . address ] , overrides ) ; } ) ; it ( 'uniswapV2Call:0' , async ( ) => { const WETHPartnerAmountV1 = expandTo18Decimals ( 2000 ) ; const ETHAmountV1 = expandTo18Decimals ( 10 ) ; await WETHPartner . approve ( WETHExchangeV1 . address , WETHPartnerAmountV1 ) ; await WETHExchangeV1 . addLiquidity ( bigNumberify ( 1 ) , WETHPartnerAmountV1 , MaxUint256 , { ... overrides , value : ETHAmountV1 } ) ; const WETHPartnerAmountV2 = expandTo18Decimals ( 1000 ) ; const ETHAmountV2 = expandTo18Decimals ( 10 ) ; await WETHPartner . transfer ( WETHPair . address , WETHPartnerAmountV2 ) ; await WETH . deposit ( { value : ETHAmountV2 } ) ; await WETH . transfer ( WETHPair . address , ETHAmountV2 ) ; await WETHPair . mint ( wallet . address , overrides ) ; const balanceBefore = await WETHPartner . balanceOf ( wallet . address ) ; const arbitrageAmount = expandTo18Decimals ( 1 ) ; const WETHPairToken0 = await WETHPair . token0 ( ) ; const amount0 = WETHPairToken0 === WETHPartner . address ? bigNumberify ( 0 ) : arbitrageAmount ; const amount1 = WETHPairToken0 === WETHPartner . address ? arbitrageAmount : bigNumberify ( 0 ) ; await WETHPair . swap ( amount0 , amount1 , flashSwapExample . address , defaultAbiCoder . encode ( [ 'uint' ] , [ bigNumberify ( 1 ) ] ) , overrides ) ; const balanceAfter = await WETHPartner . balanceOf ( wallet . address ) ; const profit = balanceAfter . sub ( balanceBefore ) . div ( expandTo18Decimals ( 1 ) ) ; const reservesV1 = [ await WETHPartner . balanceOf ( WETHExchangeV1 . address ) , await provider . getBalance ( WETHExchangeV1 . address ) ] ; const priceV1 = reservesV1 [ 0 ] . div ( reservesV1 [ 1 ] ) ; const reservesV2 = ( await WETHPair . getReserves ( ) ) . slice ( 0 , 2 ) ; const priceV2 = WETHPairToken0 === WETHPartner . address ? reservesV2 [ 0 ] . div ( reservesV2 [ 1 ] ) : reservesV2 [ 1 ] . div ( reservesV2 [ 0 ] ) ; expect ( profit . toString ( ) ) . to . eq ( '69' ) ; expect ( priceV1 . toString ( ) ) . to . eq ( '165' ) ; expect ( priceV2 . toString ( ) ) . to . eq ( '123' ) ; } ) ; it ( 'uniswapV2Call:1' , async ( ) => { const WETHPartnerAmountV1 = expandTo18Decimals ( 1000 ) ; const ETHAmountV1 = expandTo18Decimals ( 10 ) ; await WETHPartner . approve ( WETHExchangeV1 . address , WETHPartnerAmountV1 ) ; await WETHExchangeV1 . addLiquidity ( bigNumberify ( 1 ) , WETHPartnerAmountV1 , MaxUint256 , { ... overrides , value : ETHAmountV1 } ) ; const WETHPartnerAmountV2 = expandTo18Decimals ( 2000 ) ; const ETHAmountV2 = expandTo18Decimals ( 10 ) ; await WETHPartner . transfer ( WETHPair . address , WETHPartnerAmountV2 ) ; await WETH . deposit ( { value : ETHAmountV2 } ) ; await WETH . transfer ( WETHPair . address , ETHAmountV2 ) ; await WETHPair . mint ( wallet . address , overrides ) ; const balanceBefore = await provider . getBalance ( wallet . address ) ; const arbitrageAmount = expandTo18Decimals ( 200 ) ; const WETHPairToken0 = await WETHPair . token0 ( ) ; const amount0 = WETHPairToken0 === WETHPartner . address ? arbitrageAmount : bigNumberify ( 0 ) ; const amount1 = WETHPairToken0 === WETHPartner . address ? bigNumberify ( 0 ) : arbitrageAmount ; await WETHPair . swap ( amount0 , amount1 , flashSwapExample . address , defaultAbiCoder . encode ( [ 'uint' ] , [ bigNumberify ( 1 ) ] ) , overrides ) ; const balanceAfter = await provider . getBalance ( wallet . address ) ; const profit = balanceAfter . sub ( balanceBefore ) ; const reservesV1 = [ await WETHPartner . balanceOf ( WETHExchangeV1 . address ) , await provider . getBalance ( WETHExchangeV1 . address ) ] ; const priceV1 = reservesV1 [ 0 ] . div ( reservesV1 [ 1 ] ) ; const reservesV2 = ( await WETHPair . getReserves ( ) ) . slice ( 0 , 2 ) ; const priceV2 = WETHPairToken0 === WETHPartner . address ? reservesV2 [ 0 ] . div ( reservesV2 [ 1 ] ) : reservesV2 [ 1 ] . div ( reservesV2 [ 0 ] ) ; expect ( formatEther ( profit ) ) . to . eq ( '0.548043441089763649' ) ; expect ( priceV1 . toString ( ) ) . to . eq ( '143' ) ; expect ( priceV2 . toString ( ) ) . to . eq ( '161' ) ; } ) ; } ) ;