ETH Price: $2,925.46 (+4.71%)
 

Overview

Max Total Supply

90,674,971.785358746105779966 GRILLA

Holders

1,067

Market

Onchain Market Cap

$0.00

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 18 Decimals)

Balance
6,179.293981629629061434 GRILLA

Value
$0.00
0x732295d8d9108297d5f0c6b88cb0eb13ce5bbb08
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information
# Exchange Pair Price  24H Volume % Volume

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 7 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 1 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 2 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 3 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 4 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 5 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 6 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
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.