From b9962c0d96f3d55b9a7e6d50cf741464896ed4ad Mon Sep 17 00:00:00 2001 From: rajeevgopalakrishna Date: Fri, 24 May 2019 07:45:02 +0530 Subject: [PATCH] Adds five more real-world non-trivial contracts for testing. --- ...67c080e9d511a34d36152c0_MultiSigWallet.sol | 367 +++ ...64061dd23f8209f804a3b8ad2f2_FoMo3Dlong.sol | 2058 +++++++++++++++++ ...15a7d984b8c71c15e82b7_EnclavesDEXProxy.sol | 286 +++ ...d8f85f41cfb6de49b9db29_BancorConverter.sol | 1011 ++++++++ ...ea7f106ecbd1850e406adc41b51_OceanToken.sol | 454 ++++ 5 files changed, 4176 insertions(+) create mode 100644 utils/slither_format/tests/real_world/0x05cf67329a262818e67c080e9d511a34d36152c0_MultiSigWallet.sol create mode 100644 utils/slither_format/tests/real_world/0x5d0d76787d9d564061dd23f8209f804a3b8ad2f2_FoMo3Dlong.sol create mode 100644 utils/slither_format/tests/real_world/0xbf45f4280cfbe7c2d2515a7d984b8c71c15e82b7_EnclavesDEXProxy.sol create mode 100644 utils/slither_format/tests/real_world/0xc6725ae749677f21e4d8f85f41cfb6de49b9db29_BancorConverter.sol create mode 100644 utils/slither_format/tests/real_world/0xf5ed2dc77f0d1ea7f106ecbd1850e406adc41b51_OceanToken.sol diff --git a/utils/slither_format/tests/real_world/0x05cf67329a262818e67c080e9d511a34d36152c0_MultiSigWallet.sol b/utils/slither_format/tests/real_world/0x05cf67329a262818e67c080e9d511a34d36152c0_MultiSigWallet.sol new file mode 100644 index 000000000..53465564c --- /dev/null +++ b/utils/slither_format/tests/real_world/0x05cf67329a262818e67c080e9d511a34d36152c0_MultiSigWallet.sol @@ -0,0 +1,367 @@ +pragma solidity ^0.4.15; + +// From https://github.com/ConsenSys/MultiSigWallet/blob/master/contracts/solidity/MultiSigWallet.sol @ e3240481928e9d2b57517bd192394172e31da487 + +/// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution. +/// @author Stefan George - <[email protected]> +contract MultiSigWallet { + + uint constant public MAX_OWNER_COUNT = 5; + + 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 RequirementChange(uint required); + + mapping (uint => Transaction) public transactions; + mapping (uint => mapping (address => bool)) public confirmations; + mapping (address => bool) public isOwner; + address[] public owners; + uint public required; + uint public transactionCount; + + struct Transaction { + address destination; + uint value; + bytes data; + bool executed; + } + + modifier onlyWallet() { + if (msg.sender != address(this)) + throw; + _; + } + + modifier ownerDoesNotExist(address owner) { + if (isOwner[owner]) + throw; + _; + } + + modifier ownerExists(address owner) { + if (!isOwner[owner]) + throw; + _; + } + + modifier transactionExists(uint transactionId) { + if (transactions[transactionId].destination == 0) + throw; + _; + } + + modifier confirmed(uint transactionId, address owner) { + if (!confirmations[transactionId][owner]) + throw; + _; + } + + modifier notConfirmed(uint transactionId, address owner) { + if (confirmations[transactionId][owner]) + throw; + _; + } + + modifier notExecuted(uint transactionId) { + if (transactions[transactionId].executed) + throw; + _; + } + + modifier notNull(address _address) { + if (_address == 0) + throw; + _; + } + + modifier validRequirement(uint ownerCount, uint _required) { + if ( ownerCount > MAX_OWNER_COUNT + || _required > ownerCount + || _required == 0 + || ownerCount == 0) + throw; + _; + } + + /// @dev Fallback function allows to deposit ether. + function() + payable + { + if (msg.value > 0) + Deposit(msg.sender, msg.value); + } + + /* + * Public functions + */ + /// @dev Contract constructor sets initial owners and required number of confirmations. + function MultiSigWallet() + public + { + address owner1 = address(0x5117afB03e83d180D0059a1Ad733F954220D2734); + address owner2 = address(0x4F9049886d8087c7549224383075ffbb3dF2b7a0); + address owner3 = address(0x4E63227fcFF602b3Fa9e6F4e86b33194f04236B1); + owners.push(address(owner1)); + owners.push(address(owner2)); + owners.push(address(owner3)); + isOwner[owner1] = true; + isOwner[owner2] = true; + isOwner[owner3] = true; + required = 3; + } + + /// @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); + 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) + { + isOwner[owner] = false; + for (uint i=0; i owners.length) + changeRequirement(owners.length); + 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 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; + } + + /** + * @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\UintCompressor.sol + +/** +* @title -UintCompressor- v0.1.9 +* ┌┬┐┌─┐┌─┐┌┬┐ ╦╦ ╦╔═╗╔╦╗ ┌─┐┬─┐┌─┐┌─┐┌─┐┌┐┌┌┬┐┌─┐ +* │ ├┤ ├─┤│││ ║║ ║╚═╗ ║ ├─┘├┬┘├┤ └─┐├┤ │││ │ └─┐ +* ┴ └─┘┴ ┴┴ ┴ ╚╝╚═╝╚═╝ ╩ ┴ ┴└─└─┘└─┘└─┘┘└┘ ┴ └─┘ +* _____ _____ +* (, / /) /) /) (, / /) /) +* ┌─┐ / _ (/_ // // / _ // _ __ _(/ +* ├─┤ ___/___(/_/(__(_/_(/_(/_ ___/__/_)_(/_(_(_/ (_(_(_ +* ┴ ┴ / / .-/ _____ (__ / +* (__ / (_/ (, / /)™ +* / __ __ __ __ _ __ __ _ _/_ _ _(/ +* ┌─┐┬─┐┌─┐┌┬┐┬ ┬┌─┐┌┬┐ /__/ (_(__(_)/ (_/_)_(_)/ (_(_(_(__(/_(_(_ +* ├─┘├┬┘│ │ │││ ││ │ (__ / .-/ © Jekyll Island Inc. 2018 +* ┴ ┴└─└─┘─┴┘└─┘└─┘ ┴ (_/ +* _ _ __ __ _ ____ ___ __ _ _ ____ ____ ____ ____ ____ __ ____ +*===/ )( \ ( ) ( ( \(_ _)===/ __) / \ ( \/ )( _ \( _ \( __)/ ___)/ ___) / \ ( _ \===* +* ) \/ ( )( / / )( ( (__ ( O )/ \/ \ ) __/ ) / ) _) \___ \\___ \( O ) ) / +*===\____/ (__) \_)__) (__)====\___) \__/ \_)(_/(__) (__\_)(____)(____/(____/ \__/ (__\_)===* +* +* ╔═╗┌─┐┌┐┌┌┬┐┬─┐┌─┐┌─┐┌┬┐ ╔═╗┌─┐┌┬┐┌─┐ ┌──────────┐ +* ║ │ ││││ │ ├┬┘├─┤│ │ ║ │ │ ││├┤ │ Inventor │ +* ╚═╝└─┘┘└┘ ┴ ┴└─┴ ┴└─┘ ┴ ╚═╝└─┘─┴┘└─┘ └──────────┘ +*/ + +library UintCompressor { + using SafeMath for *; + + function insert(uint256 _Var, uint256 _Include, uint256 _Start, uint256 _End) + internal + pure + returns(uint256) + { + // check conditions + require(_End < 77 && _Start < 77, "start/end must be less than 77"); + require(_End >= _Start, "end must be >= start"); + + // format our start/end points + _End = exponent(_End).mul(10); + _Start = exponent(_Start); + + // check that the include data fits into its segment + require(_Include < (_End / _Start)); + + // build middle + if (_Include > 0) + _Include = _Include.mul(_Start); + + return((_Var.sub((_Var / _Start).mul(_Start))).add(_Include).add((_Var / _End).mul(_End))); + } + + function extract(uint256 _Input, uint256 _Start, uint256 _End) + internal + pure + returns(uint256) + { + // check conditions + require(_End < 77 && _Start < 77, "start/end must be less than 77"); + require(_End >= _Start, "end must be >= start"); + + // format our start/end points + _End = exponent(_End).mul(10); + _Start = exponent(_Start); + + // return requested section + return((((_Input / _Start).mul(_Start)).sub((_Input / _End).mul(_End))) / _Start); + } + + function exponent(uint256 _Position) + private + pure + returns(uint256) + { + return((10).pwr(_Position)); + } +} + +// File: contracts\library\NameFilter.sol + +/** +* @title -Name Filter- v0.1.9 +* ┌┬┐┌─┐┌─┐┌┬┐ ╦╦ ╦╔═╗╔╦╗ ┌─┐┬─┐┌─┐┌─┐┌─┐┌┐┌┌┬┐┌─┐ +* │ ├┤ ├─┤│││ ║║ ║╚═╗ ║ ├─┘├┬┘├┤ └─┐├┤ │││ │ └─┐ +* ┴ └─┘┴ ┴┴ ┴ ╚╝╚═╝╚═╝ ╩ ┴ ┴└─└─┘└─┘└─┘┘└┘ ┴ └─┘ +* _____ _____ +* (, / /) /) /) (, / /) /) +* ┌─┐ / _ (/_ // // / _ // _ __ _(/ +* ├─┤ ___/___(/_/(__(_/_(/_(/_ ___/__/_)_(/_(_(_/ (_(_(_ +* ┴ ┴ / / .-/ _____ (__ / +* (__ / (_/ (, / /)™ +* / __ __ __ __ _ __ __ _ _/_ _ _(/ +* ┌─┐┬─┐┌─┐┌┬┐┬ ┬┌─┐┌┬┐ /__/ (_(__(_)/ (_/_)_(_)/ (_(_(_(__(/_(_(_ +* ├─┘├┬┘│ │ │││ ││ │ (__ / .-/ © Jekyll Island Inc. 2018 +* ┴ ┴└─└─┘─┴┘└─┘└─┘ ┴ (_/ +* _ __ _ ____ ____ _ _ _____ ____ ___ +*=============| |\ | / /\ | |\/| | |_ =====| |_ | | | | | | | |_ | |_)==============* +*=============|_| \| /_/--\ |_| | |_|__=====|_| |_| |_|__ |_| |_|__ |_| \==============* +* +* ╔═╗┌─┐┌┐┌┌┬┐┬─┐┌─┐┌─┐┌┬┐ ╔═╗┌─┐┌┬┐┌─┐ ┌──────────┐ +* ║ │ ││││ │ ├┬┘├─┤│ │ ║ │ │ ││├┤ │ Inventor │ +* ╚═╝└─┘┘└┘ ┴ ┴└─┴ ┴└─┘ ┴ ╚═╝└─┘─┴┘└─┘ └──────────┘ +*/ + +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 + + 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\F3DKeysCalcLong.sol + +//============================================================================== +// | _ _ _ | _ . +// |<(/_\/ (_(_||(_ . +//=======/====================================================================== +library F3DKeysCalcLong { + using SafeMath for *; + /** + * @dev calculates number of keys received given X eth + * _curEth current amount of eth in contract + * _newEth eth being spent + * @return amount of ticket purchased + */ + function keysRec(uint256 _CurEth, uint256 _NewEth) + internal + pure + returns (uint256) + { + return(keys((_CurEth).add(_NewEth)).sub(keys(_CurEth))); + } + + /** + * @dev calculates amount of eth received if you sold X keys + * _curKeys current amount of keys that exist + * _sellKeys amount of keys you wish to sell + * @return amount of eth received + */ + function ethRec(uint256 _CurKeys, uint256 _SellKeys) + internal + pure + returns (uint256) + { + return((eth(_CurKeys)).sub(eth(_CurKeys.sub(_SellKeys)))); + } + + /** + * @dev calculates how many keys would exist with given an amount of eth + * _eth eth "in contract" + * @return number of keys that would exist + */ + function keys(uint256 _Eth) + internal + pure + returns(uint256) + { + return ((((((_Eth).mul(1000000000000000000)).mul(312500000000000000000000000)).add(5624988281256103515625000000000000000000000000000000000000000000)).sqrt()).sub(74999921875000000000000000000000)) / (156250000); + } + + /** + * @dev calculates how much eth would be in contract given a number of keys + * _keys number of keys "in contract" + * @return eth that would exists + */ + function eth(uint256 _Keys) + internal + pure + returns(uint256) + { + return ((78125000).mul(_Keys.sq()).add(((149999843750000).mul(_Keys.mul(1000000000000000000))) / (2))) / ((1000000000000000000).sq()); + } +} + +// File: contracts\library\F3Ddatasets.sol + +//============================================================================== +// __|_ _ __|_ _ . +// _\ | | |_|(_ | _\ . +//============================================================================== +library F3Ddatasets { + //compressedData key + // [76-33][32][31][30][29][28-18][17][16-6][5-3][2][1][0] + // 0 - new player (bool) + // 1 - joined round (bool) + // 2 - new leader (bool) + // 3-5 - air drop tracker (uint 0-999) + // 6-16 - round end time + // 17 - winnerTeam + // 18 - 28 timestamp + // 29 - team + // 30 - 0 = reinvest (round), 1 = buy (round), 2 = buy (ico), 3 = reinvest (ico) + // 31 - airdrop happened bool + // 32 - airdrop tier + // 33 - airdrop amount won + //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 P3DAmount; // amount distributed to p3d + uint256 genAmount; // amount distributed to gen + uint256 potAmount; // amount added to pot + } + 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 PlayerRounds { + uint256 eth; // eth player has added to round (used for eth limiter) + uint256 keys; // keys + uint256 mask; // player mask + uint256 ico; // ICO phase investment + } + struct Round { + uint256 plyr; // pID of player in lead, lead领导吗? + uint256 team; // tID of team 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) + uint256 mask; // global mask + uint256 ico; // total eth sent in during ICO phase + uint256 icoGen; // total eth for gen during ICO phase + uint256 icoAvg; // average key price for ICO phase + } + struct TeamFee { + uint256 gen; // % of buy in thats paid to key holders of current round + uint256 p3d; // % of buy in thats paid to p3d holders + } + struct PotSplit { + uint256 gen; // % of pot thats paid to key holders of current round + uint256 p3d; // % of pot thats paid to p3d holders + } +} + +// File: contracts\F3Devents.sol + +contract F3Devents { + // 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 keysBought, + address winnerAddr, + bytes32 winnerName, + uint256 amountWon, + uint256 newPot, + uint256 P3DAmount, + uint256 genAmount, + uint256 potAmount, + uint256 airDropPot + ); + + // 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 P3DAmount, + uint256 genAmount + ); + + // (fomo3d long only) 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 P3DAmount, + uint256 genAmount + ); + + // (fomo3d long only) 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 P3DAmount, + uint256 genAmount + ); + + // fired whenever an affiliate is paid + event OnAffiliatePayout + ( + uint256 indexed affiliateID, + address affiliateAddress, + bytes32 affiliateName, + uint256 indexed roundID, + uint256 indexed buyerID, + uint256 amount, + uint256 timeStamp + ); + + // received pot swap deposit + event OnPotSwapDeposit + ( + uint256 roundID, + uint256 amountAddedToPot + ); +} + +// File: contracts\modularLong.sol + +contract Modularlong is F3Devents {} + +// File: contracts\FoMo3Dlong.sol + +contract FoMo3Dlong is Modularlong { + using SafeMath for *; + using NameFilter for string; + using F3DKeysCalcLong for uint256; + + + //TODO: + //JIincForwarderInterface constant private Jekyll_Island_Inc = JIincForwarderInterface(0x508D1c04cd185E693d22125f3Cc6DC81F7Ce9477); + PlayerBookInterface constant private PlayerBook = PlayerBookInterface(0x19dB4339c0ad1BE41FE497795FF2c5263962a573); + + address public constant TEAMWALLET = 0xE9675cdAf47bab3Eef5B1f1c2b7f8d41cDcf9b29; + address[] public leaderWallets; +//============================================================================== +// _ _ _ |`. _ _ _ |_ | _ _ . +// (_(_)| |~|~|(_||_|| (_||_)|(/__\ . (game settings) +//=================_|=========================================================== + string constant public name = "Peach Will"; + string constant public symbol = "PW"; + uint256 private constant RNDEXTRA_ = 1 hours; //24 hours; // length of the very first ICO + uint256 private constant RNDGAP_ = 15 seconds; // length of ICO phase, set to 1 year for EOS. + uint256 constant private RNDINIT_ = 10 hours; //1 hours; // round timer starts at this + uint256 constant private RNDINC_ = 88 seconds; // every full key purchased adds this much to the timer + uint256 constant private RNDMAX_ = 10 hours; // 24 hours; // max length a round timer can be +//============================================================================== +// _| _ _|_ _ _ _ _|_ _ . +// (_|(_| | (_| _\(/_ | |_||_) . (data used to store game info that changes) +//=============================|================================================ + uint256 public airDropPot_; // person who gets the airdrop wins part of this pot + uint256 public airDropTracker_ = 0; // incremented each time a "qualified" tx occurs. used to determine winning air drop + uint256 public rID_; // round id number / total rounds that have happened +//**************** +// 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 => F3Ddatasets.Player) public plyr_; // (pID => data) player data + mapping (uint256 => mapping (uint256 => F3Ddatasets.PlayerRounds)) 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) +//**************** +// ROUND DATA +//**************** + mapping (uint256 => F3Ddatasets.Round) public round_; // (rID => data) round data + mapping (uint256 => mapping(uint256 => uint256)) public rndTmEth_; // (rID => tID => data) eth in per team, by round id and team id +//**************** +// TEAM FEE DATA , Team的费用分配数据 +//**************** + mapping (uint256 => F3Ddatasets.TeamFee) public fees_; // (team => fees) fee distribution by team + mapping (uint256 => F3Ddatasets.PotSplit) public potSplit_; // (team => fees) pot split distribution by team +//============================================================================== +// _ _ _ __|_ _ __|_ _ _ . +// (_(_)| |_\ | | |_|(_ | (_)| . (initial data setup upon contract deploy) +//============================================================================== + constructor() + public + { + // Team allocation structures + // 0 = whales + // 1 = bears + // 2 = sneks + // 3 = bulls + + // Team allocation percentages + // (F3D, P3D) + (Pot , Referrals, Community) + // Referrals / Community rewards are mathematically designed to come from the winner's share of the pot. + fees_[0] = F3Ddatasets.TeamFee(54,0); //20% to pot, 10% to aff, 10% to com, 5% to leader swap, 1% to air drop pot + fees_[1] = F3Ddatasets.TeamFee(41,0); //33% to pot, 10% to aff, 10% to com, 5% to leader swap, 1% to air drop pot + fees_[2] = F3Ddatasets.TeamFee(30,0); //44% to pot, 10% to aff, 10% to com, 5% to leader swap, 1% to air drop pot + fees_[3] = F3Ddatasets.TeamFee(40,0); //34% to pot, 10% to aff, 10% to com, 5% to leader swap, 1% to air drop pot + + // how to split up the final pot based on which team was picked + // (F3D, P3D) + potSplit_[0] = F3Ddatasets.PotSplit(37,0); //48% to winner, 10% to next round, 5% to com + potSplit_[1] = F3Ddatasets.PotSplit(34,0); //48% to winner, 13% to next round, 5% to com + potSplit_[2] = F3Ddatasets.PotSplit(25,0); //48% to winner, 22% to next round, 5% to com + potSplit_[3] = F3Ddatasets.PotSplit(32,0); //48% to winner, 15% to next round, 5% to com + + leaderWallets.length = 4; + leaderWallets[0]= 0x326d8d593195a3153f6d55d7791c10af9bcef597; + leaderWallets[1]= 0x15B474F7DE7157FA0dB9FaaA8b82761E78E804B9; + leaderWallets[2]= 0x0c2d482FBc1da4DaCf3CD05b6A5955De1A296fa8; + leaderWallets[3]= 0xD3d96E74aFAE57B5191DC44Bdb08b037355523Ba; + + } +//============================================================================== +// _ _ _ _|. |`. _ _ _ . +// | | |(_)(_||~|~|(/_| _\ . (these are safety checks) +//============================================================================== + /** + * @dev used to make sure no one can interact with contract until it has + * been activated. + */ + modifier isActivated() { + require(activated_ == true, "its not ready yet. check ?eta in discord"); + _; + } + + /** + * @dev prevents contracts from interacting with fomo3d + */ + 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 <= 100000000000000000000000, "no vitalik, no"); + _; + } + + /** + * + */ + modifier onlyDevs() { + //TODO: + require( + msg.sender == 0xE9675cdAf47bab3Eef5B1f1c2b7f8d41cDcf9b29 || + msg.sender == 0x0020116131498D968DeBCF75E5A11F77e7e1CadE, + "only team just can activate" + ); + _; + } + +//============================================================================== +// _ |_ |. _ |` _ __|_. _ _ _ . +// |_)|_||_)||(_ ~|~|_|| |(_ | |(_)| |_\ . (use these to interact with contract) +//====|========================================================================= + /** + * @dev emergency buy uses last stored affiliate ID and team snek + */ + function() + isActivated() + isHuman() + isWithinLimits(msg.value) + external + payable + { + // set up our tx event data and determine if player is new or not + F3Ddatasets.EventReturns memory _eventData_ = determinePID(_eventData_); + + // fetch player id + uint256 _PID = pIDxAddr_[msg.sender]; + + // buy core + buyCore(_PID, plyr_[_PID].laff, 2, _eventData_); + } + + /** + * @dev converts all incoming ethereum to keys. + * -functionhash- 0x8f38f309 (using ID for affiliate) + * -functionhash- 0x98a0871d (using address for affiliate) + * -functionhash- 0xa65b37a1 (using name for affiliate) + * _affCode the ID/address/name of the player who gets the affiliate fee + * _Team what team is the player playing for? + */ + function buyXid(uint256 _AffCode, uint256 _Team) + isActivated() + isHuman() + isWithinLimits(msg.value) + public + payable + { + // set up our tx event data and determine if player is new or not + F3Ddatasets.EventReturns memory _eventData_ = determinePID(_eventData_); + + // fetch player id + uint256 _PID = pIDxAddr_[msg.sender]; + + // manage affiliate residuals + // if no affiliate code was given or player tried to use their own, lolz + if (_AffCode == 0 || _AffCode == _PID) + { + // use last stored affiliate code + _AffCode = plyr_[_PID].laff; + + // if affiliate code was given & its not the same as previously stored + } else if (_AffCode != plyr_[_PID].laff) { + // update last affiliate + plyr_[_PID].laff = _AffCode; + } + + // verify a valid team was selected + _Team = verifyTeam(_Team); + + // buy core + buyCore(_PID, _AffCode, _Team, _eventData_); + } + + function buyXaddr(address _AffCode, uint256 _Team) + isActivated() + isHuman() + isWithinLimits(msg.value) + public + payable + { + // set up our tx event data and determine if player is new or not + F3Ddatasets.EventReturns memory _eventData_ = determinePID(_eventData_); + + // fetch player id + uint256 _PID = pIDxAddr_[msg.sender]; + + // manage affiliate residuals + uint256 _AffID; + // if no affiliate code was given or player tried to use their own, lolz + if (_AffCode == address(0) || _AffCode == msg.sender) + { + // use last stored affiliate code + _AffID = plyr_[_PID].laff; + + // if affiliate code was given + } else { + // get affiliate ID from aff Code + _AffID = pIDxAddr_[_AffCode]; + + // if affID is not the same as previously stored + if (_AffID != plyr_[_PID].laff) + { + // update last affiliate + plyr_[_PID].laff = _AffID; + } + } + + // verify a valid team was selected + _Team = verifyTeam(_Team); + + // buy core + buyCore(_PID, _AffID, _Team, _eventData_); + } + + function buyXname(bytes32 _AffCode, uint256 _Team) + isActivated() + isHuman() + isWithinLimits(msg.value) + public + payable + { + // set up our tx event data and determine if player is new or not + F3Ddatasets.EventReturns memory _eventData_ = determinePID(_eventData_); + + // fetch player id + uint256 _PID = pIDxAddr_[msg.sender]; + + // manage affiliate residuals + uint256 _AffID; + // if no affiliate code was given or player tried to use their own, lolz + if (_AffCode == '' || _AffCode == plyr_[_PID].name) + { + // use last stored affiliate code + _AffID = plyr_[_PID].laff; + + // if affiliate code was given + } else { + // get affiliate ID from aff Code + _AffID = pIDxName_[_AffCode]; + + // if affID is not the same as previously stored + if (_AffID != plyr_[_PID].laff) + { + // update last affiliate + plyr_[_PID].laff = _AffID; + } + } + + // verify a valid team was selected + _Team = verifyTeam(_Team); + + // buy core + buyCore(_PID, _AffID, _Team, _eventData_); + } + + /** + * @dev essentially the same as buy, but instead of you sending ether + * from your wallet, it uses your unwithdrawn earnings. + * -functionhash- 0x349cdcac (using ID for affiliate) + * -functionhash- 0x82bfc739 (using address for affiliate) + * -functionhash- 0x079ce327 (using name for affiliate) + * _affCode the ID/address/name of the player who gets the affiliate fee + * _Team what team is the player playing for? + * _eth amount of earnings to use (remainder returned to gen vault) + */ + function reLoadXid(uint256 _AffCode, uint256 _Team, uint256 _Eth) + isActivated() + isHuman() + isWithinLimits(_Eth) + public + { + // set up our tx event data + F3Ddatasets.EventReturns memory _eventData_; + + // fetch player ID + uint256 _PID = pIDxAddr_[msg.sender]; + + // manage affiliate residuals + // if no affiliate code was given or player tried to use their own, lolz + if (_AffCode == 0 || _AffCode == _PID) + { + // use last stored affiliate code + _AffCode = plyr_[_PID].laff; + + // if affiliate code was given & its not the same as previously stored + } else if (_AffCode != plyr_[_PID].laff) { + // update last affiliate + plyr_[_PID].laff = _AffCode; + } + + // verify a valid team was selected + _Team = verifyTeam(_Team); + + // reload core + reLoadCore(_PID, _AffCode, _Team, _Eth, _eventData_); + } + + function reLoadXaddr(address _AffCode, uint256 _Team, uint256 _Eth) + isActivated() + isHuman() + isWithinLimits(_Eth) + public + { + // set up our tx event data + F3Ddatasets.EventReturns memory _eventData_; + + // fetch player ID + uint256 _PID = pIDxAddr_[msg.sender]; + + // manage affiliate residuals + uint256 _AffID; + // if no affiliate code was given or player tried to use their own, lolz + if (_AffCode == address(0) || _AffCode == msg.sender) + { + // use last stored affiliate code + _AffID = plyr_[_PID].laff; + + // if affiliate code was given + } else { + // get affiliate ID from aff Code + _AffID = pIDxAddr_[_AffCode]; + + // if affID is not the same as previously stored + if (_AffID != plyr_[_PID].laff) + { + // update last affiliate + plyr_[_PID].laff = _AffID; + } + } + + // verify a valid team was selected + _Team = verifyTeam(_Team); + + // reload core + reLoadCore(_PID, _AffID, _Team, _Eth, _eventData_); + } + + function reLoadXname(bytes32 _AffCode, uint256 _Team, uint256 _Eth) + isActivated() + isHuman() + isWithinLimits(_Eth) + public + { + // set up our tx event data + F3Ddatasets.EventReturns memory _eventData_; + + // fetch player ID + uint256 _PID = pIDxAddr_[msg.sender]; + + // manage affiliate residuals + uint256 _AffID; + // if no affiliate code was given or player tried to use their own, lolz + if (_AffCode == '' || _AffCode == plyr_[_PID].name) + { + // use last stored affiliate code + _AffID = plyr_[_PID].laff; + + // if affiliate code was given + } else { + // get affiliate ID from aff Code + _AffID = pIDxName_[_AffCode]; + + // if affID is not the same as previously stored + if (_AffID != plyr_[_PID].laff) + { + // update last affiliate + plyr_[_PID].laff = _AffID; + } + } + + // verify a valid team was selected + _Team = verifyTeam(_Team); + + // reload core + reLoadCore(_PID, _AffID, _Team, _Eth, _eventData_); + } + + /** + * @dev withdraws all of your earnings. + * -functionhash- 0x3ccfd60b + */ + function withdraw() + isActivated() + isHuman() + external + { + // setup local rID + uint256 _RID = rID_; + + // grab time + uint256 _now = now; + + // fetch player ID + uint256 _PID = pIDxAddr_[msg.sender]; + + // 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 == false && round_[_RID].plyr != 0) + { + // set up our tx event data + F3Ddatasets.EventReturns memory _eventData_; + + // end the round (distributes pot) + round_[_RID].ended = true; + _eventData_ = endRound(_eventData_); + + // get their earnings + _eth = withdrawEarnings(_PID); + + // gib moni + if (_eth > 0) + plyr_[_PID].addr.transfer(_eth); + + // build event data + _eventData_.compressedData = _eventData_.compressedData + (_now * 1000000000000000000); + _eventData_.compressedIDs = _eventData_.compressedIDs + _PID; + + // fire withdraw and distribute event + emit F3Devents.OnWithdrawAndDistribute + ( + msg.sender, + plyr_[_PID].name, + _eth, + _eventData_.compressedData, + _eventData_.compressedIDs, + _eventData_.winnerAddr, + _eventData_.winnerName, + _eventData_.amountWon, + _eventData_.newPot, + _eventData_.P3DAmount, + _eventData_.genAmount + ); + + // in any other situation + } else { + // get their earnings + _eth = withdrawEarnings(_PID); + + // gib moni + if (_eth > 0) + plyr_[_PID].addr.transfer(_eth); + + // fire withdraw event + emit F3Devents.OnWithdraw(_PID, msg.sender, plyr_[_PID].name, _eth, _now); + } + } + + /** + * @dev use these to register names. they are just wrappers that will send the + * registration requests to the PlayerBook contract. So registering here is the + * same as registering there. UI will always display the last name you registered. + * but you will still own all previously registered names to use as affiliate + * links. + * - must pay a registration fee. + * - name must be unique + * - names will be converted to lowercase + * - name cannot start or end with a space + * - cannot have more than 1 space in a row + * - cannot be only numbers + * - cannot start with 0x + * - name must be at least 1 char + * - max length of 32 characters long + * - allowed characters: a-z, 0-9, and space + * -functionhash- 0x921dec21 (using ID for affiliate) + * -functionhash- 0x3ddd4698 (using address for affiliate) + * -functionhash- 0x685ffd83 (using name for affiliate) + * _NameString players desired name + * _affCode affiliate ID, address, or name of who referred you + * _all set to true if you want this to push your info to all games + * (this might cost a lot of gas) + */ + function registerNameXID(string _NameString, uint256 _AffCode, bool _All) + isHuman() + external + payable + { + bytes32 _Name = _NameString.nameFilter(); + address _Addr = msg.sender; + uint256 _paid = msg.value; + (bool _isNewPlayer, uint256 _AffID) = PlayerBook.registerNameXIDFromDapp.value(_paid)(_Addr, _Name, _AffCode, _All); + + uint256 _PID = pIDxAddr_[_Addr]; + + // fire event + emit F3Devents.OnNewName(_PID, _Addr, _Name, _isNewPlayer, _AffID, plyr_[_AffID].addr, plyr_[_AffID].name, _paid, now); + } + + function registerNameXaddr(string _NameString, address _AffCode, bool _All) + isHuman() + external + payable + { + bytes32 _Name = _NameString.nameFilter(); + address _Addr = msg.sender; + uint256 _paid = msg.value; + (bool _isNewPlayer, uint256 _AffID) = PlayerBook.registerNameXaddrFromDapp.value(msg.value)(msg.sender, _Name, _AffCode, _All); + + uint256 _PID = pIDxAddr_[_Addr]; + + // fire event + emit F3Devents.OnNewName(_PID, _Addr, _Name, _isNewPlayer, _AffID, plyr_[_AffID].addr, plyr_[_AffID].name, _paid, now); + } + + function registerNameXname(string _NameString, bytes32 _AffCode, bool _All) + isHuman() + external + payable + { + bytes32 _Name = _NameString.nameFilter(); + address _Addr = msg.sender; + uint256 _paid = msg.value; + (bool _isNewPlayer, uint256 _AffID) = PlayerBook.registerNameXnameFromDapp.value(msg.value)(msg.sender, _Name, _AffCode, _All); + + uint256 _PID = pIDxAddr_[_Addr]; + + // fire event + emit F3Devents.OnNewName(_PID, _Addr, _Name, _isNewPlayer, _AffID, plyr_[_AffID].addr, plyr_[_AffID].name, _paid, now); + } +//============================================================================== +// _ _ _|__|_ _ _ _ . +// (_|(/_ | | (/_| _\ . (for UI & viewing things on etherscan) +//=====_|======================================================================= + /** + * @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() + external + view + returns(uint256) + { + // setup local rID + uint256 _RID = rID_; + + // grab time + uint256 _now = now; + + // are we in a round? + if (_now > round_[_RID].strt + RNDGAP_ && (_now <= round_[_RID].end || (_now > round_[_RID].end && round_[_RID].plyr == 0))) + return ( (round_[_RID].keys.add(1000000000000000000)).ethRec(1000000000000000000) ); + else // rounds over. need price for new round + return ( 75000000000000 ); // init + } + + /** + * @dev returns time left. dont spam this, you'll ddos yourself from your node + * provider + * -functionhash- 0xc7e284b8 + * @return time left in seconds + */ + function getTimeLeft() + external + view + returns(uint256) + { + // setup local rID + uint256 _RID = rID_; + + // grab time + 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 + * @return affiliate vault + */ + function getPlayerVaults(uint256 _PID) + external + view + returns(uint256 ,uint256, uint256) + { + // setup local rID + uint256 _RID = rID_; + + // 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 == false && round_[_RID].plyr != 0) + { + // if player is winner + if (round_[_RID].plyr == _PID) + { + return + ( + (plyr_[_PID].win).add( ((round_[_RID].pot).mul(48)) / 100 ), + (plyr_[_PID].gen).add( getPlayerVaultsHelper(_PID, _RID).sub(plyrRnds_[_PID][_RID].mask) ), + plyr_[_PID].aff + ); + // if player is not the winner + } else { + return + ( + plyr_[_PID].win, + (plyr_[_PID].gen).add( getPlayerVaultsHelper(_PID, _RID).sub(plyrRnds_[_PID][_RID].mask) ), + plyr_[_PID].aff + ); + } + + // if round is still going on, or round has ended and round end has been ran + } else { + return + ( + plyr_[_PID].win, + (plyr_[_PID].gen).add(calcUnMaskedEarnings(_PID, plyr_[_PID].lrnd)), + plyr_[_PID].aff + ); + } + } + + /** + * solidity hates stack limits. this lets us avoid that hate + */ + function getPlayerVaultsHelper(uint256 _PID, uint256 _RID) + private + view + returns(uint256) + { + return( ((((round_[_RID].mask).add(((((round_[_RID].pot).mul(potSplit_[round_[_RID].team].gen)) / 100).mul(1000000000000000000)) / (round_[_RID].keys))).mul(plyrRnds_[_PID][_RID].keys)) / 1000000000000000000) ); + } + + /** + * @dev returns all current round info needed for front end + * -functionhash- 0x747dff42 + * @return eth invested during ICO phase + * @return round id + * @return total keys for round + * @return time round ends + * @return time round started + * @return current pot + * @return current team ID & player ID in lead + * @return current player in leads address + * @return current player in leads name + * @return whales eth in for round + * @return bears eth in for round + * @return sneks eth in for round + * @return bulls eth in for round + * @return airdrop tracker # & airdrop pot + */ + function getCurrentRoundInfo() + external + view + returns(uint256, uint256, uint256, uint256, uint256, uint256, uint256, address, bytes32, uint256, uint256, uint256, uint256, uint256) + { + // setup local rID + uint256 _RID = rID_; + + return + ( + round_[_RID].ico, //0 + _RID, //1 + round_[_RID].keys, //2 + round_[_RID].end, //3 + round_[_RID].strt, //4 + round_[_RID].pot, //5 + (round_[_RID].team + (round_[_RID].plyr * 10)), //6 + plyr_[round_[_RID].plyr].addr, //7 + plyr_[round_[_RID].plyr].name, //8 + rndTmEth_[_RID][0], //9 + rndTmEth_[_RID][1], //10 + rndTmEth_[_RID][2], //11 + rndTmEth_[_RID][3], //12 + airDropTracker_ + (airDropPot_ * 1000) //13 + ); + } + + /** + * @dev returns player info based on address. if no address is given, it will + * use msg.sender + * -functionhash- 0xee0b5d8b + * _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 affiliate vault + * @return player round eth + */ + function getPlayerInfoByAddress(address _Addr) + external + view + returns(uint256, bytes32, uint256, uint256, uint256, uint256, uint256) + { + // setup local rID + uint256 _RID = rID_; + + if (_Addr == address(0)) + { + _Addr == msg.sender; + } + uint256 _PID = pIDxAddr_[_Addr]; + + return + ( + _PID, //0 + plyr_[_PID].name, //1 + plyrRnds_[_PID][_RID].keys, //2 + plyr_[_PID].win, //3 + (plyr_[_PID].gen).add(calcUnMaskedEarnings(_PID, plyr_[_PID].lrnd)), //4 + plyr_[_PID].aff, //5 + plyrRnds_[_PID][_RID].eth //6 + ); + } + +//============================================================================== +// _ _ _ _ | _ _ . _ . +// (_(_)| (/_ |(_)(_||(_ . (this + tools + calcs + modules = our softwares engine) +//=====================_|======================================================= + /** + * @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, uint256 _AffID, uint256 _Team, F3Ddatasets.EventReturns memory _EventData_) + private + { + // setup local rID + uint256 _RID = rID_; + + // grab time + uint256 _now = now; + + // if round is active + if (_now > round_[_RID].strt + RNDGAP_ && (_now <= round_[_RID].end || (_now > round_[_RID].end && round_[_RID].plyr == 0))) + { + // call core + core(_RID, _PID, msg.value, _AffID, _Team, _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 == false) + { + // end the round (distributes pot) & start new round + round_[_RID].ended = true; + _EventData_ = endRound(_EventData_); + + // build event data + _EventData_.compressedData = _EventData_.compressedData + (_now * 1000000000000000000); + _EventData_.compressedIDs = _EventData_.compressedIDs + _PID; + + // fire buy and distribute event + emit F3Devents.OnBuyAndDistribute + ( + msg.sender, + plyr_[_PID].name, + msg.value, + _EventData_.compressedData, + _EventData_.compressedIDs, + _EventData_.winnerAddr, + _EventData_.winnerName, + _EventData_.amountWon, + _EventData_.newPot, + _EventData_.P3DAmount, + _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 _AffID, uint256 _Team, uint256 _Eth, F3Ddatasets.EventReturns memory _EventData_) + private + { + // setup local rID + uint256 _RID = rID_; + + // grab time + uint256 _now = now; + + // if round is active + if (_now > round_[_RID].strt + RNDGAP_ && (_now <= round_[_RID].end || (_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, _AffID, _Team, _EventData_); + + // if round is not active and end round needs to be ran + } else if (_now > round_[_RID].end && round_[_RID].ended == false) { + // end the round (distributes pot) & start new round + round_[_RID].ended = true; + _EventData_ = endRound(_EventData_); + + // build event data + _EventData_.compressedData = _EventData_.compressedData + (_now * 1000000000000000000); + _EventData_.compressedIDs = _EventData_.compressedIDs + _PID; + + // fire buy and distribute event + emit F3Devents.OnReLoadAndDistribute + ( + msg.sender, + plyr_[_PID].name, + _EventData_.compressedData, + _EventData_.compressedIDs, + _EventData_.winnerAddr, + _EventData_.winnerName, + _EventData_.amountWon, + _EventData_.newPot, + _EventData_.P3DAmount, + _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, uint256 _AffID, uint256 _Team, F3Ddatasets.EventReturns memory _EventData_) + private + { + // if player is new to round + if (plyrRnds_[_PID][_RID].keys == 0) + _EventData_ = managePlayer(_PID, _EventData_); + + // early round eth limiter + if (round_[_RID].eth < 100000000000000000000 && plyrRnds_[_PID][_RID].eth.add(_Eth) > 1000000000000000000) + { + uint256 _availableLimit = (1000000000000000000).sub(plyrRnds_[_PID][_RID].eth); + uint256 _refund = _Eth.sub(_availableLimit); + plyr_[_PID].gen = plyr_[_PID].gen.add(_refund); + _Eth = _availableLimit; + } + + // if eth left is greater than min eth allowed (sorry no pocket lint) + if (_Eth > 1000000000) + { + + // mint the new keys + uint256 _keys = (round_[_RID].eth).keysRec(_Eth); + + // if they bought at least 1 whole key + if (_keys >= 1000000000000000000) + { + updateTimer(_keys, _RID); + + // set new leaders + if (round_[_RID].plyr != _PID) + round_[_RID].plyr = _PID; + if (round_[_RID].team != _Team) + round_[_RID].team = _Team; + + // set the new leader bool to true + _EventData_.compressedData = _EventData_.compressedData + 100; + } + + // manage airdrops + if (_Eth >= 100000000000000000) + { + airDropTracker_++; + if (airdrop() == true) + { + // gib muni + uint256 _prize; + if (_Eth >= 10000000000000000000) + { + // calculate prize and give it to winner + _prize = ((airDropPot_).mul(75)) / 100; + plyr_[_PID].win = (plyr_[_PID].win).add(_prize); + + // adjust airDropPot + airDropPot_ = (airDropPot_).sub(_prize); + + // let event know a tier 3 prize was won + _EventData_.compressedData += 300000000000000000000000000000000; + } else if (_Eth >= 1000000000000000000 && _Eth < 10000000000000000000) { + // calculate prize and give it to winner + _prize = ((airDropPot_).mul(50)) / 100; + plyr_[_PID].win = (plyr_[_PID].win).add(_prize); + + // adjust airDropPot + airDropPot_ = (airDropPot_).sub(_prize); + + // let event know a tier 2 prize was won + _EventData_.compressedData += 200000000000000000000000000000000; + } else if (_Eth >= 100000000000000000 && _Eth < 1000000000000000000) { + // calculate prize and give it to winner + _prize = ((airDropPot_).mul(25)) / 100; + plyr_[_PID].win = (plyr_[_PID].win).add(_prize); + + // adjust airDropPot + airDropPot_ = (airDropPot_).sub(_prize); + + // let event know a tier 3 prize was won + _EventData_.compressedData += 300000000000000000000000000000000; + } + // set airdrop happened bool to true + _EventData_.compressedData += 10000000000000000000000000000000; + // let event know how much was won + _EventData_.compressedData += _prize * 1000000000000000000000000000000000; + + // reset air drop tracker + airDropTracker_ = 0; + } + } + + // store the air drop tracker number (number of buys since last airdrop) + _EventData_.compressedData = _EventData_.compressedData + (airDropTracker_ * 1000); + + // update player + plyrRnds_[_PID][_RID].keys = _keys.add(plyrRnds_[_PID][_RID].keys); + plyrRnds_[_PID][_RID].eth = _Eth.add(plyrRnds_[_PID][_RID].eth); + + // update round + round_[_RID].keys = _keys.add(round_[_RID].keys); + round_[_RID].eth = _Eth.add(round_[_RID].eth); + rndTmEth_[_RID][_Team] = _Eth.add(rndTmEth_[_RID][_Team]); + + // distribute eth + _EventData_ = distributeExternal(_RID, _PID, _Eth, _AffID, _Team, _EventData_); + _EventData_ = distributeInternal(_RID, _PID, _Eth, _Team, _keys, _EventData_); + + // call end tx function to fire end tx event. + endTx(_PID, _Team, _Eth, _keys, _EventData_); + } + } +//============================================================================== +// _ _ | _ | _ _|_ _ _ _ . +// (_(_||(_|_||(_| | (_)| _\ . +//============================================================================== + /** + * @dev calculates unmasked earnings (just calculates, does not update mask) + * @return earnings in wei format + */ + function calcUnMaskedEarnings(uint256 _PID, uint256 _RIDlast) + private + view + returns(uint256) + { + return( (((round_[_RIDlast].mask).mul(plyrRnds_[_PID][_RIDlast].keys)) / (1000000000000000000)).sub(plyrRnds_[_PID][_RIDlast].mask) ); + } + + /** + * @dev returns the amount of keys you would get given an amount of eth. + * -functionhash- 0xce89c80c + * _RID round ID you want price for + * _eth amount of eth sent in + * @return keys received + */ + function calcKeysReceived(uint256 _RID, uint256 _Eth) + external + view + returns(uint256) + { + // grab time + uint256 _now = now; + + // are we in a round? + if (_now > round_[_RID].strt + RNDGAP_ && (_now <= round_[_RID].end || (_now > round_[_RID].end && round_[_RID].plyr == 0))) + return ( (round_[_RID].eth).keysRec(_Eth) ); + else // rounds over. need keys for new round + return ( (_Eth).keys() ); + } + + /** + * @dev returns current eth price for X keys. + * -functionhash- 0xcf808000 + * _keys number of keys desired (in 18 decimal format) + * @return amount of eth needed to send + */ + function iWantXKeys(uint256 _Keys) + external + view + returns(uint256) + { + // setup local rID + uint256 _RID = rID_; + + // grab time + uint256 _now = now; + + // are we in a round? + if (_now > round_[_RID].strt + RNDGAP_ && (_now <= round_[_RID].end || (_now > round_[_RID].end && round_[_RID].plyr == 0))) + return ( (round_[_RID].keys.add(_Keys)).ethRec(_Keys) ); + else // rounds over. need price for new round + return ( (_Keys).eth() ); + } +//============================================================================== +// _|_ _ _ | _ . +// | (_)(_)|_\ . +//============================================================================== + /** + * @dev receives name/player info from names contract + */ + function receivePlayerInfo(uint256 _PID, address _Addr, bytes32 _Name, uint256 _Laff) + external + { + require (msg.sender == address(PlayerBook), "your not playerNames contract... hmmm.."); + 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 (plyr_[_PID].laff != _Laff) + plyr_[_PID].laff = _Laff; + if (plyrNames_[_PID][_Name] == false) + 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... hmmm.."); + if(plyrNames_[_PID][_Name] == false) + plyrNames_[_PID][_Name] = true; + } + + /** + * @dev gets existing or registers new pID. use this when a player may be new + * @return pID + */ + function determinePID(F3Ddatasets.EventReturns memory _EventData_) + private + returns (F3Ddatasets.EventReturns) + { + uint256 _PID = pIDxAddr_[msg.sender]; + // if player is new to this version of fomo3d + if (_PID == 0) + { + // grab their player ID, name and last aff ID, from player names contract + _PID = PlayerBook.getPlayerID(msg.sender); + bytes32 _Name = PlayerBook.getPlayerName(_PID); + uint256 _laff = PlayerBook.getPlayerLAff(_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; + } + + if (_laff != 0 && _laff != _PID) + plyr_[_PID].laff = _laff; + + // set the new player bool to true + _EventData_.compressedData = _EventData_.compressedData + 1; + } + return (_EventData_); + } + + /** + * @dev checks to make sure user picked a valid team. if not sets team + * to default (sneks) + */ + function verifyTeam(uint256 _Team) + private + pure + returns (uint256) + { + if (_Team < 0 || _Team > 3) + return(2); + else + return(_Team); + } + + /** + * @dev decides if round end needs to be run & new round started. and if + * player unmasked earnings from previously played rounds need to be moved. + */ + function managePlayer(uint256 _PID, F3Ddatasets.EventReturns memory _EventData_) + private + returns (F3Ddatasets.EventReturns) + { + // if player has played a previous round, move their unmasked earnings + // from that round to gen vault. + if (plyr_[_PID].lrnd != 0) + updateGenVault(_PID, plyr_[_PID].lrnd); + + // update player's last round played + plyr_[_PID].lrnd = rID_; + + // set the joined round bool to true + _EventData_.compressedData = _EventData_.compressedData + 10; + + return(_EventData_); + } + + /** + * @dev ends the round. manages paying out winner/splitting up pot + */ + function endRound(F3Ddatasets.EventReturns memory _EventData_) + private + returns (F3Ddatasets.EventReturns) + { + // setup local rID + uint256 _RID = rID_; + + // grab our winning player and team id's + uint256 _winPID = round_[_RID].plyr; + uint256 _winTID = round_[_RID].team; + + // grab our pot amount + uint256 _pot = round_[_RID].pot; + + // calculate our winner share, community rewards, gen share, + // p3d share, and amount reserved for next pot + uint256 _win = (_pot.mul(48)) / 100; + uint256 _com = (_pot / 20); + uint256 _gen = (_pot.mul(potSplit_[_winTID].gen)) / 100; + uint256 _p3d = (_pot.mul(potSplit_[_winTID].p3d)) / 100; + uint256 _res = (((_pot.sub(_win)).sub(_com)).sub(_gen)).sub(_p3d); + + // calculate ppt for round mask + uint256 _ppt = (_gen.mul(1000000000000000000)) / (round_[_RID].keys); + uint256 _dust = _gen.sub((_ppt.mul(round_[_RID].keys)) / 1000000000000000000); + if (_dust > 0) + { + _gen = _gen.sub(_dust); + _res = _res.add(_dust); + } + + // pay our winner + plyr_[_winPID].win = _win.add(plyr_[_winPID].win); + + // community rewards + + TEAMWALLET.transfer(_com); + + // if (!address(Jekyll_Island_Inc).call.value(_com)(bytes4(keccak256("deposit()")))) + // { + // // This ensures Team Just cannot influence the outcome of FoMo3D with + // // bank migrations by breaking outgoing transactions. + // // Something we would never do. But that's not the point. + // // We spent 2000$ in eth re-deploying just to patch this, we hold the + // // highest belief that everything we create should be trustless. + // // Team JUST, The name you shouldn't have to trust. + // _p3d = _p3d.add(_com); + // _com = 0; + // } + + // distribute gen portion to key holders + round_[_RID].mask = _ppt.add(round_[_RID].mask); + + // send share for p3d to divies + // if (_p3d > 0) + // Divies.deposit.value(_p3d)(); + + // prepare event data + _EventData_.compressedData = _EventData_.compressedData + (round_[_RID].end * 1000000); + _EventData_.compressedIDs = _EventData_.compressedIDs + (_winPID * 100000000000000000000000000) + (_winTID * 100000000000000000); + _EventData_.winnerAddr = plyr_[_winPID].addr; + _EventData_.winnerName = plyr_[_winPID].name; + _EventData_.amountWon = _win; + _EventData_.genAmount = _gen; + _EventData_.P3DAmount = _p3d; + _EventData_.newPot = _res; + + // start next round + rID_++; + _RID++; + round_[_RID].strt = now; + round_[_RID].end = now.add(RNDINIT_).add(RNDGAP_); + round_[_RID].pot = _res; + + return(_EventData_); + } + + /** + * @dev moves any unmasked earnings to gen vault. updates earnings mask + */ + function updateGenVault(uint256 _PID, uint256 _RIDlast) + private + { + uint256 _earnings = calcUnMaskedEarnings(_PID, _RIDlast); + if (_earnings > 0) + { + // put in gen vault + plyr_[_PID].gen = _earnings.add(plyr_[_PID].gen); + // zero out their earnings by updating mask + plyrRnds_[_PID][_RIDlast].mask = _earnings.add(plyrRnds_[_PID][_RIDlast].mask); + } + } + + /** + * @dev updates round timer based on number of whole keys bought. + */ + function updateTimer(uint256 _Keys, uint256 _RID) + private + { + // grab time + uint256 _now = now; + + // calculate time based on number of keys bought + uint256 _newTime; + if (_now > round_[_RID].end && round_[_RID].plyr == 0) + _newTime = (((_Keys) / (1000000000000000000)).mul(RNDINC_)).add(_now); + else + _newTime = (((_Keys) / (1000000000000000000)).mul(RNDINC_)).add(round_[_RID].end); + + // compare to max and set new end time + if (_newTime < (RNDMAX_).add(_now)) + round_[_RID].end = _newTime; + else + round_[_RID].end = RNDMAX_.add(_now); + } + + /** + * @dev generates a random number between 0-99 and checks to see if thats + * resulted in an airdrop win + * @return do we have a winner? + */ + function airdrop() + private + view + returns(bool) + { + uint256 seed = uint256(keccak256(abi.encodePacked( + + (block.timestamp).add + (block.difficulty).add + ((uint256(keccak256(abi.encodePacked(block.coinbase)))) / (now)).add + (block.gaslimit).add + ((uint256(keccak256(abi.encodePacked(msg.sender)))) / (now)).add + (block.number) + + ))); + if((seed - ((seed / 1000) * 1000)) < airDropTracker_) + return(true); + else + return(false); + } + + /** + * @dev distributes eth based on fees to com, aff, and p3d + */ + function distributeExternal(uint256 _RID, uint256 _PID, uint256 _Eth, uint256 _AffID, uint256 _Team, F3Ddatasets.EventReturns memory _EventData_) + private + returns(F3Ddatasets.EventReturns) + { + // pay 10% out to community rewards + uint256 _com = _Eth / 10; + //uint256 _p3d; + + TEAMWALLET.transfer(_com); + // if (!address(Jekyll_Island_Inc).call.value(_com)(bytes4(keccak256("deposit()")))) + // { + // // This ensures Team Just cannot influence the outcome of FoMo3D with + // // bank migrations by breaking outgoing transactions. + // // Something we would never do. But that's not the point. + // // We spent 2000$ in eth re-deploying just to patch this, we hold the + // // highest belief that everything we create should be trustless. + // // Team JUST, The name you shouldn't have to trust. + // _p3d = _com; + // _com = 0; + // } + + // pay 1% out to FoMo3D short + uint256 _leader = _Eth / 20; + //otherF3D_.potSwap.value(_long)(); + + // distribute share to affiliate + uint256 _aff = _Eth / 10; + + // decide what to do with affiliate share of fees + // affiliate must not be self, and must have a name registered + if (_AffID != _PID && plyr_[_AffID].name != '') { + plyr_[_AffID].aff = _aff.add(plyr_[_AffID].aff); + emit F3Devents.OnAffiliatePayout(_AffID, plyr_[_AffID].addr, plyr_[_AffID].name, _RID, _PID, _aff, now); + } else { + _leader =_leader.add(_aff); + } + + leaderWallets[_Team].transfer(_leader); + + // pay out p3d + // _p3d = _p3d.add((_eth.mul(fees_[_Team].p3d)) / (100)); + // if (_p3d > 0) + // { + // // deposit to divies contract + // Divies.deposit.value(_p3d)(); + + // // set up event data + // _eventData_.P3DAmount = _p3d.add(_eventData_.P3DAmount); + // } + + return(_EventData_); + } + + function potSwap() + external + payable + { + // setup local rID + uint256 _RID = rID_ + 1; + + round_[_RID].pot = round_[_RID].pot.add(msg.value); + emit F3Devents.OnPotSwapDeposit(_RID, msg.value); + } + + /** + * @dev distributes eth based on fees to gen and pot + */ + function distributeInternal(uint256 _RID, uint256 _PID, uint256 _Eth, uint256 _Team, uint256 _Keys, F3Ddatasets.EventReturns memory _EventData_) + private + returns(F3Ddatasets.EventReturns) + { + // calculate gen share + uint256 _gen = (_Eth.mul(fees_[_Team].gen)) / 100; + + // toss 1% into airdrop pot + uint256 _air = (_Eth / 100); + airDropPot_ = airDropPot_.add(_air); + + // update eth balance (eth = eth - (com share + pot swap share + aff share + p3d share + airdrop pot share)) + _Eth = _Eth.sub(((_Eth.mul(26)) / 100).add((_Eth.mul(fees_[_Team].p3d)) / 100)); + + // calculate pot + uint256 _pot = _Eth.sub(_gen); + + // distribute gen share (thats what updateMasks() does) and adjust + // balances for dust. + uint256 _dust = updateMasks(_RID, _PID, _gen, _Keys); + if (_dust > 0) + _gen = _gen.sub(_dust); + + // add eth to pot + round_[_RID].pot = _pot.add(_dust).add(round_[_RID].pot); + + // set up event data + _EventData_.genAmount = _gen.add(_EventData_.genAmount); + _EventData_.potAmount = _pot; + + return(_EventData_); + } + + /** + * @dev updates masks for round and player when keys are bought + * @return dust left over + */ + function updateMasks(uint256 _RID, uint256 _PID, uint256 _Gen, uint256 _Keys) + private + returns(uint256) + { + /* MASKING NOTES + earnings masks are a tricky thing for people to wrap their minds around. + the basic thing to understand here. is were going to have a global + tracker based on profit per share for each round, that increases in + relevant proportion to the increase in share supply. + + the player will have an additional mask that basically says "based + on the rounds mask, my shares, and how much i've already withdrawn, + how much is still owed to me?" + */ + + // calc profit per key & round mask based on this buy: (dust goes to pot) + uint256 _ppt = (_Gen.mul(1000000000000000000)) / (round_[_RID].keys); + round_[_RID].mask = _ppt.add(round_[_RID].mask); + + // calculate player earning from their own buy (only based on the keys + // they just bought). & update player earnings mask + uint256 _pearn = (_ppt.mul(_Keys)) / (1000000000000000000); + plyrRnds_[_PID][_RID].mask = (((round_[_RID].mask.mul(_Keys)) / (1000000000000000000)).sub(_pearn)).add(plyrRnds_[_PID][_RID].mask); + + // calculate & return dust + return(_Gen.sub((_ppt.mul(round_[_RID].keys)) / (1000000000000000000))); + } + + /** + * @dev adds up unmasked earnings, & vault earnings, sets them all to 0 + * @return earnings in wei format + */ + function withdrawEarnings(uint256 _PID) + private + returns(uint256) + { + // update gen vault + updateGenVault(_PID, plyr_[_PID].lrnd); + + // from vaults + uint256 _earnings = (plyr_[_PID].win).add(plyr_[_PID].gen).add(plyr_[_PID].aff); + if (_earnings > 0) + { + plyr_[_PID].win = 0; + plyr_[_PID].gen = 0; + plyr_[_PID].aff = 0; + } + + return(_earnings); + } + + /** + * @dev prepares compression data and fires event for buy or reload tx's + */ + function endTx(uint256 _PID, uint256 _Team, uint256 _Eth, uint256 _Keys, F3Ddatasets.EventReturns memory _EventData_) + private + { + _EventData_.compressedData = _EventData_.compressedData + (now * 1000000000000000000) + (_Team * 100000000000000000000000000000); + _EventData_.compressedIDs = _EventData_.compressedIDs + _PID + (rID_ * 10000000000000000000000000000000000000000000000000000); + + emit F3Devents.OnEndTx + ( + _EventData_.compressedData, + _EventData_.compressedIDs, + plyr_[_PID].name, + msg.sender, + _Eth, + _Keys, + _EventData_.winnerAddr, + _EventData_.winnerName, + _EventData_.amountWon, + _EventData_.newPot, + _EventData_.P3DAmount, + _EventData_.genAmount, + _EventData_.potAmount, + airDropPot_ + ); + } +//============================================================================== +// (~ _ _ _._|_ . +// _)(/_(_|_|| | | \/ . +//====================/========================================================= + /** upon contract deploy, it will be deactivated. this is a one time + * use function that will activate the contract. we do this so devs + * have time to set things up on the web end **/ + bool public activated_ = false; + function activate() + onlyDevs() + external + { + // make sure that its been linked. + // can only be ran once + require(activated_ == false, "fomo3d already activated"); + + // activate the contract + activated_ = true; + + // lets start first round + rID_ = 1; + round_[1].strt = now + RNDEXTRA_ - RNDGAP_; + round_[1].end = now + RNDINIT_ + RNDEXTRA_; + } +} diff --git a/utils/slither_format/tests/real_world/0xbf45f4280cfbe7c2d2515a7d984b8c71c15e82b7_EnclavesDEXProxy.sol b/utils/slither_format/tests/real_world/0xbf45f4280cfbe7c2d2515a7d984b8c71c15e82b7_EnclavesDEXProxy.sol new file mode 100644 index 000000000..4afe40fec --- /dev/null +++ b/utils/slither_format/tests/real_world/0xbf45f4280cfbe7c2d2515a7d984b8c71c15e82b7_EnclavesDEXProxy.sol @@ -0,0 +1,286 @@ +pragma solidity ^0.4.18; + +// File: contracts/EtherDeltaI.sol + +contract EtherDeltaI { + + uint public feeMake; //percentage times (1 ether) + uint public feeTake; //percentage times (1 ether) + + mapping (address => mapping (address => uint)) public tokens; //mapping of token addresses to mapping of account balances (token=0 means Ether) + mapping (address => mapping (bytes32 => bool)) public orders; //mapping of user accounts to mapping of order hashes to booleans (true = submitted by user, equivalent to offchain signature) + mapping (address => mapping (bytes32 => uint)) public orderFills; //mapping of user accounts to mapping of order hashes to uints (amount of order that has been filled) + + function deposit() payable; + + function withdraw(uint amount); + + function depositToken(address token, uint amount); + + function withdrawToken(address token, uint amount); + + function balanceOf(address token, address user) constant returns (uint); + + function order(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce); + + function trade(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s, uint amount); + + function testTrade(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s, uint amount, address sender) constant returns(bool); + + function availableVolume(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s) constant returns(uint); + + function amountFilled(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s) constant returns(uint); + + function cancelOrder(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, uint8 v, bytes32 r, bytes32 s); + +} + +// File: contracts/KindMath.sol + +/** + * @title KindMath + * @dev Math operations with safety checks that fail + */ +library KindMath { + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a * b; + require(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) { + require(b <= a); + return a - b; + } + + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + require(c >= a); + return c; + } +} + +// File: contracts/KeyValueStorage.sol + +contract KeyValueStorage { + + mapping(address => mapping(bytes32 => uint256)) _uintStorage; + mapping(address => mapping(bytes32 => address)) _addressStorage; + mapping(address => mapping(bytes32 => bool)) _boolStorage; + mapping(address => mapping(bytes32 => bytes32)) _bytes32Storage; + + /**** Get Methods ***********/ + + function getAddress(bytes32 key) public view returns (address) { + return _addressStorage[msg.sender][key]; + } + + function getUint(bytes32 key) public view returns (uint) { + return _uintStorage[msg.sender][key]; + } + + function getBool(bytes32 key) public view returns (bool) { + return _boolStorage[msg.sender][key]; + } + + function getBytes32(bytes32 key) public view returns (bytes32) { + return _bytes32Storage[msg.sender][key]; + } + + /**** Set Methods ***********/ + + function setAddress(bytes32 key, address value) public { + _addressStorage[msg.sender][key] = value; + } + + function setUint(bytes32 key, uint value) public { + _uintStorage[msg.sender][key] = value; + } + + function setBool(bytes32 key, bool value) public { + _boolStorage[msg.sender][key] = value; + } + + function setBytes32(bytes32 key, bytes32 value) public { + _bytes32Storage[msg.sender][key] = value; + } + + /**** Delete Methods ***********/ + + function deleteAddress(bytes32 key) public { + delete _addressStorage[msg.sender][key]; + } + + function deleteUint(bytes32 key) public { + delete _uintStorage[msg.sender][key]; + } + + function deleteBool(bytes32 key) public { + delete _boolStorage[msg.sender][key]; + } + + function deleteBytes32(bytes32 key) public { + delete _bytes32Storage[msg.sender][key]; + } + +} + +// File: contracts/StorageStateful.sol + +contract StorageStateful { + KeyValueStorage public keyValueStorage; +} + +// File: contracts/StorageConsumer.sol + +contract StorageConsumer is StorageStateful { + function StorageConsumer(address _storageAddress) public { + require(_storageAddress != address(0)); + keyValueStorage = KeyValueStorage(_storageAddress); + } +} + +// File: contracts/TokenI.sol + +contract Token { + /// @return total amount of tokens + function totalSupply() public returns (uint256); + + /// @param _owner The address from which the balance will be retrieved + /// @return The balance + function balanceOf(address _owner) public returns (uint256); + + /// @notice send `_value` token to `_to` from `msg.sender` + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + /// @return Whether the transfer was successful or not + function transfer(address _to, uint256 _value) public returns (bool); + + /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from` + /// @param _from The address of the sender + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + /// @return Whether the transfer was successful or not + function transferFrom(address _from, address _to, uint256 _value) public returns (bool); + + /// @notice `msg.sender` approves `_addr` to spend `_value` tokens + /// @param _spender The address of the account able to transfer the tokens + /// @param _value The amount of wei to be approved for transfer + /// @return Whether the approval was successful or not + function approve(address _spender, uint256 _value) public returns (bool); + + /// @param _owner The address of the account owning tokens + /// @param _spender The address of the account able to transfer the tokens + /// @return Amount of remaining tokens allowed to spent + function allowance(address _owner, address _spender) public returns (uint256); + + event Transfer(address indexed _from, address indexed _to, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _value); + + uint256 public decimals; + string public name; +} + +// File: contracts/EnclavesDEXProxy.sol + +contract EnclavesDEXProxy is StorageConsumer { + using KindMath for uint256; + + address public admin; //the admin address + address public feeAccount; //the account that will receive fees + + struct EtherDeltaInfo { + uint256 feeMake; + uint256 feeTake; + } + + EtherDeltaInfo public etherDeltaInfo; + + uint256 public feeTake; //percentage times 1 ether + uint256 public feeAmountThreshold; //gasPrice amount under which no fees are charged + + address public etherDelta; + + bool public useEIP712 = true; + bytes32 public tradeABIHash; + bytes32 public withdrawABIHash; + + bool freezeTrading; + bool depositTokenLock; + + mapping (address => mapping (uint256 => bool)) nonceCheck; + + mapping (address => mapping (address => uint256)) public tokens; //mapping of token addresses to mapping of account balances (token=0 means Ether) + mapping (address => mapping (bytes32 => bool)) public orders; //mapping of user accounts to mapping of order hashes to booleans (true = submitted by user, equivalent to offchain signature) + mapping (address => mapping (bytes32 => uint256)) public orderFills; //mapping of user accounts to mapping of order hashes to uints (amount of order that has been filled) + + address internal implementation; + address public proposedImplementation; + uint256 public proposedTimestamp; + + event Upgraded(address _implementation); + event UpgradedProposed(address _proposedImplementation, uint256 _proposedTimestamp); + + modifier onlyAdmin { + require(msg.sender == admin); + _; + } + + function EnclavesDEXProxy(address _storageAddress, address _implementation, address _admin, address _feeAccount, uint256 _feeTake, uint256 _feeAmountThreshold, address _etherDelta, bytes32 _tradeABIHash, bytes32 _withdrawABIHash) public + StorageConsumer(_storageAddress) + { + require(_implementation != address(0)); + implementation = _implementation; + admin = _admin; + feeAccount = _feeAccount; + feeTake = _feeTake; + feeAmountThreshold = _feeAmountThreshold; + etherDelta = _etherDelta; + tradeABIHash = _tradeABIHash; + withdrawABIHash = _withdrawABIHash; + etherDeltaInfo.feeMake = EtherDeltaI(etherDelta).feeMake(); + etherDeltaInfo.feeTake = EtherDeltaI(etherDelta).feeTake(); + } + + function getImplementation() public view returns(address) { + return implementation; + } + + function proposeUpgrade(address _proposedImplementation) public onlyAdmin { + require(implementation != _proposedImplementation); + require(_proposedImplementation != address(0)); + proposedImplementation = _proposedImplementation; + proposedTimestamp = now + 2 weeks; + UpgradedProposed(proposedImplementation, now); + } + + function upgrade() public onlyAdmin { + require(proposedImplementation != address(0)); + require(proposedTimestamp < now); + implementation = proposedImplementation; + Upgraded(implementation); + } + + function () payable public { + bytes memory data = msg.data; + address impl = getImplementation(); + + assembly { + let result := delegatecall(gas, impl, add(data, 0x20), mload(data), 0, 0) + let size := returndatasize + let ptr := mload(0x40) + returndatacopy(ptr, 0, size) + switch result + case 0 { revert(ptr, size) } + default { return(ptr, size) } + } + } + +} \ No newline at end of file diff --git a/utils/slither_format/tests/real_world/0xc6725ae749677f21e4d8f85f41cfb6de49b9db29_BancorConverter.sol b/utils/slither_format/tests/real_world/0xc6725ae749677f21e4d8f85f41cfb6de49b9db29_BancorConverter.sol new file mode 100644 index 000000000..d69f55f04 --- /dev/null +++ b/utils/slither_format/tests/real_world/0xc6725ae749677f21e4d8f85f41cfb6de49b9db29_BancorConverter.sol @@ -0,0 +1,1011 @@ +pragma solidity ^0.4.18; + +/* + Utilities & Common Modifiers +*/ +contract Utils { + /** + constructor + */ + function Utils() public { + } + + // verifies that an amount is greater than zero + modifier greaterThanZero(uint256 _amount) { + require(_amount > 0); + _; + } + + // validates an address - currently only checks that it isn't null + modifier validAddress(address _address) { + require(_address != address(0)); + _; + } + + // verifies that the address is different than this contract address + modifier notThis(address _address) { + require(_address != address(this)); + _; + } + + // Overflow protected math functions + + /** + @dev returns the sum of _x and _y, asserts if the calculation overflows + + @param _x value 1 + @param _y value 2 + + @return sum + */ + function safeAdd(uint256 _x, uint256 _y) internal pure returns (uint256) { + uint256 z = _x + _y; + assert(z >= _x); + return z; + } + + /** + @dev returns the difference of _x minus _y, asserts if the subtraction results in a negative number + + @param _x minuend + @param _y subtrahend + + @return difference + */ + function safeSub(uint256 _x, uint256 _y) internal pure returns (uint256) { + assert(_x >= _y); + return _x - _y; + } + + /** + @dev returns the product of multiplying _x by _y, asserts if the calculation overflows + + @param _x factor 1 + @param _y factor 2 + + @return product + */ + function safeMul(uint256 _x, uint256 _y) internal pure returns (uint256) { + uint256 z = _x * _y; + assert(_x == 0 || z / _x == _y); + return z; + } +} + +/* + Owned contract interface +*/ +contract IOwned { + // this function isn't abstract since the compiler emits automatically generated getter functions as external + function owner() public view returns (address) {} + + function transferOwnership(address _newOwner) public; + function acceptOwnership() public; +} + +/* + Provides support and utilities for contract ownership +*/ +contract Owned is IOwned { + address public owner; + address public newOwner; + + event OwnerUpdate(address indexed _prevOwner, address indexed _newOwner); + + /** + @dev constructor + */ + function Owned() public { + owner = msg.sender; + } + + // allows execution by the owner only + modifier ownerOnly { + assert(msg.sender == owner); + _; + } + + /** + @dev allows transferring the contract ownership + the new owner still needs to accept the transfer + can only be called by the contract owner + + @param _newOwner new contract owner + */ + function transferOwnership(address _newOwner) public ownerOnly { + require(_newOwner != owner); + newOwner = _newOwner; + } + + /** + @dev used by a new owner to accept an ownership transfer + */ + function acceptOwnership() public { + require(msg.sender == newOwner); + OwnerUpdate(owner, newOwner); + owner = newOwner; + newOwner = address(0); + } +} + +/* + Provides support and utilities for contract management +*/ +contract Managed { + address public manager; + address public newManager; + + event ManagerUpdate(address indexed _prevManager, address indexed _newManager); + + /** + @dev constructor + */ + function Managed() public { + manager = msg.sender; + } + + // allows execution by the manager only + modifier managerOnly { + assert(msg.sender == manager); + _; + } + + /** + @dev allows transferring the contract management + the new manager still needs to accept the transfer + can only be called by the contract manager + + @param _newManager new contract manager + */ + function transferManagement(address _newManager) public managerOnly { + require(_newManager != manager); + newManager = _newManager; + } + + /** + @dev used by a new manager to accept a management transfer + */ + function acceptManagement() public { + require(msg.sender == newManager); + ManagerUpdate(manager, newManager); + manager = newManager; + newManager = address(0); + } +} + +/* + ERC20 Standard Token interface +*/ +contract IERC20Token { + // these functions aren't abstract since the compiler emits automatically generated getter functions as external + function name() public view returns (string) {} + function symbol() public view returns (string) {} + function decimals() public view returns (uint8) {} + function totalSupply() public view returns (uint256) {} + function balanceOf(address _owner) public view returns (uint256) { _owner; } + function allowance(address _owner, address _spender) public view returns (uint256) { _owner; _spender; } + + function transfer(address _to, uint256 _value) public returns (bool success); + function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); + function approve(address _spender, uint256 _value) public returns (bool success); +} + +/* + Smart Token interface +*/ +contract ISmartToken is IOwned, IERC20Token { + function disableTransfers(bool _disable) public; + function issue(address _to, uint256 _amount) public; + function destroy(address _from, uint256 _amount) public; +} + +/* + Token Holder interface +*/ +contract ITokenHolder is IOwned { + function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public; +} + +/* + We consider every contract to be a 'token holder' since it's currently not possible + for a contract to deny receiving tokens. + + The TokenHolder's contract sole purpose is to provide a safety mechanism that allows + the owner to send tokens that were sent to the contract by mistake back to their sender. +*/ +contract TokenHolder is ITokenHolder, Owned, Utils { + /** + @dev constructor + */ + function TokenHolder() public { + } + + /** + @dev withdraws tokens held by the contract and sends them to an account + can only be called by the owner + + @param _token ERC20 token contract address + @param _to account to receive the new amount + @param _amount amount to withdraw + */ + function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) + public + ownerOnly + validAddress(_token) + validAddress(_to) + notThis(_to) + { + assert(_token.transfer(_to, _amount)); + } +} + +/* + The smart token controller is an upgradable part of the smart token that allows + more functionality as well as fixes for bugs/exploits. + Once it accepts ownership of the token, it becomes the token's sole controller + that can execute any of its functions. + + To upgrade the controller, ownership must be transferred to a new controller, along with + any relevant data. + + The smart token must be set on construction and cannot be changed afterwards. + Wrappers are provided (as opposed to a single 'execute' function) for each of the token's functions, for easier access. + + Note that the controller can transfer token ownership to a new controller that + doesn't allow executing any function on the token, for a trustless solution. + Doing that will also remove the owner's ability to upgrade the controller. +*/ +contract SmartTokenController is TokenHolder { + ISmartToken public token; // smart token + + /** + @dev constructor + */ + function SmartTokenController(ISmartToken _token) + public + validAddress(_token) + { + token = _token; + } + + // ensures that the controller is the token's owner + modifier active() { + assert(token.owner() == address(this)); + _; + } + + // ensures that the controller is not the token's owner + modifier inactive() { + assert(token.owner() != address(this)); + _; + } + + /** + @dev allows transferring the token ownership + the new owner still need to accept the transfer + can only be called by the contract owner + + @param _newOwner new token owner + */ + function transferTokenOwnership(address _newOwner) public ownerOnly { + token.transferOwnership(_newOwner); + } + + /** + @dev used by a new owner to accept a token ownership transfer + can only be called by the contract owner + */ + function acceptTokenOwnership() public ownerOnly { + token.acceptOwnership(); + } + + /** + @dev disables/enables token transfers + can only be called by the contract owner + + @param _disable true to disable transfers, false to enable them + */ + function disableTokenTransfers(bool _disable) public ownerOnly { + token.disableTransfers(_disable); + } + + /** + @dev withdraws tokens held by the controller and sends them to an account + can only be called by the owner + + @param _token ERC20 token contract address + @param _to account to receive the new amount + @param _amount amount to withdraw + */ + function withdrawFromToken( + IERC20Token _token, + address _to, + uint256 _amount + ) + public + ownerOnly + { + ITokenHolder(token).withdrawTokens(_token, _to, _amount); + } +} + +/* + Bancor Formula interface +*/ +contract IBancorFormula { + function calculatePurchaseReturn(uint256 _supply, uint256 _connectorBalance, uint32 _connectorWeight, uint256 _depositAmount) public view returns (uint256); + function calculateSaleReturn(uint256 _supply, uint256 _connectorBalance, uint32 _connectorWeight, uint256 _sellAmount) public view returns (uint256); + function calculateCrossConnectorReturn(uint256 _connector1Balance, uint32 _connector1Weight, uint256 _connector2Balance, uint32 _connector2Weight, uint256 _amount) public view returns (uint256); +} + +/* + Bancor Gas Price Limit interface +*/ +contract IBancorGasPriceLimit { + function gasPrice() public view returns (uint256) {} + function validateGasPrice(uint256) public view; +} + +/* + Bancor Quick Converter interface +*/ +contract IBancorQuickConverter { + function convert(IERC20Token[] _path, uint256 _amount, uint256 _minReturn) public payable returns (uint256); + function convertFor(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, address _for) public payable returns (uint256); + function convertForPrioritized(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, address _for, uint256 _block, uint256 _nonce, uint8 _v, bytes32 _r, bytes32 _s) public payable returns (uint256); +} + +/* + Bancor Converter Extensions interface +*/ +contract IBancorConverterExtensions { + function formula() public view returns (IBancorFormula) {} + function gasPriceLimit() public view returns (IBancorGasPriceLimit) {} + function quickConverter() public view returns (IBancorQuickConverter) {} +} + +/* + EIP228 Token Converter interface +*/ +contract ITokenConverter { + function convertibleTokenCount() public view returns (uint16); + function convertibleToken(uint16 _tokenIndex) public view returns (address); + function getReturn(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount) public view returns (uint256); + function convert(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256); + // deprecated, backward compatibility + function change(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256); +} + +/* + Bancor Converter v0.8 + + The Bancor version of the token converter, allows conversion between a smart token and other ERC20 tokens and between different ERC20 tokens and themselves. + + ERC20 connector balance can be virtual, meaning that the calculations are based on the virtual balance instead of relying on + the actual connector balance. This is a security mechanism that prevents the need to keep a very large (and valuable) balance in a single contract. + + The converter is upgradable (just like any SmartTokenController). + + WARNING: It is NOT RECOMMENDED to use the converter with Smart Tokens that have less than 8 decimal digits + or with very small numbers because of precision loss + + + Open issues: + - Front-running attacks are currently mitigated by the following mechanisms: + - minimum return argument for each conversion provides a way to define a minimum/maximum price for the transaction + - gas price limit prevents users from having control over the order of execution + Other potential solutions might include a commit/reveal based schemes + - Possibly add getters for the connector fields so that the client won't need to rely on the order in the struct +*/ +contract BancorConverter is ITokenConverter, SmartTokenController, Managed { + uint32 private constant MAX_WEIGHT = 1000000; + uint32 private constant MAX_CONVERSION_FEE = 1000000; + + struct Connector { + uint256 virtualBalance; // connector virtual balance + uint32 weight; // connector weight, represented in ppm, 1-1000000 + bool isVirtualBalanceEnabled; // true if virtual balance is enabled, false if not + bool isPurchaseEnabled; // is purchase of the smart token enabled with the connector, can be set by the owner + bool isSet; // used to tell if the mapping element is defined + } + + string public version = '0.8'; + string public converterType = 'bancor'; + + IBancorConverterExtensions public extensions; // bancor converter extensions contract + IERC20Token[] public connectorTokens; // ERC20 standard token addresses + IERC20Token[] public quickBuyPath; // conversion path that's used in order to buy the token with ETH + mapping (address => Connector) public connectors; // connector token addresses -> connector data + uint32 private totalConnectorWeight = 0; // used to efficiently prevent increasing the total connector weight above 100% + uint32 public maxConversionFee = 0; // maximum conversion fee for the lifetime of the contract, represented in ppm, 0...1000000 (0 = no fee, 100 = 0.01%, 1000000 = 100%) + uint32 public conversionFee = 0; // current conversion fee, represented in ppm, 0...maxConversionFee + bool public conversionsEnabled = true; // true if token conversions is enabled, false if not + IERC20Token[] private convertPath; + + // triggered when a conversion between two tokens occurs (TokenConverter event) + event Conversion(address indexed _fromToken, address indexed _toToken, address indexed _trader, uint256 _amount, uint256 _return, + int256 _conversionFee, uint256 _currentPriceN, uint256 _currentPriceD); + // triggered when the conversion fee is updated + event ConversionFeeUpdate(uint32 _prevFee, uint32 _newFee); + + /** + @dev constructor + + @param _token smart token governed by the converter + @param _extensions address of a bancor converter extensions contract + @param _maxConversionFee maximum conversion fee, represented in ppm + @param _connectorToken optional, initial connector, allows defining the first connector at deployment time + @param _connectorWeight optional, weight for the initial connector + */ + function BancorConverter(ISmartToken _token, IBancorConverterExtensions _extensions, uint32 _maxConversionFee, IERC20Token _connectorToken, uint32 _connectorWeight) + public + SmartTokenController(_token) + validAddress(_extensions) + validMaxConversionFee(_maxConversionFee) + { + extensions = _extensions; + maxConversionFee = _maxConversionFee; + + if (_connectorToken != address(0)) + addConnector(_connectorToken, _connectorWeight, false); + } + + // validates a connector token address - verifies that the address belongs to one of the connector tokens + modifier validConnector(IERC20Token _address) { + require(connectors[_address].isSet); + _; + } + + // validates a token address - verifies that the address belongs to one of the convertible tokens + modifier validToken(IERC20Token _address) { + require(_address == token || connectors[_address].isSet); + _; + } + + // validates maximum conversion fee + modifier validMaxConversionFee(uint32 _conversionFee) { + require(_conversionFee >= 0 && _conversionFee <= MAX_CONVERSION_FEE); + _; + } + + // validates conversion fee + modifier validConversionFee(uint32 _conversionFee) { + require(_conversionFee >= 0 && _conversionFee <= maxConversionFee); + _; + } + + // validates connector weight range + modifier validConnectorWeight(uint32 _weight) { + require(_weight > 0 && _weight <= MAX_WEIGHT); + _; + } + + // validates a conversion path - verifies that the number of elements is odd and that maximum number of 'hops' is 10 + modifier validConversionPath(IERC20Token[] _path) { + require(_path.length > 2 && _path.length <= (1 + 2 * 10) && _path.length % 2 == 1); + _; + } + + // allows execution only when conversions aren't disabled + modifier conversionsAllowed { + assert(conversionsEnabled); + _; + } + + // allows execution only for owner or manager + modifier ownerOrManagerOnly { + require(msg.sender == owner || msg.sender == manager); + _; + } + + // allows execution only for quick convreter + modifier quickConverterOnly { + require(msg.sender == address(extensions.quickConverter())); + _; + } + + /** + @dev returns the number of connector tokens defined + + @return number of connector tokens + */ + function connectorTokenCount() public view returns (uint16) { + return uint16(connectorTokens.length); + } + + /** + @dev returns the number of convertible tokens supported by the contract + note that the number of convertible tokens is the number of connector token, plus 1 (that represents the smart token) + + @return number of convertible tokens + */ + function convertibleTokenCount() public view returns (uint16) { + return connectorTokenCount() + 1; + } + + /** + @dev given a convertible token index, returns its contract address + + @param _tokenIndex convertible token index + + @return convertible token address + */ + function convertibleToken(uint16 _tokenIndex) public view returns (address) { + if (_tokenIndex == 0) + return token; + return connectorTokens[_tokenIndex - 1]; + } + + /* + @dev allows the owner to update the extensions contract address + + @param _extensions address of a bancor converter extensions contract + */ + function setExtensions(IBancorConverterExtensions _extensions) + public + ownerOnly + validAddress(_extensions) + notThis(_extensions) + { + extensions = _extensions; + } + + /* + @dev allows the manager to update the quick buy path + + @param _path new quick buy path, see conversion path format in the BancorQuickConverter contract + */ + function setQuickBuyPath(IERC20Token[] _path) + public + ownerOnly + validConversionPath(_path) + { + quickBuyPath = _path; + } + + /* + @dev allows the manager to clear the quick buy path + */ + function clearQuickBuyPath() public ownerOnly { + quickBuyPath.length = 0; + } + + /** + @dev returns the length of the quick buy path array + + @return quick buy path length + */ + function getQuickBuyPathLength() public view returns (uint256) { + return quickBuyPath.length; + } + + /** + @dev disables the entire conversion functionality + this is a safety mechanism in case of a emergency + can only be called by the manager + + @param _disable true to disable conversions, false to re-enable them + */ + function disableConversions(bool _disable) public ownerOrManagerOnly { + conversionsEnabled = !_disable; + } + + /** + @dev updates the current conversion fee + can only be called by the manager + + @param _conversionFee new conversion fee, represented in ppm + */ + function setConversionFee(uint32 _conversionFee) + public + ownerOrManagerOnly + validConversionFee(_conversionFee) + { + ConversionFeeUpdate(conversionFee, _conversionFee); + conversionFee = _conversionFee; + } + + /* + @dev returns the conversion fee amount for a given return amount + + @return conversion fee amount + */ + function getConversionFeeAmount(uint256 _amount) public view returns (uint256) { + return safeMul(_amount, conversionFee) / MAX_CONVERSION_FEE; + } + + /** + @dev defines a new connector for the token + can only be called by the owner while the converter is inactive + + @param _token address of the connector token + @param _weight constant connector weight, represented in ppm, 1-1000000 + @param _enableVirtualBalance true to enable virtual balance for the connector, false to disable it + */ + function addConnector(IERC20Token _token, uint32 _weight, bool _enableVirtualBalance) + public + ownerOnly + inactive + validAddress(_token) + notThis(_token) + validConnectorWeight(_weight) + { + require(_token != token && !connectors[_token].isSet && totalConnectorWeight + _weight <= MAX_WEIGHT); // validate input + + connectors[_token].virtualBalance = 0; + connectors[_token].weight = _weight; + connectors[_token].isVirtualBalanceEnabled = _enableVirtualBalance; + connectors[_token].isPurchaseEnabled = true; + connectors[_token].isSet = true; + connectorTokens.push(_token); + totalConnectorWeight += _weight; + } + + /** + @dev updates one of the token connectors + can only be called by the owner + + @param _connectorToken address of the connector token + @param _weight constant connector weight, represented in ppm, 1-1000000 + @param _enableVirtualBalance true to enable virtual balance for the connector, false to disable it + @param _virtualBalance new connector's virtual balance + */ + function updateConnector(IERC20Token _connectorToken, uint32 _weight, bool _enableVirtualBalance, uint256 _virtualBalance) + public + ownerOnly + validConnector(_connectorToken) + validConnectorWeight(_weight) + { + Connector storage connector = connectors[_connectorToken]; + require(totalConnectorWeight - connector.weight + _weight <= MAX_WEIGHT); // validate input + + totalConnectorWeight = totalConnectorWeight - connector.weight + _weight; + connector.weight = _weight; + connector.isVirtualBalanceEnabled = _enableVirtualBalance; + connector.virtualBalance = _virtualBalance; + } + + /** + @dev disables purchasing with the given connector token in case the connector token got compromised + can only be called by the owner + note that selling is still enabled regardless of this flag and it cannot be disabled by the owner + + @param _connectorToken connector token contract address + @param _disable true to disable the token, false to re-enable it + */ + function disableConnectorPurchases(IERC20Token _connectorToken, bool _disable) + public + ownerOnly + validConnector(_connectorToken) + { + connectors[_connectorToken].isPurchaseEnabled = !_disable; + } + + /** + @dev returns the connector's virtual balance if one is defined, otherwise returns the actual balance + + @param _connectorToken connector token contract address + + @return connector balance + */ + function getConnectorBalance(IERC20Token _connectorToken) + public + view + validConnector(_connectorToken) + returns (uint256) + { + Connector storage connector = connectors[_connectorToken]; + return connector.isVirtualBalanceEnabled ? connector.virtualBalance : _connectorToken.balanceOf(this); + } + + /** + @dev returns the expected return for converting a specific amount of _fromToken to _toToken + + @param _fromToken ERC20 token to convert from + @param _toToken ERC20 token to convert to + @param _amount amount to convert, in fromToken + + @return expected conversion return amount + */ + function getReturn(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount) public view returns (uint256) { + require(_fromToken != _toToken); // validate input + + // conversion between the token and one of its connectors + if (_toToken == token) + return getPurchaseReturn(_fromToken, _amount); + else if (_fromToken == token) + return getSaleReturn(_toToken, _amount); + + // conversion between 2 connectors + uint256 purchaseReturnAmount = getPurchaseReturn(_fromToken, _amount); + return getSaleReturn(_toToken, purchaseReturnAmount, safeAdd(token.totalSupply(), purchaseReturnAmount)); + } + + /** + @dev returns the expected return for buying the token for a connector token + + @param _connectorToken connector token contract address + @param _depositAmount amount to deposit (in the connector token) + + @return expected purchase return amount + */ + function getPurchaseReturn(IERC20Token _connectorToken, uint256 _depositAmount) + public + view + active + validConnector(_connectorToken) + returns (uint256) + { + Connector storage connector = connectors[_connectorToken]; + require(connector.isPurchaseEnabled); // validate input + + uint256 tokenSupply = token.totalSupply(); + uint256 connectorBalance = getConnectorBalance(_connectorToken); + uint256 amount = extensions.formula().calculatePurchaseReturn(tokenSupply, connectorBalance, connector.weight, _depositAmount); + + // deduct the fee from the return amount + uint256 feeAmount = getConversionFeeAmount(amount); + return safeSub(amount, feeAmount); + } + + /** + @dev returns the expected return for selling the token for one of its connector tokens + + @param _connectorToken connector token contract address + @param _sellAmount amount to sell (in the smart token) + + @return expected sale return amount + */ + function getSaleReturn(IERC20Token _connectorToken, uint256 _sellAmount) public view returns (uint256) { + return getSaleReturn(_connectorToken, _sellAmount, token.totalSupply()); + } + + /** + @dev converts a specific amount of _fromToken to _toToken + + @param _fromToken ERC20 token to convert from + @param _toToken ERC20 token to convert to + @param _amount amount to convert, in fromToken + @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero + + @return conversion return amount + */ + function convertInternal(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public quickConverterOnly returns (uint256) { + require(_fromToken != _toToken); // validate input + + // conversion between the token and one of its connectors + if (_toToken == token) + return buy(_fromToken, _amount, _minReturn); + else if (_fromToken == token) + return sell(_toToken, _amount, _minReturn); + + // conversion between 2 connectors + uint256 purchaseAmount = buy(_fromToken, _amount, 1); + return sell(_toToken, purchaseAmount, _minReturn); + } + + /** + @dev converts a specific amount of _fromToken to _toToken + + @param _fromToken ERC20 token to convert from + @param _toToken ERC20 token to convert to + @param _amount amount to convert, in fromToken + @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero + + @return conversion return amount + */ + function convert(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256) { + convertPath = [_fromToken, token, _toToken]; + return quickConvert(convertPath, _amount, _minReturn); + } + + /** + @dev buys the token by depositing one of its connector tokens + + @param _connectorToken connector token contract address + @param _depositAmount amount to deposit (in the connector token) + @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero + + @return buy return amount + */ + function buy(IERC20Token _connectorToken, uint256 _depositAmount, uint256 _minReturn) + internal + conversionsAllowed + greaterThanZero(_minReturn) + returns (uint256) + { + uint256 amount = getPurchaseReturn(_connectorToken, _depositAmount); + require(amount != 0 && amount >= _minReturn); // ensure the trade gives something in return and meets the minimum requested amount + + // update virtual balance if relevant + Connector storage connector = connectors[_connectorToken]; + if (connector.isVirtualBalanceEnabled) + connector.virtualBalance = safeAdd(connector.virtualBalance, _depositAmount); + + // transfer _depositAmount funds from the caller in the connector token + assert(_connectorToken.transferFrom(msg.sender, this, _depositAmount)); + // issue new funds to the caller in the smart token + token.issue(msg.sender, amount); + + dispatchConversionEvent(_connectorToken, _depositAmount, amount, true); + return amount; + } + + /** + @dev sells the token by withdrawing from one of its connector tokens + + @param _connectorToken connector token contract address + @param _sellAmount amount to sell (in the smart token) + @param _minReturn if the conversion results in an amount smaller the minimum return - it is cancelled, must be nonzero + + @return sell return amount + */ + function sell(IERC20Token _connectorToken, uint256 _sellAmount, uint256 _minReturn) + internal + conversionsAllowed + greaterThanZero(_minReturn) + returns (uint256) + { + require(_sellAmount <= token.balanceOf(msg.sender)); // validate input + + uint256 amount = getSaleReturn(_connectorToken, _sellAmount); + require(amount != 0 && amount >= _minReturn); // ensure the trade gives something in return and meets the minimum requested amount + + uint256 tokenSupply = token.totalSupply(); + uint256 connectorBalance = getConnectorBalance(_connectorToken); + // ensure that the trade will only deplete the connector if the total supply is depleted as well + assert(amount < connectorBalance || (amount == connectorBalance && _sellAmount == tokenSupply)); + + // update virtual balance if relevant + Connector storage connector = connectors[_connectorToken]; + if (connector.isVirtualBalanceEnabled) + connector.virtualBalance = safeSub(connector.virtualBalance, amount); + + // destroy _sellAmount from the caller's balance in the smart token + token.destroy(msg.sender, _sellAmount); + // transfer funds to the caller in the connector token + // the transfer might fail if the actual connector balance is smaller than the virtual balance + assert(_connectorToken.transfer(msg.sender, amount)); + + dispatchConversionEvent(_connectorToken, _sellAmount, amount, false); + return amount; + } + + /** + @dev converts the token to any other token in the bancor network by following a predefined conversion path + note that when converting from an ERC20 token (as opposed to a smart token), allowance must be set beforehand + + @param _path conversion path, see conversion path format in the BancorQuickConverter contract + @param _amount amount to convert from (in the initial source token) + @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero + + @return tokens issued in return + */ + function quickConvert(IERC20Token[] _path, uint256 _amount, uint256 _minReturn) + public + payable + validConversionPath(_path) + returns (uint256) + { + return quickConvertPrioritized(_path, _amount, _minReturn, 0x0, 0x0, 0x0, 0x0, 0x0); + } + + /** + @dev converts the token to any other token in the bancor network by following a predefined conversion path + note that when converting from an ERC20 token (as opposed to a smart token), allowance must be set beforehand + + @param _path conversion path, see conversion path format in the BancorQuickConverter contract + @param _amount amount to convert from (in the initial source token) + @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero + @param _block if the current block exceeded the given parameter - it is cancelled + @param _nonce the nonce of the sender address + @param _v parameter that can be parsed from the transaction signature + @param _r parameter that can be parsed from the transaction signature + @param _s parameter that can be parsed from the transaction signature + + @return tokens issued in return + */ + function quickConvertPrioritized(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, uint256 _block, uint256 _nonce, uint8 _v, bytes32 _r, bytes32 _s) + public + payable + validConversionPath(_path) + returns (uint256) + { + IERC20Token fromToken = _path[0]; + IBancorQuickConverter quickConverter = extensions.quickConverter(); + + // we need to transfer the source tokens from the caller to the quick converter, + // so it can execute the conversion on behalf of the caller + if (msg.value == 0) { + // not ETH, send the source tokens to the quick converter + // if the token is the smart token, no allowance is required - destroy the tokens from the caller and issue them to the quick converter + if (fromToken == token) { + token.destroy(msg.sender, _amount); // destroy _amount tokens from the caller's balance in the smart token + token.issue(quickConverter, _amount); // issue _amount new tokens to the quick converter + } else { + // otherwise, we assume we already have allowance, transfer the tokens directly to the quick converter + assert(fromToken.transferFrom(msg.sender, quickConverter, _amount)); + } + } + + // execute the conversion and pass on the ETH with the call + return quickConverter.convertForPrioritized.value(msg.value)(_path, _amount, _minReturn, msg.sender, _block, _nonce, _v, _r, _s); + } + + // deprecated, backward compatibility + function change(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256) { + return convertInternal(_fromToken, _toToken, _amount, _minReturn); + } + + /** + @dev utility, returns the expected return for selling the token for one of its connector tokens, given a total supply override + + @param _connectorToken connector token contract address + @param _sellAmount amount to sell (in the smart token) + @param _totalSupply total token supply, overrides the actual token total supply when calculating the return + + @return sale return amount + */ + function getSaleReturn(IERC20Token _connectorToken, uint256 _sellAmount, uint256 _totalSupply) + private + view + active + validConnector(_connectorToken) + greaterThanZero(_totalSupply) + returns (uint256) + { + Connector storage connector = connectors[_connectorToken]; + uint256 connectorBalance = getConnectorBalance(_connectorToken); + uint256 amount = extensions.formula().calculateSaleReturn(_totalSupply, connectorBalance, connector.weight, _sellAmount); + + // deduct the fee from the return amount + uint256 feeAmount = getConversionFeeAmount(amount); + return safeSub(amount, feeAmount); + } + + /** + @dev helper, dispatches the Conversion event + The function also takes the tokens' decimals into account when calculating the current price + + @param _connectorToken connector token contract address + @param _amount amount purchased/sold (in the source token) + @param _returnAmount amount returned (in the target token) + @param isPurchase true if it's a purchase, false if it's a sale + */ + function dispatchConversionEvent(IERC20Token _connectorToken, uint256 _amount, uint256 _returnAmount, bool isPurchase) private { + Connector storage connector = connectors[_connectorToken]; + + // calculate the new price using the simple price formula + // price = connector balance / (supply * weight) + // weight is represented in ppm, so multiplying by 1000000 + uint256 connectorAmount = safeMul(getConnectorBalance(_connectorToken), MAX_WEIGHT); + uint256 tokenAmount = safeMul(token.totalSupply(), connector.weight); + + // normalize values + uint8 tokenDecimals = token.decimals(); + uint8 connectorTokenDecimals = _connectorToken.decimals(); + if (tokenDecimals != connectorTokenDecimals) { + if (tokenDecimals > connectorTokenDecimals) + connectorAmount = safeMul(connectorAmount, 10 ** uint256(tokenDecimals - connectorTokenDecimals)); + else + tokenAmount = safeMul(tokenAmount, 10 ** uint256(connectorTokenDecimals - tokenDecimals)); + } + + uint256 feeAmount = getConversionFeeAmount(_returnAmount); + // ensure that the fee is capped at 255 bits to prevent overflow when converting it to a signed int + assert(feeAmount <= 2 ** 255); + + if (isPurchase) + Conversion(_connectorToken, token, msg.sender, _amount, _returnAmount, int256(feeAmount), connectorAmount, tokenAmount); + else + Conversion(token, _connectorToken, msg.sender, _amount, _returnAmount, int256(feeAmount), tokenAmount, connectorAmount); + } + + /** + @dev fallback, buys the smart token with ETH + note that the purchase will use the price at the time of the purchase + */ + function() payable public { + quickConvert(quickBuyPath, msg.value, 1); + } +} \ No newline at end of file diff --git a/utils/slither_format/tests/real_world/0xf5ed2dc77f0d1ea7f106ecbd1850e406adc41b51_OceanToken.sol b/utils/slither_format/tests/real_world/0xf5ed2dc77f0d1ea7f106ecbd1850e406adc41b51_OceanToken.sol new file mode 100644 index 000000000..57830e096 --- /dev/null +++ b/utils/slither_format/tests/real_world/0xf5ed2dc77f0d1ea7f106ecbd1850e406adc41b51_OceanToken.sol @@ -0,0 +1,454 @@ +pragma solidity ^0.4.18; + +/** + + Copyright (c) 2018 The Ocean. + + Licensed under the MIT License: https://opensource.org/licenses/MIT. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +**/ + +/** + * @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. + */ + function Ownable() 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) public onlyOwner { + require(newOwner != address(0)); + OwnershipTransferred(owner, newOwner); + owner = newOwner; + } + +} + + + +/** + * @title ERC20Basic + * @dev Simpler version of ERC20 interface + * @dev see https://github.com/ethereum/EIPs/issues/179 + */ +contract ERC20Basic { + function totalSupply() public view returns (uint256); + 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 SafeMath + * @dev Math operations with safety checks that throw on error + */ +library SafeMath { + + /** + * @dev Multiplies two numbers, throws on overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + if (a == 0) { + return 0; + } + uint256 c = a * b; + assert(c / a == b); + return c; + } + + /** + * @dev Integer division of two numbers, truncating the quotient. + */ + 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; + } + + /** + * @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) { + assert(b <= a); + return a - b; + } + + /** + * @dev Adds two numbers, throws on overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + assert(c >= a); + return c; + } +} + + + +/** + * @title Basic token + * @dev Basic version of StandardToken, with no allowances. + */ +contract BasicToken is ERC20Basic { + using SafeMath for uint256; + + mapping(address => uint256) balances; + + uint256 totalSupply_; + + /** + * @dev total number of tokens in existence + */ + function totalSupply() public view returns (uint256) { + return totalSupply_; + } + + /** + * @dev transfer token for a specified address + * @param _to The address to transfer to. + * @param _value The amount to be transferred. + */ + function transfer(address _to, uint256 _value) public returns (bool) { + require(_to != address(0)); + require(_value <= balances[msg.sender]); + + // SafeMath.sub will throw if there is not enough balance. + balances[msg.sender] = balances[msg.sender].sub(_value); + balances[_to] = balances[_to].add(_value); + Transfer(msg.sender, _to, _value); + return true; + } + + /** + * @dev Gets the balance of the specified address. + * @param _owner The address to query the the balance of. + * @return An uint256 representing the amount owned by the passed address. + */ + function balanceOf(address _owner) public view returns (uint256 balance) { + return balances[_owner]; + } + +} + + + + + + +/** + * @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 Standard ERC20 token + * + * @dev Implementation of the basic standard token. + * @dev https://github.com/ethereum/EIPs/issues/20 + * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol + */ +contract StandardToken is ERC20, BasicToken { + + mapping (address => mapping (address => uint256)) internal allowed; + + + /** + * @dev Transfer tokens from one address to another + * @param _from address The address which you want to send tokens from + * @param _to address The address which you want to transfer to + * @param _value uint256 the amount of tokens to be transferred + */ + function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { + require(_to != address(0)); + require(_value <= balances[_from]); + require(_value <= allowed[_from][msg.sender]); + + balances[_from] = balances[_from].sub(_value); + balances[_to] = balances[_to].add(_value); + allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); + Transfer(_from, _to, _value); + return true; + } + + /** + * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. + * + * Beware that changing an allowance with this method brings the risk that someone may use both the old + * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this + * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * @param _spender The address which will spend the funds. + * @param _value The amount of tokens to be spent. + */ + function approve(address _spender, uint256 _value) public returns (bool) { + allowed[msg.sender][_spender] = _value; + Approval(msg.sender, _spender, _value); + return true; + } + + /** + * @dev Function to check the amount of tokens that an owner allowed to a spender. + * @param _owner address The address which owns the funds. + * @param _spender address The address which will spend the funds. + * @return A uint256 specifying the amount of tokens still available for the spender. + */ + function allowance(address _owner, address _spender) public view returns (uint256) { + return allowed[_owner][_spender]; + } + + /** + * @dev Increase the amount of tokens that an owner allowed to a spender. + * + * approve should be called when allowed[_spender] == 0. To increment + * allowed value is better to use this function to avoid 2 calls (and wait until + * the first transaction is mined) + * From MonolithDAO Token.sol + * @param _spender The address which will spend the funds. + * @param _addedValue The amount of tokens to increase the allowance by. + */ + function increaseApproval(address _spender, uint _addedValue) public returns (bool) { + allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue); + Approval(msg.sender, _spender, allowed[msg.sender][_spender]); + return true; + } + + /** + * @dev Decrease the amount of tokens that an owner allowed to a spender. + * + * approve should be called when allowed[_spender] == 0. To decrement + * allowed value is better to use this function to avoid 2 calls (and wait until + * the first transaction is mined) + * From MonolithDAO Token.sol + * @param _spender The address which will spend the funds. + * @param _subtractedValue The amount of tokens to decrease the allowance by. + */ + function decreaseApproval(address _spender, uint _subtractedValue) public returns (bool) { + uint oldValue = allowed[msg.sender][_spender]; + if (_subtractedValue > oldValue) { + allowed[msg.sender][_spender] = 0; + } else { + allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue); + } + Approval(msg.sender, _spender, allowed[msg.sender][_spender]); + return true; + } + +} + + + + + + + + + + + +/** + * @title Whitelist + * @dev The Whitelist contract has a whitelist of addresses, and provides basic authorization control functions. + * @dev This simplifies the implementation of "user permissions". + */ +contract Whitelist is Ownable { + mapping(address => bool) public whitelist; + + event WhitelistedAddressAdded(address addr); + event WhitelistedAddressRemoved(address addr); + + /** + * @dev Throws if called by any account that's not whitelisted. + */ + modifier onlyWhitelisted() { + require(whitelist[msg.sender]); + _; + } + + /** + * @dev add an address to the whitelist + * @param addr address + * @return true if the address was added to the whitelist, false if the address was already in the whitelist + */ + function addAddressToWhitelist(address addr) onlyOwner public returns(bool success) { + if (!whitelist[addr]) { + whitelist[addr] = true; + WhitelistedAddressAdded(addr); + success = true; + } + } + + /** + * @dev add addresses to the whitelist + * @param addrs addresses + * @return true if at least one address was added to the whitelist, + * false if all addresses were already in the whitelist + */ + function addAddressesToWhitelist(address[] addrs) onlyOwner public returns(bool success) { + for (uint256 i = 0; i < addrs.length; i++) { + if (addAddressToWhitelist(addrs[i])) { + success = true; + } + } + } + + /** + * @dev remove an address from the whitelist + * @param addr address + * @return true if the address was removed from the whitelist, + * false if the address wasn't in the whitelist in the first place + */ + function removeAddressFromWhitelist(address addr) onlyOwner public returns(bool success) { + if (whitelist[addr]) { + whitelist[addr] = false; + WhitelistedAddressRemoved(addr); + success = true; + } + } + + /** + * @dev remove addresses from the whitelist + * @param addrs addresses + * @return true if at least one address was removed from the whitelist, + * false if all addresses weren't in the whitelist in the first place + */ + function removeAddressesFromWhitelist(address[] addrs) onlyOwner public returns(bool success) { + for (uint256 i = 0; i < addrs.length; i++) { + if (removeAddressFromWhitelist(addrs[i])) { + success = true; + } + } + } + +} + + +contract OceanTokenTransferManager is Ownable, Whitelist { + + /** + * @dev check if transferFrom is possible + * @param _from address The address which you want to send tokens from + * @param _to address The address which you want to transfer to + */ + function canTransferFrom(address _from, address _to) public constant returns (bool success) { + if (whitelist[_from] == true || whitelist[_to] == true) { + return true; + } else { + return false; + } + } +} + + +contract OceanToken is StandardToken, Ownable { + event Airdrop(address indexed _to, uint256 _amount); + + string public constant name = 'The Ocean Token'; + string public constant symbol = 'OCEAN'; + uint8 public constant decimals = 18; + + OceanTokenTransferManager public transferManagerContract; + + /** + * @dev Airdrop the specified amount to the address + * @param _to The address that will receive the airdropped tokens. + * @param _requestedAmount The amount of tokens to airdrop. + * @return A boolean that indicates if the operation was successful. + */ + function airdrop(address _to, uint256 _requestedAmount) onlyOwner public returns (bool) { + uint256 _amountToDrop = _requestedAmount; + + totalSupply_ = totalSupply_.add(_amountToDrop); + balances[_to] = balances[_to].add(_amountToDrop); + emit Airdrop(_to, _amountToDrop); + emit Transfer(address(0), _to, _amountToDrop); + + return true; + } + + /** + * @dev Transfer tokens from one address to another + * @param _from address The address which you want to send tokens from + * @param _to address The address which you want to transfer to + * @param _value uint256 the amount of tokens to be transferred + */ + function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { + require(_to != address(0)); + require(_value <= balances[_from]); + require(_value <= allowed[_from][msg.sender]); + + // trading possible when at least one from list [_from, _to] is whitelisted + require(transferManagerContract.canTransferFrom(_from, _to)); + + balances[_from] = balances[_from].sub(_value); + balances[_to] = balances[_to].add(_value); + allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); + emit Transfer(_from, _to, _value); + return true; + } + + function setTransferManagerContract(OceanTokenTransferManager _transferManagerContract) onlyOwner public { + transferManagerContract = _transferManagerContract; + } +} \ No newline at end of file