ETH Price: $1,812.39 (+10.28%)

Contract

0x73711b46A2D2dcC1c15C5021cD8D819bF315a280
 

Overview

ETH Balance

0.079358418606383018 ETH

ETH Value

$143.83 (@ $1,812.39/ETH)

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Buy Rare Item182123052025-04-21 11:20:252 days ago1745234425IN
0x73711b46...bF315a280
0.00952648 ETH0.000000190.001031
Buy Rare Item181033712025-04-18 22:49:174 days ago1745016557IN
0x73711b46...bF315a280
0.00436025 ETH0.000000050.00044609
Buy Rare Item173920052025-04-02 11:37:0521 days ago1743593825IN
0x73711b46...bF315a280
0.00121374 ETH0.000000020.00010424
Buy Rare Item172666962025-03-30 14:00:0723 days ago1743343207IN
0x73711b46...bF315a280
0.001 ETH00.00000285
Buy Rare Item164086992025-03-10 17:20:1343 days ago1741627213IN
0x73711b46...bF315a280
0.00752238 ETH0.000000020.00012342
Buy Rare Item163697822025-03-09 19:42:5944 days ago1741549379IN
0x73711b46...bF315a280
0.01135328 ETH0.000000030.00017793
Buy Rare Item156652932025-02-21 12:20:0161 days ago1740140401IN
0x73711b46...bF315a280
0.00978969 ETH0.000000010.0001622
Buy Rare Item156349662025-02-20 19:29:0761 days ago1740079747IN
0x73711b46...bF315a280
0.01281493 ETH0.000000070.0006727
Buy Rare Item155373742025-02-18 13:16:0363 days ago1739884563IN
0x73711b46...bF315a280
0.00859127 ETH0.000000030.00025757
Buy Rare Item153298032025-02-13 17:57:0168 days ago1739469421IN
0x73711b46...bF315a280
0.02277812 ETH0.000000230.00163875
Buy Rare Item153297942025-02-13 17:56:4368 days ago1739469403IN
0x73711b46...bF315a280
0.02290343 ETH0.00000030.00162422
Buy Rare Item151468782025-02-09 12:19:3173 days ago1739103571IN
0x73711b46...bF315a280
0.01913294 ETH0.000000020.00015225
Buy Rare Item148131822025-02-01 18:56:1980 days ago1738436179IN
0x73711b46...bF315a280
0.00975611 ETH0.000000020.00024782
Buy Rare Item145948472025-01-27 17:38:2985 days ago1737999509IN
0x73711b46...bF315a280
0.01541686 ETH00.00004514
Buy Rare Item145948392025-01-27 17:38:1385 days ago1737999493IN
0x73711b46...bF315a280
0.00822036 ETH00.00004582
Buy Rare Item145615312025-01-26 23:07:5786 days ago1737932877IN
0x73711b46...bF315a280
0.00662738 ETH0.000000030.00031822
Buy Rare Item145400452025-01-26 11:11:4587 days ago1737889905IN
0x73711b46...bF315a280
0.00532842 ETH0.000000010.00016637
Buy Rare Item143427652025-01-21 21:35:4591 days ago1737495345IN
0x73711b46...bF315a280
0.01309777 ETH00.00000125
Buy Rare Item141452782025-01-17 7:52:5196 days ago1737100371IN
0x73711b46...bF315a280
0.01098018 ETH0.000000060.00031628
Claim Blast Yiel...140736402025-01-15 16:04:5597 days ago1736957095IN
0x73711b46...bF315a280
0 ETH00.0000018
Buy Rare Item140585972025-01-15 7:43:2998 days ago1736927009IN
0x73711b46...bF315a280
0.00896399 ETH0.000000040.00022393
Buy Rare Item140585912025-01-15 7:43:1798 days ago1736926997IN
0x73711b46...bF315a280
0.0047975 ETH0.000000020.00022676
Buy Rare Item138093192025-01-09 13:14:13103 days ago1736428453IN
0x73711b46...bF315a280
0.00761046 ETH0.000000040.00023006
Buy Rare Item135988162025-01-04 16:17:27108 days ago1736007447IN
0x73711b46...bF315a280
0.0036145 ETH0.000000580.00314714
Buy Rare Item128132332024-12-17 11:51:21127 days ago1734436281IN
0x73711b46...bF315a280
0.04365082 ETH0.000000180.00099639
View all transactions

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
182123052025-04-21 11:20:252 days ago1745234425
0x73711b46...bF315a280
0.02290338 ETH
182123052025-04-21 11:20:252 days ago1745234425
0x73711b46...bF315a280
0.00000005 ETH
181033712025-04-18 22:49:174 days ago1745016557
0x73711b46...bF315a280
0.00859112 ETH
181033712025-04-18 22:49:174 days ago1745016557
0x73711b46...bF315a280
0.00000004 ETH
173920052025-04-02 11:37:0521 days ago1743593825
0x73711b46...bF315a280
0.001 ETH
173920052025-04-02 11:37:0521 days ago1743593825
0x73711b46...bF315a280
0 ETH
172666962025-03-30 14:00:0723 days ago1743343207
0x73711b46...bF315a280
0.00361448 ETH
164086992025-03-10 17:20:1343 days ago1741627213
0x73711b46...bF315a280
0.04191621 ETH
164086992025-03-10 17:20:1343 days ago1741627213
0x73711b46...bF315a280
0.00000003 ETH
163697822025-03-09 19:42:5944 days ago1741549379
0x73711b46...bF315a280
0.01541682 ETH
163697822025-03-09 19:42:5944 days ago1741549379
0x73711b46...bF315a280
0.00000004 ETH
156652932025-02-21 12:20:0161 days ago1740140401
0x73711b46...bF315a280
0.00975609 ETH
156652932025-02-21 12:20:0161 days ago1740140401
0x73711b46...bF315a280
0.00000004 ETH
156349662025-02-20 19:29:0761 days ago1740079747
0x73711b46...bF315a280
0.06009332 ETH
156349662025-02-20 19:29:0761 days ago1740079747
0x73711b46...bF315a280
0.00000017 ETH
155373742025-02-18 13:16:0363 days ago1739884563
0x73711b46...bF315a280
0.10206474 ETH
155373742025-02-18 13:16:0363 days ago1739884563
0x73711b46...bF315a280
0.00000014 ETH
153298032025-02-13 17:57:0168 days ago1739469421
0x73711b46...bF315a280
0.04365062 ETH
153298032025-02-13 17:57:0168 days ago1739469421
0x73711b46...bF315a280
0.0000001 ETH
153297942025-02-13 17:56:4368 days ago1739469403
0x73711b46...bF315a280
0.01913282 ETH
153297942025-02-13 17:56:4368 days ago1739469403
0x73711b46...bF315a280
0.00000004 ETH
151468782025-02-09 12:19:3173 days ago1739103571
0x73711b46...bF315a280
0.0505316 ETH
151468782025-02-09 12:19:3173 days ago1739103571
0x73711b46...bF315a280
0.00000011 ETH
148131822025-02-01 18:56:1980 days ago1738436179
0x73711b46...bF315a280
0.00822034 ETH
148131822025-02-01 18:56:1980 days ago1738436179
0x73711b46...bF315a280
0.00000002 ETH
View All Internal Transactions

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
RareItems

Compiler Version
v0.5.16+commit.9c3226ce

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, None license

Contract Source Code (Solidity)

/**
 *Submitted for verification at blastscan.io on 2024-03-17
*/

pragma solidity 0.5.16;

/**
 *
 * https://blastgoo.com
 * 
 * The competitive free2play idle game powered by Blast Native yield, play now!
 *
 */


