pragma solidity ^0.4.24;
// File: contracts/interface/PlayerBookInterface.sol
interface PlayerBookInterface {
function getPlayerID(address _addr) external returns(uint256);
function getPlayerName(uint256 _pID) external view returns(bytes32);
function getPlayerLAff(uint256 _pID) external view returns(uint256);
function getPlayerAddr(uint256 _pID) external view returns(address);
function getNameFee() external view returns(uint256);
function registerNameXIDFromDapp(address _addr, bytes32 _name, uint256 _affCode, bool _all) external payable returns(bool, uint256);
function registerNameXaddrFromDapp(address _addr, bytes32 _name, address _affCode, bool _all) external payable returns(bool, uint256);
function registerNameXnameFromDapp(address _addr, bytes32 _name, bytes32 _affCode, bool _all) external payable returns(bool, uint256);
}
// File: contracts/interface/TeamPerfitForwarderInterface.sol
interface TeamPerfitForwarderInterface {
function deposit() external payable returns(bool);
function status() external view returns(address, address);
}
// File: contracts/interface/DRSCoinInterface.sol
interface DRSCoinInterface {
function mint(address _to, uint256 _amount) external;
function profitEth() external payable;
}
// File: contracts/library/SafeMath.sol
/**
* @title SafeMath v0.1.9
* @dev Math operations with safety checks that throw on error
* change notes: original SafeMath library from OpenZeppelin modified by Inventor
* - added sqrt
* - added sq
* - added pwr
* - changed asserts to requires with error log outputs
* - removed div, its useless
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b)
internal
pure
returns (uint256 c)
{
if (a == 0) {
return 0;
}
c = a * b;
require(c / a == b, "SafeMath mul failed");
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b > 0);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b)
internal
pure
returns (uint256)
{
require(b <= a, "SafeMath sub failed");
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b)
internal
pure
returns (uint256 c)
{
c = a + b;
require(c >= a, "SafeMath add failed");
return c;
}
/**
* @dev gives square root of given x.
*/
function sqrt(uint256 x)
internal
pure
returns (uint256 y)
{
uint256 z = ((add(x,1)) / 2);
y = x;
while (z < y)
{
y = z;
z = ((add((x / z),z)) / 2);
}
}
/**
* @dev gives square. multiplies x by x
*/
function sq(uint256 x)
internal
pure
returns (uint256)
{
return (mul(x,x));
}
/**
* @dev x to the power of y
*/
function pwr(uint256 x, uint256 y)
internal
pure
returns (uint256)
{
if (x==0)
return (0);
else if (y==0)
return (1);
else
{
uint256 z = x;
for (uint256 i=1; i < y; i++)
z = mul(z,x);
return (z);
}
}
}
// File: contracts/library/NameFilter.sol
library NameFilter {
/**
* @dev filters name strings
* -converts uppercase to lower case.
* -makes sure it does not start/end with a space
* -makes sure it does not contain multiple spaces in a row
* -cannot be only numbers
* -cannot start with 0x
* -restricts characters to A-Z, a-z, 0-9, and space.
* @return reprocessed string in bytes32 format
*/
function nameFilter(string _input)
internal
pure
returns(bytes32)
{
bytes memory _temp = bytes(_input);
uint256 _length = _temp.length;
//sorry limited to 32 characters
require (_length <= 32 && _length > 0, "string must be between 1 and 32 characters");
// make sure it doesnt start with or end with space
require(_temp[0] != 0x20 && _temp[_length-1] != 0x20, "string cannot start or end with space");
// make sure first two characters are not 0x
if (_temp[0] == 0x30)
{
require(_temp[1] != 0x78, "string cannot start with 0x");
require(_temp[1] != 0x58, "string cannot start with 0X");
}
// create a bool to track if we have a non number character
bool _hasNonNumber;
// convert & check
for (uint256 i = 0; i < _length; i++)
{
// if its uppercase A-Z
if (_temp[i] > 0x40 && _temp[i] < 0x5b)
{
// convert to lower case a-z
_temp[i] = byte(uint(_temp[i]) + 32);
// we have a non number
if (_hasNonNumber == false)
_hasNonNumber = true;
} else {
require
(
// require character is a space
_temp[i] == 0x20 ||
// OR lowercase a-z
(_temp[i] > 0x60 && _temp[i] < 0x7b) ||
// or 0-9
(_temp[i] > 0x2f && _temp[i] < 0x3a),
"string contains invalid characters"
);
// make sure theres not 2x spaces in a row
if (_temp[i] == 0x20)
require( _temp[i+1] != 0x20, "string cannot contain consecutive spaces");
// see if we have a character other than a number
if (_hasNonNumber == false && (_temp[i] < 0x30 || _temp[i] > 0x39))
_hasNonNumber = true;
}
}
require(_hasNonNumber == true, "string cannot be only numbers");
bytes32 _ret;
assembly {
_ret := mload(add(_temp, 32))
}
return (_ret);
}
}
// File: contracts/library/DRSDatasets.sol
// structs ==============================================================================
library DRSDatasets {
//compressedData key
// [24-14][13-3][2][1][0]
// 0 - new player (bool)
// 1 - joined round (bool)
// 2 - new leader (bool)
// 3-13 - round end time
// 14 - 24 timestamp
//compressedIDs key
// [77-52][51-26][25-0]
// 0-25 - pID
// 26-51 - winPID
// 52-77 - rID
struct EventReturns {
uint256 compressedData;
uint256 compressedIDs;
address winnerAddr; // winner address
bytes32 winnerName; // winner name
uint256 amountWon; // amount won
uint256 newPot; // amount in new pot
uint256 genAmount; // amount distributed to gen
uint256 potAmount; // amount added to pot
address genAddr;
uint256 genKeyPrice;
}
function setNewPlayerFlag(EventReturns _event) internal pure returns(EventReturns) {
_event.compressedData = _event.compressedData + 1;
return _event;
}
function setJoinedRoundFlag(EventReturns _event) internal pure returns(EventReturns) {
_event.compressedData = _event.compressedData + 10;
return _event;
}
function setNewLeaderFlag(EventReturns _event) internal pure returns(EventReturns) {
_event.compressedData = _event.compressedData + 100;
return _event;
}
function setRoundEndTime(EventReturns _event, uint256 roundEndTime) internal pure returns(EventReturns) {
_event.compressedData = _event.compressedData + roundEndTime * (10**3);
return _event;
}
function setTimestamp(EventReturns _event, uint256 timestamp) internal pure returns(EventReturns) {
_event.compressedData = _event.compressedData + timestamp * (10**14);
return _event;
}
function setPID(EventReturns _event, uint256 _pID) internal pure returns(EventReturns) {
_event.compressedIDs = _event.compressedIDs + _pID;
return _event;
}
function setWinPID(EventReturns _event, uint256 _winPID) internal pure returns(EventReturns) {
_event.compressedIDs = _event.compressedIDs + (_winPID * (10**26));
return _event;
}
function setRID(EventReturns _event, uint256 _rID) internal pure returns(EventReturns) {
_event.compressedIDs = _event.compressedIDs + (_rID * (10**52));
return _event;
}
function setWinner(EventReturns _event, address _winnerAddr, bytes32 _winnerName, uint256 _amountWon)
internal pure returns(EventReturns) {
_event.winnerAddr = _winnerAddr;
_event.winnerName = _winnerName;
_event.amountWon = _amountWon;
return _event;
}
function setGenInfo(EventReturns _event, address _genAddr, uint256 _genKeyPrice)
internal pure returns(EventReturns) {
_event.genAddr = _genAddr;
_event.genKeyPrice = _genKeyPrice;
}
function setNewPot(EventReturns _event, uint256 _newPot) internal pure returns(EventReturns) {
_event.newPot = _newPot;
return _event;
}
function setGenAmount(EventReturns _event, uint256 _genAmount) internal pure returns(EventReturns) {
_event.genAmount = _genAmount;
return _event;
}
function setPotAmount(EventReturns _event, uint256 _potAmount) internal pure returns(EventReturns) {
_event.potAmount = _potAmount;
return _event;
}
struct Player {
address addr; // player address
bytes32 name; // player name
uint256 win; // winnings vault
uint256 gen; // general vault
// uint256 aff; // affiliate vault
uint256 lrnd; // last round played
// uint256 laff; // last affiliate id used
}
struct PlayerRound {
uint256 eth; // eth player has added to round (used for eth limiter)
uint256 keys; // keys
}
struct Round {
uint256 plyr; // pID of player in lead
uint256 end; // time ends/ended
bool ended; // has round end function been ran
uint256 strt; // time round started
uint256 keys; // keys
uint256 eth; // total eth in
uint256 pot; // eth to pot (during round) / final amount paid to winner (after round ends)
}
struct BuyInfo {
address addr; // player address
bytes32 name; // player name
uint256 pid; // player id
uint256 keyPrice;
uint256 keyIndex;
}
}
// File: contracts/DRSEvents.sol
contract DRSEvents {
// fired whenever a player registers a name
event onNewName
(
uint256 indexed playerID,
address indexed playerAddress,
bytes32 indexed playerName,
bool isNewPlayer,
// uint256 affiliateID,
// address affiliateAddress,
// bytes32 affiliateName,
uint256 amountPaid,
uint256 timeStamp
);
// fired at end of buy or reload
event onEndTx
(
uint256 compressedData,
uint256 compressedIDs,
bytes32 playerName,
address playerAddress,
uint256 ethIn,
uint256 keyIndex,
address winnerAddr,
bytes32 winnerName,
uint256 amountWon,
uint256 newPot,
uint256 genAmount,
uint256 potAmount,
address genAddr,
uint256 genKeyPrice
);
// fired whenever theres a withdraw
event onWithdraw
(
uint256 indexed playerID,
address playerAddress,
bytes32 playerName,
uint256 ethOut,
uint256 timeStamp
);
// fired whenever a withdraw forces end round to be ran
event onWithdrawAndDistribute
(
address playerAddress,
bytes32 playerName,
uint256 ethOut,
uint256 compressedData,
uint256 compressedIDs,
address winnerAddr,
bytes32 winnerName,
uint256 amountWon,
uint256 newPot,
uint256 genAmount
);
// fired whenever a player tries a buy after round timer
// hit zero, and causes end round to be ran.
event onBuyAndDistribute
(
address playerAddress,
bytes32 playerName,
uint256 ethIn,
uint256 compressedData,
uint256 compressedIDs,
address winnerAddr,
bytes32 winnerName,
uint256 amountWon,
uint256 newPot,
uint256 genAmount
);
// fired whenever a player tries a reload after round timer
// hit zero, and causes end round to be ran.
event onReLoadAndDistribute
(
address playerAddress,
bytes32 playerName,
uint256 compressedData,
uint256 compressedIDs,
address winnerAddr,
bytes32 winnerName,
uint256 amountWon,
uint256 newPot,
uint256 genAmount
);
event onBuyKeyFailure
(
uint256 roundID,
uint256 indexed playerID,
uint256 amount,
uint256 keyPrice,
uint256 timeStamp
);
}
// File: contracts/ReserveBag.sol
contract ReserveBag is DRSEvents {
using SafeMath for uint256;
using NameFilter for string;
using DRSDatasets for DRSDatasets.EventReturns;
TeamPerfitForwarderInterface public teamPerfit;
PlayerBookInterface public playerBook;
DRSCoinInterface public drsCoin;
// game settings
string constant public name = "Reserve Bag";
string constant public symbol = "RB";
uint256 constant private initKeyPrice = (10**18);
uint256 private rndExtra_ = 0; // length of the very first ICO
uint256 private rndGap_ = 0; // length of ICO phase, set to 1 year for EOS.
uint256 constant private rndMax_ = 24 hours; // max length a round timer can be
// uint256 constant private rndMax_ = 5 seconds; // max length a round timer can be
uint256 public rID_; // round id number / total rounds that have happened
uint256 public keyPrice = initKeyPrice;
uint256 public keyBought = 0;
address public owner;
uint256 public teamPerfitAmuont = 0;
uint256 public rewardInternal = 36;
// uint256 public potRatio = 8;
uint256 public keyPriceIncreaseRatio = 8;
uint256 public genRatio = 90;
uint256 public drsCoinDividendRatio = 40;
uint256 public teamPerfitRatio = 5;
uint256 public ethMintDRSCoinRate = 100;
bool public activated_ = false;
// PLAYER DATA
mapping(address => uint256) public pIDxAddr_; // (addr => pID) returns player id by address
mapping(bytes32 => uint256) public pIDxName_; // (name => pID) returns player id by name
mapping(uint256 => DRSDatasets.Player) public plyr_; // (pID => data) player data
mapping(uint256 => mapping(uint256 => DRSDatasets.PlayerRound)) public plyrRnds_; // (pID => rID => data) player round data by player id & round id
mapping(uint256 => mapping(bytes32 => bool)) public plyrNames_; // (pID => name => bool) list of names a player owns. (used so you can change your display name amongst any name you own)
DRSDatasets.BuyInfo[] buyinfos;
uint256 private startIndex;
uint256 private endIndex;
// ROUND DATA
mapping(uint256 => DRSDatasets.Round) public round_; // (rID => data) round data
// event Info(uint256 _value);
constructor(address _teamPerfit, address _playBook, address _drsCoin) public
{
owner = msg.sender;
teamPerfit = TeamPerfitForwarderInterface(_teamPerfit);
playerBook = PlayerBookInterface(_playBook);
drsCoin = DRSCoinInterface(_drsCoin);
startIndex = 0;
endIndex = 0;
}
modifier onlyOwner {
assert(owner == msg.sender);
_;
}
/**
* @dev prevents contracts from interacting with ReserveBag
*/
modifier isHuman() {
address _addr = msg.sender;
require(_addr == tx.origin);
uint256 _codeLength;
assembly {_codeLength := extcodesize(_addr)}
require(_codeLength == 0, "sorry humans only");
_;
}
/**
* @dev sets boundaries for incoming tx
*/
modifier isWithinLimits(uint256 _eth) {
require(_eth >= 1000000000, "pocket lint: not a valid currency");
require(_eth <= 100000 * (10**18), "no vitalik, no");
_;
}
function pushBuyInfo(DRSDatasets.BuyInfo info) internal {
if(endIndex == buyinfos.length) {
buyinfos.push(info);
} else if(endIndex < buyinfos.length) {
buyinfos[endIndex] = info;
} else {
// cannot happen
revert();
}
endIndex = (endIndex + 1) % (rewardInternal + 1);
if(endIndex == startIndex) {
startIndex = (startIndex + 1) % (rewardInternal + 1);
}
}
/**
* @dev emergency buy uses last stored affiliate ID and team snek
*/
function()
isActivated()
isHuman()
isWithinLimits(msg.value)
public
payable
{
// set up our tx event data and determine if player is new or not
DRSDatasets.EventReturns memory _eventData_;
_eventData_ = determinePID(_eventData_);
// fetch player id
uint256 _pID = pIDxAddr_[msg.sender];
// buy core
buyCore(_pID, _eventData_);
}
function buyKey()
isActivated()
isHuman()
isWithinLimits(msg.value)
public
payable
{
// set up our tx event data and determine if player is new or not
DRSDatasets.EventReturns memory _eventData_;
_eventData_ = determinePID(_eventData_);
// fetch player id
uint256 _pID = pIDxAddr_[msg.sender];
// buy core
buyCore(_pID, _eventData_);
}
function reLoadXaddr(uint256 _eth)
isActivated()
isHuman()
isWithinLimits(_eth)
public
{
// fetch player ID
uint256 _pID = pIDxAddr_[msg.sender];
require(_pID != 0, "reLoadXaddr can not be called by new players");
// set up our tx event data
DRSDatasets.EventReturns memory _eventData_;
// reload core
reLoadCore(_pID, _eth, _eventData_);
}
function withdrawTeamPerfit()
isActivated()
onlyOwner()
public
{
if(teamPerfitAmuont > 0) {
uint256 _perfit = teamPerfitAmuont;
teamPerfitAmuont = 0;
owner.transfer(_perfit);
}
}
function getTeamPerfitAmuont() public view returns(uint256) {
return teamPerfitAmuont;
}
/**
* @dev withdraws all of your earnings.
* -functionhash- 0x3ccfd60b
*/
function withdraw()
isActivated()
isHuman()
public
{
// fetch player ID
uint256 _pID = pIDxAddr_[msg.sender];
require(_pID != 0, "withdraw can not be called by new players");
// setup local rID
uint256 _rID = rID_;
// grab time
uint256 _now = now;
// setup temp var for player eth
uint256 _eth;
// check to see if round has ended and no one has run round end yet
if(_now > round_[_rID].end && !round_[_rID].ended && round_[_rID].plyr != 0)
{
// set up our tx event data
DRSDatasets.EventReturns memory _eventData_;
// end the round (distributes pot)
round_[_rID].ended = true;
_eventData_ = endRound(_eventData_);
// get their earnings
_eth = withdrawEarnings(_pID);
// withdraw eth
if(_eth > 0) {
plyr_[_pID].addr.transfer(_eth);
}
// build event data
_eventData_ = _eventData_.setTimestamp(_now);
_eventData_ = _eventData_.setPID(_pID);
// fire withdraw and distribute event
emit DRSEvents.onWithdrawAndDistribute
(
msg.sender,
plyr_[_pID].name,
_eth,
_eventData_.compressedData,
_eventData_.compressedIDs,
_eventData_.winnerAddr,
_eventData_.winnerName,
_eventData_.amountWon,
_eventData_.newPot,
_eventData_.genAmount
);
} else {
// get their earnings
_eth = withdrawEarnings(_pID);
// withdraw eth
if(_eth > 0) {
plyr_[_pID].addr.transfer(_eth);
}
// fire withdraw event
emit DRSEvents.onWithdraw(_pID, msg.sender, plyr_[_pID].name, _eth, _now);
}
}
function registerName(string _nameString, bool _all)
isHuman()
public
payable
{
bytes32 _name = _nameString.nameFilter();
address _addr = msg.sender;
uint256 _paid = msg.value;
(bool _isNewPlayer, ) = playerBook.registerNameXaddrFromDapp.value(msg.value)(msg.sender, _name, address(0), _all);
uint256 _pID = pIDxAddr_[_addr];
emit DRSEvents.onNewName(_pID, _addr, _name, _isNewPlayer, _paid, now);
}
/**
* @dev return the price buyer will pay for next 1 individual key.
* -functionhash- 0x018a25e8
* @return price for next key bought (in wei format)
*/
function getBuyPrice() public view returns(uint256)
{
return keyPrice;
}
/**
* @dev returns time left. dont spam this, you'll ddos yourself from your node provider
* -functionhash- 0xc7e284b8
* @return time left in seconds
*/
function getTimeLeft() public view returns(uint256)
{
uint256 _rID = rID_;
uint256 _now = now;
if(_now < round_[_rID].end)
if(_now > round_[_rID].strt + rndGap_)
return (round_[_rID].end).sub(_now);
else
return (round_[_rID].strt + rndGap_).sub(_now);
else
return 0;
}
/**
* @dev returns player earnings per vaults
* -functionhash- 0x63066434
* @return winnings vault
* @return general vault
*/
function getPlayerVaults(uint256 _pID) public view returns(uint256, uint256)
{
uint256 _rID = rID_;
uint256 _now = now;
// if round has ended. but round end has not been run (so contract has not distributed winnings)
if(_now > round_[_rID].end && !round_[_rID].ended && round_[_rID].plyr != 0) {
// if player is winner
if(round_[_rID].plyr == _pID) {
return
(
(plyr_[_pID].win).add(getWin(round_[_rID].pot)),
plyr_[_pID].gen
);
}
}
return (plyr_[_pID].win, plyr_[_pID].gen);
}
/**
* @dev returns all current round info needed for front end
* -functionhash- 0x747dff42
* @return round id
* @return total keys for round
* @return time round ends
* @return time round started
* @return current pot
* @return key price
* @return current key
* @return current player ID in lead
* @return current player address in leads
* @return current player name in leads
*/
function getCurrentRoundInfo() public view
returns(uint256, uint256, uint256, uint256, uint256, uint256, uint256, address, bytes32)
{
uint256 _rID = rID_;
uint256 _winPID = round_[_rID].plyr;
return
(
_rID, //0
round_[_rID].end, //1
round_[_rID].strt, //2
round_[_rID].pot, //3
keyPrice, //4
keyBought.add(1), //5
_winPID, //6
plyr_[_winPID].addr, //7
plyr_[_winPID].name //8
);
}
/**
* @dev returns player info based on address. if no address is given, it will
* use msg.sender
* -functionhash- 0xee0b5d8b
* @param _addr address of the player you want to lookup
* @return player ID
* @return player name
* @return keys owned (current round)
* @return winnings vault
* @return general vault
* @return player round eth
*/
function getPlayerInfoByAddress(address _addr) public view
returns(uint256, bytes32, uint256, uint256, uint256, uint256)
{
// setup local rID
uint256 _rID = rID_;
if(_addr == address(0)) {
_addr == msg.sender;
}
uint256 _pID = pIDxAddr_[_addr];
if(_pID == 0) {
return (0, "", 0, 0, 0, 0);
}
return
(
_pID, //0
plyr_[_pID].name, //1
plyrRnds_[_pID][_rID].keys, //2
plyr_[_pID].win, //3
plyr_[_pID].gen, //4
plyrRnds_[_pID][_rID].eth //5
);
}
/**
* @dev logic runs whenever a buy order is executed. determines how to handle
* incoming eth depending on if we are in an active round or not
*/
function buyCore(uint256 _pID, DRSDatasets.EventReturns memory _eventData_) private
{
uint256 _rID = rID_;
// grab time
uint256 _now = now;
// if round is active
if(_now >= round_[_rID].strt.add(rndGap_) && (_now <= round_[_rID].end || round_[_rID].plyr == 0)) {
// call core
core(_rID, _pID, msg.value, _eventData_);
// if round is not active
} else {
// check to see if end round needs to be ran
if(_now > round_[_rID].end && !round_[_rID].ended) {
// end the round (distributes pot) & start new round
round_[_rID].ended = true;
_eventData_ = endRound(_eventData_);
// build event data
_eventData_ = _eventData_.setTimestamp(_now);
_eventData_ = _eventData_.setPID(_pID);
// fire buy and distribute event
emit DRSEvents.onBuyAndDistribute
(
msg.sender,
plyr_[_pID].name,
msg.value,
_eventData_.compressedData,
_eventData_.compressedIDs,
_eventData_.winnerAddr,
_eventData_.winnerName,
_eventData_.amountWon,
_eventData_.newPot,
_eventData_.genAmount
);
}
// put eth in players vault
plyr_[_pID].gen = plyr_[_pID].gen.add(msg.value);
}
}
/**
* @dev logic runs whenever a reload order is executed. determines how to handle
* incoming eth depending on if we are in an active round or not
*/
function reLoadCore(uint256 _pID, uint256 _eth, DRSDatasets.EventReturns memory _eventData_) private
{
uint256 _rID = rID_;
uint256 _now = now;
// if round is active
if(_now > round_[_rID].strt.add(rndGap_) && (_now <= round_[_rID].end || round_[_rID].plyr == 0)) {
// get earnings from all vaults and return unused to gen vault
// because we use a custom safemath library. this will throw if player
// tried to spend more eth than they have.
plyr_[_pID].gen = withdrawEarnings(_pID).sub(_eth);
// call core
core(_rID, _pID, _eth, _eventData_);
// if round is not active and end round needs to be ran
} else {
// check to see if end round needs to be ran
if(_now > round_[_rID].end && !round_[_rID].ended) {
// end the round (distributes pot) & start new round
round_[_rID].ended = true;
_eventData_ = endRound(_eventData_);
// build event data
_eventData_ = _eventData_.setTimestamp(_now);
_eventData_ = _eventData_.setPID(_pID);
// fire buy and distribute event
emit DRSEvents.onReLoadAndDistribute
(
msg.sender,
plyr_[_pID].name,
_eventData_.compressedData,
_eventData_.compressedIDs,
_eventData_.winnerAddr,
_eventData_.winnerName,
_eventData_.amountWon,
_eventData_.newPot,
_eventData_.genAmount
);
}
}
}
/**
* @dev this is the core logic for any buy/reload that happens while a round is live.
*/
function core(uint256 _rID, uint256 _pID, uint256 _eth, DRSDatasets.EventReturns memory _eventData_) private
{
if(_eth < keyPrice) {
plyr_[_pID].gen = plyr_[_pID].gen.add(_eth);
emit onBuyKeyFailure(_rID, _pID, _eth, keyPrice, now);
return;
}
// if player is new to round
if(plyrRnds_[_pID][_rID].keys == 0) {
_eventData_ = managePlayer(_pID, _eventData_);
}
// mint the new key
uint256 _keys = 1;
uint256 _ethUsed = keyPrice;
uint256 _ethLeft = _eth.sub(keyPrice);
updateTimer(_rID);
// set new leaders
if(round_[_rID].plyr != _pID) {
round_[_rID].plyr = _pID;
}
// set the new leader bool to true
_eventData_ = _eventData_.setNewLeaderFlag();
// update player
plyrRnds_[_pID][_rID].keys = _keys.add(plyrRnds_[_pID][_rID].keys);
plyrRnds_[_pID][_rID].eth = _ethUsed.add(plyrRnds_[_pID][_rID].eth);
// update round
round_[_rID].keys = _keys.add(round_[_rID].keys);
round_[_rID].eth = _ethUsed.add(round_[_rID].eth);
// distribute eth
uint256 _ethExt = distributeExternal(_ethUsed);
_eventData_ = distributeInternal(_rID, _ethUsed, _ethExt, _eventData_);
bytes32 _name = plyr_[_pID].name;
pushBuyInfo(DRSDatasets.BuyInfo(msg.sender, _name, _pID, keyPrice, keyBought));
// key index player bought
uint256 _keyIndex = keyBought;
keyBought = keyBought.add(1);
keyPrice = keyPrice.mul(1000 + keyPriceIncreaseRatio).div(1000);
if(_ethLeft > 0) {
plyr_[_pID].gen = _ethLeft.add(plyr_[_pID].gen);
}
// call end tx function to fire end tx event.
endTx(_pID, _ethUsed, _keyIndex, _eventData_);
}
/**
* @dev receives name/player info from names contract
*/
function receivePlayerInfo(uint256 _pID, address _addr, bytes32 _name) external
{
require(msg.sender == address(playerBook), "your not playerNames contract.");
if(pIDxAddr_[_addr] != _pID)
pIDxAddr_[_addr] = _pID;
if(pIDxName_[_name] != _pID)
pIDxName_[_name] = _pID;
if(plyr_[_pID].addr != _addr)
plyr_[_pID].addr = _addr;
if(plyr_[_pID].name != _name)
plyr_[_pID].name = _name;
if(!plyrNames_[_pID][_name])
plyrNames_[_pID][_name] = true;
}
/**
* @dev receives entire player name list
*/
function receivePlayerNameList(uint256 _pID, bytes32 _name) external
{
require(msg.sender == address(playerBook), "your not playerNames contract.");
if(!plyrNames_[_pID][_name])
plyrNames_[_pID][_name] = true;
}
/**
* @dev gets existing or registers new pID. use this when a player may be new
* @return pID
*/
function determinePID(DRSDatasets.EventReturns memory _eventData_) private returns(DRSDatasets.EventReturns)
{
uint256 _pID = pIDxAddr_[msg.sender];
// if player is new to this version of ReserveBag
if(_pID == 0)
{
// grab their player ID, name from player names contract
_pID = playerBook.getPlayerID(msg.sender);
bytes32 _name = playerBook.getPlayerName(_pID);
// set up player account
pIDxAddr_[msg.sender] = _pID;
plyr_[_pID].addr = msg.sender;
if(_name != "")
{
pIDxName_[_name] = _pID;
plyr_[_pID].name = _name;
plyrNames_[_pID][_name] = true;
}
// set the new player bool to true
_eventData_ = _eventData_.setNewPlayerFlag();
}
return _eventData_;
}
function managePlayer(uint256 _pID, DRSDatasets.EventReturns memory _eventData_)
private
returns(DRSDatasets.EventReturns)
{
// update player's last round played
plyr_[_pID].lrnd = rID_;
// set the joined round bool to true
_eventData_ = _eventData_.setJoinedRoundFlag();
return _eventData_;
}
function getWin(uint256 _pot) private pure returns(uint256) {
return _pot / 2;
}
function getDRSCoinDividend(uint256 _pot) private view returns(uint256) {
return _pot.mul(drsCoinDividendRatio).div(100);
}
function getTeamPerfit(uint256 _pot) private view returns(uint256) {
return _pot.mul(teamPerfitRatio).div(100);
}
function mintDRSCoin() private {
// empty buyinfos
if(startIndex == endIndex) {
return;
}
// have one element
if((startIndex + 1) % (rewardInternal + 1) == endIndex) {
return;
}
// have more than one element
for(uint256 i = startIndex; (i + 1) % (rewardInternal + 1) != endIndex; i = (i + 1) % (rewardInternal + 1)) {
drsCoin.mint(buyinfos[i].addr, buyinfos[i].keyPrice.mul(ethMintDRSCoinRate).div(100));
}
}
/**
* @dev ends the round. manages paying out winner/splitting up pot
*/
function endRound(DRSDatasets.EventReturns memory _eventData_)
private
returns(DRSDatasets.EventReturns)
{
uint256 _rID = rID_;
uint256 _winPID = round_[_rID].plyr;
uint256 _pot = round_[_rID].pot;
// eth for last player's prize
uint256 _win = getWin(_pot);
// eth for drsCoin dividend
uint256 _drsCoinDividend = getDRSCoinDividend(_pot);
// eth for team perfit
uint256 _com = getTeamPerfit(_pot);
// eth put to next round's pot
uint256 _newPot = _pot.sub(_win).sub(_drsCoinDividend).sub(_com);
// deposit team perfit
depositTeamPerfit(_com);
// pay our winner
plyr_[_winPID].win = _win.add(plyr_[_winPID].win);
// mint DRSCoin
mintDRSCoin();
// distribute eth to drsCoin holders
drsCoin.profitEth.value(_drsCoinDividend)();
// prepare event data
_eventData_ = _eventData_.setRoundEndTime(round_[_rID].end);
_eventData_ = _eventData_.setWinPID(_winPID);
_eventData_ = _eventData_.setWinner(plyr_[_winPID].addr, plyr_[_winPID].name, _win);
_eventData_ = _eventData_.setNewPot(_newPot);
// start next round
rID_++;
_rID++;
round_[_rID].strt = now;
round_[_rID].end = now.add(rndMax_).add(rndGap_);
keyPrice = initKeyPrice;
keyBought = 0;
startIndex = 0;
endIndex = 0;
// add rest eth to next round's pot
round_[_rID].pot = _newPot;
return _eventData_;
}
/**
* @dev updates round timer based on number of whole keys bought.
*/
function updateTimer(uint256 _rID) private
{
uint256 keys = round_[_rID].keys;
uint256 keyDuration = rndMax_;
if(keys < 50) {
keyDuration = rndMax_;
} else if(keys < 100) {
keyDuration = rndMax_ / 2;
} else if(keys < 150) {
keyDuration = rndMax_ / 4;
} else {
keyDuration = rndMax_ / 8;
}
round_[_rID].end = keyDuration.add(now);
}
function depositTeamPerfit(uint256 _eth) private {
if(teamPerfit == address(0)) {
teamPerfitAmuont = teamPerfitAmuont.add(_eth);
return;
}
bool res = teamPerfit.deposit.value(_eth)();
if(!res) {
teamPerfitAmuont = teamPerfitAmuont.add(_eth);
return;
}
}
/**
* @dev distributes eth based on fees to team
*/
function distributeExternal(uint256 _eth) private returns(uint256)
{
// pay 2% out to community rewards
uint256 _com = _eth / 50;
depositTeamPerfit(_com);
return _com;
}
/**
* @dev distributes eth based on fees to gen and pot
*/
function distributeInternal(uint256 _rID, uint256 _eth, uint256 _ethExt, DRSDatasets.EventReturns memory _eventData_)
private
returns(DRSDatasets.EventReturns)
{
uint256 _gen = 0;
uint256 _pot = 0;
if(keyBought < rewardInternal) {
_gen = 0;
_pot = _eth.sub(_ethExt);
} else {
_gen = _eth.mul(genRatio).div(100);
_pot = _eth.sub(_ethExt).sub(_gen);
DRSDatasets.BuyInfo memory info = buyinfos[startIndex];
uint256 firstPID = info.pid;
plyr_[firstPID].gen = _gen.add(plyr_[firstPID].gen);
_eventData_.setGenInfo(info.addr, info.keyPrice);
}
if(_pot > 0) {
round_[_rID].pot = _pot.add(round_[_rID].pot);
}
_eventData_.setGenAmount(_gen.add(_eventData_.genAmount));
_eventData_.setPotAmount(_pot);
return _eventData_;
}
/**
* @dev adds up unmasked earnings, & vault earnings, sets them all to 0
* @return earnings in wei format
*/
function withdrawEarnings(uint256 _pID) private returns(uint256)
{
uint256 _earnings = (plyr_[_pID].win).add(plyr_[_pID].gen);
if(_earnings > 0)
{
plyr_[_pID].win = 0;
plyr_[_pID].gen = 0;
}
return _earnings;
}
/**
* @dev prepares compression data and fires event for buy or reload tx's
*/
function endTx(uint256 _pID, uint256 _eth, uint256 _keyIndex, DRSDatasets.EventReturns memory _eventData_) private
{
_eventData_ = _eventData_.setTimestamp(now);
_eventData_ = _eventData_.setPID(_pID);
_eventData_ = _eventData_.setRID(rID_);
emit DRSEvents.onEndTx
(
_eventData_.compressedData,
_eventData_.compressedIDs,
plyr_[_pID].name,
msg.sender,
_eth,
_keyIndex,
_eventData_.winnerAddr,
_eventData_.winnerName,
_eventData_.amountWon,
_eventData_.newPot,
_eventData_.genAmount,
_eventData_.potAmount,
_eventData_.genAddr,
_eventData_.genKeyPrice
);
}
modifier isActivated() {
require(activated_, "its not activated yet.");
_;
}
function activate() onlyOwner() public
{
// can only be ran once
require(!activated_, "ReserveBag already activated");
uint256 _now = now;
// activate the contract
activated_ = true;
// lets start first round
rID_ = 1;
round_[1].strt = _now.add(rndExtra_).sub(rndGap_);
round_[1].end = _now.add(rndMax_).add(rndExtra_);
}
function getActivated() public view returns(bool) {
return activated_;
}
function setTeamPerfitAddress(address _newTeamPerfitAddress) onlyOwner() public {
teamPerfit = TeamPerfitForwarderInterface(_newTeamPerfitAddress);
}
function setPlayerBookAddress(address _newPlayerBookAddress) onlyOwner() public {
playerBook = PlayerBookInterface(_newPlayerBookAddress);
}
function setDRSCoinAddress(address _newDRSCoinAddress) onlyOwner() public {
drsCoin = DRSCoinInterface(_newDRSCoinAddress);
}
}
{
"compilationTarget": {
"ReserveBag.sol": "ReserveBag"
},
"evmVersion": "byzantium",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":true,"inputs":[],"name":"getBuyPrice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"genRatio","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"activate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"keyPrice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"pIDxAddr_","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_pID","type":"uint256"},{"name":"_addr","type":"address"},{"name":"_name","type":"bytes32"}],"name":"receivePlayerInfo","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"round_","outputs":[{"name":"plyr","type":"uint256"},{"name":"end","type":"uint256"},{"name":"ended","type":"bool"},{"name":"strt","type":"uint256"},{"name":"keys","type":"uint256"},{"name":"eth","type":"uint256"},{"name":"pot","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"bytes32"}],"name":"plyrNames_","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"pIDxName_","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"playerBook","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"withdrawTeamPerfit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_eth","type":"uint256"}],"name":"reLoadXaddr","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"drsCoinDividendRatio","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"teamPerfit","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"rewardInternal","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nameString","type":"string"},{"name":"_all","type":"bool"}],"name":"registerName","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"rID_","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_pID","type":"uint256"}],"name":"getPlayerVaults","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"drsCoin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getCurrentRoundInfo","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"address"},{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getActivated","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_pID","type":"uint256"},{"name":"_name","type":"bytes32"}],"name":"receivePlayerNameList","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"name":"plyrRnds_","outputs":[{"name":"eth","type":"uint256"},{"name":"keys","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newDRSCoinAddress","type":"address"}],"name":"setDRSCoinAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"teamPerfitAmuont","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getTimeLeft","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newPlayerBookAddress","type":"address"}],"name":"setPlayerBookAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"ethMintDRSCoinRate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"activated_","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newTeamPerfitAddress","type":"address"}],"name":"setTeamPerfitAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"plyr_","outputs":[{"name":"addr","type":"address"},{"name":"name","type":"bytes32"},{"name":"win","type":"uint256"},{"name":"gen","type":"uint256"},{"name":"lrnd","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"keyBought","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getTeamPerfitAmuont","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"buyKey","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"keyPriceIncreaseRatio","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_addr","type":"address"}],"name":"getPlayerInfoByAddress","outputs":[{"name":"","type":"uint256"},{"name":"","type":"bytes32"},{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"teamPerfitRatio","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_teamPerfit","type":"address"},{"name":"_playBook","type":"address"},{"name":"_drsCoin","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"playerID","type":"uint256"},{"indexed":true,"name":"playerAddress","type":"address"},{"indexed":true,"name":"playerName","type":"bytes32"},{"indexed":false,"name":"isNewPlayer","type":"bool"},{"indexed":false,"name":"amountPaid","type":"uint256"},{"indexed":false,"name":"timeStamp","type":"uint256"}],"name":"onNewName","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"compressedData","type":"uint256"},{"indexed":false,"name":"compressedIDs","type":"uint256"},{"indexed":false,"name":"playerName","type":"bytes32"},{"indexed":false,"name":"playerAddress","type":"address"},{"indexed":false,"name":"ethIn","type":"uint256"},{"indexed":false,"name":"keyIndex","type":"uint256"},{"indexed":false,"name":"winnerAddr","type":"address"},{"indexed":false,"name":"winnerName","type":"bytes32"},{"indexed":false,"name":"amountWon","type":"uint256"},{"indexed":false,"name":"newPot","type":"uint256"},{"indexed":false,"name":"genAmount","type":"uint256"},{"indexed":false,"name":"potAmount","type":"uint256"},{"indexed":false,"name":"genAddr","type":"address"},{"indexed":false,"name":"genKeyPrice","type":"uint256"}],"name":"onEndTx","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"playerID","type":"uint256"},{"indexed":false,"name":"playerAddress","type":"address"},{"indexed":false,"name":"playerName","type":"bytes32"},{"indexed":false,"name":"ethOut","type":"uint256"},{"indexed":false,"name":"timeStamp","type":"uint256"}],"name":"onWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"playerAddress","type":"address"},{"indexed":false,"name":"playerName","type":"bytes32"},{"indexed":false,"name":"ethOut","type":"uint256"},{"indexed":false,"name":"compressedData","type":"uint256"},{"indexed":false,"name":"compressedIDs","type":"uint256"},{"indexed":false,"name":"winnerAddr","type":"address"},{"indexed":false,"name":"winnerName","type":"bytes32"},{"indexed":false,"name":"amountWon","type":"uint256"},{"indexed":false,"name":"newPot","type":"uint256"},{"indexed":false,"name":"genAmount","type":"uint256"}],"name":"onWithdrawAndDistribute","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"playerAddress","type":"address"},{"indexed":false,"name":"playerName","type":"bytes32"},{"indexed":false,"name":"ethIn","type":"uint256"},{"indexed":false,"name":"compressedData","type":"uint256"},{"indexed":false,"name":"compressedIDs","type":"uint256"},{"indexed":false,"name":"winnerAddr","type":"address"},{"indexed":false,"name":"winnerName","type":"bytes32"},{"indexed":false,"name":"amountWon","type":"uint256"},{"indexed":false,"name":"newPot","type":"uint256"},{"indexed":false,"name":"genAmount","type":"uint256"}],"name":"onBuyAndDistribute","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"playerAddress","type":"address"},{"indexed":false,"name":"playerName","type":"bytes32"},{"indexed":false,"name":"compressedData","type":"uint256"},{"indexed":false,"name":"compressedIDs","type":"uint256"},{"indexed":false,"name":"winnerAddr","type":"address"},{"indexed":false,"name":"winnerName","type":"bytes32"},{"indexed":false,"name":"amountWon","type":"uint256"},{"indexed":false,"name":"newPot","type":"uint256"},{"indexed":false,"name":"genAmount","type":"uint256"}],"name":"onReLoadAndDistribute","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"roundID","type":"uint256"},{"indexed":true,"name":"playerID","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"keyPrice","type":"uint256"},{"indexed":false,"name":"timeStamp","type":"uint256"}],"name":"onBuyKeyFailure","type":"event"}]