// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@( (@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   @  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@(  @@%@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   @@%%%%@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@(  @@&%%%%%%%&@( (@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   @@%%%%%   %%%%@@@  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@( (@@@@@@@@@@@@@@@@@(  (@%%%* *%%%%%* *%%@@(. @@@@@@@@@@@@@@@@@@( (@@@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@      @@@@@@@@@@@@@   @@%%%  %%%     %%%  %%@@   @@@@@@@@@@@@@@      @@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@(          (@@@@@@@@(  (&%%%%%* *%%%***%%%* *%%%&@@( (@@@@@@@@@(          (@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   @@%%%%%%%%%%         %%%%%%%%%@@   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@                @@   (@&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&@@@ .(@@@                @@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@,           ,  ,@                                ,,           @                @@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@    ..          @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@..              @@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@  *   *              *   *                  *    *   *              *   *    *             *   @@@@@@@@@@@@
// @@@@@@@@@@@@@@@%                                                                                         @@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@%  @@@@@@@@ @@@@@@@@@ @@@@@@@@ @@@@@@@@@%@@@@@@@@ @@@@@@@@ %@@@@@@@@ @@@@@@@@ %@@@@@@@@%  @@@@@@@@@@@@@@@
// @@@@@@@@@@@@@     @@(((((@ @@(((((@@ @@((((@@ @@(((((@@%@@((((@@ @@(((((@ %@#((((@@ @@((((@@ %@(((((@@%     @@@@@@@@@@@@
// @@@@@@@@@@@@@   . @@(@@@(@ @@(@@@(@@ @@@@@@@@ @@(@@@(@@%@@@@@(@@ @@(@@@(@ %@(@@@(@@ @@@@@@@@ %@(@@@(@@%/   @@@@@@@@@@@@@
// @@@@@@@@@@@@@*  ( @  */  @ @@ */.  @ @ **.  @ @@ */  @@%@ **.  @ @  */  @ %@ */.  @ @ **.  @ %@ */  @@%.   @@@@@@@@@@@@@
// @@@@@@@@@@@@@     @  @&    @   &&  @    @&    @  @&   @%   @&  @ @  @&    %   &&  @    @&    %  @&   @%    @@@@@@@@@@@@@
// @@@@@@@@@@@@@ **. @@((((#@*@@((((#@@*@@((((@@*@@((((#@@%@@(((#@@*@@((((#@*%@((((#@@*@@((((@@*%@((((#@@%#  *@@@@@@@@@@@@@
// @@@@@@@@@@@@@     @@@@@@@@ @@@@@@@@@ @@@@@@@@ @@@@@@@@@%@@@@@@@@ @@@@@@@@ %@@@@@@@@ @@@@@@@@ %@@@@@@@@%    @@@@@@@@@@@@@
// @@@@@@@@@@@@@ . . &&&&&&&&*&&&&&&&&&*********/************///****//***((/*******/**%&&&&&&&&*%&&&&&&&&%*  *@@@@@@@@@@@@@
// @@@@@@@@@@@@@   / @@@@@@@@ @@@@@@@@@   %%%%%%%%%.   ,   %           ,   %%%%%%%%   %@@@@@@@@ %@@@@@@@@%,   @@@@@@@@@@@@@
// @@@@@@@@@@@@@     @@ (@(.@ @@ #@@.@@ . %%%%%%.   .     .((((((((((       . %%%%%   %@@(@@(@@ %@ @@(.@@%.   @@@@@@@@@@@@@
// @@@@@@@@@@@@@     @   (  @ @@ *(   @   %%%%       .@@@@@@@@@@@@@@@@@@      ,.%%%   %@ *    @ %@  (  @@%%  %@@@@@@@@@@@@@
// @@@@@@@@@@@@@*  * @  ((   .@   (/  @.  %#      (@@@@@@@@@@@@@@@@@@@@@@@((    ,.%   %   (/   .%  ((   @%    @@@@@@@@@@@@@
// @@@@@@@@@@@@@   , @@ **  @*@@  ** @@*    ,    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@   .    %@@ ** @@*%@ **  @@%    @@@@@@@@@@@@@
// @@@@@@@@@@@@@ . , @@@@@@@@*@@@@@@@@@*     / (@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ .      %@@@@@@@@*%@@@@@@@@%* ..@@@@@@@@@@@@@
// @@@@@@@@@@@@@     %%%%%%%%%%%%%%%%%%%    % .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@       %%%%%%%%%%%%%%%%%%%%     @@@@@@@@@@@@
// @@@@@@@@@@@@@**** @@@@@@@@ @@@@@@@@@ ****%  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  *****%@@@@@@@@ %@@@@@@@@%*****@@@@@@@@@@@@
// @@@@@@@@@@@@@   / @@*   (@ @@     @@ ,   %  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   %   %@@    @@ %@*   (@@%%  %@@@@@@@@@@@@@
// @@@@@@@@@@@@@   . @(/(%(/@ @@/(%(/#@     %  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@       %@(((((#@ %@/(%(/@@%.   @@@@@@@@@@@@@
// @@@@@@@@@@@@@%  % @   (  @ @@ *(   @ %   %  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@       %@ *    @ %@  (  @@%,   @@@@@@@@@@@@@
// @@@@@@@@@@@@@     @  @&  (.@(  &&  @.    %  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   *   %(  @&  (.%( @&  (@%*  *@@@@@@@@@@@@@
// @@@@@@@@@@@@@ %%  @@@@@@@@*@@@@@@@@@*  % %  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@       %@@@@@@@@*%@@@@@@@@%    @@@@@@@@@@@@@
// @@@@@@@@@@@@@   , &&&&&&&&*&&&&&&&&&*    %  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   **  %&&@@@&&&*%&&&&&&&&%(  *@@@@@@@@@@@@@
// @@@@@@@@@@@@@   , @@@@@@@@ @@@@@@@@@     %  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@       %@@@@@@@@ %@@@@@@@@%    @@@@@@@@@@@@@
// @@@@@@@@@@@@@*  * @@@@@@@@ @@@@@@@@@ ** (%  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@       %@@((((@@ %@@(((@@@%.   @@@@@@@@@@@@@
// @@@@@@@@@@@@@   , @@ @@@*@ @@ @@@*@@     %  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   %   %@@@@@@@@ %@ @@@*@@%%  %@@@@@@@@@@@@@
// @@@@@@@@@@@@@ **. @  **  @ @@ **,  @   * %  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@       %@ **,  @ %@ **  @@%    @@@@@@@@@@@@@
// @@@@@@@@@@@@@   . @  @@    @   @&  @     %  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@    %  %   @&    %  @@   @%,   @@@@@@@@@@@@@
// @@@@@@@@@@@@@   . @@(##((@*@@((##(@@*    %  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  *   *%@@(##(@@*%@(##((@@%    @@@@@@@@@@@@@
// @@@@@@@@@@@@@     @@@@@@@@*@@@@@@@@@* % %%  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@       %@@@@@@@@*%@@@@@@@@% ,  @@@@@@@@@@@@@
// @@@@@@@@@@@@@,..( %%%%%%%%%%%%%%%%%%%    %  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@        %%%%%%%%%%%%%%%%%%%,  .@@@@@@@@@@@@@
// @@@@@@@@@@@@@...( ..................   . %  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@              ....  , .....(...@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// Forgotten Runes Gate to The Seventh Realm
// https://forgottenrunes.com
// Twitter: @forgottenrunes
pragma solidity ^0.8.0;