contract RareItems {

    mapping(uint256 => Rare) private rareInfo;
    FarmGoo constant goo = FarmGoo(0x5361ee2f443B61E60F19Dbe73a324f342619161D);
    Blast constant blast = Blast(0x4300000000000000000000000000000000000002);
    BlastPoints constant points = BlastPoints(0x2536FE9ab3F511540F2f9e2eC2A805005C3Dd800);
    address raffle = address(0x0);
    address owner = msg.sender;

    uint256 dailyDegradation = 10; // 1% a day
    uint256 minPrice = 1 finney; // 0.001 eth

    uint256 public numRares;

    struct Rare {
        uint256 rareId;
        uint256 price;
        uint256 rareClass;
        uint256 unitId;
        uint256 rareValue;
        address payable owner;
        uint256 lastFlipTime;
        uint256 saleUnlockTime;
    }

    function() payable external {}

    constructor() public {
        blast.configureClaimableYield();
        blast.configureClaimableGas();
        points.configurePointsOperator(msg.sender);
    }

    function updateRaffle(address raffleAddress) external {
        require(msg.sender == owner);
        raffle = raffleAddress;
    }

    function addRare(uint256 id, uint256 price, uint256 class, uint256 unit, uint256 value) external {
        require(msg.sender == owner);
        require(id > 0 && rareInfo[id].owner == address(0));
        if (rareInfo[id].rareId == 0) {
            numRares++;
        }
        rareInfo[id] = Rare(id, price, class, unit, value, address(0), 2**128, 2**128);
    }

    function claimBlastYield() external {
        uint256 gained = blast.claimAllYield(address(this), address(this));
        gained += blast.claimAllGas(address(this), address(this));
        goo.addEth.value(gained)();
    }

    function recoverToken(address erc20, uint256 amount) external  {
        require(msg.sender == owner); // Just incase someone accidently sends random token here
        ERC20(erc20).transfer(owner, amount);
    }

    function creditRaffleItem(address payable winner, uint256 rareId) external {
        require(msg.sender == raffle);
        Rare memory item = rareInfo[rareId];
        require(item.owner == address(0));
        require(item.unitId > 0);

        // We have to claim winner's goo before updating their production values
        goo.updatePlayersGoo(winner);
        goo.upgradeUnitMultipliersExternal(winner, item.rareClass, item.unitId, item.rareValue);

        item.owner = winner;
        item.saleUnlockTime = now + 24 hours;
        rareInfo[rareId] = item;
    }

    function buyRareItem(uint256 rareId) external payable {
        Rare memory item = rareInfo[rareId];
        require(item.owner != address(0));
        require(item.unitId > 0);
        require(now > item.saleUnlockTime);
        uint256 price = getItemPrice(item);

        require(msg.value >= price);
        if (msg.value > price) {
            require(msg.value < (price * 102) / 100); // No overbid
            msg.sender.transfer(msg.value - price);
        }

        // We have to claim buyer's goo before updating their production values
        goo.updatePlayersGoo(msg.sender);
        goo.upgradeUnitMultipliersExternal(msg.sender, item.rareClass, item.unitId, item.rareValue);

        // We have to claim seller's goo before reducing their production values
        goo.updatePlayersGoo(item.owner);
        goo.removeUnitMultipliersExternal(item.owner, item.rareClass, item.unitId, item.rareValue);

        if (item.lastFlipTime < 2 ** 128) { // Give previous owner their staked eth back
            item.owner.send((item.price * 4) / 5);
        }

        // Transfer / update rare item
        item.owner = msg.sender;
        item.price = (price * 5) / 4; // 25% price flip increase
        item.lastFlipTime = now;
        rareInfo[rareId] = item;
    }

    function getItemPrice(Rare memory rare) internal view returns (uint256 itemPrice) {
        uint256 secondsSinceLastFlip = 0;
        if (now > rare.lastFlipTime) {
            secondsSinceLastFlip = now - rare.lastFlipTime;
        }
        itemPrice = rare.price;
        uint256 priceReduction = (secondsSinceLastFlip * dailyDegradation * rare.price) / 86400000;
        if (priceReduction > itemPrice || itemPrice - priceReduction < minPrice) {
            itemPrice = minPrice;
        } else {
            itemPrice -= priceReduction;
        }
    }

    function getRareItemInfo() external view returns (address[] memory, uint256[] memory, uint256[] memory) {
        address[] memory itemOwners = new address[](numRares);
        uint256[] memory itemPrices = new uint256[](numRares);
        uint256[] memory saleUnlockTimes = new uint256[](numRares);

        for (uint256 i = 0; i < numRares; i++) {
            Rare memory item = rareInfo[i+1];
            itemOwners[i] = item.owner;
            itemPrices[i] = getItemPrice(item);
            saleUnlockTimes[i] = item.saleUnlockTime;
        }
        return (itemOwners, itemPrices, saleUnlockTimes);
    }

}

 contract BlastGooUnitRaffle {

    FarmGoo constant goo = FarmGoo(0x5361ee2f443B61E60F19Dbe73a324f342619161D);
    address owner = msg.sender;

    struct Raffle {
        uint256 endTime;
        uint256 unitId;
        uint256 maxLevel;
        uint256 totalTickets;
        uint256 winningTicket;
        address winner;
        bool drawn;
    }

    struct TicketPurchase {
        address player;
        uint256 startId;
        uint256 endId;
    }

    mapping(uint256 => Raffle[]) public raffles; // raffleId -> Raffle group
    mapping(uint256 => mapping(uint256 => mapping(address => uint256))) public ticketsBoughtByPlayer;
    mapping(uint256 => mapping(uint256 => TicketPurchase[])) public rafflePurchases;
    uint256 public raffleId;

    function buyUnitRaffleTicket(uint256 amount, uint256 raffleIndex) external {
        (uint48 playersLevel,,) = goo.playerLevel(msg.sender);
        Raffle memory raffle = raffles[raffleId][raffleIndex];
        require(raffle.endTime >= now);
        uint256 playersExistingTickets = ticketsBoughtByPlayer[raffleId][raffleIndex][msg.sender];
        require(playersExistingTickets > 0 || playersLevel < raffle.maxLevel);
        require(amount > 0);

        TicketPurchase memory purchase = TicketPurchase(msg.sender, raffle.totalTickets, raffle.totalTickets + (amount - 1));
        ticketsBoughtByPlayer[raffleId][raffleIndex][msg.sender] = playersExistingTickets + amount;
        rafflePurchases[raffleId][raffleIndex].push(purchase);

        raffles[raffleId][raffleIndex].totalTickets += amount;
        goo.updatePlayersGooFromPurchaseExternal(msg.sender, amount * 1000);
    }

    function startUnitRaffle(uint256 endTime, uint256 unitId, uint256[] calldata maxLevels) external {
        require(msg.sender == owner);
        require(now < endTime);

        uint256 numGroups = maxLevels.length;
        require(numGroups > 0);

        if (raffleId > 0) { // Sanity to assure raffle has ended before next one starts
            require(raffles[raffleId][0].drawn);
        }

        // Set current raffle info
        raffleId++;
        for (uint256 i = 0; i < numGroups; i++) {
            raffles[raffleId].push(Raffle(endTime, unitId, maxLevels[i], 0, 0, address(0), false));
        }
    }

    function drawRandomUnitWinner() public {
        require(msg.sender == owner);
        Raffle memory raffle = raffles[raffleId][0];
        require(!raffle.drawn);
        require(now >= raffle.endTime);

        uint256 numRaffles = raffles[raffleId].length;
        for (uint256 i = 0; i < numRaffles; i++) {
            drawRaffle(i);
        }
    }

    function drawRaffle(uint256 raffleIndex) internal {
        Raffle memory raffle = raffles[raffleId][raffleIndex];

        uint256 purchases = rafflePurchases[raffleId][raffleIndex].length;
        uint256 tickets = raffle.totalTickets;
        if (purchases == 0) {
            raffles[raffleId][raffleIndex].drawn = true;
        } else {
            uint256 seed = tickets + (now % raffleId);
            uint256 rng = uint256(keccak256(abi.encodePacked(seed, block.coinbase)));
            uint256 raffleKeyWinner = rng % tickets;

            bool keyFound;
            uint256 low = 0;
            uint256 high = purchases - 1;
            while (!keyFound) {
                uint256 mid = (low + high) / 2;
                TicketPurchase memory search = rafflePurchases[raffleId][raffleIndex][mid];
                if (search.endId < raffleKeyWinner) {
                    low = mid + 1;
                } else if (search.startId > raffleKeyWinner) {
                    high = mid - 1;
                } else {
                    keyFound = true;
                    raffle.winningTicket = raffleKeyWinner;
                    raffle.winner = search.player;
                    raffle.drawn = true;
                    raffles[raffleId][raffleIndex] = raffle;
                    goo.creditUnitExternal(search.player, raffle.unitId);
                }
            }
        }
    }

    function getLatestRaffles(address player) external view returns (uint256[6][] memory, address[] memory) {
        return getPastRaffle(raffleId, player);
    }

    function getPastRaffle(uint256 id, address player) public view returns (uint256[6][] memory, address[] memory) {
        uint256 raffleGroups = raffles[id].length;

        uint256[6][] memory stats = new uint256[6][](raffleGroups);
        address[] memory winners = new address[](raffleGroups);

        for (uint256 i = 0; i < raffleGroups; i++) {
            Raffle memory raffle = raffles[id][i];
            stats[i][0] = raffle.endTime;
            stats[i][1] = raffle.unitId;
            stats[i][2] = raffle.maxLevel;

            if (raffle.drawn) {
                stats[i][3] = raffle.winningTicket;
                winners[i] = raffle.winner;
            } else {
                stats[i][3] = 0;
            }
            stats[i][4] = ticketsBoughtByPlayer[id][i][player];
            stats[i][5] = raffle.totalTickets;
        }

        return (stats, winners);
    }

}

contract GooFarm {
    using SafeMath for uint256;

    FarmGoo constant farmGoo = FarmGoo(0x5361ee2f443B61E60F19Dbe73a324f342619161D);
    Blast constant blast = Blast(0x4300000000000000000000000000000000000002);
    address constant blobby = address(0xf0eCCE59887de745Ce00D445c65DBE69EF5d5B2e);
    address public farmToken; // This is native eth so has no token address

    mapping(address => uint256) public balances;
    mapping(address => uint256) public unlockTime;

    function() payable external {}

    constructor() public {
        blast.configureClaimableYield();
        blast.configureClaimableGas();
    }

    function stake(address player) external payable {
        require(msg.sender == address(farmGoo));
        balances[player] += msg.value;

        if (unlockTime[player] == 0) {
            unlockTime[player] = now + 30 days;
        }
    }

    function unstake(address payable player, uint256 amount) external {
        require(msg.sender == address(farmGoo));
        require(now > unlockTime[player]);

		balances[player] = balances[player].sub(amount);
		player.transfer(amount);
    }

    function claimBlastYield() external {
        uint256 gained = blast.claimAllYield(address(this), address(this));
        gained += blast.claimAllGas(address(this), address(this));
        farmGoo.addEth.value(gained)();
    }

    function recoverToken(address erc20, uint256 amount) external  {
        require(msg.sender == blobby); // Just incase someone accidently sends random token here
        require(erc20 != farmToken);
        ERC20(erc20).transfer(blobby, amount);
    }

}



