Created
March 6, 2020 07:58
-
-
Save muellerberndt/a0834c19eac7e50d5b5f4addb4eb46e8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.5.0; | |
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. | |
* | |
* NOTE: This is a feature of the next version of OpenZeppelin Contracts. | |
* @dev Get it via `npm install @openzeppelin/contracts@next`. | |
*/ | |
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. | |
* NOTE: This is a feature of the next version of OpenZeppelin Contracts. | |
* @dev Get it via `npm install @openzeppelin/contracts@next`. | |
*/ | |
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. | |
* | |
* NOTE: This is a feature of the next version of OpenZeppelin Contracts. | |
* @dev Get it via `npm install @openzeppelin/contracts@next`. | |
*/ | |
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
require(b != 0, errorMessage); | |
return a % b; | |
} | |
} | |
contract DeFiSim { | |
using SafeMath for uint256; | |
mapping (address => uint256) public token_balances; | |
mapping (address => uint256) public ether_balances; | |
struct ConstantProductPool { | |
uint256 eth_reserve; | |
uint256 token_reserve; | |
} | |
ConstantProductPool public exchange; | |
constructor() public { | |
exchange = ConstantProductPool(1000, 1000); | |
} | |
function getInputPrice(uint256 input_amount, uint256 input_reserve, uint256 output_reserve) internal pure returns (uint256) { | |
require(input_reserve > 0 && output_reserve > 0); | |
uint256 input_amount_with_fee = input_amount.mul(997); | |
uint256 numerator = input_amount_with_fee.mul(output_reserve); | |
uint256 denominator = (input_reserve.mul(1000)).add(input_amount_with_fee); | |
return numerator / denominator; | |
} | |
function getOutputPrice(uint256 output_amount, uint256 input_reserve, uint256 output_reserve) internal pure returns (uint256) { | |
require(input_reserve > 0 && output_reserve > 0); | |
uint256 numerator = input_reserve.mul(output_amount).mul(1000); | |
uint256 denominator = (output_reserve.sub(output_amount)).mul(997); | |
return numerator.div(denominator); | |
} | |
// Exact ETH output and maximum token input | |
function tokenToEth(uint256 eth_bought, uint256 max_tokens) public returns (uint256) { | |
require(eth_bought > 0); | |
uint256 tokens_sold = getOutputPrice(eth_bought, exchange.token_reserve, exchange.eth_reserve); | |
// tokens sold is always > 0 | |
require(max_tokens >= tokens_sold); | |
token_balances[msg.sender] = token_balances[msg.sender].sub(tokens_sold); | |
ether_balances[msg.sender] = ether_balances[msg.sender].add(eth_bought); | |
exchange.token_reserve = exchange.token_reserve.add(tokens_sold); | |
exchange.eth_reserve = exchange.eth_reserve.sub(eth_bought); | |
return tokens_sold; | |
} | |
// Exact ETH input and minimum token output | |
function ethToToken(uint256 eth_sold, uint256 min_tokens) public returns (uint256) { | |
require(eth_sold > 0 && min_tokens > 0); | |
uint256 tokens_bought = getInputPrice(eth_sold, exchange.eth_reserve.sub(eth_sold), exchange.token_reserve); | |
require(tokens_bought >= min_tokens); | |
ether_balances[msg.sender] = ether_balances[msg.sender].sub(eth_sold); | |
token_balances[msg.sender] = token_balances[msg.sender].add(tokens_bought); | |
exchange.token_reserve = exchange.token_reserve.sub(tokens_bought); | |
exchange.eth_reserve = exchange.eth_reserve.add(eth_sold); | |
return tokens_bought; | |
} | |
function getTokenToEthInputPrice(uint256 eth_bought) internal view returns (uint256) { | |
return getInputPrice(eth_bought, exchange.eth_reserve.sub(eth_bought), exchange.token_reserve); | |
} | |
function getTokenToEthOutputPrice(uint256 eth_bought) internal view returns (uint256) { | |
return getOutputPrice(eth_bought, exchange.token_reserve, exchange.eth_reserve); | |
} | |
function getEthToTokenOutputPrice(uint256 eth_sold) internal view returns (uint256) { | |
return getOutputPrice(eth_sold, exchange.token_reserve, exchange.eth_reserve); | |
} | |
function minCollateral(uint256 eth_borrow) internal view returns (uint256) { | |
uint256 numerator = exchange.token_reserve.mul(eth_borrow).mul(1000); | |
uint256 denominator = (exchange.eth_reserve.sub(eth_borrow)).mul(997); | |
return numerator.div(denominator); | |
} | |
function hasEnoughCollateral(uint256 eth_borrow, address borrower) internal view returns (bool) { | |
return (token_balances[borrower] >= minCollateral(eth_borrow)); | |
} | |
function takeLoanCollateralizedByToken(uint256 eth_borrow) public { | |
require(hasEnoughCollateral(eth_borrow, msg.sender)); | |
ether_balances[msg.sender] = ether_balances[msg.sender].add(eth_borrow); | |
} | |
} | |
contract SymbolicArbitrage is DeFiSim { | |
using SafeMath for uint256; | |
event AssertionFailed(string message); | |
constructor() public { | |
// Flash loan! | |
ether_balances[msg.sender] = ether_balances[msg.sender].add(10000); | |
} | |
function repayFlashLoan() public { | |
/* | |
# Pay back the 10,000 ETH! | |
# ... | |
# ... | |
# Profit? | |
*/ | |
ether_balances[msg.sender] = ether_balances[msg.sender].sub(10000); | |
if(ether_balances[msg.sender] > 0) { | |
emit AssertionFailed("Arbitrage opportunity found"); | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment