/** * Source Code first verified at https://etherscan.io on Monday, May 6, 2019 (UTC) */ /* * This file was generated by MyWish Platform (https://mywish.io/) * The complete code could be found at https://github.com/MyWishPlatform/ * Copyright (C) 2018 MyWish * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ pragma solidity ^0.5.7; /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor () internal { _owner = msg.sender; emit OwnershipTransferred(address(0), _owner); } /** * @return the address of the owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner()); _; } /** * @return true if `msg.sender` is the owner of the contract. */ function isOwner() public view returns (bool) { return msg.sender == _owner; } /** * @dev Allows the current owner to relinquish control of the contract. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. * @notice Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0)); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } /** * @title ERC20 interface without bool returns * @dev see https://eips.ethereum.org/EIPS/eip-20 */ interface IERC20 { function transfer(address to, uint256 value) external; function approve(address spender, uint256 value) external; function transferFrom(address from, address to, uint256 value) external; function totalSupply() external view returns (uint256); function balanceOf(address who) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } /** * @title Helps contracts guard against reentrancy attacks. * @author Remco Bloemen <[email protected]π.com>, Eenae <[email protected]> * @dev If you mark a function `nonReentrant`, you should also * mark it `external`. */ contract ReentrancyGuard { /// @dev counter to allow mutex lock with only one SSTORE operation uint256 private _guardCounter; constructor () internal { // The counter starts at one to prevent changing it from zero to a non-zero // value, which is a more expensive operation. _guardCounter = 1; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { _guardCounter += 1; uint256 localCounter = _guardCounter; _; require(localCounter == _guardCounter); } } /** * @title SafeMath * @dev Unsigned math operations with safety checks that revert on error */ library SafeMath { /** * @dev Multiplies two unsigned integers, reverts on 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-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b); return c; } /** * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a); uint256 c = a - b; return c; } /** * @dev Adds two unsigned integers, reverts on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a); return c; } /** * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), * reverts when dividing by zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0); return a % b; } } interface ISwaps { function createOrder( bytes32 _id, address _baseAddress, address _quoteAddress, uint _baseLimit, uint _quoteLimit, uint _expirationTimestamp, address _baseOnlyInvestor, uint _minBaseInvestment, uint _minQuoteInvestment ) external; function deposit( bytes32 _id, address _token, uint _amount ) payable external; function cancel(bytes32 _id) external; function refund(bytes32 _id, address _token) external; } contract Vault is Ownable { address public swaps; modifier onlySwaps() { require(msg.sender == swaps); _; } function () external payable { } function tokenFallback(address, uint, bytes calldata) external { } function setSwaps(address _swaps) public onlyOwner { swaps = _swaps; } function withdraw(address _token, address _receiver, uint _amount) public onlySwaps { if (_token == address(0)) { address(uint160(_receiver)).transfer(_amount); } else { IERC20(_token).transfer(_receiver, _amount); } } } contract Swaps is Ownable, ISwaps, ReentrancyGuard { using SafeMath for uint; uint public MAX_INVESTORS = 10; Vault public vault; mapping (bytes32 => address) public baseOnlyInvestor; mapping (bytes32 => address) public owners; mapping (bytes32 => address) public baseAddresses; mapping (bytes32 => address) public quoteAddresses; mapping (bytes32 => uint) public expirationTimestamps; mapping (bytes32 => bool) public isSwapped; mapping (bytes32 => bool) public isCancelled; mapping (bytes32 => mapping (address => uint)) public limits; mapping (bytes32 => mapping (address => uint)) public raised; mapping (bytes32 => mapping (address => address[])) public investors; mapping (bytes32 => mapping (address => mapping (address => uint))) public investments; mapping (bytes32 => mapping (address => uint)) public minInvestments; modifier onlyInvestor(bytes32 _id, address _token) { require(_isInvestor(_id, _token, msg.sender), "Allowed only for investors"); _; } modifier onlyWhenVaultDefined() { require(address(vault) != address(0), "Vault is not defined"); _; } modifier onlyOrderOwner(bytes32 _id) { require(msg.sender == owners[_id], "Allowed only for owner"); _; } modifier onlyWhenOrderExists(bytes32 _id) { require(owners[_id] != address(0), "Order doesn't exist"); _; } event OrderCreated( bytes32 id, address owner, address baseAddress, address quoteAddress, uint baseLimit, uint quoteLimit, uint expirationTimestamp, address baseOnlyInvestor, uint minBaseInvestment, uint minQuoteInvestment ); event OrderCancelled(bytes32 id); event Deposit( bytes32 id, address token, address user, uint amount, uint balance ); event Refund( bytes32 id, address token, address user, uint amount ); event OrderSwapped( bytes32 id, address byUser ); event SwapSend( bytes32 id, address token, address user, uint amount ); function tokenFallback(address, uint, bytes calldata) external { } function createOrder( bytes32 _id, address _baseAddress, address _quoteAddress, uint _baseLimit, uint _quoteLimit, uint _expirationTimestamp, address _baseOnlyInvestor, uint _minBaseInvestment, uint _minQuoteInvestment ) external nonReentrant onlyWhenVaultDefined { require(owners[_id] == address(0), "Order already exists"); require(_baseAddress != _quoteAddress, "Exchanged tokens must be different"); require(_baseLimit > 0, "Base limit must be positive"); require(_quoteLimit > 0, "Quote limit must be positive"); require(_expirationTimestamp > now, "Expiration time must be in future"); owners[_id] = msg.sender; baseAddresses[_id] = _baseAddress; quoteAddresses[_id] = _quoteAddress; expirationTimestamps[_id] = _expirationTimestamp; limits[_id][_baseAddress] = _baseLimit; limits[_id][_quoteAddress] = _quoteLimit; baseOnlyInvestor[_id] = _baseOnlyInvestor; minInvestments[_id][_baseAddress] = _minBaseInvestment; minInvestments[_id][_quoteAddress] = _minQuoteInvestment; emit OrderCreated( _id, msg.sender, _baseAddress, _quoteAddress, _baseLimit, _quoteLimit, _expirationTimestamp, _baseOnlyInvestor, _minBaseInvestment, _minQuoteInvestment ); } function deposit( bytes32 _id, address _token, uint _amount ) payable external nonReentrant onlyWhenVaultDefined onlyWhenOrderExists(_id) { if (_token == address(0)) { require(msg.value == _amount, "Payable value should be equals value"); address(vault).transfer(msg.value); } else { require(msg.value == 0, "Payable not allowed here"); uint allowance = IERC20(_token).allowance(msg.sender, address(this)); require(_amount <= allowance, "Allowance should be not less than amount"); IERC20(_token).transferFrom(msg.sender, address(vault), _amount); } _deposit(_id, _token, msg.sender, _amount); } function cancel(bytes32 _id) external nonReentrant onlyOrderOwner(_id) onlyWhenVaultDefined onlyWhenOrderExists(_id) { require(!isCancelled[_id], "Already cancelled"); require(!isSwapped[_id], "Already swapped"); address[2] memory tokens = [baseAddresses[_id], quoteAddresses[_id]]; for (uint t = 0; t < tokens.length; t++) { address token = tokens[t]; for (uint u = 0; u < investors[_id][token].length; u++) { address user = investors[_id][token][u]; uint userInvestment = investments[_id][token][user]; vault.withdraw(token, user, userInvestment); } } isCancelled[_id] = true; emit OrderCancelled(_id); } function refund(bytes32 _id, address _token) external nonReentrant onlyInvestor(_id, _token) onlyWhenVaultDefined onlyWhenOrderExists(_id) { require(!isSwapped[_id], "Already swapped"); address user = msg.sender; uint investment = investments[_id][_token][user]; if (investment > 0) { delete investments[_id][_token][user]; } _removeInvestor(investors[_id][_token], user); if (investment > 0) { raised[_id][_token] = raised[_id][_token].sub(investment); vault.withdraw(_token, user, investment); } emit Refund(_id, _token, user, investment); } function setVault(Vault _vault) external onlyOwner { vault = _vault; } function createKey(address _owner) public view returns (bytes32 result) { uint creationTime = now; result = 0x0000000000000000000000000000000000000000000000000000000000000000; assembly { result := or(result, mul(_owner, 0x1000000000000000000000000)) result := or(result, and(creationTime, 0xffffffffffffffffffffffff)) } } function baseLimit(bytes32 _id) public view returns (uint) { return limits[_id][baseAddresses[_id]]; } function quoteLimit(bytes32 _id) public view returns (uint) { return limits[_id][quoteAddresses[_id]]; } function baseRaised(bytes32 _id) public view returns (uint) { return raised[_id][baseAddresses[_id]]; } function quoteRaised(bytes32 _id) public view returns (uint) { return raised[_id][quoteAddresses[_id]]; } function isBaseFilled(bytes32 _id) public view returns (bool) { return raised[_id][baseAddresses[_id]] == limits[_id][baseAddresses[_id]]; } function isQuoteFilled(bytes32 _id) public view returns (bool) { return raised[_id][quoteAddresses[_id]] == limits[_id][quoteAddresses[_id]]; } function baseInvestors(bytes32 _id) public view returns (address[] memory) { return investors[_id][baseAddresses[_id]]; } function quoteInvestors(bytes32 _id) public view returns (address[] memory) { return investors[_id][quoteAddresses[_id]]; } function baseUserInvestment(bytes32 _id, address _user) public view returns (uint) { return investments[_id][baseAddresses[_id]][_user]; } function quoteUserInvestment(bytes32 _id, address _user) public view returns (uint) { return investments[_id][quoteAddresses[_id]][_user]; } function _swap(bytes32 _id) internal { require(!isSwapped[_id], "Already swapped"); require(!isCancelled[_id], "Already cancelled"); require(isBaseFilled(_id), "Base tokens not filled"); require(isQuoteFilled(_id), "Quote tokens not filled"); require(now <= expirationTimestamps[_id], "Contract expired"); _distribute(_id, baseAddresses[_id], quoteAddresses[_id]); _distribute(_id, quoteAddresses[_id], baseAddresses[_id]); isSwapped[_id] = true; emit OrderSwapped(_id, msg.sender); } function _distribute(bytes32 _id, address _aSide, address _bSide) internal { uint remainder = raised[_id][_bSide]; for (uint i = 0; i < investors[_id][_aSide].length; i++) { address user = investors[_id][_aSide][i]; uint toPay; // last if (i + 1 == investors[_id][_aSide].length) { toPay = remainder; } else { uint aSideRaised = raised[_id][_aSide]; uint userInvestment = investments[_id][_aSide][user]; uint bSideRaised = raised[_id][_bSide]; toPay = userInvestment.mul(bSideRaised).div(aSideRaised); remainder = remainder.sub(toPay); } vault.withdraw(_bSide, user, toPay); emit SwapSend(_id, _bSide, user, toPay); } } function _removeInvestor(address[] storage _array, address _investor) internal { uint idx = _array.length - 1; for (uint i = 0; i < _array.length - 1; i++) { if (_array[i] == _investor) { idx = i; break; } } _array[idx] = _array[_array.length - 1]; delete _array[_array.length - 1]; _array.length--; } function _deposit( bytes32 _id, address _token, address _from, uint _amount ) internal { uint amount = _amount; require(baseAddresses[_id] == _token || quoteAddresses[_id] == _token, "You can deposit only base or quote currency"); require(raised[_id][_token] < limits[_id][_token], "Limit already reached"); require(now <= expirationTimestamps[_id], "Contract expired"); if (baseAddresses[_id] == _token && baseOnlyInvestor[_id] != address(0)) { require(msg.sender == baseOnlyInvestor[_id], "Allowed only for specified address"); } if (limits[_id][_token].sub(raised[_id][_token]) > minInvestments[_id][_token]) { require(_amount >= minInvestments[_id][_token], "Should not be less than minimum value"); } if (!_isInvestor(_id, _token, _from)) { require(investors[_id][_token].length < MAX_INVESTORS, "Too many investors"); investors[_id][_token].push(_from); } uint raisedWithOverflow = raised[_id][_token].add(amount); if (raisedWithOverflow > limits[_id][_token]) { uint overflow = raisedWithOverflow.sub(limits[_id][_token]); vault.withdraw(_token, _from, overflow); amount = amount.sub(overflow); } investments[_id][_token][_from] = investments[_id][_token][_from].add(amount); raised[_id][_token] = raised[_id][_token].add(amount); emit Deposit( _id, _token, _from, amount, investments[_id][_token][_from] ); if (isBaseFilled(_id) && isQuoteFilled(_id)) { _swap(_id); } } function _isInvestor( bytes32 _id, address _token, address _who ) internal view returns (bool) { return investments[_id][_token][_who] > 0; } }