contract FarmGoo {
    using SafeMath for uint256;

    Blast constant blast = Blast(0x4300000000000000000000000000000000000002);

    uint256 public totalGooProduction;
    address public owner = msg.sender; // Minor management of game

    uint256 constant public researchDivPercent = 4;
    uint256 constant public gooDepositDivPercent = 1;

    uint256 public totalEthResearchPool; // Eth dividends to be distributed to players
    uint256 public platformGas; // 5% of gas stored for crons etc.
    uint256[] private totalGooProductionSnapshots; // The total goo production for each prior day past
    uint256[] private totalGooDepositSnapshots;  // The total goo deposited for each prior day past
    uint256[] private allocatedGooResearchSnapshots; // Div pot #1 (research eth allocated to each prior day past)
    uint256[] private allocatedGooDepositSnapshots;  // Div pot #2 (deposit eth allocated to each prior day past)
    uint256 public nextSnapshotTime;
    uint256 public nextGooDepositSnapshotTime;
    uint256 public startTime;

    mapping(address => address[]) public playersReferals;
    mapping(address => address) public playersRef;
    mapping(address => uint256) private lastUnitBoughtEpoch;
    mapping(address => uint256) private lastUpgradeBoughtEpoch;
    address[] public players;

    // Balances for each player
    mapping(address => uint256) private gooBalance;
    mapping(address => mapping(uint256 => uint256)) private gooProductionSnapshots; // Store player's goo production for given day (snapshot)
    mapping(address => mapping(uint256 => uint256)) private gooDepositSnapshots;    // Store player's goo deposited for given day (snapshot)
    mapping(address => mapping(uint256 => bool)) private gooProductionZeroedSnapshots; // This isn't great but we need know difference between 0 production and an unused/inactive day.

    mapping(address => uint256) private lastGooSaveTime; // Seconds (last time player claimed their produced goo)
    mapping(address => uint256) public lastGooProductionUpdate; // Days (last snapshot player updated their production)
    mapping(address => uint256) private lastGooResearchFundClaim; // Days (snapshot number)
    mapping(address => uint256) private lastGooDepositFundClaim; // Days (snapshot number)

    // Stuff owned by each player
    mapping(address => mapping(uint256 => uint256)) private unitsOwned;
    mapping(address => mapping(uint256 => bool)) private upgradesOwned;

    // Upgrades (Increase unit's production / attack etc.)
    mapping(address => mapping(uint256 => uint256)) private unitGooProductionIncreases; // Adds to the goo per second
    mapping(address => mapping(uint256 => uint256)) private unitGooProductionMultiplier; // Multiplies the goo per second
    mapping(address => mapping(uint256 => uint256)) private unitAttackIncreases;
    mapping(address => mapping(uint256 => uint256)) private unitAttackMultiplier;
    mapping(address => mapping(uint256 => uint256)) private unitDefenseIncreases;
    mapping(address => mapping(uint256 => uint256)) private unitDefenseMultiplier;
    mapping(address => mapping(uint256 => uint256)) private unitGooStealingIncreases;
    mapping(address => mapping(uint256 => uint256)) private unitGooStealingMultiplier;
    mapping(address => mapping(uint256 => uint256)) private unitCostReduction;

    event PlayerAttacked(address attacker, address target, bool success, uint256 gooStolen);
    event RefDeposit(address player, address referer, uint256 amount);
    event TalentPurchase(address player, uint256 class, uint256 unitId);

    modifier SnapshotCheck {
        if (now >= nextSnapshotTime && nextSnapshotTime != 0){
            snapshotDailyGooResearchFunding();
        }
        if (now >= nextGooDepositSnapshotTime && nextGooDepositSnapshotTime != 0){
            snapshotDailyGooDepositFunding();
        }
        _;
    }

    function() payable external {}

    constructor() public {
        blast.configureClaimableYield();
        blast.configureClaimableGas();
    }

    function beginGame(uint256 firstDivsTime) external {
        require(msg.sender == owner);
        require(startTime == 0);
        startTime = now;
        nextSnapshotTime = firstDivsTime;
        nextGooDepositSnapshotTime = firstDivsTime + 12 hours;
        totalGooDepositSnapshots.push(0); // Add initial-zero snapshot
    }

    function balanceOf(address player) public view returns(uint256) {
        return gooBalance[player] + balanceOfUnclaimedGoo(player);
    }

    function balanceOfUnclaimedGoo(address player) internal view returns (uint256) {
        uint256 lastSave = lastGooSaveTime[player];
        if (lastSave > 0 && lastSave < now) {
            return (getGooProduction(player) * (now - lastSave)) / 100;
        }
        return 0;
    }

    function getGooProduction(address player) public view returns (uint256){
        return gooProductionSnapshots[player][lastGooProductionUpdate[player]];
    }

    function updatePlayersGoo(address player) public {
        uint256 gooGain = balanceOfUnclaimedGoo(player);
        lastGooSaveTime[player] = now;
        gooBalance[player] += gooGain;
    }

    function updatePlayersGooFromPurchase(address player, uint256 purchaseCost) internal {
        uint256 unclaimedGoo = balanceOfUnclaimedGoo(player);

        if (purchaseCost > unclaimedGoo) {
            uint256 gooDecrease = purchaseCost - unclaimedGoo;
            require(gooBalance[player] >= gooDecrease);
            gooBalance[player] -= gooDecrease;
        } else {
            uint256 gooGain = unclaimedGoo - purchaseCost;
            gooBalance[player] += gooGain;
        }

        lastGooSaveTime[player] = now;
    }

    function increasePlayersGooProduction(address player, uint256 increase) internal {
        gooProductionSnapshots[player][allocatedGooResearchSnapshots.length] = getGooProduction(player) + increase;
        lastGooProductionUpdate[player] = allocatedGooResearchSnapshots.length;
        totalGooProduction += increase;
    }

    function reducePlayersGooProduction(address player, uint256 decrease) internal {
        uint256 previousProduction = getGooProduction(player);
        uint256 newProduction = previousProduction.sub(decrease);

        if (newProduction == 0) { // Special case which tangles with "inactive day" snapshots (claiming divs)
            gooProductionZeroedSnapshots[player][allocatedGooResearchSnapshots.length] = true;
            delete gooProductionSnapshots[player][allocatedGooResearchSnapshots.length]; // 0
        } else {
            gooProductionSnapshots[player][allocatedGooResearchSnapshots.length] = newProduction;
        }

        lastGooProductionUpdate[player] = allocatedGooResearchSnapshots.length;
        totalGooProduction -= decrease;
    }

    function addEth() external payable {
        totalEthResearchPool += msg.value;
    }

    function claimBlastYield() external {
        totalEthResearchPool += blast.claimAllYield(address(this), address(this));
    }

    function claimGasYield(bool claimAll) external {
        require(msg.sender == owner);
        uint256 gas;
        if (claimAll) {
            gas = blast.claimAllGas(address(this), address(this));
        } else {
            gas = blast.claimMaxGas(address(this), address(this));
        }
        platformGas += (gas * 5) / 100; // 5% gas saved for platform
        totalEthResearchPool += (gas * 95) / 100;
    }

    function pullGas(address payable recipient) external  {
        require(msg.sender == owner);
        recipient.transfer(platformGas);
        delete platformGas;
    }

    function recoverToken(address erc20, uint256 amount) external  {
        require(msg.sender == owner); // Just incase someone accidently sends random token here
        ERC20(erc20).transfer(owner, amount);
    }

    function withdrawEth() external SnapshotCheck {
        claimResearchDividends();
        claimGooDepositDividends();
    }

    function fundGooResearch(uint256 amount) external SnapshotCheck {
        updatePlayersGooFromPurchase(msg.sender, amount);
        address ref = playersRef[msg.sender];
        if (ref != address(0)) {
            uint256 refPercent = amount * 5 / 100;
            gooDepositSnapshots[ref][totalGooDepositSnapshots.length - 1] += refPercent;
            totalGooDepositSnapshots[totalGooDepositSnapshots.length - 1] += (amount + refPercent);
            emit RefDeposit(msg.sender, ref, refPercent);
        } else {
            totalGooDepositSnapshots[totalGooDepositSnapshots.length - 1] += amount;
        }
        gooDepositSnapshots[msg.sender][totalGooDepositSnapshots.length - 1] += amount;
    }

    function claimResearchDividends() public SnapshotCheck {
        uint256 startSnapshot = lastGooResearchFundClaim[msg.sender];
        uint256 endSnapShot = allocatedGooResearchSnapshots.length - 1;

        uint256 researchShare;
        uint256 previousProduction;
        if (startSnapshot > 0) {
            previousProduction = gooProductionSnapshots[msg.sender][startSnapshot - 1];
        }

        for (uint256 i = startSnapshot; i <= endSnapShot; i++) {

            // Slightly complex things by accounting for days/snapshots when user made no tx's
            uint256 productionDuringSnapshot = gooProductionSnapshots[msg.sender][i];
            bool soldAllProduction = gooProductionZeroedSnapshots[msg.sender][i];
            if (productionDuringSnapshot == 0 && !soldAllProduction) {
                productionDuringSnapshot = previousProduction;
            } else {
               previousProduction = productionDuringSnapshot;
            }

            uint256 totalProduction = totalGooProductionSnapshots[i];
            if (totalProduction > 0) {
                researchShare += (allocatedGooResearchSnapshots[i] * productionDuringSnapshot) / totalProduction;
            }
        }


        if (gooProductionSnapshots[msg.sender][endSnapShot] == 0 && !gooProductionZeroedSnapshots[msg.sender][endSnapShot] && previousProduction > 0) {
            gooProductionSnapshots[msg.sender][endSnapShot] = previousProduction; // Checkpoint for next claim
        }

        lastGooResearchFundClaim[msg.sender] = endSnapShot + 1;
        msg.sender.transfer(researchShare);
    }


    function claimGooDepositDividends() public SnapshotCheck {
        uint256 startSnapshot = lastGooDepositFundClaim[msg.sender];
        uint256 endSnapShot = allocatedGooDepositSnapshots.length - 1;

        uint256 depositShare;
        if (allocatedGooDepositSnapshots.length > 0) {
            for (uint256 i = startSnapshot; i <= endSnapShot; i++) {
                uint256 totalDeposited = totalGooDepositSnapshots[i];
                if (totalDeposited > 0) {
                    depositShare += (allocatedGooDepositSnapshots[i] * gooDepositSnapshots[msg.sender][i]) / totalDeposited;
                }
            }
        }

        lastGooDepositFundClaim[msg.sender] = endSnapShot + 1;
        msg.sender.transfer(depositShare);
    }


    // Allocate pot #1 divs for the day
    function snapshotDailyGooResearchFunding() public {
        require(now >= nextSnapshotTime);

        uint256 todaysGooResearchFund = (totalEthResearchPool * researchDivPercent) / 100; // 4% of pool daily
        totalEthResearchPool -= todaysGooResearchFund;

        totalGooProductionSnapshots.push(totalGooProduction);
        allocatedGooResearchSnapshots.push(todaysGooResearchFund);
        nextSnapshotTime = now + 24 hours;
    }

    // Allocate pot #2 divs for the day
    function snapshotDailyGooDepositFunding() public {
        require(now >= nextGooDepositSnapshotTime);

        uint256 todaysGooDepositFund = (totalEthResearchPool * gooDepositDivPercent) / 100; // 1% of pool daily
        totalEthResearchPool -= todaysGooDepositFund;

        totalGooDepositSnapshots.push(0); // Reset for to store next day's deposits
        allocatedGooDepositSnapshots.push(todaysGooDepositFund); // Store to payout divs for previous day deposits
        nextGooDepositSnapshotTime = now + 24 hours;
    }


    function getUnitsProduction(address player, uint256 unitId, uint256 amount) internal view returns (uint256) {
        return (amount * (unitInfo[unitId].baseGooProduction + unitGooProductionIncreases[player][unitId]) * (10 + unitGooProductionMultiplier[player][unitId]));
    }

    function getUnitsAttack(address player, uint256 unitId, uint256 amount) internal view returns (uint256) {
        return (amount * (unitInfo[unitId].attackValue + unitAttackIncreases[player][unitId]) * (10 + unitAttackMultiplier[player][unitId])) / 10;
    }

    function getUnitsDefense(address player, uint256 unitId, uint256 amount) internal view returns (uint256) {
        return (amount * (unitInfo[unitId].defenseValue + unitDefenseIncreases[player][unitId]) * (10 + unitDefenseMultiplier[player][unitId])) / 10;
    }

    function getUnitsStealingCapacity(address player, uint256 unitId, uint256 amount) internal view returns (uint256) {
        return (amount * (unitInfo[unitId].gooStealingCapacity + unitGooStealingIncreases[player][unitId]) * (10 + unitGooStealingMultiplier[player][unitId])) / 10;
    }












    ////////////////////////// GOO 2 ///////////////////////////

    mapping(uint256 => Unit) private unitInfo;
    mapping(uint256 => Upgrade) private upgradeInfo;
    mapping(uint256 => mapping(uint256 => uint256)) private talentUpgradeValues;

    mapping(address => UserLevel) public playerLevel;
    mapping(address => uint256) public lastBattleChargeTime;
    mapping(address => bool) public gooContract;

    uint256 constant public maxBattleCharges = 4;
    uint256 constant public battleChargeRechargeTime = 6 hours;
    uint256 constant public talentGooDuration = 12 hours;
    uint256 public stealingMultiplier = 1; // 1 goo per steal
    uint256 public defenseGooProtection = 1000; // 1k goo per def

    uint256 public numUpgrades;
    uint256 public numProdUnits;
    uint256 public numArmyUnits;
    uint256 public numTalents;

    struct UserLevel {
        uint48 level;
        uint48 spentPoints;
        uint160 experience;
    }

    struct Unit {
        uint256 unitId;
        uint256 baseGooCost;
        uint256 gooCostIncreaseHalf; // Halfed to make maths slightly less (cancels a 2 out)
        uint256 baseGooProduction;
        address payable farmAddress;

        uint256 attackValue;
        uint256 defenseValue;
        uint256 gooStealingCapacity;
        uint256 levelRequirement;
    }

    struct Upgrade {
        uint256 upgradeId;
        uint256 gooCost;
        uint256 upgradeClass;
        uint256 unitId;
        uint256 upgradeValue;
        uint256 prerequisiteUpgrade;
    }

    function setGooContract(address gameContract, bool isGoo) external {
        require(msg.sender == owner);
        gooContract[gameContract] = isGoo;
    }

    function updateStealingMultiplier(uint256 newStealingMultiplier) external {
        require(msg.sender == owner);
        stealingMultiplier = newStealingMultiplier;
    }

    function updateDefenseGooProtection(uint256 newDefenseGooProtection) external {
        require(msg.sender == owner);
        defenseGooProtection = newDefenseGooProtection;
    }

    function addUpgrade(uint256 id, uint256 goo, uint256 class, uint256 unit, uint256 value, uint256 prereq) external {
        require(msg.sender == owner);
        if (upgradeInfo[id].upgradeId == 0) {
            numUpgrades++;
        }
        upgradeInfo[id] = Upgrade(id, goo, class, unit, value, prereq);
    }

    function addUnit(uint256 id, uint256 goo, uint256 gooIncreaseHalf, address payable farmToken, uint256 production, uint256 attack, uint256 defense, uint256 stealing, uint256 levelRequirement) external {
        require(msg.sender == owner);
        require(unitInfo[id].farmAddress == address(0));
        if (unitInfo[id].unitId == 0) {
            if (production > 0) {
                numProdUnits++;
            } else {
                numArmyUnits++;
            }
        }
        unitInfo[id] = Unit(id, goo, gooIncreaseHalf, production, farmToken, attack, defense, stealing, levelRequirement);
    }

    function addTalent(uint256 talentClass, uint256 unitId, uint256 value) external {
        require(msg.sender == owner);
        if (talentUpgradeValues[talentClass][unitId] == 0) {
            numTalents++;
        }
        talentUpgradeValues[talentClass][unitId] = value;
    }

    function creditUnitExternal(address player, uint256 unitId) external {
        require(gooContract[msg.sender]);
        Unit memory unit = unitInfo[unitId];
        require(unit.unitId > 0); // Valid unit

        if (unit.baseGooProduction > 0) {
            updatePlayersGoo(player);
            uint256 production = (unit.baseGooProduction + unitGooProductionIncreases[player][unitId]) * (10 + unitGooProductionMultiplier[player][unitId]);
            increasePlayersGooProduction(player, production);
        }
        unitsOwned[player][unitId]++;
    }

    function creditGooExternal(address player, uint256 gooGain) external {
        require(gooContract[msg.sender]); // Item Shop contract
        gooBalance[player] += gooGain;
    }

    function updatePlayersGooFromPurchaseExternal(address player, uint256 purchaseCost) external {
        require(gooContract[msg.sender]);
        updatePlayersGooFromPurchase(player, purchaseCost);
    }

    function upgradeUnitMultipliersExternal(address player, uint256 upgradeClass, uint256 unitId, uint256 upgradeValue) external {
        require(gooContract[msg.sender]);
        upgradeUnitMultipliers(player, upgradeClass, unitId, upgradeValue);
    }

    function upgradeUnitMultipliers(address player, uint256 upgradeClass, uint256 unitId, uint256 upgradeValue) internal {
        uint256 productionGain;
        if (upgradeClass == 0) {
            unitGooProductionIncreases[player][unitId] += upgradeValue;
            productionGain = unitsOwned[player][unitId] * upgradeValue * (10 + unitGooProductionMultiplier[player][unitId]);
            increasePlayersGooProduction(player, productionGain);
        } else if (upgradeClass == 1) {
            Unit memory unit = unitInfo[unitId];
            unitGooProductionMultiplier[player][unitId] += upgradeValue;
            productionGain = unitsOwned[player][unitId] * upgradeValue * (unit.baseGooProduction + unitGooProductionIncreases[player][unitId]);
            increasePlayersGooProduction(player, productionGain);
        } else if (upgradeClass == 2) {
            unitAttackIncreases[player][unitId] += upgradeValue;
        } else if (upgradeClass == 3) {
            unitAttackMultiplier[player][unitId] += upgradeValue;
        } else if (upgradeClass == 4) {
            unitDefenseIncreases[player][unitId] += upgradeValue;
        } else if (upgradeClass == 5) {
            unitDefenseMultiplier[player][unitId] += upgradeValue;
        } else if (upgradeClass == 6) {
            unitGooStealingIncreases[player][unitId] += upgradeValue;
        } else if (upgradeClass == 7) {
            unitGooStealingMultiplier[player][unitId] += upgradeValue;
        } else if (upgradeClass == 9) {
            unitCostReduction[player][unitId] += upgradeValue; // Unit cost reduction
        }
    }

    function removeUnitMultipliersExternal(address player, uint256 upgradeClass, uint256 unitId, uint256 upgradeValue) external {
        require(gooContract[msg.sender]);
        removeUnitMultipliers(player, upgradeClass, unitId, upgradeValue);
    }

    function removeUnitMultipliers(address player, uint256 upgradeClass, uint256 unitId, uint256 upgradeValue) internal {
        uint256 productionLoss;
        if (upgradeClass == 0) {
            unitGooProductionIncreases[player][unitId] -= upgradeValue;
            productionLoss = unitsOwned[player][unitId] * upgradeValue * (10 + unitGooProductionMultiplier[player][unitId]);
            reducePlayersGooProduction(player, productionLoss);
        } else if (upgradeClass == 1) {
            Unit memory unit = unitInfo[unitId];
            unitGooProductionMultiplier[player][unitId] -= upgradeValue;
            productionLoss = unitsOwned[player][unitId] * upgradeValue * (unit.baseGooProduction + unitGooProductionIncreases[player][unitId]);
            reducePlayersGooProduction(player, productionLoss);
        } else if (upgradeClass == 2) {
            unitAttackIncreases[player][unitId] -= upgradeValue;
        } else if (upgradeClass == 3) {
            unitAttackMultiplier[player][unitId] -= upgradeValue;
        } else if (upgradeClass == 4) {
            unitDefenseIncreases[player][unitId] -= upgradeValue;
        } else if (upgradeClass == 5) {
            unitDefenseMultiplier[player][unitId] -= upgradeValue;
        } else if (upgradeClass == 6) {
            unitGooStealingIncreases[player][unitId] -= upgradeValue;
        } else if (upgradeClass == 7) {
            unitGooStealingMultiplier[player][unitId] -= upgradeValue;
        } else if (upgradeClass == 9) {
            unitCostReduction[player][unitId] -= upgradeValue; // Unit cost reduction
        }
    }

    function getGooCostForUnit(uint256 unitId, uint256 existing, uint256 amount, uint256 discount) internal view returns (uint256 totalCost) {
        Unit storage unit = unitInfo[unitId];
        if (amount == 1) { // 1
            if (existing == 0) {
                totalCost = unit.baseGooCost;
            } else {
                totalCost = unit.baseGooCost + (existing * unit.gooCostIncreaseHalf * 2);
            }
        } else if (amount > 1) {
            uint256 existingCost;
            if (existing > 0) { // Gated by unit limit
                existingCost = (unit.baseGooCost * existing) + (existing * (existing - 1) * unit.gooCostIncreaseHalf);
            }

            existing = existing.add(amount);
            totalCost = unit.baseGooCost.mul(existing).add(existing.mul(existing - 1).mul(unit.gooCostIncreaseHalf)) - existingCost;
        }

        if (discount > 0 && discount < 100) {
            totalCost = (totalCost * (100 - discount)) / 100;
        }
    }

    function daysSinceEpoch() public view returns(uint256) {
        return (now / 86400);
    }

    // To display on website
    function getGameInfo() external view returns (uint256[13] memory data) {
        UserLevel memory player = playerLevel[msg.sender];
        uint256 snapshotsLength = totalGooDepositSnapshots.length - 1;

        data[0] = now;
        data[1] = totalEthResearchPool;
        data[2] = totalGooProduction;
        data[3] = totalGooDepositSnapshots[snapshotsLength];
        data[4] = gooDepositSnapshots[msg.sender][snapshotsLength];
        data[5] = nextSnapshotTime;
        data[6] = balanceOf(msg.sender);
        data[7] = player.level;
        data[8] = player.spentPoints;
        data[9] = player.experience;
        data[10] = getGooProduction(msg.sender);
        data[11] = nextGooDepositSnapshotTime;
        data[12] = stealingMultiplier;
    }

    function getUnitsOwned(uint256[] calldata ids) external view returns (uint256[] memory) {
        uint256[] memory units = new uint256[](ids.length);

        for (uint256 i = 0; i < ids.length; i++) {
            units[i] = unitsOwned[msg.sender][ids[i]];
        }

        return units;
    }

    function getUpgradesOwned(uint256[] calldata ids) external view returns (bool[] memory) {
        bool[] memory upgrades = new bool[](ids.length);

        for (uint256 i = 0; i < ids.length; i++) {
            upgrades[i] = upgradesOwned[msg.sender][ids[i]];
        }

        return upgrades;
    }

    function getUpgradeValues(uint256[] calldata ids) external view returns (uint256[9][] memory) {
        uint256[9][] memory upgradeValues = new uint256[9][](ids.length);

        for (uint256 i = 0; i < ids.length; i++) {
            uint256 unitId = ids[i];
            upgradeValues[i][0] = unitGooProductionIncreases[msg.sender][unitId];
            upgradeValues[i][1] = unitGooProductionMultiplier[msg.sender][unitId];
            upgradeValues[i][2] = unitAttackIncreases[msg.sender][unitId];
            upgradeValues[i][3] = unitAttackMultiplier[msg.sender][unitId];
            upgradeValues[i][4] = unitDefenseIncreases[msg.sender][unitId];
            upgradeValues[i][5] = unitDefenseMultiplier[msg.sender][unitId];
            upgradeValues[i][6] = unitGooStealingIncreases[msg.sender][unitId];
            upgradeValues[i][7] = unitGooStealingMultiplier[msg.sender][unitId];
            upgradeValues[i][8] = unitCostReduction[msg.sender][unitId];
        }

        return upgradeValues;
    }

    function numPlayers() external view returns (uint256) {
        return players.length;
    }

    function getPlayersInfo(uint256 start, uint256 amount) external view returns (address[] memory, uint256[5][] memory, uint256) {
        uint256 toFetch = amount;
        uint256 totalPlayers = players.length;
        if (players.length < toFetch) {
            toFetch = totalPlayers;
        }

        address[] memory users = new address[](toFetch);
        uint256[5][] memory stats = new uint256[5][](toFetch);

        uint256 index = start;
        for (uint256 i = 0; i < toFetch; i++) {
            if (index >= totalPlayers) {
                index = index - totalPlayers;
            }

            address player = players[index];
            users[i] = player;
            stats[i][0] = balanceOf(player);
            stats[i][1] = getGooProduction(player);
            (stats[i][2], stats[i][3], stats[i][4]) = getPlayersBattlePower(player);
            index++;
        }

        return (users, stats, index);
    }

    function getDailyXP() view external returns (uint256, uint256, uint256) {
        return (daysSinceEpoch(), lastUnitBoughtEpoch[msg.sender], lastUpgradeBoughtEpoch[msg.sender]);
    }

    function getReferals() view external returns (address[] memory) {
        return playersReferals[msg.sender];
    }

    function viewUnclaimedDividends() external view returns (uint256, uint256) {
        uint256 startSnapshot = lastGooResearchFundClaim[msg.sender];
        uint256 latestSnapshot;

        uint256 researchShare;
        uint256 previousProduction;

        if (startSnapshot > 0) {
            previousProduction = gooProductionSnapshots[msg.sender][startSnapshot - 1];
        }

        if (allocatedGooResearchSnapshots.length > 0) {
            latestSnapshot = allocatedGooResearchSnapshots.length - 1;
            for (uint256 i = startSnapshot; i <= latestSnapshot; i++) {
                // Slightly complex things by accounting for days/snapshots when user made no tx's
                uint256 productionDuringSnapshot = gooProductionSnapshots[msg.sender][i];
                bool soldAllProduction = gooProductionZeroedSnapshots[msg.sender][i];
                if (productionDuringSnapshot == 0 && !soldAllProduction) {
                    productionDuringSnapshot = previousProduction;
                } else {
                   previousProduction = productionDuringSnapshot;
                }

                uint256 totalProduction = totalGooProductionSnapshots[i];
                if (totalProduction > 0) {
                    researchShare += (allocatedGooResearchSnapshots[i] * productionDuringSnapshot) / totalProduction;
                }
            }
        }

        uint256 depositShare;
        if (allocatedGooDepositSnapshots.length > 0) {
            latestSnapshot = allocatedGooDepositSnapshots.length - 1;
            for (uint256 j = lastGooDepositFundClaim[msg.sender]; j <= latestSnapshot; j++) {
                uint256 totalDeposits = totalGooDepositSnapshots[j];
                if (totalDeposits > 0) {
                    depositShare += (allocatedGooDepositSnapshots[j] * gooDepositSnapshots[msg.sender][j]) / totalDeposits;
                }
            }
        }

        return (researchShare, depositShare);
    }

    function buyFarmUnit(uint256 unitId, uint256 amount, address referer) external payable SnapshotCheck {
        Unit memory unit = unitInfo[unitId];
        require(unit.farmAddress != address(0));

        if (getGooProduction(msg.sender) == 0) {
            if (referer != msg.sender && referer != address(0)) { 
                playersRef[msg.sender] = referer;
                playersReferals[referer].push(msg.sender);
            }
            lastUnitBoughtEpoch[msg.sender] = daysSinceEpoch();
            giveExp(msg.sender, 40);
            players.push(msg.sender);
        } else {
            uint256 daysSince = daysSinceEpoch();
            if (lastUnitBoughtEpoch[msg.sender] < daysSince) {
                lastUnitBoughtEpoch[msg.sender] = daysSince;
                giveExp(msg.sender, 40);
            }
        }
        
        GooFarm farm = GooFarm(unit.farmAddress);
        if (farm.farmToken() == address(0)) {
            require(msg.value == amount * (10 ** 16)); // 1 eth = 100 units
            farm.stake.value(msg.value)(msg.sender);
        } else {
            TokenGooFarm(unit.farmAddress).stake(msg.sender, amount);
        }

        updatePlayersGoo(msg.sender);
        if (unit.baseGooProduction > 0) {
            increasePlayersGooProduction(msg.sender, (unit.baseGooProduction + unitGooProductionIncreases[msg.sender][unitId]) * (10 + unitGooProductionMultiplier[msg.sender][unitId]).mul(amount));
        }
        unitsOwned[msg.sender][unitId] = unitsOwned[msg.sender][unitId] + amount;
    }

    function sellFarmUnit(uint256 unitId, uint256 amount) external SnapshotCheck {
        Unit memory unit = unitInfo[unitId];
        require(unit.farmAddress != address(0));

        GooFarm farm = GooFarm(unit.farmAddress);
        if (farm.farmToken() == address(0)) {
            farm.unstake(msg.sender, amount * (10 ** 16)); // 1 eth = 100 units
        } else {
            TokenGooFarm(unit.farmAddress).unstake(msg.sender, amount);
        }

        updatePlayersGoo(msg.sender);
        if (unit.baseGooProduction > 0) {
            reducePlayersGooProduction(msg.sender, (unit.baseGooProduction + unitGooProductionIncreases[msg.sender][unitId]) * (10 + unitGooProductionMultiplier[msg.sender][unitId]).mul(amount));
        }
        unitsOwned[msg.sender][unitId] = unitsOwned[msg.sender][unitId].sub(amount);
    }

    function buyUnit(uint256 unitId, uint256 amount) external SnapshotCheck {
        Unit memory unit = unitInfo[unitId];
        address player = msg.sender;
        require(unit.unitId > 0); // Valid unit
        require(playerLevel[msg.sender].level >= unit.levelRequirement);
        require(unit.farmAddress == address(0));

        if (getGooProduction(player) == 0) {
            players.push(player);
        }

        uint256 existing = unitsOwned[player][unitId];
        uint256 newTotal = existing.add(amount);

        updatePlayersGooFromPurchase(player, getGooCostForUnit(unitId, existing, amount, unitCostReduction[player][unitId]));

        if (unit.baseGooProduction > 0) {
            increasePlayersGooProduction(player, (unit.baseGooProduction + unitGooProductionIncreases[player][unitId]) * (10 + unitGooProductionMultiplier[player][unitId]).mul(amount));
        }
        unitsOwned[player][unitId] = newTotal;

        uint256 daysSince = daysSinceEpoch();
        if (lastUnitBoughtEpoch[msg.sender] < daysSince) {
            lastUnitBoughtEpoch[msg.sender] = daysSince;
            giveExp(msg.sender, 40);
        }
    }

    function buyUpgrade(uint256 upgradeId) external SnapshotCheck {
        Upgrade memory upgrade = upgradeInfo[upgradeId];
        address player = msg.sender;
        require(upgrade.upgradeId > 0); // Valid upgrade
        require(!upgradesOwned[player][upgradeId]); // Haven't already purchased

        if (upgrade.prerequisiteUpgrade > 0) {
            require(upgradesOwned[player][upgrade.prerequisiteUpgrade]);
        }

        uint256 daysSince = daysSinceEpoch();
        if (lastUpgradeBoughtEpoch[msg.sender] < daysSince) {
            lastUpgradeBoughtEpoch[msg.sender] = daysSince;
            giveExp(msg.sender, 40);
        }

        // Update players goo
        updatePlayersGooFromPurchase(player, upgrade.gooCost);

        upgradeUnitMultipliers(player, upgrade.upgradeClass, upgrade.unitId, upgrade.upgradeValue);
        upgradesOwned[player][upgradeId] = true;
    }


    function getBattleChargeTime() external view returns (uint256, uint256, uint256, uint256) {
        uint256 attackingPower;
        uint256 defendingPower;
        uint256 stealingPower;

        for (uint256 i = 0; i < numArmyUnits; i++) {
            uint256 unitId = i + 101; // Starts at 101
            uint256 owned = unitsOwned[msg.sender][unitId];
            attackingPower += getUnitsAttack(msg.sender, unitId, owned);
            defendingPower += getUnitsDefense(msg.sender, unitId, owned);
            stealingPower += getUnitsStealingCapacity(msg.sender, unitId, owned);
        }

        uint256 playerbattleChargeTime = lastBattleChargeTime[msg.sender];
        if (playerbattleChargeTime == 0) {
            playerbattleChargeTime = startTime - 12 hours + 5;
        }

        return (playerbattleChargeTime, attackingPower, defendingPower, stealingPower);
    }

    function getPlayersBattlePower(address player) public view returns (uint256, uint256, uint256) {
        uint256 attackingPower;
        uint256 defendingPower;
        uint256 stealingPower;

        for (uint256 i = 0; i < numArmyUnits; i++) {
            uint256 unitId = i + 101; // Starts at 101
            uint256 owned = unitsOwned[player][unitId];
            attackingPower += getUnitsAttack(player, unitId, owned);
            defendingPower += getUnitsDefense(player, unitId, owned);
            stealingPower += getUnitsStealingCapacity(player, unitId, owned);
        }

        return (attackingPower, defendingPower, stealingPower);
    }

    function getPlayersBattlePower(address attacker, address defender) internal view returns (uint256, uint256, uint256) {
        uint256 attackingPower;
        uint256 defendingPower;
        uint256 stealingPower;

        for (uint256 i = 0; i < numArmyUnits; i++) {
            uint256 unitId = i + 101; // Starts at 101
            uint256 owned = unitsOwned[attacker][unitId];
            attackingPower += getUnitsAttack(attacker, unitId, owned);
            stealingPower += getUnitsStealingCapacity(attacker, unitId, owned);

            defendingPower += getUnitsDefense(defender, unitId, unitsOwned[defender][unitId]);
        }

        return (attackingPower, defendingPower, stealingPower);
    }

    function attackPlayer(address target) external {
        require(target != msg.sender);
        require(tx.origin == msg.sender);

        (uint256 attackingPower, uint256 defendersPower, uint256 stealingPower) = getPlayersBattlePower(msg.sender, target);
        uint256 targetsGoo = balanceOf(target);
        bool attackWon = attackingPower > defendersPower;

        uint256 playerbattleChargeTime = lastBattleChargeTime[msg.sender];
        if (playerbattleChargeTime == 0) {
            playerbattleChargeTime = startTime - 12 hours + 5;
        }

        uint256 battleCharges = (now - playerbattleChargeTime) / battleChargeRechargeTime;
        require(battleCharges > 0);

        uint256 gooStolen;
        if (attackWon) {
            uint256 stealable;
            uint256 gooProtected = defendersPower * defenseGooProtection;
            if (targetsGoo > gooProtected) {
                stealable = targetsGoo - gooProtected;
            }

            stealingPower = stealingPower * stealingMultiplier;
            if (stealable > stealingPower) {
                gooStolen = stealingPower;
            } else {
                gooStolen = stealable;
            }

            if (gooStolen > 0) {
                // Save all their unclaimed goo, then steal attacker's max capacity (at same time)
                uint256 unclaimedGoo = balanceOfUnclaimedGoo(target);
                if (gooStolen > unclaimedGoo) {
                    gooBalance[target] -= (gooStolen - unclaimedGoo);
                } else {
                    gooBalance[target] += (unclaimedGoo - gooStolen);
                }
                lastGooSaveTime[target] = now;
                gooBalance[msg.sender] += gooStolen;
            }
            giveExp(msg.sender, 40);
        } else {
            giveExp(msg.sender, 20);
        }

        uint256 newBattleChargeTime = playerbattleChargeTime + battleChargeRechargeTime - 6;
        if (battleCharges >= maxBattleCharges) {
            newBattleChargeTime = now - ((maxBattleCharges - 1) * battleChargeRechargeTime) - 6;
        }
        lastBattleChargeTime[msg.sender] = newBattleChargeTime;
        emit PlayerAttacked(msg.sender, target, attackWon, gooStolen);
    }

    function giveExp(address player, uint160 amount) internal {
        UserLevel memory existingExp = playerLevel[player];
        uint160 expRequirement = (existingExp.level + 1) * 80; // Lvl 1: 80; Lvl 2: 160, Lvl 3: 240 (480 in total) etc.
        if (existingExp.experience + amount >= expRequirement) {
            existingExp.experience = (existingExp.experience + amount) - expRequirement;
            existingExp.level++;
            playerLevel[player] = existingExp;
        } else {
            playerLevel[player].experience += amount;
        }
    }

    function spendTalentPoint(uint256 talentClass, uint256 unitId) external {
        UserLevel memory user = playerLevel[msg.sender];
        Unit memory unit = unitInfo[unitId];
        require(user.spentPoints < user.level);

        if (talentClass < 8) {
            require(unit.unitId > 0);
            upgradeUnitMultipliers(msg.sender, talentClass, unitId, talentUpgradeValues[talentClass][unitId]);
        } else if (talentClass == 8) {
            lastBattleChargeTime[msg.sender] = now - (maxBattleCharges * battleChargeRechargeTime) - 6; // Replenish battle charges
        } else {
            gooBalance[msg.sender] += (getGooProduction(msg.sender) / 100) * talentGooDuration; // 12 hours of free goo
        }
        playerLevel[msg.sender].spentPoints++;
        emit TalentPurchase(msg.sender, talentClass, unitId);
    }

}


