// File: IERC20.sol



/**
* SPDX-License-Identifier: MIT
*
* Copyright (c) 2016-2019 zOS Global Limited
*
*/
pragma solidity >=0.7;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
 * the optional functions; to access them see `ERC20Detailed`.
 */

interface IERC20 {

    // Optional functions
    function name() external view returns (string memory);

    function symbol() external view returns (string memory);

    function decimals() external view returns (uint8);

    /**
     * @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.
     *
     * > 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);
}
// File: IUniswapV2.sol



// SPDX-License-Identifier: MIT
// Copied from https://github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/UniswapV2Router02.sol
pragma solidity >=0.7;

interface IUniswapV2 {

        function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
                external payable returns (uint[] memory amounts);

        function getAmountsIn(uint amountOut, address[] memory path)
                external view returns (uint[] memory amounts);

        function getAmountsOut(uint amountIn, address[] memory path)
                external view returns (uint[] memory amounts);
        
        function WETH() external pure returns (address);
}
// File: MarketMaker.sol



/**
* SPDX-License-Identifier: LicenseRef-Aktionariat
*
* MIT License with Automated License Fee Payments
*
* Copyright (c) 2020 Aktionariat AG (aktionariat.com)
*
* Permission is hereby granted to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* - The above copyright notice and this permission notice shall be included in
*   all copies or substantial portions of the Software.
* - All automated license fee payments integrated into this and related Software
*   are preserved.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
pragma solidity >=0.7;

import "./SafeMath.sol";
import "./Ownable.sol";
import "./IERC20.sol";
import "./IUniswapV2.sol";

contract MarketMaker is Ownable {

    using SafeMath for uint256;

    address public base;  // ERC-20 currency
    address public token; // ERC-20 share token

    address public copyright;
    uint8 public licenseFeeBps; // only charged on sales, max 1% i.e. 100

    uint256 private price; // current offer price, without drift
    uint256 public increment; // increment

    uint256 public driftStart;
    uint256 public timeToDrift; // seconds until drift pushes price by one drift increment
    int256 public driftIncrement;

    bool public buyingEnabled = true;
    bool public sellingEnabled = true;

    IUniswapV2 constant uniswap = IUniswapV2(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
    address public weth;

    event Trade(address indexed token, address who, bytes ref, int amount, address base, uint totPrice, uint fee, uint newprice);

    function initialize(address baseCurrency, address shareToken) public {
        require(token == address(0), "Already initialized.");
        base = baseCurrency;
        token = shareToken;
        weth = uniswap.WETH();
        copyright = 0x29Fe8914e76da5cE2d90De98a64d0055f199d06D; // Aktionariat AG
        driftStart = block.timestamp;
    }

    function setPrice(uint256 newPrice, uint256 newIncrement) public onlyOwner {
        anchorPrice(newPrice);
        increment = newIncrement;
    }

    function hasDrift() public view returns (bool) {
        return timeToDrift != 0;
    }

    // secondsPerStep should be negative for downwards drift
    function setDrift(uint256 secondsPerStep, int256 newDriftIncrement) public onlyOwner {
        anchorPrice(getPrice());
        timeToDrift = secondsPerStep;
        driftIncrement = newDriftIncrement;
    }

    function anchorPrice(uint256 currentPrice) private {
        price = currentPrice;
        driftStart = block.timestamp;
    }

    function getPrice() public view returns (uint256) {
        return getPriceAtTime(block.timestamp);
    }

    function getPriceAtTime(uint256 timestamp) public view returns (uint256) {
        if (hasDrift()){
            uint256 passed = timestamp - driftStart;
            int256 drifted = int256(passed / timeToDrift) * driftIncrement;
            int256 driftedPrice = int256(price) + drifted;
            if (driftedPrice < 0){
                return 0;
            } else {
                return uint256(driftedPrice);
            }
        } else {
            return price;
        }
    }

    function getPriceInEther(uint256 shares) public view returns (uint256) {
        uint256 totPrice = getBuyPrice(shares);
        address[] memory path = new address[](2);
        path[0] = weth;
        path[1] = base;
        return uniswap.getAmountsIn(totPrice, path)[0];
    }

    function buyWithEther(uint256 shares, bytes calldata ref) public payable returns (uint256) {
        require(buyingEnabled);
        uint256 totPrice = getBuyPrice(shares);
        uint256 totPriceEth = getPriceInEther(shares);
        address[] memory path = new address[](2);
        path[0] = weth;
        path[1] = base;
        uint256[] memory amounts = uniswap.swapETHForExactTokens{value: totPriceEth}(totPrice, path, address(this), block.timestamp);
        assert(totPrice == amounts[1]);
        _buy(msg.sender, msg.sender, shares, amounts[1], ref);
        uint256 contractEtherBalance = address(this).balance;
        if (contractEtherBalance > 0){
            msg.sender.transfer(contractEtherBalance);
        }
        return amounts[0];
    }

    function buy(uint256 numberOfSharesToBuy, bytes calldata ref) public returns (uint256) {
        require(buyingEnabled);
        return buy(msg.sender, numberOfSharesToBuy, ref);
    }

    function buy(address recipient, uint256 numberOfSharesToBuy, bytes calldata ref) public returns (uint256) {
        require(buyingEnabled);
        return _buy(msg.sender, recipient, numberOfSharesToBuy, 0, ref);
    }

    function _buy(address paying, address recipient, uint256 shares, uint256 alreadyPaid, bytes calldata ref) internal returns (uint256) {
        uint256 totPrice = getBuyPrice(shares);
        IERC20 baseToken = IERC20(base);
        if (totPrice > alreadyPaid){
            require(baseToken.transferFrom(paying, address(this), totPrice - alreadyPaid));
        } else if (totPrice < alreadyPaid){
            // caller paid to much, return excess amount
            require(baseToken.transfer(paying, alreadyPaid - totPrice));
        }
        IERC20 shareToken = IERC20(token);
        require(shareToken.transfer(recipient, shares));
        price = price.add(shares.mul(increment));
        emit Trade(token, paying, ref, int256(shares), base, totPrice, 0, price);
        return totPrice;
    }

    function _notifyMoneyReceived(address from, uint256 amount, bytes calldata ref) internal {
        uint shares = getShares(amount);
        _buy(from, from, shares, amount, ref);
    }

    function sell(uint256 tokens, bytes calldata ref) public returns (uint256){
        require(sellingEnabled);
        return sell(msg.sender, tokens, ref);
    }

    function sell(address recipient, uint256 tokens, bytes calldata ref) public returns (uint256){
        require(sellingEnabled);
        return _sell(msg.sender, recipient, tokens, ref);
    }

    function _sell(address seller, address recipient, uint256 shares, bytes calldata ref) internal returns (uint256) {
        IERC20 shareToken = IERC20(token);
        require(shareToken.transferFrom(seller, address(this), shares));
        return _notifyTokensReceived(recipient, shares, ref);
    }

    // ERC-677 recipient
    function onTokenTransfer(address from, uint256 amount, bytes calldata ref) public returns (bool success) {
        require(msg.sender == token || msg.sender == base);
        if (msg.sender == token){
            require(sellingEnabled);
            _notifyTokensReceived(from, amount, ref);
        } else if (msg.sender == base){
            require(buyingEnabled);
            _notifyMoneyReceived(from, amount, ref);
        } else {
            require(false);
        }
        return true;
    }

    function _notifyTokensReceived(address recipient, uint256 amount, bytes calldata ref) internal returns (uint256) {
        uint256 totPrice = getSellPrice(amount);
        IERC20 baseToken = IERC20(base);
        uint256 fee = getSaleFee(totPrice);
        if (fee > 0){
            require(baseToken.transfer(copyright, fee));
        }
        require(baseToken.transfer(recipient, totPrice - fee));
        price = price.sub(amount.mul(increment));
        emit Trade(token, recipient, ref, -int256(amount), base, totPrice, fee, price);
        return totPrice;
    }

    function getSaleFee(uint256 totalPrice) public view returns (uint256) {
        return totalPrice.mul(licenseFeeBps).div(10000);
    }

    function getSaleProceeds(uint256 shares) public view returns (uint256) {
        uint256 total = getSellPrice(shares);
        return total - getSaleFee(total);
    }

    function getSellPrice(uint256 shares) public view returns (uint256) {
        return getPrice(getPrice().sub(shares.mul(increment)), shares);
    }

    function getBuyPrice(uint256 shares) public view returns (uint256) {
        return getPrice(getPrice(), shares);
    }

    function getPrice(uint256 lowest, uint256 shares) internal view returns (uint256){
        if (shares == 0){
            return 0;
        } else {
            uint256 highest = lowest + (shares - 1).mul(increment);
            return (lowest.add(highest) / 2).mul(shares);
        }
    }

    function getShares(uint256 money) public view returns (uint256) {
        uint256 currentPrice = getPrice();
        uint256 min = 0;
        uint256 max = money / currentPrice;
        while (min + 1 < max){
            uint256 middle = (min + max)/2;
            uint256 totalPrice = getPrice(currentPrice, middle);
            if (totalPrice > money){
                max = middle;
            } else {
                min = middle;
            }
        }
        return min;
    }

    function setCopyright(address newOwner) public {
        require(msg.sender == copyright);
        copyright = newOwner;
    }

    function setLicenseFee(uint8 bps) public {
        require(msg.sender == copyright);
        require(bps <= 100);
        licenseFeeBps = bps;
    }

    function withdraw(address ercAddress, address to, uint256 amount) public onlyOwner() {
        IERC20 erc20 = IERC20(ercAddress);
        require(erc20.transfer(to, amount), "Transfer failed");
    }

    function setEnabled(bool newBuyingEnabled, bool newSellingEnabled) public onlyOwner() {
        buyingEnabled = newBuyingEnabled;
        sellingEnabled = newSellingEnabled;
    }
}
// File: MarketMakerFactory.sol




/**
 * SPDX-License-Identifier: MIT
 */

pragma solidity >=0.7;

import "./MarketMaker.sol";

contract MarketMakerFactory {

  event ContractDeployed(address contractAddress);
    
  function create(bytes32 salt, address baseCurrency, address shareToken) public returns (address) {
    MarketMaker marketMaker = new MarketMaker{salt: salt}();
    marketMaker.initialize(baseCurrency, shareToken);
    emit ContractDeployed(address(marketMaker));
    marketMaker.transferOwnership(msg.sender);
    return address(marketMaker);
  }
}
// File: Ownable.sol



// SPDX-License-Identifier: MIT
//
// From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol
//
// Modifications:
// - Replaced Context._msgSender() with msg.sender
// - Added possibility to modify itself for multisig setup
// - Made leaner

pragma solidity >=0.7;

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
contract Ownable {

    address public owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () {
        owner = msg.sender;
        emit OwnershipTransferred(address(0), owner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner == msg.sender, "not owner");
        _;
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }
}
// File: SafeMath.sol



/**
* SPDX-License-Identifier: MIT
*
* Copyright (c) 2016-2019 zOS Global Limited
*/
pragma solidity >=0.7;

/**
 * @title SafeMath
 * @dev Math operations with safety checks that throw on error
 */

library SafeMath {

  /**
  * @dev Multiplies two numbers, throws on overflow.
  */
    function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
        // Gas optimization: this is cheaper than asserting 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
        if (a == 0) {
            return 0;
        }

        c = a * b;
        assert(c / a == b);
        return c;
    }

  /**
  * @dev Integer division of two numbers, truncating the quotient.
  */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        // assert(b > 0); // Solidity automatically throws when dividing by 0
        // uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold
        return a / b;
    }

  /**
  * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
  */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b <= a);
        return a - b;
    }

  /**
  * @dev Adds two numbers, throws on overflow.
  */
    function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
        c = a + b;
        assert(c >= a);
        return c;
    }
}