/** * Source Code first verified at https://etherscan.io on Thursday, March 21, 2019 (UTC) */ pragma solidity 0.4.25; contract _0xbccInterface { function buyAndSetDivPercentage(uint _0xbtcAmount, address _referredBy, uint8 _divChoice, string providedUnhashedPass) public returns(uint); function balanceOf(address who) public view returns(uint); function transfer(address _to, uint _value) public returns(bool); function transferFrom(address _from, address _toAddress, uint _amountOfTokens) public returns(bool); function exit() public; function sell(uint amountOfTokens) public; function withdraw(address _recipient) public; } contract ERC20Interface { function totalSupply() public constant returns(uint); function balanceOf(address tokenOwner) public constant returns(uint balance); function allowance(address tokenOwner, address spender) public constant returns(uint remaining); function transfer(address to, uint tokens) public returns(bool success); function approve(address spender, uint tokens) public returns(bool success); function transferFrom(address from, address to, uint tokens) public returns(bool success); event Transfer(address indexed from, address indexed to, uint tokens); event Approval(address indexed tokenOwner, address indexed spender, uint tokens); } contract ERC223Receiving { function tokenFallback(address _from, uint _amountOfTokens, bytes _data) public returns(bool); } contract _0xbtcBankroll is ERC223Receiving { using SafeMath for uint; /*================================= = EVENTS = =================================*/ event Confirmation(address indexed sender, uint indexed transactionId); event Revocation(address indexed sender, uint indexed transactionId); event Submission(uint indexed transactionId); event Execution(uint indexed transactionId); event ExecutionFailure(uint indexed transactionId); event Deposit(address indexed sender, uint value); event OwnerAddition(address indexed owner); event OwnerRemoval(address indexed owner); event WhiteListAddition(address indexed contractAddress); event WhiteListRemoval(address indexed contractAddress); event RequirementChange(uint required); event DevWithdraw(uint amountTotal, uint amountPerPerson); event _0xBTCLogged(uint amountReceived, address sender); event BankrollInvest(uint amountReceived); event DailyTokenAdmin(address gameContract); event DailyTokensSent(address gameContract, uint tokens); event DailyTokensReceived(address gameContract, uint tokens); /*================================= = WITHDRAWAL CONSTANTS = =================================*/ uint constant public MAX_OWNER_COUNT = 10; uint constant public MAX_WITHDRAW_PCT_DAILY = 100; uint constant public MAX_WITHDRAW_PCT_TX = 100; uint constant internal resetTimer = 1 days; /*================================= = 0xBTC INTERFACE = =================================*/ ERC20Interface internal _0xBTC; /*================================= = 0xbcc INTERFACE = =================================*/ address internal _0xbccAddress; _0xbccInterface public _0xbcc; /*================================= = VARIABLES = =================================*/ mapping(uint => Transaction) public transactions; mapping(uint => mapping(address => bool)) public confirmations; mapping(address => bool) public isOwner; mapping(address => bool) public isWhitelisted; mapping(address => uint) public dailyTokensPerContract; address internal divCardAddress; address[] public owners; address[] public whiteListedContracts; uint public required; uint public transactionCount; uint internal dailyResetTime; uint internal dailyTknLimit; uint internal tknsDispensedToday; bool internal reEntered = false; /*================================= = CUSTOM CONSTRUCTS = =================================*/ struct Transaction { address destination; uint value; bytes data; bool executed; } struct TKN { address sender; uint value; } /*================================= = MODIFIERS = =================================*/ modifier onlyWallet() { if (msg.sender != address(this)) revert(); _; } modifier contractIsNotWhiteListed(address contractAddress) { if (isWhitelisted[contractAddress]) revert(); _; } modifier contractIsWhiteListed(address contractAddress) { if (!isWhitelisted[contractAddress]) revert(); _; } modifier isAnOwner() { address caller = msg.sender; if (!isOwner[caller]) revert(); _; } modifier ownerDoesNotExist(address owner) { if (isOwner[owner]) revert(); _; } modifier ownerExists(address owner) { if (!isOwner[owner]) revert(); _; } modifier transactionExists(uint transactionId) { if (transactions[transactionId].destination == 0) revert(); _; } modifier confirmed(uint transactionId, address owner) { if (!confirmations[transactionId][owner]) revert(); _; } modifier notConfirmed(uint transactionId, address owner) { if (confirmations[transactionId][owner]) revert(); _; } modifier notExecuted(uint transactionId) { if (transactions[transactionId].executed) revert(); _; } modifier notNull(address _address) { if (_address == 0) revert(); _; } modifier validRequirement(uint ownerCount, uint _required) { if (ownerCount > MAX_OWNER_COUNT || _required > ownerCount || _required == 0 || ownerCount == 0) revert(); _; } /*================================= = PUBLIC FUNCTIONS = =================================*/ /// @dev Contract constructor sets initial owners and required number of confirmations. /// @param _owners List of initial owners. /// @param _required Number of required confirmations. constructor(address[] _owners, uint _required, address _btcAddress) public validRequirement(_owners.length, _required) { for (uint i = 0; i < _owners.length; i++) { if (isOwner[_owners[i]] || _owners[i] == 0) revert(); isOwner[_owners[i]] = true; } owners = _owners; required = _required; _0xBTC = ERC20Interface(_btcAddress); dailyResetTime = now - (1 days); } function add0xbccAddresses(address _0xbtc, address _divcards) public isAnOwner { _0xbccAddress = _0xbtc; divCardAddress = _divcards; _0xbcc = _0xbccInterface(_0xbccAddress); } /// @dev Fallback function accept eth. function () payable public { } uint NonICOBuyins; function deposit(uint value) public { _0xBTC.transferFrom(msg.sender, address(this), value); NonICOBuyins = NonICOBuyins.add(value); } /// @dev Function to buy tokens with contract _0xbtc balance. function buyTokens() public isAnOwner { uint savings = _0xBTC.balanceOf(address(this)); if (savings.mul(1e10) > 0.01 ether) { //ether used as 18 decimals factor _0xBTC.approve(_0xbcc, savings); _0xbcc.buyAndSetDivPercentage(savings, address(0x0), 30, ""); emit BankrollInvest(savings); } else { emit _0xBTCLogged(savings, msg.sender); } } function tokenFallback(address /*_from*/ , uint /*_amountOfTokens*/ , bytes /*_data*/ ) public returns(bool) { // Nothing, for now. Just receives tokens. } /// @dev Calculates if an amount of tokens exceeds the aggregate daily limit of 15% of contract /// balance or 5% of the contract balance on its own. function permissibleTokenWithdrawal(uint _toWithdraw) public returns(bool) { uint currentTime = now; uint tokenBalance = _0xbcc.balanceOf(address(this)); uint maxPerTx = (tokenBalance.mul(MAX_WITHDRAW_PCT_TX)).div(100); require(_toWithdraw <= maxPerTx); if (currentTime - dailyResetTime >= resetTimer) { dailyResetTime = currentTime; dailyTknLimit = (tokenBalance.mul(MAX_WITHDRAW_PCT_DAILY)).div(100); tknsDispensedToday = _toWithdraw; return true; } else { if (tknsDispensedToday.add(_toWithdraw) <= dailyTknLimit) { tknsDispensedToday += _toWithdraw; return true; } else { return false; } } } /// @dev Allows us to set the daily Token Limit function setDailyTokenLimit(uint limit) public isAnOwner { dailyTknLimit = limit; } /// @dev Allows to add a new owner. Transaction has to be sent by wallet. /// @param owner Address of new owner. function addOwner(address owner) public onlyWallet ownerDoesNotExist(owner) notNull(owner) validRequirement(owners.length + 1, required) { isOwner[owner] = true; owners.push(owner); emit OwnerAddition(owner); } /// @dev Allows to remove an owner. Transaction has to be sent by wallet. /// @param owner Address of owner. function removeOwner(address owner) public onlyWallet ownerExists(owner) validRequirement(owners.length, required) { isOwner[owner] = false; for (uint i = 0; i < owners.length - 1; i++) if (owners[i] == owner) { owners[i] = owners[owners.length - 1]; break; } owners.length -= 1; if (required > owners.length) changeRequirement(owners.length); emit OwnerRemoval(owner); } /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet. /// @param owner Address of owner to be replaced. /// @param owner Address of new owner. function replaceOwner(address owner, address newOwner) public onlyWallet ownerExists(owner) ownerDoesNotExist(newOwner) { for (uint i = 0; i < owners.length; i++) if (owners[i] == owner) { owners[i] = newOwner; break; } isOwner[owner] = false; isOwner[newOwner] = true; emit OwnerRemoval(owner); emit OwnerAddition(newOwner); } /// @dev Allows to change the number of required confirmations. Transaction has to be sent by wallet. /// @param _required Number of required confirmations. function changeRequirement(uint _required) public onlyWallet validRequirement(owners.length, _required) { required = _required; emit RequirementChange(_required); } /// @dev Allows an owner to submit and confirm a transaction. /// @param destination Transaction target address. /// @param value Transaction ether value. /// @param data Transaction data payload. /// @return Returns transaction ID. function submitTransaction(address destination, uint value, bytes data) public returns(uint transactionId) { transactionId = addTransaction(destination, value, data); confirmTransaction(transactionId); } /// @dev Allows an owner to confirm a transaction. /// @param transactionId Transaction ID. function confirmTransaction(uint transactionId) public ownerExists(msg.sender) transactionExists(transactionId) notConfirmed(transactionId, msg.sender) { confirmations[transactionId][msg.sender] = true; emit Confirmation(msg.sender, transactionId); executeTransaction(transactionId); } /// @dev Allows an owner to revoke a confirmation for a transaction. /// @param transactionId Transaction ID. function revokeConfirmation(uint transactionId) public ownerExists(msg.sender) confirmed(transactionId, msg.sender) notExecuted(transactionId) { confirmations[transactionId][msg.sender] = false; emit Revocation(msg.sender, transactionId); } /// @dev Allows anyone to execute a confirmed transaction. /// @param transactionId Transaction ID. function executeTransaction(uint transactionId) public notExecuted(transactionId) { if (isConfirmed(transactionId)) { Transaction storage txToExecute = transactions[transactionId]; txToExecute.executed = true; if (txToExecute.destination.call.value(txToExecute.value)(txToExecute.data)) emit Execution(transactionId); else { emit ExecutionFailure(transactionId); txToExecute.executed = false; } } } /// @dev Returns the confirmation status of a transaction. /// @param transactionId Transaction ID. /// @return Confirmation status. function isConfirmed(uint transactionId) public constant returns(bool) { uint count = 0; for (uint i = 0; i < owners.length; i++) { if (confirmations[transactionId][owners[i]]) count += 1; if (count == required) return true; } } /*================================= = OPERATOR FUNCTIONS = =================================*/ /// @dev Adds a new transaction to the transaction mapping, if transaction does not exist yet. /// @param destination Transaction target address. /// @param value Transaction ether value. /// @param data Transaction data payload. /// @return Returns transaction ID. function addTransaction(address destination, uint value, bytes data) internal notNull(destination) returns(uint transactionId) { transactionId = transactionCount; transactions[transactionId] = Transaction({ destination: destination, value: value, data: data, executed: false }); transactionCount += 1; emit Submission(transactionId); } /* * Web3 call functions */ /// @dev Returns number of confirmations of a transaction. /// @param transactionId Transaction ID. /// @return Number of confirmations. function getConfirmationCount(uint transactionId) public constant returns(uint count) { for (uint i = 0; i < owners.length; i++) if (confirmations[transactionId][owners[i]]) count += 1; } /// @dev Returns total number of transactions after filers are applied. /// @param pending Include pending transactions. /// @param executed Include executed transactions. /// @return Total number of transactions after filters are applied. function getTransactionCount(bool pending, bool executed) public constant returns(uint count) { for (uint i = 0; i < transactionCount; i++) if (pending && !transactions[i].executed || executed && transactions[i].executed) count += 1; } /// @dev Returns list of owners. /// @return List of owner addresses. function getOwners() public constant returns(address[]) { return owners; } /// @dev Returns array with owner addresses, which confirmed transaction. /// @param transactionId Transaction ID. /// @return Returns array of owner addresses. function getConfirmations(uint transactionId) public constant returns(address[] _confirmations) { address[] memory confirmationsTemp = new address[](owners.length); uint count = 0; uint i; for (i = 0; i < owners.length; i++) if (confirmations[transactionId][owners[i]]) { confirmationsTemp[count] = owners[i]; count += 1; } _confirmations = new address[](count); for (i = 0; i < count; i++) _confirmations[i] = confirmationsTemp[i]; } /// @dev Returns list of transaction IDs in defined range. /// @param from Index start position of transaction array. /// @param to Index end position of transaction array. /// @param pending Include pending transactions. /// @param executed Include executed transactions. /// @return Returns array of transaction IDs. function getTransactionIds(uint from, uint to, bool pending, bool executed) public constant returns(uint[] _transactionIds) { uint[] memory transactionIdsTemp = new uint[](transactionCount); uint count = 0; uint i; for (i = 0; i < transactionCount; i++) if (pending && !transactions[i].executed || executed && transactions[i].executed) { transactionIdsTemp[count] = i; count += 1; } _transactionIds = new uint[](to - from); for (i = from; i < to; i++) _transactionIds[i - from] = transactionIdsTemp[i]; } // Additions for Bankroll function whiteListContract(address contractAddress) public isAnOwner contractIsNotWhiteListed(contractAddress) notNull(contractAddress) { isWhitelisted[contractAddress] = true; whiteListedContracts.push(contractAddress); // We set the daily tokens for a particular contract in a separate call. dailyTokensPerContract[contractAddress] = 0; emit WhiteListAddition(contractAddress); } // Remove a whitelisted contract. This is an exception to the norm in that // it can be invoked directly by any owner, in the event that a game is found // to be bugged or otherwise faulty, so it can be shut down as an emergency measure. // Iterates through the whitelisted contracts to find contractAddress, // then swaps it with the last address in the list - then decrements length function deWhiteListContract(address contractAddress) public isAnOwner contractIsWhiteListed(contractAddress) { isWhitelisted[contractAddress] = false; for (uint i = 0; i < whiteListedContracts.length - 1; i++) if (whiteListedContracts[i] == contractAddress) { whiteListedContracts[i] = owners[whiteListedContracts.length - 1]; break; } whiteListedContracts.length -= 1; emit WhiteListRemoval(contractAddress); } function contractTokenWithdraw(uint amount, address target) public contractIsWhiteListed(msg.sender) { require(isWhitelisted[msg.sender]); require(_0xbcc.transfer(target, amount)); } // Alters the amount of tokens allocated to a game contract on a daily basis. function alterTokenGrant(address _contract, uint _newAmount) public isAnOwner contractIsWhiteListed(_contract) { dailyTokensPerContract[_contract] = _newAmount; } function queryTokenGrant(address _contract) public view returns(uint) { return dailyTokensPerContract[_contract]; } // Function to be run by an owner (ideally on a cron job) which performs daily // token collection and dispersal for all whitelisted contracts. function dailyAccounting() public isAnOwner { for (uint i = 0; i < whiteListedContracts.length; i++) { address _contract = whiteListedContracts[i]; if (dailyTokensPerContract[_contract] > 0) { allocateTokens(_contract); emit DailyTokenAdmin(_contract); } } } // In the event that we want to manually take tokens back from a whitelisted contract, // we can do so. function retrieveTokens(address _contract, uint _amount) public isAnOwner contractIsWhiteListed(_contract) { require(_0xbcc.transferFrom(_contract, address(this), _amount)); } // Dispenses daily amount of 0xbcc to whitelisted contract, or retrieves the excess. // Block withdraws greater than MAX_WITHDRAW_PCT_TX of 0xbtc token balance. // (May require occasional adjusting of the daily token allocation for contracts.) function allocateTokens(address _contract) public isAnOwner contractIsWhiteListed(_contract) { uint dailyAmount = dailyTokensPerContract[_contract]; uint bccPresent = _0xbcc.balanceOf(_contract); // Make sure that tokens aren't sent to a contract which is in the black. if (bccPresent <= dailyAmount) { // We need to send tokens over, make sure it's a permitted amount, and then send. uint toDispense = dailyAmount.sub(bccPresent); // Make sure amount is <= tokenbalance*MAX_WITHDRAW_PCT_TX require(permissibleTokenWithdrawal(toDispense)); require(_0xbcc.transfer(_contract, toDispense)); emit DailyTokensSent(_contract, toDispense); } else { // The contract in question has made a profit: retrieve the excess tokens. uint toRetrieve = bccPresent.sub(dailyAmount); require(_0xbcc.transferFrom(_contract, address(this), toRetrieve)); emit DailyTokensReceived(_contract, toRetrieve); } emit DailyTokenAdmin(_contract); } // Dev withdrawal of tokens - splits equally among all owners of contract function devTokenWithdraw(uint amount) public onlyWallet { require(permissibleTokenWithdrawal(amount)); uint amountPerPerson = SafeMath.div(amount, owners.length); for (uint i = 0; i < owners.length; i++) { _0xbcc.transfer(owners[i], amountPerPerson); } emit DevWithdraw(amount, amountPerPerson); } // Change the dividend card address. Can't see why this would ever need // to be invoked, but better safe than sorry. function changeDivCardAddress(address _newDivCardAddress) public isAnOwner { divCardAddress = _newDivCardAddress; } // Receive 0xbtc (from 0xbtc itself or any other source) and purchase tokens at the 30% dividend rate. // If the amount is less than 0.01 Ether, the Ether is stored by the contract until the balance // exceeds that limit and then purchases all it can. function receiveDividends(uint amount) public { _0xBTC.transferFrom(msg.sender, address(this), amount); if (!reEntered) { uint ActualBalance = (_0xBTC.balanceOf(address(this)).sub(NonICOBuyins)); if (ActualBalance.mul(1e10) > 0.01 ether) { reEntered = true; _0xBTC.approve(_0xbcc, ActualBalance); _0xbcc.buyAndSetDivPercentage(ActualBalance, address(0x0), 30, ""); emit BankrollInvest(ActualBalance); reEntered = false; } } } // Use all available balance to buy in function buyInWithAllBalance() public isAnOwner { if (!reEntered) { uint balance = _0xBTC.balanceOf(address(this)); require(balance.mul(1e10) > 0.01 ether); _0xBTC.approve(_0xbcc, balance); _0xbcc.buyAndSetDivPercentage(balance, address(0x0), 30, ""); } } /*================================= = UTILITIES = =================================*/ // Convert an hexadecimal character to their value function fromHexChar(uint c) public pure returns(uint) { if (byte(c) >= byte('0') && byte(c) <= byte('9')) { return c - uint(byte('0')); } if (byte(c) >= byte('a') && byte(c) <= byte('f')) { return 10 + c - uint(byte('a')); } if (byte(c) >= byte('A') && byte(c) <= byte('F')) { return 10 + c - uint(byte('A')); } } // Convert an hexadecimal string to raw bytes function fromHex(string s) public pure returns(bytes) { bytes memory ss = bytes(s); require(ss.length % 2 == 0); // length must be even bytes memory r = new bytes(ss.length / 2); for (uint i = 0; i < ss.length / 2; ++i) { r[i] = byte(fromHexChar(uint(ss[2 * i])) * 16 + fromHexChar(uint(ss[2 * i + 1]))); } return r; } } /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint a, uint b) internal pure returns(uint) { if (a == 0) { return 0; } uint c = a * b; assert(c / a == b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint a, uint b) internal pure returns(uint) { // assert(b > 0); // Solidity automatically throws when dividing by 0 uint 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(uint a, uint b) internal pure returns(uint) { assert(b <= a); return a - b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint a, uint b) internal pure returns(uint) { uint c = a + b; assert(c >= a); return c; } }