ETH Price: $3,268.94 (+0.80%)
Gas: 2 Gwei

Contract

0xfcFC8EACC436f336A80c63A7CeC03D9B04C62f25
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Approve199094632024-05-20 6:51:1169 days ago1716187871IN
0xfcFC8EAC...B04C62f25
0 ETH0.000121932.63738419
Permit195214502024-03-26 22:05:59123 days ago1711490759IN
0xfcFC8EAC...B04C62f25
0 ETH0.0023130242.57049696
Approve195174042024-03-26 8:23:47124 days ago1711441427IN
0xfcFC8EAC...B04C62f25
0 ETH0.0005126321.17033764
Permit193629932024-03-04 16:28:47146 days ago1709569727IN
0xfcFC8EAC...B04C62f25
0 ETH0.0037175199.84197056
Approve189355802024-01-04 17:54:35206 days ago1704390875IN
0xfcFC8EAC...B04C62f25
0 ETH0.0005947124.55962237
Approve188064572023-12-17 14:52:23224 days ago1702824743IN
0xfcFC8EAC...B04C62f25
0 ETH0.001017842.03188279
Approve188028222023-12-17 2:36:47224 days ago1702780607IN
0xfcFC8EAC...B04C62f25
0 ETH0.0009046837.36052122
Permit187649122023-12-11 19:01:47230 days ago1702321307IN
0xfcFC8EAC...B04C62f25
0 ETH0.0018673750.15235094
Permit187396002023-12-08 5:57:47233 days ago1702015067IN
0xfcFC8EAC...B04C62f25
0 ETH0.0011942432.07401451
Permit187395972023-12-08 5:57:11233 days ago1702015031IN
0xfcFC8EAC...B04C62f25
0 ETH0.0017730332.64659281
Approve185110252023-11-06 6:00:59265 days ago1699250459IN
0xfcFC8EAC...B04C62f25
0 ETH0.0006473824.69527156
Approve185110152023-11-06 5:58:59265 days ago1699250339IN
0xfcFC8EAC...B04C62f25
0 ETH0.0005689723.49665042
Approve181838792023-09-21 11:13:35311 days ago1695294815IN
0xfcFC8EAC...B04C62f25
0 ETH0.0002504110.34149201
Approve177979472023-07-29 9:57:47365 days ago1690624667IN
0xfcFC8EAC...B04C62f25
0 ETH0.0003917516.17816806
Approve176707892023-07-11 13:52:35383 days ago1689083555IN
0xfcFC8EAC...B04C62f25
0 ETH0.0008613918.52501182
Approve171712272023-05-02 6:10:35453 days ago1683007835IN
0xfcFC8EAC...B04C62f25
0 ETH0.0014489159.83529164
Approve170481552023-04-14 21:53:47470 days ago1681509227IN
0xfcFC8EAC...B04C62f25
0 ETH0.0005918824.44279455
Approve170086102023-04-09 5:13:11476 days ago1681017191IN
0xfcFC8EAC...B04C62f25
0 ETH0.0004515318.64689629
Approve168870592023-03-23 1:43:11493 days ago1679535791IN
0xfcFC8EAC...B04C62f25
0 ETH0.0006026612.96070986
Approve167148382023-02-26 20:39:23517 days ago1677443963IN
0xfcFC8EAC...B04C62f25
0 ETH0.0006014724.8389332
Approve165442832023-02-02 23:06:59541 days ago1675379219IN
0xfcFC8EAC...B04C62f25
0 ETH0.0006143325.36982774
Approve163422342023-01-05 17:59:11570 days ago1672941551IN
0xfcFC8EAC...B04C62f25
0 ETH0.0007181329.65654754
Approve161826462022-12-14 11:30:47592 days ago1671017447IN
0xfcFC8EAC...B04C62f25
0 ETH0.0003384313.97645884
Approve161539502022-12-10 11:17:23596 days ago1670671043IN
0xfcFC8EAC...B04C62f25
0 ETH0.0007158715.39540138
Approve161511632022-12-10 1:57:11596 days ago1670637431IN
0xfcFC8EAC...B04C62f25
0 ETH0.0003324313.72860957
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
GrillaToken

Compiler Version
v0.8.12+commit.f00d7308

Optimization Enabled:
Yes with 9999 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity Multiple files format)

File 1 of 11: GrillaToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import "./ERC20.sol";
import "./Ownable.sol";
import "./JungleSerum.sol";
import "./CyberGorillasStaking.sol";

/*
   ______      __              ______           _ ____          
  / ____/_  __/ /_  ___  _____/ ____/___  _____(_) / /___ ______
 / /   / / / / __ \/ _ \/ ___/ / __/ __ \/ ___/ / / / __ `/ ___/
/ /___/ /_/ / /_/ /  __/ /  / /_/ / /_/ / /  / / / / /_/ (__  ) 
\____/\__, /_.___/\___/_/   \____/\____/_/  /_/_/_/\__,_/____/  
     /____/                                                     

*/

/// @title Grilla Token
/// @author delta devs (https://twitter.com/deltadevelopers)
contract GrillaToken is ERC20, Ownable {
    /*///////////////////////////////////////////////////////////////
                            EVENTS
    //////////////////////////////////////////////////////////////*/

    /// @notice Emitted by `buyOffChainUtility` function.
    /// @dev Event logging when utility has been purchased.
    /// @param sender Address of purchaser.
    /// @param itemId Item identifier tied to utility.
    event UtilityPurchase(address indexed sender, uint256 indexed itemId);

    /*///////////////////////////////////////////////////////////////
                            STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @notice An instance of the JungleSerum contract.
    JungleSerum serumContract;

    /// @notice Retrieves price tied to specific utility item ID.
    mapping(uint256 => uint256) utilityPrices;

    /// @notice Returns true if address is authorized to make stake function calls.
    mapping(address => bool) authorizedStakingContracts;

    /*///////////////////////////////////////////////////////////////
                            CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/
    constructor() ERC20("GRILLA", "GRILLA", 18) {}

    /*///////////////////////////////////////////////////////////////
                            MINTING LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Allows the contract deployer to mint GRILLA.
    /// @param account The address which will receive the minted amount.
    /// @param amount The amount of tokens to mint.
    function ownerMint(address account, uint256 amount) public onlyOwner {
        _mint(account, amount);
    }

    /// @notice Allows authorized staking contracts to mint GRILLA.
    /// @param account The address which will receive the minted amount.
    /// @param amount The amount of tokens to mint.
    function stakerMint(address account, uint256 amount) public {
        require(
            authorizedStakingContracts[msg.sender],
            "Request only valid from staking contract"
        );
        _mint(account, amount);
    }

    /*///////////////////////////////////////////////////////////////
                        CONTRACT SETTERS
    //////////////////////////////////////////////////////////////*/

    /// @notice Allows the contract deployer to authorize a contract to stake.
    /// @param staker The address to authorize.
    function addStakingContract(address staker) public onlyOwner {
        authorizedStakingContracts[staker] = true;
    }

    /// @notice Allows the contract deployer to unauthorize a contract to stake.
    /// @param staker The address to remove authority from.
    function removeStakingContract(address staker) public onlyOwner {
        authorizedStakingContracts[staker] = false;
    }

    /// @notice Sets the address of the JungleSerum contract.
    /// @param serumContractAddress The address of the JungleSerum contract.
    function setSerumContract(address serumContractAddress) public onlyOwner {
        serumContract = JungleSerum(serumContractAddress);
    }

    /*///////////////////////////////////////////////////////////////
                        UTILITY PURCHASING LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Purchase JungleSerum.
    function buySerum() public {
        transfer(address(serumContract), serumContract.serumPrice());
        serumContract.mint(msg.sender);
    }

    /*///////////////////////////////////////////////////////////////
                    OFFCHAIN UTILITY PURCHASING LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Retrieves the price of a specific utility.
    /// @param itemId The identifier of the utility item.
    /// @return The price of a specific utility.
    function getUtilityPrice(uint256 itemId) public view returns (uint256) {
        return utilityPrices[itemId];
    }

    /// @notice Allows the contract deployer to add off-chain utility data.
    /// @param itemId The identifier of the utility item.
    /// @param itemPrice The price of the utility item.
    function addOffchainUtility(uint256 itemId, uint256 itemPrice)
        public
        onlyOwner
    {
        utilityPrices[itemId] = itemPrice;
    }

    /// @notice Allows the contract deployer to remove off-chain utility data.
    /// @param itemId The identifier of the utility item.
    function deleteUtilityPrice(uint256 itemId) public onlyOwner {
        delete utilityPrices[itemId];
    }

    /// @notice Allows the contract deployer to add off-chain utility data for multiple items.
    /// @param items List of multiple utility item identifiers.
    /// @param prices List of multiple utility item prices.
    function uploadUtilityPrices(
        uint256[] memory items,
        uint256[] memory prices
    ) public onlyOwner {
        for (uint256 i = 0; i < items.length; i++) {
            utilityPrices[items[i]] = prices[i];
        }
    }

    /// @notice Buy the requested off chain utility.
    /// @param itemId The identifier of the utility item.
    function buyOffchainUtility(uint256 itemId) public {
        require(utilityPrices[itemId] > 0, "Invalid utility id");
        transfer(address(serumContract), utilityPrices[itemId]);
        emit UtilityPurchase(msg.sender, itemId);
    }
}

File 2 of 11: CyberGorillaBabies.sol
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.10;

import "./Ownable.sol";
import "./Strings.sol";
import "./ERC721.sol";

/*
   ______      __              ______           _ ____          
  / ____/_  __/ /_  ___  _____/ ____/___  _____(_) / /___ ______
 / /   / / / / __ \/ _ \/ ___/ / __/ __ \/ ___/ / / / __ `/ ___/
/ /___/ /_/ / /_/ /  __/ /  / /_/ / /_/ / /  / / / / /_/ (__  ) 
\____/\__, /_.___/\___/_/   \____/\____/_/  /_/_/_/\__,_/____/  
     /____/                                                     

*/

