Skip to content

Commit

Permalink
Merge pull request #47 from yuichiroaoki/dodo
Browse files Browse the repository at this point in the history
Reflect changes made by the contract update
  • Loading branch information
yuichiroaoki authored Feb 13, 2022
2 parents 50088ba + 1230dcb commit 617c966
Show file tree
Hide file tree
Showing 21 changed files with 589 additions and 191 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:14.17.3
FROM node:16.14

WORKDIR /app

Expand Down
25 changes: 12 additions & 13 deletions src/abis/Flashloan.json

Large diffs are not rendered by default.

95 changes: 95 additions & 0 deletions src/abis/IDODOV2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
{
"_format": "hh-sol-artifact-1",
"contractName": "IDODOV2",
"sourceName": "contracts/dodo/IDODOV2.sol",
"abi": [
{
"inputs": [],
"name": "_BASE_TOKEN_",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "_QUOTE_TOKEN_",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "trader",
"type": "address"
},
{
"internalType": "uint256",
"name": "payBaseAmount",
"type": "uint256"
}
],
"name": "querySellBase",
"outputs": [
{
"internalType": "uint256",
"name": "receiveQuoteAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "mtFee",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "trader",
"type": "address"
},
{
"internalType": "uint256",
"name": "payQuoteAmount",
"type": "uint256"
}
],
"name": "querySellQuote",
"outputs": [
{
"internalType": "uint256",
"name": "receiveBaseAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "mtFee",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
}
],
"bytecode": "0x",
"deployedBytecode": "0x",
"linkReferences": {},
"deployedLinkReferences": {}
}
13 changes: 11 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ export const chainId = 137; // Polygon

export const explorerURL = "https://polygonscan.com";

// Token pair the bot trading
/**
* Token pair the bot trading
* baseToken -> tradingToken -> baseToken (ex: DAI -> WETH -> DAI)
* profits are sent in baseToken if a transaction is successful.
*/

