pragma solidity ^0.4.25;
/**
* @title SafeMath
* @dev Math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, 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 numbers truncating the quotient, reverts on division by zero.
*/
function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
require(_b > 0); // Solidity only automatically asserts 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;
}
/**
* @dev Subtracts two numbers, 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 numbers, 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 numbers 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;
}
}
/**
* @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);
_;
}
/**
* @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;
}
}
interface token {
function transfer(address, uint) external returns (bool);
function transferFrom(address, address, uint) external returns (bool);
function allowance(address, address) external constant returns (uint256);
function balanceOf(address) external constant returns (uint256);
}
/** LOGIC DESCRIPTION
* 11% fees in and out for ETH
* 11% fees in and out for NOVA
*
* ETH fees split:
* 6% to nova holders
* 4% to eth holders
* 1% to fixed address
*
* NOVA fees split:
* 6% to nova holders
* 4% to eth holders
* 1% airdrop to a random address based on their nova shares
* rules:
* - you need to have both nova and eth to get dividends
*/
contract NovaBox is Ownable {
using SafeMath for uint;
token tokenReward;
constructor() public {
tokenReward = token(0x72FBc0fc1446f5AcCC1B083F0852a7ef70a8ec9f);
}
event AirDrop(address to, uint amount, uint randomTicket);
event DividendsTransferred(address to, uint ethAmount, uint novaAmount);
// ether contributions
mapping (address => uint) public contributionsEth;
// token contributions
mapping (address => uint) public contributionsToken;
// investors list who have deposited BOTH ether and token
mapping (address => uint) public indexes;
mapping (uint => address) public addresses;
uint256 public lastIndex = 0;
mapping (address => bool) public addedToList;
uint _totalTokens = 0;
uint _totalWei = 0;
uint pointMultiplier = 1e18;
mapping (address => uint) public last6EthDivPoints;
uint public total6EthDivPoints = 0;
// uint public unclaimed6EthDivPoints = 0;
mapping (address => uint) public last4EthDivPoints;
uint public total4EthDivPoints = 0;
// uint public unclaimed4EthDivPoints = 0;
mapping (address => uint) public last6TokenDivPoints;
uint public total6TokenDivPoints = 0;
// uint public unclaimed6TokenDivPoints = 0;
mapping (address => uint) public last4TokenDivPoints;
uint public total4TokenDivPoints = 0;
// uint public unclaimed4TokenDivPoints = 0;
function ethDivsOwing(address _addr) public view returns (uint) {
return eth4DivsOwing(_addr).add(eth6DivsOwing(_addr));
}
function eth6DivsOwing(address _addr) public view returns (uint) {
if (!addedToList[_addr]) return 0;
uint newEth6DivPoints = total6EthDivPoints.sub(last6EthDivPoints[_addr]);
return contributionsToken[_addr].mul(newEth6DivPoints).div(pointMultiplier);
}
function eth4DivsOwing(address _addr) public view returns (uint) {
if (!addedToList[_addr]) return 0;
uint newEth4DivPoints = total4EthDivPoints.sub(last4EthDivPoints[_addr]);
return contributionsEth[_addr].mul(newEth4DivPoints).div(pointMultiplier);
}
function tokenDivsOwing(address _addr) public view returns (uint) {
return token4DivsOwing(_addr).add(token6DivsOwing(_addr));
}
function token6DivsOwing(address _addr) public view returns (uint) {
if (!addedToList[_addr]) return 0;
uint newToken6DivPoints = total6TokenDivPoints.sub(last6TokenDivPoints[_addr]);
return contributionsToken[_addr].mul(newToken6DivPoints).div(pointMultiplier);
}
function token4DivsOwing(address _addr) public view returns (uint) {
if (!addedToList[_addr]) return 0;
uint newToken4DivPoints = total4TokenDivPoints.sub(last4TokenDivPoints[_addr]);
return contributionsEth[_addr].mul(newToken4DivPoints).div(pointMultiplier);
}
function updateAccount(address account) private {
uint owingEth6 = eth6DivsOwing(account);
uint owingEth4 = eth4DivsOwing(account);
uint owingEth = owingEth4.add(owingEth6);
uint owingToken6 = token6DivsOwing(account);
uint owingToken4 = token4DivsOwing(account);
uint owingToken = owingToken4.add(owingToken6);
if (owingEth > 0) {
// send ether dividends to account
account.transfer(owingEth);
}
if (owingToken > 0) {
// send token dividends to account
tokenReward.transfer(account, owingToken);
}
last6EthDivPoints[account] = total6EthDivPoints;
last4EthDivPoints[account] = total4EthDivPoints;
last6TokenDivPoints[account] = total6TokenDivPoints;
last4TokenDivPoints[account] = total4TokenDivPoints;
emit DividendsTransferred(account, owingEth, owingToken);
}
function addToList(address sender) private {
addedToList[sender] = true;
// if the sender is not in the list
if (indexes[sender] == 0) {
_totalTokens = _totalTokens.add(contributionsToken[sender]);
_totalWei = _totalWei.add(contributionsEth[sender]);
// add the sender to the list
lastIndex++;
addresses[lastIndex] = sender;
indexes[sender] = lastIndex;
}
}
function removeFromList(address sender) private {
addedToList[sender] = false;
// if the sender is in temp eth list
if (indexes[sender] > 0) {
_totalTokens = _totalTokens.sub(contributionsToken[sender]);
_totalWei = _totalWei.sub(contributionsEth[sender]);
// remove the sender from temp eth list
addresses[indexes[sender]] = addresses[lastIndex];
indexes[addresses[lastIndex]] = indexes[sender];
indexes[sender] = 0;
delete addresses[lastIndex];
lastIndex--;
}
}
// desposit ether
function () payable public {
address sender = msg.sender;
// size of code at target address
uint codeLength;
// get the length of code at the sender address
assembly {
codeLength := extcodesize(sender)
}
// don't allow contracts to deposit ether
require(codeLength == 0);
uint weiAmount = msg.value;
updateAccount(sender);
// number of ether sent must be greater than 0
require(weiAmount > 0);
uint _89percent = weiAmount.mul(89).div(100);
uint _6percent = weiAmount.mul(6).div(100);
uint _4percent = weiAmount.mul(4).div(100);
uint _1percent = weiAmount.mul(1).div(100);
distributeEth(
_6percent, // to nova investors
_4percent // to eth investors
);
//1% goes to REX Investors
owner.transfer(_1percent);
contributionsEth[sender] = contributionsEth[sender].add(_89percent);
// if the sender is in list
if (indexes[sender]>0) {
// increase _totalWei
_totalWei = _totalWei.add(_89percent);
}
// if the sender has also deposited tokens, add sender to list
if (contributionsToken[sender]>0) addToList(sender);
}
// withdraw ether
function withdrawEth(uint amount) public {
address sender = msg.sender;
require(amount>0 && contributionsEth[sender] >= amount);
updateAccount(sender);
uint _89percent = amount.mul(89).div(100);
uint _6percent = amount.mul(6).div(100);
uint _4percent = amount.mul(4).div(100);
uint _1percent = amount.mul(1).div(100);
contributionsEth[sender] = contributionsEth[sender].sub(amount);
// if sender is in list
if (indexes[sender]>0) {
// decrease total wei
_totalWei = _totalWei.sub(amount);
}
// if the sender has withdrawn all their eth
// remove the sender from list
if (contributionsEth[sender] == 0) removeFromList(sender);
sender.transfer(_89percent);
distributeEth(
_6percent, // to nova investors
_4percent // to eth investors
);
owner.transfer(_1percent); //1% goes to REX Investors
}
// deposit tokens
function depositTokens(address randomAddr, uint randomTicket) public {
updateAccount(msg.sender);
address sender = msg.sender;
uint amount = tokenReward.allowance(sender, address(this));
// number of allowed tokens must be greater than 0
// if it is then transfer the allowed tokens from sender to the contract
// if not transferred then throw
require(amount>0 && tokenReward.transferFrom(sender, address(this), amount));
uint _89percent = amount.mul(89).div(100);
uint _6percent = amount.mul(6).div(100);
uint _4percent = amount.mul(4).div(100);
uint _1percent = amount.mul(1).div(100);
distributeTokens(
_6percent, // to nova investors
_4percent // to eth investors
);
tokenReward.transfer(randomAddr, _1percent);
// 1% for Airdrop
emit AirDrop(randomAddr, _1percent, randomTicket);
contributionsToken[sender] = contributionsToken[sender].add(_89percent);
// if sender is in list
if (indexes[sender]>0) {
// increase totaltokens
_totalTokens = _totalTokens.add(_89percent);
}
// if the sender has also contributed ether add sender to list
if (contributionsEth[sender]>0) addToList(sender);
}
// withdraw tokens
function withdrawTokens(uint amount, address randomAddr, uint randomTicket) public {
address sender = msg.sender;
updateAccount(sender);
// requested amount must be greater than 0 and
// the sender must have contributed tokens no less than `amount`
require(amount>0 && contributionsToken[sender]>=amount);
uint _89percent = amount.mul(89).div(100);
uint _6percent = amount.mul(6).div(100);
uint _4percent = amount.mul(4).div(100);
uint _1percent = amount.mul(1).div(100);
contributionsToken[sender] = contributionsToken[sender].sub(amount);
// if sender is in list
if (indexes[sender]>0) {
// decrease total tokens
_totalTokens = _totalTokens.sub(amount);
}
// if sender withdrawn all their tokens, remove them from list
if (contributionsToken[sender] == 0) removeFromList(sender);
tokenReward.transfer(sender, _89percent);
distributeTokens(
_6percent, // to nova investors
_4percent // to eth investors
);
// airdropToRandom(_1percent);
tokenReward.transfer(randomAddr, _1percent);
emit AirDrop(randomAddr, _1percent, randomTicket);
}
function distributeTokens(uint _6percent, uint _4percent) private {
uint totalTokens = getTotalTokens();
uint totalWei = getTotalWei();
if (totalWei == 0 || totalTokens == 0) return;
total4TokenDivPoints = total4TokenDivPoints.add(_4percent.mul(pointMultiplier).div(totalWei));
// unclaimed4TokenDivPoints = unclaimed4TokenDivPoints.add(_4percent);
total6TokenDivPoints = total6TokenDivPoints.add(_6percent.mul(pointMultiplier).div(totalTokens));
// unclaimed6TokenDivPoints = unclaimed6TokenDivPoints.add(_6percent);
}
function distributeEth(uint _6percent, uint _4percent) private {
uint totalTokens = getTotalTokens();
uint totalWei = getTotalWei();
if (totalWei ==0 || totalTokens == 0) return;
total4EthDivPoints = total4EthDivPoints.add(_4percent.mul(pointMultiplier).div(totalWei));
// unclaimed4EthDivPoints += _4percent;
total6EthDivPoints = total6EthDivPoints.add(_6percent.mul(pointMultiplier).div(totalTokens));
// unclaimed6EthDivPoints += _6percent;
}
// get sum of tokens contributed by the ether investors
function getTotalTokens() public view returns (uint) {
return _totalTokens;
}
// get the sum of wei contributed by the token investors
function getTotalWei() public view returns (uint) {
return _totalWei;
}
function withdrawDivs() public {
updateAccount(msg.sender);
}
// get the list of investors
function getList() public view returns (address[], uint[]) {
address[] memory _addrs = new address[](lastIndex);
uint[] memory _contributions = new uint[](lastIndex);
for (uint i = 1; i <= lastIndex; i++) {
_addrs[i-1] = addresses[i];
_contributions[i-1] = contributionsToken[addresses[i]];
}
return (_addrs, _contributions);
}
}
{
"compilationTarget": {
"NovaBox.sol": "NovaBox"
},
"evmVersion": "byzantium",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"last6EthDivPoints","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"total6EthDivPoints","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getTotalWei","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_addr","type":"address"}],"name":"token4DivsOwing","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_addr","type":"address"}],"name":"tokenDivsOwing","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"contributionsToken","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_addr","type":"address"}],"name":"eth4DivsOwing","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"last6TokenDivPoints","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"indexes","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_addr","type":"address"}],"name":"token6DivsOwing","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_addr","type":"address"}],"name":"eth6DivsOwing","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_addr","type":"address"}],"name":"ethDivsOwing","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"randomAddr","type":"address"},{"name":"randomTicket","type":"uint256"}],"name":"depositTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"addedToList","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"total4EthDivPoints","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getList","outputs":[{"name":"","type":"address[]"},{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"withdrawDivs","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"total4TokenDivPoints","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"withdrawEth","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"contributionsEth","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"last4EthDivPoints","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"last4TokenDivPoints","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"total6TokenDivPoints","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"addresses","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getTotalTokens","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"lastIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"},{"name":"randomAddr","type":"address"},{"name":"randomTicket","type":"uint256"}],"name":"withdrawTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"randomTicket","type":"uint256"}],"name":"AirDrop","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"ethAmount","type":"uint256"},{"indexed":false,"name":"novaAmount","type":"uint256"}],"name":"DividendsTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}]