/// @title Cyber Gorillas Babies
/// @author delta devs (https://twitter.com/deltadevelopers)
contract CyberGorillaBabies is ERC721, Ownable {
    using Strings for uint256;

    /// @notice The address which is allowed to breed Cyber Gorillas.
    address private gorillaBreeder;
    /// @notice Base URI pointing to CyberGorillaBabies metadata.
    string public baseURI;
    /// @notice Returns true if the requested gorilla baby has the genesis trait, false otherwise.
    mapping(uint256 => bool) public isGenesis;

    constructor(string memory initialBaseURI)
        ERC721("Cyber Gorilla Babies", "CyberGorillaBabies")
    {
        baseURI = initialBaseURI;
    }

    /// @notice Set the address which is allowed to breed gorillas.
    /// @param newGorillaBreeder The target address, authorized to breed.
    function setGorillaBreeder(address newGorillaBreeder) public onlyOwner {
        gorillaBreeder = newGorillaBreeder;
    }

    /// @notice Allows the contract deployer to set the Base URI for CyberGorillaBabies' metadata.
    /// @param newBaseURI The new Base URI.
    function setBaseURI(string memory newBaseURI) public onlyOwner {
        baseURI = newBaseURI;
    }

    /// @notice Allows the authorized breeder address to mint a gorilla baby to a specific address.
    /// @param to The address to receive the minted gorilla baby.
    /// @param _isGenesis Whether the baby to be minted has the genesis trait or not.
    function mintBaby(address to, bool _isGenesis) public {
        require(msg.sender == gorillaBreeder, "Not Authorized");
        isGenesis[totalSupply] = _isGenesis;
        _mint(to, totalSupply);
    }

    /// @notice Returns the token URI of a specific gorilla baby.
    /// @param tokenId The token ID of the requested gorilla baby.
    /// @return The full URI of the requested gorilla baby.
    function tokenURI(uint256 tokenId)
        public
        view
        override
        returns (string memory)
    {
        return
            bytes(baseURI).length > 0
                ? string(abi.encodePacked(baseURI, tokenId.toString(), ".json"))
                : "";
    }


    function supportsInterface(bytes4 interfaceId) public pure override(ERC721, Ownable) returns (bool) {
        return
            interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
            interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
            interfaceId == 0x5b5e139f || // ERC165 Interface ID for ERC721Metadata
            interfaceId == 0x7f5828d0;   // ERC165 Interface ID for ERC173
    }
}

File 3 of 11: CyberGorillas.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import "./Strings.sol";
import "./ERC721.sol";
import "./Ownable.sol";

error SoldOut();
error SaleClosed();
error InvalidMintParameters();
error MintingTooMany();
error NotWhitelisted();
error NotAuthorized();

/*
   ______      __              ______           _ ____          
  / ____/_  __/ /_  ___  _____/ ____/___  _____(_) / /___ ______
 / /   / / / / __ \/ _ \/ ___/ / __/ __ \/ ___/ / / / __ `/ ___/
/ /___/ /_/ / /_/ /  __/ /  / /_/ / /_/ / /  / / / / /_/ (__  ) 
\____/\__, /_.___/\___/_/   \____/\____/_/  /_/_/_/\__,_/____/  
     /____/                                                     

*/

/// @author distractedm1nd
contract CyberGorillas is ERC721, Ownable {
    using Strings for uint256;
    address private passwordSigner;
    address private gorillaBurner;

    bool publicSaleActive;

    uint256 constant PRESALE_MAX_TX = 2;
    uint256 constant PUBLIC_MAX_TX = 5;
    uint256 public constant MAX_SUPPLY = 3333;
    uint256 constant PRICE = 0.08 ether;

    string public baseURI;

    mapping(address => uint256) private presaleWalletLimits;
    mapping(address => uint256) private mainsaleWalletLimits;

    constructor(string memory initialBaseURI, address initialPasswordSigner)
        ERC721("Cyber Gorillas", "CyberGorillas")
    {
        baseURI = initialBaseURI;
        passwordSigner = initialPasswordSigner;
    }

    function airdrop(address[] calldata airdropAddresses) public onlyOwner {
        for (uint256 i = 0; i < airdropAddresses.length; i++) {
            _mint(airdropAddresses[i], totalSupply);
        }
    }

    function setGorilliaBurner(address newGorillaBurner) public onlyOwner {
        gorillaBurner = newGorillaBurner;
    }

    function setPasswordSigner(address signer) public onlyOwner {
        passwordSigner = signer;
    }

    function setBaseURI(string memory newBaseURI) public onlyOwner {
        baseURI = newBaseURI;
    }

    function setPublicSale(bool publicSale) public onlyOwner {
        publicSaleActive = publicSale;
    }

    function specialMintForTests(address to, uint256 tokenId) public {
        if (ownerOf[tokenId] == address(0)) _mint(to, tokenId);
    }

    function purchase(uint256 amount) public payable {
        if (!publicSaleActive) revert SaleClosed();
        if (totalSupply + amount > MAX_SUPPLY) revert SoldOut();
        if (
            mainsaleWalletLimits[msg.sender] + amount > PUBLIC_MAX_TX ||
            msg.value < PRICE * amount
        ) revert InvalidMintParameters();

        mainsaleWalletLimits[msg.sender] += amount;
        for (uint256 i = 0; i < amount; i++) {
            _mint(msg.sender, totalSupply);
        }
    }

    function presale(uint256 amount, bytes memory signature) public payable {
        if (publicSaleActive) revert SaleClosed();
        if (totalSupply + amount > MAX_SUPPLY) revert SoldOut();
        if (!isWhitelisted(msg.sender, signature)) revert NotWhitelisted();
        if (
            presaleWalletLimits[msg.sender] + amount > PRESALE_MAX_TX ||
            msg.value < PRICE * amount
        ) revert InvalidMintParameters();

        presaleWalletLimits[msg.sender] += amount;
        for (uint256 i = 0; i < amount; i++) {
            _mint(msg.sender, totalSupply);
        }
    }

    function unstake(address payable recipient) external onlyOwner {
        recipient.transfer(address(this).balance);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override
        returns (string memory)
    {
        return
            bytes(baseURI).length > 0
                ? string(abi.encodePacked(baseURI, tokenId.toString(), ".json"))
                : "";
    }

    function isWhitelisted(address user, bytes memory signature)
        public
        view
        returns (bool)
    {
        bytes32 messageHash = keccak256(abi.encode(user));
        bytes32 ethSignedMessageHash = getEthSignedMessageHash(messageHash);

        return recoverSigner(ethSignedMessageHash, signature) == passwordSigner;
    }

    function getEthSignedMessageHash(bytes32 _messageHash)
        private
        pure
        returns (bytes32)
    {
        /*
        Signature is produced by signing a keccak256 hash with the following format:
        "\x19Ethereum Signed Message\n" + len(msg) + msg
        */
        return
            keccak256(
                abi.encodePacked(
                    "\x19Ethereum Signed Message:\n32",
                    _messageHash
                )
            );
    }

    function recoverSigner(
        bytes32 _ethSignedMessageHash,
        bytes memory _signature
    ) private pure returns (address) {
        (bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature);
        return ecrecover(_ethSignedMessageHash, v, r, s);
    }

    function recoverSignerTest(
        bytes32 _ethSignedMessageHash,
        bytes memory _signature
    ) private pure returns (address) {
        (bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature);
        return ecrecover(_ethSignedMessageHash, v, r, s);
    }

    function splitSignature(bytes memory sig)
        private
        pure
        returns (
            bytes32 r,
            bytes32 s,
            uint8 v
        )
    {
        require(sig.length == 65, "sig invalid");

        assembly {
            /*
        First 32 bytes stores the length of the signature

        add(sig, 32) = pointer of sig + 32
        effectively, skips first 32 bytes of signature

        mload(p) loads next 32 bytes starting at the memory address p into memory
        */

            // first 32 bytes, after the length prefix
            r := mload(add(sig, 32))
            // second 32 bytes
            s := mload(add(sig, 64))
            // final byte (first byte of the next 32 bytes)
            v := byte(0, mload(add(sig, 96)))
        }

        // implicitly return (r, s, v)
    }


    function supportsInterface(bytes4 interfaceId) public pure override(ERC721, Ownable) returns (bool) {
        return
            interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
            interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
            interfaceId == 0x5b5e139f || // ERC165 Interface ID for ERC721Metadata
            interfaceId == 0x7f5828d0;   // ERC165 Interface ID for ERC173
    }

    function burn(uint256 tokenId) public {
        if (msg.sender != gorillaBurner) revert NotAuthorized();
        _burn(tokenId);
    }
}

File 4 of 11: CyberGorillasStaking.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import "./CyberGorillas.sol";
import "./GrillaToken.sol";
import "./ERC721.sol";
import "./Strings.sol";
import "./RewardBoostProvider.sol";
import "./Ownable.sol";

/*
   ______      __              ______           _ ____          
  / ____/_  __/ /_  ___  _____/ ____/___  _____(_) / /___ ______
 / /   / / / / __ \/ _ \/ ___/ / __/ __ \/ ___/ / / / __ `/ ___/
/ /___/ /_/ / /_/ /  __/ /  / /_/ / /_/ / /  / / / / /_/ (__  ) 
\____/\__, /_.___/\___/_/   \____/\____/_/  /_/_/_/\__,_/____/  
     /____/                                                     

*/

/// @title Cyber Gorillas Staking
/// @author delta devs (https://twitter.com/deltadevelopers)
contract CyberGorillasStaking is Ownable {
    /*///////////////////////////////////////////////////////////////
                        CONTRACT STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @notice An instance of the GRILLA token, paid out as staking reward.
    GrillaToken public rewardsToken;
    /// @notice An ERC721 instance of the Cyber Gorillas contract.
    ERC721 public gorillaContract;

    /// @notice An address slot for a future contract to allow users to withdraw their rewards from multiple staking contracts in one call.
    address public rewardAggregator;

    /// @notice The reward rate for staking a regular gorilla.
    /// @dev The reward rate is fixed to 10 * 1E18 GRILLA every 86400 seconds, 1157407407407400 per second.
    uint256 constant normalRate = (100 * 1E18) / uint256(1 days);

    /// @notice The reward rate for staking a genesis gorilla.
    /// @dev The reward rate is fixed to 15 * 1E18 GRILLA every 86400 seconds, 1736111111111110 per second.
    uint256 constant genesisRate = (150 * 1E18) / uint256(1 days);

    /*///////////////////////////////////////////////////////////////
                    GORILLA METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @notice Keeps track of which gorilla's have the genesis trait.
    mapping(uint256 => bool) private genesisTokens;
    /// @notice A list of reward boost providers.
    RewardBoostProvider[] rewardBoostProviders;

    /*///////////////////////////////////////////////////////////////
                        STAKING STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @notice Returns the owner of the specified gorilla.
    mapping(uint256 => address) public tokenToAddr;
    /// @notice Returns the reward amount for the specified address.
    mapping(address => uint256) public rewards;
    /// @notice Returns the number of normal gorillas staked by specified address.
    mapping(address => uint256) public _balancesNormal;
    /// @notice Returns the number of genesis gorillas staked by specified address.
    mapping(address => uint256) public _balancesGenesis;
    /// @notice Returns the start time of staking rewards accumulation for a specified address.
    /// @dev The UNIX timestamp in seconds in which staking rewards were last claimed.
    /// This is later compared with block.timestamp to calculate the accumulated staking rewards.
    mapping(address => uint256) public _updateTimes;

    /*///////////////////////////////////////////////////////////////
                            CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address _gorillaContract, address _rewardsToken) {
        gorillaContract = ERC721(_gorillaContract);
        rewardsToken = GrillaToken(_rewardsToken);
    }

    /*///////////////////////////////////////////////////////////////
                            SETTERS
    //////////////////////////////////////////////////////////////*/

    /// @notice Allows the contract deployer to specify which gorillas are to be considered of type genesis.
    /// @param genesisIndexes An array of indexes specifying which gorillas are of type genesis.
    function uploadGenesisArray(uint256[] memory genesisIndexes)
        public
        onlyOwner
    {
        for (uint256 i = 0; i < genesisIndexes.length; i++) {
            genesisTokens[genesisIndexes[i]] = true;
        }
    }

    /*///////////////////////////////////////////////////////////////
                                VIEWS
    //////////////////////////////////////////////////////////////*/

    /// @notice Returns the accumulated staking rewards of the function caller.
    /// @return The amount of GRILLA earned while staking.
    function viewReward() public view returns (uint256) {
        return rewards[msg.sender] + rewardDifferential(msg.sender);
    }

    /// @notice Calculates the accumulated staking reward for the requested address.
    /// @param account The address of the staker.
    /// @return The amount of GRILLA earned while staking.
    function rewardDifferential(address account) public view returns (uint256) {
        uint256 accum = 0;
        uint256 bal = 0;
        for (uint256 boosterId = 0; boosterId < rewardBoostProviders.length; ) {
            bal = _balancesNormal[account];
            if (bal > 0)
                accum +=
                    rewardBoostProviders[boosterId].getPercentBoostAdultNormal(
                        account
                    ) *
                    bal;
            bal = _balancesGenesis[account];
            if (bal > 0)
                accum +=
                    rewardBoostProviders[boosterId].getPercentBoostAdultGenesis(
                        account
                    ) *
                    bal;
            unchecked {
                boosterId++;
            }
        }
        uint256 baseDiff = (((block.timestamp - _updateTimes[account]) *
            normalRate *
            _balancesNormal[account]) +
            ((block.timestamp - _updateTimes[account]) *
                genesisRate *
                _balancesGenesis[account]));
        return baseDiff + (baseDiff * accum) / 100;
    }

    /// @notice Returns true if gorilla has the genesis trait, false otherwise.
    /// @return Whether the requested gorilla has the genesis trait.
    function isGenesis(uint256 tokenId) private view returns (bool) {
        return genesisTokens[tokenId];
    }

    /// @notice Returns true if the requested address is staking at least one genesis gorilla, false otherwise.
    /// @return Whether the requested address is staking genesis gorillas.
    function isStakingGenesis(address account) public view returns (bool) {
        return _balancesGenesis[account] > 0;
    }

    /// @notice Returns true if the requested address is staking normal gorillas, false otherwise.
    /// @return Whether the requested address is staking normal gorillas.
    function isStakingNormal(address account) public view returns (bool) {
        return _balancesNormal[account] > 0;
    }

    /// @notice Modifier which updates the timestamp of when a staker last withdrew staking rewards.
    /// @param account The address of the staker.
    modifier updateReward(address account) {
        uint256 reward = rewardDifferential(account);
        _updateTimes[account] = block.timestamp;
        rewards[account] += reward;
        _;
    }

    /// @notice Sets the reward aggregator.
    /// @param _rewardAggregator The address of the reward aggregation contract.
    function setRewardAggregator(address _rewardAggregator) public onlyOwner {
        rewardAggregator = _rewardAggregator;
    }

    /// @notice Adds a reward booster.
    /// @param booster The address of the booster.
    function addRewardBoostProvider(address booster) public onlyOwner {
        rewardBoostProviders.push(RewardBoostProvider(booster));
    }

    /// @notice Remove a specific reward booster at a specific index.
    /// @param index Index of the booster to remove.
    function removeRewardBoostProvider(uint256 index) public onlyOwner {
        delete rewardBoostProviders[index];
    }

    /*///////////////////////////////////////////////////////////////
                            STAKING LOGIC
    //////////////////////////////////////////////////////////////*/

    // TODO: This function is only for testing, can be removed
    // REASONING: Nothing else calls it, and a user would not spend the gas
    //            necessary in order to updateReward()
    function earned(address account)
        public
        updateReward(account)
        returns (uint256)
    {
        return rewards[account];
    }

    /// @notice Allows a staker to withdraw their rewards.
    /// @return The amount of GRILLA earned from staking.
    function withdrawReward()
        public
        updateReward(msg.sender)
        returns (uint256)
    {
        uint256 reward = rewards[msg.sender];
        rewards[msg.sender] = 0;
        rewardsToken.stakerMint(msg.sender, reward);
        return reward;
    }

    /// @notice Allows a contract to withdraw the rewards on behalf of a user.
    /// @return The amount of GRILLA earned from staking.
    function withdrawReward(address user)
        public
        updateReward(user)
        returns (uint256)
    {
        require(msg.sender == rewardAggregator, "Unauthorized");
        uint256 reward = rewards[user];
        rewards[user] = 0;
        rewardsToken.stakerMint(user, reward);
        return reward;
    }

    /// @notice Allows a holder to stake a gorilla.
    /// @dev First checks whether the specified gorilla has the genesis trait. Updates balances accordingly.
    /// unchecked, because no arithmetic overflow is possible.
    /// @param _tokenId A specific gorilla, identified by its token ID.
    function stake(uint256 _tokenId) public updateReward(msg.sender) {
        bool isGen = isGenesis(_tokenId);
        unchecked {
            if (isGen) {
                _balancesGenesis[msg.sender]++;
            } else {
                _balancesNormal[msg.sender]++;
            }
        }
        tokenToAddr[_tokenId] = msg.sender;
        gorillaContract.transferFrom(msg.sender, address(this), _tokenId);
    }

    /// @notice Allows a staker to stake multiple gorillas at once.
    /// @param tokenIds An array of token IDs, representing multiple gorillas.
    function stakeMultiple(uint256[] memory tokenIds)
        public
        updateReward(msg.sender)
    {
        for (uint256 i = 0; i < tokenIds.length; i++) {
            stake(tokenIds[i]);
        }
    }

    /// @notice Allows a staker to unstake a staked gorilla.
    /// @param _tokenId A specific gorilla, identified by its token ID.
    function unstake(uint256 _tokenId) public updateReward(msg.sender) {
        require(tokenToAddr[_tokenId] == msg.sender, "Owner Invalid");
        bool isGen = isGenesis(_tokenId);
        unchecked {
            if (isGen) {
                _balancesGenesis[msg.sender]--;
            } else {
                _balancesNormal[msg.sender]--;
            }
        }
        delete tokenToAddr[_tokenId];
        gorillaContract.transferFrom(address(this), msg.sender, _tokenId);
    }

    /// @notice Allows a staker to unstake multiple gorillas at once.
    /// @param tokenIds An array of token IDs, representing multiple gorillas.
    function unstakeMultiple(uint256[] memory tokenIds)
        public
        updateReward(msg.sender)
    {
        for (uint256 i = 0; i < tokenIds.length; i++) {
            unstake(tokenIds[i]);
        }
    }
}

