ETH Price: $3,287.42 (-2.81%)

Contract Diff Checker

Contract Name:
MakerDAOInsurance

Contract Source Code:

File 1 of 1 : MakerDAOInsurance

pragma solidity ^0.5.16;

contract MakerDAOInsurance {
    using SafeMath for *;

    //*********
    // STRUCTS
    //*********
    struct Player {
        uint256 id;     // agent id
        bytes32 name;   // agent name
        uint256 gen;    // general vault
        uint256 ref;    // referral vault
        bool isAgent;   // referral activated
        uint256 eth;    // eth player has paid
        uint256 shares;   // shares
        uint256 units;  // uints of insurance
        uint256 plyrLastSeen; // last day player played
        uint256 mask;   // player mask
        uint256 level;
        uint256 accumulatedRef;
    }


    //***************
    // EXTERNAL DATA
    //***************

    TOP SAI_TOP = TOP(0x9b0ccf7C8994E19F39b2B4CF708e0A7DF65fA8a3);
    TUB SAI_TUB = TUB(0x448a5065aeBB8E423F0896E6c5D525C040f59af3);
    Underwriter underwriter = Underwriter(0xE58cDe3CbEeCC8d9306f482729084B909Afa2357);
    Agency agency = Agency(0x7Bc360ebD65eFa503FF189A0F81f61f85D310Ec3);
    
    address payable constant private developer = address(0x1e20B7d24d429b0DE705d3FF8B88Ec651d28A935);
    address payable constant private hakka = address(0xD0533664013a82c31584B7FFDB215139f38Ad77A);
    uint256 public end;
    bool public ended;


    //******************
    // GLOBAL VARIABLES
    //******************
    mapping(address => mapping(uint256 => uint256)) public unitToExpirePlayer;
    mapping(uint256 => uint256) public unitToExpire; // unit of insurance due at day x

    uint256 public issuedInsurance; // all issued insurance
    uint256 public ethOfShare;        // virtual eth of bought shares
    uint256 public shares;            // totalSupply of share
    uint256 public pool;             // eth gonna pay to beneficiary
    uint256 public today;           // today's date
    uint256 public _now;            // current time
    uint256 public mask;            // global mask
    uint256 public agents;          // number of agent

    // player data
    mapping(address => Player) public player; // player data
    mapping(uint256 => address) public agentxID_; // return agent address by id
    mapping(bytes32 => address) public agentxName_; // return agent address by name

    // constant parameters
    uint256 constant maxInsurePeriod = 100;
    uint256 constant maxLevel = 10;

    // rate of buying x day insurance
    uint256[101] public rate =
    [0,
    1000000000000000000,
    1990000000000000000,
    2970100000000000000,
    3940399000000000000,
    4900995010000000000,
    5851985059900000000,
    6793465209301000000,
    7725530557207990000,
    8648275251635910100,
    9561792499119550999,
    10466174574128355489,
    11361512828387071934,
    12247897700103201215,
    13125418723102169203,
    13994164535871147511,
    14854222890512436036,
    15705680661607311676,
    16548623854991238559,
    17383137616441326173,
    18209306240276912911,
    19027213177874143782,
    19836941046095402344,
    20638571635634448321,
    21432185919278103838,
    22217864060085322800,
    22995685419484469572,
    23765728565289624876,
    24528071279636728627,
    25282790566840361341,
    26029962661171957728,
    26769663034560238151,
    27501966404214635769,
    28226946740172489411,
    28944677272770764517,
    29655230500043056872,
    30358678195042626303,
    31055091413092200040,
    31744540498961278040,
    32427095093971665260,
    33102824143031948607,
    33771795901601629121,
    34434077942585612830,
    35089737163159756702,
    35738839791528159135,
    36381451393612877544,
    37017636879676748769,
    37647460510879981281,
    38270985905771181468,
    38888276046713469653,
    39499393286246334956,
    40104399353383871606,
    40703355359850032890,
    41296321806251532561,
    41883358588189017235,
    42464525002307127063,
    43039879752284055792,
    43609480954761215234,
    44173386145213603082,
    44731652283761467051,
    45284335760923852380,
    45831492403314613856,
    46373177479281467717,
    46909445704488653040,
    47440351247443766510,
    47965947734969328845,
    48486288257619635557,
    49001425375043439201,
    49511411121293004809,
    50016297010080074761,
    50516134039979274013,
    51010972699579481273,
    51500862972583686460,
    51985854342857849595,
    52465995799429271099,
    52941335841434978388,
    53411922483020628604,
    53877803258190422318,
    54339025225608518095,
    54795634973352432914,
    55247678623618908585,
    55695201837382719499,
    56138249819008892304,
    56576867320818803381,
    57011098647610615347,
    57440987661134509194,
    57866577784523164102,
    58287912006677932461,
    58705032886611153136,
    59117982557745041605,
    59526802732167591189,
    59931534704845915277,
    60332219357797456124,
    60728897164219481563,
    61121608192577286747,
    61510392110651513880,
    61895288189544998741,
    62276335307649548754,
    62653571954573053266,
    63027036235027322733,
    63396765872677049506];

    // threshold of agent upgrade
    uint256[10] public requirement =
    [0,
    73890560989306501,
    200855369231876674,
    545981500331442382,
    1484131591025766010,
    4034287934927351160,
    10966331584284585813,
    29809579870417282259,
    81030839275753838749,
    220264657948067161559];


    //******************
    // EVENT
    //******************
    event UPGRADE (address indexed agent, uint256 indexed level);
    event BUYINSURANCE(address indexed buyer, uint256 indexed start, uint256 unit,  uint256 date);


    //******************
    // MODIFIER
    //******************
    modifier isHuman() {
        require(msg.sender == tx.origin, "sorry humans only");
        _;
    }

    //******************
    // CORE FUNCTIONS
    //******************

    /**
     * @dev Constructor
     * @notice Initialize the time
     */
    constructor() public {
        _now = now;
        today = _now / 1 days;
    }

    /**
     * @dev Ticker
     * @notice It is called everytime when a player interacts with this contract
     * @return true if MakerDAO has been shut down, false otherwise
     */
    function tick()
        internal
        returns(bool)
    {
        if(!ended) {
            if (_now != now) {
                _now = now;
                uint256 _today; // the current day as soon as ticker is called

                //check MakerDAO status
                ended = SAI_TUB.off();
                end = SAI_TOP.caged();

                _today = ended ? end / 1 days : _now / 1 days;

                // calculate the outdated issuedInsurance
                while (today < _today) {
                    issuedInsurance = issuedInsurance.sub(unitToExpire[today]);
                    today += 1;
                }
            }
        }
        
        return ended;
    }

    /**
     * @dev Register
     * @notice Register a name by a human player
     */
    function register(string calldata _nameString)
        external
        payable
        isHuman()
    {
        bytes32 _name = agency.register(_nameString);
        address _agent = msg.sender;
        require(msg.value >= 10000000000000000, "insufficient amount");
        require(agentxName_[_name] == address(0), "name registered");

        if(!player[_agent].isAgent){
            agents += 1;
            player[_agent].isAgent = true;
            player[_agent].id = agents;
            player[_agent].level = 1;
            agentxID_[agents] = _agent;
        }
        // set name active for the player
        player[_agent].name = _name;
        agentxName_[_name] = _agent;

        (new SafeSend).value(msg.value)(hakka);

    }

    /**
     * @dev Upgrade
     * @notice Upgrade when a player's referral bonus meet the promotion
     */
    function upgrade()
        external
        isHuman()
    {
        address _agent = msg.sender;
        require(player[_agent].isAgent);
        require(player[_agent].level < maxLevel);

        if(player[_agent].accumulatedRef >= requirement[player[_agent].level]){
            player[_agent].level = (1).add(player[_agent].level);
            emit UPGRADE(_agent,player[_agent].level);
        }
    }

    //using address for referral
    function buy(address payable _agent, uint256 _date)
        isHuman()
        public
        payable
    {
        // ticker
        if(tick()){
            msg.sender.transfer(msg.value);
            return;
        }

        // validate agent
        if(!player[_agent].isAgent){
            _agent = address(0);
        }

        buyCore(msg.sender, msg.value, _date, _agent);
    }

    //using ID for referral
    function buy(uint256 _agentId, uint256 _date)
        isHuman()
        public
        payable
    {
        // ticker
        if(tick()){
            msg.sender.transfer(msg.value);
            return;
        }

        //query agent
        address payable _agent = address(uint160(agentxID_[_agentId]));

        buyCore(msg.sender, msg.value, _date, _agent);
    }

    //using name for referral
    function buy(bytes32 _agentName, uint256 _date)
        isHuman()
        public
        payable
    {
        // ticker
        if(tick()){
            msg.sender.transfer(msg.value);
            return;
        }

        //query agent
        address payable _agent = address(uint160(agentxName_[_agentName]));

        buyCore(msg.sender, msg.value, _date, _agent);
    }

    // contract wallets, sorry insurance only for human
    function buy()
        public
        payable
    {
        // ticker
        if(tick()) {
            (new SafeSend).value(msg.value)(msg.sender);
            return;
        }

        buyCore(msg.sender, msg.value, 0, address(0));
    }

    // fallback
    function () external payable {
        buy();
    }

    /**
     * @dev Core part of buying
     */
    function buyCore(address _buyer, uint256 _eth, uint256 _date, address payable _agent) internal {

        updatePlayerUnit(_buyer);
        
        require(_eth >= 1000000000, "pocket lint: not a valid currency");
        require(_eth <= 10000000000000000000000, "no vitalik, no");

        if(_date > maxInsurePeriod){
            _date = maxInsurePeriod;
        }
        uint256 _rate = rate[_date] + 1000000000000000000;
        uint256 ethToBuyShare = _eth.mul(1000000000000000000) / _rate;
        //-- ethToBuyShare is a virtual amount used to represent the eth player paid for buying shares
        //which is usually different from _eth

        // get value of shares and insurances can be bought
        uint256 _share = underwriter.mintShare(ethOfShare, ethToBuyShare);
        uint256 _unit = (_date == 0)? 0: _share;
        uint256 newDate = today + _date - 1;


        // update global data
        ethOfShare = ethOfShare.add(ethToBuyShare);
        shares = shares.add(_share);
        unitToExpire[newDate] = unitToExpire[newDate].add(_unit);
        issuedInsurance = issuedInsurance.add(_unit);

        // update player data
        player[_buyer].eth = player[_buyer].eth.add(_eth);
        player[_buyer].shares = player[_buyer].shares.add(_share);
        player[_buyer].units = player[_buyer].units.add(_unit);
        unitToExpirePlayer[_buyer][newDate] = unitToExpirePlayer[_buyer][newDate].add(_unit);

        distributeEx(_eth, _agent);
        distributeIn(_buyer, _eth, _share);
        emit BUYINSURANCE(_buyer, today, _unit, _date);
        emit Transfer(address(0), _buyer, _unit);
    }

    /**
     * @dev Update player's units of insurance
     */
    function updatePlayerUnit(address _player) internal {
        uint256 _today = player[_player].plyrLastSeen;
        uint256 expiredUnit = 0;
        if(_today != 0){
            while(_today < today){
                expiredUnit = expiredUnit.add(unitToExpirePlayer[_player][_today]);
                _today += 1;
            }
            player[_player].units = player[_player].units.sub(expiredUnit);
        }
        player[_player].plyrLastSeen = today;
    }

    /**
     * @dev pay external stakeholder
     */
    function distributeEx(uint256 _eth, address payable _agent) internal {
        uint256 ex = _eth / 4 ;

        if(player[_agent].isAgent){
            uint256 refRate = player[_agent].level.add(6);
            uint256 _ref = _eth.mul(refRate) / 100;
            player[_agent].ref = player[_agent].ref.add(_ref);
            player[_agent].accumulatedRef = player[_agent].accumulatedRef.add(_ref);
            ex = ex.sub(_ref);
        }

        uint256 _dev = ex / 3;
        uint256 _hak = ex.sub(_dev);

        (new SafeSend).value(_dev)(developer);
        (new SafeSend).value(_hak)(hakka);
    }

    /**
     * @dev Distribute to internal
     */
    function distributeIn(address _buyer, uint256 _eth, uint256 _shares) internal {
        uint256 _gen = _eth.mul(3) / 20;

        // internal eth balance (eth = eth - (community share + ref share + dev share))
        _eth = _eth.sub(_eth / 4);

        // calculate pool
        uint256 _pool = _eth.sub(_gen);

        // distribute gen share and collect dust
        uint256 _dust = updateMasks(_buyer, _gen, _shares);

        // add eth to pool
        pool = pool.add(_dust).add(_pool);
    }

    function updateMasks(address  _player, uint256 _gen, uint256 _shares)
        private
        returns(uint256)
    {
        // calculate profit per share & global mask based on this buy: (dust goes to pool)
        uint256 _ppt = _gen.mul(1000000000000000000) / shares;
        mask = mask.add(_ppt);

        // calculate player earning from their own buy (only based on the shares
        // they just bought). & update player earnings mask
        uint256 _pearn = (_ppt.mul(_shares)) / 1000000000000000000;
        player[_player].mask = (((mask.mul(_shares)) / 1000000000000000000).sub(_pearn)).add(player[_player].mask);

        // calculate & return dust
        return(_gen.sub( _ppt.mul(shares) / 1000000000000000000));
    }

    /**
     * @dev Submit a claim from a beneficiary
     */
    function claim()
        isHuman()
        public
    {
        require(tick(), "not yet"); // MakerDAO shutdown!
        address payable beneficiary = msg.sender;
        updatePlayerUnit(beneficiary);
        uint256 amount = pool.mul(player[beneficiary].units) / issuedInsurance;
        player[beneficiary].units = 0;
        beneficiary.transfer(amount);
    }

    /**
     * @dev Withdraw dividends and ref
     */
    function withdraw()
        public
    {
        // get player earnings
        uint256 _eth;
        _eth = withdrawEarnings(msg.sender);

        // pay
        if (_eth > 0){
            if(msg.sender == tx.origin)
                msg.sender.transfer(_eth);
            else
                (new SafeSend).value(_eth)(msg.sender);
        }
    }

    function withdrawEarnings(address _player)
        private
        returns(uint256)
    {
        // update gen vault
        updateGenVault(_player);

        // from vaults
        uint256 _earnings = player[_player].gen.add(player[_player].ref);
        if (_earnings > 0) {
            player[_player].gen = 0;
            player[_player].ref = 0;
        }

        return(_earnings);
    }

    function updateGenVault(address _player)
        private
    {
        uint256 _earnings = calcUnMaskedEarnings(_player);
        if (_earnings > 0) {
            // put in gen vault
            player[_player].gen = _earnings.add(player[_player].gen);
            // zero out their earnings by updating mask
            player[_player].mask = _earnings.add(player[_player].mask);
        }
    }

    function calcUnMaskedEarnings(address _player)
        private
        view
        returns(uint256)
    {
        return (mask.mul(player[_player].shares) / 1000000000000000000).sub(player[_player].mask);
    }

    //******************
    // GETTERS
    //******************

    /**
     * @dev Return the price buyer will pay for next 1 individual share.
     * @return Price for next share bought (in wei format)
     */
    function getBuyPrice() external view returns(uint256) {
        return underwriter.burnShare(shares.add(1000000000000000000), 1000000000000000000);
    }

    /**
     * @dev Get the units of insurance of player
     * @return Amount of existing units of insurance
     */
    function getCurrentUnit(address _player)
        external
        view
        returns(uint256)
    {
        uint256 _unit = player[_player].units;
        uint256 _today = player[_player].plyrLastSeen;
        uint256 expiredUnit = 0;
        if(_today != 0) {
            while(_today < today){
                expiredUnit = expiredUnit.add(unitToExpirePlayer[_player][_today]);
                _today += 1;
            }

        }
        return _unit == 0 ? 0 : _unit.sub(expiredUnit);
    }

    /**
     * @dev Get the list of units of insurace going to expire of a player
     * @return List of units of insurance going to expire from a player
     */
    function getExpiringUnitListPlayer(address _player)
        external
        view
        returns(uint256[maxInsurePeriod] memory expiringUnitList)
    {
        for(uint256 i=0; i<maxInsurePeriod; i++) {
            expiringUnitList[i] = unitToExpirePlayer[_player][today+i];
        }
        return expiringUnitList;
    }

    /**
     * @dev Get the list of units of insurace going to expire
     * @return List of units of insurance going to expire
     */
    function getExpiringUnitList()
        external
        view
        returns(uint256[maxInsurePeriod] memory expiringUnitList)
    {
        for(uint256 i=0; i<maxInsurePeriod; i++){
            expiringUnitList[i] = unitToExpire[today+i];
        }
        return expiringUnitList;
    }

    //******************
    // ERC20
    //******************
    string  public constant name     = "Third Floor Mutual SAI";
    string  public constant symbol   = "3FMS";
    uint8   public constant decimals = 18;

    function totalSupply() external view returns(uint256) {
        return shares;
    }

    function balanceOf(address who) external view returns(uint256) {
        return player[who].shares;
    }

    event Transfer(address indexed from, address indexed to, uint256 amount);

}