interface TokenGooFarm {
    function stake(address player, uint256 amount) external;
    function unstake(address payable player, uint256 amount) external;
}


interface Blast {
    function configureClaimableYield() external;
    function configureClaimableGas() external;
    function claimAllYield(address contractAddress, address recipientOfYield) external returns (uint256);
    function claimMaxGas(address contractAddress, address recipient) external returns (uint256);
    function claimAllGas(address contractAddress, address recipientOfGas) external returns (uint256);
    function readClaimableYield(address contractAddress) external view returns (uint256);
}

interface BlastPoints {
  function configurePointsOperator(address operator) external;
}


interface ERC20 {
	function totalSupply() external view returns (uint256);
	function balanceOf(address who) external view returns (uint256);
	function allowance(address owner, address spender) external view returns (uint256);
	function transfer(address to, uint256 value) external returns (bool);
	function approve(address spender, uint256 value) external returns (bool);
	function approveAndCall(address spender, uint tokens, bytes calldata data) external returns (bool success);
	function transferFrom(address from, address to, uint256 value) external returns (bool);
	function burn(uint256 amount) external;
    function decimals() external view returns (uint8);

	event Transfer(address indexed from, address indexed to, uint256 value);
	event Approval(address indexed owner, address indexed spender, uint256 value);
}


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

  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a / b;
    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;
  }

  function ceil(uint256 a, uint256 m) internal pure returns (uint256) {
    uint256 c = add(a,m);
    uint256 d = sub(c,1);
    return mul(div(d,m),m);
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":false,"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"class","type":"uint256"},{"internalType":"uint256","name":"unit","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"addRare","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"rareId","type":"uint256"}],"name":"buyRareItem","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"claimBlastYield","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable","name":"winner","type":"address"},{"internalType":"uint256","name":"rareId","type":"uint256"}],"name":"creditRaffleItem","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getRareItemInfo","outputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"numRares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"erc20","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"recoverToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"raffleAddress","type":"address"}],"name":"updateRaffle","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]

