Skip to content

Instantly share code, notes, and snippets.

@gh639
Created February 2, 2021 03:01
Show Gist options
  • Save gh639/e4ecf1fa2cf0f259d24801deb24faf5b to your computer and use it in GitHub Desktop.
Save gh639/e4ecf1fa2cf0f259d24801deb24faf5b to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.6.12+commit.27d51765.js&optimize=false&runs=200&gist=
//https://github.com/node-cache/node-cache
//npm install md5-node
const axios = require('axios')
//const _ = require('lodash')
const ethers = require('ethers')
const AbiCoder = ethers.utils.AbiCoder;
//const BN = ethers.BigNumber
const NodeCache = require("node-cache");//本缓存只在node任务运行期有效,重启任务时失效
const md5 = require('md5-node');
const fs= require("fs");
const myCache = new NodeCache();
const moment = require('moment');//npm install moment
//const mysql = require('./mysql.js');
const FlashRobot = require("./FlashRobot.json");
//const { ChainId, Fetcher, TokenAmount, WETH, Route, TradeType, Trade, Percent, Token } = require('@uniswap/sdk');
const UNISWAP = require('@uniswap/sdk');
const SUSHISWAP = require('@sushiswap/sdk');
const chainId = UNISWAP.ChainId.KOVAN;//UNISWAP.ChainId.KOVAN;
//console.log(ChainId.MAINNET);
//console.log("chainId:" + chainId);
const uni_weth = UNISWAP.WETH[chainId];
//console.log(uni_weth);
const INFURA_PROJECT_ID = "fb2bfd8dd737444ab5702d9a6516f9cf"
const DEPLOYMENT_ACCOUNT_KEY = "0xa3de2d07c4a3fde691f38312743d604167d8d16c1a53f614dd87bcb4ec5492ba";// "0xa3de2d07c4a3fde691f38312743d604167d8d16c1a53f614dd87bcb4ec5492ba"
let provider;
let IKyberNetworkProxy_ADDRESS;
//mainnet=1,kovan=42,ropsten=3
if (chainId == 1){
IKyberNetworkProxy_ADDRESS = '0x9AAb3f75489902f3a48495025729a0AF77d4b11e';
provider = new ethers.providers.InfuraProvider("mainnet", {projectId: INFURA_PROJECT_ID,});
}else if (chainId == 3){
IKyberNetworkProxy_ADDRESS = '0xd719c34261e099Fdb33030ac8909d5788D3039C4';
provider = new ethers.providers.InfuraProvider("ropsten", {projectId: INFURA_PROJECT_ID,});
}else if (chainId == 42){
IKyberNetworkProxy_ADDRESS = '0xc153eeAD19e0DBbDb3462Dcc2B703cC6D738A37c';
provider = new ethers.providers.InfuraProvider("kovan", {projectId: INFURA_PROJECT_ID,});
}
const wallet = new ethers.Wallet(DEPLOYMENT_ACCOUNT_KEY, provider);
const flashRobot = new ethers.Contract(
'0x1b7195E7dbC5B836629A88d4d72A30F19FA9F2ee',
FlashRobot.abi,
wallet
)
//const IKyberNetworkProxy_ABI = '[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"src","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"dest","type":"address"},{"indexed":false,"internalType":"address","name":"destAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"actualSrcAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"actualDestAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"platformWallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"platformFeeBps","type":"uint256"}],"name":"ExecuteTrade","type":"event"},{"inputs":[{"internalType":"contract ERC20","name":"src","type":"address"},{"internalType":"contract ERC20","name":"dest","type":"address"},{"internalType":"uint256","name":"srcQty","type":"uint256"}],"name":"getExpectedRate","outputs":[{"internalType":"uint256","name":"expectedRate","type":"uint256"},{"internalType":"uint256","name":"worstRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"src","type":"address"},{"internalType":"contract IERC20","name":"dest","type":"address"},{"internalType":"uint256","name":"srcQty","type":"uint256"},{"internalType":"uint256","name":"platformFeeBps","type":"uint256"},{"internalType":"bytes","name":"hint","type":"bytes"}],"name":"getExpectedRateAfterFee","outputs":[{"internalType":"uint256","name":"expectedRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"src","type":"address"},{"internalType":"uint256","name":"srcAmount","type":"uint256"},{"internalType":"contract IERC20","name":"dest","type":"address"},{"internalType":"address payable","name":"destAddress","type":"address"},{"internalType":"uint256","name":"maxDestAmount","type":"uint256"},{"internalType":"uint256","name":"minConversionRate","type":"uint256"},{"internalType":"address payable","name":"platformWallet","type":"address"}],"name":"trade","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"src","type":"address"},{"internalType":"uint256","name":"srcAmount","type":"uint256"},{"internalType":"contract ERC20","name":"dest","type":"address"},{"internalType":"address payable","name":"destAddress","type":"address"},{"internalType":"uint256","name":"maxDestAmount","type":"uint256"},{"internalType":"uint256","name":"minConversionRate","type":"uint256"},{"internalType":"address payable","name":"walletId","type":"address"},{"internalType":"bytes","name":"hint","type":"bytes"}],"name":"tradeWithHint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"src","type":"address"},{"internalType":"uint256","name":"srcAmount","type":"uint256"},{"internalType":"contract IERC20","name":"dest","type":"address"},{"internalType":"address payable","name":"destAddress","type":"address"},{"internalType":"uint256","name":"maxDestAmount","type":"uint256"},{"internalType":"uint256","name":"minConversionRate","type":"uint256"},{"internalType":"address payable","name":"platformWallet","type":"address"},{"internalType":"uint256","name":"platformFeeBps","type":"uint256"},{"internalType":"bytes","name":"hint","type":"bytes"}],"name":"tradeWithHintAndFee","outputs":[{"internalType":"uint256","name":"destAmount","type":"uint256"}],"stateMutability":"payable","type":"function"}]';
const IKyberNetworkProxy_ABI = '[{"inputs":[{"internalType":"address","name":"_admin","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"}],"name":"AdminClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newAlerter","type":"address"},{"indexed":false,"internalType":"bool","name":"isAdd","type":"bool"}],"name":"AlerterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"sendTo","type":"address"}],"name":"EtherWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"src","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"dest","type":"address"},{"indexed":false,"internalType":"address","name":"destAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"actualSrcAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"actualDestAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"platformWallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"platformFeeBps","type":"uint256"}],"name":"ExecuteTrade","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IKyberHint","name":"kyberHintHandler","type":"address"}],"name":"KyberHintHandlerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IKyberNetwork","name":"newKyberNetwork","type":"address"},{"indexed":false,"internalType":"contract IKyberNetwork","name":"previousKyberNetwork","type":"address"}],"name":"KyberNetworkSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOperator","type":"address"},{"indexed":false,"internalType":"bool","name":"isAdd","type":"bool"}],"name":"OperatorAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"sendTo","type":"address"}],"name":"TokenWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pendingAdmin","type":"address"}],"name":"TransferAdminPending","type":"event"},{"inputs":[{"internalType":"address","name":"newAlerter","type":"address"}],"name":"addAlerter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOperator","type":"address"}],"name":"addOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"enabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAlerters","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"src","type":"address"},{"internalType":"contract ERC20","name":"dest","type":"address"},{"internalType":"uint256","name":"srcQty","type":"uint256"}],"name":"getExpectedRate","outputs":[{"internalType":"uint256","name":"expectedRate","type":"uint256"},{"internalType":"uint256","name":"worstRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"src","type":"address"},{"internalType":"contract IERC20","name":"dest","type":"address"},{"internalType":"uint256","name":"srcQty","type":"uint256"},{"internalType":"uint256","name":"platformFeeBps","type":"uint256"},{"internalType":"bytes","name":"hint","type":"bytes"}],"name":"getExpectedRateAfterFee","outputs":[{"internalType":"uint256","name":"expectedRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOperators","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"kyberHintHandler","outputs":[{"internalType":"contract IKyberHint","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"kyberNetwork","outputs":[{"internalType":"contract IKyberNetwork","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxGasPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"alerter","type":"address"}],"name":"removeAlerter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"removeOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IKyberHint","name":"_kyberHintHandler","type":"address"}],"name":"setHintHandler","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IKyberNetwork","name":"_kyberNetwork","type":"address"}],"name":"setKyberNetwork","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"minConversionRate","type":"uint256"}],"name":"swapEtherToToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"srcAmount","type":"uint256"},{"internalType":"uint256","name":"minConversionRate","type":"uint256"}],"name":"swapTokenToEther","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"src","type":"address"},{"internalType":"uint256","name":"srcAmount","type":"uint256"},{"internalType":"contract IERC20","name":"dest","type":"address"},{"internalType":"uint256","name":"minConversionRate","type":"uint256"}],"name":"swapTokenToToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"src","type":"address"},{"internalType":"uint256","name":"srcAmount","type":"uint256"},{"internalType":"contract IERC20","name":"dest","type":"address"},{"internalType":"address payable","name":"destAddress","type":"address"},{"internalType":"uint256","name":"maxDestAmount","type":"uint256"},{"internalType":"uint256","name":"minConversionRate","type":"uint256"},{"internalType":"address payable","name":"platformWallet","type":"address"}],"name":"trade","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"src","type":"address"},{"internalType":"uint256","name":"srcAmount","type":"uint256"},{"internalType":"contract ERC20","name":"dest","type":"address"},{"internalType":"address payable","name":"destAddress","type":"address"},{"internalType":"uint256","name":"maxDestAmount","type":"uint256"},{"internalType":"uint256","name":"minConversionRate","type":"uint256"},{"internalType":"address payable","name":"walletId","type":"address"},{"internalType":"bytes","name":"hint","type":"bytes"}],"name":"tradeWithHint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"src","type":"address"},{"internalType":"uint256","name":"srcAmount","type":"uint256"},{"internalType":"contract IERC20","name":"dest","type":"address"},{"internalType":"address payable","name":"destAddress","type":"address"},{"internalType":"uint256","name":"maxDestAmount","type":"uint256"},{"internalType":"uint256","name":"minConversionRate","type":"uint256"},{"internalType":"address payable","name":"platformWallet","type":"address"},{"internalType":"uint256","name":"platformFeeBps","type":"uint256"},{"internalType":"bytes","name":"hint","type":"bytes"}],"name":"tradeWithHintAndFee","outputs":[{"internalType":"uint256","name":"destAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"transferAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"transferAdminQuickly","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"sendTo","type":"address"}],"name":"withdrawEther","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"sendTo","type":"address"}],"name":"withdrawToken","outputs":[],"stateMutability":"nonpayable","type":"function"}]';
//console.log(IKyberNetworkProxy_ADDRESS);
const kyberswap = new ethers.Contract(
IKyberNetworkProxy_ADDRESS,
IKyberNetworkProxy_ABI,
wallet
);
const httpApi = {
async get (url, data) {
try {
let res = await axios.get(url, {params: data})
res = res.data
return new Promise((resolve) => {
if (res.code === 0) {
resolve(res)
} else {
resolve(res)
}
})
} catch (err) {
//alert('服务器出错')
console.log(err)
}
},
async post (url, data) {
try {
let res = await axios.post(url, qs.stringify(data))
res = res.data
return new Promise((resolve, reject) => {
if (res.code === 0) {
resolve(res)
} else {
reject(res)
}
})
} catch (err) {
// return (e.message)
//alert('服务器出错')
console.log(err)
}
},
}
//将uniswap Token类型转为sushiswap的token类型
function uniTokenToSushiToken(uniToken) {
return new SUSHISWAP.Token(uniToken.chainId, uniToken.address, uniToken.decimals, uniToken.symbol, uniToken.name);
}
async function getSushiPriceTest(uni_from_token, uni_to_token, from_amount) {
let sushi_from_token = uniTokenToSushiToken(uni_from_token);
let sushi_to_token = uniTokenToSushiToken(uni_to_token);
let sushi_weth = SUSHISWAP.WETH[uni_from_token.chainId];
let out = await getUniSushiPriceTest(sushi_from_token, sushi_to_token, from_amount, 2, 'Sushiswap', sushi_weth, SUSHISWAP.Fetcher, SUSHISWAP.Route, SUSHISWAP.Trade, SUSHISWAP.TokenAmount, SUSHISWAP.TradeType, SUSHISWAP.Percent);
out.fromToken = uni_from_token;
out.toToken = uni_to_token;
return out;
}
/*
checks tokens decimals,
gets Uniswap expected exchange rates and slippage rate.
Also compares direct and multi paths
*/
async function getUniPriceTest(uni_from_token, uni_to_token, from_amount) {
return await getUniSushiPriceTest(uni_from_token, uni_to_token, from_amount, 1, 'uniswap', uni_weth, UNISWAP.Fetcher, UNISWAP.Route, UNISWAP.Trade, UNISWAP.TokenAmount, UNISWAP.TradeType, UNISWAP.Percent);
}
async function getUniSushiPriceTest(from_token, to_token, from_amount, service, serviceName, weth, Fetcher, Route, Trade, TokenAmount, TradeType, Percent) {
let amountIn = 0
const slippageTolerance = new UNISWAP.Percent('3', '1000');//==> 3/1000=0.3%
//console.log(slippageTolerance);
amountIn = ethers.utils.parseUnits(parseFloat(from_amount).toFixed(from_token.decimals).toString(),from_token.decimals)
//console.log(amountIn);
/*
exsID, exsName, exsParamA, exsParamB, exactIn, rate, minOut
*/
let out = {};
out.service = service;
out.serviceName = serviceName;//'Uniswap';
if (serviceName == 'uniswap'){
out.target = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D';
}else if (serviceName == 'sushiswap'){
out.target = '0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F';
}
out.fromToken = from_token;
out.toToken = to_token;
out.exactIn = from_amount;
out.rate = 0;
out.minOut = 0;
out.bytesData = '0x';//参数abiCoder.encode(["address", "uint256"], ['0x6B175474E89094C44Da98b954EedeAC495271d0F', 1000]);
out.bytesDataDesc = 'path';//byte_data = web3.codec.encode_abi(["address[]"], [path])
out.path = [];
const path_num = 2;
if (from_token == weth || to_token == weth) {
const pair = await Fetcher.fetchPairData(from_token, to_token, provider)
//const pair = await getPair(from_token, to_token);
const route = new Route([pair], from_token)
const trade = new Trade(route, new TokenAmount(from_token, amountIn), TradeType.EXACT_INPUT)
const finalRate = trade.executionPrice.toFixed(6);//返回 是否已经扣除了0.3%的手续费? getAmountOut(), pair.getOutputAmount() 扣除了千之分三的交易手续费
//console.log(trade.nextMidPrice.toSignificant(6))
const amountOutMin = BigInt(trade.minimumAmountOut(slippageTolerance).raw);// amountIn * finalRate * (1- slippageTolerance)
//out.exsParamA = amountOutMin;
//out.exsParamB = 2;
out.rate = finalRate;
out.minOut = (out.exactIn * finalRate * 0.997).toFixed(6);
} else {
const pair = await Fetcher.fetchPairData(from_token, to_token, provider)
const pair1 = await Fetcher.fetchPairData(from_token, weth, provider)
const pair2 = await Fetcher.fetchPairData(weth, to_token, provider)
const route = new Route([pair], from_token)
const routeMixed = new Route([pair1, pair2], from_token)
const trade = new Trade(route, new TokenAmount(from_token, amountIn.toString()), TradeType.EXACT_INPUT)
const tradeMix = new Trade(routeMixed, new TokenAmount(from_token, amountIn.toString()), TradeType.EXACT_INPUT)
const rate = trade.executionPrice.toFixed(6)
const rateMix = tradeMix.executionPrice.toFixed(6)
const amountOutMin1 = BigInt(trade.minimumAmountOut(slippageTolerance).raw)
const amountOutMin2 = BigInt(tradeMix.minimumAmountOut(slippageTolerance).raw)
const finalRate = rate > rateMix ? rate : rateMix
const amountOutMin = rate > rateMix ? amountOutMin1 : amountOutMin2
const path_num = rate > rateMix ? 2 : 3
//out.exsParamA = amountOutMin;
//out.exsParamB = path;
out.rate = finalRate;
out.minOut = (out.exactIn * finalRate * 0.997).toFixed(6);
}
const abiCoder = new AbiCoder();
let path = [];
if (path_num == 3){
path.push(from_token.address);
path.push(weth.address);
path.push(to_token.address);
}else{
path.push(from_token.address);
path.push(to_token.address);
}
out.path = path;
//out.bytesData = abiCoder.encode(["address[]"], [path]);
return out;
}
/*
checks token decimals,
checks kyber exchange rate
returns a number and a BigNumber
*/
async function getKyberPriceTest(from_token, to_token, from_amount) {
const EMPTY_HINT = "0x";
let hint = EMPTY_HINT;
let out = {};
out.service = 0;
out.serviceName = 'kyber';
out.target = IKyberNetworkProxy_ADDRESS;
out.fromToken = from_token;
out.toToken = to_token;
out.exactIn = from_amount;
out.bytesData = '0x';//web3.codec.encode_abi(["address", "address"], [token_a, token_b])
out.bytesDataDesc = '';
out.path = [];
out.path.push(from_token.address);
out.path.push(to_token.address);
//const abiCoder = new AbiCoder();
//out.bytesData = abiCoder.encode(["address","address"], [from_token.address,to_token.address]);
let amountIn = ethers.utils.parseUnits(parseFloat(from_amount).toFixed(from_token.decimals).toString(),from_token.decimals)
const forward = await kyberswap.getExpectedRateAfterFee(from_token.address, to_token.address, amountIn,0,hint);//返回 是否已经扣除了0.3%的手续费? 包括平台费
const rate = ethers.utils.formatEther(forward)
//out.exsParamA = forward;
out.rate = rate;
out.minOut = (out.exactIn * rate).toFixed(6);
return out;
}
function compare(arg) {
return function(a, b) {
return a[arg] - b[arg];
}
}
async function fastGasPrice(type) {
try{
if (chainId == 1){
if(type == 1){
const url = 'https://data-api.defipulse.com/api/v1/egs/api/ethgasAPI.json?api-key=d1a64e1a4d0fa24976556e5d6c5bad136b313c5db0b30880e2fbc30c14e7';
let tracker = await httpApi.get(url);
let fastest = tracker.fastest / 10;
return fastest;
}else{
const url = 'https://api.etherscan.io/api?module=gastracker&action=gasoracle&apikey=6M5IBH6CQA6WH3DZ7X3Y143BKTT5BAKK9D';
let tracker = await httpApi.get(url);
//console.log(tracker);
if (tracker.status == 1){
return tracker.result.FastGasPrice; //GWei = 1e9 wei
}else{
return 99999999999999;
}
}
}else{
return 2;
}
} catch (err) {
console.log(err);
fs.writeFileSync("./logs/" + moment().format('YYYYMMDD') + "_error.txt",err,{flag:'a'});
return 99999999999999;
}
}
//某个币对(from_token===>to_token),在各交易所中交易,获得to_token数量大最的一个所在交易所
//此处,需要判断,这个币对,在这个 交易所 中是否存在?存在则加入数组,不存在,则不加入
async function swap2Max(from_uniToken, to_uniToken, _amount) {
exs = [];
exs.push(getUniPriceTest(from_uniToken, to_uniToken, _amount));
//exs.push(getKyberPriceTest(from_uniToken, to_uniToken, _amount));
//exs.push(getSushiPriceTest(from_uniToken, to_uniToken, _amount)); //此处,需要判断,这个币对,在这个 交易所 中是否存在?存在则加入数组,不存在,则不加入
let promises = await Promise.all(exs);
promises.sort(compare('minOut'));//返回 minOut 是已经扣除了交易所手续费,从小到大排序
//console.log(promises);
let max_minOut_ex = promises[promises.length-1];//max minOut,rate
return max_minOut_ex;
}
//_unipair 应该有顺序,上一个币对的输出,是下一个币对的输入,第一个跟最后一个应该是相同的
//如:[[eth, usdt], [usdt, usdc], [usdc, eth]] ==>eth,usdt,usdc,eth
//[[eth, usdt], [usdt, eth]] ===>eth,usdt
async function pathFinder(_unipair, _amount) {
const swapSetpOut = [];
if(_unipair.length > 0 && _unipair[0][0] == _unipair[_unipair.length - 1][1]) {
for (i = 0; i < _unipair.length; i++){
if (i < _unipair.length - 1){
if(_unipair[i][1] != _unipair[i + 1][0]) break;
}
let setp_choice = await swap2Max(_unipair[i][0],_unipair[i][1],_amount);
swapSetpOut.push(setp_choice);
_amount = setp_choice.minOut;
}
}
//console.log(swapSetpOut);
if (swapSetpOut.length != _unipair.length){
return [];
}else{
return swapSetpOut;
}
}
//借款币 与 eth 之间的兑换比例
async function getLoanTokenUniRate(loadToken) {
if (loadToken == uni_weth){
return 1;
}else{
let key = "eth_rate" + loadToken.address;
let rate = myCache.get(key);
if (rate == undefined){
let reserve = await getUniPriceTest(uni_weth, loadToken, 1),
rate = reserve.rate;
myCache.set("key",rate,300);//缓存5分钟
}
return rate;
}
}
//获得fastGasPrice
async function getfastGasPrice() {
let fastest = myCache.get("fastGasPrice");
if (fastest == undefined || _gasPrice == 0){
fastest = await fastGasPrice(1);
myCache.set("FastGasPrice",fastest,60);//缓存1分钟
}
return fastest;
}
async function check_swap(_flashex, _payOwner, _unipair, _amount) {
let out = {};
out.status = 0;//0:路由未执行成功;1:路由已经执行成功;2:不含执行合约手续费时,可套利;3:可套利;4:套利合约执行成功
out.datetime = moment().format('YYYY-MM-DD HH:mm:ss');
out.flashex = _flashex;
out.payOwner = _payOwner;
out.amount = _amount;
let amount = _amount;
let flashex = _flashex;//0:aave; 1:dydx
let payOwner = _payOwner;//1:收益直接转到owner中,当收益为负数时,终止交易损失gas费用; 0:收益保留在合约中,收益可以为负数,只要合约中的币够还款,主要用于测试时使用
console.log("amount:" + amount);
try{
let swapSetpOut = await pathFinder(_unipair, amount);
out.swapSetpOut = swapSetpOut;
if (swapSetpOut.length > 0 && swapSetpOut.length == _unipair.length){
let outAmount = swapSetpOut[swapSetpOut.length -1].minOut;
out.status = 1;
out.outAmount = outAmount;
//通过 交易路径 查找,最后一步获得的 minOut > amount 时,才有套利空间
// if (outAmount > amount){
console.log("outAmount:" + outAmount);
let loanToken = swapSetpOut[0].fromToken;//第一个为借款币
let amountIn = ethers.utils.parseUnits(parseFloat(amount).toFixed(loanToken.decimals).toString(),loanToken.decimals);//转为没小数位的,合约可以直接使用的数字
//let tokens = [];
//let exsID = [];
//let exsParamA = [];
//let exsParamB = [];
let gasLimitCacheKey = "gKey;" + flashex + ";" + payOwner + ";" + amount + ";";
let legos = [];
for (i = 0; i < swapSetpOut.length; i++){
//tokens.push(swapSetpOut[i].fromToken.address);
//exsID.push(swapSetpOut[i].exsID);
//exsParamA.push(swapSetpOut[i].exsParamA);
//exsParamB.push(swapSetpOut[i].exsParamB);
//console.log(swapSetpOut[i]);
//console.log(swapSetpOut[i].service);
let lego = {};
lego.service = swapSetpOut[i].service;
lego.target = swapSetpOut[i].target;
//lego.tokenIn = swapSetpOut[i].fromToken.address;
//lego.tokenOut = swapSetpOut[i].toToken.address;
lego.path = swapSetpOut[i].path;
lego.data = swapSetpOut[i].bytesData;
lego.out = 0;
//console.log(lego);
legos.push(lego);
gasLimitCacheKey = gasLimitCacheKey + swapSetpOut[i].fromToken.address + ";exsID:" + swapSetpOut[i].exsID + ";";
}
//console.log(key);
let gasLimitCacheMD5Key = md5(gasLimitCacheKey);
//exsID =[2,1];
console.log("flashex:" + flashex);
console.log("amountIn:" + amountIn);
console.log(legos);
//console.log(exsID);
//console.log(exsParamA);
//console.log(exsParamB);
//获得执行合约,需要的gas数量
let _gasLimit = myCache.get(gasLimitCacheMD5Key);
let gasLimitCache = true;
if (_gasLimit == undefined){
gasLimitCache = false;
/*
console.log("==================");
let legos2 = [];
let lego = legos[0];
legos2[0] = [[lego.service,lego.target,lego.tokenIn,lego.tokenOut,lego.data,lego.out]];//legos[0];
legos2[1] = [[lego.service,lego.target,lego.tokenIn,lego.tokenOut,lego.data,lego.out]];
console.log(legos2);
console.log("start estimateGas.flashloan");
let _pathPairs = await flashRobot.pathFinder1(legos2,amountIn);
console.log(_pathPairs[0]);
return {};
*/
console.log("start estimateGas.flashloan");
let gasUsed = await flashRobot.estimateGas.flashloan(
flashex,//_flashex 0:aave; 1:dydx
loanToken.address,//_reserve Kovan WETH
amountIn,//_amount
payOwner,
legos
);
_gasLimit = (ethers.utils.formatEther(gasUsed)*1e18 + 0.5).toFixed(0);
myCache.set(gasLimitCacheMD5Key,_gasLimit,10);//单位是秒,时间可以保存长点,大体值每次获得的应该差不多
console.log("end estimateGas._gasLimit:" + _gasLimit);
let data = moment().format('HH:mm:ss') + ";" + gasLimitCacheMD5Key + ";gasLimit:" + _gasLimit + ";" + gasLimitCacheKey + "\n";
fs.writeFileSync("./logs/getlimit_" + moment().format('YYYYMMDD') + ".txt",data,{flag:'a'});
}
out.gasLimitCacheKey = gasLimitCacheMD5Key;
out.gasLimitCache = gasLimitCache;
out.gasLimit = _gasLimit;
console.log(_gasLimit);
//获得gas 费用
let _gasPrice = await getfastGasPrice();
out.gasPrice = _gasPrice;
_gasPrice = _gasPrice * 1.1;//加10%GWei, 使合约被执行速度更快些
console.log(_gasPrice);
//Transaction (Tx) Fees= gasUsed * gasPrice [GWei]
// 3250000 * gasPrice / 1e9 [ETH]
let estimateTxFees = _gasLimit * _gasPrice / 1e9;// 计算执行合约的手续费,单位是eth
console.log("estimateTxFees:" + estimateTxFees);
out.estimateTxFees = estimateTxFees;//单位是eth
//借款币 与 eth 之间的兑换比例
//weth 与 loanToken 之间的价格,这里直接调用缓存,减少这边的执行速度
txcost = await getLoanTokenUniRate(loanToken) * estimateTxFees; //单位是 借款币种
out.txcost = txcost;// out.amount 正常要远远大于 out.txcost 才有可能套利
console.log("txcost:" + txcost);
if (flashex == 0){//aave
amount = amount * 1.0009;//aave 借款成本0.09%; dydx 的借款成本可以忽略不计只加了2wei
}
out.getAmountNotIncTxCost = outAmount - amount;//扣除手续费外的,预计获利数量
out.getAmount = outAmount - amount - txcost;//预计将获利数量
out.txHash = "";
if (out.getAmountNotIncTxCost > 0){
out.status = 2;
}
console.log("getAmountNotIncTxCost:" + out.getAmountNotIncTxCost);
console.log("getAmount:" + out.getAmount);
if (out.getAmount > 0 || true){
out.status = 3;
//_gasPrice = 2;//测试用
/*
//如果获得getEstimateGas值时,是从缓存获得,则需要再次执行一次flashRobot.estimateGas.flashloan,确保可以正常执行,而不会损失手续费;
//正式运行时,payOwner = 1,确保获得的outAmount在合约内够还款;如果不够还款时,estimateGas会报错,计算不出gasUsed值
if (gasLimitCache){
let gasUsed = await flashRobot.estimateGas.flashloan(
flashex,//_flashex 0:aave; 1:dydx
loanToken.address,//_reserve Kovan WETH
amountIn,//_amount
payOwner,
tokens,
exsID,
exsParamA,
exsParamB
);
_gasLimit = (ethers.utils.formatEther(gasUsed)*1e18 + 0.5).toFixed(0);
myCache.set(gasLimitCacheKey,_gasLimit,60);//单位是秒,时间可以保存长点,大体值每次获得的应该差不多
}
*/
const tx = await flashRobot.flashloan(
flashex,//_flashex 0:aave; 1:dydx
loanToken.address,//_reserve Kovan WETH
amountIn,//_amount
payOwner,
legos,
{gasLimit:_gasLimit,gasPrice:_gasPrice*1e9}
)
let receipt = await tx.wait()
// Inspect the transaction hash
out.txHash = receipt.transactionHash;
console.log("Tx Hash: ", out.txHash);
//console.log("Flashloan successful");
out.status = 4;
}
// }
}
}catch (err) {
console.log(err);
//fs.writeFileSync("./logs/" + moment().format('YYYYMMDD') + "_error.txt",err,{flag:'a'});
}
return out;
}
async function myFunc() {
let promises = await Promise.all([
getfastGasPrice(), //定时缓存,gas费用
//getLoadTokenRate(loadToken1),//除weth外,其它可以借款的币1
//getLoadTokenRate(loadToken2),//除weth外,其它可以借款的币2
]);
}
//_pairs 应该有顺序,上一个币对的输出,是下一个币对的输入,第一个跟最后一个应该是相同的
//如:[[eth, usdt], [usdt, usdc], [usdc, eth]] ==>eth,usdt,usdc,eth
//[[eth, usdt], [usdt, eth]] ===>eth,usdt
function getPathFinderPairs(_pairs) {
//const legos = [];
const pairs = [];
//第一个跟最后一个应该是相同的
if(_pairs.length > 0 && _pairs[0][0] == _pairs[_pairs.length - 1][1]) {
//检查币对执行顺序是否正确[上一个币对的输出,是下一个币对的输入]
let k = 0;
for (i = 0; i < _pairs.length; i++){
if (i < _pairs.length - 1){
if(_pairs[i][1] != _pairs[i + 1][0]) break;
}
k = k + 1;
}
if(k == _pairs.length){
for (i = 0; i < _pairs.length; i++){
pairs[i] = getSwapStructPairs(_pairs[i][0],_pairs[i][1]);
if (pairs[i].length == 0){
pairs = [];
break;//某个币对,没有一个交易所支持,则直接退出
}
}
}
}
//console.log(pairs);
if (pairs.length != _pairs.length){
return [];
}else{
return pairs;
}
}
//获得一个币对,返回所有支持的交易所队列
function getSwapStructPairs(_tokenIn, _tokenOut) {
const pairs = [];
for (j = 0; j < 6; j++){
let legos = getSwapStructs(j,_tokenIn,_tokenOut);
//console.log(legos);
for (k = 0; k < legos.length; k++){
let pair = legos[k];
if (pair.service >= 0){
pairs.push(pair);
}
}
}
return pairs;
}
//判断币对(_tokenIn===>_tokenOut),有几个交易所支持,把支持的交易所加入队列
function getSwapStructs(_service, _tokenIn, _tokenOut) {
//这里可以加个判断(从缓存中取出来),过滤币对不支付的交易所
let swapStructs = [];
let swapStruct = {};
swapStruct.service = _service;
swapStruct.target = '';
swapStruct.path = [];
swapStruct.path.push(_tokenIn);
swapStruct.path.push(_tokenOut);
swapStruct.data = '0x';
swapStruct.out = 0;
if (_service == 0){//kyber
swapStruct.target = IKyberNetworkProxy_ADDRESS;
}else if (_service == 1){//uniswap
swapStruct.target = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D';
/*
const abiCoder = new AbiCoder();
let path = [];
path.push(_tokenIn);
path.push(_tokenOut);
swapStruct.data = abiCoder.encode(["address[]"], [path]);
*/
if (!(_tokenIn == UNISWAP.WETH[chainId].address || _tokenOut == UNISWAP.WETH[chainId].address)){
let swapStruct2 = {};
swapStruct2.service = swapStruct.service;
swapStruct2.target = swapStruct.target;
swapStruct2.out = swapStruct.out;
swapStruct2.path = [];
swapStruct2.path.push(_tokenIn);
swapStruct2.path.push(UNISWAP.WETH[chainId].address);
swapStruct2.path.push(_tokenOut);
swapStructs.push(swapStruct2);
};
}else if (_service == 2){//sushiswap
swapStruct.service = -1;
/*
swapStruct.target = '0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F';
if (!(_tokenIn == SUSHISWAP.WETH[chainId].address || _tokenOut == SUSHISWAP.WETH[chainId].address)){
let swapStruct2 = {};
swapStruct2.service = swapStruct.service;
swapStruct2.target = swapStruct.target;
swapStruct2.out = swapStruct.out;
swapStruct2.path = [];
swapStruct2.path.push(_tokenIn);
swapStruct2.path.push(SUSHISWAP.WETH[chainId].address);
swapStruct2.path.push(_tokenOut);
swapStructs.push(swapStruct2);
};
*/
}else if (_service == 3){//balancer
swapStruct.service = -1;
}else if (_service == 4){//curve
swapStruct.service = -1;
}else if (_service == 5){//mooniswap
swapStruct.service = -1;
}else{
swapStruct.service = -1;
}
swapStructs.push(swapStruct);
return swapStructs;
}
let inTransaction = false;
const DAI = new UNISWAP.Token(42, '0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa', 18); //42
const KNC = new UNISWAP.Token(42, '0xad67cB4d63C9da94AcA37fDF2761AaDF780ff4a2', 18);
//const DAI = new UNISWAP.Token(1, '0x6B175474E89094C44Da98b954EedeAC495271d0F', 18);//1
async function myFunc2() {
if (inTransaction == false){
inTransaction = true;
let out = await check_swap(0,0,[[uni_weth, KNC], [KNC, uni_weth]],0.02);
if (out.status >= 2){//0:路由未执行成功;1:路由已经执行成功;2:不含执行合约手续费时,可套利;3:可套利;4:套利合约执行成功
fs.writeFileSync("./logs/status" + out.status + "_" + moment().format('YYYYMMDD') + ".txt", JSON.stringify(out) + "\n",{flag:'a'});
}
inTransaction = false;
}
}
//setInterval(myFunc, 1000);
//setInterval(myFunc2, 1000);
async function myFunc3() {
let loanToken = '';//借款币种
let amount = 0.01;//借款数量
let pairs = [[uni_weth.address, KNC.address], [KNC.address, uni_weth.address]];
let pathFinderPairs = getPathFinderPairs(pairs);
console.log(pathFinderPairs);
loanToken = pairs[0][0];//借款币种,等于pairs第一个币种
let wethToLoanPairs = [];
if (loanToken != uni_weth.address){
wethToLoanPairs = getSwapStructPairs(uni_weth.address, loanToken);//
}
//wethToLoanPairs = getSwapStructPairs(uni_weth.address, KNC.address);//
console.log(wethToLoanPairs);
let estimateTxFees = 1;//预估的合约执行费用(eth) estimateTxFees
let amountIn = ethers.utils.parseUnits(parseFloat(amount).toFixed(uni_weth.decimals).toString(),uni_weth.decimals);//转为没小数位的,合约可以直接使用的数字
console.log(amountIn);
console.log(ethers.utils.formatEther(amountIn));
console.log("=============1==============");
//pathFinderPairs = [[[1, "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",["0xd0A1E359811322d97991E03f863a0C30C2cF029C","0xad67cB4d63C9da94AcA37fDF2761AaDF780ff4a2"],"0x",0],[0, "0xc153eeAD19e0DBbDb3462Dcc2B703cC6D738A37c",["0xd0A1E359811322d97991E03f863a0C30C2cF029C","0xad67cB4d63C9da94AcA37fDF2761AaDF780ff4a2"],"0x",0]]];
//将预估的合约执行费用(eth) estimateTxFees,通过 wethToLoanPairs 换算成 借款币,如果借款币为weth则,无需换算,直接是1:1
let pathFinder = await flashRobot.pathFinder(pathFinderPairs,amountIn,wethToLoanPairs,estimateTxFees);
console.log(getAmountsOut(pathFinder[0]));
console.log(getContractPairs(pathFinder[1]));
console.log(getContractPairs(pathFinder[2]));
}
function getAmountsOut(_allPairs) {
let pairs = [];
for (i = 0; i < _allPairs.length; i++){
pairs[i] = getContractPairs(_allPairs[i]);
}
return pairs;
}
function getContractPairs(_contractPairs) {
let paths = [];
for (i = 0; i < _contractPairs.length; i++){
let pair = {};
pair.service = _contractPairs[i].service;
pair.target = _contractPairs[i].target;
pair.path = _contractPairs[i].path;
pair.data = _contractPairs[i].data;
pair.out = ethers.utils.formatEther(_contractPairs[i].out);
paths.push(pair);
}
return paths;
}
myFunc3();
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import { IERC20 } from "./Interfaces.sol";
import { SafeMath,SafeERC20 } from "./Libraries.sol";
import "./ISoloMargin.sol";
contract DydxFlashloanBase {
using SafeMath for uint256;
// -- Internal Helper functions -- //
function _getMarketIdFromTokenAddress(address _solo, address token)
internal
view
returns (uint256)
{
ISoloMargin solo = ISoloMargin(_solo);
uint256 numMarkets = solo.getNumMarkets();
address curToken;
for (uint256 i = 0; i < numMarkets; i++) {
curToken = solo.getMarketTokenAddress(i);
if (curToken == token) {
return i;
}
}
revert("No marketId found for provided token");
}
function _getRepaymentAmountInternal(uint256 amount)
internal
view
returns (uint256)
{
// Needs to be overcollateralize
// Needs to provide +2 wei to be safe
return amount.add(2);
}
function _getAccountInfo() internal view returns (Account.Info memory) {
return Account.Info({owner: address(this), number: 1});
}
function _getWithdrawAction(uint marketId, uint256 amount)
internal
view
returns (Actions.ActionArgs memory)
{
return
Actions.ActionArgs({
actionType: Actions.ActionType.Withdraw,
accountId: 0,
amount: Types.AssetAmount({
sign: false,
denomination: Types.AssetDenomination.Wei,
ref: Types.AssetReference.Delta,
value: amount
}),
primaryMarketId: marketId,
secondaryMarketId: 0,
otherAddress: address(this),
otherAccountId: 0,
data: ""
});
}
function _getCallAction(bytes memory data)
internal
view
returns (Actions.ActionArgs memory)
{
return
Actions.ActionArgs({
actionType: Actions.ActionType.Call,
accountId: 0,
amount: Types.AssetAmount({
sign: false,
denomination: Types.AssetDenomination.Wei,
ref: Types.AssetReference.Delta,
value: 0
}),
primaryMarketId: 0,
secondaryMarketId: 0,
otherAddress: address(this),
otherAccountId: 0,
data: data
});
}
function _getDepositAction(uint marketId, uint256 amount)
internal
view
returns (Actions.ActionArgs memory)
{
return
Actions.ActionArgs({
actionType: Actions.ActionType.Deposit,
accountId: 0,
amount: Types.AssetAmount({
sign: true,
denomination: Types.AssetDenomination.Wei,
ref: Types.AssetReference.Delta,
value: amount
}),
primaryMarketId: marketId,
secondaryMarketId: 0,
otherAddress: address(this),
otherAccountId: 0,
data: ""
});
}
}
import { IERC20,IWETH9 } from "./Interfaces.sol";
import { SafeMath,SafeERC20 } from "./Libraries.sol";
contract FlashBase {
using SafeMath for uint256;
using SafeERC20 for IERC20;
address ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
address payable public owner;
IWETH9 WETH9;
modifier onlyOwner {
require(msg.sender == owner);
_;
}
receive() external payable {}
constructor(uint256 network_id) public {
owner = msg.sender;
if (network_id == 1){
//mainnet
WETH9 = IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
}else if (network_id == 3){
//ropsten aave v2 没有 ropsten 网络的测试合约
}else if (network_id == 42){
//kovan
WETH9 = IWETH9(0xd0A1E359811322d97991E03f863a0C30C2cF029C);
}
}
function tokenBalanceOf(address _assetAddress) public view returns (uint256) {
if (_assetAddress == ETH_TOKEN_ADDRESS){
return address(this).balance;
}else{
return IERC20(_assetAddress).balanceOf(address(this));
}
}
// 合约自毁并且把金额传到指定账户
function kill() public onlyOwner{
selfdestruct(owner); // 自毁函数,ERC20代币不会被转走
}
/**
* @dev Withdraw asset.
* @param _assetAddress Asset to be withdrawn.
*/
function withdraw(address _assetAddress) public onlyOwner {
withdraw2(_assetAddress);
}
function withdraw2(address _assetAddress) internal {
uint assetBalance;
if (_assetAddress == ETH_TOKEN_ADDRESS) {
address self = address(this); // workaround for a possible solidity bug
assetBalance = self.balance;
owner.transfer(assetBalance);
} else {
assetBalance = IERC20(_assetAddress).balanceOf(address(this));
IERC20(_assetAddress).safeTransfer(owner, assetBalance);
}
//emit LogWithdraw(msg.sender, _assetAddress, assetBalance);
}
}
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import { IFlashLoanReceiver, ILendingPoolAddressesProvider, ILendingPool, IERC20,IWETH9,UniswapV2Router02,IKyberNetworkProxy,ISimpleKyberProxy,ICurvePool,Mooniswap,BPool } from "./Interfaces.sol";
import { SafeMath,SafeERC20 } from "./Libraries.sol";
import { Account,ISoloMargin,Actions } from "./ISoloMargin.sol";
import { DydxFlashloanBase } from "./DydxFlashloanBase.sol";
import { FlashBase } from "./FlashBase.sol";
//import { ExSwap } from "./ExSwap.sol";
contract FlashRobot is IFlashLoanReceiver, DydxFlashloanBase, FlashBase {
address dydxSoloMarginAddr;
struct MyCustomData {
address token;
uint256 amount;
uint256 repayAmount;
bytes params;
}
enum Service {
kyber, //0
uniswap, //1
sushiswap,//2
balancer,//3
curve,//4
mooniswap,//5
wethtoeth,//6
ethtoweth//7
}
struct SwapStruct {
Service service;
address target;
address[] path;//path[0] = tokenIn, path[1] = tokenOut
bytes data;
uint out;
}
ILendingPoolAddressesProvider addressesProvider;
ILendingPool aaveLendingPool;
//ExSwap exSwap;
//constructor(uint256 network_id) public payable {
constructor(uint256 network_id) FlashBase(network_id) public {
//owner = msg.sender;
if (network_id == 1){
//mainnet
addressesProvider = ILendingPoolAddressesProvider(address(0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5));
dydxSoloMarginAddr = 0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e;
}else if (network_id == 3){
//ropsten aave v2 没有 ropsten 网络的测试合约
//_AaveLendingPool = ILendingPool(ILendingPoolAddressesProvider(address(0x0000000000000000000000000000000000000000)).getLendingPool());
//_addressProvider = ILendingPoolAddressesProvider(address(0x652B2937Efd0B5beA1c8d54293FC1289672AFC6b));
}else if (network_id == 42){
//kovan
addressesProvider = ILendingPoolAddressesProvider(address(0x652B2937Efd0B5beA1c8d54293FC1289672AFC6b));
dydxSoloMarginAddr = 0x4EC3570cADaAEE08Ae384779B0f3A45EF85289DE;
}
aaveLendingPool = ILendingPool(addressesProvider.getLendingPool());
}
function flashloan(uint8 _flashex,address _reserve, uint _amount, uint8 _payOwner, SwapStruct[] memory _swapStructs) public {
bytes memory params = abi.encode(_payOwner,_swapStructs);
//exSwap = ExSwap(_exSwap);
if (_flashex == 0){//"aave"
address[] memory assets = new address[](1);
assets[0] = _reserve;//address(0xFf795577d9AC8bD7D90Ee22b6C1703490b6512FD); // Kovan DAI
uint256[] memory amounts = new uint256[](1);
amounts[0] = _amount;
// 0 = no debt, 1 = stable, 2 = variable
uint256[] memory modes = new uint256[](1);
modes[0] = 0;
address onBehalfOf = address(this);
//bytes memory params = "";
uint16 referralCode = 0;
aaveLendingPool.flashLoan(
address(this),
assets,
amounts,
modes,
onBehalfOf,
params,
referralCode
);
//withdraw2(_reserve);
}else if (_flashex == 1){//'dydx'
dydxFlashLoan(dydxSoloMarginAddr,_reserve,_amount,params);
//withdraw2(_reserve);
}else{
require(false,"_flashex error!");
}
//withdraw(_reserve);
}
// _solo = dydxAddr
// _token = WETHAddr
// _amount 借贷数量
function dydxFlashLoan(address _solo, address _token, uint256 _amount, bytes memory params) public
{
ISoloMargin solo = ISoloMargin(_solo);
// Get marketId from token address
uint256 marketId = _getMarketIdFromTokenAddress(_solo, _token);
// Calculate repay amount (_amount + (2 wei))
// Approve transfer from
uint256 repayAmount = _getRepaymentAmountInternal(_amount);
IERC20(_token).approve(_solo, repayAmount);
// 1. Withdraw $
// 2. Call callFunction(...)
// 3. Deposit back $
Actions.ActionArgs[] memory operations = new Actions.ActionArgs[](3);
operations[0] = _getWithdrawAction(marketId, _amount);
operations[1] = _getCallAction(
// Encode MyCustomData for callFunction
abi.encode(MyCustomData({token: _token, amount: _amount, repayAmount: repayAmount, params: params}))
//abi.encode(_token)
);
operations[2] = _getDepositAction(marketId, repayAmount);
Account.Info[] memory accountInfos = new Account.Info[](1);
accountInfos[0] = _getAccountInfo();
solo.operate(accountInfos, operations);
}
function callFunction(
address sender,
Account.Info memory account,
bytes memory data
) public {
MyCustomData memory mcd = abi.decode(data, (MyCustomData));
//IERC20(mcd.token).safeTransfer(address(exSwap), mcd.amount);
//exSwap.executeSwap(mcd.token,mcd.amount,mcd.repayAmount,mcd.params);
//executeSwap(mcd.token,mcd.amount,mcd.repayAmount,mcd.params);
execBatch(mcd.token,mcd.amount,mcd.repayAmount,mcd.params);
uint256 balOfLoanedToken = IERC20(mcd.token).balanceOf(address(this));
// Note that you can ignore the line below
// if your dydx account (this contract in this case)
// has deposited at least ~2 Wei of assets into the account
// to balance out the collaterization ratio
//WETH9.deposit{value: balOfLoanedToken.add(2)};
//uint256 newBal = IERC20(mcd.token).balanceOf(address(this));
require(
balOfLoanedToken >= mcd.repayAmount,
"Not enough funds to repay dydx loan!"
);
}
/**
This function is called after your contract has received the flash loaned amount
*/
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address initiator,
bytes calldata params
)
external
override
returns (bool)
{
require(assets.length == 1,"Failed assets.length > 1");
uint256 amountOwing = amounts[0].add(premiums[0]);
//IERC20(assets[0]).safeTransfer(address(exSwap), amounts[0]);
//exSwap.executeSwap(assets[0],amounts[0],amountOwing,params);
//executeSwap(assets[0],amounts[0],amountOwing,params);
execBatch(assets[0],amounts[0],amountOwing,params);
IERC20(assets[0]).approve(address(aaveLendingPool), amountOwing);
return true;
}
//executes the pipe execution
function execBatch(address _reserve, uint256 _amount, uint256 _repayAmount, bytes memory _params) public{
SwapStruct[] memory _swapStructs;
uint8 payOwner;
(payOwner,_swapStructs) = abi.decode(_params,(uint8,SwapStruct[]));
uint256 io = _amount;
for (uint i = 0; i < _swapStructs.length; i++) {
io = _executeBlock(_swapStructs[i], io);
}
//payOwner == 1 将赚的币(扣除借款+手续费后)转走; 如果赚的币,不够还款,sub则会报错,终止操作,损失gas手续费
//payOwner == 0,赚取的币先保留在合约中, 如果赚取的币,不够还款时,会使用合约上的币来还款(合约上有币的话),如果都不够时,失败,损失gas手续费;
//payOwner == 0 可以给合约转币,用来还款使用,方便测试(有时无法套利到,但需要测试时,可以这种方式来操作)
if (payOwner == 1){
uint assetBalance = io.sub(_repayAmount);
IERC20(_reserve).safeTransfer(owner, assetBalance);
//IERC20(_reserve).safeTransfer(msg.sender, _repayAmount);
}else{
//IERC20(_reserve).safeTransfer(msg.sender, to_amount);
}
}
function _executeBlock(SwapStruct memory _swapStruct, uint _in) internal returns(uint out) {
if (_swapStruct.service == Service.uniswap || _swapStruct.service == Service.sushiswap) {
//address[] memory path = _decodeUniswap(_swapStruct.data);
IERC20(_swapStruct.path[0]).approve(_swapStruct.target, _in);
uint[] memory outputs = UniswapV2Router02(_swapStruct.target).swapExactTokensForTokens(_in, 0, _swapStruct.path, address(this), now);//block.timestamp + 10);
out = outputs[outputs.length - 1];
}
else if (_swapStruct.service == Service.mooniswap) {
if (_swapStruct.path[0] == address(0)) {
WETH9.withdraw(_in);
out = Mooniswap(_swapStruct.target).swap{value: _in}(_swapStruct.path[0], _swapStruct.path[1], _in, 0, address(0));
}
else{
IERC20(_swapStruct.path[0]).approve(_swapStruct.target, _in);
out = Mooniswap(_swapStruct.target).swap(_swapStruct.path[0], _swapStruct.path[1], _in, 0, address(0));
if (_swapStruct.path[1] == address(0))
WETH9.deposit{value: out}();
}
}
else if (_swapStruct.service == Service.balancer) {
IERC20(_swapStruct.path[0]).approve(_swapStruct.target, _in);
(out,) = BPool(_swapStruct.target).swapExactAmountIn(_swapStruct.path[0], _in, _swapStruct.path[1], 0, uint(-1));
}
else if (_swapStruct.service == Service.kyber) {
IERC20(_swapStruct.path[0]).approve(_swapStruct.target, _in);
out = ISimpleKyberProxy(_swapStruct.target).swapTokenToToken(IERC20(_swapStruct.path[0]),_in,IERC20(_swapStruct.path[1]),0);
}
else if (_swapStruct.service == Service.curve) {
(int128 a, int128 b) = _decodeCurve(_swapStruct.data);
IERC20(ICurvePool(_swapStruct.target).underlying_coins(a)).approve(_swapStruct.target, _in);
ICurvePool(_swapStruct.target).exchange_underlying(a, b, _in, out);
out = IERC20(ICurvePool(_swapStruct.target).underlying_coins(b)).balanceOf(address(this));
}
else if (_swapStruct.service == Service.wethtoeth) {
WETH9.withdraw(_in);//weth==>eth;
out = _in;
}
else if (_swapStruct.service == Service.ethtoweth) {
WETH9.deposit{value: _in}();//eth ==> weth
out = _in;
}
}
function _decodeCurve(bytes memory data) internal pure returns(int128, int128) {
return abi.decode(data, (int128, int128));
}
//将预估的合约执行费用(eth) estimateTxFees,通过 wethToLoanPairs 换算成 借款币,如果借款币为weth则,无需换算,直接是1:1
function pathFinder(SwapStruct[][] memory _pairs, uint _amount, SwapStruct[] memory _wethToLoanPairs, uint _estimateTxFees) public view returns(SwapStruct[][] memory, SwapStruct[] memory, SwapStruct[] memory) {
SwapStruct[] memory pathPairs = new SwapStruct[](_pairs.length);
uint in_out = _amount;
for (uint i = 0; i < _pairs.length; i++) {
pathPairs[i] = _pairs[i][0];
for (uint j = 0; j < _pairs[i].length; j++) {
try this._getAmountsOut(_pairs[i][j], in_out) returns (uint out) {
_pairs[i][j].out = out;
} catch {
_pairs[i][j].out = 0;
}
//_pairs[i][j].out = _getAmountsOut(_pairs[i][j], in_out);
if (pathPairs[i].out < _pairs[i][j].out){
pathPairs[i] = _pairs[i][j];
}
}
in_out = pathPairs[i].out;
}
for (uint i = 0; i < _wethToLoanPairs.length; i++) {
try this._getAmountsOut(_wethToLoanPairs[i], _estimateTxFees) returns (uint out) {
_wethToLoanPairs[i].out = out;
} catch {
_wethToLoanPairs[i].out = 0;
}
}
return (_pairs,pathPairs,_wethToLoanPairs);
}
//获得币对 在交易所中的兑换数量; 如果返回 0,则说明该交易所不存在这个币对
function getAmountsOut(SwapStruct[] memory _swapStructs, uint _in) public view returns(SwapStruct[] memory) {
for (uint i = 0; i < _swapStructs.length; i++) {
try this._getAmountsOut(_swapStructs[i], _in) returns (uint out) {
_swapStructs[i].out = out;
} catch {
_swapStructs[i].out = 0;
}
}
return _swapStructs;
}
//获得币对 在交易所中的兑换数量; 如果报错,则说明该交易所不存在这个币对
function _getAmountsOut(SwapStruct memory _swapStruct, uint _in) public view returns (uint out){
if (_swapStruct.service == Service.uniswap || _swapStruct.service == Service.sushiswap) {
//address[] memory path = _decodeUniswap(_swapStruct.data);
uint[] memory outputs = UniswapV2Router02(_swapStruct.target).getAmountsOut(_in, _swapStruct.path);
out = outputs[outputs.length - 1];
}
else if (_swapStruct.service == Service.mooniswap) {
out = Mooniswap(_swapStruct.target).getReturn(_swapStruct.path[0], _swapStruct.path[1], _in);
}
else if (_swapStruct.service == Service.balancer) {
BPool pool = BPool(_swapStruct.target);
out = BPool(_swapStruct.target).calcOutGivenIn(
pool.getBalance(_swapStruct.path[0]),
pool.getDenormalizedWeight(_swapStruct.path[0]),
pool.getBalance(_swapStruct.path[1]),
pool.getDenormalizedWeight(_swapStruct.path[1]),
_in,
pool.getSwapFee());
}
else if (_swapStruct.service == Service.curve) {
(int128 a, int128 b) = _decodeCurve(_swapStruct.data);
out = ICurvePool(_swapStruct.target).get_dy_underlying(a, b, _in).mul(99999).div(100000);
}
else if (_swapStruct.service == Service.kyber) {
out = IKyberNetworkProxy(_swapStruct.target).getExpectedRateAfterFee(IERC20(_swapStruct.path[0]),IERC20(_swapStruct.path[1]),_in,0,'');
}
}
}
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import { DataTypes } from "./Libraries.sol";
interface IWETH9 {
function balanceOf(address account) external view returns (uint256);
function deposit() external payable;
function withdraw(uint wad) external;
function transfer(address recipient, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
}
interface BPool {
function swapExactAmountIn(
address tokenIn,
uint tokenAmountIn,
address tokenOut,
uint minAmountOut,
uint maxPrice
) external returns (uint tokenAmountOut, uint spotPriceAfter);
function calcOutGivenIn(
uint tokenBalanceIn,
uint tokenWeightIn,
uint tokenBalanceOut,
uint tokenWeightOut,
uint tokenAmountIn,
uint swapFee
) external pure returns (uint tokenAmountOut);
function getDenormalizedWeight(address token) external view returns (uint);
function totalSupply() external view returns (uint);
function getTotalDenormalizedWeight() external view returns (uint);
function getSwapFee() external view returns (uint);
function getBalance(address token) external view returns (uint);
}
interface ICurvePool {
function get_dy_underlying(int128 i, int128 j, uint256 dx) external view returns(uint256);
function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 min_dy) external;
function exchange(int128 i, int128 j, uint256 min_dy) external;
function underlying_coins(int128 coin) external view returns(address);
}
// deployer is 0x71CD6666064C3A1354a3B4dca5fA1E2D3ee7D303
interface Mooniswap {
function getReturn(address src, address dst, uint256 amount) external view returns(uint256);
function swap(
address src,
address dst,
uint256 amount,
uint256 minReturn,
address referral) external payable returns(uint256 result);
}
interface ISimpleKyberProxy {
function swapTokenToEther(
IERC20 token,
uint256 srcAmount,
uint256 minConversionRate
) external returns (uint256 destAmount);
function swapEtherToToken(IERC20 token, uint256 minConversionRate)
external
payable
returns (uint256 destAmount);
function swapTokenToToken(
IERC20 src,
uint256 srcAmount,
IERC20 dest,
uint256 minConversionRate
) external returns (uint256 destAmount);
}
interface IKyberNetworkProxy {
event ExecuteTrade(
address indexed trader,
IERC20 src,
IERC20 dest,
address destAddress,
uint256 actualSrcAmount,
uint256 actualDestAmount,
address platformWallet,
uint256 platformFeeBps
);
function tradeWithHintAndFee(
IERC20 src,
uint256 srcAmount,
IERC20 dest,
address payable destAddress,
uint256 maxDestAmount,
uint256 minConversionRate,
address payable platformWallet,
uint256 platformFeeBps,
bytes calldata hint
) external payable returns (uint256 destAmount);
function trade(
IERC20 src,
uint256 srcAmount,
IERC20 dest,
address payable destAddress,
uint256 maxDestAmount,
uint256 minConversionRate,
address payable platformWallet
) external payable returns (uint256);
function getExpectedRateAfterFee(
IERC20 src,
IERC20 dest,
uint256 srcQty,
uint256 platformFeeBps,
bytes calldata hint
) external view returns (uint256 expectedRate);
}
interface UniswapV2Router02 {
function WETH() external pure returns (address);
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
}
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
interface IFlashLoanReceiver {
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address initiator,
bytes calldata params
) external returns (bool);
}
/**
* @title LendingPoolAddressesProvider contract
* @dev Main registry of addresses part of or connected to the protocol, including permissioned roles
* - Acting also as factory of proxies and admin of those, so with right to change its implementations
* - Owned by the Aave Governance
* @author Aave
**/
interface ILendingPoolAddressesProvider {
event LendingPoolUpdated(address indexed newAddress);
event ConfigurationAdminUpdated(address indexed newAddress);
event EmergencyAdminUpdated(address indexed newAddress);
event LendingPoolConfiguratorUpdated(address indexed newAddress);
event LendingPoolCollateralManagerUpdated(address indexed newAddress);
event PriceOracleUpdated(address indexed newAddress);
event LendingRateOracleUpdated(address indexed newAddress);
event ProxyCreated(bytes32 id, address indexed newAddress);
event AddressSet(bytes32 id, address indexed newAddress, bool hasProxy);
function setAddress(bytes32 id, address newAddress) external;
function setAddressAsProxy(bytes32 id, address impl) external;
function getAddress(bytes32 id) external view returns (address);
function getLendingPool() external view returns (address);
function setLendingPoolImpl(address pool) external;
function getLendingPoolConfigurator() external view returns (address);
function setLendingPoolConfiguratorImpl(address configurator) external;
function getLendingPoolCollateralManager() external view returns (address);
function setLendingPoolCollateralManager(address manager) external;
function getPoolAdmin() external view returns (address);
function setPoolAdmin(address admin) external;
function getEmergencyAdmin() external view returns (address);
function setEmergencyAdmin(address admin) external;
function getPriceOracle() external view returns (address);
function setPriceOracle(address priceOracle) external;
function getLendingRateOracle() external view returns (address);
function setLendingRateOracle(address lendingRateOracle) external;
}
interface ILendingPool {
/**
* @dev Emitted on deposit()
* @param reserve The address of the underlying asset of the reserve
* @param user The address initiating the deposit
* @param onBehalfOf The beneficiary of the deposit, receiving the aTokens
* @param amount The amount deposited
* @param referral The referral code used
**/
event Deposit(
address indexed reserve,
address user,
address indexed onBehalfOf,
uint256 amount,
uint16 indexed referral
);
/**
* @dev Emitted on withdraw()
* @param reserve The address of the underlyng asset being withdrawn
* @param user The address initiating the withdrawal, owner of aTokens
* @param to Address that will receive the underlying
* @param amount The amount to be withdrawn
**/
event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);
/**
* @dev Emitted on borrow() and flashLoan() when debt needs to be opened
* @param reserve The address of the underlying asset being borrowed
* @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just
* initiator of the transaction on flashLoan()
* @param onBehalfOf The address that will be getting the debt
* @param amount The amount borrowed out
* @param borrowRateMode The rate mode: 1 for Stable, 2 for Variable
* @param borrowRate The numeric rate at which the user has borrowed
* @param referral The referral code used
**/
event Borrow(
address indexed reserve,
address user,
address indexed onBehalfOf,
uint256 amount,
uint256 borrowRateMode,
uint256 borrowRate,
uint16 indexed referral
);
/**
* @dev Emitted on repay()
* @param reserve The address of the underlying asset of the reserve
* @param user The beneficiary of the repayment, getting his debt reduced
* @param repayer The address of the user initiating the repay(), providing the funds
* @param amount The amount repaid
**/
event Repay(
address indexed reserve,
address indexed user,
address indexed repayer,
uint256 amount
);
/**
* @dev Emitted on swapBorrowRateMode()
* @param reserve The address of the underlying asset of the reserve
* @param user The address of the user swapping his rate mode
* @param rateMode The rate mode that the user wants to swap to
**/
event Swap(address indexed reserve, address indexed user, uint256 rateMode);
/**
* @dev Emitted on setUserUseReserveAsCollateral()
* @param reserve The address of the underlying asset of the reserve
* @param user The address of the user enabling the usage as collateral
**/
event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);
/**
* @dev Emitted on setUserUseReserveAsCollateral()
* @param reserve The address of the underlying asset of the reserve
* @param user The address of the user enabling the usage as collateral
**/
event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
/**
* @dev Emitted on rebalanceStableBorrowRate()
* @param reserve The address of the underlying asset of the reserve
* @param user The address of the user for which the rebalance has been executed
**/
event RebalanceStableBorrowRate(address indexed reserve, address indexed user);
/**
* @dev Emitted on flashLoan()
* @param target The address of the flash loan receiver contract
* @param initiator The address initiating the flash loan
* @param asset The address of the asset being flash borrowed
* @param amount The amount flash borrowed
* @param premium The fee flash borrowed
* @param referralCode The referral code used
**/
event FlashLoan(
address indexed target,
address indexed initiator,
address indexed asset,
uint256 amount,
uint256 premium,
uint16 referralCode
);
/**
* @dev Emitted when the pause is triggered.
*/
event Paused();
/**
* @dev Emitted when the pause is lifted.
*/
event Unpaused();
/**
* @dev Emitted when a borrower is liquidated. This event is emitted by the LendingPool via
* LendingPoolCollateral manager using a DELEGATECALL
* This allows to have the events in the generated ABI for LendingPool.
* @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
* @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
* @param user The address of the borrower getting liquidated
* @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
* @param liquidatedCollateralAmount The amount of collateral received by the liiquidator
* @param liquidator The address of the liquidator
* @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
* to receive the underlying collateral asset directly
**/
event LiquidationCall(
address indexed collateralAsset,
address indexed debtAsset,
address indexed user,
uint256 debtToCover,
uint256 liquidatedCollateralAmount,
address liquidator,
bool receiveAToken
);
/**
* @dev Emitted when the state of a reserve is updated. NOTE: This event is actually declared
* in the ReserveLogic library and emitted in the updateInterestRates() function. Since the function is internal,
* the event will actually be fired by the LendingPool contract. The event is therefore replicated here so it
* gets added to the LendingPool ABI
* @param reserve The address of the underlying asset of the reserve
* @param liquidityRate The new liquidity rate
* @param stableBorrowRate The new stable borrow rate
* @param variableBorrowRate The new variable borrow rate
* @param liquidityIndex The new liquidity index
* @param variableBorrowIndex The new variable borrow index
**/
event ReserveDataUpdated(
address indexed reserve,
uint256 liquidityRate,
uint256 stableBorrowRate,
uint256 variableBorrowRate,
uint256 liquidityIndex,
uint256 variableBorrowIndex
);
/**
* @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
* - E.g. User deposits 100 USDC and gets in return 100 aUSDC
* @param asset The address of the underlying asset to deposit
* @param amount The amount to be deposited
* @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
* wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
* is a different wallet
* @param referralCode Code used to register the integrator originating the operation, for potential rewards.
* 0 if the action is executed directly by the user, without any middle-man
**/
function deposit(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode
) external;
/**
* @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
* E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
* @param asset The address of the underlying asset to withdraw
* @param amount The underlying amount to be withdrawn
* - Send the value type(uint256).max in order to withdraw the whole aToken balance
* @param to Address that will receive the underlying, same as msg.sender if the user
* wants to receive it on his own wallet, or a different address if the beneficiary is a
* different wallet
**/
function withdraw(
address asset,
uint256 amount,
address to
) external;
/**
* @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower
* already deposited enough collateral, or he was given enough allowance by a credit delegator on the
* corresponding debt token (StableDebtToken or VariableDebtToken)
* - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet
* and 100 stable/variable debt tokens, depending on the `interestRateMode`
* @param asset The address of the underlying asset to borrow
* @param amount The amount to be borrowed
* @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable
* @param referralCode Code used to register the integrator originating the operation, for potential rewards.
* 0 if the action is executed directly by the user, without any middle-man
* @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself
* calling the function if he wants to borrow against his own collateral, or the address of the credit delegator
* if he has been given credit delegation allowance
**/
function borrow(
address asset,
uint256 amount,
uint256 interestRateMode,
uint16 referralCode,
address onBehalfOf
) external;
/**
* @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned
* - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address
* @param asset The address of the borrowed underlying asset previously borrowed
* @param amount The amount to repay
* - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
* @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
* @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the
* user calling the function if he wants to reduce/remove his own debt, or the address of any other
* other borrower whose debt should be removed
**/
function repay(
address asset,
uint256 amount,
uint256 rateMode,
address onBehalfOf
) external;
/**
* @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa
* @param asset The address of the underlying asset borrowed
* @param rateMode The rate mode that the user wants to swap to
**/
function swapBorrowRateMode(address asset, uint256 rateMode) external;
/**
* @dev Rebalances the stable interest rate of a user to the current stable rate defined on the reserve.
* - Users can be rebalanced if the following conditions are satisfied:
* 1. Usage ratio is above 95%
* 2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been
* borrowed at a stable rate and depositors are not earning enough
* @param asset The address of the underlying asset borrowed
* @param user The address of the user to be rebalanced
**/
function rebalanceStableBorrowRate(address asset, address user) external;
/**
* @dev Allows depositors to enable/disable a specific deposited asset as collateral
* @param asset The address of the underlying asset deposited
* @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise
**/
function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external;
/**
* @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1
* - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives
* a proportionally amount of the `collateralAsset` plus a bonus to cover market risk
* @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
* @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
* @param user The address of the borrower getting liquidated
* @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
* @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
* to receive the underlying collateral asset directly
**/
function liquidationCall(
address collateralAsset,
address debtAsset,
address user,
uint256 debtToCover,
bool receiveAToken
) external;
/**
* @dev Allows smartcontracts to access the liquidity of the pool within one transaction,
* as long as the amount taken plus a fee is returned.
* IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration.
* For further details please visit https://developers.aave.com
* @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface
* @param assets The addresses of the assets being flash-borrowed
* @param amounts The amounts amounts being flash-borrowed
* @param modes Types of the debt to open if the flash loan is not returned:
* 0 -> Don't open any debt, just revert if funds can't be transferred from the receiver
* 1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
* 2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
* @param onBehalfOf The address that will receive the debt in the case of using on `modes` 1 or 2
* @param params Variadic packed params to pass to the receiver as extra information
* @param referralCode Code used to register the integrator originating the operation, for potential rewards.
* 0 if the action is executed directly by the user, without any middle-man
**/
function flashLoan(
address receiverAddress,
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata modes,
address onBehalfOf,
bytes calldata params,
uint16 referralCode
) external;
/**
* @dev Returns the user account data across all the reserves
* @param user The address of the user
* @return totalCollateralETH the total collateral in ETH of the user
* @return totalDebtETH the total debt in ETH of the user
* @return availableBorrowsETH the borrowing power left of the user
* @return currentLiquidationThreshold the liquidation threshold of the user
* @return ltv the loan to value of the user
* @return healthFactor the current health factor of the user
**/
function getUserAccountData(address user)
external
view
returns (
uint256 totalCollateralETH,
uint256 totalDebtETH,
uint256 availableBorrowsETH,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
);
function initReserve(
address reserve,
address aTokenAddress,
address stableDebtAddress,
address variableDebtAddress,
address interestRateStrategyAddress
) external;
function setReserveInterestRateStrategyAddress(address reserve, address rateStrategyAddress)
external;
function setConfiguration(address reserve, uint256 configuration) external;
/**
* @dev Returns the configuration of the reserve
* @param asset The address of the underlying asset of the reserve
* @return The configuration of the reserve
**/
function getConfiguration(address asset) external view returns (DataTypes.ReserveConfigurationMap memory);
/**
* @dev Returns the configuration of the user across all the reserves
* @param user The user address
* @return The configuration of the user
**/
function getUserConfiguration(address user) external view returns (DataTypes.UserConfigurationMap memory);
/**
* @dev Returns the normalized income normalized income of the reserve
* @param asset The address of the underlying asset of the reserve
* @return The reserve's normalized income
*/
function getReserveNormalizedIncome(address asset) external view returns (uint256);
/**
* @dev Returns the normalized variable debt per unit of asset
* @param asset The address of the underlying asset of the reserve
* @return The reserve normalized variable debt
*/
function getReserveNormalizedVariableDebt(address asset) external view returns (uint256);
/**
* @dev Returns the state and configuration of the reserve
* @param asset The address of the underlying asset of the reserve
* @return The state of the reserve
**/
function getReserveData(address asset) external view returns (DataTypes.ReserveData memory);
function finalizeTransfer(
address asset,
address from,
address to,
uint256 amount,
uint256 balanceFromAfter,
uint256 balanceToBefore
) external;
function getReservesList() external view returns (address[] memory);
function getAddressesProvider() external view returns (ILendingPoolAddressesProvider);
function setPause(bool val) external;
function paused() external view returns (bool);
}
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
library Account {
enum Status {Normal, Liquid, Vapor}
struct Info {
address owner; // The address that owns the account
uint256 number; // A nonce that allows a single address to control many accounts
}
struct Storage {
mapping(uint256 => Types.Par) balances; // Mapping from marketId to principal
Status status;
}
}
library Actions {
enum ActionType {
Deposit, // supply tokens
Withdraw, // borrow tokens
Transfer, // transfer balance between accounts
Buy, // buy an amount of some token (publicly)
Sell, // sell an amount of some token (publicly)
Trade, // trade tokens against another account
Liquidate, // liquidate an undercollateralized or expiring account
Vaporize, // use excess tokens to zero-out a completely negative account
Call // send arbitrary data to an address
}
enum AccountLayout {OnePrimary, TwoPrimary, PrimaryAndSecondary}
enum MarketLayout {ZeroMarkets, OneMarket, TwoMarkets}
struct ActionArgs {
ActionType actionType;
uint256 accountId;
Types.AssetAmount amount;
uint256 primaryMarketId;
uint256 secondaryMarketId;
address otherAddress;
uint256 otherAccountId;
bytes data;
}
struct DepositArgs {
Types.AssetAmount amount;
Account.Info account;
uint256 market;
address from;
}
struct WithdrawArgs {
Types.AssetAmount amount;
Account.Info account;
uint256 market;
address to;
}
struct TransferArgs {
Types.AssetAmount amount;
Account.Info accountOne;
Account.Info accountTwo;
uint256 market;
}
struct BuyArgs {
Types.AssetAmount amount;
Account.Info account;
uint256 makerMarket;
uint256 takerMarket;
address exchangeWrapper;
bytes orderData;
}
struct SellArgs {
Types.AssetAmount amount;
Account.Info account;
uint256 takerMarket;
uint256 makerMarket;
address exchangeWrapper;
bytes orderData;
}
struct TradeArgs {
Types.AssetAmount amount;
Account.Info takerAccount;
Account.Info makerAccount;
uint256 inputMarket;
uint256 outputMarket;
address autoTrader;
bytes tradeData;
}
struct LiquidateArgs {
Types.AssetAmount amount;
Account.Info solidAccount;
Account.Info liquidAccount;
uint256 owedMarket;
uint256 heldMarket;
}
struct VaporizeArgs {
Types.AssetAmount amount;
Account.Info solidAccount;
Account.Info vaporAccount;
uint256 owedMarket;
uint256 heldMarket;
}
struct CallArgs {
Account.Info account;
address callee;
bytes data;
}
}
library Decimal {
struct D256 {
uint256 value;
}
}
library Interest {
struct Rate {
uint256 value;
}
struct Index {
uint96 borrow;
uint96 supply;
uint32 lastUpdate;
}
}
library Monetary {
struct Price {
uint256 value;
}
struct Value {
uint256 value;
}
}
library Storage {
// All information necessary for tracking a market
struct Market {
// Contract address of the associated ERC20 token
address token;
// Total aggregated supply and borrow amount of the entire market
Types.TotalPar totalPar;
// Interest index of the market
Interest.Index index;
// Contract address of the price oracle for this market
address priceOracle;
// Contract address of the interest setter for this market
address interestSetter;
// Multiplier on the marginRatio for this market
Decimal.D256 marginPremium;
// Multiplier on the liquidationSpread for this market
Decimal.D256 spreadPremium;
// Whether additional borrows are allowed for this market
bool isClosing;
}
// The global risk parameters that govern the health and security of the system
struct RiskParams {
// Required ratio of over-collateralization
Decimal.D256 marginRatio;
// Percentage penalty incurred by liquidated accounts
Decimal.D256 liquidationSpread;
// Percentage of the borrower's interest fee that gets passed to the suppliers
Decimal.D256 earningsRate;
// The minimum absolute borrow value of an account
// There must be sufficient incentivize to liquidate undercollateralized accounts
Monetary.Value minBorrowedValue;
}
// The maximum RiskParam values that can be set
struct RiskLimits {
uint64 marginRatioMax;
uint64 liquidationSpreadMax;
uint64 earningsRateMax;
uint64 marginPremiumMax;
uint64 spreadPremiumMax;
uint128 minBorrowedValueMax;
}
// The entire storage state of Solo
struct State {
// number of markets
uint256 numMarkets;
// marketId => Market
mapping(uint256 => Market) markets;
// owner => account number => Account
mapping(address => mapping(uint256 => Account.Storage)) accounts;
// Addresses that can control other users accounts
mapping(address => mapping(address => bool)) operators;
// Addresses that can control all users accounts
mapping(address => bool) globalOperators;
// mutable risk parameters of the system
RiskParams riskParams;
// immutable risk limits of the system
RiskLimits riskLimits;
}
}
library Types {
enum AssetDenomination {
Wei, // the amount is denominated in wei
Par // the amount is denominated in par
}
enum AssetReference {
Delta, // the amount is given as a delta from the current value
Target // the amount is given as an exact number to end up at
}
struct AssetAmount {
bool sign; // true if positive
AssetDenomination denomination;
AssetReference ref;
uint256 value;
}
struct TotalPar {
uint128 borrow;
uint128 supply;
}
struct Par {
bool sign; // true if positive
uint128 value;
}
struct Wei {
bool sign; // true if positive
uint256 value;
}
}
abstract contract ISoloMargin { //
struct OperatorArg {
address operator;
bool trusted;
}
function ownerSetSpreadPremium(
uint256 marketId,
Decimal.D256 memory spreadPremium
) public virtual;
function getIsGlobalOperator(address operator) public virtual view returns (bool);
function getMarketTokenAddress(uint256 marketId)
public
virtual
view
returns (address);
function ownerSetInterestSetter(uint256 marketId, address interestSetter)
public virtual;
function getAccountValues(Account.Info memory account)
public
virtual
view
returns (Monetary.Value memory, Monetary.Value memory);
function getMarketPriceOracle(uint256 marketId)
public
virtual
view
returns (address);
function getMarketInterestSetter(uint256 marketId)
public
virtual
view
returns (address);
function getMarketSpreadPremium(uint256 marketId)
public
virtual
view
returns (Decimal.D256 memory);
function getNumMarkets() public virtual view returns (uint256);
function ownerWithdrawUnsupportedTokens(address token, address recipient)
public virtual
returns (uint256);
function ownerSetMinBorrowedValue(Monetary.Value memory minBorrowedValue)
public virtual;
function ownerSetLiquidationSpread(Decimal.D256 memory spread) public virtual;
function ownerSetEarningsRate(Decimal.D256 memory earningsRate) public virtual;
function getIsLocalOperator(address owner, address operator)
public
virtual
view
returns (bool);
function getAccountPar(Account.Info memory account, uint256 marketId)
public
virtual
view
returns (Types.Par memory);
function ownerSetMarginPremium(
uint256 marketId,
Decimal.D256 memory marginPremium
) public virtual;
function getMarginRatio() public virtual view returns (Decimal.D256 memory);
function getMarketCurrentIndex(uint256 marketId)
public
virtual
view
returns (Interest.Index memory);
function getMarketIsClosing(uint256 marketId) public virtual view returns (bool);
function getRiskParams() public virtual view returns (Storage.RiskParams memory);
function getAccountBalances(Account.Info memory account)
public
virtual
view
returns (address[] memory, Types.Par[] memory, Types.Wei[] memory);
function renounceOwnership() public virtual;
function getMinBorrowedValue() public virtual view returns (Monetary.Value memory);
function setOperators(OperatorArg[] memory args) public virtual;
function getMarketPrice(uint256 marketId) public virtual view returns (address);
function owner() public virtual view returns (address);
function isOwner() public virtual view returns (bool);
function ownerWithdrawExcessTokens(uint256 marketId, address recipient)
public virtual
returns (uint256);
function ownerAddMarket(
address token,
address priceOracle,
address interestSetter,
Decimal.D256 memory marginPremium,
Decimal.D256 memory spreadPremium
) public virtual;
function operate(
Account.Info[] memory accounts,
Actions.ActionArgs[] memory actions
) public virtual;
function getMarketWithInfo(uint256 marketId)
public
virtual
view
returns (
Storage.Market memory,
Interest.Index memory,
Monetary.Price memory,
Interest.Rate memory
);
function ownerSetMarginRatio(Decimal.D256 memory ratio) public virtual;
function getLiquidationSpread() public virtual view returns (Decimal.D256 memory);
function getAccountWei(Account.Info memory account, uint256 marketId)
public
virtual
view
returns (Types.Wei memory);
function getMarketTotalPar(uint256 marketId)
public
virtual
view
returns (Types.TotalPar memory);
function getLiquidationSpreadForPair(
uint256 heldMarketId,
uint256 owedMarketId
) public virtual view returns (Decimal.D256 memory);
function getNumExcessTokens(uint256 marketId)
public
virtual
view
returns (Types.Wei memory);
function getMarketCachedIndex(uint256 marketId)
public
virtual
view
returns (Interest.Index memory);
function getAccountStatus(Account.Info memory account)
public
virtual
view
returns (uint8);
function getEarningsRate() public virtual view returns (Decimal.D256 memory);
function ownerSetPriceOracle(uint256 marketId, address priceOracle) public virtual;
function getRiskLimits() public virtual view returns (Storage.RiskLimits memory);
function getMarket(uint256 marketId)
public
virtual
view
returns (Storage.Market memory);
function ownerSetIsClosing(uint256 marketId, bool isClosing) public virtual;
function ownerSetGlobalOperator(address operator, bool approved) public virtual;
function transferOwnership(address newOwner) public virtual;
function getAdjustedAccountValues(Account.Info memory account)
public
virtual
view
returns (Monetary.Value memory, Monetary.Value memory);
function getMarketMarginPremium(uint256 marketId)
public
virtual
view
returns (Decimal.D256 memory);
function getMarketInterestRate(uint256 marketId)
public
virtual
view
returns (Interest.Rate memory);
}
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import { IERC20 } from "./Interfaces.sol";
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, 'SafeMath: addition overflow');
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, 'SafeMath: subtraction overflow');
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, 'SafeMath: multiplication overflow');
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, 'SafeMath: division by zero');
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, 'SafeMath: modulo by zero');
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// solhint-disable-next-line no-inline-assembly
assembly {
codehash := extcodehash(account)
}
return (codehash != accountHash && codehash != 0x0);
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, 'Address: insufficient balance');
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(bool success, ) = recipient.call{value: amount}('');
require(success, 'Address: unable to send value, recipient may have reverted');
}
}
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
'SafeERC20: approve from non-zero to non-zero allowance'
);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function callOptionalReturn(IERC20 token, bytes memory data) private {
require(address(token).isContract(), 'SafeERC20: call to non-contract');
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = address(token).call(data);
require(success, 'SafeERC20: low-level call failed');
if (returndata.length > 0) {
// Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), 'SafeERC20: ERC20 operation did not succeed');
}
}
}
library DataTypes {
// refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties.
struct ReserveData {
//stores the reserve configuration
ReserveConfigurationMap configuration;
//the liquidity index. Expressed in ray
uint128 liquidityIndex;
//variable borrow index. Expressed in ray
uint128 variableBorrowIndex;
//the current supply rate. Expressed in ray
uint128 currentLiquidityRate;
//the current variable borrow rate. Expressed in ray
uint128 currentVariableBorrowRate;
//the current stable borrow rate. Expressed in ray
uint128 currentStableBorrowRate;
uint40 lastUpdateTimestamp;
//tokens addresses
address aTokenAddress;
address stableDebtTokenAddress;
address variableDebtTokenAddress;
//address of the interest rate strategy
address interestRateStrategyAddress;
//the id of the reserve. Represents the position in the list of the active reserves
uint8 id;
}
struct ReserveConfigurationMap {
//bit 0-15: LTV
//bit 16-31: Liq. threshold
//bit 32-47: Liq. bonus
//bit 48-55: Decimals
//bit 56: Reserve is active
//bit 57: reserve is frozen
//bit 58: borrowing is enabled
//bit 59: stable rate borrowing enabled
//bit 60-63: reserved
//bit 64-79: reserve factor
uint256 data;
}
struct UserConfigurationMap {
uint256 data;
}
enum InterestRateMode {NONE, STABLE, VARIABLE}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment