POL Price: $0.377133 (-2.13%)
 

Sponsored

Transaction Hash
Method
Block
From
To
Exit Forest554334042024-04-04 11:26:54166 days ago1712230014IN
0x8d528e98...4818C3942
0 POL0.0067077232.02837552
Exit Forest554329942024-04-04 11:10:10167 days ago1712229010IN
0x8d528e98...4818C3942
0 POL0.00479233165.77297313
Exit Forest554327862024-04-04 11:01:12167 days ago1712228472IN
0x8d528e98...4818C3942
0 POL0.01801365623.11571507
Exit Forest554324532024-04-04 10:46:36167 days ago1712227596IN
0x8d528e98...4818C3942
0 POL0.01144159395.77976958
Exit Forest554323652024-04-04 10:42:54167 days ago1712227374IN
0x8d528e98...4818C3942
0 POL0.01591829550.63452981
Exit Forest554323272024-04-04 10:41:06167 days ago1712227266IN
0x8d528e98...4818C3942
0 POL0.00638701220.93502627
Exit Forest494956902023-11-03 13:50:25319 days ago1699019425IN
0x8d528e98...4818C3942
0 POL0.0020031769.29231047
Exit Forest494947752023-11-03 13:18:00319 days ago1699017480IN
0x8d528e98...4818C3942
0 POL0.002742594.86681112
Exit Forest481237782023-09-29 13:08:29354 days ago1695992909IN
0x8d528e98...4818C3942
0 POL0.00301377104.25039462
Exit Forest477638142023-09-20 8:49:01364 days ago1695199741IN
0x8d528e98...4818C3942
0 POL0.0024990786.44629357
Exit Forest475715992023-09-15 12:34:11368 days ago1694781251IN
0x8d528e98...4818C3942
0 POL0.00307168106.25370076
Exit Forest475619892023-09-15 6:49:47369 days ago1694760587IN
0x8d528e98...4818C3942
0 POL0.00339316117.373988
Exit Forest475605052023-09-15 5:55:44369 days ago1694757344IN
0x8d528e98...4818C3942
0 POL0.0035057121.26706286
Exit Forest475585972023-09-15 4:45:55369 days ago1694753155IN
0x8d528e98...4818C3942
0 POL0.00469399162.37128187
Exit Forest474234292023-09-11 19:02:41372 days ago1694458961IN
0x8d528e98...4818C3942
0 POL0.00435889150.779886
Exit Forest474228512023-09-11 18:41:37372 days ago1694457697IN
0x8d528e98...4818C3942
0 POL0.00534376184.84782733
Exit Forest439062252023-06-14 14:54:56461 days ago1686754496IN
0x8d528e98...4818C3942
0 POL0.06426208396
Exit Forest439062172023-06-14 14:54:38461 days ago1686754478IN
0x8d528e98...4818C3942
0 POL0.06537088396
Exit Forest439062092023-06-14 14:54:22461 days ago1686754462IN
0x8d528e98...4818C3942
0 POL0.07024168396
Exit Forest362215792022-11-29 17:34:46658 days ago1669743286IN
0x8d528e98...4818C3942
0 POL0.0486834300
Exit Forest362215752022-11-29 17:34:38658 days ago1669743278IN
0x8d528e98...4818C3942
0 POL0.0495234300
Exit Forest362215712022-11-29 17:34:30658 days ago1669743270IN
0x8d528e98...4818C3942
0 POL0.0495234300
Exit Forest362215682022-11-29 17:34:24658 days ago1669743264IN
0x8d528e98...4818C3942
0 POL0.0527247300
Exit Forest362215642022-11-29 17:34:12658 days ago1669743252IN
0x8d528e98...4818C3942
0 POL0.0518847300
Exit Forest297967072022-06-20 16:10:29820 days ago1655741429IN
0x8d528e98...4818C3942
0 POL0.03331703193.25984399
View all transactions

Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
DarkForest

Compiler Version
v0.8.9+commit.e5eed63a

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, Unlicense license
File 1 of 25 : DarkForest.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "VRFConsumerBase.sol";
import "TerminusFacet.sol";
import "ERC20Burnable.sol";
import "IERC721.sol";
import "IERC721Receiver.sol";
import "Ownable.sol";
import "ReentrancyGuard.sol";
import "IStatsFacet.sol";

contract DarkForest is
    Ownable,
    IERC721Receiver,
    VRFConsumerBase,
    ReentrancyGuard
{
    struct Sacrifice {
        // Statuses:
        // 1 -> offered
        // 2 -> randomness fulfilled
        // 3 -> completed
        uint32 status;
        address sacrificer;
        // threshold is an index into UnicornMilkThresholds - determined when the sacrifice is offered
        uint256 threshold;
        uint256 randomness;
    }

    address CryptoUnicornsAddress;
    address UnicornMilkTokenAddress;
    address TerminusAddress;
    uint256 UnicornMilkStakingReward;
    uint256 StakePeriodSeconds;
    mapping(uint256 => address) Staker;
    mapping(uint256 => uint256) UnstakesAt;
    mapping(address => uint256[]) TokensStaked;
    mapping(uint256 => uint256) StakedTokenIndexInStakerArray;
    uint256[3] UnicornMilkThresholds;
    uint256[3] ShadowcornPoolIds;
    uint256[3] LootboxPoolIds;
    uint256 ChainlinkVRFFee;
    bytes32 ChainlinkVRFKeyhash;
    uint256[3] ShadowcornEggBonusesToUnicornMilk;
    mapping(bytes32 => Sacrifice) ActiveSacrifices;
    mapping(address => bytes32) CurrentSacrificeForSacrificer;

    event EnteredForest(uint256 indexed tokenId, address indexed staker);
    event ExitedForest(
        uint256 indexed tokenId,
        address indexed staker,
        uint256 reward
    );
    event SacrificeOffered(address indexed sacrificer, uint256 amount);
    event SacrificeCompleted(
        address indexed sacrificer,
        uint256 indexed terminusPoolId,
        bytes32 requestId
    );

    constructor(
        address _CryptoUnicornsAddress,
        address _UnicornMilkTokenAddress,
        address _TerminusAddress,
        uint256 _UnicornMilkStakingReward,
        uint256 _StakePeriodSeconds,
        address _VRFCoordinatorAddress,
        address _LinkTokenAddress,
        uint256 _ChainlinkVRFFee,
        bytes32 _ChainlinkVRFKeyhash
    ) VRFConsumerBase(_VRFCoordinatorAddress, _LinkTokenAddress) {
        transferOwnership(_msgSender());
        CryptoUnicornsAddress = _CryptoUnicornsAddress;
        UnicornMilkTokenAddress = _UnicornMilkTokenAddress;
        TerminusAddress = _TerminusAddress;
        UnicornMilkStakingReward = _UnicornMilkStakingReward;
        StakePeriodSeconds = _StakePeriodSeconds;
        ChainlinkVRFFee = _ChainlinkVRFFee;
        ChainlinkVRFKeyhash = _ChainlinkVRFKeyhash;
    }

    function setCryptoUnicornsAddress(address _CryptoUnicornsAddress)
        external
        onlyOwner
    {
        CryptoUnicornsAddress = _CryptoUnicornsAddress;
    }

    function cryptoUnicornsAddress() external view returns (address) {
        return CryptoUnicornsAddress;
    }

    function setUnicornMilkTokenAddress(address _UnicornMilkTokenAddress)
        external
        onlyOwner
    {
        UnicornMilkTokenAddress = _UnicornMilkTokenAddress;
    }

    function unicornMilkTokenAddress() external view returns (address) {
        return UnicornMilkTokenAddress;
    }

    function setTerminusAddress(address _TerminusAddress) external onlyOwner {
        TerminusAddress = _TerminusAddress;
    }

    function terminusAddress() external view returns (address) {
        return TerminusAddress;
    }

    function setUnicornMilkStakingReward(uint256 _UnicornMilkStakingReward)
        external
        onlyOwner
    {
        UnicornMilkStakingReward = _UnicornMilkStakingReward;
    }

    function unicornMilkStakingReward() external view returns (uint256) {
        return UnicornMilkStakingReward;
    }

    function setStakePeriodSeconds(uint256 _StakePeriodSeconds)
        external
        onlyOwner
    {
        StakePeriodSeconds = _StakePeriodSeconds;
    }

    function stakePeriodSeconds() external view returns (uint256) {
        return StakePeriodSeconds;
    }

    function setUnicornMilkThresholds(uint256[3] memory _UnicornMilkThresholds)
        external
        onlyOwner
    {
        UnicornMilkThresholds = _UnicornMilkThresholds;
    }

    function unicornMilkThresholds() external view returns (uint256[3] memory) {
        return UnicornMilkThresholds;
    }

    function setShadowcornPoolIds(uint256[3] memory _ShadowcornPoolIds)
        external
        onlyOwner
    {
        ShadowcornPoolIds = _ShadowcornPoolIds;
    }

    function shadowcornPoolIds() external view returns (uint256[3] memory) {
        return ShadowcornPoolIds;
    }

    function setLootboxPoolIds(uint256[3] memory _LootboxPoolIds)
        external
        onlyOwner
    {
        LootboxPoolIds = _LootboxPoolIds;
    }

    function lootboxPoolIds() external view returns (uint256[3] memory) {
        return LootboxPoolIds;
    }

    function setShadowcornEggBonusesToUnicornMilk(uint256[3] memory _bonuses)
        external
        onlyOwner
    {
        ShadowcornEggBonusesToUnicornMilk = _bonuses;
    }

    function shadowcornEggBonusesToUnicornMilk()
        external
        view
        returns (uint256[3] memory)
    {
        return ShadowcornEggBonusesToUnicornMilk;
    }

    function contractIsInitialized() public view returns (bool) {
        return
            UnicornMilkThresholds[0] > 0 &&
            ShadowcornPoolIds[0] > 0 &&
            LootboxPoolIds[0] > 0;
    }

    // This function surrenders control of Terminus pools back to the contract owner. This is so that,
    // in case this contract is deprecated, the owner can still perform operations on the shadowcorn
    // eggs and lootboxes.
    function surrenderTerminusPools() external onlyOwner {
        address _owner = owner();
        TerminusFacet terminusContract = TerminusFacet(TerminusAddress);
        for (uint256 i = 0; i < 3; i++) {
            terminusContract.setPoolController(ShadowcornPoolIds[i], _owner);
            terminusContract.setPoolController(LootboxPoolIds[i], _owner);
        }
    }

    function numStaked(address _staker) public view returns (uint256) {
        return TokensStaked[_staker].length;
    }

    function tokenOfStakerByIndex(address _staker, uint256 index)
        public
        view
        returns (uint256)
    {
        require(
            index < numStaked(_staker),
            "DarkForest: tokenOfStakerByIndex -- staker index out of bounds"
        );
        return TokensStaked[_staker][index];
    }

    function _addTokenToStakerEnumeration(address _staker, uint256 _tokenId)
        private
    {
        uint256 numPreviouslyStaked = numStaked(_staker);
        TokensStaked[_staker].push(_tokenId);
        StakedTokenIndexInStakerArray[_tokenId] = numPreviouslyStaked;
    }

    function _removeTokenFromStakerEnumeration(
        address _staker,
        uint256 _tokenId
    ) private {
        // Swap to last index (if necessary), then pop.
        uint256 lastIndex = numStaked(_staker) - 1;
        uint256 tokenIndex = StakedTokenIndexInStakerArray[_tokenId];
        require(
            TokensStaked[_staker][tokenIndex] == _tokenId,
            "DarkForest: _removeTokenFromStakerEnumeration -- token was not found where expected in TokensStaked array"
        );
        if (tokenIndex != lastIndex) {
            uint256 lastId = TokensStaked[_staker][lastIndex];
            TokensStaked[_staker][tokenIndex] = lastId;
            StakedTokenIndexInStakerArray[lastId] = tokenIndex;
        }

        TokensStaked[_staker].pop();
        delete StakedTokenIndexInStakerArray[_tokenId];
    }

    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4) {
        require(
            _msgSender() == CryptoUnicornsAddress,
            "DarkForest: onERC721Received -- Only Crypto Unicorns may enter the Dark Forest (nice try, my friend)"
        );
        IStatsFacet CUContract = IStatsFacet(CryptoUnicornsAddress);
        (, bool gameLocked, , uint256 lifecycleStage, , , ) = CUContract
            .getUnicornMetadata(tokenId);
        require(
            !gameLocked,
            "DarkForest: onERC721Received -- Cannot enter the Dark Forest while the Crypto Unicorns game has locked your token"
        );
        require(
            lifecycleStage == 2,
            "DarkForest: onERC721Received -- Only adult unicorns can enter the dark forest"
        );
        Staker[tokenId] = from;
        UnstakesAt[tokenId] = block.timestamp + StakePeriodSeconds;
        _addTokenToStakerEnumeration(from, tokenId);
        emit EnteredForest(tokenId, from);
        return IERC721Receiver.onERC721Received.selector;
    }

    function exitForest(uint256 tokenId)
        external
        nonReentrant
        returns (uint256)
    {
        require(
            _msgSender() == Staker[tokenId],
            "DarkForest: exitForest -- only the address which staked the token can make it exit the dark forest"
        );
        require(
            block.timestamp >= UnstakesAt[tokenId],
            "DarkForest: exitForest -- unicorn cannot find the exit yet"
        );
        IERC721(CryptoUnicornsAddress).safeTransferFrom(
            address(this),
            _msgSender(),
            tokenId
        );
        ERC20Burnable unimContract = ERC20Burnable(UnicornMilkTokenAddress);
        uint256 darkForestUnicornMilkBalance = unimContract.balanceOf(
            address(this)
        );
        uint256 reward = UnicornMilkStakingReward;
        TerminusFacet terminusContract = TerminusFacet(TerminusAddress);
        for (uint256 i = 0; i < 3; i++) {
            reward += (ShadowcornEggBonusesToUnicornMilk[i] *
                terminusContract.balanceOf(_msgSender(), ShadowcornPoolIds[i]));
        }
        if (darkForestUnicornMilkBalance < reward) {
            reward = UnicornMilkStakingReward;
        }
        if (darkForestUnicornMilkBalance >= reward) {
            unimContract.transfer(_msgSender(), reward);
        } else {
            reward = 0;
        }
        _removeTokenFromStakerEnumeration(_msgSender(), tokenId);
        Staker[tokenId] = address(0);
        UnstakesAt[tokenId] = 0;
        emit ExitedForest(tokenId, _msgSender(), reward);
        return reward;
    }

    function remainingShadowcornEggs()
        public
        view
        returns (uint256[3] memory, uint256)
    {
        TerminusFacet terminusContract = TerminusFacet(TerminusAddress);

        uint256[3] memory remainingShadowcornEggsByType;
        uint256 totalRemainingShadowcornEggs;
        for (uint256 i = 0; i < 3; i++) {
            uint256 availableCapacity = terminusContract.terminusPoolCapacity(
                ShadowcornPoolIds[i]
            ) - terminusContract.terminusPoolSupply(ShadowcornPoolIds[i]);
            totalRemainingShadowcornEggs += availableCapacity;
            remainingShadowcornEggsByType[i] = availableCapacity;
        }

        return (remainingShadowcornEggsByType, totalRemainingShadowcornEggs);
    }

    function offerSacrifice(uint256 amount) external nonReentrant {
        require(
            contractIsInitialized(),
            "DarkForest: offerSacrifice -- The altar is in ruins"
        );
        require(
            CurrentSacrificeForSacrificer[_msgSender()] == 0,
            "DarkForest: offerSacrifice -- Sender already has a sacrifice in progress"
        );

        uint256 totalRemainingShadowcornEggs;
        (, totalRemainingShadowcornEggs) = remainingShadowcornEggs();
        require(
            totalRemainingShadowcornEggs > 0,
            "DarkForest: offerSacrifice -- The altar seems lifeless and without energy"
        );

        emit SacrificeOffered(_msgSender(), amount);

        ERC20Burnable unimContract = ERC20Burnable(UnicornMilkTokenAddress);

        // Determine how generous of a sacrifice the caller is making. This is stored in the threshold
        // variable. 0 means low, 1 means medium, 2 means high.
        require(
            amount >= UnicornMilkThresholds[0],
            "DarkForest: offerSacrifice -- The altar rejects your paltry sacrifice"
        );
        uint256 threshold = 0;
        if (amount >= UnicornMilkThresholds[2]) {
            threshold = 2;
        } else if (amount >= UnicornMilkThresholds[1]) {
            threshold = 1;
        }
        uint256 sacrificeAmount = UnicornMilkThresholds[threshold];
        require(
            unimContract.allowance(_msgSender(), address(this)) >=
                sacrificeAmount,
            "DarkForest: offerSacrifice -- Insufficient allowance on Unicorm Milk token"
        );
        unimContract.burnFrom(_msgSender(), sacrificeAmount);

        Sacrifice memory currentSacrifice;
        currentSacrifice.status = 1;
        currentSacrifice.sacrificer = _msgSender();
        currentSacrifice.threshold = threshold;

        bytes32 requestId = requestRandomness(
            ChainlinkVRFKeyhash,
            ChainlinkVRFFee
        );
        ActiveSacrifices[requestId] = currentSacrifice;
        CurrentSacrificeForSacrificer[_msgSender()] = requestId;
    }

    function fulfillRandomness(bytes32 requestId, uint256 randomness)
        internal
        override
    {
        Sacrifice storage currentSacrifice = ActiveSacrifices[requestId];
        if (currentSacrifice.status != 1) {
            return;
        }
        currentSacrifice.status = 2;
        currentSacrifice.randomness = randomness;
    }

    function completeSacrifice() external nonReentrant {
        bytes32 requestId = CurrentSacrificeForSacrificer[_msgSender()];
        require(
            requestId != 0,
            "DarkForest: completeSacrifice -- Sender has no sacrifice in progress"
        );
        Sacrifice storage currentSacrifice = ActiveSacrifices[requestId];

        TerminusFacet terminusContract = TerminusFacet(TerminusAddress);

        uint256[3] memory remainingShadowcornEggsByType;
        uint256 totalRemainingShadowcornEggs;

        (
            remainingShadowcornEggsByType,
            totalRemainingShadowcornEggs
        ) = remainingShadowcornEggs();

        uint256 terminusPoolId = 0;
        if (totalRemainingShadowcornEggs > 0) {
            require(
                currentSacrifice.status == 2,
                "DarkForest: completeSacrifice -- Ritual cannot be completed at this time"
            );

            // The following extraction of randomness assumes maximal entropy (256 bits worth) in the random number returned by
            // Chainlink VRF. This was confirmed by Max Melcher from Chainlink.
            // There will be a slight bias introduced by how we use this randomness.
            // The first random number we are selecting is being selected modulo 100, which has factors prime to 2.
            // The second random number will be reduced modulo the number of remaining shadowcorn eggs, which may or may not be a power of 2.
            // Either way, the bias will be very small because 2^(256-m)/N is very large for m = 0, N = 100 or m = 7, N = number of remaining shadowcorn eggs.
            // The bias amounts to a difference between probabilities of outcomes closer to 0 compared to those of outcomes closer to N and is
            // 1/[floor(2^(256-m)/N)*(floor(2^(256-m)/N) + 1)].
            // Even at the largest number of available shadowcorn eggs (3000), this bias is a difference in probabilities of: 1.0997770908598577e-143
            uint256 initialRandomness = currentSacrifice.randomness % 100;
            uint256 secondaryRandomness = currentSacrifice.randomness >> 7;

            terminusPoolId = LootboxPoolIds[currentSacrifice.threshold];
            bool selectShadowcornEgg = (currentSacrifice.threshold == 0 &&
                initialRandomness < 10) ||
                (currentSacrifice.threshold == 1 && initialRandomness < 50) ||
                (currentSacrifice.threshold == 2);
            if (selectShadowcornEgg) {
                uint256 eggNumber = secondaryRandomness %
                    totalRemainingShadowcornEggs;
                if (eggNumber < remainingShadowcornEggsByType[0]) {
                    terminusPoolId = ShadowcornPoolIds[0];
                } else if (
                    eggNumber <
                    remainingShadowcornEggsByType[0] +
                        remainingShadowcornEggsByType[1]
                ) {
                    terminusPoolId = ShadowcornPoolIds[1];
                } else {
                    terminusPoolId = ShadowcornPoolIds[2];
                }
            }
            if (terminusPoolId > 0) {
                terminusContract.mint(_msgSender(), terminusPoolId, 1, "");
            }
        }
        currentSacrifice.status = 3;
        delete CurrentSacrificeForSacrificer[_msgSender()];
        emit SacrificeCompleted(_msgSender(), terminusPoolId, requestId);
    }

    function staker(uint256 tokenId) external view returns (address) {
        return Staker[tokenId];
    }

    function unstakesAt(uint256 tokenId) external view returns (uint256) {
        return UnstakesAt[tokenId];
    }

    function setChainlinkVRFFee(uint256 _fee) external onlyOwner {
        ChainlinkVRFFee = _fee;
    }

    function chainlinkVRFFee() external view returns (uint256) {
        return ChainlinkVRFFee;
    }

    function setChainlinkVRFKeyhash(bytes32 _keyhash) external onlyOwner {
        ChainlinkVRFKeyhash = _keyhash;
    }

    function chainlinkVRFKeyhash() external view returns (bytes32) {
        return ChainlinkVRFKeyhash;
    }

    function viewSacrifice(bytes32 _requestId)
        external
        view
        returns (Sacrifice memory)
    {
        Sacrifice memory requestedSacrifice = ActiveSacrifices[_requestId];
        return requestedSacrifice;
    }

    function currentSacrificeId(address sacrificer)
        external
        view
        returns (bytes32)
    {
        return CurrentSacrificeForSacrificer[sacrificer];
    }

    function withdrawERC20(address tokenAddress, uint256 amount)
        external
        onlyOwner
    {
        ERC20Burnable erc20Contract = ERC20Burnable(tokenAddress);
        erc20Contract.transfer(_msgSender(), amount);
    }

    function drainERC20(address tokenAddress) external onlyOwner {
        ERC20Burnable erc20Contract = ERC20Burnable(tokenAddress);
        uint256 darkForestUnicornMilkBalance = erc20Contract.balanceOf(
            address(this)
        );
        erc20Contract.transfer(_msgSender(), darkForestUnicornMilkBalance);
    }

    function rescueUnicorn(uint256 tokenId) external onlyOwner {
        IERC721 cryptoUnicornsContract = IERC721(CryptoUnicornsAddress);
        require(
            cryptoUnicornsContract.ownerOf(tokenId) == address(this),
            "DarkForest: recoverUnicorn -- That unicorn is not in the Dark Forest"
        );
        address _staker = Staker[tokenId];
        _removeTokenFromStakerEnumeration(_staker, tokenId);
        delete Staker[tokenId];
        delete UnstakesAt[tokenId];
        IERC721(CryptoUnicornsAddress).transferFrom(
            address(this),
            _msgSender(),
            tokenId
        );
    }

    function resetSacrificeForSacrificer(address sacrificer)
        external
        onlyOwner
    {
        delete CurrentSacrificeForSacrificer[sacrificer];
    }
}