6080604052600180546001600160a01b03199081169091556002805490911633179055600a60035566038d7ea4c6800060045534801561003e57600080fd5b507343000000000000000000000000000000000000026001600160a01b031663f098767a6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561008e57600080fd5b505af11580156100a2573d6000803e3d6000fd5b505050507343000000000000000000000000000000000000026001600160a01b0316634e606c476040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156100f557600080fd5b505af1158015610109573d6000803e3d6000fd5b5050604080516336b91f2b60e01b81523360048201529051732536fe9ab3f511540f2f9e2ec2a805005c3dd80093506336b91f2b9250602480830192600092919082900301818387803b15801561015f57600080fd5b505af1158015610173573d6000803e3d6000fd5b50505050610ebd806101866000396000f3fe60806040526004361061007b5760003560e01c806384770ab31161004e57806384770ab31461012e5780639e30dd5f14610167578063b1d6600314610184578063b29a8140146102775761007b565b8063357ac1041461007d5780633a60c8c7146100bf57806351e5602c146100d457806361c0353614610107575b005b34801561008957600080fd5b5061007b600480360360a08110156100a057600080fd5b50803590602081013590604081013590606081013590608001356102b0565b3480156100cb57600080fd5b5061007b6103b6565b3480156100e057600080fd5b5061007b600480360360208110156100f757600080fd5b50356001600160a01b031661051f565b34801561011357600080fd5b5061011c610558565b60408051918252519081900360200190f35b34801561013a57600080fd5b5061007b6004803603604081101561015157600080fd5b506001600160a01b03813516906020013561055e565b61007b6004803603602081101561017d57600080fd5b503561079c565b34801561019057600080fd5b50610199610b9b565b60405180806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b838110156101e15781810151838201526020016101c9565b50505050905001848103835286818151815260200191508051906020019060200280838360005b83811015610220578181015183820152602001610208565b50505050905001848103825285818151815260200191508051906020019060200280838360005b8381101561025f578181015183820152602001610247565b50505050905001965050505050505060405180910390f35b34801561028357600080fd5b5061007b6004803603604081101561029a57600080fd5b506001600160a01b038135169060200135610d40565b6002546001600160a01b031633146102c757600080fd5b6000851180156102ef57506000858152602081905260409020600501546001600160a01b0316155b6102f857600080fd5b600085815260208190526040902054610315576005805460010190555b604080516101008101825286815260208082019687528183019586526060820194855260808201938452600060a08301818152600160801b60c0850181815260e086019182529a83529282905293902091518255955160018201559351600285015591516003840155516004830155516005820180546001600160a01b0319166001600160a01b039092169190911790559151600683015551600790910155565b6040805163430021db60e11b81523060048201819052602482015290516000916002604360981b019163860043b69160448082019260209290919082900301818787803b15801561040657600080fd5b505af115801561041a573d6000803e3d6000fd5b505050506040513d602081101561043057600080fd5b505160408051634aa7d2f760e11b81523060048201819052602482015290519192506002604360981b019163954fa5ee916044808201926020929091908290030181600087803b15801561048357600080fd5b505af1158015610497573d6000803e3d6000fd5b505050506040513d60208110156104ad57600080fd5b50516040805163c2ac3aff60e01b815290519290910191735361ee2f443b61e60f19dbe73a324f342619161d9163c2ac3aff91849160048082019260009290919082900301818588803b15801561050357600080fd5b505af1158015610517573d6000803e3d6000fd5b505050505050565b6002546001600160a01b0316331461053657600080fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60055481565b6001546001600160a01b0316331461057557600080fd5b61057d610e3a565b506000818152602081815260409182902082516101008101845281548152600182015492810192909252600281015492820192909252600382015460608201526004820154608082015260058201546001600160a01b031660a08201819052600683015460c083015260079092015460e082015290156105fc57600080fd5b600081606001511161060d57600080fd5b60408051635acb3f0b60e11b81526001600160a01b03851660048201529051735361ee2f443b61e60f19dbe73a324f342619161d9163b5967e1691602480830192600092919082900301818387803b15801561066857600080fd5b505af115801561067c573d6000803e3d6000fd5b5050505060408181015160608301516080840151835163b6db0bb760e01b81526001600160a01b03881660048201526024810193909352604483019190915260648201529051735361ee2f443b61e60f19dbe73a324f342619161d9163b6db0bb791608480830192600092919082900301818387803b1580156106fe57600080fd5b505af1158015610712573d6000803e3d6000fd5b505050506001600160a01b0392831660a0820190815242620151800160e083019081526000938452602084815260409485902084518155908401516001820155938301516002850155606083015160038501556080830151600485015590516005840180546001600160a01b031916919095161790935560c0015160068201559051600790910155565b6107a4610e3a565b506000818152602081815260409182902082516101008101845281548152600182015492810192909252600281015492820192909252600382015460608201526004820154608082015260058201546001600160a01b031660a08201819052600683015460c083015260079092015460e08201529061082257600080fd5b600081606001511161083357600080fd5b8060e00151421161084357600080fd5b600061084e82610ddd565b90508034101561085d57600080fd5b803411156108a85760646066820204341061087757600080fd5b60405133903483900380156108fc02916000818181858888f193505050501580156108a6573d6000803e3d6000fd5b505b60408051635acb3f0b60e11b81523360048201529051735361ee2f443b61e60f19dbe73a324f342619161d9163b5967e1691602480830192600092919082900301818387803b1580156108fa57600080fd5b505af115801561090e573d6000803e3d6000fd5b5050505060408281015160608401516080850151835163b6db0bb760e01b81523360048201526024810193909352604483019190915260648201529051735361ee2f443b61e60f19dbe73a324f342619161d9163b6db0bb791608480830192600092919082900301818387803b15801561098757600080fd5b505af115801561099b573d6000803e3d6000fd5b50505050735361ee2f443b61e60f19dbe73a324f342619161d6001600160a01b031663b5967e168360a001516040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b03168152602001915050600060405180830381600087803b158015610a0f57600080fd5b505af1158015610a23573d6000803e3d6000fd5b5050505060a0820151604080840151606085015160808601518351634f4bbbdf60e01b81526001600160a01b03909516600486015260248501929092526044840152606483015251735361ee2f443b61e60f19dbe73a324f342619161d91634f4bbbdf91608480830192600092919082900301818387803b158015610aa757600080fd5b505af1158015610abb573d6000803e3d6000fd5b50505050600160801b8260c001511015610b0d578160a001516001600160a01b03166108fc6005846020015160040281610af157fe5b049081150290604051600060405180830381858888f150505050505b3360a08301526004600582020460208381019182524260c085019081526000958652908590526040948590208451815591516001830155938301516002820155606083015160038201556080830151600482015560a08301516005820180546001600160a01b0319166001600160a01b03909216919091179055925160068401555060e00151600790910155565b606080606080600554604051908082528060200260200182016040528015610bcd578160200160208202803883390190505b5090506060600554604051908082528060200260200182016040528015610bfe578160200160208202803883390190505b5090506060600554604051908082528060200260200182016040528015610c2f578160200160208202803883390190505b50905060005b600554811015610d3357610c47610e3a565b50600180820160009081526020818152604091829020825161010081018452815481529381015491840191909152600281015491830191909152600381015460608301526004810154608083015260058101546001600160a01b031660a08301819052600682015460c084015260079091015460e08301528551869084908110610ccd57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050610cf681610ddd565b848381518110610d0257fe5b6020026020010181815250508060e00151838381518110610d1f57fe5b602090810291909101015250600101610c35565b5091945092509050909192565b6002546001600160a01b03163314610d5757600080fd5b6002546040805163a9059cbb60e01b81526001600160a01b0392831660048201526024810184905290519184169163a9059cbb916044808201926020929091908290030181600087803b158015610dad57600080fd5b505af1158015610dc1573d6000803e3d6000fd5b505050506040513d6020811015610dd757600080fd5b50505050565b60c08101516000908190421115610df7575060c082015142035b60208301516003549092506305265c0090820283020482811180610e1e5750600454818403105b15610e2d576004549250610e33565b80830392505b5050919050565b604051806101000160405280600081526020016000815260200160008152602001600081526020016000815260200160006001600160a01b031681526020016000815260200160008152509056fea265627a7a72315820857bffe1694c113d3831816f13bb5635ba1dbc7f5c42f55daa11d816c433605764736f6c63430005100032