export const baseTokens = [
ERC20Token.DAI,
// ERC20Token.WETH,
Expand All @@ -37,8 +42,12 @@ export const tradingTokens = [
* if you have deployed your own contract, you can use it instead of the default one
*/
export const flashloanAddress: string =
"0x568a23AD22041683468CD1D3a6968D7E7dC20D40";
"0x33d8d437796bd43bdccc6740c585f4a15d1070b7";

/**
* The bot can trade on UniswapV2 fork dexes(ex. SushiSwap) and UniswapV3
* For UniswapV2, you can trade between any token pair, but for UniswapV3, you have to check their pool fees and list them on src/price/uniswap/v3/fee.ts.
*/
// protocols the bot will use
export const protocols =
"POLYGON_SUSHISWAP,POLYGON_QUICKSWAP,POLYGON_APESWAP,POLYGON_JETSWAP,POLYGON_WAULTSWAP,POLYGON_UNISWAP_V3";
Expand Down
18 changes: 17 additions & 1 deletion src/constants/addresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,22 @@ export const ERC20Token: erc20Token = {
logoURI:
"https://tokens.1inch.io/0x7ceb23fd6bc0add59e62ac25578270cff1b9f619.png",
},
UNI: {
symbol: "UNI",
name: "Uniswap",
decimals: 18,
address: "0xb33eaad8d922b1083446dc23f610c2567fb5180f",
logoURI:
"https://tokens.1inch.io/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984.png",
},
AAVE: {
symbol: "AAVE",
name: "Aave",
decimals: 18,
address: "0xd6df932a45c0f255f85145f286ea0b292b21c90b",
logoURI:
"https://tokens.1inch.io/0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9.png",
},
CRV: {
symbol: "CRV",
name: "CRV",
Expand All @@ -118,7 +134,7 @@ export const dodoV2Pool: PoolMap = {
USDT_DAI: "0xDa43a4aAB20D313Ab3AA07d8E09f3521F32a3D83",
WETH_USDC: "0x5333Eb1E32522F1893B7C9feA3c263807A02d561",
WMATIC_USDC: "0x10Dd6d8A29D489BEDE472CC1b22dc695c144c5c7",
USDC_USDT: "0xA0020444b98f67B77a3d6dE6E66aF11c87da086e",
USDT_USDC: "0xA0020444b98f67B77a3d6dE6E66aF11c87da086e",
WBTC_USDC: "0xe020008465cD72301A18b97d33D73bF44858A4b7",
};

Expand Down
2 changes: 1 addition & 1 deletion src/expect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const expectPriceOnDex = async (
return getBigNumber(0);
}
if (protocol === 0) {
const fee = getUniswapV3PoolFee([tokenIn, tokenOut])[0];
const fee = getUniswapV3PoolFee([tokenIn, tokenOut]);
return await getPriceOnUniV3(tokenIn, tokenOut, amountIn, fee);
} else {
const routerAddress = findRouterFromProtocol(protocol);
Expand Down
75 changes: 3 additions & 72 deletions src/flashloan.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import { ethers } from "ethers";
import * as FlashloanJson from "./abis/Flashloan.json";
import { flashloanAddress, loanAmount, gasLimit, gasPrice } from "./config";
import {
IToken,
dodoV2Pool,
uniswapRouter,
ERC20Token,
} from "./constants/addresses";
import { IProtocol } from "./interfaces/inch";
import { Hop, IFlashloanRoute, IParams, Swap } from "./interfaces/main";
import { getBigNumber, replaceTokenAddress } from "./utils/index";
import { getRouteParts, toInt } from "./utils/split";
import { IToken, dodoV2Pool } from "./constants/addresses";
import { IFlashloanRoute, IParams } from "./interfaces/main";
import { getBigNumber } from "./utils/index";

const maticProvider = new ethers.providers.JsonRpcProvider(
process.env.ALCHEMY_POLYGON_RPC_URL
Expand Down Expand Up @@ -65,66 +58,4 @@ export const flashloan = async (
gasLimit: gasLimit,
gasPrice: ethers.utils.parseUnits(`${gasPrice}`, "gwei"),
});
// const polyscanURL = "https://polygonscan.com/tx/" + tx.hash;
// console.log("Flashloan tx: ", tx.hash);
// console.log("Polyscan URL: ", polyscanURL);
};

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}`);
};

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 toSwaps = (results: IProtocol[]) => {
let swaps: Swap[] = [];
for (const result of results) {
swaps.push({
protocol: protocolNameToNumber(result.name),
part: toInt(result.part),
});
}
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;
};
104 changes: 58 additions & 46 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import {
loanAmount,
diffAmount,
} from "./config";
import { createRoutes, flashloan } from "./flashloan";
import { flashloan } from "./flashloan";
import { expectAmountOut } from "./expect";
import { getBigNumber } from "./utils";
import { ethers } from "ethers";
import { chalkDifference, chalkPercentage, chalkTime } from "./utils/chalk";
import { flashloanTable, priceTable } from "./consoleUI/table";
import { initPriceTable, renderTables } from "./consoleUI";
import { createRoutes } from "./price/1inch/route";

export const main = async () => {
console.clear();
Expand Down Expand Up @@ -75,13 +76,19 @@ export const main = async () => {
const secondRoutes = createRoutes(secondProtocols);

const bnLoanAmount = getBigNumber(loanAmount, baseToken.decimals);
// estimate the token amount you get atfer swaps
const bnExpectedAmountOut = await expectAmountOut(
firstRoutes,
bnLoanAmount
).then((firstAmountOut) =>
expectAmountOut(secondRoutes, firstAmountOut)
);
let bnExpectedAmountOut = getBigNumber(0);
// double check the price by qeurying dex contracts
try {
bnExpectedAmountOut = await expectAmountOut(
firstRoutes,
bnLoanAmount
).then((firstAmountOut) =>
expectAmountOut(secondRoutes, firstAmountOut)
);
} catch (e) {
// skip flashloan when failed to estimate price
return;
}
// check if the expected amount is larger than the loan amount
const isOpportunity = bnLoanAmount
.add(getBigNumber(diffAmount, baseToken.decimals))
Expand All @@ -106,44 +113,49 @@ export const main = async () => {

const startTime = Date.now();

const tx = await flashloan(
baseToken,
firstRoutes,
secondRoutes
);

pp.addRow({
baseToken: baseToken.symbol.padEnd(6),
tradingToken: tradingToken.symbol.padEnd(6),

amount: (amount || "").padStart(7),
difference: (chalkDifference(difference) || "").padStart(6),
percentage: (chalkPercentage(percentage) || "").padStart(4),

firstRoutes: firstProtocols.map((routes) =>
routes.map((hops) =>
hops
.map((swap) => swap.name.replace("POLYGON_", ""))
.join(" → ")
)
),
secondRoutes: secondProtocols.map((routes) =>
routes.map((hops) =>
hops
.map((swap) => swap.name.replace("POLYGON_", ""))
.join(" → ")
)
),

txHash: tx.hash.padStart(66),

time: chalkTime((Date.now() - startTime) / 1000).padStart(6),
timestamp: new Date().toISOString(),
});

isFlashLoaning = false;

renderTables(p, pp);
try {
const tx = await flashloan(
baseToken,
firstRoutes,
secondRoutes
);

pp.addRow({
baseToken: baseToken.symbol.padEnd(6),
tradingToken: tradingToken.symbol.padEnd(6),

amount: (amount || "").padStart(7),
difference: (chalkDifference(difference) || "").padStart(6),
percentage: (chalkPercentage(percentage) || "").padStart(4),

firstRoutes: firstProtocols.map((routes) =>
routes.map((hops) =>
hops
.map((swap) => swap.name.replace("POLYGON_", ""))
.join(" → ")
)
),
secondRoutes: secondProtocols.map((routes) =>
routes.map((hops) =>
hops
.map((swap) => swap.name.replace("POLYGON_", ""))
.join(" → ")
)
),

txHash: tx.hash.padStart(66),

time: chalkTime((Date.now() - startTime) / 1000).padStart(
6
),
timestamp: new Date().toISOString(),
});

renderTables(p, pp);
} catch (e) {
} finally {
isFlashLoaning = false;
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/interfaces/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface IRoute {
export interface Swap {
protocol: number;
part: number;
data: string;
}

export interface Hop {
Expand Down
Loading

0 comments on commit 617c966

Please sign in to comment.