File 2 of 25 : VRFConsumerBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "LinkTokenInterface.sol";

import "VRFRequestIDBase.sol";

/** ****************************************************************************
 * @notice Interface for contracts using VRF randomness
 * *****************************************************************************
 * @dev PURPOSE
 *
 * @dev Reggie the Random Oracle (not his real job) wants to provide randomness
 * @dev to Vera the verifier in such a way that Vera can be sure he's not
 * @dev making his output up to suit himself. Reggie provides Vera a public key
 * @dev to which he knows the secret key. Each time Vera provides a seed to
 * @dev Reggie, he gives back a value which is computed completely
 * @dev deterministically from the seed and the secret key.
 *
 * @dev Reggie provides a proof by which Vera can verify that the output was
 * @dev correctly computed once Reggie tells it to her, but without that proof,
 * @dev the output is indistinguishable to her from a uniform random sample
 * @dev from the output space.
 *
 * @dev The purpose of this contract is to make it easy for unrelated contracts
 * @dev to talk to Vera the verifier about the work Reggie is doing, to provide
 * @dev simple access to a verifiable source of randomness.
 * *****************************************************************************
 * @dev USAGE
 *
 * @dev Calling contracts must inherit from VRFConsumerBase, and can
 * @dev initialize VRFConsumerBase's attributes in their constructor as
 * @dev shown:
 *
 * @dev   contract VRFConsumer {
 * @dev     constuctor(<other arguments>, address _vrfCoordinator, address _link)
 * @dev       VRFConsumerBase(_vrfCoordinator, _link) public {
 * @dev         <initialization with other arguments goes here>
 * @dev       }
 * @dev   }
 *
 * @dev The oracle will have given you an ID for the VRF keypair they have
 * @dev committed to (let's call it keyHash), and have told you the minimum LINK
 * @dev price for VRF service. Make sure your contract has sufficient LINK, and
 * @dev call requestRandomness(keyHash, fee, seed), where seed is the input you
 * @dev want to generate randomness from.
 *
 * @dev Once the VRFCoordinator has received and validated the oracle's response
 * @dev to your request, it will call your contract's fulfillRandomness method.
 *
 * @dev The randomness argument to fulfillRandomness is the actual random value
 * @dev generated from your seed.
 *
 * @dev The requestId argument is generated from the keyHash and the seed by
 * @dev makeRequestId(keyHash, seed). If your contract could have concurrent
 * @dev requests open, you can use the requestId to track which seed is
 * @dev associated with which randomness. See VRFRequestIDBase.sol for more
 * @dev details. (See "SECURITY CONSIDERATIONS" for principles to keep in mind,
 * @dev if your contract could have multiple requests in flight simultaneously.)
 *
 * @dev Colliding `requestId`s are cryptographically impossible as long as seeds
 * @dev differ. (Which is critical to making unpredictable randomness! See the
 * @dev next section.)
 *
 * *****************************************************************************
 * @dev SECURITY CONSIDERATIONS
 *
 * @dev A method with the ability to call your fulfillRandomness method directly
 * @dev could spoof a VRF response with any random value, so it's critical that
 * @dev it cannot be directly called by anything other than this base contract
 * @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method).
 *
 * @dev For your users to trust that your contract's random behavior is free
 * @dev from malicious interference, it's best if you can write it so that all
 * @dev behaviors implied by a VRF response are executed *during* your
 * @dev fulfillRandomness method. If your contract must store the response (or
 * @dev anything derived from it) and use it later, you must ensure that any
 * @dev user-significant behavior which depends on that stored value cannot be
 * @dev manipulated by a subsequent VRF request.
 *
 * @dev Similarly, both miners and the VRF oracle itself have some influence
 * @dev over the order in which VRF responses appear on the blockchain, so if
 * @dev your contract could have multiple VRF requests in flight simultaneously,
 * @dev you must ensure that the order in which the VRF responses arrive cannot
 * @dev be used to manipulate your contract's user-significant behavior.
 *
 * @dev Since the ultimate input to the VRF is mixed with the block hash of the
 * @dev block in which the request is made, user-provided seeds have no impact
 * @dev on its economic security properties. They are only included for API
 * @dev compatability with previous versions of this contract.
 *
 * @dev Since the block hash of the block which contains the requestRandomness
 * @dev call is mixed into the input to the VRF *last*, a sufficiently powerful
 * @dev miner could, in principle, fork the blockchain to evict the block
 * @dev containing the request, forcing the request to be included in a
 * @dev different block with a different hash, and therefore a different input
 * @dev to the VRF. However, such an attack would incur a substantial economic
 * @dev cost. This cost scales with the number of blocks the VRF oracle waits
 * @dev until it calls responds to a request.
 */
abstract contract VRFConsumerBase is VRFRequestIDBase {

  /**
   * @notice fulfillRandomness handles the VRF response. Your contract must
   * @notice implement it. See "SECURITY CONSIDERATIONS" above for important
   * @notice principles to keep in mind when implementing your fulfillRandomness
   * @notice method.
   *
   * @dev VRFConsumerBase expects its subcontracts to have a method with this
   * @dev signature, and will call it once it has verified the proof
   * @dev associated with the randomness. (It is triggered via a call to
   * @dev rawFulfillRandomness, below.)
   *
   * @param requestId The Id initially returned by requestRandomness
   * @param randomness the VRF output
   */
  function fulfillRandomness(
    bytes32 requestId,
    uint256 randomness
  )
    internal
    virtual;

  /**
   * @dev In order to keep backwards compatibility we have kept the user
   * seed field around. We remove the use of it because given that the blockhash
   * enters later, it overrides whatever randomness the used seed provides.
   * Given that it adds no security, and can easily lead to misunderstandings,
   * we have removed it from usage and can now provide a simpler API.
   */
  uint256 constant private USER_SEED_PLACEHOLDER = 0;

  /**
   * @notice requestRandomness initiates a request for VRF output given _seed
   *
   * @dev The fulfillRandomness method receives the output, once it's provided
   * @dev by the Oracle, and verified by the vrfCoordinator.
   *
   * @dev The _keyHash must already be registered with the VRFCoordinator, and
   * @dev the _fee must exceed the fee specified during registration of the
   * @dev _keyHash.
   *
   * @dev The _seed parameter is vestigial, and is kept only for API
   * @dev compatibility with older versions. It can't *hurt* to mix in some of
   * @dev your own randomness, here, but it's not necessary because the VRF
   * @dev oracle will mix the hash of the block containing your request into the
   * @dev VRF seed it ultimately uses.
   *
   * @param _keyHash ID of public key against which randomness is generated
   * @param _fee The amount of LINK to send with the request
   *
   * @return requestId unique ID for this request
   *
   * @dev The returned requestId can be used to distinguish responses to
   * @dev concurrent requests. It is passed as the first argument to
   * @dev fulfillRandomness.
   */
  function requestRandomness(
    bytes32 _keyHash,
    uint256 _fee
  )
    internal
    returns (
      bytes32 requestId
    )
  {
    LINK.transferAndCall(vrfCoordinator, _fee, abi.encode(_keyHash, USER_SEED_PLACEHOLDER));
    // This is the seed passed to VRFCoordinator. The oracle will mix this with
    // the hash of the block containing this request to obtain the seed/input
    // which is finally passed to the VRF cryptographic machinery.
    uint256 vRFSeed  = makeVRFInputSeed(_keyHash, USER_SEED_PLACEHOLDER, address(this), nonces[_keyHash]);
    // nonces[_keyHash] must stay in sync with
    // VRFCoordinator.nonces[_keyHash][this], which was incremented by the above
    // successful LINK.transferAndCall (in VRFCoordinator.randomnessRequest).
    // This provides protection against the user repeating their input seed,
    // which would result in a predictable/duplicate output, if multiple such
    // requests appeared in the same block.
    nonces[_keyHash] = nonces[_keyHash] + 1;
    return makeRequestId(_keyHash, vRFSeed);
  }

  LinkTokenInterface immutable internal LINK;
  address immutable private vrfCoordinator;

  // Nonces for each VRF key from which randomness has been requested.
  //
  // Must stay in sync with VRFCoordinator[_keyHash][this]
  mapping(bytes32 /* keyHash */ => uint256 /* nonce */) private nonces;

  /**
   * @param _vrfCoordinator address of VRFCoordinator contract
   * @param _link address of LINK token contract
   *
   * @dev https://docs.chain.link/docs/link-token-contracts
   */
  constructor(
    address _vrfCoordinator,
    address _link
  ) {
    vrfCoordinator = _vrfCoordinator;
    LINK = LinkTokenInterface(_link);
  }

  // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF
  // proof. rawFulfillRandomness then calls fulfillRandomness, after validating
  // the origin of the call
  function rawFulfillRandomness(
    bytes32 requestId,
    uint256 randomness
  )
    external
  {
    require(msg.sender == vrfCoordinator, "Only VRFCoordinator can fulfill");
    fulfillRandomness(requestId, randomness);
  }
}