File 5 of 11: ERC1155.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Minimalist and gas efficient standard ERC1155 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol)
abstract contract ERC1155 {
    /*///////////////////////////////////////////////////////////////
                                EVENTS
    //////////////////////////////////////////////////////////////*/

    event TransferSingle(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256 id,
        uint256 amount
    );

    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] amounts
    );

    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    event URI(string value, uint256 indexed id);

    /*///////////////////////////////////////////////////////////////
                            ERC1155 STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(address => mapping(uint256 => uint256)) public balanceOf;

    mapping(address => mapping(address => bool)) public isApprovedForAll;

    /*///////////////////////////////////////////////////////////////
                             METADATA LOGIC
    //////////////////////////////////////////////////////////////*/

    function uri(uint256 id) public view virtual returns (string memory);

    /*///////////////////////////////////////////////////////////////
                             ERC1155 LOGIC
    //////////////////////////////////////////////////////////////*/

    function setApprovalForAll(address operator, bool approved) public virtual {
        isApprovedForAll[msg.sender][operator] = approved;

        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) public virtual {
        require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");

        balanceOf[from][id] -= amount;
        balanceOf[to][id] += amount;

        emit TransferSingle(msg.sender, from, to, id, amount);

        require(
            to.code.length == 0
                ? to != address(0)
                : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) ==
                    ERC1155TokenReceiver.onERC1155Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) public virtual {
        uint256 idsLength = ids.length; // Saves MLOADs.

        require(idsLength == amounts.length, "LENGTH_MISMATCH");

        require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");

        for (uint256 i = 0; i < idsLength; ) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            balanceOf[from][id] -= amount;
            balanceOf[to][id] += amount;

            // An array can't have a total length
            // larger than the max uint256 value.
            unchecked {
                i++;
            }
        }

        emit TransferBatch(msg.sender, from, to, ids, amounts);

        require(
            to.code.length == 0
                ? to != address(0)
                : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) ==
                    ERC1155TokenReceiver.onERC1155BatchReceived.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function balanceOfBatch(address[] memory owners, uint256[] memory ids)
        public
        view
        virtual
        returns (uint256[] memory balances)
    {
        uint256 ownersLength = owners.length; // Saves MLOADs.

        require(ownersLength == ids.length, "LENGTH_MISMATCH");

        balances = new uint256[](owners.length);

        // Unchecked because the only math done is incrementing
        // the array index counter which cannot possibly overflow.
        unchecked {
            for (uint256 i = 0; i < ownersLength; i++) {
                balances[i] = balanceOf[owners[i]][ids[i]];
            }
        }
    }

    /*///////////////////////////////////////////////////////////////
                              ERC165 LOGIC
    //////////////////////////////////////////////////////////////*/

    function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) {
        return
            interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
            interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155
            interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI
    }

    /*///////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal {
        balanceOf[to][id] += amount;

        emit TransferSingle(msg.sender, address(0), to, id, amount);

        require(
            to.code.length == 0
                ? to != address(0)
                : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, amount, data) ==
                    ERC1155TokenReceiver.onERC1155Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function _batchMint(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal {
        uint256 idsLength = ids.length; // Saves MLOADs.

        require(idsLength == amounts.length, "LENGTH_MISMATCH");

        for (uint256 i = 0; i < idsLength; ) {
            balanceOf[to][ids[i]] += amounts[i];

            // An array can't have a total length
            // larger than the max uint256 value.
            unchecked {
                i++;
            }
        }

        emit TransferBatch(msg.sender, address(0), to, ids, amounts);

        require(
            to.code.length == 0
                ? to != address(0)
                : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) ==
                    ERC1155TokenReceiver.onERC1155BatchReceived.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function _batchBurn(
        address from,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal {
        uint256 idsLength = ids.length; // Saves MLOADs.

        require(idsLength == amounts.length, "LENGTH_MISMATCH");

        for (uint256 i = 0; i < idsLength; ) {
            balanceOf[from][ids[i]] -= amounts[i];

            // An array can't have a total length
            // larger than the max uint256 value.
            unchecked {
                i++;
            }
        }

        emit TransferBatch(msg.sender, from, address(0), ids, amounts);
    }

    function _burn(
        address from,
        uint256 id,
        uint256 amount
    ) internal {
        balanceOf[from][id] -= amount;

        emit TransferSingle(msg.sender, from, address(0), id, amount);
    }
}

/// @notice A generic interface for a contract which properly accepts ERC1155 tokens.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol)
interface ERC1155TokenReceiver {
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 amount,
        bytes calldata data
    ) external returns (bytes4);

    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) external returns (bytes4);
}

File 6 of 11: ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*///////////////////////////////////////////////////////////////
                                  EVENTS
    //////////////////////////////////////////////////////////////*/

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

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*///////////////////////////////////////////////////////////////
                             METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*///////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*///////////////////////////////////////////////////////////////
                             EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    bytes32 public constant PERMIT_TYPEHASH =
        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*///////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*///////////////////////////////////////////////////////////////
                              ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*///////////////////////////////////////////////////////////////
                              EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            bytes32 digest = keccak256(
                abi.encodePacked(
                    "\x19\x01",
                    DOMAIN_SEPARATOR(),
                    keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
                )
            );

            address recoveredAddress = ecrecover(digest, v, r, s);

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*///////////////////////////////////////////////////////////////
                       INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

