Skip to content

Commit

Permalink
arb_bot and arb smart contract
Browse files Browse the repository at this point in the history
arb_bot and arb smart contract
  • Loading branch information
celey committed Sep 13, 2020
1 parent 12979e8 commit 98ab163
Show file tree
Hide file tree
Showing 100 changed files with 30,046 additions and 0 deletions.
1 change: 1 addition & 0 deletions DPI_Arb_Bot-master/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
script/
1 change: 1 addition & 0 deletions DPI_Arb_Bot-master/setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
openethereum --chain kovan --jsonrpc-port=9999
3 changes: 3 additions & 0 deletions DPI_Arb_Bot-master/smart-contract/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
build
.env
11 changes: 11 additions & 0 deletions DPI_Arb_Bot-master/smart-contract/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
1. Run `npm install`
2. Create a `.env` file with the following contents:

```
KOVAN_NODE_URL=
PRIV_KEY_TEST=
PRIV_KEY_DEPLOY=
```
3. Run `npm start` to start a local Ganache chain with forked Mainnet state
4. Run `npm run migrate` in order to compile and migrate the project's contracts to this local Ganache chain
5. Run `npm test` in order to run the tests.
4 changes: 4 additions & 0 deletions DPI_Arb_Bot-master/smart-contract/abi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"erc20Mock": [{"inputs":[{"internalType":"address","name":"_initialAccount","type":"address"},{"internalType":"uint256","name":"_initialBalance","type":"uint256"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint8","name":"_decimals","type":"uint8"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"greedIsGood","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}],
"issueModule":[{"inputs":[{"internalType":"contract IController","name":"_controller","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_setToken","type":"address"},{"indexed":false,"internalType":"address","name":"_issuer","type":"address"},{"indexed":false,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"address","name":"_hookContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"_quantity","type":"uint256"}],"name":"SetTokenIssued","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_setToken","type":"address"},{"indexed":false,"internalType":"address","name":"_redeemer","type":"address"},{"indexed":false,"internalType":"uint256","name":"_quantity","type":"uint256"}],"name":"SetTokenRedeemed","type":"event"},{"inputs":[],"name":"controller","outputs":[{"internalType":"contract IController","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"uint256","name":"_quantity","type":"uint256"}],"name":"getRequiredComponentUnitsForIssue","outputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"contract IManagerIssuanceHook","name":"_preIssueHook","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"uint256","name":"_quantity","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"issue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"uint256","name":"_quantity","type":"uint256"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"contract IDepositor","name":"_issueContract","type":"address"}],"name":"issueFromContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"","type":"address"}],"name":"managerIssuanceHook","outputs":[{"internalType":"contract IManagerIssuanceHook","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"uint256","name":"_quantity","type":"uint256"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"removeModule","outputs":[],"stateMutability":"nonpayable","type":"function"}]
}
10 changes: 10 additions & 0 deletions DPI_Arb_Bot-master/smart-contract/addresses.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"WETH": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"Controller": "0xF1B12A7b1f0AF744ED21eEC7d3E891C48Fd3c329",
"SetTokenCreator": "0x86a73e16D2dEce40b378B6aF645A93678214EF43",
"BasicIssuanceModule": "0x508910aA6fF3D029Dc358dD0f775877A355BA35B",
"StreamingFeeModule": "0x3D8d14b7eFb8e342189ee14c3d40dCe005EB901B",
"ProtocolViewer": "0x17efe2A497C7B173be4eB5bb8481C2dc741509fE",
"DPI": "0x9F295d05bAeE0E95F52aC1db0C2bDc26A226e4BB",
"DPIUniswapPool": "0x8bd7244f23d583c7418a2be53461e4c45abb2e6f"
}
4 changes: 4 additions & 0 deletions DPI_Arb_Bot-master/smart-contract/arb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
let s;
DefiPulseIndexTrade.deployed().then((k) => s = k)
s.arbTokensForIndex("0xA6a801124c243a3507e8b9b503F154c02eAe48E9")