File 3 of 25 : LinkTokenInterface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface LinkTokenInterface {

  function allowance(
    address owner,
    address spender
  )
    external
    view
    returns (
      uint256 remaining
    );

  function approve(
    address spender,
    uint256 value
  )
    external
    returns (
      bool success
    );

  function balanceOf(
    address owner
  )
    external
    view
    returns (
      uint256 balance
    );

  function decimals()
    external
    view
    returns (
      uint8 decimalPlaces
    );

  function decreaseApproval(
    address spender,
    uint256 addedValue
  )
    external
    returns (
      bool success
    );

  function increaseApproval(
    address spender,
    uint256 subtractedValue
  ) external;

  function name()
    external
    view
    returns (
      string memory tokenName
    );

  function symbol()
    external
    view
    returns (
      string memory tokenSymbol
    );

  function totalSupply()
    external
    view
    returns (
      uint256 totalTokensIssued
    );

  function transfer(
    address to,
    uint256 value
  )
    external
    returns (
      bool success
    );

  function transferAndCall(
    address to,
    uint256 value,
    bytes calldata data
  )
    external
    returns (
      bool success
    );

  function transferFrom(
    address from,
    address to,
    uint256 value
  )
    external
    returns (
      bool success
    );

}

File 4 of 25 : VRFRequestIDBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract VRFRequestIDBase {

  /**
   * @notice returns the seed which is actually input to the VRF coordinator
   *
   * @dev To prevent repetition of VRF output due to repetition of the
   * @dev user-supplied seed, that seed is combined in a hash with the
   * @dev user-specific nonce, and the address of the consuming contract. The
   * @dev risk of repetition is mostly mitigated by inclusion of a blockhash in
   * @dev the final seed, but the nonce does protect against repetition in
   * @dev requests which are included in a single block.
   *
   * @param _userSeed VRF seed input provided by user
   * @param _requester Address of the requesting contract
   * @param _nonce User-specific nonce at the time of the request
   */
  function makeVRFInputSeed(
    bytes32 _keyHash,
    uint256 _userSeed,
    address _requester,
    uint256 _nonce
  )
    internal
    pure
    returns (
      uint256
    )
  {
    return uint256(keccak256(abi.encode(_keyHash, _userSeed, _requester, _nonce)));
  }

  /**
   * @notice Returns the id for this request
   * @param _keyHash The serviceAgreement ID to be used for this request
   * @param _vRFInputSeed The seed to be passed directly to the VRF
   * @return The id for this request
   *
   * @dev Note that _vRFInputSeed is not the seed passed by the consuming
   * @dev contract, but the one generated by makeVRFInputSeed
   */
  function makeRequestId(
    bytes32 _keyHash,
    uint256 _vRFInputSeed
  )
    internal
    pure
    returns (
      bytes32
    )
  {
    return keccak256(abi.encodePacked(_keyHash, _vRFInputSeed));
  }
}

File 5 of 25 : TerminusFacet.sol
// SPDX-License-Identifier: Apache-2.0

/**
 * Authors: Moonstream Engineering ([email protected])
 * GitHub: https://github.com/bugout-dev/dao
 *
 * This is an implementation of the Terminus decentralized authorization contract.
 *
 * Terminus users can create authorization pools. Each authorization pool has the following properties:
 * 1. Controller: The address that controls the pool. Initially set to be the address of the pool creator.
 * 2. Pool URI: Metadata URI for the authorization pool.
 * 3. Pool capacity: The total number of tokens that can be minted in that authorization pool.
 * 4. Pool supply: The number of tokens that have actually been minted in that authorization pool.
 * 5. Transferable: A boolean value which denotes whether or not tokens from that pool can be transfered
 *    between addresses. (Note: Implemented by TerminusStorage.poolNotTransferable since we expect most
 *    pools to be transferable. This negation is better for storage + gas since false is default value
 *    in map to bool.)
 * 6. Burnable: A boolean value which denotes whether or not tokens from that pool can be burned.
 */

pragma solidity ^0.8.0;

import "IERC20.sol";
import "ERC1155WithTerminusStorage.sol";
import "LibTerminus.sol";
import "LibDiamond.sol";

contract TerminusFacet is ERC1155WithTerminusStorage {
    constructor() {
        LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
        ts.controller = msg.sender;
    }

    event PoolMintBatch(
        uint256 indexed id,
        address indexed operator,
        address from,
        address[] toAddresses,
        uint256[] amounts
    );

    function poolMintBatch(
        uint256 id,
        address[] memory toAddresses,
        uint256[] memory amounts
    ) public {
        address operator = _msgSender();
        LibTerminus.enforcePoolIsController(id, operator);
        require(
            toAddresses.length == amounts.length,
            "TerminusFacet: _poolMintBatch -- toAddresses and amounts length mismatch"
        );

        LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();

        uint256 i = 0;
        uint256 totalAmount = 0;

        for (i = 0; i < toAddresses.length; i++) {
            address to = toAddresses[i];
            uint256 amount = amounts[i];
            require(
                to != address(0),
                "TerminusFacet: _poolMintBatch -- cannot mint to zero address"
            );
            totalAmount += amount;
            ts.poolBalances[id][to] += amount;
            emit TransferSingle(operator, address(0), to, id, amount);
        }

        require(
            ts.poolSupply[id] + totalAmount <= ts.poolCapacity[id],
            "TerminusFacet: _poolMintBatch -- Minted tokens would exceed pool capacity"
        );
        ts.poolSupply[id] += totalAmount;

        emit PoolMintBatch(id, operator, address(0), toAddresses, amounts);
    }

    function terminusController() external view returns (address) {
        return LibTerminus.terminusStorage().controller;
    }

    function paymentToken() external view returns (address) {
        return LibTerminus.terminusStorage().paymentToken;
    }

    function setPaymentToken(address newPaymentToken) external {
        LibTerminus.enforceIsController();
        LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
        ts.paymentToken = newPaymentToken;
    }

    function poolBasePrice() external view returns (uint256) {
        return LibTerminus.terminusStorage().poolBasePrice;
    }

    function setPoolBasePrice(uint256 newBasePrice) external {
        LibTerminus.enforceIsController();
        LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
        ts.poolBasePrice = newBasePrice;
    }

    function _paymentTokenContract() internal view returns (IERC20) {
        address paymentTokenAddress = LibTerminus
            .terminusStorage()
            .paymentToken;
        require(
            paymentTokenAddress != address(0),
            "TerminusFacet: Payment token has not been set"
        );
        return IERC20(paymentTokenAddress);
    }

    function withdrawPayments(address toAddress, uint256 amount) external {
        LibTerminus.enforceIsController();
        require(
            _msgSender() == toAddress,
            "TerminusFacet: withdrawPayments -- Controller can only withdraw to self"
        );
        IERC20 paymentTokenContract = _paymentTokenContract();
        paymentTokenContract.transfer(toAddress, amount);
    }

    function setURI(uint256 poolID, string memory poolURI) external {
        LibTerminus.enforcePoolIsController(poolID, _msgSender());
        LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
        ts.poolURI[poolID] = poolURI;
    }

    function totalPools() external view returns (uint256) {
        return LibTerminus.terminusStorage().currentPoolID;
    }

    function setPoolController(uint256 poolID, address newController) external {
        LibTerminus.enforcePoolIsController(poolID, msg.sender);
        LibTerminus.setPoolController(poolID, newController);
    }

    function terminusPoolController(uint256 poolID)
        external
        view
        returns (address)
    {
        return LibTerminus.terminusStorage().poolController[poolID];
    }

    function terminusPoolCapacity(uint256 poolID)
        external
        view
        returns (uint256)
    {
        return LibTerminus.terminusStorage().poolCapacity[poolID];
    }

    function terminusPoolSupply(uint256 poolID)
        external
        view
        returns (uint256)
    {
        return LibTerminus.terminusStorage().poolSupply[poolID];
    }

    function createSimplePool(uint256 _capacity) external returns (uint256) {
        LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
        uint256 requiredPayment = ts.poolBasePrice;
        IERC20 paymentTokenContract = _paymentTokenContract();
        require(
            paymentTokenContract.allowance(_msgSender(), address(this)) >=
                requiredPayment,
            "TerminusFacet: createSimplePool -- Insufficient allowance on payment token"
        );
        paymentTokenContract.transferFrom(
            msg.sender,
            address(this),
            requiredPayment
        );
        return LibTerminus.createSimplePool(_capacity);
    }

    function createPoolV1(
        uint256 _capacity,
        bool _transferable,
        bool _burnable
    ) external returns (uint256) {
        LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
        // TODO(zomglings): Implement requiredPayment update based on pool features.
        uint256 requiredPayment = ts.poolBasePrice;
        IERC20 paymentTokenContract = _paymentTokenContract();
        require(
            paymentTokenContract.allowance(_msgSender(), address(this)) >=
                requiredPayment,
            "TerminusFacet: createPoolV1 -- Insufficient allowance on payment token"
        );
        paymentTokenContract.transferFrom(
            msg.sender,
            address(this),
            requiredPayment
        );
        uint256 poolID = LibTerminus.createSimplePool(_capacity);
        if (!_transferable) {
            ts.poolNotTransferable[poolID] = true;
        }
        if (_burnable) {
            ts.poolBurnable[poolID] = true;
        }
        return poolID;
    }

    function mint(
        address to,
        uint256 poolID,
        uint256 amount,
        bytes memory data
    ) external {
        LibTerminus.enforcePoolIsController(poolID, msg.sender);
        _mint(to, poolID, amount, data);
    }

    function mintBatch(
        address to,
        uint256[] memory poolIDs,
        uint256[] memory amounts,
        bytes memory data
    ) external {
        for (uint256 i = 0; i < poolIDs.length; i++) {
            LibTerminus.enforcePoolIsController(poolIDs[i], _msgSender());
        }
        _mintBatch(to, poolIDs, amounts, data);
    }

    function burn(
        address from,
        uint256 poolID,
        uint256 amount
    ) external {
        address operator = _msgSender();
        require(
            operator == from || isApprovedForPool(poolID, operator),
            "TerminusFacet: burn -- caller is neither owner nor approved"
        );
        _burn(from, poolID, amount);
    }
}

File 6 of 25 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 7 of 25 : ERC1155WithTerminusStorage.sol
// SPDX-License-Identifier: Apache-2.0

/**
 * Authors: Moonstream Engineering ([email protected])
 * GitHub: https://github.com/bugout-dev/dao
 *
 * An ERC1155 implementation which uses the Moonstream DAO common storage structure for proxies.
 * EIP1155: https://eips.ethereum.org/EIPS/eip-1155
 *
 * The Moonstream contract is used to delegate calls from an EIP2535 Diamond proxy.
 *
 * This implementation is adapted from the OpenZeppelin ERC1155 implementation:
 * https://github.com/OpenZeppelin/openzeppelin-contracts/tree/6bd6b76d1156e20e45d1016f355d154141c7e5b9/contracts/token/ERC1155
 */

pragma solidity ^0.8.9;

import "IERC1155.sol";
import "IERC1155Receiver.sol";
import "IERC1155MetadataURI.sol";
import "Address.sol";
import "Context.sol";
import "ERC165.sol";
import "LibTerminus.sol";