File 7 of 11: ERC721.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)
/// @dev Note that balanceOf does not revert if passed the zero address, in defiance of the ERC.
abstract contract ERC721 {
    /*///////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

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

    event Approval(address indexed owner, address indexed spender, uint256 indexed id);

    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /*///////////////////////////////////////////////////////////////
                          METADATA STORAGE/LOGIC
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    function tokenURI(uint256 id) public view virtual returns (string memory);

    /*///////////////////////////////////////////////////////////////
                            ERC721 STORAGE                        
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(uint256 => address) public ownerOf;

    mapping(uint256 => address) public getApproved;

    mapping(address => mapping(address => bool)) public isApprovedForAll;

    /*///////////////////////////////////////////////////////////////
                              CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(string memory _name, string memory _symbol) {
        name = _name;
        symbol = _symbol;
    }

    /*///////////////////////////////////////////////////////////////
                              ERC721 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 id) public virtual {
        address owner = ownerOf[id];

        require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");

        getApproved[id] = spender;

        emit Approval(owner, spender, id);
    }

    function setApprovalForAll(address operator, bool approved) public virtual {
        isApprovedForAll[msg.sender][operator] = approved;

        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function transferFrom(
        address from,
        address to,
        uint256 id
    ) public virtual {
        require(from == ownerOf[id], "WRONG_FROM");

        require(to != address(0), "INVALID_RECIPIENT");

        require(
            msg.sender == from || msg.sender == getApproved[id] || isApprovedForAll[from][msg.sender],
            "NOT_AUTHORIZED"
        );

        // Underflow of the sender's balance is impossible because we check for
        // ownership above and the recipient's balance can't realistically overflow.
        unchecked {
            balanceOf[from]--;

            balanceOf[to]++;
        }

        ownerOf[id] = to;

        delete getApproved[id];

        emit Transfer(from, to, id);
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id
    ) public virtual {
        transferFrom(from, to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        bytes memory data
    ) public virtual {
        transferFrom(from, to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    /*///////////////////////////////////////////////////////////////
                              ERC165 LOGIC
    //////////////////////////////////////////////////////////////*/

    function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) {
        return
            interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
            interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
            interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
    }

    /*///////////////////////////////////////////////////////////////
                       INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 id) internal virtual {
        require(to != address(0), "INVALID_RECIPIENT");

        require(ownerOf[id] == address(0), "ALREADY_MINTED");

        // Counter overflow is incredibly unrealistic.
        unchecked {
            totalSupply++;

            balanceOf[to]++;
        }

        ownerOf[id] = to;

        emit Transfer(address(0), to, id);
    }

    function _burn(uint256 id) internal virtual {
        address owner = ownerOf[id];

        require(ownerOf[id] != address(0), "NOT_MINTED");

        // Ownership check above ensures no underflow.
        unchecked {
            totalSupply--;

            balanceOf[owner]--;
        }

        delete ownerOf[id];

        delete getApproved[id];

        emit Transfer(owner, address(0), id);
    }

    /*///////////////////////////////////////////////////////////////
                       INTERNAL SAFE MINT LOGIC
    //////////////////////////////////////////////////////////////*/

    function _safeMint(address to, uint256 id) internal virtual {
        _mint(to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function _safeMint(
        address to,
        uint256 id,
        bytes memory data
    ) internal virtual {
        _mint(to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }
}

/// @notice A generic interface for a contract which properly accepts ERC721 tokens.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)
interface ERC721TokenReceiver {
    function onERC721Received(
        address operator,
        address from,
        uint256 id,
        bytes calldata data
    ) external returns (bytes4);
}

File 8 of 11: JungleSerum.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import "./ERC1155.sol";
import "./Ownable.sol";
import "./Strings.sol";
import "./CyberGorillas.sol";
import "./CyberGorillaBabies.sol";
import "./CyberGorillasStaking.sol";
import "./GrillaToken.sol";

/*
   ______      __              ______           _ ____          
  / ____/_  __/ /_  ___  _____/ ____/___  _____(_) / /___ ______
 / /   / / / / __ \/ _ \/ ___/ / __/ __ \/ ___/ / / / __ `/ ___/
/ /___/ /_/ / /_/ /  __/ /  / /_/ / /_/ / /  / / / / /_/ (__  ) 
\____/\__, /_.___/\___/_/   \____/\____/_/  /_/_/_/\__,_/____/  
     /____/                                                     
*/

/// @title Jungle Serum
/// @author delta devs (https://twitter.com/deltadevelopers)
/// @dev Inspired by BoredApeChemistryClub.sol (https://etherscan.io/address/0x22c36bfdcef207f9c0cc941936eff94d4246d14a)
abstract contract JungleSerum is ERC1155, Ownable {
    using Strings for uint256;
    /*///////////////////////////////////////////////////////////////
                            EVENTS
    //////////////////////////////////////////////////////////////*/

    /// @notice Emitted by `breed` function.
    /// @dev Event logging when breeding occurs.
    /// @param firstGorilla First Cyber Gorilla parent used for breeding.
    /// @param secondGorilla Second Cyber Gorilla parent used for breeding.
    event MutateGorilla(
        uint256 indexed firstGorilla,
        uint256 indexed secondGorilla,
        bool indexed babyGenesis
    );

    /*///////////////////////////////////////////////////////////////
                        METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @notice Keeps track of which gorilla adults have the genesis trait.
    mapping(uint256 => bool) private genesisTokens;

    /// @notice String pointing to Jungle Serum URI.
    string serumURI;
    /// @notice Set name as Jungle Serum.
    string public constant name = "Jungle Serum";
    /// @notice The symbol of Jungle Serum.
    string public constant symbol = "JS";

    /*///////////////////////////////////////////////////////////////
                            STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @notice The price of a Jungle Serum.
    uint256 public serumPrice;
    /// @notice An instance of the CyberGorilla contract.
    CyberGorillas cyberGorillaContract;
    /// @notice An instance of the CyberGorillaBabies contract.
    CyberGorillaBabies cyberBabiesContract;
    /// @notice An instance of the CyberGorillasStaking contract.
    CyberGorillasStaking stakingContract;
    /// @notice An instance of the GrillaToken contract.
    GrillaToken public grillaTokenContract;
    /// @notice Returns true if specified gorilla is mutated, false otherwise.
    mapping(uint256 => bool) mutatedGorillas;

    /*///////////////////////////////////////////////////////////////
                            CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _serumURI,
        uint256 _serumPrice,
        address _cyberGorillaContract,
        address _cyberBabiesContract,
        address _stakingContract
    ) {
        serumURI = _serumURI;
        serumPrice = _serumPrice;
        cyberGorillaContract = CyberGorillas(_cyberGorillaContract);
        cyberBabiesContract = CyberGorillaBabies(_cyberBabiesContract);
        stakingContract = CyberGorillasStaking(_stakingContract);
    }

    /*///////////////////////////////////////////////////////////////
                        STORAGE SETTERS
    //////////////////////////////////////////////////////////////*/

    /// @notice Set the URI pointing to Jungle Serum metadata.
    /// @param _serumURI the target URI.
    function setSerumURI(string memory _serumURI) public onlyOwner {
        serumURI = _serumURI;
    }

    /// @notice Set the price for a Jungle Serum.
    /// @param _serumPrice the price to set it to.
    function setSerumPrice(uint256 _serumPrice) public onlyOwner {
        serumPrice = _serumPrice;
    }

    /// @notice Sets the address of the GrillaToken contract.
    /// @param _grillaTokenContract The address of the GrillaToken contract.
    function setGrillaTokenContract(address _grillaTokenContract)
        public
        onlyOwner
    {
        grillaTokenContract = GrillaToken(_grillaTokenContract);
    }

    /// @notice Sets the address of the CyberGorilla contract.
    /// @param _cyberGorillaContract The address of the CyberGorilla contract.
    function setCyberGorillaContract(address _cyberGorillaContract)
        public
        onlyOwner
    {
        cyberGorillaContract = CyberGorillas(_cyberGorillaContract);
    }

    /// @notice Sets the address of the CyberGorillaBabies contract.
    /// @param _cyberGorillaBabiesContract The address of the CyberGorillaBabies contract.
    function setCyberBabiesContract(address _cyberGorillaBabiesContract)
        public
        onlyOwner
    {
        cyberBabiesContract = CyberGorillaBabies(_cyberGorillaBabiesContract);
    }

    /// @notice Sets the address of the CyberGorillasStaking contract.
    /// @param _stakingContract The address of the GrillaToken contract.
    function setStakingContract(address _stakingContract) public onlyOwner {
        stakingContract = CyberGorillasStaking(_stakingContract);
    }

    /*///////////////////////////////////////////////////////////////
                            ADMIN LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Allows the contract deployer to withdraw the GRILLA held by this contract to a specified address.
    /// @param receiver The address which receives the funds.
    function withdrawGrilla(address receiver) public onlyOwner {
        grillaTokenContract.transfer(
            receiver,
            grillaTokenContract.balanceOf(address(this))
        );
    }

    /// @notice Allows the contract deployer to specify which adult gorillas are to be considered of type genesis.
    /// @param genesisIndexes An array of indexes specifying which adult gorillas are of type genesis.
    function uploadGenesisArray(uint256[] memory genesisIndexes)
        public
        onlyOwner
    {
        for (uint256 i = 0; i < genesisIndexes.length; i++) {
            genesisTokens[genesisIndexes[i]] = true;
        }
    }

    /*///////////////////////////////////////////////////////////////
                        METADATA LOGIC
    //////////////////////////////////////////////////////////////*/

    function uri(uint256 id) public view override returns (string memory) {
        return
            bytes(serumURI).length > 0
                ? string(abi.encodePacked(serumURI, id.toString(), ".json"))
                : "";
    }

    /*///////////////////////////////////////////////////////////////
                            MINTING LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Allows the GrillaToken contract to mint a Jungle Serum for a specified address.
    /// @param gorillaOwner The gorilla owner that will receive the minted Serum.
    function mint(address gorillaOwner) public {
        require(msg.sender == address(grillaTokenContract), "Not authorized");
        _mint(gorillaOwner, 1, 1, "");
    }

    /*///////////////////////////////////////////////////////////////
                            BREEDING LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Allows a gorilla holder to breed a baby gorilla.
    /// @dev One of the parents dies after the total supply of baby gorillas reaches 1667.
    /// @param firstGorilla The tokenID of the first parent used for breeding.
    /// @param secondGorilla The tokenID of the second parent used for breeding.
    function breed(uint256 firstGorilla, uint256 secondGorilla) public virtual;

    /// @notice Psuedorandom number to determine which parent dies during breeding.
    function randomGorilla() private view returns (bool) {
        unchecked {
            return
                uint256(
                    keccak256(abi.encodePacked(block.timestamp, block.number))
                ) %
                    2 ==
                0;
        }
    }


    function supportsInterface(bytes4 interfaceId) public pure override(ERC1155, Ownable) returns (bool) {
        return
            interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
            interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155
            interfaceId == 0x0e89341c || // ERC165 Interface ID for ERC1155MetadataURI
            interfaceId == 0x7f5828d0;   // ERC165 Interface ID for ERC173
    }
}

File 9 of 11: Ownable.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.10;

error NotOwner();

// https://github.com/m1guelpf/erc721-drop/blob/main/src/LilOwnable.sol
abstract contract Ownable {
    address internal _owner;

    event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
    );

    modifier onlyOwner() {
        require(_owner == msg.sender);
        _;
    }

    constructor() {
        _owner = msg.sender;
    }

    function owner() external view returns (address) {
        return _owner;
    }

    function transferOwnership(address _newOwner) external {
        if (msg.sender != _owner) revert NotOwner();

        _owner = _newOwner;
    }

    function renounceOwnership() public {
        if (msg.sender != _owner) revert NotOwner();

        _owner = address(0);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        pure
        virtual
        returns (bool)
    {
        return interfaceId == 0x7f5828d0; // ERC165 Interface ID for ERC173
    }
}

File 10 of 11: RewardBoostProvider.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

/*
   ______      __              ______           _ ____          
  / ____/_  __/ /_  ___  _____/ ____/___  _____(_) / /___ ______
 / /   / / / / __ \/ _ \/ ___/ / __/ __ \/ ___/ / / / __ `/ ___/
/ /___/ /_/ / /_/ /  __/ /  / /_/ / /_/ / /  / / / / /_/ (__  ) 
\____/\__, /_.___/\___/_/   \____/\____/_/  /_/_/_/\__,_/____/  
     /____/                                                     

*/

/// @title Reward Boost Provider
/// @author delta devs (https://twitter.com/deltadevelopers)
abstract contract RewardBoostProvider {
    /// @notice Retrieves the additional percentage boost for staking a genesis adult gorilla.
    /// @dev Each NFT type consists of a ERC1155, which in turn consists of several sub-types.
    /// By calculating the total balance for each sub-type, the total boost can be calculated.
    /// @param account The address of the account to which the boost is eligible.
    /// @return Returns the total boost.
    function getPercentBoostAdultGenesis(address account)
        public
        view
        virtual
        returns (uint256)
    {
        return 0;
    }

    /// @notice Retrieves the additional percentage boost for staking a normal adult gorilla.
    /// @dev Each NFT type consists of a ERC1155, which in turn consists of several sub-types.
    /// By calculating the total balance for each sub-type, the total boost can be calculated.
    /// @param account The address of the account to which the boost is eligible.
    /// @return Returns the total boost.
    function getPercentBoostAdultNormal(address account)
        public
        view
        virtual
        returns (uint256)
    {
        return 0;
    }

    /// @notice Retrieves the additional percentage boost for staking a genesis baby gorilla.
    /// @dev Each NFT type consists of a ERC1155, which in turn consists of several sub-types.
    /// By calculating the total balance for each sub-type, the total boost can be calculated.
    /// @param account The address of the account to which the boost is eligible.
    /// @return Returns the total boost.
    function getPercentBoostBabyGenesis(address account)
        public
        view
        virtual
        returns (uint256)
    {
        return 0;
    }

    /// @notice Retrieves the additional percentage boost for staking a normal baby gorilla.
    /// @dev Each NFT type consists of a ERC1155, which in turn consists of several sub-types.
    /// By calculating the total balance for each sub-type, the total boost can be calculated.
    /// @param account The address of the account to which the boost is eligible.
    /// @return Returns the total boost.
    function getPercentBoostBabyNormal(address account)
        public
        view
        virtual
        returns (uint256)
    {
        return 0;
    }
}

File 11 of 11: Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }
}

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"NotOwner","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"uint256","name":"itemId","type":"uint256"}],"name":"UtilityPurchase","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"itemId","type":"uint256"},{"internalType":"uint256","name":"itemPrice","type":"uint256"}],"name":"addOffchainUtility","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"addStakingContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"itemId","type":"uint256"}],"name":"buyOffchainUtility","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"buySerum","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"itemId","type":"uint256"}],"name":"deleteUtilityPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"itemId","type":"uint256"}],"name":"getUtilityPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ownerMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"removeStakingContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"serumContractAddress","type":"address"}],"name":"setSerumContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"stakerMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"items","type":"uint256[]"},{"internalType":"uint256[]","name":"prices","type":"uint256[]"}],"name":"uploadUtilityPrices","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60e06040523480156200001157600080fd5b506040805180820182526006808252654752494c4c4160d01b6020808401828152855180870190965292855284015281519192916012916200005791600091906200013d565b5081516200006d9060019060208501906200013d565b5060ff81166080524660a05262000083620000a1565b60c0525050600680546001600160a01b0319163317905550620002c4565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051620000d5919062000220565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b8280546200014b90620001e3565b90600052602060002090601f0160209004810192826200016f5760008555620001ba565b82601f106200018a57805160ff1916838001178555620001ba565b82800160010185558215620001ba579182015b82811115620001ba5782518255916020019190600101906200019d565b50620001c8929150620001cc565b5090565b5b80821115620001c85760008155600101620001cd565b600181811c90821680620001f857607f821691505b602082108114156200021a57634e487b7160e01b600052602260045260246000fd5b50919050565b600080835481600182811c9150808316806200023d57607f831692505b60208084108214156200025e57634e487b7160e01b86526022600452602486fd5b8180156200027557600181146200028757620002b6565b60ff19861689528489019650620002b6565b60008a81526020902060005b86811015620002ae5781548b82015290850190830162000293565b505084890196505b509498975050505050505050565b60805160a05160c05161180d620002f46000396000610834015260006107ff015260006102d9015261180d6000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c80637ecebe00116100f9578063d145fb3111610097578063e35c9b4411610071578063e35c9b4414610452578063eb9c0baf14610465578063f0dc2c2014610478578063f2fde38b1461048b57600080fd5b8063d145fb3114610401578063d505accf14610414578063dd62ed3e1461042757600080fd5b80639b9965ef116100d35780639b9965ef146103a85780639ba990c8146103c8578063a9059cbb146103db578063c14a785b146103ee57600080fd5b80637ecebe00146103585780638da5cb5b1461037857806395d89b41146103a057600080fd5b806330adf81f11610166578063484b973c11610140578063484b973c1461031557806348c7ab841461032857806370a0823114610330578063715018a61461035057600080fd5b806330adf81f146102ad578063313ce567146102d45780633644e5151461030d57600080fd5b80630bbd52bc116101a25780630bbd52bc1461025b57806318160ddd146102705780631faa82831461028757806323b872dd1461029a57600080fd5b806301ffc9a7146101c957806306fdde0314610233578063095ea7b314610248575b600080fd5b61021e6101d736600461123e565b7fffffffff00000000000000000000000000000000000000000000000000000000167f7f5828d0000000000000000000000000000000000000000000000000000000001490565b60405190151581526020015b60405180910390f35b61023b61049e565b60405161022a9190611287565b61021e610256366004611323565b61052c565b61026e61026936600461134d565b6105a5565b005b61027960025481565b60405190815260200161022a565b61026e61029536600461134d565b610682565b61021e6102a8366004611366565b6106b7565b6102797f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6102fb7f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff909116815260200161022a565b6102796107fb565b61026e610323366004611323565b610856565b61026e610888565b61027961033e3660046113a2565b60036020526000908152604090205481565b61026e6109a6565b6102796103663660046113a2565b60056020526000908152604090205481565b60065460405173ffffffffffffffffffffffffffffffffffffffff909116815260200161022a565b61023b610a21565b6102796103b636600461134d565b60009081526008602052604090205490565b61026e6103d63660046113bd565b610a2e565b61021e6103e9366004611323565b610a64565b61026e6103fc366004611323565b610ae9565b61026e61040f3660046113a2565b610b88565b61026e6104223660046113df565b610bf3565b610279610435366004611452565b600460209081526000928352604080842090915290825290205481565b61026e6104603660046113a2565b610f1f565b61026e61047336600461156d565b610f8f565b61026e6104863660046113a2565b611021565b61026e6104993660046113a2565b611094565b600080546104ab906115d1565b80601f01602080910402602001604051908101604052809291908181526020018280546104d7906115d1565b80156105245780601f106104f957610100808354040283529160200191610524565b820191906000526020600020905b81548152906001019060200180831161050757829003601f168201915b505050505081565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906105949086815260200190565b60405180910390a350600192915050565b60008181526008602052604090205461061f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f496e76616c6964207574696c697479206964000000000000000000000000000060448201526064015b60405180910390fd5b6007546000828152600860205260409020546106519173ffffffffffffffffffffffffffffffffffffffff1690610a64565b50604051819033907fc77719ad160dfe81cd33098aab1a7d627a37dbe9ae6c8a3e13963ee2396015db90600090a350565b60065473ffffffffffffffffffffffffffffffffffffffff1633146106a657600080fd5b600090815260086020526040812055565b73ffffffffffffffffffffffffffffffffffffffff831660009081526004602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461074b576107198382611654565b73ffffffffffffffffffffffffffffffffffffffff861660009081526004602090815260408083203384529091529020555b73ffffffffffffffffffffffffffffffffffffffff851660009081526003602052604081208054859290610780908490611654565b909155505073ffffffffffffffffffffffffffffffffffffffff808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906107e89087815260200190565b60405180910390a3506001949350505050565b60007f000000000000000000000000000000000000000000000000000000000000000046146108315761082c61112c565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b60065473ffffffffffffffffffffffffffffffffffffffff16331461087a57600080fd5b61088482826111c6565b5050565b600754604080517f88af7c4e00000000000000000000000000000000000000000000000000000000815290516109209273ffffffffffffffffffffffffffffffffffffffff169182916388af7c4e916004808201926020929091908290030181865afa1580156108fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103e9919061166b565b506007546040517f6a62784200000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff90911690636a62784290602401600060405180830381600087803b15801561098c57600080fd5b505af11580156109a0573d6000803e3d6000fd5b50505050565b60065473ffffffffffffffffffffffffffffffffffffffff1633146109f7576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b600180546104ab906115d1565b60065473ffffffffffffffffffffffffffffffffffffffff163314610a5257600080fd5b60009182526008602052604090912055565b33600090815260036020526040812080548391908390610a85908490611654565b909155505073ffffffffffffffffffffffffffffffffffffffff8316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906105949086815260200190565b3360009081526009602052604090205460ff1661087a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f52657175657374206f6e6c792076616c69642066726f6d207374616b696e672060448201527f636f6e74726163740000000000000000000000000000000000000000000000006064820152608401610616565b60065473ffffffffffffffffffffffffffffffffffffffff163314610bac57600080fd5b600780547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b42841015610c5d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401610616565b6000610c676107fb565b73ffffffffffffffffffffffffffffffffffffffff89811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938c166060840152608083018b905260a083019390935260c08083018a90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa158015610dc6573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590610e4157508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b610ea7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e56414c49445f5349474e45520000000000000000000000000000000000006044820152606401610616565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526004602090815260408083208b8516808552908352928190208a905551898152919350918a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b60065473ffffffffffffffffffffffffffffffffffffffff163314610f4357600080fd5b73ffffffffffffffffffffffffffffffffffffffff16600090815260096020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b60065473ffffffffffffffffffffffffffffffffffffffff163314610fb357600080fd5b60005b825181101561101c57818181518110610fd157610fd1611684565b602002602001015160086000858481518110610fef57610fef611684565b60200260200101518152602001908152602001600020819055508080611014906116b3565b915050610fb6565b505050565b60065473ffffffffffffffffffffffffffffffffffffffff16331461104557600080fd5b73ffffffffffffffffffffffffffffffffffffffff16600090815260096020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b60065473ffffffffffffffffffffffffffffffffffffffff1633146110e5576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600060405161115e91906116ec565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b80600260008282546111d891906117bf565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b60006020828403121561125057600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461128057600080fd5b9392505050565b600060208083528351808285015260005b818110156112b457858101830151858201604001528201611298565b818111156112c6576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461131e57600080fd5b919050565b6000806040838503121561133657600080fd5b61133f836112fa565b946020939093013593505050565b60006020828403121561135f57600080fd5b5035919050565b60008060006060848603121561137b57600080fd5b611384846112fa565b9250611392602085016112fa565b9150604084013590509250925092565b6000602082840312156113b457600080fd5b611280826112fa565b600080604083850312156113d057600080fd5b50508035926020909101359150565b600080600080600080600060e0888a0312156113fa57600080fd5b611403886112fa565b9650611411602089016112fa565b95506040880135945060608801359350608088013560ff8116811461143557600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561146557600080fd5b61146e836112fa565b915061147c602084016112fa565b90509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126114c557600080fd5b8135602067ffffffffffffffff808311156114e2576114e2611485565b8260051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f8301168101818110848211171561152557611525611485565b60405293845285810183019383810192508785111561154357600080fd5b83870191505b8482101561156257813583529183019190830190611549565b979650505050505050565b6000806040838503121561158057600080fd5b823567ffffffffffffffff8082111561159857600080fd5b6115a4868387016114b4565b935060208501359150808211156115ba57600080fd5b506115c7858286016114b4565b9150509250929050565b600181811c908216806115e557607f821691505b6020821081141561161f577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008282101561166657611666611625565b500390565b60006020828403121561167d57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156116e5576116e5611625565b5060010190565b600080835481600182811c91508083168061170857607f831692505b6020808410821415611741577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b8180156117555760018114611784576117b1565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008616895284890196506117b1565b60008a81526020902060005b868110156117a95781548b820152908501908301611790565b505084890196505b509498975050505050505050565b600082198211156117d2576117d2611625565b50019056fea26469706673582212204f4831a9dee4c59609534fe97ea1ff46ae1761bf6891d3d5eb03224e24b113c164736f6c634300080c0033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101c45760003560e01c80637ecebe00116100f9578063d145fb3111610097578063e35c9b4411610071578063e35c9b4414610452578063eb9c0baf14610465578063f0dc2c2014610478578063f2fde38b1461048b57600080fd5b8063d145fb3114610401578063d505accf14610414578063dd62ed3e1461042757600080fd5b80639b9965ef116100d35780639b9965ef146103a85780639ba990c8146103c8578063a9059cbb146103db578063c14a785b146103ee57600080fd5b80637ecebe00146103585780638da5cb5b1461037857806395d89b41146103a057600080fd5b806330adf81f11610166578063484b973c11610140578063484b973c1461031557806348c7ab841461032857806370a0823114610330578063715018a61461035057600080fd5b806330adf81f146102ad578063313ce567146102d45780633644e5151461030d57600080fd5b80630bbd52bc116101a25780630bbd52bc1461025b57806318160ddd146102705780631faa82831461028757806323b872dd1461029a57600080fd5b806301ffc9a7146101c957806306fdde0314610233578063095ea7b314610248575b600080fd5b61021e6101d736600461123e565b7fffffffff00000000000000000000000000000000000000000000000000000000167f7f5828d0000000000000000000000000000000000000000000000000000000001490565b60405190151581526020015b60405180910390f35b61023b61049e565b60405161022a9190611287565b61021e610256366004611323565b61052c565b61026e61026936600461134d565b6105a5565b005b61027960025481565b60405190815260200161022a565b61026e61029536600461134d565b610682565b61021e6102a8366004611366565b6106b7565b6102797f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6102fb7f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff909116815260200161022a565b6102796107fb565b61026e610323366004611323565b610856565b61026e610888565b61027961033e3660046113a2565b60036020526000908152604090205481565b61026e6109a6565b6102796103663660046113a2565b60056020526000908152604090205481565b60065460405173ffffffffffffffffffffffffffffffffffffffff909116815260200161022a565b61023b610a21565b6102796103b636600461134d565b60009081526008602052604090205490565b61026e6103d63660046113bd565b610a2e565b61021e6103e9366004611323565b610a64565b61026e6103fc366004611323565b610ae9565b61026e61040f3660046113a2565b610b88565b61026e6104223660046113df565b610bf3565b610279610435366004611452565b600460209081526000928352604080842090915290825290205481565b61026e6104603660046113a2565b610f1f565b61026e61047336600461156d565b610f8f565b61026e6104863660046113a2565b611021565b61026e6104993660046113a2565b611094565b600080546104ab906115d1565b80601f01602080910402602001604051908101604052809291908181526020018280546104d7906115d1565b80156105245780601f106104f957610100808354040283529160200191610524565b820191906000526020600020905b81548152906001019060200180831161050757829003601f168201915b505050505081565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906105949086815260200190565b60405180910390a350600192915050565b60008181526008602052604090205461061f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f496e76616c6964207574696c697479206964000000000000000000000000000060448201526064015b60405180910390fd5b6007546000828152600860205260409020546106519173ffffffffffffffffffffffffffffffffffffffff1690610a64565b50604051819033907fc77719ad160dfe81cd33098aab1a7d627a37dbe9ae6c8a3e13963ee2396015db90600090a350565b60065473ffffffffffffffffffffffffffffffffffffffff1633146106a657600080fd5b600090815260086020526040812055565b73ffffffffffffffffffffffffffffffffffffffff831660009081526004602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461074b576107198382611654565b73ffffffffffffffffffffffffffffffffffffffff861660009081526004602090815260408083203384529091529020555b73ffffffffffffffffffffffffffffffffffffffff851660009081526003602052604081208054859290610780908490611654565b909155505073ffffffffffffffffffffffffffffffffffffffff808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906107e89087815260200190565b60405180910390a3506001949350505050565b60007f000000000000000000000000000000000000000000000000000000000000000146146108315761082c61112c565b905090565b507f1daec8d3393b9a54b830aed5e8c5462cafcde431813f093fe64a301c67ad7e0890565b60065473ffffffffffffffffffffffffffffffffffffffff16331461087a57600080fd5b61088482826111c6565b5050565b600754604080517f88af7c4e00000000000000000000000000000000000000000000000000000000815290516109209273ffffffffffffffffffffffffffffffffffffffff169182916388af7c4e916004808201926020929091908290030181865afa1580156108fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103e9919061166b565b506007546040517f6a62784200000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff90911690636a62784290602401600060405180830381600087803b15801561098c57600080fd5b505af11580156109a0573d6000803e3d6000fd5b50505050565b60065473ffffffffffffffffffffffffffffffffffffffff1633146109f7576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b600180546104ab906115d1565b60065473ffffffffffffffffffffffffffffffffffffffff163314610a5257600080fd5b60009182526008602052604090912055565b33600090815260036020526040812080548391908390610a85908490611654565b909155505073ffffffffffffffffffffffffffffffffffffffff8316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906105949086815260200190565b3360009081526009602052604090205460ff1661087a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f52657175657374206f6e6c792076616c69642066726f6d207374616b696e672060448201527f636f6e74726163740000000000000000000000000000000000000000000000006064820152608401610616565b60065473ffffffffffffffffffffffffffffffffffffffff163314610bac57600080fd5b600780547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b42841015610c5d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401610616565b6000610c676107fb565b73ffffffffffffffffffffffffffffffffffffffff89811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938c166060840152608083018b905260a083019390935260c08083018a90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa158015610dc6573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590610e4157508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b610ea7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e56414c49445f5349474e45520000000000000000000000000000000000006044820152606401610616565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526004602090815260408083208b8516808552908352928190208a905551898152919350918a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b60065473ffffffffffffffffffffffffffffffffffffffff163314610f4357600080fd5b73ffffffffffffffffffffffffffffffffffffffff16600090815260096020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b60065473ffffffffffffffffffffffffffffffffffffffff163314610fb357600080fd5b60005b825181101561101c57818181518110610fd157610fd1611684565b602002602001015160086000858481518110610fef57610fef611684565b60200260200101518152602001908152602001600020819055508080611014906116b3565b915050610fb6565b505050565b60065473ffffffffffffffffffffffffffffffffffffffff16331461104557600080fd5b73ffffffffffffffffffffffffffffffffffffffff16600090815260096020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b60065473ffffffffffffffffffffffffffffffffffffffff1633146110e5576040517f30cd747100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600060405161115e91906116ec565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b80600260008282546111d891906117bf565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b60006020828403121561125057600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461128057600080fd5b9392505050565b600060208083528351808285015260005b818110156112b457858101830151858201604001528201611298565b818111156112c6576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461131e57600080fd5b919050565b6000806040838503121561133657600080fd5b61133f836112fa565b946020939093013593505050565b60006020828403121561135f57600080fd5b5035919050565b60008060006060848603121561137b57600080fd5b611384846112fa565b9250611392602085016112fa565b9150604084013590509250925092565b6000602082840312156113b457600080fd5b611280826112fa565b600080604083850312156113d057600080fd5b50508035926020909101359150565b600080600080600080600060e0888a0312156113fa57600080fd5b611403886112fa565b9650611411602089016112fa565b95506040880135945060608801359350608088013560ff8116811461143557600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561146557600080fd5b61146e836112fa565b915061147c602084016112fa565b90509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126114c557600080fd5b8135602067ffffffffffffffff808311156114e2576114e2611485565b8260051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f8301168101818110848211171561152557611525611485565b60405293845285810183019383810192508785111561154357600080fd5b83870191505b8482101561156257813583529183019190830190611549565b979650505050505050565b6000806040838503121561158057600080fd5b823567ffffffffffffffff8082111561159857600080fd5b6115a4868387016114b4565b935060208501359150808211156115ba57600080fd5b506115c7858286016114b4565b9150509250929050565b600181811c908216806115e557607f821691505b6020821081141561161f577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008282101561166657611666611625565b500390565b60006020828403121561167d57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156116e5576116e5611625565b5060010190565b600080835481600182811c91508083168061170857607f831692505b6020808410821415611741577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b8180156117555760018114611784576117b1565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008616895284890196506117b1565b60008a81526020902060005b868110156117a95781548b820152908501908301611790565b505084890196505b509498975050505050505050565b600082198211156117d2576117d2611625565b50019056fea26469706673582212204f4831a9dee4c59609534fe97ea1ff46ae1761bf6891d3d5eb03224e24b113c164736f6c634300080c0033