Deployed Bytecode

0x60806040526004361061007b5760003560e01c806384770ab31161004e57806384770ab31461012e5780639e30dd5f14610167578063b1d6600314610184578063b29a8140146102775761007b565b8063357ac1041461007d5780633a60c8c7146100bf57806351e5602c146100d457806361c0353614610107575b005b34801561008957600080fd5b5061007b600480360360a08110156100a057600080fd5b50803590602081013590604081013590606081013590608001356102b0565b3480156100cb57600080fd5b5061007b6103b6565b3480156100e057600080fd5b5061007b600480360360208110156100f757600080fd5b50356001600160a01b031661051f565b34801561011357600080fd5b5061011c610558565b60408051918252519081900360200190f35b34801561013a57600080fd5b5061007b6004803603604081101561015157600080fd5b506001600160a01b03813516906020013561055e565b61007b6004803603602081101561017d57600080fd5b503561079c565b34801561019057600080fd5b50610199610b9b565b60405180806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b838110156101e15781810151838201526020016101c9565b50505050905001848103835286818151815260200191508051906020019060200280838360005b83811015610220578181015183820152602001610208565b50505050905001848103825285818151815260200191508051906020019060200280838360005b8381101561025f578181015183820152602001610247565b50505050905001965050505050505060405180910390f35b34801561028357600080fd5b5061007b6004803603604081101561029a57600080fd5b506001600160a01b038135169060200135610d40565b6002546001600160a01b031633146102c757600080fd5b6000851180156102ef57506000858152602081905260409020600501546001600160a01b0316155b6102f857600080fd5b600085815260208190526040902054610315576005805460010190555b604080516101008101825286815260208082019687528183019586526060820194855260808201938452600060a08301818152600160801b60c0850181815260e086019182529a83529282905293902091518255955160018201559351600285015591516003840155516004830155516005820180546001600160a01b0319166001600160a01b039092169190911790559151600683015551600790910155565b6040805163430021db60e11b81523060048201819052602482015290516000916002604360981b019163860043b69160448082019260209290919082900301818787803b15801561040657600080fd5b505af115801561041a573d6000803e3d6000fd5b505050506040513d602081101561043057600080fd5b505160408051634aa7d2f760e11b81523060048201819052602482015290519192506002604360981b019163954fa5ee916044808201926020929091908290030181600087803b15801561048357600080fd5b505af1158015610497573d6000803e3d6000fd5b505050506040513d60208110156104ad57600080fd5b50516040805163c2ac3aff60e01b815290519290910191735361ee2f443b61e60f19dbe73a324f342619161d9163c2ac3aff91849160048082019260009290919082900301818588803b15801561050357600080fd5b505af1158015610517573d6000803e3d6000fd5b505050505050565b6002546001600160a01b0316331461053657600080fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60055481565b6001546001600160a01b0316331461057557600080fd5b61057d610e3a565b506000818152602081815260409182902082516101008101845281548152600182015492810192909252600281015492820192909252600382015460608201526004820154608082015260058201546001600160a01b031660a08201819052600683015460c083015260079092015460e082015290156105fc57600080fd5b600081606001511161060d57600080fd5b60408051635acb3f0b60e11b81526001600160a01b03851660048201529051735361ee2f443b61e60f19dbe73a324f342619161d9163b5967e1691602480830192600092919082900301818387803b15801561066857600080fd5b505af115801561067c573d6000803e3d6000fd5b5050505060408181015160608301516080840151835163b6db0bb760e01b81526001600160a01b03881660048201526024810193909352604483019190915260648201529051735361ee2f443b61e60f19dbe73a324f342619161d9163b6db0bb791608480830192600092919082900301818387803b1580156106fe57600080fd5b505af1158015610712573d6000803e3d6000fd5b505050506001600160a01b0392831660a0820190815242620151800160e083019081526000938452602084815260409485902084518155908401516001820155938301516002850155606083015160038501556080830151600485015590516005840180546001600160a01b031916919095161790935560c0015160068201559051600790910155565b6107a4610e3a565b506000818152602081815260409182902082516101008101845281548152600182015492810192909252600281015492820192909252600382015460608201526004820154608082015260058201546001600160a01b031660a08201819052600683015460c083015260079092015460e08201529061082257600080fd5b600081606001511161083357600080fd5b8060e00151421161084357600080fd5b600061084e82610ddd565b90508034101561085d57600080fd5b803411156108a85760646066820204341061087757600080fd5b60405133903483900380156108fc02916000818181858888f193505050501580156108a6573d6000803e3d6000fd5b505b60408051635acb3f0b60e11b81523360048201529051735361ee2f443b61e60f19dbe73a324f342619161d9163b5967e1691602480830192600092919082900301818387803b1580156108fa57600080fd5b505af115801561090e573d6000803e3d6000fd5b5050505060408281015160608401516080850151835163b6db0bb760e01b81523360048201526024810193909352604483019190915260648201529051735361ee2f443b61e60f19dbe73a324f342619161d9163b6db0bb791608480830192600092919082900301818387803b15801561098757600080fd5b505af115801561099b573d6000803e3d6000fd5b50505050735361ee2f443b61e60f19dbe73a324f342619161d6001600160a01b031663b5967e168360a001516040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b03168152602001915050600060405180830381600087803b158015610a0f57600080fd5b505af1158015610a23573d6000803e3d6000fd5b5050505060a0820151604080840151606085015160808601518351634f4bbbdf60e01b81526001600160a01b03909516600486015260248501929092526044840152606483015251735361ee2f443b61e60f19dbe73a324f342619161d91634f4bbbdf91608480830192600092919082900301818387803b158015610aa757600080fd5b505af1158015610abb573d6000803e3d6000fd5b50505050600160801b8260c001511015610b0d578160a001516001600160a01b03166108fc6005846020015160040281610af157fe5b049081150290604051600060405180830381858888f150505050505b3360a08301526004600582020460208381019182524260c085019081526000958652908590526040948590208451815591516001830155938301516002820155606083015160038201556080830151600482015560a08301516005820180546001600160a01b0319166001600160a01b03909216919091179055925160068401555060e00151600790910155565b606080606080600554604051908082528060200260200182016040528015610bcd578160200160208202803883390190505b5090506060600554604051908082528060200260200182016040528015610bfe578160200160208202803883390190505b5090506060600554604051908082528060200260200182016040528015610c2f578160200160208202803883390190505b50905060005b600554811015610d3357610c47610e3a565b50600180820160009081526020818152604091829020825161010081018452815481529381015491840191909152600281015491830191909152600381015460608301526004810154608083015260058101546001600160a01b031660a08301819052600682015460c084015260079091015460e08301528551869084908110610ccd57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050610cf681610ddd565b848381518110610d0257fe5b6020026020010181815250508060e00151838381518110610d1f57fe5b602090810291909101015250600101610c35565b5091945092509050909192565b6002546001600160a01b03163314610d5757600080fd5b6002546040805163a9059cbb60e01b81526001600160a01b0392831660048201526024810184905290519184169163a9059cbb916044808201926020929091908290030181600087803b158015610dad57600080fd5b505af1158015610dc1573d6000803e3d6000fd5b505050506040513d6020811015610dd757600080fd5b50505050565b60c08101516000908190421115610df7575060c082015142035b60208301516003549092506305265c0090820283020482811180610e1e5750600454818403105b15610e2d576004549250610e33565b80830392505b5050919050565b604051806101000160405280600081526020016000815260200160008152602001600081526020016000815260200160006001600160a01b031681526020016000815260200160008152509056fea265627a7a72315820857bffe1694c113d3831816f13bb5635ba1dbc7f5c42f55daa11d816c433605764736f6c63430005100032