contract ERC1155WithTerminusStorage is
    Context,
    ERC165,
    IERC1155,
    IERC1155MetadataURI
{
    using Address for address;

    constructor() {}

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(ERC165, IERC165)
        returns (bool)
    {
        return
            interfaceId == type(IERC1155).interfaceId ||
            interfaceId == type(IERC1155MetadataURI).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    function uri(uint256 poolID)
        public
        view
        virtual
        override
        returns (string memory)
    {
        return LibTerminus.terminusStorage().poolURI[poolID];
    }

    /**
     * @dev See {IERC1155-balanceOf}.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id)
        public
        view
        virtual
        override
        returns (uint256)
    {
        require(
            account != address(0),
            "ERC1155WithTerminusStorage: balance query for the zero address"
        );
        return LibTerminus.terminusStorage().poolBalances[id][account];
    }

    /**
     * @dev See {IERC1155-balanceOfBatch}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
        public
        view
        virtual
        override
        returns (uint256[] memory)
    {
        require(
            accounts.length == ids.length,
            "ERC1155WithTerminusStorage: accounts and ids length mismatch"
        );

        uint256[] memory batchBalances = new uint256[](accounts.length);

        for (uint256 i = 0; i < accounts.length; ++i) {
            batchBalances[i] = balanceOf(accounts[i], ids[i]);
        }

        return batchBalances;
    }

    /**
     * @dev See {IERC1155-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved)
        public
        virtual
        override
    {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC1155-isApprovedForAll}.
     */
    function isApprovedForAll(address account, address operator)
        public
        view
        virtual
        override
        returns (bool)
    {
        return
            LibTerminus.terminusStorage().globalOperatorApprovals[account][
                operator
            ];
    }

    function isApprovedForPool(uint256 poolID, address operator)
        public
        view
        returns (bool)
    {
        return LibTerminus._isApprovedForPool(poolID, operator);
    }

    function approveForPool(uint256 poolID, address operator) external {
        LibTerminus.enforcePoolIsController(poolID, _msgSender());
        LibTerminus._approveForPool(poolID, operator);
    }

    /**
     * @dev See {IERC1155-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) public virtual override {
        require(
            from == _msgSender() ||
                isApprovedForAll(from, _msgSender()) ||
                isApprovedForPool(id, _msgSender()),
            "ERC1155WithTerminusStorage: caller is not owner nor approved"
        );
        _safeTransferFrom(from, to, id, amount, data);
    }

    /**
     * @dev See {IERC1155-safeBatchTransferFrom}.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) public virtual override {
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "ERC1155WithTerminusStorage: transfer caller is not owner nor approved"
        );
        _safeBatchTransferFrom(from, to, ids, amounts, data);
    }

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function _safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        require(
            to != address(0),
            "ERC1155WithTerminusStorage: transfer to the zero address"
        );
        LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
        require(
            !ts.poolNotTransferable[id],
            "ERC1155WithTerminusStorage: _safeTransferFrom -- pool is not transferable"
        );

        address operator = _msgSender();

        _beforeTokenTransfer(
            operator,
            from,
            to,
            _asSingletonArray(id),
            _asSingletonArray(amount),
            data
        );

        uint256 fromBalance = ts.poolBalances[id][from];
        require(
            fromBalance >= amount,
            "ERC1155WithTerminusStorage: insufficient balance for transfer"
        );
        unchecked {
            ts.poolBalances[id][from] = fromBalance - amount;
        }
        ts.poolBalances[id][to] += amount;

        emit TransferSingle(operator, from, to, id, amount);

        _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function _safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        require(
            ids.length == amounts.length,
            "ERC1155WithTerminusStorage: ids and amounts length mismatch"
        );
        require(
            to != address(0),
            "ERC1155WithTerminusStorage: transfer to the zero address"
        );

        address operator = _msgSender();

        _beforeTokenTransfer(operator, from, to, ids, amounts, data);

        LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();

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

            uint256 fromBalance = ts.poolBalances[id][from];
            require(
                fromBalance >= amount,
                "ERC1155WithTerminusStorage: insufficient balance for transfer"
            );
            unchecked {
                ts.poolBalances[id][from] = fromBalance - amount;
            }
            ts.poolBalances[id][to] += amount;
        }

        emit TransferBatch(operator, from, to, ids, amounts);

        _doSafeBatchTransferAcceptanceCheck(
            operator,
            from,
            to,
            ids,
            amounts,
            data
        );
    }

    /**
     * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function _mint(
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        require(
            to != address(0),
            "ERC1155WithTerminusStorage: mint to the zero address"
        );
        LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
        require(
            ts.poolSupply[id] + amount <= ts.poolCapacity[id],
            "ERC1155WithTerminusStorage: _mint -- Minted tokens would exceed pool capacity"
        );

        address operator = _msgSender();

        _beforeTokenTransfer(
            operator,
            address(0),
            to,
            _asSingletonArray(id),
            _asSingletonArray(amount),
            data
        );

        ts.poolSupply[id] += amount;
        ts.poolBalances[id][to] += amount;
        emit TransferSingle(operator, address(0), to, id, amount);

        _doSafeTransferAcceptanceCheck(
            operator,
            address(0),
            to,
            id,
            amount,
            data
        );
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function _mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        require(
            to != address(0),
            "ERC1155WithTerminusStorage: mint to the zero address"
        );
        require(
            ids.length == amounts.length,
            "ERC1155WithTerminusStorage: ids and amounts length mismatch"
        );

        LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();

        for (uint256 i = 0; i < ids.length; i++) {
            require(
                ts.poolSupply[ids[i]] + amounts[i] <= ts.poolCapacity[ids[i]],
                "ERC1155WithTerminusStorage: _mintBatch -- Minted tokens would exceed pool capacity"
            );
        }

        address operator = _msgSender();

        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; i++) {
            ts.poolSupply[ids[i]] += amounts[i];
            ts.poolBalances[ids[i]][to] += amounts[i];
        }

        emit TransferBatch(operator, address(0), to, ids, amounts);

        _doSafeBatchTransferAcceptanceCheck(
            operator,
            address(0),
            to,
            ids,
            amounts,
            data
        );
    }

    /**
     * @dev Destroys `amount` tokens of token type `id` from `from`
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `from` must have at least `amount` tokens of token type `id`.
     */
    function _burn(
        address from,
        uint256 id,
        uint256 amount
    ) internal virtual {
        require(
            from != address(0),
            "ERC1155WithTerminusStorage: burn from the zero address"
        );
        LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
        require(
            ts.poolBurnable[id],
            "ERC1155WithTerminusStorage: _burn -- pool is not burnable"
        );

        address operator = _msgSender();

        _beforeTokenTransfer(
            operator,
            from,
            address(0),
            _asSingletonArray(id),
            _asSingletonArray(amount),
            ""
        );

        uint256 fromBalance = ts.poolBalances[id][from];
        require(
            fromBalance >= amount,
            "ERC1155WithTerminusStorage: burn amount exceeds balance"
        );
        unchecked {
            ts.poolBalances[id][from] = fromBalance - amount;
            ts.poolSupply[id] -= amount;
        }

        emit TransferSingle(operator, from, address(0), id, amount);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     */
    function _burnBatch(
        address from,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal virtual {
        require(
            from != address(0),
            "ERC1155WithTerminusStorage: burn from the zero address"
        );
        require(
            ids.length == amounts.length,
            "ERC1155WithTerminusStorage: ids and amounts length mismatch"
        );

        address operator = _msgSender();

        LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
        for (uint256 i = 0; i < ids.length; i++) {
            require(
                ts.poolBurnable[ids[i]],
                "ERC1155WithTerminusStorage: _burnBatch -- pool is not burnable"
            );
        }

        _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");

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

            uint256 fromBalance = ts.poolBalances[id][from];
            require(
                fromBalance >= amount,
                "ERC1155WithTerminusStorage: burn amount exceeds balance"
            );
            unchecked {
                ts.poolBalances[id][from] = fromBalance - amount;
                ts.poolSupply[id] -= amount;
            }
        }

        emit TransferBatch(operator, from, address(0), ids, amounts);
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits a {ApprovalForAll} event.
     */
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual {
        require(
            owner != operator,
            "ERC1155WithTerminusStorage: setting approval status for self"
        );
        LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
        ts.globalOperatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning, as well as batched variants.
     *
     * The same hook is called on both single and batched variants. For single
     * transfers, the length of the `id` and `amount` arrays will be 1.
     *
     * Calling conditions (for each `id` and `amount` pair):
     *
     * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * of token type `id` will be  transferred to `to`.
     * - When `from` is zero, `amount` tokens of token type `id` will be minted
     * for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
     * will be burned.
     * - `from` and `to` are never both zero.
     * - `ids` and `amounts` have the same, non-zero length.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {}

    function _doSafeTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) private {
        if (to.isContract()) {
            try
                IERC1155Receiver(to).onERC1155Received(
                    operator,
                    from,
                    id,
                    amount,
                    data
                )
            returns (bytes4 response) {
                if (response != IERC1155Receiver.onERC1155Received.selector) {
                    revert(
                        "ERC1155WithTerminusStorage: ERC1155Receiver rejected tokens"
                    );
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert(
                    "ERC1155WithTerminusStorage: transfer to non ERC1155Receiver implementer"
                );
            }
        }
    }

    function _doSafeBatchTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) private {
        if (to.isContract()) {
            try
                IERC1155Receiver(to).onERC1155BatchReceived(
                    operator,
                    from,
                    ids,
                    amounts,
                    data
                )
            returns (bytes4 response) {
                if (
                    response != IERC1155Receiver.onERC1155BatchReceived.selector
                ) {
                    revert(
                        "ERC1155WithTerminusStorage: ERC1155Receiver rejected tokens"
                    );
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert(
                    "ERC1155WithTerminusStorage: transfer to non ERC1155Receiver implementer"
                );
            }
        }
    }

    function _asSingletonArray(uint256 element)
        private
        pure
        returns (uint256[] memory)
    {
        uint256[] memory array = new uint256[](1);
        array[0] = element;

        return array;
    }
}

File 8 of 25 : IERC1155.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "IERC165.sol";

/**
 * @dev Required interface of an ERC1155 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1155[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155 is IERC165 {
    /**
     * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
     */
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

    /**
     * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
     * transfers.
     */
    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] values
    );

    /**
     * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
     * `approved`.
     */
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    /**
     * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
     *
     * If an {URI} event was emitted for `id`, the standard
     * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
     * returned by {IERC1155MetadataURI-uri}.
     */
    event URI(string value, uint256 indexed id);

    /**
     * @dev Returns the amount of tokens of token type `id` owned by `account`.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) external view returns (uint256);

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
        external
        view
        returns (uint256[] memory);

    /**
     * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
     *
     * Emits an {ApprovalForAll} event.
     *
     * Requirements:
     *
     * - `operator` cannot be the caller.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address account, address operator) external view returns (bool);

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes calldata data
    ) external;

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) external;
}

File 9 of 25 : IERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 10 of 25 : IERC1155Receiver.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "IERC165.sol";

/**
 * @dev _Available since v3.1._
 */
interface IERC1155Receiver is IERC165 {
    /**
        @dev Handles the receipt of a single ERC1155 token type. This function is
        called at the end of a `safeTransferFrom` after the balance has been updated.
        To accept the transfer, this must return
        `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
        (i.e. 0xf23a6e61, or its own function selector).
        @param operator The address which initiated the transfer (i.e. msg.sender)
        @param from The address which previously owned the token
        @param id The ID of the token being transferred
        @param value The amount of tokens being transferred
        @param data Additional data with no specified format
        @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
    */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);

    /**
        @dev Handles the receipt of a multiple ERC1155 token types. This function
        is called at the end of a `safeBatchTransferFrom` after the balances have
        been updated. To accept the transfer(s), this must return
        `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
        (i.e. 0xbc197c81, or its own function selector).
        @param operator The address which initiated the batch transfer (i.e. msg.sender)
        @param from The address which previously owned the token
        @param ids An array containing ids of each token being transferred (order and length must match values array)
        @param values An array containing amounts of each token being transferred (order and length must match ids array)
        @param data Additional data with no specified format
        @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
    */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}

File 11 of 25 : IERC1155MetadataURI.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "IERC1155.sol";

/**
 * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
 * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155MetadataURI is IERC1155 {
    /**
     * @dev Returns the URI for token type `id`.
     *
     * If the `\{id\}` substring is present in the URI, it must be replaced by
     * clients with the actual token type ID.
     */
    function uri(uint256 id) external view returns (string memory);
}

File 12 of 25 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 13 of 25 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 14 of 25 : ERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 15 of 25 : LibTerminus.sol
// SPDX-License-Identifier: Apache-2.0

/**
 * Authors: Moonstream Engineering ([email protected])
 * GitHub: https://github.com/bugout-dev/dao
 *
 * Common storage structure and internal methods for Moonstream DAO Terminus contracts.
 * As Terminus is an extension of ERC1155, this library can also be used to implement bare ERC1155 contracts
 * using the common storage pattern (e.g. for use in diamond proxies).
 */

// TODO(zomglings): Should we support EIP1761 in addition to ERC1155 or roll our own scopes and feature flags?
// https://eips.ethereum.org/EIPS/eip-1761

pragma solidity ^0.8.9;

library LibTerminus {
    bytes32 constant TERMINUS_STORAGE_POSITION =
        keccak256("moonstreamdao.eth.storage.terminus");

    struct TerminusStorage {
        // Terminus administration
        address controller;
        bool isTerminusActive;
        uint256 currentPoolID;
        address paymentToken;
        uint256 poolBasePrice;
        // Terminus pools
        mapping(uint256 => address) poolController;
        mapping(uint256 => string) poolURI;
        mapping(uint256 => uint256) poolCapacity;
        mapping(uint256 => uint256) poolSupply;
        mapping(uint256 => mapping(address => uint256)) poolBalances;
        mapping(uint256 => bool) poolNotTransferable;
        mapping(uint256 => bool) poolBurnable;
        mapping(address => mapping(address => bool)) globalOperatorApprovals;
        mapping(uint256 => mapping(address => bool)) globalPoolOperatorApprovals;
    }

    function terminusStorage()
        internal
        pure
        returns (TerminusStorage storage es)
    {
        bytes32 position = TERMINUS_STORAGE_POSITION;
        assembly {
            es.slot := position
        }
    }

    event ControlTransferred(
        address indexed previousController,
        address indexed newController
    );

    event PoolControlTransferred(
        uint256 indexed poolID,
        address indexed previousController,
        address indexed newController
    );

    function setController(address newController) internal {
        TerminusStorage storage ts = terminusStorage();
        address previousController = ts.controller;
        ts.controller = newController;
        emit ControlTransferred(previousController, newController);
    }

    function enforceIsController() internal view {
        TerminusStorage storage ts = terminusStorage();
        require(msg.sender == ts.controller, "LibTerminus: Must be controller");
    }

    function setTerminusActive(bool active) internal {
        TerminusStorage storage ts = terminusStorage();
        ts.isTerminusActive = active;
    }

    function setPoolController(uint256 poolID, address newController) internal {
        TerminusStorage storage ts = terminusStorage();
        address previousController = ts.poolController[poolID];
        ts.poolController[poolID] = newController;
        emit PoolControlTransferred(poolID, previousController, newController);
    }

    function createSimplePool(uint256 _capacity) internal returns (uint256) {
        TerminusStorage storage ts = terminusStorage();
        uint256 poolID = ts.currentPoolID + 1;
        setPoolController(poolID, msg.sender);
        ts.poolCapacity[poolID] = _capacity;
        ts.currentPoolID++;
        return poolID;
    }

    function enforcePoolIsController(uint256 poolID, address maybeController)
        internal
        view
    {
        TerminusStorage storage ts = terminusStorage();
        require(
            ts.poolController[poolID] == maybeController,
            "LibTerminus: Must be pool controller"
        );
    }

    function _isApprovedForPool(uint256 poolID, address operator)
        internal
        view
        returns (bool)
    {
        LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
        if (operator == ts.poolController[poolID]) {
            return true;
        } else if (ts.globalPoolOperatorApprovals[poolID][operator]) {
            return true;
        }
        return false;
    }

    function _approveForPool(uint256 poolID, address operator) internal {
        LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage();
        ts.globalPoolOperatorApprovals[poolID][operator] = true;
    }
}

File 16 of 25 : LibDiamond.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Adapted from the Diamond 3 reference implementation by Nick Mudge:
// https://github.com/mudgen/diamond-3-hardhat

import { IDiamondCut } from "IDiamondCut.sol";