Deployed Bytecode Sourcemap

653:5434:6:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;840:201:8;;;;;;:::i;:::-;975:25;;;;;840:201;;;;516:14:11;;509:22;491:41;;479:2;464:18;840:201:8;;;;;;;;1032:18:4;;;:::i;:::-;;;;;;;:::i;2618:211::-;;;;;;:::i;:::-;;:::i;5846:239:6:-;;;;;;:::i;:::-;;:::i;:::-;;1306:26:4;;;;;;;;;1995:25:11;;;1983:2;1968:18;1306:26:4;1849:177:11;5162:106:6;;;;;;:::i;:::-;;:::i;3214:592:4:-;;;;;;:::i;:::-;;:::i;1647:145::-;;1697:95;1647:145;;1084:31;;;;;;;;2718:4:11;2706:17;;;2688:36;;2676:2;2661:18;1084:31:4;2546:184:11;4996:177:4;;;:::i;2286:108:6:-;;;;;;:::i;:::-;;:::i;4047:144::-;;;:::i;1339:44:4:-;;;;;;:::i;:::-;;;;;;;;;;;;;;708:126:8;;;:::i;1907:41:4:-;;;;;;:::i;:::-;;;;;;;;;;;;;;473:79:8;539:6;;473:79;;539:6;;;;3072:74:11;;3060:2;3045:18;473:79:8;2926:226:11;1057:20:4;;;:::i;4557:116:6:-;;;;;;:::i;:::-;4619:7;4645:21;;;:13;:21;;;;;;;4557:116;4869:150;;;;;;:::i;:::-;;:::i;2835:373:4:-;;;;;;:::i;:::-;;:::i;2593:234:6:-;;;;;;:::i;:::-;;:::i;3675:139::-;;;;;;:::i;:::-;;:::i;3997:993:4:-;;;;;;:::i;:::-;;:::i;1390:64::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;3407:123:6;;;;;;:::i;:::-;;:::i;5493:236::-;;;;;;:::i;:::-;;:::i;3141:119::-;;;;;;:::i;:::-;;:::i;558:144:8:-;;;;;;:::i;:::-;;:::i;1032:18:4:-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;2618:211::-;2718:10;2692:4;2708:21;;;:9;:21;;;;;;;;;:30;;;;;;;;;;:39;;;2763:37;2692:4;;2708:30;;2763:37;;;;2741:6;1995:25:11;;1983:2;1968:18;;1849:177;2763:37:4;;;;;;;;-1:-1:-1;2818:4:4;2618:211;;;;:::o;5846:239:6:-;5939:1;5915:21;;;:13;:21;;;;;;5907:56;;;;;;;6772:2:11;5907:56:6;;;6754:21:11;6811:2;6791:18;;;6784:30;6850:20;6830:18;;;6823:48;6888:18;;5907:56:6;;;;;;;;;5990:13;;;6006:21;;;:13;:21;;;;;;5973:55;;5990:13;;;5973:8;:55::i;:::-;-1:-1:-1;6043:35:6;;6071:6;;6059:10;;6043:35;;;;;5846:239;:::o;5162:106::-;372:6:8;;:20;:6;382:10;372:20;364:29;;;;;;5240:21:6::1;::::0;;;:13:::1;:21;::::0;;;;5233:28;5162:106::o;3214:592:4:-;3366:15;;;3332:4;3366:15;;;:9;:15;;;;;;;;3382:10;3366:27;;;;;;;;3455:17;3444:28;;3440:80;;3504:16;3514:6;3504:7;:16;:::i;:::-;3474:15;;;;;;;:9;:15;;;;;;;;3490:10;3474:27;;;;;;;:46;3440:80;3531:15;;;;;;;:9;:15;;;;;:25;;3550:6;;3531:15;:25;;3550:6;;3531:25;:::i;:::-;;;;-1:-1:-1;;3702:13:4;;;;;;;;:9;:13;;;;;;;:23;;;;;;3751:26;3702:13;;3751:26;;;;;;;3719:6;1995:25:11;;1983:2;1968:18;;1849:177;3751:26:4;;;;;;;;-1:-1:-1;3795:4:4;;3214:592;-1:-1:-1;;;;3214:592:4:o;4996:177::-;5053:7;5096:16;5079:13;:33;:87;;5142:24;:22;:24::i;:::-;5072:94;;4996:177;:::o;5079:87::-;-1:-1:-1;5115:24:4;;4996:177::o;2286:108:6:-;372:6:8;;:20;:6;382:10;372:20;364:29;;;;;;2365:22:6::1;2371:7;2380:6;2365:5;:22::i;:::-;2286:108:::0;;:::o;4047:144::-;4101:13;;4117:26;;;;;;;;4084:60;;4101:13;;;;;4117:24;;:26;;;;;;;;;;;;;;;4101:13;4117:26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;4084:60::-;-1:-1:-1;4154:13:6;;:30;;;;;4173:10;4154:30;;;3072:74:11;4154:13:6;;;;;:18;;3045::11;;4154:30:6;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4047:144::o;708:126:8:-;772:6;;;;758:10;:20;754:43;;787:10;;;;;;;;;;;;;;754:43;808:6;:19;;;;;;708:126::o;1057:20:4:-;;;;;;;:::i;4869:150:6:-;372:6:8;;:20;:6;382:10;372:20;364:29;;;;;;4979:21:6::1;::::0;;;:13:::1;:21;::::0;;;;;:33;4869:150::o;2835:373:4:-;2931:10;2905:4;2921:21;;;:9;:21;;;;;:31;;2946:6;;2921:21;2905:4;;2921:31;;2946:6;;2921:31;:::i;:::-;;;;-1:-1:-1;;3098:13:4;;;;;;;:9;:13;;;;;;;:23;;;;;;3147:32;3156:10;;3147:32;;;;3115:6;1995:25:11;;1983:2;1968:18;;1849:177;2593:234:6;2711:10;2684:38;;;;:26;:38;;;;;;;;2663:125;;;;;;;7627:2:11;2663:125:6;;;7609:21:11;7666:2;7646:18;;;7639:30;7705:34;7685:18;;;7678:62;7776:10;7756:18;;;7749:38;7804:19;;2663:125:6;7425:404:11;3675:139:6;372:6:8;;:20;:6;382:10;372:20;364:29;;;;;;3758:13:6::1;:49:::0;;;::::1;;::::0;;;::::1;::::0;;;::::1;::::0;;3675:139::o;3997:993:4:-;4216:15;4204:8;:27;;4196:63;;;;;;;8036:2:11;4196:63:4;;;8018:21:11;8075:2;8055:18;;;8048:30;8114:25;8094:18;;;8087:53;8157:18;;4196:63:4;7834:347:11;4196:63:4;4424:14;4538:18;:16;:18::i;:::-;4639:13;;;;;;;;:6;:13;;;;;;;;;:15;;;;;;;;4588:77;;1697:95;4588:77;;;8473:25:11;8575:18;;;8568:43;;;;8647:15;;;8627:18;;;8620:43;8679:18;;;8672:34;;;8722:19;;;8715:35;;;;8766:19;;;;8759:35;;;4588:77:4;;;;;;;;;;8445:19:11;;;4588:77:4;;;4578:88;;;;;;;;9075:66:11;4468:216:4;;;9063:79:11;9158:11;;;9151:27;;;;9194:12;;;9187:28;;;;9231:12;;4468:216:4;;;;;;;;;;;;;4441:257;;4468:216;4441:257;;;;4713:24;4740:26;;;;;;;;;9481:25:11;;;9554:4;9542:17;;9522:18;;;9515:45;;;;9576:18;;;9569:34;;;9619:18;;;9612:34;;;4441:257:4;;-1:-1:-1;4713:24:4;4740:26;;9453:19:11;;4740:26:4;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;4740:26:4;;;;;;-1:-1:-1;;4789:30:4;;;;;;;:59;;;4843:5;4823:25;;:16;:25;;;4789:59;4781:86;;;;;;;9859:2:11;4781:86:4;;;9841:21:11;9898:2;9878:18;;;9871:30;9937:16;9917:18;;;9910:44;9971:18;;4781:86:4;9657:338:11;4781:86:4;4882:27;;;;;;;;:9;:27;;;;;;;;:36;;;;;;;;;;;;;:44;;;4952:31;1995:25:11;;;4882:36:4;;-1:-1:-1;4952:31:4;;;;;;1968:18:11;4952:31:4;;;;;;;3997:993;;;;;;;:::o;3407:123:6:-;372:6:8;;:20;:6;382:10;372:20;364:29;;;;;;3481:34:6::1;;3518:5;3481:34:::0;;;:26:::1;:34;::::0;;;;:42;;;::::1;::::0;;3407:123::o;5493:236::-;372:6:8;;:20;:6;382:10;372:20;364:29;;;;;;5625:9:6::1;5620:103;5644:5;:12;5640:1;:16;5620:103;;;5703:6;5710:1;5703:9;;;;;;;;:::i;:::-;;;;;;;5677:13;:23;5691:5;5697:1;5691:8;;;;;;;;:::i;:::-;;;;;;;5677:23;;;;;;;;;;;:35;;;;5658:3;;;;;:::i;:::-;;;;5620:103;;;;5493:236:::0;;:::o;3141:119::-;372:6:8;;:20;:6;382:10;372:20;364:29;;;;;;3212:34:6::1;;;::::0;;;:26:::1;:34;::::0;;;;:41;;;::::1;3249:4;3212:41;::::0;;3141:119::o;558:144:8:-;641:6;;;;627:10;:20;623:43;;656:10;;;;;;;;;;;;;;623:43;677:6;:18;;;;;;;;;;;;;;;558:144::o;5179:446:4:-;5244:7;5341:95;5474:4;5458:22;;;;;;:::i;:::-;;;;;;;;;;5309:295;;;12001:25:11;;;;12042:18;;12035:34;;;;5502:14:4;12085:18:11;;;12078:34;5538:13:4;12128:18:11;;;12121:34;5581:4:4;12171:19:11;;;12164:84;11973:19;;5309:295:4;;;;;;;;;;;;5282:336;;;;;;5263:355;;5179:446;:::o;5819:325::-;5904:6;5889:11;;:21;;;;;;;:::i;:::-;;;;-1:-1:-1;;6056:13:4;;;;;;;:9;:13;;;;;;;;:23;;;;;;6105:32;1995:25:11;;;6105:32:4;;1968:18:11;6105:32:4;;;;;;;5819:325;;:::o;14:332:11:-;72:6;125:2;113:9;104:7;100:23;96:32;93:52;;;141:1;138;131:12;93:52;180:9;167:23;230:66;223:5;219:78;212:5;209:89;199:117;;312:1;309;302:12;199:117;335:5;14:332;-1:-1:-1;;;14:332:11:o;543:656::-;655:4;684:2;713;702:9;695:21;745:6;739:13;788:6;783:2;772:9;768:18;761:34;813:1;823:140;837:6;834:1;831:13;823:140;;;932:14;;;928:23;;922:30;898:17;;;917:2;894:26;887:66;852:10;;823:140;;;981:6;978:1;975:13;972:91;;;1051:1;1046:2;1037:6;1026:9;1022:22;1018:31;1011:42;972:91;-1:-1:-1;1115:2:11;1103:15;1120:66;1099:88;1084:104;;;;1190:2;1080:113;;543:656;-1:-1:-1;;;543:656:11:o;1204:196::-;1272:20;;1332:42;1321:54;;1311:65;;1301:93;;1390:1;1387;1380:12;1301:93;1204:196;;;:::o;1405:254::-;1473:6;1481;1534:2;1522:9;1513:7;1509:23;1505:32;1502:52;;;1550:1;1547;1540:12;1502:52;1573:29;1592:9;1573:29;:::i;:::-;1563:39;1649:2;1634:18;;;;1621:32;;-1:-1:-1;;;1405:254:11:o;1664:180::-;1723:6;1776:2;1764:9;1755:7;1751:23;1747:32;1744:52;;;1792:1;1789;1782:12;1744:52;-1:-1:-1;1815:23:11;;1664:180;-1:-1:-1;1664:180:11:o;2031:328::-;2108:6;2116;2124;2177:2;2165:9;2156:7;2152:23;2148:32;2145:52;;;2193:1;2190;2183:12;2145:52;2216:29;2235:9;2216:29;:::i;:::-;2206:39;;2264:38;2298:2;2287:9;2283:18;2264:38;:::i;:::-;2254:48;;2349:2;2338:9;2334:18;2321:32;2311:42;;2031:328;;;;;:::o;2735:186::-;2794:6;2847:2;2835:9;2826:7;2822:23;2818:32;2815:52;;;2863:1;2860;2853:12;2815:52;2886:29;2905:9;2886:29;:::i;3157:248::-;3225:6;3233;3286:2;3274:9;3265:7;3261:23;3257:32;3254:52;;;3302:1;3299;3292:12;3254:52;-1:-1:-1;;3325:23:11;;;3395:2;3380:18;;;3367:32;;-1:-1:-1;3157:248:11:o;3410:693::-;3521:6;3529;3537;3545;3553;3561;3569;3622:3;3610:9;3601:7;3597:23;3593:33;3590:53;;;3639:1;3636;3629:12;3590:53;3662:29;3681:9;3662:29;:::i;:::-;3652:39;;3710:38;3744:2;3733:9;3729:18;3710:38;:::i;:::-;3700:48;;3795:2;3784:9;3780:18;3767:32;3757:42;;3846:2;3835:9;3831:18;3818:32;3808:42;;3900:3;3889:9;3885:19;3872:33;3945:4;3938:5;3934:16;3927:5;3924:27;3914:55;;3965:1;3962;3955:12;3914:55;3410:693;;;;-1:-1:-1;3410:693:11;;;;3988:5;4040:3;4025:19;;4012:33;;-1:-1:-1;4092:3:11;4077:19;;;4064:33;;3410:693;-1:-1:-1;;3410:693:11:o;4108:260::-;4176:6;4184;4237:2;4225:9;4216:7;4212:23;4208:32;4205:52;;;4253:1;4250;4243:12;4205:52;4276:29;4295:9;4276:29;:::i;:::-;4266:39;;4324:38;4358:2;4347:9;4343:18;4324:38;:::i;:::-;4314:48;;4108:260;;;;;:::o;4373:184::-;4425:77;4422:1;4415:88;4522:4;4519:1;4512:15;4546:4;4543:1;4536:15;4562:961;4616:5;4669:3;4662:4;4654:6;4650:17;4646:27;4636:55;;4687:1;4684;4677:12;4636:55;4723:6;4710:20;4749:4;4772:18;4809:2;4805;4802:10;4799:36;;;4815:18;;:::i;:::-;4861:2;4858:1;4854:10;4893:2;4887:9;4952:66;4947:2;4943;4939:11;4935:84;4927:6;4923:97;5070:6;5058:10;5055:22;5050:2;5038:10;5035:18;5032:46;5029:72;;;5081:18;;:::i;:::-;5117:2;5110:22;5167:18;;;5243:15;;;5239:24;;;5201:15;;;;-1:-1:-1;5275:15:11;;;5272:35;;;5303:1;5300;5293:12;5272:35;5339:2;5331:6;5327:15;5316:26;;5351:142;5367:6;5362:3;5359:15;5351:142;;;5433:17;;5421:30;;5471:12;;;;5384;;;;5351:142;;;5511:6;4562:961;-1:-1:-1;;;;;;;4562:961:11:o;5528:595::-;5646:6;5654;5707:2;5695:9;5686:7;5682:23;5678:32;5675:52;;;5723:1;5720;5713:12;5675:52;5763:9;5750:23;5792:18;5833:2;5825:6;5822:14;5819:34;;;5849:1;5846;5839:12;5819:34;5872:61;5925:7;5916:6;5905:9;5901:22;5872:61;:::i;:::-;5862:71;;5986:2;5975:9;5971:18;5958:32;5942:48;;6015:2;6005:8;6002:16;5999:36;;;6031:1;6028;6021:12;5999:36;;6054:63;6109:7;6098:8;6087:9;6083:24;6054:63;:::i;:::-;6044:73;;;5528:595;;;;;:::o;6128:437::-;6207:1;6203:12;;;;6250;;;6271:61;;6325:4;6317:6;6313:17;6303:27;;6271:61;6378:2;6370:6;6367:14;6347:18;6344:38;6341:218;;;6415:77;6412:1;6405:88;6516:4;6513:1;6506:15;6544:4;6541:1;6534:15;6341:218;;6128:437;;;:::o;6917:184::-;6969:77;6966:1;6959:88;7066:4;7063:1;7056:15;7090:4;7087:1;7080:15;7106:125;7146:4;7174:1;7171;7168:8;7165:34;;;7179:18;;:::i;:::-;-1:-1:-1;7216:9:11;;7106:125::o;7236:184::-;7306:6;7359:2;7347:9;7338:7;7334:23;7330:32;7327:52;;;7375:1;7372;7365:12;7327:52;-1:-1:-1;7398:16:11;;7236:184;-1:-1:-1;7236:184:11:o;10000:::-;10052:77;10049:1;10042:88;10149:4;10146:1;10139:15;10173:4;10170:1;10163:15;10189:195;10228:3;10259:66;10252:5;10249:77;10246:103;;;10329:18;;:::i;:::-;-1:-1:-1;10376:1:11;10365:13;;10189:195::o;10518:1219::-;10648:3;10677:1;10710:6;10704:13;10740:3;10762:1;10790:9;10786:2;10782:18;10772:28;;10850:2;10839:9;10835:18;10872;10862:61;;10916:4;10908:6;10904:17;10894:27;;10862:61;10942:2;10990;10982:6;10979:14;10959:18;10956:38;10953:222;;;11029:77;11024:3;11017:90;11130:4;11127:1;11120:15;11160:4;11155:3;11148:17;10953:222;11191:18;11218:162;;;;11394:1;11389:323;;;;11184:528;;11218:162;11266:66;11255:9;11251:82;11246:3;11239:95;11363:6;11358:3;11354:16;11347:23;;11218:162;;11389:323;10465:1;10458:14;;;10502:4;10489:18;;11487:1;11501:165;11515:6;11512:1;11509:13;11501:165;;;11593:14;;11580:11;;;11573:35;11636:16;;;;11530:10;;11501:165;;;11505:3;;11695:6;11690:3;11686:16;11679:23;;11184:528;-1:-1:-1;11728:3:11;;10518:1219;-1:-1:-1;;;;;;;;10518:1219:11:o;12259:128::-;12299:3;12330:1;12326:6;12323:1;12320:13;12317:39;;;12336:18;;:::i;:::-;-1:-1:-1;12372:9:11;;12259:128::o

Swarm Source

ipfs://4f4831a9dee4c59609534fe97ea1ff46ae1761bf6891d3d5eb03224e24b113c1

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
[ 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.