Transaction Hash:
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 | ||
|---|---|---|---|---|---|
| 0x182D4990...abad575E7 | |||||
| 0x36d7C4d0...1AB1Fd469 |
2.15398045 Eth
Nonce: 46
|
0.253829422 Eth
Nonce: 47
| 1.900151028 | ||
| 0x60e7CB73...f7275aF0C | 28.750749656113274155 Eth | 30.650749656113274155 Eth | 1.9 | ||
| 0xa9375923...415C02696 | |||||
|
0xAA5c4244...ba1aCF05E
Miner
| 22,612.664496189598657558 Eth | 22,612.664647217598657558 Eth | 0.000151028 |
Execution Trace
ETH 1.9
OwnersExchange.fillSellOrder( _key=977 )
- ETH 1.9
0x60e7cb73b6bb7506cffd803e60fcec5f7275af0c.CALL( ) -
PoolOwners.sendOwnership( _receiver=0x36d7C4d0cc3feeD1D6446dF3A56975B1AB1Fd469, _amount=40000000000000000 )
fillSellOrder[OwnersExchange (ln:436)]
get[OwnersExchange (ln:437)]ORDER_TYPE[OwnersExchange (ln:438)]addressRegister[OwnersExchange (ln:440)]div[OwnersExchange (ln:449)]mul[OwnersExchange (ln:449)]remove[OwnersExchange (ln:452)]transfer[OwnersExchange (ln:454)]sendOwnership[OwnersExchange (ln:455)]sub[OwnersExchange (ln:458)]mul[OwnersExchange (ln:459)]sub[OwnersExchange (ln:460)]transfer[OwnersExchange (ln:461)]OrderFilled[OwnersExchange (ln:464)]
File 1 of 2: OwnersExchange
File 2 of 2: PoolOwners
// 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;
}
}