library LibDiamond {
    bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage");

    struct FacetAddressAndPosition {
        address facetAddress;
        uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array
    }

    struct FacetFunctionSelectors {
        bytes4[] functionSelectors;
        uint256 facetAddressPosition; // position of facetAddress in facetAddresses array
    }

    struct DiamondStorage {
        // maps function selector to the facet address and
        // the position of the selector in the facetFunctionSelectors.selectors array
        mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition;
        // maps facet addresses to function selectors
        mapping(address => FacetFunctionSelectors) facetFunctionSelectors;
        // facet addresses
        address[] facetAddresses;
        // Used to query if a contract implements an interface.
        // Used to implement ERC-165.
        mapping(bytes4 => bool) supportedInterfaces;
        // owner of the contract
        address contractOwner;
    }

    function diamondStorage() internal pure returns (DiamondStorage storage ds) {
        bytes32 position = DIAMOND_STORAGE_POSITION;
        assembly {
            ds.slot := position
        }
    }

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

    function setContractOwner(address _newOwner) internal {
        DiamondStorage storage ds = diamondStorage();
        address previousOwner = ds.contractOwner;
        ds.contractOwner = _newOwner;
        emit OwnershipTransferred(previousOwner, _newOwner);
    }

    function contractOwner() internal view returns (address contractOwner_) {
        contractOwner_ = diamondStorage().contractOwner;
    }

    function enforceIsContractOwner() internal view {
        require(msg.sender == diamondStorage().contractOwner, "LibDiamond: Must be contract owner");
    }

    event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata);

    // Internal function version of diamondCut
    function diamondCut(
        IDiamondCut.FacetCut[] memory _diamondCut,
        address _init,
        bytes memory _calldata
    ) internal {
        for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) {
            IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action;
            if (action == IDiamondCut.FacetCutAction.Add) {
                addFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
            } else if (action == IDiamondCut.FacetCutAction.Replace) {
                replaceFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
            } else if (action == IDiamondCut.FacetCutAction.Remove) {
                removeFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
            } else {
                revert("LibDiamondCut: Incorrect FacetCutAction");
            }
        }
        emit DiamondCut(_diamondCut, _init, _calldata);
        initializeDiamondCut(_init, _calldata);
    }

    function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
        require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut");
        DiamondStorage storage ds = diamondStorage();
        require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)");
        uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
        // add new facet address if it does not exist
        if (selectorPosition == 0) {
            addFacet(ds, _facetAddress);
        }
        for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) {
            bytes4 selector = _functionSelectors[selectorIndex];
            address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
            require(oldFacetAddress == address(0), "LibDiamondCut: Can't add function that already exists");
            addFunction(ds, selector, selectorPosition, _facetAddress);
            selectorPosition++;
        }
    }

    function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
        require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut");
        DiamondStorage storage ds = diamondStorage();
        require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)");
        uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
        // add new facet address if it does not exist
        if (selectorPosition == 0) {
            addFacet(ds, _facetAddress);
        }
        for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) {
            bytes4 selector = _functionSelectors[selectorIndex];
            address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
            require(oldFacetAddress != _facetAddress, "LibDiamondCut: Can't replace function with same function");
            removeFunction(ds, oldFacetAddress, selector);
            addFunction(ds, selector, selectorPosition, _facetAddress);
            selectorPosition++;
        }
    }

    function removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
        require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut");
        DiamondStorage storage ds = diamondStorage();
        // if function does not exist then do nothing and return
        require(_facetAddress == address(0), "LibDiamondCut: Remove facet address must be address(0)");
        for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) {
            bytes4 selector = _functionSelectors[selectorIndex];
            address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
            removeFunction(ds, oldFacetAddress, selector);
        }
    }

    function addFacet(DiamondStorage storage ds, address _facetAddress) internal {
        enforceHasContractCode(_facetAddress, "LibDiamondCut: New facet has no code");
        ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = ds.facetAddresses.length;
        ds.facetAddresses.push(_facetAddress);
    }


    function addFunction(DiamondStorage storage ds, bytes4 _selector, uint96 _selectorPosition, address _facetAddress) internal {
        ds.selectorToFacetAndPosition[_selector].functionSelectorPosition = _selectorPosition;
        ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(_selector);
        ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress;
    }

    function removeFunction(DiamondStorage storage ds, address _facetAddress, bytes4 _selector) internal {
        require(_facetAddress != address(0), "LibDiamondCut: Can't remove function that doesn't exist");
        // an immutable function is a function defined directly in a diamond
        require(_facetAddress != address(this), "LibDiamondCut: Can't remove immutable function");
        // replace selector with last selector, then delete last selector
        uint256 selectorPosition = ds.selectorToFacetAndPosition[_selector].functionSelectorPosition;
        uint256 lastSelectorPosition = ds.facetFunctionSelectors[_facetAddress].functionSelectors.length - 1;
        // if not the same then replace _selector with lastSelector
        if (selectorPosition != lastSelectorPosition) {
            bytes4 lastSelector = ds.facetFunctionSelectors[_facetAddress].functionSelectors[lastSelectorPosition];
            ds.facetFunctionSelectors[_facetAddress].functionSelectors[selectorPosition] = lastSelector;
            ds.selectorToFacetAndPosition[lastSelector].functionSelectorPosition = uint96(selectorPosition);
        }
        // delete the last selector
        ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop();
        delete ds.selectorToFacetAndPosition[_selector];

        // if no more selectors for facet address then delete the facet address
        if (lastSelectorPosition == 0) {
            // replace facet address with last facet address and delete last facet address
            uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1;
            uint256 facetAddressPosition = ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
            if (facetAddressPosition != lastFacetAddressPosition) {
                address lastFacetAddress = ds.facetAddresses[lastFacetAddressPosition];
                ds.facetAddresses[facetAddressPosition] = lastFacetAddress;
                ds.facetFunctionSelectors[lastFacetAddress].facetAddressPosition = facetAddressPosition;
            }
            ds.facetAddresses.pop();
            delete ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
        }
    }

    function initializeDiamondCut(address _init, bytes memory _calldata) internal {
        if (_init == address(0)) {
            require(_calldata.length == 0, "LibDiamondCut: _init is address(0) but_calldata is not empty");
        } else {
            require(_calldata.length > 0, "LibDiamondCut: _calldata is empty but _init is not address(0)");
            if (_init != address(this)) {
                enforceHasContractCode(_init, "LibDiamondCut: _init address has no code");
            }
            (bool success, bytes memory error) = _init.delegatecall(_calldata);
            if (!success) {
                if (error.length > 0) {
                    // bubble up the error
                    revert(string(error));
                } else {
                    revert("LibDiamondCut: _init function reverted");
                }
            }
        }
    }

    function enforceHasContractCode(address _contract, string memory _errorMessage) internal view {
        uint256 contractSize;
        assembly {
            contractSize := extcodesize(_contract)
        }
        require(contractSize > 0, _errorMessage);
    }
}

File 17 of 25 : IDiamondCut.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Adapted from the Diamond 3 reference implementation by Nick Mudge:
// https://github.com/mudgen/diamond-3-hardhat

interface IDiamondCut {
    enum FacetCutAction {
        Add,
        Replace,
        Remove
    }
    // Add=0, Replace=1, Remove=2

    struct FacetCut {
        address facetAddress;
        FacetCutAction action;
        bytes4[] functionSelectors;
    }

    /// @notice Add/replace/remove any number of functions and optionally execute
    ///         a function with delegatecall
    /// @param _diamondCut Contains the facet addresses and function selectors
    /// @param _init The address of the contract or facet to execute _calldata
    /// @param _calldata A function call, including function selector and arguments
    ///                  _calldata is executed with delegatecall on _init
    function diamondCut(
        FacetCut[] calldata _diamondCut,
        address _init,
        bytes calldata _calldata
    ) external;

    event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
}

File 18 of 25 : ERC20Burnable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "ERC20.sol";
import "Context.sol";

/**
 * @dev Extension of {ERC20} that allows token holders to destroy both their own
 * tokens and those that they have an allowance for, in a way that can be
 * recognized off-chain (via event analysis).
 */