243 changes: 243 additions & 0 deletions DPI_Arb_Bot-master/smart-contract/contracts/DefiPulseIndexTrade.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
pragma solidity >=0.6.6;
pragma experimental ABIEncoderV2;

import '@uniswap/lib/contracts/libraries/Babylonian.sol';

import "./aave/FlashLoanReceiverBase.sol";
import "./aave/ILendingPoolAddressesProvider.sol";
import "./aave/ILendingPool.sol";

import './libraries/UniswapV2Library.sol';
import './interfaces/IUniswapV2Router02.sol';
import './interfaces/IUniswapV2Factory.sol';
import './interfaces/IERC20.sol';
import './interfaces/IWETH.sol';
import './interfaces/IBasicIssuanceModule.sol';
import './interfaces/ISetToken.sol';



contract DefiPulseIndexTrade is FlashLoanReceiverBase {
address immutable factory;
address immutable setController;
address immutable issueModule;
address immutable WETH;
IUniswapV2Router02 immutable router;
uint256 MAX_INT = 2**256 - 1;


constructor(address _factory, address _router, address _setController, address _issueModule, address _addressProvider)
FlashLoanReceiverBase(_addressProvider) public {
factory = _factory; // Uniswap Factory V02
router = IUniswapV2Router02(_router); // Uniswap Router
setController = _setController; // Set Controller
issueModule = _issueModule; // Set Token Issue Module
WETH = IUniswapV2Router01(_router).WETH();
}


function initiateArbitrage(address _indexToken, uint256 _targetPriceInEther) public payable {
if(msg.value > 0) IWETH(WETH).deposit{value: msg.value}();
(bool aToB, uint256 indexAmount, address pairAddress) = getIndexAmountToBuyOrSell(_indexToken, _targetPriceInEther);
if(aToB) {
buyIndex(_indexToken, indexAmount, pairAddress);
} else {
require(indexAmount > 1 ether, "issue amount must be > 0");
sellIndex(_indexToken, indexAmount, pairAddress);
}
msg.sender.transfer(address(this).balance);
}

function approveToken(address _token) public {
IERC20(_token).approve(address(router), MAX_INT); // Set Max Approval for neccessary tokens;
IERC20(_token).approve(setController, MAX_INT); // Set Max Approval for neccessary tokens;
IERC20(_token).approve(issueModule, MAX_INT); // Set Max Approval for neccessary tokens;
}

function approveSetToken(address _index) public {
IERC20(_index).approve(address(router), MAX_INT); // Set Max Approval for neccessary tokens;
ISetToken.Position[] memory positions = ISetToken(_index).getPositions();

for(uint256 i=0; i<positions.length; i++) {
IERC20(positions[i].component).approve(setController, MAX_INT); // Set Max Approval for Set Contract;
IERC20(positions[i].component).approve(issueModule, MAX_INT); // Set Max Approval for Set Contract;
IERC20(positions[i].component).approve(address(router), MAX_INT); // Set Max Approval for neccessary tokens;
}

}

function issueSetToken(address _index, uint256 _amount) private {
IBasicIssuanceModule(issueModule).issue(ISetToken(_index), _amount, address(this));
}

function redeemSetToken(address _index, uint256 _amount) private {
IBasicIssuanceModule(issueModule).redeem(ISetToken(_index), _amount, address(this));
}

function getIndexAmountToBuyOrSell(address _index, uint256 _targetPriceInEther) public view returns (bool isBuy, uint256 amountIn, address pairAddress_) {
address pairAddress = IUniswapV2Factory(factory).getPair(WETH, _index);

(uint256 reserveA, uint256 reserveB) = UniswapV2Library.getReserves(factory, WETH, _index);
(bool aToB, uint256 _amount) = computeProfitMaximizingTrade(_targetPriceInEther, 1000000000, reserveA, reserveB);

uint256 maxAmount = _amount < reserveB ? _amount : reserveB;

return (aToB, maxAmount, pairAddress);
}

// Loan WETH and trade for individual parts, then redeem and sell Index (will trigger executeOperation);
function sellIndex(address _index, uint256 amountIndexToSell, address pairAddress) internal {
ISetToken.Position[] memory positions = ISetToken(_index).getPositions();

// Second calculate amount of ETH needed to get individual tokens
uint256 totalEthNeeded = 0;
uint256[] memory tokenAmounts = new uint256[](positions.length);
address[] memory tokens = new address[](positions.length);

{
for(uint256 i=0; i<positions.length; i++) {
(uint256 tokenReserveA, uint256 tokenReserveB) = UniswapV2Library.getReserves(factory, WETH, positions[i].component);
uint256 tokensNeeded = preciseMulCeil(uint256(positions[i].unit), amountIndexToSell);
tokenAmounts[i] = tokensNeeded;
tokens[i] = positions[i].component;

uint256 ethNeeded = router.getAmountIn(tokensNeeded, tokenReserveA, tokenReserveB);
totalEthNeeded = totalEthNeeded.add(ethNeeded).mul(1000) / 950;
}
}
bytes memory data = abi.encode(_index, amountIndexToSell, tokenAmounts, tokens);

// Third get Flash Loan, and execute rest once funds are received from aave (executeOperation)
ILendingPool lendingPool = ILendingPool(addressesProvider.getLendingPool());
lendingPool.flashLoan(address(this), 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, totalEthNeeded, data);
}

function executeOperation(address _reserve, uint256 _amount, uint256 _fee, bytes calldata _params)
external override {
require(_amount <= getBalanceInternal(address(this), _reserve), "Invalid balance, was the flashLoan successful?");
uint totalDebt = _amount.add(_fee);

// Wrap Ether
IWETH(WETH).deposit{value: address(this).balance}();

(address _index, uint256 _indexAmount, uint256[] memory tokenAmounts, address[] memory tokens) = abi.decode(_params, (address, uint256, uint256[], address[]));


acquireTokensOfSet(tokens, tokenAmounts);

// issue Set Token
issueSetToken(_index, _indexAmount * (1 ether));


address[] memory path = new address[](2);
path[0] = _index;
path[1] = WETH;

router.swapExactTokensForTokens(_indexAmount * (1 ether), 0, path, address(this), block.timestamp);


IWETH(WETH).withdraw(IERC20(WETH).balanceOf(address(this)));

transferFundsBackToPoolInternal(_reserve, totalDebt);

}




// Loan Index and Sell for Individual Parts
function buyIndex(address _index, uint256 amountIndexToBuy, address pairAddress) internal returns (address t0, address t1, uint256 a0, uint256 a1) {

bytes memory data = abi.encode(_index, amountIndexToBuy, pairAddress);

address token0 = IUniswapV2Pair(pairAddress).token0();
address token1 = IUniswapV2Pair(pairAddress).token1();
uint amount0Out = _index == token0 ? amountIndexToBuy : 0;
uint amount1Out = _index == token1 ? amountIndexToBuy : 0;

IUniswapV2Pair(pairAddress).swap(amount0Out, amount1Out, address(this), data);

return (token0, token1, amount0Out, amount1Out);
}

function completeBuyIndex(address _index, uint256 _amountIndexToBuy) private {
redeemSetToken(_index, _amountIndexToBuy);

liquidateSetPositions(_index);
}

function uniswapV2Call(address _sender, uint _amount0, uint _amount1, bytes calldata _data) external {
address token0 = IUniswapV2Pair(msg.sender).token0(); // fetch the address of token0
address token1 = IUniswapV2Pair(msg.sender).token1(); // fetch the address of token1
assert(msg.sender == IUniswapV2Factory(factory).getPair(token0, token1));

(address _index, uint256 _indexAmount) = abi.decode(_data, (address, uint256));

completeBuyIndex(_index, _indexAmount);

uint256 amount = token0 == address(WETH) ? _amount1 : _amount0;


address[] memory path = new address[](2);
path[0] = _amount0 == 0 ? token0 : token1;
path[1] = _amount0 == 0 ? token1 : token0;

IERC20(WETH).transfer(msg.sender, amount);
IWETH(WETH).withdraw(IERC20(WETH).balanceOf(address(this)));
}

function acquireTokensOfSet(address[] memory tokens, uint256[] memory tokenAmounts) private {

for(uint256 i=0; i<tokens.length; i++) {
address[] memory path = new address[](2);
path[0] = WETH;
path[1] = tokens[i];
router.swapTokensForExactTokens(tokenAmounts[i], MAX_INT, path, address(this), block.timestamp);
}

}

function liquidateSetPositions(address _index) private {
ISetToken.Position[] memory positions = ISetToken(_index).getPositions();

for(uint256 i=0; i<positions.length; i++) {
uint256 tokenBalance = IERC20(positions[i].component).balanceOf(address(this));
address[] memory path = new address[](2);
path[0] = positions[i].component;
path[1] = WETH;
router.swapExactTokensForTokens(IERC20(positions[i].component).balanceOf(address(this)), 0, path, address(this), block.timestamp);
}
}

// computes the direction and magnitude of the profit-maximizing trade
// https://github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/examples/ExampleSwapToPrice.sol
function computeProfitMaximizingTrade(
uint256 truePriceTokenA,
uint256 truePriceTokenB,
uint256 reserveA,
uint256 reserveB
) private pure returns (bool aToB, uint256 amountIn) {
aToB = reserveA.mul(truePriceTokenB) / reserveB < truePriceTokenA;

uint256 invariant = reserveA.mul(reserveB);

uint256 leftSide = Babylonian.sqrt(
invariant.mul(aToB ? truePriceTokenA : truePriceTokenB).mul(1000) /
uint256(aToB ? truePriceTokenB : truePriceTokenA).mul(997)
);
uint256 rightSide = (aToB ? reserveA.mul(1000) : reserveB.mul(1000)) / 997;

// compute the amount that must be sent to move the price to the profit-maximizing price
amountIn = leftSide.sub(rightSide);
}


// From Precise Unit SafeMath
function preciseMulCeil(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0 || b == 0) {
return 0;
}
return a.mul(b).sub(1).div(10 ** 18).add(1);
}

}
25 changes: 25 additions & 0 deletions DPI_Arb_Bot-master/smart-contract/contracts/GSN/Context.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}

function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
19 changes: 19 additions & 0 deletions DPI_Arb_Bot-master/smart-contract/contracts/Migrations.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.6;

contract Migrations {
address public owner;
uint public last_completed_migration;

constructor() public {
owner = msg.sender;
}

modifier restricted() {
if (msg.sender == owner) _;
}

function setCompleted(uint completed) public restricted {
last_completed_migration = completed;
}
}
19 changes: 19 additions & 0 deletions DPI_Arb_Bot-master/smart-contract/contracts/MyDapp.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.21 <0.7.0;

import "./interfaces/IERC20.sol";
import "./UniswapLiteBase.sol";

contract MyDapp is UniswapLiteBase {
address constant daiAddress = 0x6B175474E89094C44Da98b954EedeAC495271d0F;

function getEthPriceInDai() public view returns (uint256 tokenAmount) {
return _getEthToTokenInput(daiAddress, 1 ether);
}

function swapEthToDai() external payable returns (uint256 tokensBought) {
uint256 amount = _ethToToken(daiAddress, msg.value);
IERC20(daiAddress).transfer(msg.sender, amount);
return amount;
}
}
Loading

0 comments on commit 98ab163

Please sign in to comment.