diff --git a/constants/bsc.ts b/constants/bsc.ts index 3515847a..1385c3b3 100644 --- a/constants/bsc.ts +++ b/constants/bsc.ts @@ -1,6 +1,6 @@ import { ERC20Map, PoolMap, RouterMap } from "./addresses"; -export const BSC_WHALE = "0x5c9F6cA2e81EaEad3D9aFf26efca7Fa307403297"; +export const BSC_WHALE = "0xB224228b0Fe71ceBf95EE25339166CD626759b52"; export const bscTokens: ERC20Map = { DAI: "0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3", diff --git a/contracts/Flashloan.sol b/contracts/Flashloan.sol index 0ca1dab1..5136b1cb 100644 --- a/contracts/Flashloan.sol +++ b/contracts/Flashloan.sol @@ -35,11 +35,10 @@ contract Flashloan is IFlashloan, DodoBase, FlashloanValidation, Withdraw { me: msg.sender, flashLoanPool: params.flashLoanPool, loanAmount: params.loanAmount, - firstRoutes: params.firstRoutes, - secondRoutes: params.secondRoutes + routes: params.routes }) ); - address loanToken = RouteUtils.getInitialToken(params.firstRoutes[0]); + address loanToken = RouteUtils.getInitialToken(params.routes[0]); IDODO(params.flashLoanPool).flashLoan( IDODO(params.flashLoanPool)._BASE_TOKEN_() == loanToken ? params.loanAmount @@ -63,17 +62,14 @@ contract Flashloan is IFlashloan, DodoBase, FlashloanValidation, Withdraw { (FlashCallbackData) ); - address loanToken = RouteUtils.getInitialToken(decoded.firstRoutes[0]); - address toToken = RouteUtils.getInitialToken(decoded.secondRoutes[0]); + address loanToken = RouteUtils.getInitialToken(decoded.routes[0]); require( IERC20(loanToken).balanceOf(address(this)) >= decoded.loanAmount, "Failed to borrow loan token" ); - routeLoop(decoded.firstRoutes, decoded.loanAmount); - uint256 toTokenAmount = IERC20(toToken).balanceOf(address(this)); - routeLoop(decoded.secondRoutes, toTokenAmount); + routeLoop(decoded.routes, decoded.loanAmount); emit SwapFinished( loanToken, @@ -106,37 +102,20 @@ contract Flashloan is IFlashloan, DodoBase, FlashloanValidation, Withdraw { function hopLoop(Route memory route, uint256 totalAmount) internal { uint256 amountIn = totalAmount; for (uint256 i = 0; i < route.hops.length; i++) { - amountIn = swapLoop(route.hops[i], amountIn); + amountIn = pickProtocol(route.hops[i], amountIn); } } - function swapLoop(Hop memory hop, uint256 totalAmount) + function pickProtocol(Hop memory hop, uint256 amountIn) internal - checkTotalSwapPart(hop.swaps) - returns (uint256) + returns (uint256 amountOut) { - uint256 amountOut = 0; - for (uint256 i = 0; i < hop.swaps.length; i++) { - uint256 amountIn = Part.partToAmountIn( - hop.swaps[i].part, - totalAmount - ); - amountOut += pickProtocol(hop.swaps[i], amountIn, hop.path); - } - return amountOut; - } - - function pickProtocol( - Swap memory swap, - uint256 amountIn, - address[] memory path - ) internal checkRouteProtocol(swap) returns (uint256 amountOut) { - if (swap.protocol == 0) { - amountOut = uniswapV3(swap.data, amountIn, path); - } else if (swap.protocol < 8) { - amountOut = uniswapV2(swap.data, amountIn, path); + if (hop.protocol == 0) { + amountOut = uniswapV3(hop.data, amountIn, hop.path); + } else if (hop.protocol < 8) { + amountOut = uniswapV2(hop.data, amountIn, hop.path); } else { - amountOut = dodoV2Swap(swap.data, amountIn, path); + amountOut = dodoV2Swap(hop.data, amountIn, hop.path); } } diff --git a/contracts/base/DodoBase.sol b/contracts/base/DodoBase.sol index 23914551..9dedf508 100644 --- a/contracts/base/DodoBase.sol +++ b/contracts/base/DodoBase.sol @@ -44,10 +44,12 @@ contract DodoBase is IFlashloan { ) internal virtual {} modifier checkParams(FlashParams memory params) { - address loanToken = RouteUtils.getInitialToken(params.firstRoutes[0]); - bool loanEqBase = loanToken == IDODO(params.flashLoanPool)._BASE_TOKEN_(); - bool loanEqQuote = loanToken == IDODO(params.flashLoanPool)._QUOTE_TOKEN_(); + address loanToken = RouteUtils.getInitialToken(params.routes[0]); + bool loanEqBase = loanToken == + IDODO(params.flashLoanPool)._BASE_TOKEN_(); + bool loanEqQuote = loanToken == + IDODO(params.flashLoanPool)._QUOTE_TOKEN_(); require(loanEqBase || loanEqQuote, "Wrong flashloan pool address"); - _; - } -} \ No newline at end of file + _; + } +} diff --git a/contracts/base/FlashloanValidation.sol b/contracts/base/FlashloanValidation.sol index 3b30d28f..ce8dbb84 100644 --- a/contracts/base/FlashloanValidation.sol +++ b/contracts/base/FlashloanValidation.sol @@ -14,18 +14,4 @@ abstract contract FlashloanValidation { require(totalPart == 10000, "Route part error"); _; } - - modifier checkTotalSwapPart(IFlashloan.Swap[] memory swaps) { - uint16 totalPart = 0; - for (uint256 i = 0; i < uint256(swaps.length); i++) { - totalPart += swaps[i].part; - } - require(totalPart == 10000, "Swap part error"); - _; - } - - modifier checkRouteProtocol(IFlashloan.Swap memory swap) { - require(MAX_PROTOCOL >= swap.protocol, "Wrong protocol"); - _; - } } diff --git a/contracts/interfaces/IFlashloan.sol b/contracts/interfaces/IFlashloan.sol index 6672fae9..b45d9d37 100644 --- a/contracts/interfaces/IFlashloan.sol +++ b/contracts/interfaces/IFlashloan.sol @@ -2,14 +2,9 @@ pragma solidity ^0.8.0; interface IFlashloan { - struct Swap { + struct Hop { uint8 protocol; - uint16 part; bytes data; - } - - struct Hop { - Swap[] swaps; address[] path; } @@ -21,15 +16,13 @@ interface IFlashloan { struct FlashParams { address flashLoanPool; uint256 loanAmount; - Route[] firstRoutes; - Route[] secondRoutes; + Route[] routes; } struct FlashCallbackData { address me; address flashLoanPool; uint256 loanAmount; - Route[] firstRoutes; - Route[] secondRoutes; + Route[] routes; } } diff --git a/test/bsc/flashloan.test.ts b/test/bsc/flashloan.test.ts index 2c359889..f47f4fc3 100644 --- a/test/bsc/flashloan.test.ts +++ b/test/bsc/flashloan.test.ts @@ -50,42 +50,25 @@ describe("Flashloan on BSC", () => { { flashLoanPool: dodoV2Pool.ETH_BUSD, loanAmount: getBigNumber(1000), - firstRoutes: [ + routes: [ { hops: [ { - swaps: [ - { - // sushiswap - protocol: 1, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [uniswapRouter.BSC_SUSHISWAP] - ), - }, - ], + // sushiswap + protocol: 1, + data: ethers.utils.defaultAbiCoder.encode( + ["address"], + [uniswapRouter.BSC_SUSHISWAP] + ), path: [bscTokens.BUSD, bscTokens.USDC], }, - ], - part: 10000, - }, - ], - secondRoutes: [ - { - hops: [ { - swaps: [ - { - // pancakeswap - protocol: 1, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [uniswapRouter.BSC_PANCAKESWAP] - ), - }, - ], + // pancakeswap + protocol: 1, + data: ethers.utils.defaultAbiCoder.encode( + ["address"], + [uniswapRouter.BSC_PANCAKESWAP] + ), path: [bscTokens.USDC, bscTokens.BUSD], }, ], @@ -101,180 +84,5 @@ describe("Flashloan on BSC", () => { const balance = await BUSD.balanceOf(owner.address); expect(balance.gt(getBigNumber(0))).to.be.true; }); - - // it("USDC - WETH - WBTC", async () => { - // await expect( - // Flashloan.dodoFlashLoan( - // { - // flashLoanPool: dodoV2Pool.WETH_USDC, - // loanAmount: getBigNumber(1000, 6), - // firstRoutes: [ - // { - // hops: [ - // { - // swaps: [ - // { - // protocol: 1, - // part: 10000, - // data: ethers.utils.defaultAbiCoder.encode( - // ["address"], - // [findRouterFromProtocol(1)] - // ), - // }, - // ], - // path: [ERC20Token.USDC.address, ERC20Token.WETH.address], - // }, - // { - // swaps: [ - // { - // protocol: 2, - // part: 10000, - // data: ethers.utils.defaultAbiCoder.encode( - // ["address"], - // [findRouterFromProtocol(1)] - // ), - // }, - // ], - // path: [ERC20Token.WETH.address, ERC20Token.WBTC.address], - // }, - // ], - // part: 10000, - // }, - // ], - // secondRoutes: [ - // { - // hops: [ - // { - // swaps: [ - // { - // protocol: 3, - // part: 10000, - // data: ethers.utils.defaultAbiCoder.encode( - // ["address"], - // [findRouterFromProtocol(3)] - // ), - // }, - // ], - // path: [ERC20Token.WBTC.address, ERC20Token.WETH.address], - // }, - // { - // swaps: [ - // { - // protocol: 3, - // part: 10000, - // data: ethers.utils.defaultAbiCoder.encode( - // ["address"], - // [findRouterFromProtocol(3)] - // ), - // }, - // ], - // path: [ERC20Token.WETH.address, ERC20Token.USDC.address], - // }, - // ], - // part: 10000, - // }, - // ], - // }, - // { gasLimit: 1000000 } - // ) - // ) - // .emit(Flashloan, "SwapFinished") - // .emit(Flashloan, "SentProfit"); - // const balance = await USDC.balanceOf(owner.address); - // expect(balance.gt(getBigNumber(0))).to.be.true; - // }); - - // it("should execute a flashloan with multihop swaps.", async () => { - // await expect( - // Flashloan.dodoFlashLoan( - // { - // flashLoanPool: dodoV2Pool.WETH_USDC, - // loanAmount: getBigNumber(1000, 6), - // firstRoutes: [ - // { - // hops: [ - // { - // swaps: [ - // { - // protocol: 1, - // part: 5000, - // data: ethers.utils.defaultAbiCoder.encode( - // ["address"], - // [findRouterFromProtocol(4)] - // ), - // }, - // { - // protocol: 2, - // part: 5000, - // data: ethers.utils.defaultAbiCoder.encode( - // ["address"], - // [findRouterFromProtocol(5)] - // ), - // }, - // ], - // path: [bscTokens.USDC, bscTokens.DAI], - // }, - // { - // swaps: [ - // { - // protocol: 2, - // part: 9000, - // data: ethers.utils.defaultAbiCoder.encode( - // ["address"], - // [findRouterFromProtocol(1)] - // ), - // }, - // { - // protocol: 3, - // part: 1000, - // data: ethers.utils.defaultAbiCoder.encode( - // ["address"], - // [findRouterFromProtocol(2)] - // ), - // }, - // ], - // path: [bscTokens.DAI, bscTokens.WETH], - // }, - // ], - // part: 10000, - // }, - // ], - // secondRoutes: [ - // { - // hops: [ - // { - // swaps: [ - // { - // protocol: 1, - // part: 10000, - // data: ethers.utils.defaultAbiCoder.encode( - // ["address"], - // [findRouterFromProtocol(3)] - // ), - // }, - // ], - // path: [bscTokens.WETH, bscTokens.USDC], - // }, - // ], - // part: 10000, - // }, - // ], - // }, - // { gasLimit: 1000000 } - // ) - // ) - // .emit(Flashloan, "SwapFinished") - // .emit(Flashloan, "SentProfit"); - // const balance = await USDC.balanceOf(owner.address); - // expect(balance.gt(getBigNumber(0))).to.be.true; - // expect( - // (await DAI.balanceOf(Flashloan.address)).lt(ethers.BigNumber.from(1000)) - // ).to.be.true; - // expect( - // (await WETH.balanceOf(Flashloan.address)).lt( - // ethers.BigNumber.from(1000) - // ) - // ).to.be.true; - // }); }); }); diff --git a/test/polygon/errorMsg.test.ts b/test/polygon/errorMsg.test.ts index 57d5fb98..868e0d37 100644 --- a/test/polygon/errorMsg.test.ts +++ b/test/polygon/errorMsg.test.ts @@ -1,7 +1,8 @@ import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { expect } from "chai"; import { ethers } from "hardhat"; -import { dodoV2Pool, erc20Address } from "../../constants/addresses"; +import { dodoV2Pool } from "../../constants/addresses"; +import { ERC20Token } from "../../constants/token"; import { ERC20Mock, Flashloan, Flashloan__factory } from "../../typechain"; import { deployContractFromName, @@ -24,11 +25,11 @@ describe("Flashloan Error Message", () => { const loanAmount = ethers.BigNumber.from(1000); before(async () => { - USDC = await getERC20ContractFromAddress(erc20Address.USDC); - USDT = await getERC20ContractFromAddress(erc20Address.USDT); - DAI = await getERC20ContractFromAddress(erc20Address.DAI); - WETH = await getERC20ContractFromAddress(erc20Address.WETH); - WMATIC = await getERC20ContractFromAddress(erc20Address.WMATIC); + USDC = await getERC20ContractFromAddress(ERC20Token.USDC.address); + USDT = await getERC20ContractFromAddress(ERC20Token.USDT.address); + DAI = await getERC20ContractFromAddress(ERC20Token.DAI.address); + WETH = await getERC20ContractFromAddress(ERC20Token.WETH.address); + WMATIC = await getERC20ContractFromAddress(ERC20Token.WMATIC.address); }); beforeEach(async () => { @@ -45,41 +46,24 @@ describe("Flashloan Error Message", () => { { flashLoanPool: dodoV2Pool.WMATIC_WETH, loanAmount: getBigNumber(1, 6), - firstRoutes: [ + routes: [ { hops: [ { - swaps: [ - { - protocol: 1, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(1)] - ), - }, - ], - path: [erc20Address.USDC, erc20Address.WETH], + protocol: 1, + data: ethers.utils.defaultAbiCoder.encode( + ["address"], + [findRouterFromProtocol(1)] + ), + path: [ERC20Token.USDC.address, ERC20Token.WETH.address], }, - ], - part: 10000, - }, - ], - secondRoutes: [ - { - hops: [ { - swaps: [ - { - protocol: 1, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(1)] - ), - }, - ], - path: [erc20Address.WETH, erc20Address.USDC], + protocol: 1, + data: ethers.utils.defaultAbiCoder.encode( + ["address"], + [findRouterFromProtocol(1)] + ), + path: [ERC20Token.WETH.address, ERC20Token.USDC.address], }, ], part: 10000, @@ -90,178 +74,6 @@ describe("Flashloan Error Message", () => { ) ).to.be.revertedWith("Wrong flashloan pool address"); }); - - it("should revert flashloan with the total route parts error.", async () => { - await expect( - Flashloan.dodoFlashLoan( - { - flashLoanPool: dodoV2Pool.WMATIC_WETH, - loanAmount: getBigNumber(1, 6), - firstRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 1, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(1)] - ), - }, - ], - path: [erc20Address.WMATIC, erc20Address.DAI], - }, - ], - part: 10000, - }, - { - hops: [ - { - swaps: [ - { - protocol: 1, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(1)] - ), - }, - ], - path: [erc20Address.DAI, erc20Address.USDC], - }, - ], - part: 10000, - }, - ], - secondRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 1, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(1)] - ), - }, - ], - path: [erc20Address.USDC, erc20Address.WMATIC], - }, - ], - part: 10000, - }, - ], - }, - { gasLimit: 1000000 } - ) - ).to.be.revertedWith("Route part error"); - }); - - it("should revert flashloan with the total route parts error.", async () => { - await expect( - Flashloan.dodoFlashLoan( - { - flashLoanPool: dodoV2Pool.WMATIC_WETH, - loanAmount: getBigNumber(1, 6), - firstRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 1, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(1)] - ), - }, - { - protocol: 2, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(2)] - ), - }, - ], - path: [erc20Address.WMATIC, erc20Address.DAI], - }, - ], - part: 10000, - }, - ], - secondRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 1, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(1)] - ), - }, - ], - path: [erc20Address.DAI, erc20Address.WMATIC], - }, - ], - part: 10000, - }, - ], - }, - { gasLimit: 1000000 } - ) - ).to.be.revertedWith("Swap part error"); - }); - - // it("should revert flashloan when you borrow and swap tokens from the same pool.", async () => { - // await expect( - // Flashloan.dodoFlashLoan({ - // flashLoanPool: dodoV2Pool.WMATIC_USDC, - // loanAmount: getBigNumber(1, 6), - // firstRoutes: [{ - // path: [erc20Address.USDC, erc20Address.WMATIC], - // pool: uniswapRouter.POLYGON_QUICKSWAP, - // protocol: 1, - // fee: [] - // }], - // secondRoutes: [{ - // path: [erc20Address.WMATIC, erc20Address.USDC], - // pool: dodoV2Pool.WMATIC_USDC, - // protocol: 0, - // fee: [] - // }], - // }, { gasLimit: 1000000 }) - // ).to.be.revertedWith("REENTRANT"); - // }); - - // it("should revert flashloan when the dodo pool address is wrong.", async () => { - // await expect( - // Flashloan.dodoFlashLoan({ - // flashLoanPool: dodoV2Pool.USDT_DAI, - // loanAmount: getBigNumber(1, 6), - // firstRoutes: [{ - // path: [erc20Address.USDT, erc20Address.WETH], - // pool: dodoV2Pool.WMATIC_WETH, - // protocol: 0, - // fee: [] - // }], - // secondRoutes: [{ - // path: [erc20Address.WETH, erc20Address.DAI], - // pool: uniswapRouter.POLYGON_SUSHISWAP, - // protocol: 1, - // fee: [] - // }] - // }, { gasLimit: 1000000 }) - // ).to.be.revertedWith("Wrong dodo V2 pool address"); - // }); }); it("should revert flashloan when it cannot pay back the loan.", async () => { @@ -270,41 +82,24 @@ describe("Flashloan Error Message", () => { { flashLoanPool: dodoV2Pool.WETH_USDC, loanAmount: getBigNumber(1000, 6), - firstRoutes: [ + routes: [ { hops: [ { - swaps: [ - { - protocol: 1, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(1)] - ), - }, - ], - path: [erc20Address.USDC, erc20Address.WETH], + protocol: 1, + data: ethers.utils.defaultAbiCoder.encode( + ["address"], + [findRouterFromProtocol(1)] + ), + path: [ERC20Token.USDC.address, ERC20Token.WETH.address], }, - ], - part: 10000, - }, - ], - secondRoutes: [ - { - hops: [ { - swaps: [ - { - protocol: 1, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(1)] - ), - }, - ], - path: [erc20Address.WETH, erc20Address.USDC], + protocol: 1, + data: ethers.utils.defaultAbiCoder.encode( + ["address"], + [findRouterFromProtocol(1)] + ), + path: [ERC20Token.WETH.address, ERC20Token.USDC.address], }, ], part: 10000, @@ -315,56 +110,4 @@ describe("Flashloan Error Message", () => { ) ).to.be.revertedWith("Not enough amount to return loan"); }); - - it("should be reverted when you input a wrong protocol number.", async () => { - await expect( - Flashloan.dodoFlashLoan( - { - flashLoanPool: dodoV2Pool.WETH_USDC, - loanAmount: loanAmount, - firstRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 28, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(1)] - ), - }, - ], - path: [erc20Address.USDC, erc20Address.WETH], - }, - ], - part: 10000, - }, - ], - secondRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 1, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(1)] - ), - }, - ], - path: [erc20Address.WETH, erc20Address.USDC], - }, - ], - part: 10000, - }, - ], - }, - { gasLimit: 1000000 } - ) - ).to.be.revertedWith("Wrong protocol"); - }); }); diff --git a/test/polygon/flashloan.test.ts b/test/polygon/flashloan.test.ts deleted file mode 100644 index cd1b5db5..00000000 --- a/test/polygon/flashloan.test.ts +++ /dev/null @@ -1,728 +0,0 @@ -import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; -import { expect } from "chai"; -import { ethers } from "hardhat"; -import { - DODOApprove, - dodoV2Pool, - DODOV2Proxy, - erc20Address, - USDC_WHALE, -} from "../../constants/addresses"; -import { ERC20Token } from "../../constants/token"; -import { ERC20Mock, Flashloan, Flashloan__factory } from "../../typechain"; -import { - deployContractFromName, - findRouterFromProtocol, - getBigNumber, - getERC20ContractFromAddress, -} from "../../utils"; -import { impersonateFundErc20 } from "../../utils/token"; - -describe("Flashloan", () => { - let Flashloan: Flashloan; - let owner: SignerWithAddress; - let addr1: SignerWithAddress; - let addr2: SignerWithAddress; - let addrs: SignerWithAddress[]; - let DAI: ERC20Mock; - let USDC: ERC20Mock; - let WMATIC: ERC20Mock; - let WETH: ERC20Mock; - let USDT: ERC20Mock; - - before(async () => { - USDC = await getERC20ContractFromAddress(erc20Address.USDC); - USDT = await getERC20ContractFromAddress(erc20Address.USDT); - DAI = await getERC20ContractFromAddress(erc20Address.DAI); - WETH = await getERC20ContractFromAddress(erc20Address.WETH); - WMATIC = await getERC20ContractFromAddress(erc20Address.WMATIC); - }); - - beforeEach(async () => { - [owner, addr1, addr2, ...addrs] = await ethers.getSigners(); - Flashloan = await deployContractFromName("Flashloan", Flashloan__factory); - await Flashloan.deployed(); - await impersonateFundErc20( - USDC, - USDC_WHALE, - Flashloan.address, - "1000.0", - 6 - ); - }); - - describe("UniswapV2", () => { - it("USDC - WETH", async () => { - await expect( - Flashloan.dodoFlashLoan( - { - flashLoanPool: dodoV2Pool.WETH_USDC, - loanAmount: getBigNumber(1000, 6), - firstRoutes: [ - { - hops: [ - { - swaps: [ - { - // sushiswap - protocol: 1, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(1)] - ), - }, - ], - path: [erc20Address.USDC, erc20Address.WETH], - }, - ], - part: 10000, - }, - ], - secondRoutes: [ - { - hops: [ - { - swaps: [ - { - // sushiswap - protocol: 1, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(1)] - ), - }, - ], - path: [erc20Address.WETH, erc20Address.USDC], - }, - ], - part: 10000, - }, - ], - }, - { gasLimit: 1000000 } - ) - ) - .emit(Flashloan, "SwapFinished") - .emit(Flashloan, "SentProfit"); - const balance = await USDC.balanceOf(owner.address); - expect(balance.gt(getBigNumber(0))).to.be.true; - }); - - it("USDC - WETH - WBTC", async () => { - await expect( - Flashloan.dodoFlashLoan( - { - flashLoanPool: dodoV2Pool.WETH_USDC, - loanAmount: getBigNumber(1000, 6), - firstRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 1, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(1)] - ), - }, - ], - path: [ERC20Token.USDC.address, ERC20Token.WETH.address], - }, - { - swaps: [ - { - protocol: 2, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(1)] - ), - }, - ], - path: [ERC20Token.WETH.address, ERC20Token.WBTC.address], - }, - ], - part: 10000, - }, - ], - secondRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 3, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(3)] - ), - }, - ], - path: [ERC20Token.WBTC.address, ERC20Token.WETH.address], - }, - { - swaps: [ - { - protocol: 3, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(3)] - ), - }, - ], - path: [ERC20Token.WETH.address, ERC20Token.USDC.address], - }, - ], - part: 10000, - }, - ], - }, - { gasLimit: 1000000 } - ) - ) - .emit(Flashloan, "SwapFinished") - .emit(Flashloan, "SentProfit"); - const balance = await USDC.balanceOf(owner.address); - expect(balance.gt(getBigNumber(0))).to.be.true; - }); - - it("should execute a flashloan with multihop swaps.", async () => { - await expect( - Flashloan.dodoFlashLoan( - { - flashLoanPool: dodoV2Pool.WETH_USDC, - loanAmount: getBigNumber(1000, 6), - firstRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 1, - part: 5000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(4)] - ), - }, - { - protocol: 2, - part: 5000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(5)] - ), - }, - ], - path: [erc20Address.USDC, erc20Address.DAI], - }, - { - swaps: [ - { - protocol: 2, - part: 9000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(1)] - ), - }, - { - protocol: 3, - part: 1000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(2)] - ), - }, - ], - path: [erc20Address.DAI, erc20Address.WETH], - }, - ], - part: 10000, - }, - ], - secondRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 1, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(3)] - ), - }, - ], - path: [erc20Address.WETH, erc20Address.USDC], - }, - ], - part: 10000, - }, - ], - }, - { gasLimit: 1000000 } - ) - ) - .emit(Flashloan, "SwapFinished") - .emit(Flashloan, "SentProfit"); - const balance = await USDC.balanceOf(owner.address); - expect(balance.gt(getBigNumber(0))).to.be.true; - expect( - (await DAI.balanceOf(Flashloan.address)).lt(ethers.BigNumber.from(1000)) - ).to.be.true; - expect( - (await WETH.balanceOf(Flashloan.address)).lt( - ethers.BigNumber.from(1000) - ) - ).to.be.true; - }); - }); - - describe("UniswapV3", () => { - it("should execute uniswapV3 flashloan.", async () => { - await expect( - Flashloan.dodoFlashLoan( - { - flashLoanPool: dodoV2Pool.WETH_USDC, - loanAmount: getBigNumber(1000, 6), - firstRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 0, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address", "uint24"], - // 0.05 % => 500 - [findRouterFromProtocol(0), 500] - ), - }, - ], - path: [erc20Address.USDC, erc20Address.DAI], - }, - ], - part: 10000, - }, - ], - secondRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 0, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address", "uint24"], - [findRouterFromProtocol(0), 500] - ), - }, - ], - path: [erc20Address.DAI, erc20Address.USDC], - }, - ], - part: 10000, - }, - ], - }, - { gasLimit: 1000000 } - ) - ) - .emit(Flashloan, "SwapFinished") - .emit(Flashloan, "SentProfit"); - const balance = await USDC.balanceOf(owner.address); - expect(balance.gt(getBigNumber(0))).to.be.true; - }); - - it("USDC - WETH - LINK - USDC", async () => { - await expect( - Flashloan.dodoFlashLoan( - { - flashLoanPool: dodoV2Pool.WETH_USDC, - loanAmount: getBigNumber(1000, 6), - firstRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 0, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address", "uint24"], - [findRouterFromProtocol(0), 500] - ), - }, - ], - path: [erc20Address.USDC, erc20Address.WETH], - }, - { - swaps: [ - { - protocol: 0, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address", "uint24"], - [findRouterFromProtocol(0), 3000] - ), - }, - ], - path: [erc20Address.WETH, ERC20Token.LINK.address], - }, - ], - part: 10000, - }, - ], - secondRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 0, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address", "uint24"], - [findRouterFromProtocol(0), 3000] - ), - }, - ], - path: [ERC20Token.LINK.address, erc20Address.USDC], - }, - ], - part: 10000, - }, - ], - }, - { gasLimit: 1000000 } - ) - ) - .emit(Flashloan, "SwapFinished") - .emit(Flashloan, "SentProfit"); - const balance = await USDC.balanceOf(owner.address); - expect(balance.gt(getBigNumber(0))).to.be.true; - }); - - it("USDC - WETH - UNI", async () => { - await expect( - Flashloan.dodoFlashLoan( - { - flashLoanPool: dodoV2Pool.WETH_USDC, - loanAmount: getBigNumber(1000, 6), - firstRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 0, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address", "uint24"], - [findRouterFromProtocol(0), 500] - ), - }, - ], - path: [ERC20Token.USDC.address, ERC20Token.WETH.address], - }, - { - swaps: [ - { - protocol: 0, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address", "uint24"], - [findRouterFromProtocol(0), 3000] - ), - }, - ], - path: [ERC20Token.WETH.address, ERC20Token.UNI.address], - }, - ], - part: 10000, - }, - ], - secondRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 0, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address", "uint24"], - [findRouterFromProtocol(0), 3000] - ), - }, - ], - path: [ERC20Token.UNI.address, ERC20Token.WETH.address], - }, - { - swaps: [ - { - protocol: 0, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address", "uint24"], - [findRouterFromProtocol(0), 500] - ), - }, - ], - path: [ERC20Token.WETH.address, ERC20Token.USDC.address], - }, - ], - part: 10000, - }, - ], - }, - { gasLimit: 1000000 } - ) - ) - .emit(Flashloan, "SwapFinished") - .emit(Flashloan, "SentProfit"); - const balance = await USDC.balanceOf(owner.address); - expect(balance.gt(getBigNumber(0))).to.be.true; - }); - - it("should execute uniswapV3 flashloan with multiple swaps.", async () => { - await expect( - Flashloan.dodoFlashLoan( - { - flashLoanPool: dodoV2Pool.WETH_USDC, - loanAmount: getBigNumber(1000, 6), - firstRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 0, - part: 5000, - data: ethers.utils.defaultAbiCoder.encode( - ["address", "uint24"], - [findRouterFromProtocol(0), 500] - ), - }, - { - protocol: 1, - part: 2500, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(1)] - ), - }, - { - protocol: 2, - part: 1000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(2)] - ), - }, - { - protocol: 3, - part: 825, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(3)] - ), - }, - { - protocol: 4, - part: 675, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(4)] - ), - }, - ], - path: [erc20Address.USDC, erc20Address.DAI], - }, - ], - part: 10000, - }, - ], - secondRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 0, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address", "uint24"], - [findRouterFromProtocol(0), 500] - ), - }, - ], - path: [erc20Address.DAI, erc20Address.USDC], - }, - ], - part: 10000, - }, - ], - }, - { gasLimit: 1000000 } - ) - ) - .emit(Flashloan, "SwapFinished") - .emit(Flashloan, "SentProfit"); - const balance = await USDC.balanceOf(owner.address); - expect(balance.gt(getBigNumber(0))).to.be.true; - expect((await DAI.balanceOf(Flashloan.address)).eq(getBigNumber(0))).to.be - .true; - }); - - it("should execute uniswapV3 flashloan with multiple routes.", async () => { - await expect( - Flashloan.dodoFlashLoan( - { - flashLoanPool: dodoV2Pool.WETH_USDC, - loanAmount: getBigNumber(1000, 6), - firstRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 0, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address", "uint24"], - [findRouterFromProtocol(0), 500] - ), - }, - ], - path: [erc20Address.USDC, erc20Address.DAI], - }, - ], - part: 9000, - }, - { - hops: [ - { - swaps: [ - { - protocol: 1, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(1)] - ), - }, - ], - path: [erc20Address.USDC, erc20Address.DAI], - }, - ], - part: 1000, - }, - ], - secondRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 0, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address", "uint24"], - [findRouterFromProtocol(0), 500] - ), - }, - ], - path: [erc20Address.DAI, erc20Address.USDC], - }, - ], - part: 10000, - }, - ], - }, - { gasLimit: 1000000 } - ) - ) - .emit(Flashloan, "SwapFinished") - .emit(Flashloan, "SentProfit"); - const balance = await USDC.balanceOf(owner.address); - expect(balance.gt(getBigNumber(0))).to.be.true; - expect((await DAI.balanceOf(Flashloan.address)).eq(getBigNumber(0))).to.be - .true; - }); - }); - - describe("DODO", () => { - it("USDC - WMATIC", async () => { - await expect( - Flashloan.dodoFlashLoan( - { - flashLoanPool: dodoV2Pool.WETH_USDC, - loanAmount: getBigNumber(1000, 6), - firstRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 0, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address", "uint24"], - [findRouterFromProtocol(0), 500] - ), - }, - ], - path: [erc20Address.USDC, erc20Address.WMATIC], - }, - ], - part: 10000, - }, - ], - secondRoutes: [ - { - hops: [ - { - swaps: [ - { - protocol: 8, - part: 10000, - data: ethers.utils.defaultAbiCoder.encode( - ["address", "address", "address"], - [ - dodoV2Pool.WMATIC_USDC, - DODOApprove.Polygon, - DODOV2Proxy.Polygon, - ] - ), - }, - ], - path: [erc20Address.WMATIC, erc20Address.USDC], - }, - ], - part: 10000, - }, - ], - }, - { gasLimit: 1000000 } - ) - ) - .emit(Flashloan, "SwapFinished") - .emit(Flashloan, "SentProfit"); - const balance = await USDC.balanceOf(owner.address); - expect(balance.gt(getBigNumber(0))).to.be.true; - expect((await DAI.balanceOf(Flashloan.address)).eq(getBigNumber(0))).to.be - .true; - }); - }); -}); diff --git a/test/polygon/oneinch.test.ts b/test/polygon/oneinch.test.ts deleted file mode 100644 index c8b0092a..00000000 --- a/test/polygon/oneinch.test.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; -import { expect } from "chai"; -import { ethers } from "hardhat"; -import { - dodoV2Pool, - erc20Address, - USDC_WHALE, -} from "../../constants/addresses"; -import { ERC20Token } from "../../constants/token"; -import { ERC20Mock, Flashloan, Flashloan__factory } from "../../typechain"; -import { - deployContractFromName, - getBigNumber, - getERC20ContractFromAddress, -} from "../../utils"; -import { impersonateFundErc20 } from "../../utils/token"; -import { oneinchRoutes } from "../../utils/1inch"; - -describe("Flashloan with 1inch routes", () => { - let Flashloan: Flashloan; - let owner: SignerWithAddress; - let addr1: SignerWithAddress; - let addr2: SignerWithAddress; - let addrs: SignerWithAddress[]; - let DAI: ERC20Mock; - let USDC: ERC20Mock; - let WMATIC: ERC20Mock; - let WETH: ERC20Mock; - let USDT: ERC20Mock; - - before(async () => { - USDC = await getERC20ContractFromAddress(erc20Address.USDC); - USDT = await getERC20ContractFromAddress(erc20Address.USDT); - DAI = await getERC20ContractFromAddress(erc20Address.DAI); - WETH = await getERC20ContractFromAddress(erc20Address.WETH); - WMATIC = await getERC20ContractFromAddress(erc20Address.WMATIC); - }); - - beforeEach(async () => { - [owner, addr1, addr2, ...addrs] = await ethers.getSigners(); - Flashloan = await deployContractFromName("Flashloan", Flashloan__factory); - await Flashloan.deployed(); - await impersonateFundErc20( - USDC, - USDC_WHALE, - Flashloan.address, - "1000.0", - 6 - ); - }); - - describe("UniswapV2", () => { - it("USDC - WETH", async () => { - const [firstRoutes, secondRoutes] = await oneinchRoutes( - ERC20Token.USDC, - ERC20Token.WETH, - 1000 - ); - if (firstRoutes && secondRoutes) { - await expect( - Flashloan.dodoFlashLoan({ - flashLoanPool: dodoV2Pool.WETH_USDC, - loanAmount: getBigNumber(1000, 6), - firstRoutes: firstRoutes, - secondRoutes: secondRoutes, - }) - ) - .emit(Flashloan, "SwapFinished") - .emit(Flashloan, "SentProfit"); - const balance = await USDC.balanceOf(owner.address); - expect(balance.gt(getBigNumber(0))).to.be.true; - } - }); - - it("USDC - WETH - WBTC", async () => { - const [firstRoutes, secondRoutes] = await oneinchRoutes( - ERC20Token.USDC, - ERC20Token.WETH, - 1000 - ); - if (firstRoutes && secondRoutes) { - await expect( - Flashloan.dodoFlashLoan( - { - flashLoanPool: dodoV2Pool.WETH_USDC, - loanAmount: getBigNumber(1000, 6), - firstRoutes: firstRoutes, - secondRoutes: secondRoutes, - }, - { gasLimit: 1000000 } - ) - ) - .emit(Flashloan, "SwapFinished") - .emit(Flashloan, "SentProfit"); - const balance = await USDC.balanceOf(owner.address); - expect(balance.gt(getBigNumber(0))).to.be.true; - } - }); - - it("should execute a flashloan with multihop swaps.", async () => { - const [firstRoutes, secondRoutes] = await oneinchRoutes( - ERC20Token.USDC, - ERC20Token.WETH, - 1000 - ); - if (firstRoutes && secondRoutes) { - await expect( - Flashloan.dodoFlashLoan({ - flashLoanPool: dodoV2Pool.WETH_USDC, - loanAmount: getBigNumber(1000, 6), - firstRoutes: firstRoutes, - secondRoutes: secondRoutes, - }) - ) - .emit(Flashloan, "SwapFinished") - .emit(Flashloan, "SentProfit"); - const balance = await USDC.balanceOf(owner.address); - expect(balance.gt(getBigNumber(0))).to.be.true; - expect( - (await DAI.balanceOf(Flashloan.address)).lt( - ethers.BigNumber.from(1000) - ) - ).to.be.true; - expect( - (await WETH.balanceOf(Flashloan.address)).lt( - ethers.BigNumber.from(1000) - ) - ).to.be.true; - } - }); - }); - - describe("UniswapV3", () => { - it("should execute uniswapV3 flashloan.", async () => { - const [firstRoutes, secondRoutes] = await oneinchRoutes( - ERC20Token.USDC, - ERC20Token.WETH, - 1000 - ); - if (firstRoutes && secondRoutes) { - await expect( - Flashloan.dodoFlashLoan( - { - flashLoanPool: dodoV2Pool.WETH_USDC, - loanAmount: getBigNumber(1000, 6), - firstRoutes: firstRoutes, - secondRoutes: secondRoutes, - }, - { gasLimit: 1000000 } - ) - ) - .emit(Flashloan, "SwapFinished") - .emit(Flashloan, "SentProfit"); - const balance = await USDC.balanceOf(owner.address); - expect(balance.gt(getBigNumber(0))).to.be.true; - } - }); - - it("USDC - WETH - UNI", async () => { - const [firstRoutes, secondRoutes] = await oneinchRoutes( - ERC20Token.USDC, - ERC20Token.WETH, - 1000 - ); - if (firstRoutes && secondRoutes) { - await expect( - Flashloan.dodoFlashLoan( - { - flashLoanPool: dodoV2Pool.WETH_USDC, - loanAmount: getBigNumber(1000, 6), - firstRoutes: firstRoutes, - secondRoutes: secondRoutes, - }, - { gasLimit: 1000000 } - ) - ) - .emit(Flashloan, "SwapFinished") - .emit(Flashloan, "SentProfit"); - const balance = await USDC.balanceOf(owner.address); - expect(balance.gt(getBigNumber(0))).to.be.true; - } - }); - - it("should execute uniswapV3 flashloan with multiple swaps.", async () => { - const [firstRoutes, secondRoutes] = await oneinchRoutes( - ERC20Token.USDC, - ERC20Token.WETH, - 1000 - ); - if (firstRoutes && secondRoutes) { - await expect( - Flashloan.dodoFlashLoan( - { - flashLoanPool: dodoV2Pool.WETH_USDC, - loanAmount: getBigNumber(1000, 6), - firstRoutes: firstRoutes, - secondRoutes: secondRoutes, - }, - { gasLimit: 1000000 } - ) - ) - .emit(Flashloan, "SwapFinished") - .emit(Flashloan, "SentProfit"); - const balance = await USDC.balanceOf(owner.address); - expect(balance.gt(getBigNumber(0))).to.be.true; - expect((await DAI.balanceOf(Flashloan.address)).eq(getBigNumber(0))).to - .be.true; - } - }); - }); -}); diff --git a/test/polygon/uniswap/flashloan.test.ts b/test/polygon/uniswap/flashloan.test.ts new file mode 100644 index 00000000..f4f0c889 --- /dev/null +++ b/test/polygon/uniswap/flashloan.test.ts @@ -0,0 +1,180 @@ +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { expect } from "chai"; +import { ethers } from "hardhat"; +import { dodoV2Pool, USDC_WHALE } from "../../../constants/addresses"; +import { ERC20Token } from "../../../constants/token"; +import { ERC20Mock, Flashloan, Flashloan__factory } from "../../../typechain"; +import { + deployContractFromName, + findRouterFromProtocol, + getBigNumber, + getERC20ContractFromAddress, +} from "../../../utils"; +import { impersonateFundErc20 } from "../../../utils/token"; + +describe("Uniswap Flashloan", () => { + let Flashloan: Flashloan; + let owner: SignerWithAddress; + let addr1: SignerWithAddress; + let addr2: SignerWithAddress; + let addrs: SignerWithAddress[]; + let DAI: ERC20Mock; + let USDC: ERC20Mock; + let WMATIC: ERC20Mock; + let WETH: ERC20Mock; + let USDT: ERC20Mock; + + before(async () => { + USDC = await getERC20ContractFromAddress(ERC20Token.USDC.address); + USDT = await getERC20ContractFromAddress(ERC20Token.USDT.address); + DAI = await getERC20ContractFromAddress(ERC20Token.DAI.address); + WETH = await getERC20ContractFromAddress(ERC20Token.WETH.address); + WMATIC = await getERC20ContractFromAddress(ERC20Token.WMATIC.address); + }); + + beforeEach(async () => { + [owner, addr1, addr2, ...addrs] = await ethers.getSigners(); + Flashloan = await deployContractFromName("Flashloan", Flashloan__factory); + await Flashloan.deployed(); + await impersonateFundErc20( + USDC, + USDC_WHALE, + Flashloan.address, + "1000.0", + 6 + ); + }); + + describe("UniswapV2", () => { + it("USDC - WETH", async () => { + await expect( + Flashloan.dodoFlashLoan( + { + flashLoanPool: dodoV2Pool.WETH_USDC, + loanAmount: getBigNumber(1000, 6), + routes: [ + { + hops: [ + { + protocol: 1, + data: ethers.utils.defaultAbiCoder.encode( + ["address"], + [findRouterFromProtocol(1)] + ), + path: [ERC20Token.USDC.address, ERC20Token.WETH.address], + }, + { + protocol: 1, + data: ethers.utils.defaultAbiCoder.encode( + ["address"], + [findRouterFromProtocol(1)] + ), + path: [ERC20Token.WETH.address, ERC20Token.USDC.address], + }, + ], + part: 10000, + }, + ], + }, + { gasLimit: 1000000 } + ) + ).to.not.reverted; + const balance = await USDC.balanceOf(owner.address); + expect(balance.gt(getBigNumber(0))).to.be.true; + }); + }); + + describe("UniswapV3", () => { + it("should execute uniswapV3 flashloan.", async () => { + await expect( + Flashloan.dodoFlashLoan( + { + flashLoanPool: dodoV2Pool.WETH_USDC, + loanAmount: getBigNumber(1000, 6), + routes: [ + { + hops: [ + { + protocol: 0, + data: ethers.utils.defaultAbiCoder.encode( + ["address", "uint24"], + [findRouterFromProtocol(0), 500] + ), + path: [ERC20Token.USDC.address, ERC20Token.DAI.address], + }, + { + protocol: 0, + data: ethers.utils.defaultAbiCoder.encode( + ["address", "uint24"], + [findRouterFromProtocol(0), 500] + ), + path: [ERC20Token.DAI.address, ERC20Token.USDC.address], + }, + ], + part: 10000, + }, + ], + }, + { gasLimit: 1000000 } + ) + ).to.not.reverted; + const balance = await USDC.balanceOf(owner.address); + expect(balance.gt(getBigNumber(0))).to.be.true; + }); + + it("USDC - WETH - UNI", async () => { + await expect( + Flashloan.dodoFlashLoan( + { + flashLoanPool: dodoV2Pool.WETH_USDC, + loanAmount: getBigNumber(1000, 6), + routes: [ + { + hops: [ + { + protocol: 0, + data: ethers.utils.defaultAbiCoder.encode( + ["address", "uint24"], + [findRouterFromProtocol(0), 500] + ), + path: [ERC20Token.USDC.address, ERC20Token.WETH.address], + }, + { + protocol: 0, + data: ethers.utils.defaultAbiCoder.encode( + ["address", "uint24"], + [findRouterFromProtocol(0), 3000] + ), + path: [ERC20Token.WETH.address, ERC20Token.UNI.address], + }, + { + protocol: 0, + data: ethers.utils.defaultAbiCoder.encode( + ["address", "uint24"], + [findRouterFromProtocol(0), 3000] + ), + path: [ERC20Token.UNI.address, ERC20Token.WETH.address], + }, + { + protocol: 0, + data: ethers.utils.defaultAbiCoder.encode( + ["address", "uint24"], + [findRouterFromProtocol(0), 500] + ), + path: [ERC20Token.WETH.address, ERC20Token.USDC.address], + }, + ], + part: 10000, + }, + ], + }, + { gasLimit: 1000000 } + ) + ) + .emit(Flashloan, "SwapFinished") + .emit(Flashloan, "SentProfit"); + const balance = await USDC.balanceOf(owner.address); + expect(balance.gt(getBigNumber(0))).to.be.true; + }); + }); +}); diff --git a/utils/1inch/config.ts b/utils/1inch/config.ts deleted file mode 100644 index 7f0f9c41..00000000 --- a/utils/1inch/config.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const chainId = 137; - -export const protocols = - "POLYGON_SUSHISWAP,POLYGON_QUICKSWAP,POLYGON_APESWAP,POLYGON_JETSWAP,POLYGON_WAULTSWAP,POLYGON_UNISWAP_V3"; diff --git a/utils/1inch/index.ts b/utils/1inch/index.ts deleted file mode 100644 index f65d2f67..00000000 --- a/utils/1inch/index.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { ethers } from "hardhat"; -import { ERC20Token, IToken } from "../../constants/token"; -import { uniswapRouter } from "../../constants/addresses"; -import { Hop, IFlashloanRoute, IProtocol, Swap } from "./interfaces"; -import { sendRequest } from "../../utils/request"; -import { get1inchQuoteCallUrl } from "./url"; -import { getRouteParts, toInt } from "../split"; -import { chainId } from "./config"; -import { findRouterFromProtocol } from ".."; - -export async function oneinchRoutes( - fromToken: IToken, - toToken: IToken, - loanAmount: number -): Promise<[IFlashloanRoute[] | null, IFlashloanRoute[] | null]> { - const fromTokenDecimal = fromToken.decimals; - - const amount = ethers.utils.parseUnits( - loanAmount.toString(), - fromTokenDecimal - ); - const firstCallURL = get1inchQuoteCallUrl( - chainId, - fromToken.address, - toToken.address, - amount - ); - - const resultData1 = await sendRequest(firstCallURL); - if (!!resultData1.isAxiosError) { - const e = resultData1; - return [null, null]; - } - - const firstProtocols = resultData1.protocols; - const returnAmount = resultData1.toTokenAmount; - const secondCallURL = get1inchQuoteCallUrl( - chainId, - toToken.address, - fromToken.address, - returnAmount - ); - - const resultData2 = await sendRequest(secondCallURL); - if (!!resultData2.isAxiosError) { - const e = resultData2; - return [null, null]; - } - const secondProtocols = resultData2.protocols; - return [createRoutes(firstProtocols), createRoutes(secondProtocols)]; -} - -export const createRoutes = (routes: IProtocol[][][]): IFlashloanRoute[] => { - let flashloanRoutes: IFlashloanRoute[] = []; - let i = 0; - const routeParts = getRouteParts(routes.length); - for (const hops of routes) { - const part = routeParts[i]; - let route: IFlashloanRoute = { - part: part, - hops: toHops(hops), - }; - flashloanRoutes.push(route); - i++; - } - return flashloanRoutes; -}; - -const createData = (protocol: number) => { - if (protocol === 0) { - return ethers.utils.defaultAbiCoder.encode( - ["address", "uint24"], - [findRouterFromProtocol(protocol), 500] - ); - } else { - return ethers.utils.defaultAbiCoder.encode( - ["address"], - [findRouterFromProtocol(protocol)] - ); - } -}; - -const toSwaps = (results: IProtocol[]) => { - let swaps: Swap[] = []; - for (const result of results) { - const protocol = protocolNameToNumber(result.name); - const data = createData(protocol); - swaps.push({ - protocol: protocol, - part: toInt(result.part), - data: data, - }); - } - return swaps; -}; - -const toHops = (results: IProtocol[][]) => { - let hops: Hop[] = []; - for (const result of results) { - const path = [result[0].fromTokenAddress, result[0].toTokenAddress].map( - (token) => { - return replaceTokenAddress( - token, - ERC20Token.MATIC.address, - ERC20Token.WMATIC.address - ); - } - ); - let hop: Hop = { - path: path, - swaps: toSwaps(result), - }; - hops.push(hop); - } - return hops; -}; - -const protocolNameToNumber = (protocolName: string): number => { - let protocolNumber = 0; - for (const name of Object.keys(uniswapRouter)) { - if (name === protocolName) { - return protocolNumber; - } - protocolNumber++; - } - throw new Error(`Unknown protocol name: ${protocolName}`); -}; - -const replaceTokenAddress = ( - token: string, - address: string, - newAddress: string -) => { - return token === address ? newAddress : token; -}; diff --git a/utils/1inch/interfaces.ts b/utils/1inch/interfaces.ts deleted file mode 100644 index 7c20213e..00000000 --- a/utils/1inch/interfaces.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { ethers } from "hardhat"; -const { BigNumber } = ethers; - -export interface IRoute { - name: string; - toTokenAddress: string; -} - -export interface Swap { - protocol: number; - part: number; - data: string; -} - -export interface Hop { - swaps: Swap[]; - path: string[]; -} - -export interface IFlashloanRoute { - hops: Hop[]; - part: number; -} - -export interface IParams { - flashLoanPool: string; - loanAmount: typeof BigNumber; - firstRoutes: IFlashloanRoute[]; - secondRoutes: IFlashloanRoute[]; -} - -export interface IProtocol { - name: string; - part: number; - fromTokenAddress: string; - toTokenAddress: string; -} diff --git a/utils/1inch/url.ts b/utils/1inch/url.ts deleted file mode 100644 index 7309d7be..00000000 --- a/utils/1inch/url.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { BigNumber } from "ethers"; -import { protocols } from "./config"; - -/** - * Will get the 1inch API call URL for a trade - * @param chainId chain id of the network - * @param fromTokenAddress token address of the token you want to sell - * @param toTokenAddress token address of the token you want to buy - * @param amount amount of the token you want to sell - * @returns call URL for 1inch API - */ -export function get1inchQuoteCallUrl( - chainId: number, - fromTokenAddress: string, - toTokenAddress: string, - amount: BigNumber -): string { - const params = { - fromTokenAddress: fromTokenAddress, - toTokenAddress: toTokenAddress, - amount: amount.toString(), - protocols: protocols, - }; - const apiURL = "https://api.1inch.exchange/v4.0/"; - const searchString = new URLSearchParams(params); - const callURL = `${apiURL}${chainId}/quote?${searchString}`; - return callURL; -} diff --git a/utils/request.ts b/utils/request.ts deleted file mode 100644 index 619d0206..00000000 --- a/utils/request.ts +++ /dev/null @@ -1,14 +0,0 @@ -import axios from "axios"; - -export const sendRequest = async (url: string) => { - let response: any = await axios - .get(url) - .then((result) => { - return result.data; - }) - .catch((error) => { - return error; - }); - - return response; -}; diff --git a/utils/split.ts b/utils/split.ts deleted file mode 100644 index 58a11d4d..00000000 --- a/utils/split.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { BigNumber } from "ethers"; - -const routeParts = [ - [10000], - [8000, 2000], - [5000, 4000, 1000], - [5000, 3000, 1000, 1000], - [3000, 2000, 2000, 2000, 1000], - [2000, 2000, 2000, 2000, 1000, 1000], -]; - -export const getRouteParts = (length: number) => { - try { - return routeParts[length - 1]; - } catch { - throw new Error(`Route length ${length} is not supported`); - } -}; - -export const toInt = (float: number) => { - return float * 100; -}; - -export const splitLoanAmount = ( - loanAmount: BigNumber, - part: number -): BigNumber => { - return loanAmount.mul(part).div(10000); -};