ETH Price: $2,019.51 (+1.83%)

Transaction Decoder

Block:
8204330 at Jul-23-2019 02:44:59 AM +UTC
Transaction Fee:
0.000151028 ETH $0.31
Gas Used:
151,028 Gas / 1 Gwei

Emitted Events:

155 PoolOwners.OwnershipTransferred( previousOwner=[Receiver] OwnersExchange, newOwner=[Sender] 0x36d7c4d0cc3feed1d6446df3a56975b1ab1fd469, amount=40000000000000000 )
156 OwnersExchange.OrderFilled( orderType=2, sender=0x60e7cb73b6bb7506cffd803e60fcec5f7275af0c, receiver=[Sender] 0x36d7c4d0cc3feed1d6446df3a56975b1ab1fd469, price=47500000000000000000, amount=40000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x182D4990...abad575E7
0x36d7C4d0...1AB1Fd469
2.15398045 Eth
Nonce: 46
0.253829422 Eth
Nonce: 47
1.900151028
0x60e7CB73...f7275aF0C 28.750749656113274155 Eth30.650749656113274155 Eth1.9
0xa9375923...415C02696
22,612.664496189598657558 Eth22,612.664647217598657558 Eth0.000151028

Execution Trace

ETH 1.9 OwnersExchange.fillSellOrder( _key=977 )
  • ETH 1.9 0x60e7cb73b6bb7506cffd803e60fcec5f7275af0c.CALL( )
  • PoolOwners.sendOwnership( _receiver=0x36d7C4d0cc3feeD1D6446dF3A56975B1AB1Fd469, _amount=40000000000000000 )
    File 1 of 2: OwnersExchange
    // File: contracts/interface/PoolOwnersInterface.sol
    
    pragma solidity ^0.4.23;
    
    contract PoolOwnersInterface {
    
        bool public distributionActive;
    
        function sendOwnership(address _receiver, uint256 _amount) public;
        function sendOwnershipFrom(address _owner, address _receiver, uint256 _amount) public;
        function getOwnerTokens(address _owner) public view returns (uint);
        function getOwnerPercentage(address _owner) public view returns (uint);
    
    }
    
    // File: contracts/std/ERC20Basic.sol
    
    pragma solidity ^0.4.2;
    
    /**
     * @title ERC20Basic
     * @dev Simpler version of ERC20 interface
     * @dev see https://github.com/ethereum/EIPs/issues/179
     */
    contract ERC20Basic {
        uint256 public totalSupply;
        function balanceOf(address who) public view returns (uint256);
        function transfer(address to, uint256 value) public returns (bool);
        event Transfer(address indexed from, address indexed to, uint256 value);
    }
    
    // File: contracts/std/ERC20.sol
    
    pragma solidity ^0.4.2;
    
    
    /**
     * @title ERC20 interface
     * @dev see https://github.com/ethereum/EIPs/issues/20
     */
    contract ERC20 is ERC20Basic {
        function allowance(address owner, address spender) public view returns (uint256);
        function transferFrom(address from, address to, uint256 value) public returns (bool);
        function approve(address spender, uint256 value) public returns (bool);
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    
    // File: contracts/std/SafeMath.sol
    
    pragma solidity ^0.4.2;
    
    
    /**
     * @title SafeMath
     * @dev Math operations with safety checks that throw on error
     */
    library SafeMath {
      function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a * b;
        assert(a == 0 || c / a == b);
        return c;
      }
    
      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 c;
      }
    
      function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b <= a);
        return a - b;
      }
    
      function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        assert(c >= a);
        return c;
      }
    }
    
    // File: contracts/std/Ownable.sol
    
    pragma solidity ^0.4.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 public 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() public {
        owner = msg.sender;
      }
    
    
      /**
       * @dev Throws if called by any account other than the owner.
       */
      modifier onlyOwner() {
        require(msg.sender == owner, "Sender not authorised");
        _;
      }
    
    
      /**
       * @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) onlyOwner public {
        require(newOwner != address(0));
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
      }
    
    }
    
    // File: contracts/lib/ItMap.sol
    
    pragma solidity ^0.4.3;
    
    /**
        @title ItMap, a solidity iterable map
        @dev Credit to: https://gist.github.com/ethers/7e6d443818cbc9ad2c38efa7c0f363d1
     */
    library itmap {
        struct entry {
            // Equal to the index of the key of this item in keys, plus 1.
            uint keyIndex;
            uint value;
        }
    
        struct itmap {
            mapping(uint => entry) data;
            uint[] keys;
        }
    
        function insert(itmap storage self, uint key, uint value) internal returns (bool replaced) {
            entry storage e = self.data[key];
            e.value = value;
            if (e.keyIndex > 0) {
                return true;
            } else {
                e.keyIndex = ++self.keys.length;
                self.keys[e.keyIndex - 1] = key;
                return false;
            }
        }
    
        function remove(itmap storage self, uint key) internal returns (bool success) {
            entry storage e = self.data[key];
    
            if (e.keyIndex == 0) {
                return false;
            }
    
            if (e.keyIndex < self.keys.length) {
                // Move an existing element into the vacated key slot.
                self.data[self.keys[self.keys.length - 1]].keyIndex = e.keyIndex;
                self.keys[e.keyIndex - 1] = self.keys[self.keys.length - 1];
            }
    
            self.keys.length -= 1;
            delete self.data[key];
            return true;
        }
    
        function contains(itmap storage self, uint key) internal view returns (bool exists) {
            return self.data[key].keyIndex > 0;
        }
    
        function size(itmap storage self) internal view returns (uint) {
            return self.keys.length;
        }
    
        function get(itmap storage self, uint key) internal view returns (uint) {
            return self.data[key].value;
        }
    
        function getKey(itmap storage self, uint idx) internal view returns (uint) {
            return self.keys[idx];
        }
    }
    
    // File: contracts/OwnersExchange.sol
    
    pragma solidity ^0.4.23;
    
    
    
    
    
    
    /**
        @title OwnersExchange
        @dev Allow for trustless exchange of LP owners tokens
     */
    contract OwnersExchange is Ownable {
    
        using SafeMath for uint;
        using itmap for itmap.itmap;
    
        enum ORDER_TYPE {
            NULL, BUY, SELL
        }
        uint public orderCount;
        uint public fee;
        uint public lockedFees;
        uint public totalFees;
        mapping(uint => uint) public feeBalances;
        address[] public addressRegistry; 
        mapping(address => uint) public addressIndex;
    
        itmap.itmap orderBook;
    
        PoolOwnersInterface public poolOwners;
        ERC20 public feeToken;
    
        event NewOrder(ORDER_TYPE indexed orderType, address indexed sender, uint price, uint amount);
        event OrderRemoved(ORDER_TYPE indexed orderType, address indexed sender, uint price, uint amount);
        event OrderFilled(ORDER_TYPE indexed orderType, address indexed sender, address receiver, uint price, uint amount);
    
        /**
            @dev Initialise the contract
            @param _poolOwners Set the address of the PoolOwners contract used in this DEX
         */
        constructor(address _poolOwners, address _feeToken) public {
            require(_poolOwners != address(0), "_poolOwners needs to be set");
            poolOwners = PoolOwnersInterface(_poolOwners);
            feeToken = ERC20(_feeToken);
            addressRegistry.push(address(0));
            orderCount = 1;
        }
    
        /**
            @dev Register an address to a uint allowing packing in orders
            @param _address The address to register
         */
        function addressRegister(address _address) private returns (uint) {
            if (addressIndex[_address] != 0) {
                return addressIndex[_address];
            } else {
                require(addressRegistry.length < 1 << 32, "Registered addresses hit maximum");
                addressIndex[_address] = addressRegistry.length;
                addressRegistry.push(_address);
                return addressRegistry.length - 1;
            }
        }
    
        /**
            @dev ERC677 Reciever for fee token transfer (Always expected to be LINK)
            @param _sender The address of the sender of the token
            @param _value The amount of token received
            @param _data Extra data, not needed in this use-case
         */
        function onTokenTransfer(address _sender, uint256 _value, bytes _data) public {
            require(msg.sender == address(feeToken), "Sender needs to be the fee token");
            uint index = addressRegister(_sender);
            feeBalances[index] = feeBalances[index].add(_value);
            totalFees = totalFees.add(_value);
        }
    
        /**
            @dev Allow users to withdraw any tokens used for fees
            @param _value The amount wanting to be withdrawn
         */
        function withdrawFeeToken(uint256 _value) public {
            uint index = addressRegister(msg.sender);
            require(feeBalances[index] >= _value, "You're withdrawing more than your balance");
            feeBalances[index] = feeBalances[index].sub(_value);
            totalFees = totalFees.sub(_value);
            if (feeBalances[index] == 0) {
                delete feeBalances[index];
            }
            feeToken.transfer(msg.sender, _value);
        }
    
        /**
            @dev Set the fee percentage
            @param _fee The percentage of fees to be taken in LINK
         */
        function setFee(uint _fee) public onlyOwner {
            require(_fee <= 500 finney, "Fees can't be more than 50%");
            fee = _fee;
        }
    
        /**
            @dev Returns the fee cost based on a price & amount
            @param _price The price of the order
            @param _amount The amount requested
         */
        function feeForOrder(uint _price, uint _amount) public view returns (uint) {
            return _price
                .mul(_amount)
                .div(1 ether)
                .mul(fee)
                .div(1 ether);
        }
    
        /**
            @dev Returns the ETH cost of an order
            @param _price The price of the order
            @param _amount The amount requested
         */
        function costOfOrder(uint _price, uint _amount) public pure returns (uint) {
            return _price.mul(_amount).div(1 ether);
        }
    
        /**
            @dev Create a new sell order
            @param _price The price of the order per 1 ether of token
            @param _amount The amount of tokens being sent
         */
        function addSellOrder(uint _price, uint _amount) public {
            require(is111bit(_price) && is111bit(_amount), "Price or amount exceeds 111 bits");
    
            require(_price > 0, "Price needs to be greater than 0");
            require(_amount > 0, "Amount needs to be greater than 0");
    
            uint orderFee = feeForOrder(_price, _amount);
            uint index = addressRegister(msg.sender);
            if (orderFee > 0) {
                require(feeBalances[index] >= orderFee, "You do not have enough deposited for fees");
                feeBalances[index] = feeBalances[index].sub(orderFee);
            }
            poolOwners.sendOwnershipFrom(msg.sender, this, _amount);
    
            require(
                !orderBook.insert(orderCount, (((uint(ORDER_TYPE.SELL) << 32 | index) << 111 | _price) << 111) | _amount), 
                "Map replacement detected"
            );
            orderCount += 1;
        
            emit NewOrder(ORDER_TYPE.SELL, msg.sender, _price, _amount);
        }
    
        /**
            @dev Add a new buy order, ETH sent needs to equal: (price * amount) / 18
            @param _price The price of the buy order per 1 ether of LP token
            @param _amount The amount of tokens wanting to be purchased
         */
        function addBuyOrder(uint _price, uint _amount) public payable {
            require(is111bit(_price) && is111bit(_amount), "Price or amount exceeds 111 bits");
    
            require(_price > 0, "Price needs to be greater than 0");
            require(_amount > 0, "Amount needs to be greater than 0");
    
            uint orderFee = feeForOrder(_price, _amount);
            uint index = addressRegister(msg.sender);
            if (orderFee > 0) {
                require(feeBalances[index] >= orderFee, "You do not have enough deposited for fees");
                feeBalances[index] = feeBalances[index].sub(orderFee);
            }
    
            uint cost = _price.mul(_amount).div(1 ether);
            require(_price.mul(_amount) == cost.mul(1 ether), "The price and amount of this order is too small");
            require(msg.value == cost, "ETH sent needs to equal the cost");
    
            require(
                !orderBook.insert(orderCount, (((uint(ORDER_TYPE.BUY) << 32 | index) << 111 | _price) << 111) | _amount), 
                "Map replacement detected"
            );
            orderCount += 1;
        
            emit NewOrder(ORDER_TYPE.BUY, msg.sender, _price, _amount);
        }
    
        /**
            @dev Remove a buy order and refund ETH back to the sender
            @param _key The key of the order in the book
         */
        function removeBuyOrder(uint _key) public {
            uint order = orderBook.get(_key);
            ORDER_TYPE orderType = ORDER_TYPE(order >> 254);
            require(orderType == ORDER_TYPE.BUY, "This is not a buy order");
            uint index = addressIndex[msg.sender];
            require(index == (order << 2) >> 224, "You are not the sender of this order");
    
            uint price = (order << 34) >> 145;
            uint amount = (order << 145) >> 145;
            require(orderBook.remove(_key), "Map remove failed");
    
            uint orderFee = feeForOrder(price, amount);
            if (orderFee > 0) {
                feeBalances[index] = feeBalances[index].add(orderFee);
            }
    
            uint cost = price.mul(amount).div(1 ether);
            msg.sender.transfer(cost);
    
            emit OrderRemoved(orderType, msg.sender, price, amount);
        }
    
        /**
            @dev Remove a sell order and refund the LP tokens back to the sender
            @param _key The key of the order in the book
         */
        function removeSellOrder(uint _key) public {
            uint order = orderBook.get(_key);
            ORDER_TYPE orderType = ORDER_TYPE(order >> 254);
            require(orderType == ORDER_TYPE.SELL, "This is not a sell order");
            uint index = addressIndex[msg.sender];
            require(index == (order << 2) >> 224, "You are not the sender of this order");
    
            uint price = (order << 34) >> 145;
            uint amount = (order << 145) >> 145;
            require(orderBook.remove(_key), "Map remove failed");
    
            uint orderFee = feeForOrder(price, amount);
            if (orderFee > 0) {
                feeBalances[index] = feeBalances[index].add(orderFee);
            }
    
            poolOwners.sendOwnership(msg.sender, amount);
    
            emit OrderRemoved(orderType, msg.sender, price, amount);
        }
    
        /**
            @dev Fill a sell order in the order book
            @dev Orders have to be filled in whole amounts
            @param _key Key of the order as per orderbook
         */
        function fillSellOrder(uint _key) public payable {
            uint order = orderBook.get(_key);
            ORDER_TYPE orderType = ORDER_TYPE(order >> 254);
            require(orderType == ORDER_TYPE.SELL, "This is not a sell order");
            uint index = addressRegister(msg.sender);
            require(index != (order << 2) >> 224, "You cannot fill your own order");
    
            uint price = (order << 34) >> 145;
            uint amount = (order << 145) >> 145;
    
            uint orderFee = feeForOrder(price, amount);
            require(feeBalances[index] >= orderFee, "You do not have enough deposited fees to fill this order");
    
            uint cost = price.mul(amount).div(1 ether);
            require(msg.value == cost, "ETH sent needs to equal the cost");
    
            require(orderBook.remove(_key), "Map remove failed");
    
            addressRegistry[(order << 2) >> 224].transfer(msg.value);
            poolOwners.sendOwnership(msg.sender, amount);
    
            if (orderFee > 0) {
                feeBalances[index] = feeBalances[index].sub(orderFee);
                uint totalFee = orderFee.mul(2);
                totalFees = totalFees.sub(totalFee);
                feeToken.transfer(poolOwners, totalFee);
            }
    
            emit OrderFilled(orderType, addressRegistry[(order << 2) >> 224], msg.sender, price, amount);
        }
    
        /**
            @dev Fill a buy order in the order book
            @dev Orders have to be filled in whole amounts
            @param _key Key of the order, which is the buyers address
         */
        function fillBuyOrder(uint _key) public {
            uint order = orderBook.get(_key);
            ORDER_TYPE orderType = ORDER_TYPE(order >> 254);
            require(orderType == ORDER_TYPE.BUY, "This is not a buy order");
            uint index = addressRegister(msg.sender);
            require(index != (order << 2) >> 224, "You cannot fill your own order");
    
            uint price = (order << 34) >> 145;
            uint amount = (order << 145) >> 145;
    
            uint orderFee = feeForOrder(price, amount);
            require(feeBalances[index] >= orderFee, "You do not have enough deposited fees to fill this order");
    
            uint cost = price.mul(amount).div(1 ether);
            
            require(orderBook.remove(_key), "Map remove failed");
    
            msg.sender.transfer(cost);
            poolOwners.sendOwnershipFrom(msg.sender, addressRegistry[(order << 2) >> 224], amount);
    
            if (orderFee > 0) {
                feeBalances[index] = feeBalances[index].sub(orderFee);
                uint totalFee = orderFee.mul(2);
                totalFees = totalFees.sub(totalFee);
                feeToken.transfer(poolOwners, totalFee);
            }
    
            emit OrderFilled(orderType, addressRegistry[(order << 2) >> 224], msg.sender, price, amount);
        }
    
        /**
            @dev Send any fee token earned via PoolOwners distribution back to be re-distributed
         */
        function withdrawDistributedToPoolOwners() public {
            uint balance = feeToken.balanceOf(this).sub(totalFees);
            require(balance > 0, "There is no distributed fee token balance in the contract");
            feeToken.transfer(poolOwners, balance);
        }
    
        /**
            @dev Get a single order by its key
            @param _key The key of the order as per the book
         */
        function getOrder(uint _key) public view returns (ORDER_TYPE, address, uint, uint) {
            uint order = orderBook.get(_key);
            return (
                ORDER_TYPE(order >> 254), 
                addressRegistry[(order << 2) >> 224], 
                (order << 34) >> 145, 
                (order << 145) >> 145
            );
        }
    
        /**
            @dev Get a batch of 10 orders by a given array of keys
            @dev ID's has to be equal or less than 10 in length, or an empty response is given
            @param _start The starting index in the order book to return from
         */
        function getOrders(uint _start) public view returns (
            uint[10] keys,
            address[10] addresses, 
            ORDER_TYPE[10] orderTypes, 
            uint[10] prices, 
            uint[10] amounts
        ) {
            for (uint i = 0; i < 10; i++) {
                if (orderBook.size() == _start + i) {
                    break;
                }
                uint key = orderBook.getKey(_start + i);
                keys[i] = key;
                uint order = orderBook.get(key);
                addresses[i] = addressRegistry[(order << 2) >> 224];
                orderTypes[i] = ORDER_TYPE(order >> 254);
                prices[i] = (order << 34) >> 145;
                amounts[i] = (order << 145) >> 145;
            }
            return (keys, addresses, orderTypes, prices, amounts);
        }
    
        /**
            @dev Get an orderbook key from the orderbook index
            @param _i The index to fetch the key for
         */
        function getOrderBookKey(uint _i) public view returns (uint key) {
            if (_i < orderBook.size()) {
                key = orderBook.getKey(_i);
            } else {
                key = 0;
            }
            return key;
        }
    
        /**
            @dev Get orderbook keys in batches of 10
            @param _start The start of the index for the batch
         */
        function getOrderBookKeys(uint _start) public view returns (uint[10] keys) {
            for (uint i = 0; i < 10; i++) {
                if (i + _start < orderBook.size()) {
                    keys[i] = orderBook.getKey(_start + i);
                } else {
                    keys[i] = 0;
                }
            }
            return keys;
        }
    
        /**
            @dev Get the orderbook size to allow for batch fetching of keys
         */
        function getOrderBookSize() public view returns (uint) {
            return orderBook.size();
        }
    
        /**
            @dev Verify that the number being passed fits into 111 bits for packing
            @param _val The value to check
         */
        function is111bit(uint _val) private pure returns (bool) {
            return (_val < 1 << 111);
        }
    
    }

    File 2 of 2: PoolOwners
    /**
     * @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 public 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() public {
        owner = msg.sender;
      }
    
    
      /**
       * @dev Throws if called by any account other than the owner.
       */
      modifier onlyOwner() {
        require(msg.sender == owner, "Sender not authorised.");
        _;
      }
    
    
      /**
       * @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) onlyOwner public {
        require(newOwner != address(0));
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
      }
    
    }
    
    /**
     * @title ERC20Basic
     * @dev Simpler version of ERC20 interface
     * @dev see https://github.com/ethereum/EIPs/issues/179
     */
    contract ERC20Basic {
        uint256 public totalSupply;
        function balanceOf(address who) public view returns (uint256);
        function transfer(address to, uint256 value) public returns (bool);
        event Transfer(address indexed from, address indexed to, uint256 value);
    }
    
    /**
     * @title ERC20 interface
     * @dev see https://github.com/ethereum/EIPs/issues/20
     */
    contract ERC20 is ERC20Basic {
        function allowance(address owner, address spender) public view returns (uint256);
        function transferFrom(address from, address to, uint256 value) public returns (bool);
        function approve(address spender, uint256 value) public returns (bool);
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    
    /**
     * @title SafeMath
     * @dev Math operations with safety checks that throw on error
     */
    library SafeMath {
      function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a * b;
        assert(a == 0 || c / a == b);
        return c;
      }
    
      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 c;
      }
    
      function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b <= a);
        return a - b;
      }
    
      function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        assert(c >= a);
        return c;
      }
    }
    
    /**
        @title ItMap, a solidity iterable map
        @dev Credit to: https://gist.github.com/ethers/7e6d443818cbc9ad2c38efa7c0f363d1
     */
    library itmap {
        struct entry {
            // Equal to the index of the key of this item in keys, plus 1.
            uint keyIndex;
            uint value;
        }
    
        struct itmap {
            mapping(uint => entry) data;
            uint[] keys;
        }
        
        function insert(itmap storage self, uint key, uint value) internal returns (bool replaced) {
            entry storage e = self.data[key];
            e.value = value;
            if (e.keyIndex > 0) {
                return true;
            } else {
                e.keyIndex = ++self.keys.length;
                self.keys[e.keyIndex - 1] = key;
                return false;
            }
        }
        
        function remove(itmap storage self, uint key) internal returns (bool success) {
            entry storage e = self.data[key];
    
            if (e.keyIndex == 0) {
                return false;
            }
    
            if (e.keyIndex < self.keys.length) {
                // Move an existing element into the vacated key slot.
                self.data[self.keys[self.keys.length - 1]].keyIndex = e.keyIndex;
                self.keys[e.keyIndex - 1] = self.keys[self.keys.length - 1];
            }
    
            self.keys.length -= 1;
            delete self.data[key];
            return true;
        }
        
        function contains(itmap storage self, uint key) internal constant returns (bool exists) {
            return self.data[key].keyIndex > 0;
        }
        
        function size(itmap storage self) internal constant returns (uint) {
            return self.keys.length;
        }
        
        function get(itmap storage self, uint key) internal constant returns (uint) {
            return self.data[key].value;
        }
        
        function getKey(itmap storage self, uint idx) internal constant returns (uint) {
            return self.keys[idx];
        }
    }
    
    /**
        @title OwnersReceiver
        @dev PoolOwners supporting receiving contract
     */
    contract OwnersReceiver {
        function onOwnershipTransfer(address _sender, uint _value, bytes _data) public;
        function onOwnershipStake(address _sender, uint _value, bytes _data) public;
        function onOwnershipStakeRemoval(address _sender, uint _value, bytes _data) public;
    }
    
    /**
        @title PoolOwners
        @dev ERC20 token distribution to holders based on share ownership
     */
    contract PoolOwners is Ownable {
    
        using SafeMath for uint256;
        using itmap for itmap.itmap;
    
        itmap.itmap private ownerMap;
    
        mapping(address => mapping(address => uint256)) public allowance;
        mapping(address => mapping(address => uint256)) public stakes;
        mapping(address => uint256) public stakeTotals;
        mapping(address => bool) public tokenWhitelist;
        mapping(address => bool) public whitelist;
        mapping(address => uint256) public distributionMinimum;
        
        uint256 public totalContributed = 0;
        uint256 public precisionMinimum = 0.04 ether;
        uint256 private valuation = 4000 ether;
        uint256 private hardCap = 1000 ether;
        uint256 private distribution = 1;
        
        bool public distributionActive = false;
        bool public locked = false;
        bool private contributionStarted = false;
    
        address public wallet;
        address private dToken = address(0);
        
        uint   public constant totalSupply = 4000 ether;
        string public constant name = "LinkPool Owners";
        uint8  public constant decimals = 18;
        string public constant symbol = "LP";
    
        event Contribution(address indexed sender, uint256 share, uint256 amount);
        event TokenDistributionActive(address indexed token, uint256 amount, uint256 amountOfOwners);
        event TokenWithdrawal(address indexed token, address indexed owner, uint256 amount);
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner, uint256 amount);
        event TokenDistributionComplete(address indexed token, uint amount, uint256 amountOfOwners);
        event OwnershipStaked(address indexed owner, address indexed receiver, uint256 amount);
        event OwnershipStakeRemoved(address indexed owner, address indexed receiver, uint256 amount);
    
        modifier onlyPoolOwner() {
            require(ownerMap.get(uint(msg.sender)) != 0, "You are not authorised to call this function");
            _;
        }
    
        modifier withinPrecision(uint256 _amount) {
            require(_amount > 0, "Cannot use zero");
            require(_amount % precisionMinimum == 0, "Your amount isn't divisible by the minimum precision amount");
            _;
        }
    
        /**
            @dev Constructor set set the wallet initally
            @param _wallet Address of the ETH wallet
         */
        constructor(address _wallet) public {
            require(_wallet != address(0), "The ETH wallet address needs to be set");
            wallet = _wallet;
            tokenWhitelist[address(0)] = true; // 0x0 treated as ETH
        }
    
        /**
            @dev Fallback function, redirects to contribution
            @dev Transfers tokens to the wallet address
         */
        function() public payable {
            if (!locked) {
                require(contributionStarted, "Contribution is not active");
                require(whitelist[msg.sender], "You are not whitelisted");
                contribute(msg.sender, msg.value); 
                wallet.transfer(msg.value);
            }
        }
    
        /**
            @dev Manually set a contribution, used by owners to increase owners amounts
            @param _sender The address of the sender to set the contribution for you
            @param _value The amount that the owner has sent
         */
        function addContribution(address _sender, uint256 _value) public onlyOwner() { contribute(_sender, _value); }
    
        /**
            @dev Registers a new contribution, sets their share
            @param _sender The address of the wallet contributing
            @param _value The amount that the owner has sent
         */
        function contribute(address _sender, uint256 _value) private withinPrecision(_value) {
            require(_is128Bit(_value), "Contribution amount isn't 128bit or smaller");
            require(!locked, "Crowdsale period over, contribution is locked");
            require(!distributionActive, "Cannot contribute when distribution is active");
            require(_value >= precisionMinimum, "Amount needs to be above the minimum contribution");
            require(hardCap >= _value, "Your contribution is greater than the hard cap");
            require(hardCap >= totalContributed.add(_value), "Your contribution would cause the total to exceed the hardcap");
    
            totalContributed = totalContributed.add(_value);
            uint256 share = percent(_value, valuation, 5);
    
            uint owner = ownerMap.get(uint(_sender));
            if (owner != 0) { // Existing owner
                share += owner >> 128;
                uint value = (owner << 128 >> 128).add(_value);
                require(ownerMap.insert(uint(_sender), share << 128 | value), "Sender does not exist in the map");
            } else { // New owner
                require(!ownerMap.insert(uint(_sender), share << 128 | _value), "Map replacement detected");
            }
    
            emit Contribution(_sender, share, _value);
        }
    
        /**
            @dev Whitelist a wallet address
            @param _owner Wallet of the owner
         */
        function whitelistWallet(address _owner) external onlyOwner() {
            require(!locked, "Can't whitelist when the contract is locked");
            require(_owner != address(0), "Blackhole address");
            whitelist[_owner] = true;
        }
    
        /**
            @dev Start the distribution phase
         */
        function startContribution() external onlyOwner() {
            require(!contributionStarted, "Contribution has started");
            contributionStarted = true;
        }
    
        /**
            @dev Manually set a share directly, used to set the LinkPool members as owners
            @param _owner Wallet address of the owner
            @param _value The equivalent contribution value
         */
        function setOwnerShare(address _owner, uint256 _value) public onlyOwner() withinPrecision(_value) {
            require(!locked, "Can't manually set shares, it's locked");
            require(!distributionActive, "Cannot set owners share when distribution is active");
            require(_is128Bit(_value), "Contribution value isn't 128bit or smaller");
    
            uint owner = ownerMap.get(uint(_owner));
            uint share;
            if (owner == 0) {
                share = percent(_value, valuation, 5);
                require(!ownerMap.insert(uint(_owner), share << 128 | _value), "Map replacement detected");
            } else {
                share = (owner >> 128).add(percent(_value, valuation, 5));
                uint value = (owner << 128 >> 128).add(_value);
                require(ownerMap.insert(uint(_owner), share << 128 | value), "Sender does not exist in the map");
            }
        }
    
        /**
            @dev Transfer part or all of your ownership to another address
            @param _receiver The address that you're sending to
            @param _amount The amount of ownership to send, for your balance refer to `ownerShareTokens`
         */
        function sendOwnership(address _receiver, uint256 _amount) public onlyPoolOwner() {
            _sendOwnership(msg.sender, _receiver, _amount);
        }
    
        /**
            @dev Transfer part or all of your ownership to another address and call the receiving contract
            @param _receiver The address that you're sending to
            @param _amount The amount of ownership to send, for your balance refer to `ownerShareTokens`
         */
        function sendOwnershipAndCall(address _receiver, uint256 _amount, bytes _data) public onlyPoolOwner() {
            _sendOwnership(msg.sender, _receiver, _amount);
            if (_isContract(_receiver)) {
                OwnersReceiver(_receiver).onOwnershipTransfer(msg.sender, _amount, _data);
            }
        }
    
        /**
            @dev Transfer part or all of your ownership to another address on behalf of an owner
            @dev Same principle as approval in ERC20, to be used mostly by external contracts, eg DEX's
            @param _owner The address of the owner who's having tokens sent on behalf of
            @param _receiver The address that you're sending to
            @param _amount The amount of ownership to send, for your balance refer to `ownerShareTokens`
         */
        function sendOwnershipFrom(address _owner, address _receiver, uint256 _amount) public {
            require(allowance[_owner][msg.sender] >= _amount, "Sender is not approved to send ownership of that amount");
            allowance[_owner][msg.sender] = allowance[_owner][msg.sender].sub(_amount);
            if (allowance[_owner][msg.sender] == 0) {
                delete allowance[_owner][msg.sender];
            }
            _sendOwnership(_owner, _receiver, _amount);
        }
    
        /**
            @dev Increase the allowance of a sender
            @param _sender The address of the sender on behalf of the owner
            @param _amount The amount to increase approval by
         */
        function increaseAllowance(address _sender, uint256 _amount) public withinPrecision(_amount) {
            uint o = ownerMap.get(uint(msg.sender));
            require(o << 128 >> 128 >= _amount, "The amount to increase allowance by is higher than your balance");
            allowance[msg.sender][_sender] = allowance[msg.sender][_sender].add(_amount);
        }
    
        /**
            @dev Decrease the allowance of a sender
            @param _sender The address of the sender on behalf of the owner
            @param _amount The amount to decrease approval by
         */
        function decreaseAllowance(address _sender, uint256 _amount) public withinPrecision(_amount) {
            require(allowance[msg.sender][_sender] >= _amount, "The amount to decrease allowance by is higher than the current allowance");
            allowance[msg.sender][_sender] = allowance[msg.sender][_sender].sub(_amount);
            if (allowance[msg.sender][_sender] == 0) {
                delete allowance[msg.sender][_sender];
            }
        }
    
        /**
            @dev Stakes ownership with a contract, locking it from being transferred
            @dev Calls the `onOwnershipStake` implementation on the receiver
            @param _receiver The contract address to receive the stake
            @param _amount The amount to be staked
            @param _data Subsequent data to be sent with the stake
         */
        function stakeOwnership(address _receiver, uint256 _amount, bytes _data) public withinPrecision(_amount) {
            uint o = ownerMap.get(uint(msg.sender));
            require((o << 128 >> 128).sub(stakeTotals[msg.sender]) >= _amount, "The amount to be staked is higher than your balance");
            stakeTotals[msg.sender] = stakeTotals[msg.sender].add(_amount);
            stakes[msg.sender][_receiver] = stakes[msg.sender][_receiver].add(_amount);
            OwnersReceiver(_receiver).onOwnershipStake(msg.sender, _amount, _data);
            emit OwnershipStaked(msg.sender, _receiver, _amount);
        }
    
        /**
            @dev Removes an ownership stake
            @dev Calls the `onOwnershipStakeRemoval` implementation on the receiver
            @param _receiver The contract address to remove the stake
            @param _amount The amount of the stake to be removed
            @param _data Subsequent data to be sent with the stake
         */
        function removeOwnershipStake(address _receiver, uint256 _amount, bytes _data) public withinPrecision(_amount) {
            require(stakeTotals[msg.sender] >= _amount, "The stake amount to remove is higher than what's staked");
            require(stakes[msg.sender][_receiver] >= _amount, "The stake amount to remove is greater than what's staked with the receiver");
            stakeTotals[msg.sender] = stakeTotals[msg.sender].sub(_amount);
            stakes[msg.sender][_receiver] = stakes[msg.sender][_receiver].sub(_amount);
            if (stakes[msg.sender][_receiver] == 0) {
                delete stakes[msg.sender][_receiver];
            }
            if (stakeTotals[msg.sender] == 0) {
                delete stakeTotals[msg.sender];
            }
            OwnersReceiver(_receiver).onOwnershipStakeRemoval(msg.sender, _amount, _data);
            emit OwnershipStakeRemoved(msg.sender, _receiver, _amount);
        }
    
        /**
            @dev Lock the contribution/shares methods
         */
        function lockShares() public onlyOwner() {
            require(!locked, "Shares already locked");
            locked = true;
        }
    
        /**
            @dev Start the distribution phase in the contract so owners can claim their tokens
            @param _token The token address to start the distribution of
         */
        function distributeTokens(address _token) public onlyPoolOwner() {
            require(tokenWhitelist[_token], "Token is not whitelisted to be distributed");
            require(!distributionActive, "Distribution is already active");
            distributionActive = true;
    
            uint256 currentBalance;
            if (_token == address(0)) {
                currentBalance = address(this).balance;
            } else {
                currentBalance = ERC20(_token).balanceOf(this);
            }
            if (!_is128Bit(currentBalance)) {
                currentBalance = 1 << 128;
            }
            require(currentBalance > distributionMinimum[_token], "Amount in the contract isn't above the minimum distribution limit");
    
            distribution = currentBalance << 128;
            dToken = _token;
    
            emit TokenDistributionActive(_token, currentBalance, ownerMap.size());
        }
    
        /**
            @dev Batch claiming of tokens for owners
            @param _count The amount of owners to claim tokens for
         */
        function batchClaim(uint256 _count) public onlyPoolOwner() {
            require(distributionActive, "Distribution isn't active");
            uint claimed = distribution << 128 >> 128;
            uint to = _count.add(claimed);
            distribution = distribution >> 128 << 128 | to;
            require(_count.add(claimed) <= ownerMap.size(), "To value is greater than the amount of owners");
    
            if (to == ownerMap.size()) {
                distributionActive = false;
                emit TokenDistributionComplete(dToken, distribution >> 128, ownerMap.size());
            }
            for (uint256 i = claimed; i < to; i++) {
                _claimTokens(i);
            }
        }
    
        /**
            @dev Whitelist a token so it can be distributed
            @dev Token whitelist is due to the potential of minting tokens and constantly lock this contract in distribution
         */
        function whitelistToken(address _token, uint256 _minimum) public onlyOwner() {
            require(!tokenWhitelist[_token], "Token is already whitelisted");
            tokenWhitelist[_token] = true;
            distributionMinimum[_token] = _minimum;
        }
    
        /**
            @dev Set the minimum amount to be of transfered in this contract to start distribution
            @param _minimum The minimum amount
         */
        function setDistributionMinimum(address _token, uint256 _minimum) public onlyOwner() {
            distributionMinimum[_token] = _minimum;
        }
    
        /**
            @dev ERC20 implementation of balances to allow for viewing in supported wallets
            @param _owner The address of the owner
         */
        function balanceOf(address _owner) public view returns (uint) {
            return ownerMap.get(uint(_owner)) << 128 >> 128;
        }
    
        /**
            @dev Get the amount of unclaimed owners in a distribution cycle
         */
        function getClaimedOwners() public view returns (uint) {
            return distribution << 128 >> 128;
        }
    
        /**
            @dev Return an owners percentage
            @param _owner The address of the owner
         */
        function getOwnerPercentage(address _owner) public view returns (uint) {
            return ownerMap.get(uint(_owner)) >> 128;
        }
    
        /**
            @dev Return an owners share token amount
            @param _owner The address of the owner
         */
        function getOwnerTokens(address _owner) public view returns (uint) {
            return ownerMap.get(uint(_owner)) << 128 >> 128;
        }
    
        /**
            @dev Returns the current amount of active owners, ie share above 0
         */
        function getCurrentOwners() public view returns (uint) {
            return ownerMap.size();
        }
    
        /**
            @dev Returns owner address based on the key
            @param _i The index of the owner in the map
         */
        function getOwnerAddress(uint _i) public view returns (address) {
            require(_i < ownerMap.size(), "Index is greater than the map size");
            return address(ownerMap.getKey(_i));
        }
    
        /**
            @dev Returns the allowance amount for a sender address
            @param _owner The address of the owner
            @param _sender The address of the sender on an owners behalf
         */
        function getAllowance(address _owner, address _sender) public view returns (uint256) {
            return allowance[_owner][_sender];
        }
    
        /**
            @dev Credit to Rob Hitchens: https://stackoverflow.com/a/42739843
         */
        function percent(uint numerator, uint denominator, uint precision) private pure returns (uint quotient) {
            uint _numerator = numerator * 10 ** (precision+1);
            uint _quotient = ((_numerator / denominator) + 5) / 10;
            return ( _quotient);
        }
    
        // Private Methods
    
        /**
            @dev Claim the tokens for the next owner in the map
         */
        function _claimTokens(uint _i) private {
            address owner = address(ownerMap.getKey(_i));
            uint o = ownerMap.get(uint(owner));
            uint256 tokenAmount = (distribution >> 128).mul(o >> 128).div(100000);
            if (dToken == address(0) && !_isContract(owner)) {
                owner.transfer(tokenAmount);
            } else {
                require(ERC20(dToken).transfer(owner, tokenAmount), "ERC20 transfer failed");
            }
        }  
    
        /**
            @dev Transfers tokens to a different address
            @dev Shared by all transfer implementations
         */
        function _sendOwnership(address _owner, address _receiver, uint256 _amount) private withinPrecision(_amount) {
            uint o = ownerMap.get(uint(_owner));
            uint r = ownerMap.get(uint(_receiver));
    
            uint oTokens = o << 128 >> 128;
            uint rTokens = r << 128 >> 128;
    
            require(_is128Bit(_amount), "Amount isn't 128bit or smaller");
            require(_owner != _receiver, "You can't send to yourself");
            require(_receiver != address(0), "Ownership cannot be blackholed");
            require(oTokens > 0, "You don't have any ownership");
            require(oTokens.sub(stakeTotals[_owner]) >= _amount, "The amount to send exceeds the addresses balance");
            require(!distributionActive, "Distribution cannot be active when sending ownership");
            require(_amount % precisionMinimum == 0, "Your amount isn't divisible by the minimum precision amount");
    
            oTokens = oTokens.sub(_amount);
    
            if (oTokens == 0) {
                require(ownerMap.remove(uint(_owner)), "Address doesn't exist in the map");
            } else {
                uint oPercentage = percent(oTokens, valuation, 5);
                require(ownerMap.insert(uint(_owner), oPercentage << 128 | oTokens), "Sender does not exist in the map");
            }
            
            uint rTNew = rTokens.add(_amount);
            uint rPercentage = percent(rTNew, valuation, 5);
            if (rTokens == 0) {
                require(!ownerMap.insert(uint(_receiver), rPercentage << 128 | rTNew), "Map replacement detected");
            } else {
                require(ownerMap.insert(uint(_receiver), rPercentage << 128 | rTNew), "Sender does not exist in the map");
            }
    
            emit OwnershipTransferred(_owner, _receiver, _amount);
        }
    
        /**
            @dev Check whether an address given is a contract
         */
        function _isContract(address _addr) private view returns (bool hasCode) {
            uint length;
            assembly { length := extcodesize(_addr) }
            return length > 0;
        }
    
        /**
            @dev Strict type check for data packing
            @param _val The value for checking
         */
        function _is128Bit(uint _val) private pure returns (bool) {
            return _val < 1 << 128;
        }
    }