Skip to content

Instantly share code, notes, and snippets.

@muellerberndt
Created March 6, 2020 07:58
Show Gist options
  • Save muellerberndt/a0834c19eac7e50d5b5f4addb4eb46e8 to your computer and use it in GitHub Desktop.
Save muellerberndt/a0834c19eac7e50d5b5f4addb4eb46e8 to your computer and use it in GitHub Desktop.
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