abstract contract ERC20Burnable is Context, ERC20 {
    /**
     * @dev Destroys `amount` tokens from the caller.
     *
     * See {ERC20-_burn}.
     */
    function burn(uint256 amount) public virtual {
        _burn(_msgSender(), amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, deducting from the caller's
     * allowance.
     *
     * See {ERC20-_burn} and {ERC20-allowance}.
     *
     * Requirements:
     *
     * - the caller must have allowance for ``accounts``'s tokens of at least
     * `amount`.
     */
    function burnFrom(address account, uint256 amount) public virtual {
        uint256 currentAllowance = allowance(account, _msgSender());
        require(currentAllowance >= amount, "ERC20: burn amount exceeds allowance");
        unchecked {
            _approve(account, _msgSender(), currentAllowance - amount);
        }
        _burn(account, amount);
    }
}

File 19 of 25 : ERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "IERC20.sol";
import "IERC20Metadata.sol";
import "Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);

        uint256 currentAllowance = _allowances[sender][_msgSender()];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        unchecked {
            _approve(sender, _msgSender(), currentAllowance - amount);
        }

        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        uint256 currentAllowance = _allowances[_msgSender()][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(_msgSender(), spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `sender` to `recipient`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[sender] = senderBalance - amount;
        }
        _balances[recipient] += amount;

        emit Transfer(sender, recipient, amount);

        _afterTokenTransfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}

File 20 of 25 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 21 of 25 : IERC721.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;
}

File 22 of 25 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 23 of 25 : Ownable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _setOwner(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _setOwner(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _setOwner(newOwner);
    }

    function _setOwner(address newOwner) private {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 24 of 25 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

File 25 of 25 : IStatsFacet.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

interface IStatsFacet {
    function getUnicornMetadata(uint256 _tokenId)
        external
        view
        returns (
            bool origin,
            bool gameLocked,
            bool limitedEdition,
            uint256 lifecycleStage,
            uint256 breedingPoints,
            uint256 unicornClass,
            uint256 hatchBirthday
        );

    function getStats(uint256 _dna)
        external
        view
        returns (
            uint256 attack,
            uint256 accuracy,
            uint256 movementSpeed,
            uint256 attackSpeed,
            uint256 defense,
            uint256 vitality,
            uint256 resistance,
            uint256 magic
        );
}

Settings
{
  "evmVersion": "istanbul",
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "libraries": {
    "DarkForest.sol": {}
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_CryptoUnicornsAddress","type":"address"},{"internalType":"address","name":"_UnicornMilkTokenAddress","type":"address"},{"internalType":"address","name":"_TerminusAddress","type":"address"},{"internalType":"uint256","name":"_UnicornMilkStakingReward","type":"uint256"},{"internalType":"uint256","name":"_StakePeriodSeconds","type":"uint256"},{"internalType":"address","name":"_VRFCoordinatorAddress","type":"address"},{"internalType":"address","name":"_LinkTokenAddress","type":"address"},{"internalType":"uint256","name":"_ChainlinkVRFFee","type":"uint256"},{"internalType":"bytes32","name":"_ChainlinkVRFKeyhash","type":"bytes32"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"staker","type":"address"}],"name":"EnteredForest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"ExitedForest","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":"sacrificer","type":"address"},{"indexed":true,"internalType":"uint256","name":"terminusPoolId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"requestId","type":"bytes32"}],"name":"SacrificeCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sacrificer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SacrificeOffered","type":"event"},{"inputs":[],"name":"chainlinkVRFFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chainlinkVRFKeyhash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"completeSacrifice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contractIsInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cryptoUnicornsAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sacrificer","type":"address"}],"name":"currentSacrificeId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"drainERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"exitForest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lootboxPoolIds","outputs":[{"internalType":"uint256[3]","name":"","type":"uint256[3]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_staker","type":"address"}],"name":"numStaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"offerSacrifice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"},{"internalType":"uint256","name":"randomness","type":"uint256"}],"name":"rawFulfillRandomness","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"remainingShadowcornEggs","outputs":[{"internalType":"uint256[3]","name":"","type":"uint256[3]"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"rescueUnicorn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sacrificer","type":"address"}],"name":"resetSacrificeForSacrificer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"setChainlinkVRFFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_keyhash","type":"bytes32"}],"name":"setChainlinkVRFKeyhash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_CryptoUnicornsAddress","type":"address"}],"name":"setCryptoUnicornsAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[3]","name":"_LootboxPoolIds","type":"uint256[3]"}],"name":"setLootboxPoolIds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[3]","name":"_bonuses","type":"uint256[3]"}],"name":"setShadowcornEggBonusesToUnicornMilk","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[3]","name":"_ShadowcornPoolIds","type":"uint256[3]"}],"name":"setShadowcornPoolIds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_StakePeriodSeconds","type":"uint256"}],"name":"setStakePeriodSeconds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_TerminusAddress","type":"address"}],"name":"setTerminusAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_UnicornMilkStakingReward","type":"uint256"}],"name":"setUnicornMilkStakingReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[3]","name":"_UnicornMilkThresholds","type":"uint256[3]"}],"name":"setUnicornMilkThresholds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_UnicornMilkTokenAddress","type":"address"}],"name":"setUnicornMilkTokenAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"shadowcornEggBonusesToUnicornMilk","outputs":[{"internalType":"uint256[3]","name":"","type":"uint256[3]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"shadowcornPoolIds","outputs":[{"internalType":"uint256[3]","name":"","type":"uint256[3]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakePeriodSeconds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"staker","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"surrenderTerminusPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"terminusAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_staker","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfStakerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unicornMilkStakingReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unicornMilkThresholds","outputs":[{"internalType":"uint256[3]","name":"","type":"uint256[3]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unicornMilkTokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"unstakesAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_requestId","type":"bytes32"}],"name":"viewSacrifice","outputs":[{"components":[{"internalType":"uint32","name":"status","type":"uint32"},{"internalType":"address","name":"sacrificer","type":"address"},{"internalType":"uint256","name":"threshold","type":"uint256"},{"internalType":"uint256","name":"randomness","type":"uint256"}],"internalType":"struct DarkForest.Sacrifice","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawERC20","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60c06040523480156200001157600080fd5b5060405162002f8538038062002f858339810160408190526200003491620001fd565b83836200004133620000bb565b6001600160a01b0391821660a05216608052600160025562000063336200010b565b600380546001600160a01b03199081166001600160a01b039b8c1617909155600480548216998b1699909917909855600580549098169690981695909517909555506006919091556007556015555060165562000294565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000546001600160a01b031633146200016b5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b6001600160a01b038116620001d25760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840162000162565b620001dd81620000bb565b50565b80516001600160a01b0381168114620001f857600080fd5b919050565b60008060008060008060008060006101208a8c0312156200021d57600080fd5b620002288a620001e0565b98506200023860208b01620001e0565b97506200024860408b01620001e0565b965060608a0151955060808a015194506200026660a08b01620001e0565b93506200027660c08b01620001e0565b925060e08a015191506101008a015190509295985092959850929598565b60805160a051612cc4620002c1600039600081816116f201526125fb015260006125cc0152612cc46000f3fe608060405234801561001057600080fd5b50600436106102695760003560e01c8063a6df663511610151578063d8dfe755116100c3578063ea38fab011610087578063ea38fab01461058d578063ed58f7f0146105b6578063f2fde38b146105be578063f53504ee146105d1578063f575877a146105d9578063f8d0e5861461060257600080fd5b8063d8dfe75514610539578063dbc0850e1461054c578063dca95cfe1461055f578063e5482d6814610572578063e83998121461057a57600080fd5b8063b405be3211610115578063b405be32146104cb578063c0289df5146104d3578063cb6453b9146104e6578063cb83c595146104f9578063cf1a26ed14610519578063d3434ba71461053157600080fd5b8063a6df663514610472578063aadd977414610483578063ab51260014610494578063b04f9344146104a7578063b2b15d5f146104ba57600080fd5b806364e8456b116101ea5780637c8a5fd5116101ae5780637c8a5fd5146103f95780637c9eb1821461040c5780638bc068e9146104145780638da5cb5b1461042757806394985ddd1461044c578063a1db97821461045f57600080fd5b806364e8456b14610363578063715018a61461037657806375d05f2d1461037e57806379710f07146103915780637bf79bf3146103a457600080fd5b806323ed526d1161023157806323ed526d146103185780632e53f69c1461032b57806354a814051461033357806355ba9eaf1461033b5780635fc9ab481461034e57600080fd5b80630309c9241461026e57806308ee0c741461028d57806312bd4053146102c4578063150b7a02146102d957806316b359fe14610305575b600080fd5b61027661060a565b604051610284929190612825565b60405180910390f35b6102b661029b366004612855565b6001600160a01b03166000908152600a602052604090205490565b604051908152602001610284565b6102d76102d2366004612879565b6107aa565b005b6102ec6102e7366004612905565b6107ee565b6040516001600160e01b03199091168152602001610284565b6102d7610313366004612855565b610b35565b6102d7610326366004612879565b610b81565b6007546102b6565b6102d7610bb8565b6102d7610349366004612855565b610f1d565b610356610f69565b60405161028491906129a4565b6102b66103713660046129b2565b610fa4565b6102d7611441565b6102d761038c3660046129b2565b611477565b6102d761039f366004612855565b6114a6565b6103b76103b23660046129b2565b6115d6565b6040516102849190815163ffffffff1681526020808301516001600160a01b031690820152604080830151908201526060918201519181019190915260800190565b6102d7610407366004612855565b611636565b610356611682565b6102d76104223660046129b2565b6116b8565b6000546001600160a01b03165b6040516001600160a01b039091168152602001610284565b6102d761045a3660046129cb565b6116e7565b6102d761046d3660046129ed565b611769565b6005546001600160a01b0316610434565b6004546001600160a01b0316610434565b6102d76104a2366004612879565b6117c8565b6102d76104b5366004612879565b6117ff565b6003546001600160a01b0316610434565b610356611836565b6102d76104e13660046129b2565b61186c565b6102d76104f43660046129b2565b611a68565b6102b66105073660046129b2565b60009081526009602052604090205490565b610521611a97565b6040519015158152602001610284565b6016546102b6565b6102b66105473660046129ed565b611abe565b6102d761055a3660046129b2565b611b89565b6102d761056d366004612855565b611bb8565b6006546102b6565b6102d76105883660046129b2565b611bfc565b61043461059b3660046129b2565b6000908152600860205260409020546001600160a01b031690565b6102d76120fa565b6102d76105cc366004612855565b61226c565b610356612307565b6102b66105e7366004612855565b6001600160a01b03166000908152601b602052604090205490565b6015546102b6565b61061261275a565b6005546000906001600160a01b031661062961275a565b6000805b600381101561079f576000846001600160a01b031663a44cfc82600f846003811061065a5761065a612a19565b01546040518263ffffffff1660e01b815260040161067a91815260200190565b60206040518083038186803b15801561069257600080fd5b505afa1580156106a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106ca9190612a2f565b856001600160a01b0316635dc8bdf8600f85600381106106ec576106ec612a19565b01546040518263ffffffff1660e01b815260040161070c91815260200190565b60206040518083038186803b15801561072457600080fd5b505afa158015610738573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061075c9190612a2f565b6107669190612a5e565b90506107728184612a75565b92508084836003811061078757610787612a19565b6020020152508061079781612a8d565b91505061062d565b509094909350915050565b6000546001600160a01b031633146107dd5760405162461bcd60e51b81526004016107d490612aa8565b60405180910390fd5b6107ea600c826003612778565b5050565b6003546000906001600160a01b0316336001600160a01b0316146108ae5760405162461bcd60e51b8152602060048201526064602482018190527f4461726b466f726573743a206f6e4552433732315265636569766564202d2d2060448301527f4f6e6c792043727970746f20556e69636f726e73206d617920656e7465722074908201527f6865204461726b20466f7265737420286e696365207472792c206d7920667269608482015263656e642960e01b60a482015260c4016107d4565b60035460405163a28eaae960e01b8152600481018690526001600160a01b03909116906000908190839063a28eaae99060240160e06040518083038186803b1580156108f957600080fd5b505afa15801561090d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109319190612af2565b50505093505092505081156109ee5760405162461bcd60e51b815260206004820152607160248201527f4461726b466f726573743a206f6e4552433732315265636569766564202d2d2060448201527f43616e6e6f7420656e74657220746865204461726b20466f726573742077686960648201527f6c65207468652043727970746f20556e69636f726e732067616d6520686173206084820152703637b1b5b2b2103cb7bab9103a37b5b2b760791b60a482015260c4016107d4565b80600214610a7a5760405162461bcd60e51b815260206004820152604d60248201527f4461726b466f726573743a206f6e4552433732315265636569766564202d2d2060448201527f4f6e6c79206164756c7420756e69636f726e732063616e20656e74657220746860648201526c194819185c9ac8199bdc995cdd609a1b608482015260a4016107d4565b600087815260086020526040902080546001600160a01b0319166001600160a01b038a16179055600754610aae9042612a75565b6000888152600960209081526040808320939093556001600160a01b038b16808352600a8252838320805460018101825590845282842081018c90558b8452600b90925283832091909155915189917fb27144af50191928f7aeb7b1976c2511dfc68ae4707f3aaea6ffa0bab1a29f9091a350630a85bd0160e11b98975050505050505050565b6000546001600160a01b03163314610b5f5760405162461bcd60e51b81526004016107d490612aa8565b600580546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001600160a01b03163314610bab5760405162461bcd60e51b81526004016107d490612aa8565b6107ea600f826003612778565b600280541415610bda5760405162461bcd60e51b81526004016107d490612b5c565b60028055336000908152601b602052604090205480610c6f5760405162461bcd60e51b8152602060048201526044602482018190527f4461726b466f726573743a20636f6d706c657465536163726966696365202d2d908201527f2053656e64657220686173206e6f2073616372696669636520696e2070726f676064820152637265737360e01b608482015260a4016107d4565b6000818152601a602052604090206005546001600160a01b0316610c9161275a565b6000610c9b61060a565b909250905060008115610e9757845463ffffffff16600214610d365760405162461bcd60e51b815260206004820152604860248201527f4461726b466f726573743a20636f6d706c657465536163726966696365202d2d60448201527f2052697475616c2063616e6e6f7420626520636f6d706c6574656420617420746064820152676869732074696d6560c01b608482015260a4016107d4565b600060648660020154610d499190612b93565b6002870154600188015491925060071c9060129060038110610d6d57610d6d612a19565b01549250600087600101546000148015610d875750600a83105b80610da1575087600101546001148015610da15750603283105b80610db0575087600101546002145b90508015610e07576000610dc48684612b93565b8751909150811015610dde57600f60005b01549450610e05565b60208701518751610def9190612a75565b811015610dff57600f6001610dd5565b60115494505b505b8315610e93576001600160a01b03871663731133e9336040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024810187905260016044820152608060648201526000608482015260a401600060405180830381600087803b158015610e7a57600080fd5b505af1158015610e8e573d6000803e3d6000fd5b505050505b5050505b845463ffffffff19166003178555601b6000610eb03390565b6001600160a01b03168152602081019190915260400160009081205580336001600160a01b03167fe739d115cfe9fc3f29ca4534bd55d355944cd17dc184db3671b9adf5cfcee77b88604051610f0891815260200190565b60405180910390a35050600160025550505050565b6000546001600160a01b03163314610f475760405162461bcd60e51b81526004016107d490612aa8565b600380546001600160a01b0319166001600160a01b0392909216919091179055565b610f7161275a565b60408051606081019182905290600f9060039082845b815481526020019060010190808311610f87575050505050905090565b6000600280541415610fc85760405162461bcd60e51b81526004016107d490612b5c565b600280556000828152600860205260409020546001600160a01b0316336001600160a01b0316146110925760405162461bcd60e51b815260206004820152606260248201527f4461726b466f726573743a2065786974466f72657374202d2d206f6e6c79207460448201527f68652061646472657373207768696368207374616b65642074686520746f6b6560648201527f6e2063616e206d616b65206974206578697420746865206461726b20666f72656084820152611cdd60f21b60a482015260c4016107d4565b6000828152600960205260409020544210156111165760405162461bcd60e51b815260206004820152603a60248201527f4461726b466f726573743a2065786974466f72657374202d2d20756e69636f7260448201527f6e2063616e6e6f742066696e642074686520657869742079657400000000000060648201526084016107d4565b6003546001600160a01b03166342842e0e30336040516001600160e01b031960e085901b1681526001600160a01b0392831660048201529116602482015260448101859052606401600060405180830381600087803b15801561117857600080fd5b505af115801561118c573d6000803e3d6000fd5b5050600480546040516370a0823160e01b815230928101929092526001600160a01b031692506000915082906370a082319060240160206040518083038186803b1580156111d957600080fd5b505afa1580156111ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112119190612a2f565b600654600554919250906001600160a01b031660005b600381101561130d576001600160a01b03821662fdd58e33600f846003811061125257611252612a19565b01546040516001600160e01b031960e085901b1681526001600160a01b039092166004830152602482015260440160206040518083038186803b15801561129857600080fd5b505afa1580156112ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112d09190612a2f565b601782600381106112e3576112e3612a19565b01546112ef9190612bb5565b6112f99084612a75565b92508061130581612a8d565b915050611227565b508183101561131c5760065491505b8183106113b9576001600160a01b03841663a9059cbb336040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260248101859052604401602060405180830381600087803b15801561137b57600080fd5b505af115801561138f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113b39190612bd4565b506113be565b600091505b6113c8338761233d565b600086815260086020908152604080832080546001600160a01b03191690556009909152812055336001600160a01b0316867ffadbedc5dfdd53b1b6e36f0e11192437cb1da3ce1d213473bccda055366401208460405161142b91815260200190565b60405180910390a3506001600255949350505050565b6000546001600160a01b0316331461146b5760405162461bcd60e51b81526004016107d490612aa8565b6114756000612541565b565b6000546001600160a01b031633146114a15760405162461bcd60e51b81526004016107d490612aa8565b600755565b6000546001600160a01b031633146114d05760405162461bcd60e51b81526004016107d490612aa8565b6040516370a0823160e01b815230600482015281906000906001600160a01b038316906370a082319060240160206040518083038186803b15801561151457600080fd5b505afa158015611528573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061154c9190612a2f565b60405163a9059cbb60e01b8152336004820152602481018290529091506001600160a01b0383169063a9059cbb906044015b602060405180830381600087803b15801561159857600080fd5b505af11580156115ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115d09190612bd4565b50505050565b6115de6127b6565b506000908152601a60209081526040918290208251608081018452815463ffffffff8116825264010000000090046001600160a01b031692810192909252600181015492820192909252600290910154606082015290565b6000546001600160a01b031633146116605760405162461bcd60e51b81526004016107d490612aa8565b600480546001600160a01b0319166001600160a01b0392909216919091179055565b61168a61275a565b6040805160608101918290526012805482529091600390601360208501808311610f87575050505050905090565b6000546001600160a01b031633146116e25760405162461bcd60e51b81526004016107d490612aa8565b601555565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461175f5760405162461bcd60e51b815260206004820152601f60248201527f4f6e6c7920565246436f6f7264696e61746f722063616e2066756c66696c6c0060448201526064016107d4565b6107ea8282612591565b6000546001600160a01b031633146117935760405162461bcd60e51b81526004016107d490612aa8565b60405163a9059cbb60e01b81523360048201526024810182905282906001600160a01b0382169063a9059cbb9060440161157e565b6000546001600160a01b031633146117f25760405162461bcd60e51b81526004016107d490612aa8565b6107ea6012826003612778565b6000546001600160a01b031633146118295760405162461bcd60e51b81526004016107d490612aa8565b6107ea6017826003612778565b61183e61275a565b604080516060810191829052600c805482529091600390600d60208501808311610f87575050505050905090565b6000546001600160a01b031633146118965760405162461bcd60e51b81526004016107d490612aa8565b6003546040516331a9108f60e11b8152600481018390526001600160a01b039091169030908290636352211e9060240160206040518083038186803b1580156118de57600080fd5b505afa1580156118f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119169190612bef565b6001600160a01b0316146119a05760405162461bcd60e51b8152602060048201526044602482018190527f4461726b466f726573743a207265636f766572556e69636f726e202d2d205468908201527f617420756e69636f726e206973206e6f7420696e20746865204461726b20466f6064820152631c995cdd60e21b608482015260a4016107d4565b6000828152600860205260409020546001600160a01b03166119c2818461233d565b600083815260086020908152604080832080546001600160a01b031916905560099091528120556003546001600160a01b03166323b872dd30336040516001600160e01b031960e085901b1681526001600160a01b0392831660048201529116602482015260448101869052606401600060405180830381600087803b158015611a4b57600080fd5b505af1158015611a5f573d6000803e3d6000fd5b50505050505050565b6000546001600160a01b03163314611a925760405162461bcd60e51b81526004016107d490612aa8565b601655565b600c5460009015801590611aac5750600f5415155b8015611ab9575060125415155b905090565b6001600160a01b0382166000908152600a60205260408120548210611b4b5760405162461bcd60e51b815260206004820152603e60248201527f4461726b466f726573743a20746f6b656e4f665374616b65724279496e64657860448201527f202d2d207374616b657220696e646578206f7574206f6620626f756e6473000060648201526084016107d4565b6001600160a01b0383166000908152600a60205260409020805483908110611b7557611b75612a19565b906000526020600020015490505b92915050565b6000546001600160a01b03163314611bb35760405162461bcd60e51b81526004016107d490612aa8565b600655565b6000546001600160a01b03163314611be25760405162461bcd60e51b81526004016107d490612aa8565b6001600160a01b03166000908152601b6020526040812055565b600280541415611c1e5760405162461bcd60e51b81526004016107d490612b5c565b60028055611c2a611a97565b611c925760405162461bcd60e51b815260206004820152603360248201527f4461726b466f726573743a206f66666572536163726966696365202d2d2054686044820152726520616c74617220697320696e207275696e7360681b60648201526084016107d4565b336000908152601b602052604090205415611d265760405162461bcd60e51b815260206004820152604860248201527f4461726b466f726573743a206f66666572536163726966696365202d2d20536560448201527f6e64657220616c72656164792068617320612073616372696669636520696e2060648201526770726f677265737360c01b608482015260a4016107d4565b6000611d3061060a565b91505080611db85760405162461bcd60e51b815260206004820152604960248201527f4461726b466f726573743a206f66666572536163726966696365202d2d20546860448201527f6520616c746172207365656d73206c6966656c65737320616e6420776974686f606482015268757420656e6572677960b81b608482015260a4016107d4565b60405182815233907f3039284378a58fc5ba12de5469844bc08755e40c60292ff5c629f03436bc734e9060200160405180910390a26004546001600160a01b0316600c60000154831015611e825760405162461bcd60e51b815260206004820152604560248201527f4461726b466f726573743a206f66666572536163726966696365202d2d20546860448201527f6520616c7461722072656a6563747320796f75722070616c7472792073616372606482015264696669636560d81b608482015260a4016107d4565b600e546000908410611e9657506002611ea3565b600d548410611ea3575060015b6000600c8260038110611eb857611eb8612a19565b01549050806001600160a01b03841663dd62ed3e336040516001600160e01b031960e084901b1681526001600160a01b03909116600482015230602482015260440160206040518083038186803b158015611f1257600080fd5b505afa158015611f26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f4a9190612a2f565b1015611fd15760405162461bcd60e51b815260206004820152604a60248201527f4461726b466f726573743a206f66666572536163726966696365202d2d20496e60448201527f73756666696369656e7420616c6c6f77616e6365206f6e20556e69636f726d2060648201526926b4b635903a37b5b2b760b11b608482015260a4016107d4565b6001600160a01b0383166379cc6790336040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260248101849052604401600060405180830381600087803b15801561202957600080fd5b505af115801561203d573d6000803e3d6000fd5b505050506120496127b6565b600181523360208201526040810183905260165460155460009161206c916125c8565b6000818152601a6020908152604080832086518154938801516001600160a01b0316640100000000026001600160c01b031990941663ffffffff90911617929092178255850151600182015560608501516002909101559091508190601b906120d23390565b6001600160a01b03168152602081019190915260400160002055505060016002555050505050565b6000546001600160a01b031633146121245760405162461bcd60e51b81526004016107d490612aa8565b600080546001600160a01b03166005549091506001600160a01b031660005b600381101561226757816001600160a01b031663dc55d0b2600f836003811061216e5761216e612a19565b01546040516001600160e01b031960e084901b16815260048101919091526001600160a01b0386166024820152604401600060405180830381600087803b1580156121b857600080fd5b505af11580156121cc573d6000803e3d6000fd5b50505050816001600160a01b031663dc55d0b2601283600381106121f2576121f2612a19565b01546040516001600160e01b031960e084901b16815260048101919091526001600160a01b0386166024820152604401600060405180830381600087803b15801561223c57600080fd5b505af1158015612250573d6000803e3d6000fd5b50505050808061225f90612a8d565b915050612143565b505050565b6000546001600160a01b031633146122965760405162461bcd60e51b81526004016107d490612aa8565b6001600160a01b0381166122fb5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016107d4565b61230481612541565b50565b61230f61275a565b6040805160608101918290526017805482529091600390601860208501808311610f87575050505050905090565b6001600160a01b0382166000908152600a602052604081205461236290600190612a5e565b6000838152600b60209081526040808320546001600160a01b0388168452600a90925290912080549293509091849190839081106123a2576123a2612a19565b9060005260206000200154146124585760405162461bcd60e51b815260206004820152606960248201527f4461726b466f726573743a205f72656d6f7665546f6b656e46726f6d5374616b60448201527f6572456e756d65726174696f6e202d2d20746f6b656e20776173206e6f74206660648201527f6f756e6420776865726520657870656374656420696e20546f6b656e735374616084820152686b656420617272617960b81b60a482015260c4016107d4565b8181146124ed576001600160a01b0384166000908152600a6020526040812080548490811061248957612489612a19565b9060005260206000200154905080600a6000876001600160a01b03166001600160a01b0316815260200190815260200160002083815481106124cd576124cd612a19565b6000918252602080832090910192909255918252600b9052604090208190555b6001600160a01b0384166000908152600a6020526040902080548061251457612514612c0c565b600082815260208082208301600019908101839055909201909255938152600b9093525050604081205550565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000828152601a60205260409020805463ffffffff166001146125b357505050565b805463ffffffff191660029081178255015550565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634000aea07f000000000000000000000000000000000000000000000000000000000000000084866000604051602001612638929190918252602082015260400190565b6040516020818303038152906040526040518463ffffffff1660e01b815260040161266593929190612c22565b602060405180830381600087803b15801561267f57600080fd5b505af1158015612693573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126b79190612bd4565b50600083815260016020818152604080842054815180840189905280830186905230606082015260808082018390528351808303909101815260a0909101909252815191830191909120938790529082905261271291612a75565b6000858152600160205260409020556127528482604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b949350505050565b60405180606001604052806003906020820280368337509192915050565b82600381019282156127a6579160200282015b828111156127a657825182559160200191906001019061278b565b506127b29291506127ed565b5090565b6040518060800160405280600063ffffffff16815260200160006001600160a01b0316815260200160008152602001600081525090565b5b808211156127b257600081556001016127ee565b8060005b60038110156115d0578151845260209384019390910190600101612806565b608081016128338285612802565b8260608301529392505050565b6001600160a01b038116811461230457600080fd5b60006020828403121561286757600080fd5b813561287281612840565b9392505050565b60006060828403121561288b57600080fd5b82601f83011261289a57600080fd5b6040516060810181811067ffffffffffffffff821117156128cb57634e487b7160e01b600052604160045260246000fd5b6040528060608401858111156128e057600080fd5b845b818110156128fa5780358352602092830192016128e2565b509195945050505050565b60008060008060006080868803121561291d57600080fd5b853561292881612840565b9450602086013561293881612840565b935060408601359250606086013567ffffffffffffffff8082111561295c57600080fd5b818801915088601f83011261297057600080fd5b81358181111561297f57600080fd5b89602082850101111561299157600080fd5b9699959850939650602001949392505050565b60608101611b838284612802565b6000602082840312156129c457600080fd5b5035919050565b600080604083850312156129de57600080fd5b50508035926020909101359150565b60008060408385031215612a0057600080fd5b8235612a0b81612840565b946020939093013593505050565b634e487b7160e01b600052603260045260246000fd5b600060208284031215612a4157600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b600082821015612a7057612a70612a48565b500390565b60008219821115612a8857612a88612a48565b500190565b6000600019821415612aa157612aa1612a48565b5060010190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b80518015158114612aed57600080fd5b919050565b600080600080600080600060e0888a031215612b0d57600080fd5b612b1688612add565b9650612b2460208901612add565b9550612b3260408901612add565b9450606088015193506080880151925060a0880151915060c0880151905092959891949750929550565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b600082612bb057634e487b7160e01b600052601260045260246000fd5b500690565b6000816000190483118215151615612bcf57612bcf612a48565b500290565b600060208284031215612be657600080fd5b61287282612add565b600060208284031215612c0157600080fd5b815161287281612840565b634e487b7160e01b600052603160045260246000fd5b60018060a01b038416815260006020848184015260606040840152835180606085015260005b81811015612c6457858101830151858201608001528201612c48565b81811115612c76576000608083870101525b50601f01601f1916929092016080019594505050505056fea264697066735822122051f09a53f9efea47fd8b2028b2a444f73351435227d408ce491a01b35299df8564736f6c63430008090033000000000000000000000000dc0479cc5bba033b3e7de9f178607150b3abce1f00000000000000000000000064060ab139feaae7f06ca4e63189d86adeb5169100000000000000000000000099a558bdbde247c2b2716f0d4cfb0e246dfb697d00000000000000000000000000000000000000000000001b1ae4d6e2ef50000000000000000000000000000000000000000000000000000000000000000151800000000000000000000000003d2341adb2d31f1c5530cdc622016af293177ae0000000000000000000000000b0897686c545045afc77cf20ec7a532e3120e0f100000000000000000000000000000000000000000000000000005af3107a4000f86195cf7690c55907b2b611ebb7343a6f649bff128701cc542f0569e2c549da

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102695760003560e01c8063a6df663511610151578063d8dfe755116100c3578063ea38fab011610087578063ea38fab01461058d578063ed58f7f0146105b6578063f2fde38b146105be578063f53504ee146105d1578063f575877a146105d9578063f8d0e5861461060257600080fd5b8063d8dfe75514610539578063dbc0850e1461054c578063dca95cfe1461055f578063e5482d6814610572578063e83998121461057a57600080fd5b8063b405be3211610115578063b405be32146104cb578063c0289df5146104d3578063cb6453b9146104e6578063cb83c595146104f9578063cf1a26ed14610519578063d3434ba71461053157600080fd5b8063a6df663514610472578063aadd977414610483578063ab51260014610494578063b04f9344146104a7578063b2b15d5f146104ba57600080fd5b806364e8456b116101ea5780637c8a5fd5116101ae5780637c8a5fd5146103f95780637c9eb1821461040c5780638bc068e9146104145780638da5cb5b1461042757806394985ddd1461044c578063a1db97821461045f57600080fd5b806364e8456b14610363578063715018a61461037657806375d05f2d1461037e57806379710f07146103915780637bf79bf3146103a457600080fd5b806323ed526d1161023157806323ed526d146103185780632e53f69c1461032b57806354a814051461033357806355ba9eaf1461033b5780635fc9ab481461034e57600080fd5b80630309c9241461026e57806308ee0c741461028d57806312bd4053146102c4578063150b7a02146102d957806316b359fe14610305575b600080fd5b61027661060a565b604051610284929190612825565b60405180910390f35b6102b661029b366004612855565b6001600160a01b03166000908152600a602052604090205490565b604051908152602001610284565b6102d76102d2366004612879565b6107aa565b005b6102ec6102e7366004612905565b6107ee565b6040516001600160e01b03199091168152602001610284565b6102d7610313366004612855565b610b35565b6102d7610326366004612879565b610b81565b6007546102b6565b6102d7610bb8565b6102d7610349366004612855565b610f1d565b610356610f69565b60405161028491906129a4565b6102b66103713660046129b2565b610fa4565b6102d7611441565b6102d761038c3660046129b2565b611477565b6102d761039f366004612855565b6114a6565b6103b76103b23660046129b2565b6115d6565b6040516102849190815163ffffffff1681526020808301516001600160a01b031690820152604080830151908201526060918201519181019190915260800190565b6102d7610407366004612855565b611636565b610356611682565b6102d76104223660046129b2565b6116b8565b6000546001600160a01b03165b6040516001600160a01b039091168152602001610284565b6102d761045a3660046129cb565b6116e7565b6102d761046d3660046129ed565b611769565b6005546001600160a01b0316610434565b6004546001600160a01b0316610434565b6102d76104a2366004612879565b6117c8565b6102d76104b5366004612879565b6117ff565b6003546001600160a01b0316610434565b610356611836565b6102d76104e13660046129b2565b61186c565b6102d76104f43660046129b2565b611a68565b6102b66105073660046129b2565b60009081526009602052604090205490565b610521611a97565b6040519015158152602001610284565b6016546102b6565b6102b66105473660046129ed565b611abe565b6102d761055a3660046129b2565b611b89565b6102d761056d366004612855565b611bb8565b6006546102b6565b6102d76105883660046129b2565b611bfc565b61043461059b3660046129b2565b6000908152600860205260409020546001600160a01b031690565b6102d76120fa565b6102d76105cc366004612855565b61226c565b610356612307565b6102b66105e7366004612855565b6001600160a01b03166000908152601b602052604090205490565b6015546102b6565b61061261275a565b6005546000906001600160a01b031661062961275a565b6000805b600381101561079f576000846001600160a01b031663a44cfc82600f846003811061065a5761065a612a19565b01546040518263ffffffff1660e01b815260040161067a91815260200190565b60206040518083038186803b15801561069257600080fd5b505afa1580156106a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106ca9190612a2f565b856001600160a01b0316635dc8bdf8600f85600381106106ec576106ec612a19565b01546040518263ffffffff1660e01b815260040161070c91815260200190565b60206040518083038186803b15801561072457600080fd5b505afa158015610738573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061075c9190612a2f565b6107669190612a5e565b90506107728184612a75565b92508084836003811061078757610787612a19565b6020020152508061079781612a8d565b91505061062d565b509094909350915050565b6000546001600160a01b031633146107dd5760405162461bcd60e51b81526004016107d490612aa8565b60405180910390fd5b6107ea600c826003612778565b5050565b6003546000906001600160a01b0316336001600160a01b0316146108ae5760405162461bcd60e51b8152602060048201526064602482018190527f4461726b466f726573743a206f6e4552433732315265636569766564202d2d2060448301527f4f6e6c792043727970746f20556e69636f726e73206d617920656e7465722074908201527f6865204461726b20466f7265737420286e696365207472792c206d7920667269608482015263656e642960e01b60a482015260c4016107d4565b60035460405163a28eaae960e01b8152600481018690526001600160a01b03909116906000908190839063a28eaae99060240160e06040518083038186803b1580156108f957600080fd5b505afa15801561090d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109319190612af2565b50505093505092505081156109ee5760405162461bcd60e51b815260206004820152607160248201527f4461726b466f726573743a206f6e4552433732315265636569766564202d2d2060448201527f43616e6e6f7420656e74657220746865204461726b20466f726573742077686960648201527f6c65207468652043727970746f20556e69636f726e732067616d6520686173206084820152703637b1b5b2b2103cb7bab9103a37b5b2b760791b60a482015260c4016107d4565b80600214610a7a5760405162461bcd60e51b815260206004820152604d60248201527f4461726b466f726573743a206f6e4552433732315265636569766564202d2d2060448201527f4f6e6c79206164756c7420756e69636f726e732063616e20656e74657220746860648201526c194819185c9ac8199bdc995cdd609a1b608482015260a4016107d4565b600087815260086020526040902080546001600160a01b0319166001600160a01b038a16179055600754610aae9042612a75565b6000888152600960209081526040808320939093556001600160a01b038b16808352600a8252838320805460018101825590845282842081018c90558b8452600b90925283832091909155915189917fb27144af50191928f7aeb7b1976c2511dfc68ae4707f3aaea6ffa0bab1a29f9091a350630a85bd0160e11b98975050505050505050565b6000546001600160a01b03163314610b5f5760405162461bcd60e51b81526004016107d490612aa8565b600580546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001600160a01b03163314610bab5760405162461bcd60e51b81526004016107d490612aa8565b6107ea600f826003612778565b600280541415610bda5760405162461bcd60e51b81526004016107d490612b5c565b60028055336000908152601b602052604090205480610c6f5760405162461bcd60e51b8152602060048201526044602482018190527f4461726b466f726573743a20636f6d706c657465536163726966696365202d2d908201527f2053656e64657220686173206e6f2073616372696669636520696e2070726f676064820152637265737360e01b608482015260a4016107d4565b6000818152601a602052604090206005546001600160a01b0316610c9161275a565b6000610c9b61060a565b909250905060008115610e9757845463ffffffff16600214610d365760405162461bcd60e51b815260206004820152604860248201527f4461726b466f726573743a20636f6d706c657465536163726966696365202d2d60448201527f2052697475616c2063616e6e6f7420626520636f6d706c6574656420617420746064820152676869732074696d6560c01b608482015260a4016107d4565b600060648660020154610d499190612b93565b6002870154600188015491925060071c9060129060038110610d6d57610d6d612a19565b01549250600087600101546000148015610d875750600a83105b80610da1575087600101546001148015610da15750603283105b80610db0575087600101546002145b90508015610e07576000610dc48684612b93565b8751909150811015610dde57600f60005b01549450610e05565b60208701518751610def9190612a75565b811015610dff57600f6001610dd5565b60115494505b505b8315610e93576001600160a01b03871663731133e9336040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526024810187905260016044820152608060648201526000608482015260a401600060405180830381600087803b158015610e7a57600080fd5b505af1158015610e8e573d6000803e3d6000fd5b505050505b5050505b845463ffffffff19166003178555601b6000610eb03390565b6001600160a01b03168152602081019190915260400160009081205580336001600160a01b03167fe739d115cfe9fc3f29ca4534bd55d355944cd17dc184db3671b9adf5cfcee77b88604051610f0891815260200190565b60405180910390a35050600160025550505050565b6000546001600160a01b03163314610f475760405162461bcd60e51b81526004016107d490612aa8565b600380546001600160a01b0319166001600160a01b0392909216919091179055565b610f7161275a565b60408051606081019182905290600f9060039082845b815481526020019060010190808311610f87575050505050905090565b6000600280541415610fc85760405162461bcd60e51b81526004016107d490612b5c565b600280556000828152600860205260409020546001600160a01b0316336001600160a01b0316146110925760405162461bcd60e51b815260206004820152606260248201527f4461726b466f726573743a2065786974466f72657374202d2d206f6e6c79207460448201527f68652061646472657373207768696368207374616b65642074686520746f6b6560648201527f6e2063616e206d616b65206974206578697420746865206461726b20666f72656084820152611cdd60f21b60a482015260c4016107d4565b6000828152600960205260409020544210156111165760405162461bcd60e51b815260206004820152603a60248201527f4461726b466f726573743a2065786974466f72657374202d2d20756e69636f7260448201527f6e2063616e6e6f742066696e642074686520657869742079657400000000000060648201526084016107d4565b6003546001600160a01b03166342842e0e30336040516001600160e01b031960e085901b1681526001600160a01b0392831660048201529116602482015260448101859052606401600060405180830381600087803b15801561117857600080fd5b505af115801561118c573d6000803e3d6000fd5b5050600480546040516370a0823160e01b815230928101929092526001600160a01b031692506000915082906370a082319060240160206040518083038186803b1580156111d957600080fd5b505afa1580156111ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112119190612a2f565b600654600554919250906001600160a01b031660005b600381101561130d576001600160a01b03821662fdd58e33600f846003811061125257611252612a19565b01546040516001600160e01b031960e085901b1681526001600160a01b039092166004830152602482015260440160206040518083038186803b15801561129857600080fd5b505afa1580156112ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112d09190612a2f565b601782600381106112e3576112e3612a19565b01546112ef9190612bb5565b6112f99084612a75565b92508061130581612a8d565b915050611227565b508183101561131c5760065491505b8183106113b9576001600160a01b03841663a9059cbb336040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260248101859052604401602060405180830381600087803b15801561137b57600080fd5b505af115801561138f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113b39190612bd4565b506113be565b600091505b6113c8338761233d565b600086815260086020908152604080832080546001600160a01b03191690556009909152812055336001600160a01b0316867ffadbedc5dfdd53b1b6e36f0e11192437cb1da3ce1d213473bccda055366401208460405161142b91815260200190565b60405180910390a3506001600255949350505050565b6000546001600160a01b0316331461146b5760405162461bcd60e51b81526004016107d490612aa8565b6114756000612541565b565b6000546001600160a01b031633146114a15760405162461bcd60e51b81526004016107d490612aa8565b600755565b6000546001600160a01b031633146114d05760405162461bcd60e51b81526004016107d490612aa8565b6040516370a0823160e01b815230600482015281906000906001600160a01b038316906370a082319060240160206040518083038186803b15801561151457600080fd5b505afa158015611528573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061154c9190612a2f565b60405163a9059cbb60e01b8152336004820152602481018290529091506001600160a01b0383169063a9059cbb906044015b602060405180830381600087803b15801561159857600080fd5b505af11580156115ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115d09190612bd4565b50505050565b6115de6127b6565b506000908152601a60209081526040918290208251608081018452815463ffffffff8116825264010000000090046001600160a01b031692810192909252600181015492820192909252600290910154606082015290565b6000546001600160a01b031633146116605760405162461bcd60e51b81526004016107d490612aa8565b600480546001600160a01b0319166001600160a01b0392909216919091179055565b61168a61275a565b6040805160608101918290526012805482529091600390601360208501808311610f87575050505050905090565b6000546001600160a01b031633146116e25760405162461bcd60e51b81526004016107d490612aa8565b601555565b336001600160a01b037f0000000000000000000000003d2341adb2d31f1c5530cdc622016af293177ae0161461175f5760405162461bcd60e51b815260206004820152601f60248201527f4f6e6c7920565246436f6f7264696e61746f722063616e2066756c66696c6c0060448201526064016107d4565b6107ea8282612591565b6000546001600160a01b031633146117935760405162461bcd60e51b81526004016107d490612aa8565b60405163a9059cbb60e01b81523360048201526024810182905282906001600160a01b0382169063a9059cbb9060440161157e565b6000546001600160a01b031633146117f25760405162461bcd60e51b81526004016107d490612aa8565b6107ea6012826003612778565b6000546001600160a01b031633146118295760405162461bcd60e51b81526004016107d490612aa8565b6107ea6017826003612778565b61183e61275a565b604080516060810191829052600c805482529091600390600d60208501808311610f87575050505050905090565b6000546001600160a01b031633146118965760405162461bcd60e51b81526004016107d490612aa8565b6003546040516331a9108f60e11b8152600481018390526001600160a01b039091169030908290636352211e9060240160206040518083038186803b1580156118de57600080fd5b505afa1580156118f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119169190612bef565b6001600160a01b0316146119a05760405162461bcd60e51b8152602060048201526044602482018190527f4461726b466f726573743a207265636f766572556e69636f726e202d2d205468908201527f617420756e69636f726e206973206e6f7420696e20746865204461726b20466f6064820152631c995cdd60e21b608482015260a4016107d4565b6000828152600860205260409020546001600160a01b03166119c2818461233d565b600083815260086020908152604080832080546001600160a01b031916905560099091528120556003546001600160a01b03166323b872dd30336040516001600160e01b031960e085901b1681526001600160a01b0392831660048201529116602482015260448101869052606401600060405180830381600087803b158015611a4b57600080fd5b505af1158015611a5f573d6000803e3d6000fd5b50505050505050565b6000546001600160a01b03163314611a925760405162461bcd60e51b81526004016107d490612aa8565b601655565b600c5460009015801590611aac5750600f5415155b8015611ab9575060125415155b905090565b6001600160a01b0382166000908152600a60205260408120548210611b4b5760405162461bcd60e51b815260206004820152603e60248201527f4461726b466f726573743a20746f6b656e4f665374616b65724279496e64657860448201527f202d2d207374616b657220696e646578206f7574206f6620626f756e6473000060648201526084016107d4565b6001600160a01b0383166000908152600a60205260409020805483908110611b7557611b75612a19565b906000526020600020015490505b92915050565b6000546001600160a01b03163314611bb35760405162461bcd60e51b81526004016107d490612aa8565b600655565b6000546001600160a01b03163314611be25760405162461bcd60e51b81526004016107d490612aa8565b6001600160a01b03166000908152601b6020526040812055565b600280541415611c1e5760405162461bcd60e51b81526004016107d490612b5c565b60028055611c2a611a97565b611c925760405162461bcd60e51b815260206004820152603360248201527f4461726b466f726573743a206f66666572536163726966696365202d2d2054686044820152726520616c74617220697320696e207275696e7360681b60648201526084016107d4565b336000908152601b602052604090205415611d265760405162461bcd60e51b815260206004820152604860248201527f4461726b466f726573743a206f66666572536163726966696365202d2d20536560448201527f6e64657220616c72656164792068617320612073616372696669636520696e2060648201526770726f677265737360c01b608482015260a4016107d4565b6000611d3061060a565b91505080611db85760405162461bcd60e51b815260206004820152604960248201527f4461726b466f726573743a206f66666572536163726966696365202d2d20546860448201527f6520616c746172207365656d73206c6966656c65737320616e6420776974686f606482015268757420656e6572677960b81b608482015260a4016107d4565b60405182815233907f3039284378a58fc5ba12de5469844bc08755e40c60292ff5c629f03436bc734e9060200160405180910390a26004546001600160a01b0316600c60000154831015611e825760405162461bcd60e51b815260206004820152604560248201527f4461726b466f726573743a206f66666572536163726966696365202d2d20546860448201527f6520616c7461722072656a6563747320796f75722070616c7472792073616372606482015264696669636560d81b608482015260a4016107d4565b600e546000908410611e9657506002611ea3565b600d548410611ea3575060015b6000600c8260038110611eb857611eb8612a19565b01549050806001600160a01b03841663dd62ed3e336040516001600160e01b031960e084901b1681526001600160a01b03909116600482015230602482015260440160206040518083038186803b158015611f1257600080fd5b505afa158015611f26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f4a9190612a2f565b1015611fd15760405162461bcd60e51b815260206004820152604a60248201527f4461726b466f726573743a206f66666572536163726966696365202d2d20496e60448201527f73756666696369656e7420616c6c6f77616e6365206f6e20556e69636f726d2060648201526926b4b635903a37b5b2b760b11b608482015260a4016107d4565b6001600160a01b0383166379cc6790336040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260248101849052604401600060405180830381600087803b15801561202957600080fd5b505af115801561203d573d6000803e3d6000fd5b505050506120496127b6565b600181523360208201526040810183905260165460155460009161206c916125c8565b6000818152601a6020908152604080832086518154938801516001600160a01b0316640100000000026001600160c01b031990941663ffffffff90911617929092178255850151600182015560608501516002909101559091508190601b906120d23390565b6001600160a01b03168152602081019190915260400160002055505060016002555050505050565b6000546001600160a01b031633146121245760405162461bcd60e51b81526004016107d490612aa8565b600080546001600160a01b03166005549091506001600160a01b031660005b600381101561226757816001600160a01b031663dc55d0b2600f836003811061216e5761216e612a19565b01546040516001600160e01b031960e084901b16815260048101919091526001600160a01b0386166024820152604401600060405180830381600087803b1580156121b857600080fd5b505af11580156121cc573d6000803e3d6000fd5b50505050816001600160a01b031663dc55d0b2601283600381106121f2576121f2612a19565b01546040516001600160e01b031960e084901b16815260048101919091526001600160a01b0386166024820152604401600060405180830381600087803b15801561223c57600080fd5b505af1158015612250573d6000803e3d6000fd5b50505050808061225f90612a8d565b915050612143565b505050565b6000546001600160a01b031633146122965760405162461bcd60e51b81526004016107d490612aa8565b6001600160a01b0381166122fb5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016107d4565b61230481612541565b50565b61230f61275a565b6040805160608101918290526017805482529091600390601860208501808311610f87575050505050905090565b6001600160a01b0382166000908152600a602052604081205461236290600190612a5e565b6000838152600b60209081526040808320546001600160a01b0388168452600a90925290912080549293509091849190839081106123a2576123a2612a19565b9060005260206000200154146124585760405162461bcd60e51b815260206004820152606960248201527f4461726b466f726573743a205f72656d6f7665546f6b656e46726f6d5374616b60448201527f6572456e756d65726174696f6e202d2d20746f6b656e20776173206e6f74206660648201527f6f756e6420776865726520657870656374656420696e20546f6b656e735374616084820152686b656420617272617960b81b60a482015260c4016107d4565b8181146124ed576001600160a01b0384166000908152600a6020526040812080548490811061248957612489612a19565b9060005260206000200154905080600a6000876001600160a01b03166001600160a01b0316815260200190815260200160002083815481106124cd576124cd612a19565b6000918252602080832090910192909255918252600b9052604090208190555b6001600160a01b0384166000908152600a6020526040902080548061251457612514612c0c565b600082815260208082208301600019908101839055909201909255938152600b9093525050604081205550565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000828152601a60205260409020805463ffffffff166001146125b357505050565b805463ffffffff191660029081178255015550565b60007f000000000000000000000000b0897686c545045afc77cf20ec7a532e3120e0f16001600160a01b0316634000aea07f0000000000000000000000003d2341adb2d31f1c5530cdc622016af293177ae084866000604051602001612638929190918252602082015260400190565b6040516020818303038152906040526040518463ffffffff1660e01b815260040161266593929190612c22565b602060405180830381600087803b15801561267f57600080fd5b505af1158015612693573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126b79190612bd4565b50600083815260016020818152604080842054815180840189905280830186905230606082015260808082018390528351808303909101815260a0909101909252815191830191909120938790529082905261271291612a75565b6000858152600160205260409020556127528482604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b949350505050565b60405180606001604052806003906020820280368337509192915050565b82600381019282156127a6579160200282015b828111156127a657825182559160200191906001019061278b565b506127b29291506127ed565b5090565b6040518060800160405280600063ffffffff16815260200160006001600160a01b0316815260200160008152602001600081525090565b5b808211156127b257600081556001016127ee565b8060005b60038110156115d0578151845260209384019390910190600101612806565b608081016128338285612802565b8260608301529392505050565b6001600160a01b038116811461230457600080fd5b60006020828403121561286757600080fd5b813561287281612840565b9392505050565b60006060828403121561288b57600080fd5b82601f83011261289a57600080fd5b6040516060810181811067ffffffffffffffff821117156128cb57634e487b7160e01b600052604160045260246000fd5b6040528060608401858111156128e057600080fd5b845b818110156128fa5780358352602092830192016128e2565b509195945050505050565b60008060008060006080868803121561291d57600080fd5b853561292881612840565b9450602086013561293881612840565b935060408601359250606086013567ffffffffffffffff8082111561295c57600080fd5b818801915088601f83011261297057600080fd5b81358181111561297f57600080fd5b89602082850101111561299157600080fd5b9699959850939650602001949392505050565b60608101611b838284612802565b6000602082840312156129c457600080fd5b5035919050565b600080604083850312156129de57600080fd5b50508035926020909101359150565b60008060408385031215612a0057600080fd5b8235612a0b81612840565b946020939093013593505050565b634e487b7160e01b600052603260045260246000fd5b600060208284031215612a4157600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b600082821015612a7057612a70612a48565b500390565b60008219821115612a8857612a88612a48565b500190565b6000600019821415612aa157612aa1612a48565b5060010190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b80518015158114612aed57600080fd5b919050565b600080600080600080600060e0888a031215612b0d57600080fd5b612b1688612add565b9650612b2460208901612add565b9550612b3260408901612add565b9450606088015193506080880151925060a0880151915060c0880151905092959891949750929550565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b600082612bb057634e487b7160e01b600052601260045260246000fd5b500690565b6000816000190483118215151615612bcf57612bcf612a48565b500290565b600060208284031215612be657600080fd5b61287282612add565b600060208284031215612c0157600080fd5b815161287281612840565b634e487b7160e01b600052603160045260246000fd5b60018060a01b038416815260006020848184015260606040840152835180606085015260005b81811015612c6457858101830151858201608001528201612c48565b81811115612c76576000608083870101525b50601f01601f1916929092016080019594505050505056fea264697066735822122051f09a53f9efea47fd8b2028b2a444f73351435227d408ce491a01b35299df8564736f6c63430008090033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000dc0479cc5bba033b3e7de9f178607150b3abce1f00000000000000000000000064060ab139feaae7f06ca4e63189d86adeb5169100000000000000000000000099a558bdbde247c2b2716f0d4cfb0e246dfb697d00000000000000000000000000000000000000000000001b1ae4d6e2ef50000000000000000000000000000000000000000000000000000000000000000151800000000000000000000000003d2341adb2d31f1c5530cdc622016af293177ae0000000000000000000000000b0897686c545045afc77cf20ec7a532e3120e0f100000000000000000000000000000000000000000000000000005af3107a4000f86195cf7690c55907b2b611ebb7343a6f649bff128701cc542f0569e2c549da

-----Decoded View---------------
Arg [0] : _CryptoUnicornsAddress (address): 0xdC0479CC5BbA033B3e7De9F178607150B3AbCe1f
Arg [1] : _UnicornMilkTokenAddress (address): 0x64060aB139Feaae7f06Ca4E63189D86aDEb51691
Arg [2] : _TerminusAddress (address): 0x99A558BDBdE247C2B2716f0D4cFb0E246DFB697D
Arg [3] : _UnicornMilkStakingReward (uint256): 500000000000000000000
Arg [4] : _StakePeriodSeconds (uint256): 86400
Arg [5] : _VRFCoordinatorAddress (address): 0x3d2341ADb2D31f1c5530cDC622016af293177AE0
Arg [6] : _LinkTokenAddress (address): 0xb0897686c545045aFc77CF20eC7A532E3120E0F1
Arg [7] : _ChainlinkVRFFee (uint256): 100000000000000
Arg [8] : _ChainlinkVRFKeyhash (bytes32): 0xf86195cf7690c55907b2b611ebb7343a6f649bff128701cc542f0569e2c549da

-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 000000000000000000000000dc0479cc5bba033b3e7de9f178607150b3abce1f
Arg [1] : 00000000000000000000000064060ab139feaae7f06ca4e63189d86adeb51691
Arg [2] : 00000000000000000000000099a558bdbde247c2b2716f0d4cfb0e246dfb697d
Arg [3] : 00000000000000000000000000000000000000000000001b1ae4d6e2ef500000
Arg [4] : 0000000000000000000000000000000000000000000000000000000000015180
Arg [5] : 0000000000000000000000003d2341adb2d31f1c5530cdc622016af293177ae0
Arg [6] : 000000000000000000000000b0897686c545045afc77cf20ec7a532e3120e0f1
Arg [7] : 00000000000000000000000000000000000000000000000000005af3107a4000
Arg [8] : f86195cf7690c55907b2b611ebb7343a6f649bff128701cc542f0569e2c549da


Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

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

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