contract TOP {
    function caged() public returns(uint256);
}

contract TUB {
    function off() public returns(bool);
}

contract SafeSend {
    constructor(address payable to) public payable {
        selfdestruct(to);
    }
}

contract Underwriter {
    function mintShare(uint256 _curEth, uint256 _newEth) external pure returns (uint256);
    function burnShare(uint256 _curShares, uint256 _sellShares) external pure returns (uint256);
    function shares(uint256 _eth) public pure returns(uint256);
    function eth(uint256 _shares) public pure returns(uint256);
}


contract Agency {
    function register(string memory _input) public pure returns(bytes32);
}

library SafeMath {

    function mul(uint256 a, uint256 b)
        internal
        pure
        returns (uint256 c)
    {
        if (a == 0) return 0;
        c = a * b;
        require(c / a == b);
    }

    function sub(uint256 a, uint256 b)
        internal
        pure
        returns (uint256 c)
    {
        require(b <= a);
        c = a - b;
    }

    function add(uint256 a, uint256 b)
        internal
        pure
        returns (uint256 c)
    {
        c = a + b;
        require(c >= a);
    }

    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);
        }
    }

    function sq(uint256 x)
        internal
        pure
        returns (uint256)
    {
        return (mul(x,x));
    }

    function pwr(uint256 x, uint256 y) internal pure returns(uint256 z) {
        z = 1;
        while(y != 0){
            if(y % 2 == 1)
                z = mul(z,x);
            x = sq(x);
            y = y / 2;
        }
        return z;
    }

}

Please enter a contract address above to load the contract details and source code.

Context size (optional):