import '../util/Ownablearama.sol';
import '../interfaces/IBookOfLore.sol';
import './interfaces/IBeastsAuctionHouse.sol';
import '@openzeppelin/contracts/security/ReentrancyGuard.sol';
import '@openzeppelin/contracts/token/ERC721/IERC721.sol';
import '@openzeppelin/contracts/utils/cryptography/ECDSA.sol';
import '@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol';
import '@openzeppelin/contracts/utils/cryptography/MerkleProof.sol';

interface IMintable {
    function mint(address recipient, uint256 tokenId) external;
}

contract GateToTheSeventhRealmMinter is Ownablearama, ReentrancyGuard, EIP712 {
    /// @notice The start block for minting
    uint256 public startBlock =
        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;

    /// @notice The Merkle root for keyholders
    bytes32 public keysMerkleRoot;

    /// @notice The Merkle root for spellholders
    bytes32 public spellsMerkleRoot;

    /// @notice The number of locks on this gate
    uint256 public constant NUM_LOCKS = 21;

    /// @notice The number of locks unlocked
    uint256 public numUnlocked = 0;

    /// @notice The address to the Book of Lore
    address public bookOfLoreAddress;

    /// @notice The address of the Wizards contract
    address public wizardsAddress;

    /// @notice The address of the Souls contract
    address public soulsAddress;

    /// @notice The address to the Gate of the Seventh Realm token contract
    address public gateOfTheSeventhRealmAddress;

    /// @notice The address to the Beasts Auction House token contract
    address public auctionHouseAddress;

    /// @notice The EIP-712 typehash for the unlock struct used by the contract
    bytes32 public constant UNLOCK_TYPEHASH =
        keccak256('Unlock(address ownerA,uint256 wizardIdA,address ownerB)');

    /// @notice A mapping of the addresses of those who participated in the unlock ceremony
    mapping(address => bool) public unlockers;

    /// @notice A mapping of wizard (or soul) IDs who participated in the unlock ceremony
    mapping(uint256 => bool) public unlockerWizards;

    /// @notice The Unlock event is emitted when a lock is unlocked
    event Unlocked(
        address wizA,
        uint256 tokenIdA,
        address wizB,
        uint256 tokenIdB,
        uint256 lockIdx
    );

    /**
     * @dev Create the contract and set initial configuration
     * @param _bookOfLoreAddress address to the Book of Lore
     * @param _wizardsAddress address to the Wizards token
     * @param _soulsAddress address to the Souls token
     */
    constructor(
        address _bookOfLoreAddress,
        address _wizardsAddress,
        address _soulsAddress
    ) EIP712('GateToTheSeventhRealmMinter', '1') {
        setBookOfLoreAddress(_bookOfLoreAddress);
        setWizardsAddress(_wizardsAddress);
        setSoulsAddress(_soulsAddress);
    }

    /**
     * @dev Convenient way to initialize the post-constructor configuration
     */
    function initialize(
        uint256 newStartBlock,
        bytes32 newKeysMerkleRoot,
        bytes32 newSpellsMerkleRoot,
        address newGateOfTheSeventhRealmAddress,
        address newAuctionHouseAddress
    ) public onlyOwner {
        setStartBlock(newStartBlock);
        setKeysMerkleRoot(newKeysMerkleRoot);
        setSpellsMerkleRoot(newSpellsMerkleRoot);
        setGateOfTheSeventhRealmAddress(newGateOfTheSeventhRealmAddress);
        setAuctionHouseAddress(newAuctionHouseAddress);
    }

    /**
     * @notice Returns if the minting has started
     * @return bool if the minting has started
     */
    function started() public view returns (bool) {
        return block.number >= startBlock;
    }

    /**
     * @notice Unlock a lock from the gate
     * @dev Unlocking the gate is a collaboration between two wizards: one with a key and one with a spell. ownerA signs a message and gives that signature to ownerB. ownerB must be the msg.sender. The frontend needs to submit proper proofs matching wizard A and B with a key or spell - this means the frontend needs to know a mapping of wizard (or soul) ids to keys and spells.
     * @param ownerA address the partner's address and signer of `signatureA`
     * @param wizIdA uint256 the ID of the partner's wizard
     * @param wizIdB uint256 the ID of the sender's wizard
     * @param wizAMerkleProof bytes32 the proof that wizIdA holds a key or a spell
     * @param wizBMerkleProof bytes32 the proof that wizIdB holds a key or a spell
     * @param signatureA bytes the signature from the partner
     */
    function unlock(
        address ownerA,
        uint256 wizIdA,
        uint256 wizIdB,
        bytes32[] calldata wizAMerkleProof,
        bytes32[] calldata wizBMerkleProof,
        bytes memory signatureA
    ) public nonReentrant {
        require(started(), 'Not started');
        require(numUnlocked < NUM_LOCKS, 'Already unlocked');
        require(_ownsWizardOrSoul(ownerA, wizIdA), 'Invalid Wiz A Owner');
        require(_ownsWizardOrSoul(msg.sender, wizIdB), 'Invalid Wiz B Owner');
        require(!unlockers[ownerA], 'Partner already unlocked');
        require(!unlockers[msg.sender], 'You already unlocked');
        require(!unlockerWizards[wizIdA], 'Wizard A already unlocked');
        require(!unlockerWizards[wizIdB], 'Wizard B already unlocked');

        require(
            (_wizardHoldsKey(wizIdA, wizAMerkleProof) &&
                _wizardHoldsSpell(wizIdB, wizBMerkleProof)) ||
                (_wizardHoldsKey(wizIdB, wizBMerkleProof) &&
                    _wizardHoldsSpell(wizIdA, wizAMerkleProof)),
            'Wizard pairing failed'
        );

        // construct an expected hash, given the parameters
        bytes32 digest = _hashTypedDataV4(
            keccak256(abi.encode(UNLOCK_TYPEHASH, ownerA, wizIdA, msg.sender))
        );

        // now recover the signer from the provided signature
        address signer = ECDSA.recover(digest, signatureA);

        // make sure the recover extracted a signer, but beware, because this
        // can return non-zero for some invalid cases (apparently?)
        require(signer != address(0), 'ECDSA: invalid signature');
        require(signer == ownerA, 'unlock: signature is not the partner owner');
        require(ownerA != msg.sender, 'nice try');

        unlockers[msg.sender] = true;
        unlockers[ownerA] = true;
        unlockerWizards[wizIdA] = true;
        unlockerWizards[wizIdB] = true;

        IMintable(gateOfTheSeventhRealmAddress).mint(
            msg.sender,
            numUnlocked * 2
        );
        IMintable(gateOfTheSeventhRealmAddress).mint(
            ownerA,
            (numUnlocked * 2) + 1
        );

        // write the Lore
        if (bookOfLoreAddress != address(0)) {
            IBookOfLore(bookOfLoreAddress).addLoreWithScribe(
                _loreContractAddress(wizIdB),
                wizIdB,
                0,
                false,
                ''
            );
        }

        emit Unlocked(ownerA, wizIdA, msg.sender, wizIdB, numUnlocked);

        numUnlocked += 1;

        if (numUnlocked == NUM_LOCKS) {
            IBeastsAuctionHouse(auctionHouseAddress).openTheGate();
        }
    }

    function _wizardHoldsSpell(uint256 wizId, bytes32[] calldata _merkleProof)
        private
        view
        returns (bool)
    {
        return
            MerkleProof.verify(
                _merkleProof,
                spellsMerkleRoot,
                keccak256(abi.encodePacked(wizId))
            );
    }

    function _wizardHoldsKey(uint256 wizId, bytes32[] calldata _merkleProof)
        private
        view
        returns (bool)
    {
        return
            MerkleProof.verify(
                _merkleProof,
                keysMerkleRoot,
                keccak256(abi.encodePacked(wizId))
            );
    }

    function _loreContractAddress(uint256 wizId)
        private
        view
        returns (address)
    {
        try IERC721(wizardsAddress).ownerOf(wizId) returns (address) {
            return wizardsAddress;
        } catch Error(string memory) {
            // try a soul
        }

        try IERC721(soulsAddress).ownerOf(wizId) returns (address) {
            return soulsAddress;
        } catch Error(string memory) {
            // not soul either
        }

        return wizardsAddress;
    }

    function _ownsWizardOrSoul(address owner, uint256 wizId)
        private
        view
        returns (bool)
    {
        require(owner != address(0), 'invalid owner');

        try IERC721(wizardsAddress).ownerOf(wizId) returns (
            address wizardOwner
        ) {
            if (wizardOwner == owner) {
                return true;
            }
        } catch Error(string memory) {
            // try a soul
        }

        try IERC721(soulsAddress).ownerOf(wizId) returns (address soulOwner) {
            if (soulOwner == owner) {
                return true;
            }
        } catch Error(string memory) {
            // not soul either
        }

        return false;
    }

    function domainSeparator() external view returns (bytes32) {
        return _domainSeparatorV4();
    }

    /**
     * Owner Controls
     */

    /**
     * @dev Set the start block
     * @param newStartBlock uint256 the block to start at
     */
    function setStartBlock(uint256 newStartBlock) public onlyOwner {
        startBlock = newStartBlock;
    }

    /**
     * @dev Set the merkle root for key-holder IDs
     * @param newMerkleRoot bytes32 the merkle root
     */
    function setKeysMerkleRoot(bytes32 newMerkleRoot) public onlyOwner {
        keysMerkleRoot = newMerkleRoot;
    }

    /**
     * @dev Set the merkle root for spell-holder IDs
     * @param newMerkleRoot bytes32 the merkle root
     */
    function setSpellsMerkleRoot(bytes32 newMerkleRoot) public onlyOwner {
        spellsMerkleRoot = newMerkleRoot;
    }

    /**
     * @dev Set the address of the Gate of the Seventh Realm
     * @param newGateOfTheSeventhRealmAddress address the address to the contract
     */
    function setGateOfTheSeventhRealmAddress(
        address newGateOfTheSeventhRealmAddress
    ) public onlyOwner {
        gateOfTheSeventhRealmAddress = newGateOfTheSeventhRealmAddress;
    }

    /**
     * @dev Set the address of the Auction House
     * @param newAuctionHouseAddress address the address to the contract
     */
    function setAuctionHouseAddress(address newAuctionHouseAddress)
        public
        onlyOwner
    {
        auctionHouseAddress = newAuctionHouseAddress;
    }

    /**
     * @dev Set the address of the Book of Lore contract
     * @param newBookOfLoreAddress address the address to the contract
     */
    function setBookOfLoreAddress(address newBookOfLoreAddress)
        public
        onlyOwner
    {
        bookOfLoreAddress = newBookOfLoreAddress;
    }

    /**
     * @dev Set the address of the Forgotten Runes Wizards Cult tokens
     * @param newWizardsAddress address the address to the contract
     */
    function setWizardsAddress(address newWizardsAddress) public onlyOwner {
        wizardsAddress = newWizardsAddress;
    }

    /**
     * @dev Set the address of Forgotten Souls contract
     * @param newSoulsAddress address the address to the contract
     */
    function setSoulsAddress(address newSoulsAddress) public onlyOwner {
        soulsAddress = newSoulsAddress;
    }

    /**
     * @dev Recover ERC721's accidentally sent to this contract
     * @param token IERC721 the address of the token
     * @param to address where to send the token
     * @param tokenId uint256 the token ID
     */
    function transferFrom(
        IERC721 token,
        address to,
        uint256 tokenId
    ) public onlyOwner {
        token.transferFrom(address(this), to, tokenId);
    }

    /**
     * @dev Set approval for an operator to manage our ERC721s, again, for emergencies
     * @param token IERC721 the address of the token
     * @param operator address the operator's address
     * @param approved bool operator approval
     */
    function setApprovalForAll(
        IERC721 token,
        address operator,
        bool approved
    ) public onlyOwner {
        token.setApprovalForAll(operator, approved);
    }

    function __remove(address unlocker, uint256 tokenId) public onlyOwner {
        delete unlockers[unlocker];
        delete unlockerWizards[tokenId];
    }
}
[{"inputs":[{"internalType":"address","name":"_bookOfLoreAddress","type":"address"},{"internalType":"address","name":"_wizardsAddress","type":"address"},{"internalType":"address","name":"_soulsAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"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":false,"internalType":"address","name":"wizA","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenIdA","type":"uint256"},{"indexed":false,"internalType":"address","name":"wizB","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenIdB","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lockIdx","type":"uint256"}],"name":"Unlocked","type":"event"},{"inputs":[],"name":"NUM_LOCKS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNLOCK_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"unlocker","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"__remove","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"auctionHouseAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bookOfLoreAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"domainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"forwardERC20s","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gateOfTheSeventhRealmAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"newStartBlock","type":"uint256"},{"internalType":"bytes32","name":"newKeysMerkleRoot","type":"bytes32"},{"internalType":"bytes32","name":"newSpellsMerkleRoot","type":"bytes32"},{"internalType":"address","name":"newGateOfTheSeventhRealmAddress","type":"address"},{"internalType":"address","name":"newAuctionHouseAddress","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"keysMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numUnlocked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"token","type":"address"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAuctionHouseAddress","type":"address"}],"name":"setAuctionHouseAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newBookOfLoreAddress","type":"address"}],"name":"setBookOfLoreAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newGateOfTheSeventhRealmAddress","type":"address"}],"name":"setGateOfTheSeventhRealmAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"newMerkleRoot","type":"bytes32"}],"name":"setKeysMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newSoulsAddress","type":"address"}],"name":"setSoulsAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"newMerkleRoot","type":"bytes32"}],"name":"setSpellsMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newStartBlock","type":"uint256"}],"name":"setStartBlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newWizardsAddress","type":"address"}],"name":"setWizardsAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"soulsAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"spellsMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"started","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"ownerA","type":"address"},{"internalType":"uint256","name":"wizIdA","type":"uint256"},{"internalType":"uint256","name":"wizIdB","type":"uint256"},{"internalType":"bytes32[]","name":"wizAMerkleProof","type":"bytes32[]"},{"internalType":"bytes32[]","name":"wizBMerkleProof","type":"bytes32[]"},{"internalType":"bytes","name":"signatureA","type":"bytes"}],"name":"unlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"unlockerWizards","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"unlockers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawAll","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"wizardsAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]