/** * Source Code first verified at https://etherscan.io on Friday, May 3, 2019 (UTC) */ // File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol pragma solidity ^0.5.2; /** * @title ERC20 interface * @dev see https://eips.ethereum.org/EIPS/eip-20 */ interface IERC20 { function transfer(address to, uint256 value) external returns (bool); function approve(address spender, uint256 value) external returns (bool); function transferFrom(address from, address to, uint256 value) external returns (bool); 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); } // File: openzeppelin-solidity/contracts/math/SafeMath.sol pragma solidity ^0.5.2; /** * @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; } } // File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol pragma solidity ^0.5.2; /** * @title Standard ERC20 token * * @dev Implementation of the basic standard token. * https://eips.ethereum.org/EIPS/eip-20 * Originally based on code by FirstBlood: * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol * * This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for * all accounts just by listening to said events. Note that this isn't required by the specification, and other * compliant implementations may not do it. */ contract ERC20 is IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowed; uint256 private _totalSupply; /** * @dev Total number of tokens in existence */ function totalSupply() public view returns (uint256) { return _totalSupply; } /** * @dev Gets the balance of the specified address. * @param owner The address to query the balance of. * @return A uint256 representing the amount owned by the passed address. */ function balanceOf(address owner) public view returns (uint256) { return _balances[owner]; } /** * @dev Function to check the amount of tokens that an owner allowed to a spender. * @param owner address The address which owns the funds. * @param spender address The address which will spend the funds. * @return A uint256 specifying the amount of tokens still available for the spender. */ function allowance(address owner, address spender) public view returns (uint256) { return _allowed[owner][spender]; } /** * @dev Transfer token to a specified address * @param to The address to transfer to. * @param value The amount to be transferred. */ function transfer(address to, uint256 value) public returns (bool) { _transfer(msg.sender, to, value); return true; } /** * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. * 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 * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. */ function approve(address spender, uint256 value) public returns (bool) { _approve(msg.sender, spender, value); return true; } /** * @dev Transfer tokens from one address to another. * Note that while this function emits an Approval event, this is not required as per the specification, * and other compliant implementations may not emit the event. * @param from address The address which you want to send tokens from * @param to address The address which you want to transfer to * @param value uint256 the amount of tokens to be transferred */ function transferFrom(address from, address to, uint256 value) public returns (bool) { _transfer(from, to, value); _approve(from, msg.sender, _allowed[from][msg.sender].sub(value)); return true; } /** * @dev Increase the amount of tokens that an owner allowed to a spender. * approve should be called when _allowed[msg.sender][spender] == 0. To increment * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * Emits an Approval event. * @param spender The address which will spend the funds. * @param addedValue The amount of tokens to increase the allowance by. */ function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { _approve(msg.sender, spender, _allowed[msg.sender][spender].add(addedValue)); return true; } /** * @dev Decrease the amount of tokens that an owner allowed to a spender. * approve should be called when _allowed[msg.sender][spender] == 0. To decrement * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * Emits an Approval event. * @param spender The address which will spend the funds. * @param subtractedValue The amount of tokens to decrease the allowance by. */ function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { _approve(msg.sender, spender, _allowed[msg.sender][spender].sub(subtractedValue)); return true; } /** * @dev Transfer token for a specified addresses * @param from The address to transfer from. * @param to The address to transfer to. * @param value The amount to be transferred. */ function _transfer(address from, address to, uint256 value) internal { require(to != address(0)); _balances[from] = _balances[from].sub(value); _balances[to] = _balances[to].add(value); emit Transfer(from, to, value); } /** * @dev Internal function that mints an amount of the token and assigns it to * an account. This encapsulates the modification of balances such that the * proper events are emitted. * @param account The account that will receive the created tokens. * @param value The amount that will be created. */ function _mint(address account, uint256 value) internal { require(account != address(0)); _totalSupply = _totalSupply.add(value); _balances[account] = _balances[account].add(value); emit Transfer(address(0), account, value); } /** * @dev Internal function that burns an amount of the token of a given * account. * @param account The account whose tokens will be burnt. * @param value The amount that will be burnt. */ function _burn(address account, uint256 value) internal { require(account != address(0)); _totalSupply = _totalSupply.sub(value); _balances[account] = _balances[account].sub(value); emit Transfer(account, address(0), value); } /** * @dev Approve an address to spend another addresses' tokens. * @param owner The address that owns the tokens. * @param spender The address that will spend the tokens. * @param value The number of tokens that can be spent. */ function _approve(address owner, address spender, uint256 value) internal { require(spender != address(0)); require(owner != address(0)); _allowed[owner][spender] = value; emit Approval(owner, spender, value); } /** * @dev Internal function that burns an amount of the token of a given * account, deducting from the sender's allowance for said account. Uses the * internal burn function. * Emits an Approval event (reflecting the reduced allowance). * @param account The account whose tokens will be burnt. * @param value The amount that will be burnt. */ function _burnFrom(address account, uint256 value) internal { _burn(account, value); _approve(account, msg.sender, _allowed[account][msg.sender].sub(value)); } } // File: openzeppelin-solidity/contracts/ownership/Ownable.sol pragma solidity ^0.5.2; /** * @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; } } // File: @gnosis.pm/mock-contract/contracts/MockContract.sol pragma solidity ^0.5.0; interface MockInterface { /** * @dev After calling this method, the mock will return `response` when it is called * with any calldata that is not mocked more specifically below * (e.g. using givenMethodReturn). * @param response ABI encoded response that will be returned if method is invoked */ function givenAnyReturn(bytes calldata response) external; function givenAnyReturnBool(bool response) external; function givenAnyReturnUint(uint response) external; function givenAnyReturnAddress(address response) external; function givenAnyRevert() external; function givenAnyRevertWithMessage(string calldata message) external; function givenAnyRunOutOfGas() external; /** * @dev After calling this method, the mock will return `response` when the given * methodId is called regardless of arguments. If the methodId and arguments * are mocked more specifically (using `givenMethodAndArguments`) the latter * will take precedence. * @param method ABI encoded methodId. It is valid to pass full calldata (including arguments). The mock will extract the methodId from it * @param response ABI encoded response that will be returned if method is invoked */ function givenMethodReturn(bytes calldata method, bytes calldata response) external; function givenMethodReturnBool(bytes calldata method, bool response) external; function givenMethodReturnUint(bytes calldata method, uint response) external; function givenMethodReturnAddress(bytes calldata method, address response) external; function givenMethodRevert(bytes calldata method) external; function givenMethodRevertWithMessage(bytes calldata method, string calldata message) external; function givenMethodRunOutOfGas(bytes calldata method) external; /** * @dev After calling this method, the mock will return `response` when the given * methodId is called with matching arguments. These exact calldataMocks will take * precedence over all other calldataMocks. * @param call ABI encoded calldata (methodId and arguments) * @param response ABI encoded response that will be returned if contract is invoked with calldata */ function givenCalldataReturn(bytes calldata call, bytes calldata response) external; function givenCalldataReturnBool(bytes calldata call, bool response) external; function givenCalldataReturnUint(bytes calldata call, uint response) external; function givenCalldataReturnAddress(bytes calldata call, address response) external; function givenCalldataRevert(bytes calldata call) external; function givenCalldataRevertWithMessage(bytes calldata call, string calldata message) external; function givenCalldataRunOutOfGas(bytes calldata call) external; /** * @dev Returns the number of times anything has been called on this mock since last reset */ function invocationCount() external returns (uint); /** * @dev Returns the number of times the given method has been called on this mock since last reset * @param method ABI encoded methodId. It is valid to pass full calldata (including arguments). The mock will extract the methodId from it */ function invocationCountForMethod(bytes calldata method) external returns (uint); /** * @dev Returns the number of times this mock has been called with the exact calldata since last reset. * @param call ABI encoded calldata (methodId and arguments) */ function invocationCountForCalldata(bytes calldata call) external returns (uint); /** * @dev Resets all mocked methods and invocation counts. */ function reset() external; } /** * Implementation of the MockInterface. */ contract MockContract is MockInterface { enum MockType { Return, Revert, OutOfGas } bytes32 public constant MOCKS_LIST_START = hex"01"; bytes public constant MOCKS_LIST_END = "0xff"; bytes32 public constant MOCKS_LIST_END_HASH = keccak256(MOCKS_LIST_END); bytes4 public constant SENTINEL_ANY_MOCKS = hex"01"; // A linked list allows easy iteration and inclusion checks mapping(bytes32 => bytes) calldataMocks; mapping(bytes => MockType) calldataMockTypes; mapping(bytes => bytes) calldataExpectations; mapping(bytes => string) calldataRevertMessage; mapping(bytes32 => uint) calldataInvocations; mapping(bytes4 => bytes4) methodIdMocks; mapping(bytes4 => MockType) methodIdMockTypes; mapping(bytes4 => bytes) methodIdExpectations; mapping(bytes4 => string) methodIdRevertMessages; mapping(bytes32 => uint) methodIdInvocations; MockType fallbackMockType; bytes fallbackExpectation; string fallbackRevertMessage; uint invocations; uint resetCount; constructor() public { calldataMocks[MOCKS_LIST_START] = MOCKS_LIST_END; methodIdMocks[SENTINEL_ANY_MOCKS] = SENTINEL_ANY_MOCKS; } function trackCalldataMock(bytes memory call) private { bytes32 callHash = keccak256(call); if (calldataMocks[callHash].length == 0) { calldataMocks[callHash] = calldataMocks[MOCKS_LIST_START]; calldataMocks[MOCKS_LIST_START] = call; } } function trackMethodIdMock(bytes4 methodId) private { if (methodIdMocks[methodId] == 0x0) { methodIdMocks[methodId] = methodIdMocks[SENTINEL_ANY_MOCKS]; methodIdMocks[SENTINEL_ANY_MOCKS] = methodId; } } function _givenAnyReturn(bytes memory response) internal { fallbackMockType = MockType.Return; fallbackExpectation = response; } function givenAnyReturn(bytes calldata response) external { _givenAnyReturn(response); } function givenAnyReturnBool(bool response) external { uint flag = response ? 1 : 0; _givenAnyReturn(uintToBytes(flag)); } function givenAnyReturnUint(uint response) external { _givenAnyReturn(uintToBytes(response)); } function givenAnyReturnAddress(address response) external { _givenAnyReturn(uintToBytes(uint(response))); } function givenAnyRevert() external { fallbackMockType = MockType.Revert; fallbackRevertMessage = ""; } function givenAnyRevertWithMessage(string calldata message) external { fallbackMockType = MockType.Revert; fallbackRevertMessage = message; } function givenAnyRunOutOfGas() external { fallbackMockType = MockType.OutOfGas; } function _givenCalldataReturn(bytes memory call, bytes memory response) private { calldataMockTypes[call] = MockType.Return; calldataExpectations[call] = response; trackCalldataMock(call); } function givenCalldataReturn(bytes calldata call, bytes calldata response) external { _givenCalldataReturn(call, response); } function givenCalldataReturnBool(bytes calldata call, bool response) external { uint flag = response ? 1 : 0; _givenCalldataReturn(call, uintToBytes(flag)); } function givenCalldataReturnUint(bytes calldata call, uint response) external { _givenCalldataReturn(call, uintToBytes(response)); } function givenCalldataReturnAddress(bytes calldata call, address response) external { _givenCalldataReturn(call, uintToBytes(uint(response))); } function _givenMethodReturn(bytes memory call, bytes memory response) private { bytes4 method = bytesToBytes4(call); methodIdMockTypes[method] = MockType.Return; methodIdExpectations[method] = response; trackMethodIdMock(method); } function givenMethodReturn(bytes calldata call, bytes calldata response) external { _givenMethodReturn(call, response); } function givenMethodReturnBool(bytes calldata call, bool response) external { uint flag = response ? 1 : 0; _givenMethodReturn(call, uintToBytes(flag)); } function givenMethodReturnUint(bytes calldata call, uint response) external { _givenMethodReturn(call, uintToBytes(response)); } function givenMethodReturnAddress(bytes calldata call, address response) external { _givenMethodReturn(call, uintToBytes(uint(response))); } function givenCalldataRevert(bytes calldata call) external { calldataMockTypes[call] = MockType.Revert; calldataRevertMessage[call] = ""; trackCalldataMock(call); } function givenMethodRevert(bytes calldata call) external { bytes4 method = bytesToBytes4(call); methodIdMockTypes[method] = MockType.Revert; trackMethodIdMock(method); } function givenCalldataRevertWithMessage(bytes calldata call, string calldata message) external { calldataMockTypes[call] = MockType.Revert; calldataRevertMessage[call] = message; trackCalldataMock(call); } function givenMethodRevertWithMessage(bytes calldata call, string calldata message) external { bytes4 method = bytesToBytes4(call); methodIdMockTypes[method] = MockType.Revert; methodIdRevertMessages[method] = message; trackMethodIdMock(method); } function givenCalldataRunOutOfGas(bytes calldata call) external { calldataMockTypes[call] = MockType.OutOfGas; trackCalldataMock(call); } function givenMethodRunOutOfGas(bytes calldata call) external { bytes4 method = bytesToBytes4(call); methodIdMockTypes[method] = MockType.OutOfGas; trackMethodIdMock(method); } function invocationCount() external returns (uint) { return invocations; } function invocationCountForMethod(bytes calldata call) external returns (uint) { bytes4 method = bytesToBytes4(call); return methodIdInvocations[keccak256(abi.encodePacked(resetCount, method))]; } function invocationCountForCalldata(bytes calldata call) external returns (uint) { return calldataInvocations[keccak256(abi.encodePacked(resetCount, call))]; } function reset() external { // Reset all exact calldataMocks bytes memory nextMock = calldataMocks[MOCKS_LIST_START]; bytes32 mockHash = keccak256(nextMock); // We cannot compary bytes while(mockHash != MOCKS_LIST_END_HASH) { // Reset all mock maps calldataMockTypes[nextMock] = MockType.Return; calldataExpectations[nextMock] = hex""; calldataRevertMessage[nextMock] = ""; // Set next mock to remove nextMock = calldataMocks[mockHash]; // Remove from linked list calldataMocks[mockHash] = ""; // Update mock hash mockHash = keccak256(nextMock); } // Clear list calldataMocks[MOCKS_LIST_START] = MOCKS_LIST_END; // Reset all any calldataMocks bytes4 nextAnyMock = methodIdMocks[SENTINEL_ANY_MOCKS]; while(nextAnyMock != SENTINEL_ANY_MOCKS) { bytes4 currentAnyMock = nextAnyMock; methodIdMockTypes[currentAnyMock] = MockType.Return; methodIdExpectations[currentAnyMock] = hex""; methodIdRevertMessages[currentAnyMock] = ""; nextAnyMock = methodIdMocks[currentAnyMock]; // Remove from linked list methodIdMocks[currentAnyMock] = 0x0; } // Clear list methodIdMocks[SENTINEL_ANY_MOCKS] = SENTINEL_ANY_MOCKS; fallbackExpectation = ""; fallbackMockType = MockType.Return; invocations = 0; resetCount += 1; } function useAllGas() private { while(true) { bool s; assembly { //expensive call to EC multiply contract s := call(sub(gas, 2000), 6, 0, 0x0, 0xc0, 0x0, 0x60) } } } function bytesToBytes4(bytes memory b) private pure returns (bytes4) { bytes4 out; for (uint i = 0; i < 4; i++) { out |= bytes4(b[i] & 0xFF) >> (i * 8); } return out; } function uintToBytes(uint256 x) private pure returns (bytes memory b) { b = new bytes(32); assembly { mstore(add(b, 32), x) } } function updateInvocationCount(bytes4 methodId, bytes memory originalMsgData) public { require(msg.sender == address(this), "Can only be called from the contract itself"); invocations += 1; methodIdInvocations[keccak256(abi.encodePacked(resetCount, methodId))] += 1; calldataInvocations[keccak256(abi.encodePacked(resetCount, originalMsgData))] += 1; } function() payable external { bytes4 methodId; assembly { methodId := calldataload(0) } // First, check exact matching overrides if (calldataMockTypes[msg.data] == MockType.Revert) { revert(calldataRevertMessage[msg.data]); } if (calldataMockTypes[msg.data] == MockType.OutOfGas) { useAllGas(); } bytes memory result = calldataExpectations[msg.data]; // Then check method Id overrides if (result.length == 0) { if (methodIdMockTypes[methodId] == MockType.Revert) { revert(methodIdRevertMessages[methodId]); } if (methodIdMockTypes[methodId] == MockType.OutOfGas) { useAllGas(); } result = methodIdExpectations[methodId]; } // Last, use the fallback override if (result.length == 0) { if (fallbackMockType == MockType.Revert) { revert(fallbackRevertMessage); } if (fallbackMockType == MockType.OutOfGas) { useAllGas(); } result = fallbackExpectation; } // Record invocation as separate call so we don't rollback in case we are called with STATICCALL (, bytes memory r) = address(this).call.gas(100000)(abi.encodeWithSignature("updateInvocationCount(bytes4,bytes)", methodId, msg.data)); assert(r.length == 0); assembly { return(add(0x20, result), mload(result)) } } } // File: openzeppelin-solidity/contracts/access/Roles.sol pragma solidity ^0.5.2; /** * @title Roles * @dev Library for managing addresses assigned to a Role. */ library Roles { struct Role { mapping (address => bool) bearer; } /** * @dev give an account access to this role */ function add(Role storage role, address account) internal { require(account != address(0)); require(!has(role, account)); role.bearer[account] = true; } /** * @dev remove an account's access to this role */ function remove(Role storage role, address account) internal { require(account != address(0)); require(has(role, account)); role.bearer[account] = false; } /** * @dev check if an account has this role * @return bool */ function has(Role storage role, address account) internal view returns (bool) { require(account != address(0)); return role.bearer[account]; } } // File: openzeppelin-solidity/contracts/access/roles/MinterRole.sol pragma solidity ^0.5.2; contract MinterRole { using Roles for Roles.Role; event MinterAdded(address indexed account); event MinterRemoved(address indexed account); Roles.Role private _minters; constructor () internal { _addMinter(msg.sender); } modifier onlyMinter() { require(isMinter(msg.sender)); _; } function isMinter(address account) public view returns (bool) { return _minters.has(account); } function addMinter(address account) public onlyMinter { _addMinter(account); } function renounceMinter() public { _removeMinter(msg.sender); } function _addMinter(address account) internal { _minters.add(account); emit MinterAdded(account); } function _removeMinter(address account) internal { _minters.remove(account); emit MinterRemoved(account); } } // File: openzeppelin-solidity/contracts/token/ERC20/ERC20Mintable.sol pragma solidity ^0.5.2; /** * @title ERC20Mintable * @dev ERC20 minting logic */ contract ERC20Mintable is ERC20, MinterRole { /** * @dev Function to mint tokens * @param to The address that will receive the minted tokens. * @param value The amount of tokens to mint. * @return A boolean that indicates if the operation was successful. */ function mint(address to, uint256 value) public onlyMinter returns (bool) { _mint(to, value); return true; } } // File: contracts/interfaces/IDutchExchange.sol pragma solidity ^0.5.0; contract IDutchExchange { mapping(address => mapping(address => mapping(uint => mapping(address => uint)))) public sellerBalances; mapping(address => mapping(address => mapping(uint => mapping(address => uint)))) public buyerBalances; mapping(address => mapping(address => mapping(uint => mapping(address => uint)))) public claimedAmounts; mapping(address => mapping(address => uint)) public balances; function withdraw(address tokenAddress, uint amount) public returns (uint); function deposit(address tokenAddress, uint amount) public returns (uint); function ethToken() public returns(address); function frtToken() public returns(address); function owlToken() public returns(address); function getAuctionIndex(address token1, address token2) public view returns(uint256); function postBuyOrder(address token1, address token2, uint256 auctionIndex, uint256 amount) public returns(uint256); function postSellOrder(address token1, address token2, uint256 auctionIndex, uint256 tokensBought) public returns(uint256, uint256); function getCurrentAuctionPrice(address token1, address token2, uint256 auctionIndex) public view returns(uint256, uint256); function claimSellerFunds(address sellToken, address buyToken, address user, uint auctionIndex) public returns (uint returned, uint frtsIssued); } // File: @gnosis.pm/util-contracts/contracts/Proxy.sol pragma solidity ^0.5.2; /// @title Proxied - indicates that a contract will be proxied. Also defines storage requirements for Proxy. /// @author Alan Lu - <[email protected]> contract Proxied { address public masterCopy; } /// @title Proxy - Generic proxy contract allows to execute all transactions applying the code of a master contract. /// @author Stefan George - <[email protected]> contract Proxy is Proxied { /// @dev Constructor function sets address of master copy contract. /// @param _masterCopy Master copy address. constructor(address _masterCopy) public { require(_masterCopy != address(0), "The master copy is required"); masterCopy = _masterCopy; } /// @dev Fallback function forwards all transactions and returns all received return data. function() external payable { address _masterCopy = masterCopy; assembly { calldatacopy(0, 0, calldatasize) let success := delegatecall(not(0), _masterCopy, 0, calldatasize, 0, 0) returndatacopy(0, 0, returndatasize) switch success case 0 { revert(0, returndatasize) } default { return(0, returndatasize) } } } } // File: @gnosis.pm/util-contracts/contracts/Token.sol /// Implements ERC 20 Token standard: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md pragma solidity ^0.5.2; /// @title Abstract token contract - Functions to be implemented by token contracts contract Token { /* * Events */ event Transfer(address indexed from, address indexed to, uint value); event Approval(address indexed owner, address indexed spender, uint value); /* * Public functions */ function transfer(address to, uint value) public returns (bool); function transferFrom(address from, address to, uint value) public returns (bool); function approve(address spender, uint value) public returns (bool); function balanceOf(address owner) public view returns (uint); function allowance(address owner, address spender) public view returns (uint); function totalSupply() public view returns (uint); } // File: @gnosis.pm/util-contracts/contracts/Math.sol pragma solidity ^0.5.2; /// @title Math library - Allows calculation of logarithmic and exponential functions /// @author Alan Lu - <[email protected]> /// @author Stefan George - <[email protected]> library GnosisMath { /* * Constants */ // This is equal to 1 in our calculations uint public constant ONE = 0x10000000000000000; uint public constant LN2 = 0xb17217f7d1cf79ac; uint public constant LOG2_E = 0x171547652b82fe177; /* * Public functions */ /// @dev Returns natural exponential function value of given x /// @param x x /// @return e**x function exp(int x) public pure returns (uint) { // revert if x is > MAX_POWER, where // MAX_POWER = int(mp.floor(mp.log(mpf(2**256 - 1) / ONE) * ONE)) require(x <= 2454971259878909886679); // return 0 if exp(x) is tiny, using // MIN_POWER = int(mp.floor(mp.log(mpf(1) / ONE) * ONE)) if (x < -818323753292969962227) return 0; // Transform so that e^x -> 2^x x = x * int(ONE) / int(LN2); // 2^x = 2^whole(x) * 2^frac(x) // ^^^^^^^^^^ is a bit shift // so Taylor expand on z = frac(x) int shift; uint z; if (x >= 0) { shift = x / int(ONE); z = uint(x % int(ONE)); } else { shift = x / int(ONE) - 1; z = ONE - uint(-x % int(ONE)); } // 2^x = 1 + (ln 2) x + (ln 2)^2/2! x^2 + ... // // Can generate the z coefficients using mpmath and the following lines // >>> from mpmath import mp // >>> mp.dps = 100 // >>> ONE = 0x10000000000000000 // >>> print('\n'.join(hex(int(mp.log(2)**i / mp.factorial(i) * ONE)) for i in range(1, 7))) // 0xb17217f7d1cf79ab // 0x3d7f7bff058b1d50 // 0xe35846b82505fc5 // 0x276556df749cee5 // 0x5761ff9e299cc4 // 0xa184897c363c3 uint zpow = z; uint result = ONE; result += 0xb17217f7d1cf79ab * zpow / ONE; zpow = zpow * z / ONE; result += 0x3d7f7bff058b1d50 * zpow / ONE; zpow = zpow * z / ONE; result += 0xe35846b82505fc5 * zpow / ONE; zpow = zpow * z / ONE; result += 0x276556df749cee5 * zpow / ONE; zpow = zpow * z / ONE; result += 0x5761ff9e299cc4 * zpow / ONE; zpow = zpow * z / ONE; result += 0xa184897c363c3 * zpow / ONE; zpow = zpow * z / ONE; result += 0xffe5fe2c4586 * zpow / ONE; zpow = zpow * z / ONE; result += 0x162c0223a5c8 * zpow / ONE; zpow = zpow * z / ONE; result += 0x1b5253d395e * zpow / ONE; zpow = zpow * z / ONE; result += 0x1e4cf5158b * zpow / ONE; zpow = zpow * z / ONE; result += 0x1e8cac735 * zpow / ONE; zpow = zpow * z / ONE; result += 0x1c3bd650 * zpow / ONE; zpow = zpow * z / ONE; result += 0x1816193 * zpow / ONE; zpow = zpow * z / ONE; result += 0x131496 * zpow / ONE; zpow = zpow * z / ONE; result += 0xe1b7 * zpow / ONE; zpow = zpow * z / ONE; result += 0x9c7 * zpow / ONE; if (shift >= 0) { if (result >> (256 - shift) > 0) return (2 ** 256 - 1); return result << shift; } else return result >> (-shift); } /// @dev Returns natural logarithm value of given x /// @param x x /// @return ln(x) function ln(uint x) public pure returns (int) { require(x > 0); // binary search for floor(log2(x)) int ilog2 = floorLog2(x); int z; if (ilog2 < 0) z = int(x << uint(-ilog2)); else z = int(x >> uint(ilog2)); // z = x * 2^-⌊log₂x⌋ // so 1 <= z < 2 // and ln z = ln x - ⌊log₂x⌋/log₂e // so just compute ln z using artanh series // and calculate ln x from that int term = (z - int(ONE)) * int(ONE) / (z + int(ONE)); int halflnz = term; int termpow = term * term / int(ONE) * term / int(ONE); halflnz += termpow / 3; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 5; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 7; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 9; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 11; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 13; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 15; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 17; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 19; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 21; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 23; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 25; return (ilog2 * int(ONE)) * int(ONE) / int(LOG2_E) + 2 * halflnz; } /// @dev Returns base 2 logarithm value of given x /// @param x x /// @return logarithmic value function floorLog2(uint x) public pure returns (int lo) { lo = -64; int hi = 193; // I use a shift here instead of / 2 because it floors instead of rounding towards 0 int mid = (hi + lo) >> 1; while ((lo + 1) < hi) { if (mid < 0 && x << uint(-mid) < ONE || mid >= 0 && x >> uint(mid) < ONE) hi = mid; else lo = mid; mid = (hi + lo) >> 1; } } /// @dev Returns maximum of an array /// @param nums Numbers to look through /// @return Maximum number function max(int[] memory nums) public pure returns (int maxNum) { require(nums.length > 0); maxNum = -2 ** 255; for (uint i = 0; i < nums.length; i++) if (nums[i] > maxNum) maxNum = nums[i]; } /// @dev Returns whether an add operation causes an overflow /// @param a First addend /// @param b Second addend /// @return Did no overflow occur? function safeToAdd(uint a, uint b) internal pure returns (bool) { return a + b >= a; } /// @dev Returns whether a subtraction operation causes an underflow /// @param a Minuend /// @param b Subtrahend /// @return Did no underflow occur? function safeToSub(uint a, uint b) internal pure returns (bool) { return a >= b; } /// @dev Returns whether a multiply operation causes an overflow /// @param a First factor /// @param b Second factor /// @return Did no overflow occur? function safeToMul(uint a, uint b) internal pure returns (bool) { return b == 0 || a * b / b == a; } /// @dev Returns sum if no overflow occurred /// @param a First addend /// @param b Second addend /// @return Sum function add(uint a, uint b) internal pure returns (uint) { require(safeToAdd(a, b)); return a + b; } /// @dev Returns difference if no overflow occurred /// @param a Minuend /// @param b Subtrahend /// @return Difference function sub(uint a, uint b) internal pure returns (uint) { require(safeToSub(a, b)); return a - b; } /// @dev Returns product if no overflow occurred /// @param a First factor /// @param b Second factor /// @return Product function mul(uint a, uint b) internal pure returns (uint) { require(safeToMul(a, b)); return a * b; } /// @dev Returns whether an add operation causes an overflow /// @param a First addend /// @param b Second addend /// @return Did no overflow occur? function safeToAdd(int a, int b) internal pure returns (bool) { return (b >= 0 && a + b >= a) || (b < 0 && a + b < a); } /// @dev Returns whether a subtraction operation causes an underflow /// @param a Minuend /// @param b Subtrahend /// @return Did no underflow occur? function safeToSub(int a, int b) internal pure returns (bool) { return (b >= 0 && a - b <= a) || (b < 0 && a - b > a); } /// @dev Returns whether a multiply operation causes an overflow /// @param a First factor /// @param b Second factor /// @return Did no overflow occur? function safeToMul(int a, int b) internal pure returns (bool) { return (b == 0) || (a * b / b == a); } /// @dev Returns sum if no overflow occurred /// @param a First addend /// @param b Second addend /// @return Sum function add(int a, int b) internal pure returns (int) { require(safeToAdd(a, b)); return a + b; } /// @dev Returns difference if no overflow occurred /// @param a Minuend /// @param b Subtrahend /// @return Difference function sub(int a, int b) internal pure returns (int) { require(safeToSub(a, b)); return a - b; } /// @dev Returns product if no overflow occurred /// @param a First factor /// @param b Second factor /// @return Product function mul(int a, int b) internal pure returns (int) { require(safeToMul(a, b)); return a * b; } } // File: @gnosis.pm/util-contracts/contracts/GnosisStandardToken.sol pragma solidity ^0.5.2; /** * Deprecated: Use Open Zeppeling one instead */ contract StandardTokenData { /* * Storage */ mapping(address => uint) balances; mapping(address => mapping(address => uint)) allowances; uint totalTokens; } /** * Deprecated: Use Open Zeppeling one instead */ /// @title Standard token contract with overflow protection contract GnosisStandardToken is Token, StandardTokenData { using GnosisMath for *; /* * Public functions */ /// @dev Transfers sender's tokens to a given address. Returns success /// @param to Address of token receiver /// @param value Number of tokens to transfer /// @return Was transfer successful? function transfer(address to, uint value) public returns (bool) { if (!balances[msg.sender].safeToSub(value) || !balances[to].safeToAdd(value)) { return false; } balances[msg.sender] -= value; balances[to] += value; emit Transfer(msg.sender, to, value); return true; } /// @dev Allows allowed third party to transfer tokens from one address to another. Returns success /// @param from Address from where tokens are withdrawn /// @param to Address to where tokens are sent /// @param value Number of tokens to transfer /// @return Was transfer successful? function transferFrom(address from, address to, uint value) public returns (bool) { if (!balances[from].safeToSub(value) || !allowances[from][msg.sender].safeToSub( value ) || !balances[to].safeToAdd(value)) { return false; } balances[from] -= value; allowances[from][msg.sender] -= value; balances[to] += value; emit Transfer(from, to, value); return true; } /// @dev Sets approved amount of tokens for spender. Returns success /// @param spender Address of allowed account /// @param value Number of approved tokens /// @return Was approval successful? function approve(address spender, uint value) public returns (bool) { allowances[msg.sender][spender] = value; emit Approval(msg.sender, spender, value); return true; } /// @dev Returns number of allowed tokens for given address /// @param owner Address of token owner /// @param spender Address of token spender /// @return Remaining allowance for spender function allowance(address owner, address spender) public view returns (uint) { return allowances[owner][spender]; } /// @dev Returns number of tokens owned by given address /// @param owner Address of token owner /// @return Balance of owner function balanceOf(address owner) public view returns (uint) { return balances[owner]; } /// @dev Returns total supply of tokens /// @return Total supply function totalSupply() public view returns (uint) { return totalTokens; } } // File: @gnosis.pm/dx-contracts/contracts/TokenFRT.sol pragma solidity ^0.5.2; /// @title Standard token contract with overflow protection contract TokenFRT is Proxied, GnosisStandardToken { address public owner; string public constant symbol = "MGN"; string public constant name = "Magnolia Token"; uint8 public constant decimals = 18; struct UnlockedToken { uint amountUnlocked; uint withdrawalTime; } /* * Storage */ address public minter; // user => UnlockedToken mapping(address => UnlockedToken) public unlockedTokens; // user => amount mapping(address => uint) public lockedTokenBalances; /* * Public functions */ // @dev allows to set the minter of Magnolia tokens once. // @param _minter the minter of the Magnolia tokens, should be the DX-proxy function updateMinter(address _minter) public { require(msg.sender == owner, "Only the minter can set a new one"); require(_minter != address(0), "The new minter must be a valid address"); minter = _minter; } // @dev the intention is to set the owner as the DX-proxy, once it is deployed // Then only an update of the DX-proxy contract after a 30 days delay could change the minter again. function updateOwner(address _owner) public { require(msg.sender == owner, "Only the owner can update the owner"); require(_owner != address(0), "The new owner must be a valid address"); owner = _owner; } function mintTokens(address user, uint amount) public { require(msg.sender == minter, "Only the minter can mint tokens"); lockedTokenBalances[user] = add(lockedTokenBalances[user], amount); totalTokens = add(totalTokens, amount); } /// @dev Lock Token function lockTokens(uint amount) public returns (uint totalAmountLocked) { // Adjust amount by balance uint actualAmount = min(amount, balances[msg.sender]); // Update state variables balances[msg.sender] = sub(balances[msg.sender], actualAmount); lockedTokenBalances[msg.sender] = add(lockedTokenBalances[msg.sender], actualAmount); // Get return variable totalAmountLocked = lockedTokenBalances[msg.sender]; } function unlockTokens() public returns (uint totalAmountUnlocked, uint withdrawalTime) { // Adjust amount by locked balances uint amount = lockedTokenBalances[msg.sender]; if (amount > 0) { // Update state variables lockedTokenBalances[msg.sender] = sub(lockedTokenBalances[msg.sender], amount); unlockedTokens[msg.sender].amountUnlocked = add(unlockedTokens[msg.sender].amountUnlocked, amount); unlockedTokens[msg.sender].withdrawalTime = now + 24 hours; } // Get return variables totalAmountUnlocked = unlockedTokens[msg.sender].amountUnlocked; withdrawalTime = unlockedTokens[msg.sender].withdrawalTime; } function withdrawUnlockedTokens() public { require(unlockedTokens[msg.sender].withdrawalTime < now, "The tokens cannot be withdrawn yet"); balances[msg.sender] = add(balances[msg.sender], unlockedTokens[msg.sender].amountUnlocked); unlockedTokens[msg.sender].amountUnlocked = 0; } function min(uint a, uint b) public pure returns (uint) { if (a < b) { return a; } else { return b; } } /// @dev Returns whether an add operation causes an overflow /// @param a First addend /// @param b Second addend /// @return Did no overflow occur? function safeToAdd(uint a, uint b) public pure returns (bool) { return a + b >= a; } /// @dev Returns whether a subtraction operation causes an underflow /// @param a Minuend /// @param b Subtrahend /// @return Did no underflow occur? function safeToSub(uint a, uint b) public pure returns (bool) { return a >= b; } /// @dev Returns sum if no overflow occurred /// @param a First addend /// @param b Second addend /// @return Sum function add(uint a, uint b) public pure returns (uint) { require(safeToAdd(a, b), "It must be a safe adition"); return a + b; } /// @dev Returns difference if no overflow occurred /// @param a Minuend /// @param b Subtrahend /// @return Difference function sub(uint a, uint b) public pure returns (uint) { require(safeToSub(a, b), "It must be a safe substraction"); return a - b; } } // File: openzeppelin-solidity/contracts/utils/Address.sol pragma solidity ^0.5.2; /** * Utility library of inline functions on addresses */ library Address { /** * Returns whether the target address is a contract * @dev This function will return false if invoked during the constructor of a contract, * as the code is not actually created until after the constructor finishes. * @param account address of the account to check * @return whether the target address is a contract */ function isContract(address account) internal view returns (bool) { uint256 size; // XXX Currently there is no better way to check if there is a contract in an address // than to check the size of the code at that address. // See https://ethereum.stackexchange.com/a/14016/36603 // for more details about how this works. // TODO Check this again before the Serenity release, because all addresses will be // contracts then. // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } } // File: @daostack/arc/contracts/libs/SafeERC20.sol /* SafeERC20 by daostack. The code is based on a fix by SECBIT Team. USE WITH CAUTION & NO WARRANTY REFERENCE & RELATED READING - https://github.com/ethereum/solidity/issues/4116 - https://medium.com/@chris_77367/explaining-unexpected-reverts-starting-with-solidity-0-4-22-3ada6e82308c - https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca - https://gist.github.com/BrendanChou/88a2eeb80947ff00bcf58ffdafeaeb61 */ pragma solidity ^0.5.2; library SafeERC20 { using Address for address; bytes4 constant private TRANSFER_SELECTOR = bytes4(keccak256(bytes("transfer(address,uint256)"))); bytes4 constant private TRANSFERFROM_SELECTOR = bytes4(keccak256(bytes("transferFrom(address,address,uint256)"))); bytes4 constant private APPROVE_SELECTOR = bytes4(keccak256(bytes("approve(address,uint256)"))); function safeTransfer(address _erc20Addr, address _to, uint256 _value) internal { // Must be a contract addr first! require(_erc20Addr.isContract()); (bool success, bytes memory returnValue) = // solhint-disable-next-line avoid-low-level-calls _erc20Addr.call(abi.encodeWithSelector(TRANSFER_SELECTOR, _to, _value)); // call return false when something wrong require(success); //check return value require(returnValue.length == 0 || (returnValue.length == 32 && (returnValue[31] != 0))); } function safeTransferFrom(address _erc20Addr, address _from, address _to, uint256 _value) internal { // Must be a contract addr first! require(_erc20Addr.isContract()); (bool success, bytes memory returnValue) = // solhint-disable-next-line avoid-low-level-calls _erc20Addr.call(abi.encodeWithSelector(TRANSFERFROM_SELECTOR, _from, _to, _value)); // call return false when something wrong require(success); //check return value require(returnValue.length == 0 || (returnValue.length == 32 && (returnValue[31] != 0))); } function safeApprove(address _erc20Addr, address _spender, uint256 _value) internal { // Must be a contract addr first! require(_erc20Addr.isContract()); // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. require((_value == 0) || (IERC20(_erc20Addr).allowance(msg.sender, _spender) == 0)); (bool success, bytes memory returnValue) = // solhint-disable-next-line avoid-low-level-calls _erc20Addr.call(abi.encodeWithSelector(APPROVE_SELECTOR, _spender, _value)); // call return false when something wrong require(success); //check return value require(returnValue.length == 0 || (returnValue.length == 32 && (returnValue[31] != 0))); } } // File: contracts/DxMgnPool.sol pragma solidity ^0.5.0; contract DxMgnPool is Ownable { using SafeMath for uint; uint constant OWL_ALLOWANCE = 10**36; struct Participation { uint startAuctionCount; // how many auction passed when this participation started contributing uint poolShares; // number of shares this participation accounts for (absolute) } mapping (address => bool) public hasParticpationWithdrawn; enum State { Pooling, PoolingEnded, DepositWithdrawnFromDx, MgnUnlocked } State public currentState = State.Pooling; mapping (address => Participation[]) public participationsByAddress; uint public totalPoolShares; // total number of shares in this pool uint public totalPoolSharesCummulative; // over all auctions, the rolling sum of all shares participated uint public totalDeposit; uint public totalMgn; uint public lastParticipatedAuctionIndex; uint public auctionCount; ERC20 public depositToken; ERC20 public secondaryToken; TokenFRT public mgnToken; IDutchExchange public dx; uint public poolingPeriodEndTime; constructor ( ERC20 _depositToken, ERC20 _secondaryToken, IDutchExchange _dx, uint _poolingTimeSeconds ) public Ownable() { depositToken = _depositToken; secondaryToken = _secondaryToken; dx = _dx; mgnToken = TokenFRT(dx.frtToken()); ERC20(dx.owlToken()).approve(address(dx), OWL_ALLOWANCE); poolingPeriodEndTime = now + _poolingTimeSeconds; } /** * Public interface */ function deposit(uint amount) public { checkForStateUpdate(); require(currentState == State.Pooling, "Pooling is already over"); uint poolShares = calculatePoolShares(amount); Participation memory participation = Participation({ startAuctionCount: isDepositTokenTurn() ? auctionCount : auctionCount + 1, poolShares: poolShares }); participationsByAddress[msg.sender].push(participation); totalPoolShares += poolShares; totalDeposit += amount; SafeERC20.safeTransferFrom(address(depositToken), msg.sender, address(this), amount); } function withdrawDeposit() public returns(uint) { require(currentState == State.DepositWithdrawnFromDx || currentState == State.MgnUnlocked, "Funds not yet withdrawn from dx"); require(!hasParticpationWithdrawn[msg.sender],"sender has already withdrawn funds"); uint totalDepositAmount = 0; Participation[] storage participations = participationsByAddress[msg.sender]; uint length = participations.length; for (uint i = 0; i < length; i++) { totalDepositAmount += calculateClaimableDeposit(participations[i]); } hasParticpationWithdrawn[msg.sender] = true; SafeERC20.safeTransfer(address(depositToken), msg.sender, totalDepositAmount); return totalDepositAmount; } function withdrawMagnolia() public returns(uint) { require(currentState == State.MgnUnlocked, "MGN has not been unlocked, yet"); require(hasParticpationWithdrawn[msg.sender], "Withdraw deposits first"); uint totalMgnClaimed = 0; Participation[] memory participations = participationsByAddress[msg.sender]; for (uint i = 0; i < participations.length; i++) { totalMgnClaimed += calculateClaimableMgn(participations[i]); } delete participationsByAddress[msg.sender]; delete hasParticpationWithdrawn[msg.sender]; SafeERC20.safeTransfer(address(mgnToken), msg.sender, totalMgnClaimed); return totalMgnClaimed; } function withdrawDepositandMagnolia() public returns(uint, uint){ return (withdrawDeposit(),withdrawMagnolia()); } function participateInAuction() public onlyOwner() { checkForStateUpdate(); require(currentState == State.Pooling, "Pooling period is over."); uint auctionIndex = dx.getAuctionIndex(address(depositToken), address(secondaryToken)); require(auctionIndex > lastParticipatedAuctionIndex, "Has to wait for new auction to start"); (address sellToken, address buyToken) = sellAndBuyToken(); uint depositAmount = depositToken.balanceOf(address(this)); if (isDepositTokenTurn()) { totalPoolSharesCummulative += 2 * totalPoolShares; if( depositAmount > 0){ //depositing new tokens depositToken.approve(address(dx), depositAmount); dx.deposit(address(depositToken), depositAmount); } } // Don't revert if we can't claimSellerFunds address(dx).call(abi.encodeWithSignature("claimSellerFunds(address,address,address,uint256)", buyToken, sellToken, address(this), lastParticipatedAuctionIndex)); uint amount = dx.balances(address(sellToken), address(this)); if (isDepositTokenTurn()) { totalDeposit = amount; } (lastParticipatedAuctionIndex, ) = dx.postSellOrder(sellToken, buyToken, 0, amount); auctionCount += 1; } function triggerMGNunlockAndClaimTokens() public { checkForStateUpdate(); require(currentState == State.PoolingEnded, "Pooling period is not yet over."); require( dx.getAuctionIndex(address(depositToken), address(secondaryToken)) > lastParticipatedAuctionIndex, "Last auction is still running" ); // Don't revert if wen can't claimSellerFunds address(dx).call(abi.encodeWithSignature("claimSellerFunds(address,address,address,uint256)", secondaryToken, depositToken, address(this), lastParticipatedAuctionIndex)); mgnToken.unlockTokens(); uint amountOfFundsInDX = dx.balances(address(depositToken), address(this)); totalDeposit = amountOfFundsInDX + depositToken.balanceOf(address(this)); if(amountOfFundsInDX > 0){ dx.withdraw(address(depositToken), amountOfFundsInDX); } currentState = State.DepositWithdrawnFromDx; } function withdrawUnlockedMagnoliaFromDx() public { require(currentState == State.DepositWithdrawnFromDx, "Unlocking not yet triggered"); // Implicitly we also have: // require(unlockedTokens[msg.sender].withdrawalTime < now, "The tokens cannot be withdrawn yet"); mgnToken.withdrawUnlockedTokens(); totalMgn = mgnToken.balanceOf(address(this)); currentState = State.MgnUnlocked; } function checkForStateUpdate() public { if (now >= poolingPeriodEndTime && isDepositTokenTurn() && currentState == State.Pooling) { currentState = State.PoolingEnded; } } /// @dev updates state and returns val function updateAndGetCurrentState() public returns(State) { checkForStateUpdate(); return currentState; } /** * Public View Functions */ function numberOfParticipations(address addr) public view returns (uint) { return participationsByAddress[addr].length; } function participationAtIndex(address addr, uint index) public view returns (uint, uint) { Participation memory participation = participationsByAddress[addr][index]; return (participation.startAuctionCount, participation.poolShares); } function poolSharesByAddress(address userAddress) external view returns(uint[] memory) { uint length = participationsByAddress[userAddress].length; uint[] memory userTotalPoolShares = new uint[](length); for (uint i = 0; i < length; i++) { userTotalPoolShares[i] = participationsByAddress[userAddress][i].poolShares; } return userTotalPoolShares; } function sellAndBuyToken() public view returns(address sellToken, address buyToken) { if (isDepositTokenTurn()) { return (address(depositToken), address(secondaryToken)); } else { return (address(secondaryToken), address(depositToken)); } } function getAllClaimableMgnAndDeposits(address userAddress) external view returns(uint[] memory, uint[] memory) { uint length = participationsByAddress[userAddress].length; uint[] memory allUserClaimableMgn = new uint[](length); uint[] memory allUserClaimableDeposit = new uint[](length); for (uint i = 0; i < length; i++) { allUserClaimableMgn[i] = calculateClaimableMgn(participationsByAddress[userAddress][i]); allUserClaimableDeposit[i] = calculateClaimableDeposit(participationsByAddress[userAddress][i]); } return (allUserClaimableMgn, allUserClaimableDeposit); } /** * Internal Helpers */ function calculatePoolShares(uint amount) private view returns (uint) { if (totalDeposit == 0) { return amount; } else { return totalPoolShares.mul(amount) / totalDeposit; } } function isDepositTokenTurn() private view returns (bool) { return auctionCount % 2 == 0; } function calculateClaimableMgn(Participation memory participation) private view returns (uint) { if (totalPoolSharesCummulative == 0) { return 0; } uint duration = auctionCount - participation.startAuctionCount; return totalMgn.mul(participation.poolShares).mul(duration) / totalPoolSharesCummulative; } function calculateClaimableDeposit(Participation memory participation) private view returns (uint) { if (totalPoolShares == 0) { return 0; } return totalDeposit.mul(participation.poolShares) / totalPoolShares; } } // File: contracts/Coordinator.sol pragma solidity ^0.5.0; contract Coordinator { DxMgnPool public dxMgnPool1; DxMgnPool public dxMgnPool2; constructor ( ERC20 _token1, ERC20 _token2, IDutchExchange _dx, uint _poolingTime ) public { dxMgnPool1 = new DxMgnPool(_token1, _token2, _dx, _poolingTime); dxMgnPool2 = new DxMgnPool(_token2, _token1, _dx, _poolingTime); } function participateInAuction() public { dxMgnPool1.participateInAuction(); dxMgnPool2.participateInAuction(); } function canParticipate() public returns (bool) { uint auctionIndex = dxMgnPool1.dx().getAuctionIndex( address(dxMgnPool1.depositToken()), address(dxMgnPool1.secondaryToken()) ); // update the state before checking the currentState dxMgnPool1.checkForStateUpdate(); // Since both auctions start at the same time, it suffices to check one. return auctionIndex > dxMgnPool1.lastParticipatedAuctionIndex() && dxMgnPool1.currentState() == DxMgnPool.State.Pooling; } }