Deployed Bytecode Sourcemap

160:5079:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1294:372;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1294:372:0;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;-1:-1;1294:372:0;;;;;;;;;;;;;;;;;;;;;;:::i;1674:226::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1674:226:0;;;:::i;1152:134::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1152:134:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;1152:134:0;-1:-1:-1;;;;;1152:134:0;;:::i;656:23::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;656:23:0;;;:::i;:::-;;;;;;;;;;;;;;;;2131:582;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2131:582:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;2131:582:0;;;;;;;;:::i;2721:1305::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;2721:1305:0;;:::i;4611:623::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;4611:623:0;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;4611:623:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;4611:623:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;4611:623:0;;;;;;;;;;;;;;;;;;;;;1908:215;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1908:215:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;1908:215:0;;;;;;;;:::i;1294:372::-;1424:5;;-1:-1:-1;;;;;1424:5:0;1410:10;:19;1402:28;;;;;;1454:1;1449:2;:6;:42;;;;-1:-1:-1;1489:1:0;1459:12;;;;;;;;;;:18;;;-1:-1:-1;;;;;1459:18:0;:32;1449:42;1441:51;;;;;;1507:8;:12;;;;;;;;;;:19;1503:67;;1548:8;:10;;;;;;1503:67;1595:63;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1595:63:0;;;;;;-1:-1:-1;;;1595:63:0;;;;;;;;;;;;1580:12;;;;;;;;;;:78;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;1580:78:0;-1:-1:-1;;;;;1580:78:0;;;;;;;;;;;;;;;;;;;;;1294:372::o;1674:226::-;1738:49;;;-1:-1:-1;;;1738:49:0;;1766:4;1738:49;;;;;;;;;;;;1721:14;;-1:-1:-1;;;;;346:42:0;1738:19;;:49;;;;;;;;;;;;;;;1721:14;346:42;1738:49;;;5:2:-1;;;;30:1;27;20:12;5:2;1738:49:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;1738:49:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;1738:49:0;1808:47;;;-1:-1:-1;;;1808:47:0;;1834:4;1808:47;;;;;;;;;;;;1738:49;;-1:-1:-1;;;;;;346:42:0;1808:17;;:47;;;;;1738:49;;1808:47;;;;;;;;-1:-1:-1;346:42:0;1808:47;;;5:2:-1;;;;30:1;27;20:12;5:2;1808:47:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;1808:47:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;1808:47:0;1866:26;;;-1:-1:-1;;;1866:26:0;;;;1798:57;;;;;267:42;;1866:10;;1798:57;;1866:26;;;;;;;;;;;;;;;1798:57;267:42;1866:26;;;5:2:-1;;;;30:1;27;20:12;5:2;1866:26:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;1866:26:0;;;;;1674:226;:::o;1152:134::-;1239:5;;-1:-1:-1;;;;;1239:5:0;1225:10;:19;1217:28;;;;;;1256:6;:22;;-1:-1:-1;;;;;;1256:22:0;-1:-1:-1;;;;;1256:22:0;;;;;;;;;;1152:134::o;656:23::-;;;;:::o;2131:582::-;2239:6;;-1:-1:-1;;;;;2239:6:0;2225:10;:20;2217:29;;;;;;2257:16;;:::i;:::-;-1:-1:-1;2276:8:0;:16;;;;;;;;;;;;2257:35;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2257:35:0;;;;;;;;;;;;;;;;;;;;;;;;;2311:24;2303:33;;;;;;2369:1;2355:4;:11;;;:15;2347:24;;;;;;2466:28;;;-1:-1:-1;;;2466:28:0;;-1:-1:-1;;;;;2466:28:0;;;;;;;;267:42;;2466:20;;:28;;;;;-1:-1:-1;;2466:28:0;;;;;;;-1:-1:-1;267:42:0;2466:28;;;5:2:-1;;;;30:1;27;20:12;5:2;2466:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;;;2548:14:0;;;;;2564:11;;;;2577:14;;;;2505:87;;-1:-1:-1;;;2505:87:0;;-1:-1:-1;;;;;2505:87:0;;;;;;;;;;;;;;;;;;;;;;;;;;267:42;;2505:34;;:87;;;;;-1:-1:-1;;2505:87:0;;;;;;;-1:-1:-1;267:42:0;2505:87;;;5:2:-1;;;;30:1;27;20:12;5:2;2505:87:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;;;;;;;;2605:19:0;;;:10;;;:19;;;2657:3;2663:8;2657:14;2635:19;;;:36;;;-1:-1:-1;2682:16:0;;;;;;;;;;;;:23;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;2682:23:0;;;;;;;;;;;;;;;;;;;;;;;2131:582::o;2721:1305::-;2786:16;;:::i;:::-;-1:-1:-1;2805:8:0;:16;;;;;;;;;;;;2786:35;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2786:35:0;;;;;;;;;;;;;;;;;;;;;;;;;2832:33;;;;;;2898:1;2884:4;:11;;;:15;2876:24;;;;;;2925:4;:19;;;2919:3;:25;2911:34;;;;;;2956:13;2972:18;2985:4;2972:12;:18::i;:::-;2956:34;;3024:5;3011:9;:18;;3003:27;;;;;;3057:5;3045:9;:17;3041:157;;;3115:3;3108;3100:11;;3099:19;3087:9;:31;3079:40;;;;;;3148:38;;:10;;3168:9;:17;;;3148:38;;;;;;;;;3168:17;3148:10;:38;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;3148:38:0;3041:157;3291:32;;;-1:-1:-1;;;3291:32:0;;3312:10;3291:32;;;;;;267:42;;3291:20;;:32;;;;;-1:-1:-1;;3291:32:0;;;;;;;-1:-1:-1;267:42:0;3291:32;;;5:2:-1;;;;30:1;27;20:12;5:2;3291:32:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;;;3381:14:0;;;;;3397:11;;;;3410:14;;;;3334:91;;-1:-1:-1;;;3334:91:0;;3369:10;3334:91;;;;;;;;;;;;;;;;;;;;;;;;267:42;;3334:34;;:91;;;;;-1:-1:-1;;3334:91:0;;;;;;;-1:-1:-1;267:42:0;3334:91;;;5:2:-1;;;;30:1;27;20:12;5:2;3334:91:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;3334:91:0;;;;267:42;-1:-1:-1;;;;;3520:20:0;;3541:4;:10;;;3520:32;;;;;;;;;;;;;-1:-1:-1;;;;;3520:32:0;-1:-1:-1;;;;;3520:32:0;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3520:32:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;;;3597:10:0;;;;3609:14;;;;;3625:11;;;;3638:14;;;;3563:90;;-1:-1:-1;;;3563:90:0;;-1:-1:-1;;;;;3563:90:0;;;;;;;;;;;;;;;;;;;;;;;267:42;;3563:33;;:90;;;;;-1:-1:-1;;3563:90:0;;;;;;;-1:-1:-1;267:42:0;3563:90;;;5:2:-1;;;;30:1;27;20:12;5:2;3563:90:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;3563:90:0;;;;-1:-1:-1;;;3670:4:0;:17;;;:28;3666:143;;;3760:4;:10;;;-1:-1:-1;;;;;3760:15:0;:37;3795:1;3777:4;:10;;;3790:1;3777:14;3776:20;;;;;;3760:37;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;3666:143:0;3874:10;3861;;;:23;3922:1;3917;3909:9;;3908:15;3895:10;;;;:28;;;3981:3;3961:17;;;:23;;;-1:-1:-1;3995:16:0;;;;;;;;;;;;:23;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;3995:23:0;-1:-1:-1;;;;;3995:23:0;;;;;;;;;;;;;;;-1:-1:-1;3995:23:0;;;;;;;;2721:1305::o;4611:623::-;4661:16;4679;4697;4726:27;4770:8;;4756:23;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;4756:23:0;;4726:53;;4790:27;4834:8;;4820:23;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;4820:23:0;;4790:53;;4854:32;4903:8;;4889:23;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;4889:23:0;-1:-1:-1;4854:58:0;-1:-1:-1;4930:9:0;4925:243;4949:8;;4945:1;:12;4925:243;;;4979:16;;:::i;:::-;-1:-1:-1;5009:1:0;5007:3;;;4998:8;:13;;;;;;;;;;;;4979:32;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;4979:32:0;;;;;;;;;;;;;;;;;;;;;;;;5026:13;;:10;;5007:1;;5026:13;;;;;;;;;;;:26;-1:-1:-1;;;;;5026:26:0;;;-1:-1:-1;;;;;5026:26:0;;;;;5083:18;5096:4;5083:12;:18::i;:::-;5067:10;5078:1;5067:13;;;;;;;;;;;;;:34;;;;;5137:4;:19;;;5116:15;5132:1;5116:18;;;;;;;;;;;;;;;;;:40;-1:-1:-1;4959:3:0;;4925:243;;;-1:-1:-1;5186:10:0;;-1:-1:-1;5198:10:0;-1:-1:-1;5210:15:0;-1:-1:-1;4611:623:0;;;:::o;1908:215::-;2004:5;;-1:-1:-1;;;;;2004:5:0;1990:10;:19;1982:28;;;;;;2101:5;;2079:36;;;-1:-1:-1;;;2079:36:0;;-1:-1:-1;;;;;2101:5:0;;;2079:36;;;;;;;;;;;;:21;;;;;;:36;;;;;;;;;;;;;;;2101:5;2079:21;:36;;;5:2:-1;;;;30:1;27;20:12;5:2;2079:36:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;2079:36:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;1908:215:0:o;4034:569::-;4180:17;;;;4097;;;;4174:3;:23;4170:102;;;-1:-1:-1;4243:17:0;;;;4237:3;:23;4170:102;4294:10;;;;4364:16;;4294:10;;-1:-1:-1;4397:8:0;4341:39;;;:52;;4340:65;4420:26;;;;:67;;;4479:8;;4462:14;4450:9;:26;:37;4420:67;4416:180;;;4516:8;;4504:20;;4416:180;;;4570:14;4557:27;;;;4416:180;4034:569;;;;;:::o;160:5079::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;160:5079:0;;;;;;;;;;;;;;;:::o

Swarm Source

bzzr://857bffe1694c113d3831816f13bb5635ba1dbc7f5c42f55daa11d816c4336057

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.