ETH Price: $2,431.21 (-0.20%)

Transaction Decoder

Block:
14883657 at Jun-01-2022 07:47:42 AM +UTC
Transaction Fee:
0.013836163472813328 ETH $33.64
Gas Used:
413,464 Gas / 33.464010102 Gwei

Emitted Events:

36 PartyBidFactory.PartyBidDeployed( partyBidProxy=InitializedProxy, creator=[Sender] 0x63760e7c402f64661da16c13c2353c839b210e93, nftContract=Media, tokenId=17795, marketWrapper=ZoraMarketWrapper, auctionId=5769, splitRecipient=[Sender] 0x63760e7c402f64661da16c13c2353c839b210e93, splitBasisPoints=100, gatedToken=0x00000000...000000000, gatedTokenAmount=0, name=The First Porn NFT, symbol=HODLERS )

Account State Difference:

  Address   Before After State Difference Code
0x0accf637...76dC63D33
0x4b53360F...653524b5B
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 2836385237192454245840109197161555693698274061854228132515509364362060641527870964007947195405181321299558590811448188373980324921395541377601574706493514505085429731184950134749895650048519361805138498233539209014746915646598802851555241941984055572966665121416684589943783163512609890861892647817889698504570800615171253117802496623487257891975208379567108314068263720661400613012844851486037293866643697130580826323708098142387523387585056039633738540564929348468684317164780878439843576924800020044952593238461606236066319251649441527166570562924033034882318255654525771903798915053118299612324661887027
0x63760E7c...39b210e93
0.202771262401402852 Eth
Nonce: 20
0.188935098928589524 Eth
Nonce: 21
0.013836163472813328
(F2Pool Old)
2,145.496773646855699095 Eth2,145.497393842855699095 Eth0.000620196

Execution Trace

PartyBidFactory.startParty( _marketWrapper=0x11c07cE1315a3b92C9755F90cDF40B04b88c5731, _nftContract=0xabEFBc9fD2F806065b4f3C237d4b59D9A97Bcac7, _tokenId=17795, _auctionId=5769, _split=[{name:addr, type:address, order:1, indexed:false, value:0x63760E7c402f64661da16c13C2353C839b210e93, valueString:0x63760E7c402f64661da16c13C2353C839b210e93}, {name:amount, type:uint256, order:2, indexed:false, value:100, valueString:100}], _tokenGate=[{name:addr, type:address, order:1, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:amount, type:uint256, order:2, indexed:false, value:0, valueString:0}], _name=The First Porn NFT, _symbol=HODLERS ) => ( partyBidProxy=0x4b53360F855a9F8231257b98Af73Ece653524b5B )
  • InitializedProxy.60a06040( )
    • PartyBid.initialize( _marketWrapper=0x11c07cE1315a3b92C9755F90cDF40B04b88c5731, _nftContract=0xabEFBc9fD2F806065b4f3C237d4b59D9A97Bcac7, _tokenId=17795, _auctionId=5769, _split=[{name:addr, type:address, order:1, indexed:false, value:0x63760E7c402f64661da16c13C2353C839b210e93, valueString:0x63760E7c402f64661da16c13C2353C839b210e93}, {name:amount, type:uint256, order:2, indexed:false, value:100, valueString:100}], _tokenGate=[{name:addr, type:address, order:1, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:amount, type:uint256, order:2, indexed:false, value:0, valueString:0}], _name=The First Porn NFT, _symbol=HODLERS )
      • ZoraMarketWrapper.auctionIdMatchesToken( auctionId=5769, nftContract=0xabEFBc9fD2F806065b4f3C237d4b59D9A97Bcac7, tokenId=17795 ) => ( True )
        • AuctionHouse.auctions( 5769 ) => ( tokenId=17795, tokenContract=0xabEFBc9fD2F806065b4f3C237d4b59D9A97Bcac7, approved=True, amount=0, duration=259200, firstBidTime=0, reservePrice=10000000000000000000, curatorFeePercentage=0, tokenOwner=0x63760E7c402f64661da16c13C2353C839b210e93, bidder=0x0000000000000000000000000000000000000000, curator=0x63760E7c402f64661da16c13C2353C839b210e93, auctionCurrency=0x0000000000000000000000000000000000000000 )
        • Media.ownerOf( tokenId=17795 ) => ( 0xE468cE99444174Bd3bBBEd09209577d25D1ad673 )
          File 1 of 6: PartyBidFactory
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.5;
          import {InitializedProxy} from "./InitializedProxy.sol";
          import {PartyBid} from "./PartyBid.sol";
          import {Structs} from "./Structs.sol";
          /**
           * @title PartyBid Factory
           * @author Anna Carroll
           *
           * WARNING: A malicious MarketWrapper contract could be used to steal user funds;
           * A poorly implemented MarketWrapper contract could permanently lose access to the NFT.
           * When deploying a PartyBid, exercise extreme caution.
           * Only use MarketWrapper contracts that have been audited and tested.
           */
          contract PartyBidFactory {
              //======== Events ========
              event PartyBidDeployed(
                  address partyBidProxy,
                  address creator,
                  address nftContract,
                  uint256 tokenId,
                  address marketWrapper,
                  uint256 auctionId,
                  address splitRecipient,
                  uint256 splitBasisPoints,
                  address gatedToken,
                  uint256 gatedTokenAmount,
                  string name,
                  string symbol
              );
              //======== Immutable storage =========
              address public immutable logic;
              address public immutable partyDAOMultisig;
              address public immutable tokenVaultFactory;
              address public immutable weth;
              //======== Mutable storage =========
              // PartyBid proxy => block number deployed at
              mapping(address => uint256) public deployedAt;
              //======== Constructor =========
              constructor(
                  address _partyDAOMultisig,
                  address _tokenVaultFactory,
                  address _weth
              ) {
                  partyDAOMultisig = _partyDAOMultisig;
                  tokenVaultFactory = _tokenVaultFactory;
                  weth = _weth;
                  // deploy logic contract
                  PartyBid _logicContract = new PartyBid(_partyDAOMultisig, _tokenVaultFactory, _weth);
                  // store logic contract address
                  logic = address(_logicContract);
              }
              //======== Deploy function =========
              function startParty(
                  address _marketWrapper,
                  address _nftContract,
                  uint256 _tokenId,
                  uint256 _auctionId,
                  Structs.AddressAndAmount calldata _split,
                  Structs.AddressAndAmount calldata _tokenGate,
                  string memory _name,
                  string memory _symbol
              ) external returns (address partyBidProxy) {
                  bytes memory _initializationCalldata =
                      abi.encodeWithSelector(
                          PartyBid.initialize.selector,
                          _marketWrapper,
                          _nftContract,
                          _tokenId,
                          _auctionId,
                          _split,
                          _tokenGate,
                          _name,
                          _symbol
                      );
                  partyBidProxy = address(
                      new InitializedProxy(
                          logic,
                          _initializationCalldata
                      )
                  );
                  deployedAt[partyBidProxy] = block.number;
                  emit PartyBidDeployed(
                      partyBidProxy,
                      msg.sender,
                      _nftContract,
                      _tokenId,
                      _marketWrapper,
                      _auctionId,
                      _split.addr,
                      _split.amount,
                      _tokenGate.addr,
                      _tokenGate.amount,
                      _name,
                      _symbol
                  );
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.5;
          /**
           * @title InitializedProxy
           * @author Anna Carroll
           */
          contract InitializedProxy {
              // address of logic contract
              address public immutable logic;
              // ======== Constructor =========
              constructor(
                  address _logic,
                  bytes memory _initializationCalldata
              ) {
                  logic = _logic;
                  // Delegatecall into the logic contract, supplying initialization calldata
                  (bool _ok, bytes memory returnData) =
                      _logic.delegatecall(_initializationCalldata);
                  // Revert if delegatecall to implementation reverts
                  require(_ok, string(returnData));
              }
              // ======== Fallback =========
              fallback() external payable {
                  address _impl = logic;
                  assembly {
                      let ptr := mload(0x40)
                      calldatacopy(ptr, 0, calldatasize())
                      let result := delegatecall(gas(), _impl, ptr, calldatasize(), 0, 0)
                      let size := returndatasize()
                      returndatacopy(ptr, 0, size)
                      switch result
                          case 0 {
                              revert(ptr, size)
                          }
                          default {
                              return(ptr, size)
                          }
                  }
              }
              // ======== Receive =========
              receive() external payable {} // solhint-disable-line no-empty-blocks
          }
          /*
                ___           ___           ___           ___           ___           ___                       ___
               /\\  \\         /\\  \\         /\\  \\         /\\  \\         |\\__\\         /\\  \\          ___        /\\  \\
              /::\\  \\       /::\\  \\       /::\\  \\        \\:\\  \\        |:|  |       /::\\  \\        /\\  \\      /::\\  \\
             /:/\\:\\  \\     /:/\\:\\  \\     /:/\\:\\  \\        \\:\\  \\       |:|  |      /:/\\:\\  \\       \\:\\  \\    /:/\\:\\  \\
            /::\\~\\:\\  \\   /::\\~\\:\\  \\   /::\\~\\:\\  \\       /::\\  \\      |:|__|__   /::\\~\\:\\__\\      /::\\__\\  /:/  \\:\\__\\
           /:/\\:\\ \\:\\__\\ /:/\\:\\ \\:\\__\\ /:/\\:\\ \\:\\__\\     /:/\\:\\__\\     /::::\\__\\ /:/\\:\\ \\:|__|  __/:/\\/__/ /:/__/ \\:|__|
           \\/__\\:\\/:/  / \\/__\\:\\/:/  / \\/_|::\\/:/  /    /:/  \\/__/    /:/~~/~    \\:\\~\\:\\/:/  / /\\/:/  /    \\:\\  \\ /:/  /
                \\::/  /       \\::/  /     |:|::/  /    /:/  /        /:/  /       \\:\\ \\::/  /  \\::/__/      \\:\\  /:/  /
                 \\/__/        /:/  /      |:|\\/__/     \\/__/         \\/__/         \\:\\/:/  /    \\:\\__\\       \\:\\/:/  /
                             /:/  /       |:|  |                                    \\::/__/      \\/__/        \\::/__/
                             \\/__/         \\|__|                                     ~~                        ~~
          Anna Carroll for PartyDAO
          */
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.5;
          // ============ Internal Imports ============
          import {Party} from "./Party.sol";
          import {IMarketWrapper} from "./market-wrapper/IMarketWrapper.sol";
          import {Structs} from "./Structs.sol";
          contract PartyBid is Party {
              // partyStatus Transitions:
              //   (1) PartyStatus.ACTIVE on deploy
              //   (2) PartyStatus.WON or PartyStatus.LOST on finalize()
              // ============ Internal Constants ============
              // PartyBid version 3
              uint16 public constant VERSION = 3;
              // ============ Public Not-Mutated Storage ============
              // market wrapper contract exposing interface for
              // market auctioning the NFT
              IMarketWrapper public marketWrapper;
              // ID of auction within market contract
              uint256 public auctionId;
              // ============ Public Mutable Storage ============
              // the highest bid submitted by PartyBid
              uint256 public highestBid;
              // ============ Events ============
              event Bid(uint256 amount);
              event Finalized(PartyStatus result, uint256 totalSpent, uint256 fee, uint256 totalContributed);
              // ======== Constructor =========
              constructor(
                  address _partyDAOMultisig,
                  address _tokenVaultFactory,
                  address _weth
              ) Party(_partyDAOMultisig, _tokenVaultFactory, _weth) {}
              // ======== Initializer =========
              function initialize(
                  address _marketWrapper,
                  address _nftContract,
                  uint256 _tokenId,
                  uint256 _auctionId,
                  Structs.AddressAndAmount calldata _split,
                  Structs.AddressAndAmount calldata _tokenGate,
                  string memory _name,
                  string memory _symbol
              ) external initializer {
                  // validate auction exists
                  require(
                      IMarketWrapper(_marketWrapper).auctionIdMatchesToken(
                          _auctionId,
                          _nftContract,
                          _tokenId
                      ),
                      "PartyBid::initialize: auctionId doesn't match token"
                  );
                  // initialize & validate shared Party variables
                  __Party_init(_nftContract, _tokenId, _split, _tokenGate, _name, _symbol);
                  // set PartyBid-specific state variables
                  marketWrapper = IMarketWrapper(_marketWrapper);
                  auctionId = _auctionId;
              }
              // ======== External: Contribute =========
              /**
               * @notice Contribute to the Party's treasury
               * while the Party is still active
               * @dev Emits a Contributed event upon success; callable by anyone
               */
              function contribute() external payable nonReentrant {
                  _contribute();
              }
              // ======== External: Bid =========
              /**
               * @notice Submit a bid to the Market
               * @dev Reverts if insufficient funds to place the bid and pay PartyDAO fees,
               * or if any external auction checks fail (including if PartyBid is current high bidder)
               * Emits a Bid event upon success.
               * Callable by any contributor
               */
              function bid() external nonReentrant {
                  require(
                      partyStatus == PartyStatus.ACTIVE,
                      "PartyBid::bid: auction not active"
                  );
                  require(
                      totalContributed[msg.sender] > 0,
                      "PartyBid::bid: only contributors can bid"
                  );
                  require(
                      address(this) !=
                          marketWrapper.getCurrentHighestBidder(
                              auctionId
                          ),
                      "PartyBid::bid: already highest bidder"
                  );
                  require(
                      !marketWrapper.isFinalized(auctionId),
                      "PartyBid::bid: auction already finalized"
                  );
                  // get the minimum next bid for the auction
                  uint256 _bid = marketWrapper.getMinimumBid(auctionId);
                  // ensure there is enough ETH to place the bid including PartyDAO fee
                  require(
                      _bid <= getMaximumBid(),
                      "PartyBid::bid: insufficient funds to bid"
                  );
                  // submit bid to Auction contract using delegatecall
                  (bool success, bytes memory returnData) =
                      address(marketWrapper).delegatecall(
                          abi.encodeWithSignature("bid(uint256,uint256)", auctionId, _bid)
                      );
                  require(
                      success,
                      string(
                          abi.encodePacked(
                              "PartyBid::bid: place bid failed: ",
                              returnData
                          )
                      )
                  );
                  // update highest bid submitted & emit success event
                  highestBid = _bid;
                  emit Bid(_bid);
              }
              // ======== External: Finalize =========
              /**
               * @notice Finalize the state of the auction
               * @dev Emits a Finalized event upon success; callable by anyone
               */
              function finalize() external nonReentrant {
                  require(
                      partyStatus == PartyStatus.ACTIVE,
                      "PartyBid::finalize: auction not active"
                  );
                  // finalize auction if it hasn't already been done
                  if (!marketWrapper.isFinalized(auctionId)) {
                      marketWrapper.finalize(auctionId);
                  }
                  // after the auction has been finalized,
                  // if the NFT is owned by the PartyBid, then the PartyBid won the auction
                  address _owner = _getOwner();
                  partyStatus = _owner == address(this) ? PartyStatus.WON : PartyStatus.LOST;
                  uint256 _ethFee;
                  // if the auction was won,
                  if (partyStatus == PartyStatus.WON) {
                      // record totalSpent,
                      // send ETH fees to PartyDAO,
                      // fractionalize the Token
                      // send Token fees to PartyDAO & split proceeds to split recipient
                      _ethFee = _closeSuccessfulParty(highestBid);
                  }
                  // set the contract status & emit result
                  emit Finalized(partyStatus, totalSpent, _ethFee, totalContributedToParty);
              }
              // ======== Public: Utility Calculations =========
              /**
               * @notice The maximum bid that can be submitted
               * while paying the ETH fee to PartyDAO
               * @return _maxBid the maximum bid
               */
              function getMaximumBid() public view returns (uint256 _maxBid) {
                  _maxBid = getMaximumSpend();
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.5;
          interface Structs {
              struct AddressAndAmount {
                  address addr;
                  uint256 amount;
              }
          }
          /*
          __/\\\\\\\\\\\\\\\\\\\\\\\\\\_____________________________________________________________/\\\\\\\\\\\\\\\\\\\\\\\\________/\\\\\\\\\\\\\\\\\\__________/\\\\\\\\\\______
           _\\/\\\\\\/////////\\\\\\__________________________________________________________\\/\\\\\\////////\\\\\\____/\\\\\\\\\\\\\\\\\\\\\\\\\\______/\\\\\\///\\\\\\____
            _\\/\\\\\\_______\\/\\\\\\__________________________________/\\\\\\_________/\\\\\\__/\\\\\\_\\/\\\\\\______\\//\\\\\\__/\\\\\\/////////\\\\\\___/\\\\\\/__\\///\\\\\\__
             _\\/\\\\\\\\\\\\\\\\\\\\\\\\\\/___/\\\\\\\\\\\\\\\\\\_____/\\\\/\\\\\\\\\\\\\\___/\\\\\\\\\\\\\\\\\\\\\\___\\//\\\\\\/\\\\\\__\\/\\\\\\_______\\/\\\\\\_\\/\\\\\\_______\\/\\\\\\__/\\\\\\______\\//\\\\\\_
              _\\/\\\\\\/////////____\\////////\\\\\\___\\/\\\\\\/////\\\\\\_\\////\\\\\\////_____\\//\\\\\\\\\\___\\/\\\\\\_______\\/\\\\\\_\\/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\_\\/\\\\\\_______\\/\\\\\\_
               _\\/\\\\\\_______________/\\\\\\\\\\\\\\\\\\\\__\\/\\\\\\___\\///_____\\/\\\\\\__________\\//\\\\\\____\\/\\\\\\_______\\/\\\\\\_\\/\\\\\\/////////\\\\\\_\\//\\\\\\______/\\\\\\__
                _\\/\\\\\\______________/\\\\\\/////\\\\\\__\\/\\\\\\____________\\/\\\\\\_/\\\\___/\\\\_/\\\\\\_____\\/\\\\\\_______/\\\\\\__\\/\\\\\\_______\\/\\\\\\__\\///\\\\\\__/\\\\\\____
                 _\\/\\\\\\_____________\\//\\\\\\\\\\\\\\\\/\\\\_\\/\\\\\\____________\\//\\\\\\\\\\___\\//\\\\\\\\/______\\/\\\\\\\\\\\\\\\\\\\\\\\\/___\\/\\\\\\_______\\/\\\\\\____\\///\\\\\\\\\\/_____
                  _\\///_______________\\////////\\//__\\///______________\\/////_____\\////________\\////////////_____\\///________\\///_______\\/////_______
          Anna Carroll for PartyDAO
          */
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.5;
          // ============ External Imports: Inherited Contracts ============
          // NOTE: we inherit from OpenZeppelin upgradeable contracts
          // because of the proxy structure used for cheaper deploys
          // (the proxies are NOT actually upgradeable)
          import {
          ReentrancyGuardUpgradeable
          } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
          import {
          ERC721HolderUpgradeable
          } from "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol";
          // ============ External Imports: External Contracts & Contract Interfaces ============
          import {
          IERC721VaultFactory
          } from "./external/interfaces/IERC721VaultFactory.sol";
          import {ITokenVault} from "./external/interfaces/ITokenVault.sol";
          import {IWETH} from "./external/interfaces/IWETH.sol";
          import {
          IERC721Metadata
          } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
          import {
          IERC20
          } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
          // ============ Internal Imports ============
          import {Structs} from "./Structs.sol";
          contract Party is ReentrancyGuardUpgradeable, ERC721HolderUpgradeable {
              // ============ Enums ============
              // State Transitions:
              //   (1) ACTIVE on deploy
              //   (2) WON if the Party has won the token
              //   (2) LOST if the Party is over & did not win the token
              enum PartyStatus {ACTIVE, WON, LOST}
              // ============ Structs ============
              struct Contribution {
                  uint256 amount;
                  uint256 previousTotalContributedToParty;
              }
              // ============ Internal Constants ============
              // tokens are minted at a rate of 1 ETH : 1000 tokens
              uint16 internal constant TOKEN_SCALE = 1000;
              // PartyDAO receives an ETH fee equal to 2.5% of the amount spent
              uint16 internal constant ETH_FEE_BASIS_POINTS = 250;
              // PartyDAO receives a token fee equal to 2.5% of the total token supply
              uint16 internal constant TOKEN_FEE_BASIS_POINTS = 250;
              // token is relisted on Fractional with an
              // initial reserve price equal to 2x the price of the token
              uint8 internal constant RESALE_MULTIPLIER = 2;
              // ============ Immutables ============
              address public immutable partyFactory;
              address public immutable partyDAOMultisig;
              IERC721VaultFactory public immutable tokenVaultFactory;
              IWETH public immutable weth;
              // ============ Public Not-Mutated Storage ============
              // NFT contract
              IERC721Metadata public nftContract;
              // ID of token within NFT contract
              uint256 public tokenId;
              // Fractionalized NFT vault responsible for post-purchase experience
              ITokenVault public tokenVault;
              // the address that will receive a portion of the tokens
              // if the Party successfully buys the token
              address public splitRecipient;
              // percent of the total token supply
              // taken by the splitRecipient
              uint256 public splitBasisPoints;
              // address of token that users need to hold to contribute
              // address(0) if party is not token gated
              IERC20 public gatedToken;
              // amount of token that users need to hold to contribute
              // 0 if party is not token gated
              uint256 public gatedTokenAmount;
              // ERC-20 name and symbol for fractional tokens
              string public name;
              string public symbol;
              // ============ Public Mutable Storage ============
              // state of the contract
              PartyStatus public partyStatus;
              // total ETH deposited by all contributors
              uint256 public totalContributedToParty;
              // the total spent buying the token;
              // 0 if the NFT is not won; price of token + 2.5% PartyDAO fee if NFT is won
              uint256 public totalSpent;
              // contributor => array of Contributions
              mapping(address => Contribution[]) public contributions;
              // contributor => total amount contributed
              mapping(address => uint256) public totalContributed;
              // contributor => true if contribution has been claimed
              mapping(address => bool) public claimed;
              // ============ Events ============
              event Contributed(
                  address indexed contributor,
                  uint256 amount,
                  uint256 previousTotalContributedToParty,
                  uint256 totalFromContributor
              );
              event Claimed(
                  address indexed contributor,
                  uint256 totalContributed,
                  uint256 excessContribution,
                  uint256 tokenAmount
              );
              // ======== Modifiers =========
              modifier onlyPartyDAO() {
                  require(
                      msg.sender == partyDAOMultisig,
                      "Party:: only PartyDAO multisig"
                  );
                  _;
              }
              // ======== Constructor =========
              constructor(
                  address _partyDAOMultisig,
                  address _tokenVaultFactory,
                  address _weth
              ) {
                  partyFactory = msg.sender;
                  partyDAOMultisig = _partyDAOMultisig;
                  tokenVaultFactory = IERC721VaultFactory(_tokenVaultFactory);
                  weth = IWETH(_weth);
              }
              // ======== Internal: Initialize =========
              function __Party_init(
                  address _nftContract,
                  uint256 _tokenId,
                  Structs.AddressAndAmount calldata _split,
                  Structs.AddressAndAmount calldata _tokenGate,
                  string memory _name,
                  string memory _symbol
              ) internal {
                  require(msg.sender == partyFactory, "Party::__Party_init: only factory can init");
                  // validate token exists (must set nftContract & tokenId before _getOwner)
                  nftContract = IERC721Metadata(_nftContract);
                  tokenId = _tokenId;
                  require(_getOwner() != address(0), "Party::__Party_init: NFT getOwner failed");
                  // if split is non-zero,
                  if (_split.addr != address(0) && _split.amount != 0) {
                      // validate that party split won't retain the total token supply
                      uint256 _remainingBasisPoints = 10000 - TOKEN_FEE_BASIS_POINTS;
                      require(_split.amount < _remainingBasisPoints, "Party::__Party_init: basis points can't take 100%");
                      splitBasisPoints = _split.amount;
                      splitRecipient = _split.addr;
                  }
                  // if token gating is non-zero
                  if (_tokenGate.addr != address(0) && _tokenGate.amount != 0) {
                      // call totalSupply to verify that address is ERC-20 token contract
                      IERC20(_tokenGate.addr).totalSupply();
                      gatedToken = IERC20(_tokenGate.addr);
                      gatedTokenAmount = _tokenGate.amount;
                  }
                  // initialize ReentrancyGuard and ERC721Holder
                  __ReentrancyGuard_init();
                  __ERC721Holder_init();
                  // set storage variables
                  name = _name;
                  symbol = _symbol;
              }
              // ======== Internal: Contribute =========
              /**
               * @notice Contribute to the Party's treasury
               * while the Party is still active
               * @dev Emits a Contributed event upon success; callable by anyone
               */
              function _contribute() internal {
                  require(
                      partyStatus == PartyStatus.ACTIVE,
                      "Party::contribute: party not active"
                  );
                  address _contributor = msg.sender;
                  uint256 _amount = msg.value;
                  // if token gated, require that contributor has balance of gated tokens
                  if (address(gatedToken) != address(0)) {
                      require(gatedToken.balanceOf(_contributor) >= gatedTokenAmount, "Party::contribute: must hold tokens to contribute");
                  }
                  require(_amount > 0, "Party::contribute: must contribute more than 0");
                  // get the current contract balance
                  uint256 _previousTotalContributedToParty = totalContributedToParty;
                  // add contribution to contributor's array of contributions
                  Contribution memory _contribution =
                      Contribution({
                          amount: _amount,
                          previousTotalContributedToParty: _previousTotalContributedToParty
                      });
                  contributions[_contributor].push(_contribution);
                  // add to contributor's total contribution
                  totalContributed[_contributor] = totalContributed[_contributor] + _amount;
                  // add to party's total contribution & emit event
                  totalContributedToParty = _previousTotalContributedToParty + _amount;
                  emit Contributed(
                      _contributor,
                      _amount,
                      _previousTotalContributedToParty,
                      totalContributed[_contributor]
                  );
              }
              // ======== External: Claim =========
              /**
               * @notice Claim the tokens and excess ETH owed
               * to a single contributor after the party has ended
               * @dev Emits a Claimed event upon success
               * callable by anyone (doesn't have to be the contributor)
               * @param _contributor the address of the contributor
               */
              function claim(address _contributor) external nonReentrant {
                  // ensure party has finalized
                  require(
                      partyStatus != PartyStatus.ACTIVE,
                      "Party::claim: party not finalized"
                  );
                  // ensure contributor submitted some ETH
                  require(
                      totalContributed[_contributor] != 0,
                      "Party::claim: not a contributor"
                  );
                  // ensure the contributor hasn't already claimed
                  require(
                      !claimed[_contributor],
                      "Party::claim: contribution already claimed"
                  );
                  // mark the contribution as claimed
                  claimed[_contributor] = true;
                  // calculate the amount of fractional NFT tokens owed to the user
                  // based on how much ETH they contributed towards the party,
                  // and the amount of excess ETH owed to the user
                  (uint256 _tokenAmount, uint256 _ethAmount) =
                  getClaimAmounts(_contributor);
                  // transfer tokens to contributor for their portion of ETH used
                  _transferTokens(_contributor, _tokenAmount);
                  // if there is excess ETH, send it back to the contributor
                  _transferETHOrWETH(_contributor, _ethAmount);
                  emit Claimed(
                      _contributor,
                      totalContributed[_contributor],
                      _ethAmount,
                      _tokenAmount
                  );
              }
              // ======== External: Emergency Escape Hatches (PartyDAO Multisig Only) =========
              /**
               * @notice Escape hatch: in case of emergency,
               * PartyDAO can use emergencyWithdrawEth to withdraw
               * ETH stuck in the contract
               */
              function emergencyWithdrawEth(uint256 _value)
                  external
                  onlyPartyDAO
              {
                  _transferETHOrWETH(partyDAOMultisig, _value);
              }
              /**
               * @notice Escape hatch: in case of emergency,
               * PartyDAO can use emergencyCall to call an external contract
               * (e.g. to withdraw a stuck NFT or stuck ERC-20s)
               */
              function emergencyCall(address _contract, bytes memory _calldata)
                  external
                  onlyPartyDAO
                  returns (bool _success, bytes memory _returnData)
              {
                  (_success, _returnData) = _contract.call(_calldata);
                  require(_success, string(_returnData));
              }
              /**
               * @notice Escape hatch: in case of emergency,
               * PartyDAO can force the Party to finalize with status LOST
               * (e.g. if finalize is not callable)
               */
              function emergencyForceLost()
                  external
                  onlyPartyDAO
              {
                  // set partyStatus to LOST
                  partyStatus = PartyStatus.LOST;
              }
              // ======== Public: Utility Calculations =========
              /**
               * @notice Convert ETH value to equivalent token amount
               */
              function valueToTokens(uint256 _value)
                  public
                  pure
                  returns (uint256 _tokens)
              {
                  _tokens = _value * TOKEN_SCALE;
              }
              /**
               * @notice The maximum amount that can be spent by the Party
               * while paying the ETH fee to PartyDAO
               * @return _maxSpend the maximum spend
               */
              function getMaximumSpend() public view returns (uint256 _maxSpend) {
                  _maxSpend = (totalContributedToParty * 10000) / (10000 + ETH_FEE_BASIS_POINTS);
              }
              /**
               * @notice Calculate the amount of fractional NFT tokens owed to the contributor
               * based on how much ETH they contributed towards buying the token,
               * and the amount of excess ETH owed to the contributor
               * based on how much ETH they contributed *not* used towards buying the token
               * @param _contributor the address of the contributor
               * @return _tokenAmount the amount of fractional NFT tokens owed to the contributor
               * @return _ethAmount the amount of excess ETH owed to the contributor
               */
              function getClaimAmounts(address _contributor)
                  public
                  view
                  returns (uint256 _tokenAmount, uint256 _ethAmount)
              {
                  require(partyStatus != PartyStatus.ACTIVE, "Party::getClaimAmounts: party still active; amounts undetermined");
                  uint256 _totalContributed = totalContributed[_contributor];
                  if (partyStatus == PartyStatus.WON) {
                      // calculate the amount of this contributor's ETH
                      // that was used to buy the token
                      uint256 _totalEthUsed = totalEthUsed(_contributor);
                      if (_totalEthUsed > 0) {
                          _tokenAmount = valueToTokens(_totalEthUsed);
                      }
                      // the rest of the contributor's ETH should be returned
                      _ethAmount = _totalContributed - _totalEthUsed;
                  } else {
                      // if the token wasn't bought, no ETH was spent;
                      // all of the contributor's ETH should be returned
                      _ethAmount = _totalContributed;
                  }
              }
              /**
               * @notice Calculate the total amount of a contributor's funds
               * that were used towards the buying the token
               * @dev always returns 0 until the party has been finalized
               * @param _contributor the address of the contributor
               * @return _total the sum of the contributor's funds that were
               * used towards buying the token
               */
              function totalEthUsed(address _contributor)
                  public
                  view
                  returns (uint256 _total)
              {
                  require(partyStatus != PartyStatus.ACTIVE, "Party::totalEthUsed: party still active; amounts undetermined");
                  // load total amount spent once from storage
                  uint256 _totalSpent = totalSpent;
                  // get all of the contributor's contributions
                  Contribution[] memory _contributions = contributions[_contributor];
                  for (uint256 i = 0; i < _contributions.length; i++) {
                      // calculate how much was used from this individual contribution
                      uint256 _amount = _ethUsed(_totalSpent, _contributions[i]);
                      // if we reach a contribution that was not used,
                      // no subsequent contributions will have been used either,
                      // so we can stop calculating to save some gas
                      if (_amount == 0) break;
                      _total = _total + _amount;
                  }
              }
              // ============ Internal ============
              function _closeSuccessfulParty(uint256 _nftCost) internal returns (uint256 _ethFee) {
                  // calculate PartyDAO fee & record total spent
                  _ethFee = _getEthFee(_nftCost);
                  totalSpent = _nftCost + _ethFee;
                  // transfer ETH fee to PartyDAO
                  _transferETHOrWETH(partyDAOMultisig, _ethFee);
                  // deploy fractionalized NFT vault
                  // and mint fractional ERC-20 tokens
                  _fractionalizeNFT(_nftCost);
              }
              /**
               * @notice Calculate ETH fee for PartyDAO
               * NOTE: Remove this fee causes a critical vulnerability
               * allowing anyone to exploit a Party via price manipulation.
               * See Security Review in README for more info.
               * @return _fee the portion of _amount represented by scaling to ETH_FEE_BASIS_POINTS
               */
              function _getEthFee(uint256 _amount) internal pure returns (uint256 _fee) {
                  _fee = (_amount * ETH_FEE_BASIS_POINTS) / 10000;
              }
              /**
               * @notice Calculate token amount for specified token recipient
               * @return _totalSupply the total token supply
               * @return _partyDAOAmount the amount of tokens for partyDAO fee,
               * which is equivalent to TOKEN_FEE_BASIS_POINTS of total supply
               * @return _splitRecipientAmount the amount of tokens for the token recipient,
               * which is equivalent to splitBasisPoints of total supply
               */
              function _getTokenInflationAmounts(uint256 _amountSpent)
                  internal
                  view
                  returns (uint256 _totalSupply, uint256 _partyDAOAmount, uint256 _splitRecipientAmount)
              {
                  // the token supply will be inflated to provide a portion of the
                  // total supply for PartyDAO, and a portion for the splitRecipient
                  uint256 inflationBasisPoints = TOKEN_FEE_BASIS_POINTS + splitBasisPoints;
                  _totalSupply = valueToTokens((_amountSpent * 10000) / (10000 - inflationBasisPoints));
                  // PartyDAO receives TOKEN_FEE_BASIS_POINTS of the total supply
                  _partyDAOAmount = (_totalSupply * TOKEN_FEE_BASIS_POINTS) / 10000;
                  // splitRecipient receives splitBasisPoints of the total supply
                  _splitRecipientAmount = (_totalSupply * splitBasisPoints) / 10000;
              }
              /**
              * @notice Query the NFT contract to get the token owner
              * @dev nftContract must implement the ERC-721 token standard exactly:
              * function ownerOf(uint256 _tokenId) external view returns (address);
              * See https://eips.ethereum.org/EIPS/eip-721
              * @dev Returns address(0) if NFT token or NFT contract
              * no longer exists (token burned or contract self-destructed)
              * @return _owner the owner of the NFT
              */
              function _getOwner() internal view returns (address _owner) {
                  (bool _success, bytes memory _returnData) =
                      address(nftContract).staticcall(
                          abi.encodeWithSignature(
                              "ownerOf(uint256)",
                              tokenId
                          )
                  );
                  if (_success && _returnData.length > 0) {
                      _owner = abi.decode(_returnData, (address));
                  }
              }
              /**
               * @notice Upon winning the token, transfer the NFT
               * to fractional.art vault & mint fractional ERC-20 tokens
               */
              function _fractionalizeNFT(uint256 _amountSpent) internal {
                  // approve fractionalized NFT Factory to withdraw NFT
                  nftContract.approve(address(tokenVaultFactory), tokenId);
                  // Party "votes" for a reserve price on Fractional
                  // equal to 2x the price of the token
                  uint256 _listPrice = RESALE_MULTIPLIER * _amountSpent;
                  // users receive tokens at a rate of 1:TOKEN_SCALE for each ETH they contributed that was ultimately spent
                  // partyDAO receives a percentage of the total token supply equivalent to TOKEN_FEE_BASIS_POINTS
                  // splitRecipient receives a percentage of the total token supply equivalent to splitBasisPoints
                  (uint256 _tokenSupply, uint256 _partyDAOAmount, uint256 _splitRecipientAmount) = _getTokenInflationAmounts(totalSpent);
                  // deploy fractionalized NFT vault
                  uint256 vaultNumber =
                      tokenVaultFactory.mint(
                          name,
                          symbol,
                          address(nftContract),
                          tokenId,
                          _tokenSupply,
                          _listPrice,
                          0
                      );
                  // store token vault address to storage
                  tokenVault = ITokenVault(tokenVaultFactory.vaults(vaultNumber));
                  // transfer curator to null address (burn the curator role)
                  tokenVault.updateCurator(address(0));
                  // transfer tokens to PartyDAO multisig
                  _transferTokens(partyDAOMultisig, _partyDAOAmount);
                  // transfer tokens to token recipient
                  if (splitRecipient != address(0)) {
                      _transferTokens(splitRecipient, _splitRecipientAmount);
                  }
              }
              // ============ Internal: Claim ============
              /**
               * @notice Calculate the amount of a single Contribution
               * that was used towards buying the token
               * @param _contribution the Contribution struct
               * @return the amount of funds from this contribution
               * that were used towards buying the token
               */
              function _ethUsed(uint256 _totalSpent, Contribution memory _contribution)
                  internal
                  pure
                  returns (uint256)
              {
                  if (
                      _contribution.previousTotalContributedToParty +
                      _contribution.amount <=
                      _totalSpent
                  ) {
                      // contribution was fully used
                      return _contribution.amount;
                  } else if (
                      _contribution.previousTotalContributedToParty < _totalSpent
                  ) {
                      // contribution was partially used
                      return _totalSpent - _contribution.previousTotalContributedToParty;
                  }
                  // contribution was not used
                  return 0;
              }
              // ============ Internal: TransferTokens ============
              /**
              * @notice Transfer tokens to a recipient
              * @param _to recipient of tokens
              * @param _value amount of tokens
              */
              function _transferTokens(address _to, uint256 _value) internal {
                  // skip if attempting to send 0 tokens
                  if (_value == 0) {
                      return;
                  }
                  // guard against rounding errors;
                  // if token amount to send is greater than contract balance,
                  // send full contract balance
                  uint256 _partyBalance = tokenVault.balanceOf(address(this));
                  if (_value > _partyBalance) {
                      _value = _partyBalance;
                  }
                  tokenVault.transfer(_to, _value);
              }
              // ============ Internal: TransferEthOrWeth ============
              /**
               * @notice Attempt to transfer ETH to a recipient;
               * if transferring ETH fails, transfer WETH insteads
               * @param _to recipient of ETH or WETH
               * @param _value amount of ETH or WETH
               */
              function _transferETHOrWETH(address _to, uint256 _value) internal {
                  // skip if attempting to send 0 ETH
                  if (_value == 0) {
                      return;
                  }
                  // guard against rounding errors;
                  // if ETH amount to send is greater than contract balance,
                  // send full contract balance
                  if (_value > address(this).balance) {
                      _value = address(this).balance;
                  }
                  // Try to transfer ETH to the given recipient.
                  if (!_attemptETHTransfer(_to, _value)) {
                      // If the transfer fails, wrap and send as WETH
                      weth.deposit{value: _value}();
                      weth.transfer(_to, _value);
                      // At this point, the recipient can unwrap WETH.
                  }
              }
              /**
               * @notice Attempt to transfer ETH to a recipient
               * @dev Sending ETH is not guaranteed to succeed
               * this method will return false if it fails.
               * We will limit the gas used in transfers, and handle failure cases.
               * @param _to recipient of ETH
               * @param _value amount of ETH
               */
              function _attemptETHTransfer(address _to, uint256 _value)
                  internal
                  returns (bool)
              {
                  // Here increase the gas limit a reasonable amount above the default, and try
                  // to send ETH to the recipient.
                  // NOTE: This might allow the recipient to attempt a limited reentrancy attack.
                  (bool success, ) = _to.call{value: _value, gas: 30000}("");
                  return success;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.5;
          /**
           * @title IMarketWrapper
           * @author Anna Carroll
           * @notice IMarketWrapper provides a common interface for
           * interacting with NFT auction markets.
           * Contracts can abstract their interactions with
           * different NFT markets using IMarketWrapper.
           * NFT markets can become compatible with any contract
           * using IMarketWrapper by deploying a MarketWrapper contract
           * that implements this interface using the logic of their Market.
           *
           * WARNING: MarketWrapper contracts should NEVER write to storage!
           * When implementing a MarketWrapper, exercise caution; a poorly implemented
           * MarketWrapper contract could permanently lose access to the NFT or user funds.
           */
          interface IMarketWrapper {
              /**
               * @notice Given the auctionId, nftContract, and tokenId, check that:
               * 1. the auction ID matches the token
               * referred to by tokenId + nftContract
               * 2. the auctionId refers to an *ACTIVE* auction
               * (e.g. an auction that will accept bids)
               * within this market contract
               * 3. any additional validation to ensure that
               * a PartyBid can bid on this auction
               * (ex: if the market allows arbitrary bidding currencies,
               * check that the auction currency is ETH)
               * Note: This function probably should have been named "isValidAuction"
               * @dev Called in PartyBid.sol in `initialize` at line 174
               * @return TRUE if the auction is valid
               */
              function auctionIdMatchesToken(
                  uint256 auctionId,
                  address nftContract,
                  uint256 tokenId
              ) external view returns (bool);
              /**
               * @notice Calculate the minimum next bid for this auction.
               * PartyBid contracts always submit the minimum possible
               * bid that will be accepted by the Market contract.
               * usually, this is either the reserve price (if there are no bids)
               * or a certain percentage increase above the current highest bid
               * @dev Called in PartyBid.sol in `bid` at line 251
               * @return minimum bid amount
               */
              function getMinimumBid(uint256 auctionId) external view returns (uint256);
              /**
               * @notice Query the current highest bidder for this auction
               * It is assumed that there is always 1 winning highest bidder for an auction
               * This is used to ensure that PartyBid cannot outbid itself if it is already winning
               * @dev Called in PartyBid.sol in `bid` at line 241
               * @return highest bidder
               */
              function getCurrentHighestBidder(uint256 auctionId)
                  external
                  view
                  returns (address);
              /**
               * @notice Submit bid to Market contract
               * @dev Called in PartyBid.sol in `bid` at line 259
               */
              function bid(uint256 auctionId, uint256 bidAmount) external;
              /**
               * @notice Determine whether the auction has been finalized
               * Used to check if it is still possible to bid
               * And to determine whether the PartyBid should finalize the auction
               * @dev Called in PartyBid.sol in `bid` at line 247
               * @dev and in `finalize` at line 288
               * @return TRUE if the auction has been finalized
               */
              function isFinalized(uint256 auctionId) external view returns (bool);
              /**
               * @notice Finalize the results of the auction
               * on the Market contract
               * It is assumed  that this operation is performed once for each auction,
               * that after it is done the auction is over and the NFT has been
               * transferred to the auction winner.
               * @dev Called in PartyBid.sol in `finalize` at line 289
               */
              function finalize(uint256 auctionId) external;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "../proxy/utils/Initializable.sol";
          /**
           * @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 ReentrancyGuardUpgradeable is Initializable {
              // 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;
              function __ReentrancyGuard_init() internal initializer {
                  __ReentrancyGuard_init_unchained();
              }
              function __ReentrancyGuard_init_unchained() internal initializer {
                  _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;
              }
              uint256[49] private __gap;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "../IERC721ReceiverUpgradeable.sol";
          import "../../../proxy/utils/Initializable.sol";
            /**
             * @dev Implementation of the {IERC721Receiver} interface.
             *
             * Accepts all token transfers.
             * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
             */
          contract ERC721HolderUpgradeable is Initializable, IERC721ReceiverUpgradeable {
              function __ERC721Holder_init() internal initializer {
                  __ERC721Holder_init_unchained();
              }
              function __ERC721Holder_init_unchained() internal initializer {
              }
              /**
               * @dev See {IERC721Receiver-onERC721Received}.
               *
               * Always returns `IERC721Receiver.onERC721Received.selector`.
               */
              function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) {
                  return this.onERC721Received.selector;
              }
              uint256[50] private __gap;
          }
          //SPDX-License-Identifier: MIT
          pragma solidity 0.8.5;
          interface IERC721VaultFactory {
              /// @notice the mapping of vault number to vault address
              function vaults(uint256) external returns (address);
              /// @notice the function to mint a new vault
              /// @param _name the desired name of the vault
              /// @param _symbol the desired sumbol of the vault
              /// @param _token the ERC721 token address fo the NFT
              /// @param _id the uint256 ID of the token
              /// @param _listPrice the initial price of the NFT
              /// @return the ID of the vault
              function mint(string memory _name, string memory _symbol, address _token, uint256 _id, uint256 _supply, uint256 _listPrice, uint256 _fee) external returns(uint256);
          }
          //SPDX-License-Identifier: MIT
          pragma solidity 0.8.5;
          interface ITokenVault {
              /// @notice allow curator to update the curator address
              /// @param _curator the new curator
              function updateCurator(address _curator) external;
              /**
               * @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) external returns (bool);
              /**
               * @dev See {IERC20-balanceOf}.
               */
              function balanceOf(address account) external view returns (uint256);
          }// SPDX-License-Identifier: MIT
          pragma solidity 0.8.5;
          interface IWETH {
              function deposit() external payable;
              function transfer(address to, uint256 value) external returns (bool);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "../IERC721.sol";
          /**
           * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
           * @dev See https://eips.ethereum.org/EIPS/eip-721
           */
          interface IERC721Metadata is IERC721 {
              /**
               * @dev Returns the token collection name.
               */
              function name() external view returns (string memory);
              /**
               * @dev Returns the token collection symbol.
               */
              function symbol() external view returns (string memory);
              /**
               * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
               */
              function tokenURI(uint256 tokenId) external view returns (string memory);
          }
          // 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);
          }
          // SPDX-License-Identifier: MIT
          // solhint-disable-next-line compiler-version
          pragma solidity ^0.8.0;
          /**
           * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
           * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
           * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
           * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
           *
           * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
           * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
           *
           * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
           * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
           */
          abstract contract Initializable {
              /**
               * @dev Indicates that the contract has been initialized.
               */
              bool private _initialized;
              /**
               * @dev Indicates that the contract is in the process of being initialized.
               */
              bool private _initializing;
              /**
               * @dev Modifier to protect an initializer function from being invoked twice.
               */
              modifier initializer() {
                  require(_initializing || !_initialized, "Initializable: contract is already initialized");
                  bool isTopLevelCall = !_initializing;
                  if (isTopLevelCall) {
                      _initializing = true;
                      _initialized = true;
                  }
                  _;
                  if (isTopLevelCall) {
                      _initializing = false;
                  }
              }
          }
          // 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 IERC721ReceiverUpgradeable {
              /**
               * @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);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "../../utils/introspection/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;
          }
          // 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 2 of 6: InitializedProxy
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.5;
          /**
           * @title InitializedProxy
           * @author Anna Carroll
           */
          contract InitializedProxy {
              // address of logic contract
              address public immutable logic;
              // ======== Constructor =========
              constructor(
                  address _logic,
                  bytes memory _initializationCalldata
              ) {
                  logic = _logic;
                  // Delegatecall into the logic contract, supplying initialization calldata
                  (bool _ok, bytes memory returnData) =
                      _logic.delegatecall(_initializationCalldata);
                  // Revert if delegatecall to implementation reverts
                  require(_ok, string(returnData));
              }
              // ======== Fallback =========
              fallback() external payable {
                  address _impl = logic;
                  assembly {
                      let ptr := mload(0x40)
                      calldatacopy(ptr, 0, calldatasize())
                      let result := delegatecall(gas(), _impl, ptr, calldatasize(), 0, 0)
                      let size := returndatasize()
                      returndatacopy(ptr, 0, size)
                      switch result
                          case 0 {
                              revert(ptr, size)
                          }
                          default {
                              return(ptr, size)
                          }
                  }
              }
              // ======== Receive =========
              receive() external payable {} // solhint-disable-line no-empty-blocks
          }
          

          File 3 of 6: PartyBid
          /*
                ___           ___           ___           ___           ___           ___                       ___
               /\\  \\         /\\  \\         /\\  \\         /\\  \\         |\\__\\         /\\  \\          ___        /\\  \\
              /::\\  \\       /::\\  \\       /::\\  \\        \\:\\  \\        |:|  |       /::\\  \\        /\\  \\      /::\\  \\
             /:/\\:\\  \\     /:/\\:\\  \\     /:/\\:\\  \\        \\:\\  \\       |:|  |      /:/\\:\\  \\       \\:\\  \\    /:/\\:\\  \\
            /::\\~\\:\\  \\   /::\\~\\:\\  \\   /::\\~\\:\\  \\       /::\\  \\      |:|__|__   /::\\~\\:\\__\\      /::\\__\\  /:/  \\:\\__\\
           /:/\\:\\ \\:\\__\\ /:/\\:\\ \\:\\__\\ /:/\\:\\ \\:\\__\\     /:/\\:\\__\\     /::::\\__\\ /:/\\:\\ \\:|__|  __/:/\\/__/ /:/__/ \\:|__|
           \\/__\\:\\/:/  / \\/__\\:\\/:/  / \\/_|::\\/:/  /    /:/  \\/__/    /:/~~/~    \\:\\~\\:\\/:/  / /\\/:/  /    \\:\\  \\ /:/  /
                \\::/  /       \\::/  /     |:|::/  /    /:/  /        /:/  /       \\:\\ \\::/  /  \\::/__/      \\:\\  /:/  /
                 \\/__/        /:/  /      |:|\\/__/     \\/__/         \\/__/         \\:\\/:/  /    \\:\\__\\       \\:\\/:/  /
                             /:/  /       |:|  |                                    \\::/__/      \\/__/        \\::/__/
                             \\/__/         \\|__|                                     ~~                        ~~
          Anna Carroll for PartyDAO
          */
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.5;
          // ============ Internal Imports ============
          import {Party} from "./Party.sol";
          import {IMarketWrapper} from "./market-wrapper/IMarketWrapper.sol";
          import {Structs} from "./Structs.sol";
          contract PartyBid is Party {
              // partyStatus Transitions:
              //   (1) PartyStatus.ACTIVE on deploy
              //   (2) PartyStatus.WON or PartyStatus.LOST on finalize()
              // ============ Internal Constants ============
              // PartyBid version 3
              uint16 public constant VERSION = 3;
              // ============ Public Not-Mutated Storage ============
              // market wrapper contract exposing interface for
              // market auctioning the NFT
              IMarketWrapper public marketWrapper;
              // ID of auction within market contract
              uint256 public auctionId;
              // ============ Public Mutable Storage ============
              // the highest bid submitted by PartyBid
              uint256 public highestBid;
              // ============ Events ============
              event Bid(uint256 amount);
              event Finalized(PartyStatus result, uint256 totalSpent, uint256 fee, uint256 totalContributed);
              // ======== Constructor =========
              constructor(
                  address _partyDAOMultisig,
                  address _tokenVaultFactory,
                  address _weth
              ) Party(_partyDAOMultisig, _tokenVaultFactory, _weth) {}
              // ======== Initializer =========
              function initialize(
                  address _marketWrapper,
                  address _nftContract,
                  uint256 _tokenId,
                  uint256 _auctionId,
                  Structs.AddressAndAmount calldata _split,
                  Structs.AddressAndAmount calldata _tokenGate,
                  string memory _name,
                  string memory _symbol
              ) external initializer {
                  // validate auction exists
                  require(
                      IMarketWrapper(_marketWrapper).auctionIdMatchesToken(
                          _auctionId,
                          _nftContract,
                          _tokenId
                      ),
                      "PartyBid::initialize: auctionId doesn't match token"
                  );
                  // initialize & validate shared Party variables
                  __Party_init(_nftContract, _tokenId, _split, _tokenGate, _name, _symbol);
                  // set PartyBid-specific state variables
                  marketWrapper = IMarketWrapper(_marketWrapper);
                  auctionId = _auctionId;
              }
              // ======== External: Contribute =========
              /**
               * @notice Contribute to the Party's treasury
               * while the Party is still active
               * @dev Emits a Contributed event upon success; callable by anyone
               */
              function contribute() external payable nonReentrant {
                  _contribute();
              }
              // ======== External: Bid =========
              /**
               * @notice Submit a bid to the Market
               * @dev Reverts if insufficient funds to place the bid and pay PartyDAO fees,
               * or if any external auction checks fail (including if PartyBid is current high bidder)
               * Emits a Bid event upon success.
               * Callable by any contributor
               */
              function bid() external nonReentrant {
                  require(
                      partyStatus == PartyStatus.ACTIVE,
                      "PartyBid::bid: auction not active"
                  );
                  require(
                      totalContributed[msg.sender] > 0,
                      "PartyBid::bid: only contributors can bid"
                  );
                  require(
                      address(this) !=
                          marketWrapper.getCurrentHighestBidder(
                              auctionId
                          ),
                      "PartyBid::bid: already highest bidder"
                  );
                  require(
                      !marketWrapper.isFinalized(auctionId),
                      "PartyBid::bid: auction already finalized"
                  );
                  // get the minimum next bid for the auction
                  uint256 _bid = marketWrapper.getMinimumBid(auctionId);
                  // ensure there is enough ETH to place the bid including PartyDAO fee
                  require(
                      _bid <= getMaximumBid(),
                      "PartyBid::bid: insufficient funds to bid"
                  );
                  // submit bid to Auction contract using delegatecall
                  (bool success, bytes memory returnData) =
                      address(marketWrapper).delegatecall(
                          abi.encodeWithSignature("bid(uint256,uint256)", auctionId, _bid)
                      );
                  require(
                      success,
                      string(
                          abi.encodePacked(
                              "PartyBid::bid: place bid failed: ",
                              returnData
                          )
                      )
                  );
                  // update highest bid submitted & emit success event
                  highestBid = _bid;
                  emit Bid(_bid);
              }
              // ======== External: Finalize =========
              /**
               * @notice Finalize the state of the auction
               * @dev Emits a Finalized event upon success; callable by anyone
               */
              function finalize() external nonReentrant {
                  require(
                      partyStatus == PartyStatus.ACTIVE,
                      "PartyBid::finalize: auction not active"
                  );
                  // finalize auction if it hasn't already been done
                  if (!marketWrapper.isFinalized(auctionId)) {
                      marketWrapper.finalize(auctionId);
                  }
                  // after the auction has been finalized,
                  // if the NFT is owned by the PartyBid, then the PartyBid won the auction
                  address _owner = _getOwner();
                  partyStatus = _owner == address(this) ? PartyStatus.WON : PartyStatus.LOST;
                  uint256 _ethFee;
                  // if the auction was won,
                  if (partyStatus == PartyStatus.WON) {
                      // record totalSpent,
                      // send ETH fees to PartyDAO,
                      // fractionalize the Token
                      // send Token fees to PartyDAO & split proceeds to split recipient
                      _ethFee = _closeSuccessfulParty(highestBid);
                  }
                  // set the contract status & emit result
                  emit Finalized(partyStatus, totalSpent, _ethFee, totalContributedToParty);
              }
              // ======== Public: Utility Calculations =========
              /**
               * @notice The maximum bid that can be submitted
               * while paying the ETH fee to PartyDAO
               * @return _maxBid the maximum bid
               */
              function getMaximumBid() public view returns (uint256 _maxBid) {
                  _maxBid = getMaximumSpend();
              }
          }
          /*
          __/\\\\\\\\\\\\\\\\\\\\\\\\\\_____________________________________________________________/\\\\\\\\\\\\\\\\\\\\\\\\________/\\\\\\\\\\\\\\\\\\__________/\\\\\\\\\\______
           _\\/\\\\\\/////////\\\\\\__________________________________________________________\\/\\\\\\////////\\\\\\____/\\\\\\\\\\\\\\\\\\\\\\\\\\______/\\\\\\///\\\\\\____
            _\\/\\\\\\_______\\/\\\\\\__________________________________/\\\\\\_________/\\\\\\__/\\\\\\_\\/\\\\\\______\\//\\\\\\__/\\\\\\/////////\\\\\\___/\\\\\\/__\\///\\\\\\__
             _\\/\\\\\\\\\\\\\\\\\\\\\\\\\\/___/\\\\\\\\\\\\\\\\\\_____/\\\\/\\\\\\\\\\\\\\___/\\\\\\\\\\\\\\\\\\\\\\___\\//\\\\\\/\\\\\\__\\/\\\\\\_______\\/\\\\\\_\\/\\\\\\_______\\/\\\\\\__/\\\\\\______\\//\\\\\\_
              _\\/\\\\\\/////////____\\////////\\\\\\___\\/\\\\\\/////\\\\\\_\\////\\\\\\////_____\\//\\\\\\\\\\___\\/\\\\\\_______\\/\\\\\\_\\/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\_\\/\\\\\\_______\\/\\\\\\_
               _\\/\\\\\\_______________/\\\\\\\\\\\\\\\\\\\\__\\/\\\\\\___\\///_____\\/\\\\\\__________\\//\\\\\\____\\/\\\\\\_______\\/\\\\\\_\\/\\\\\\/////////\\\\\\_\\//\\\\\\______/\\\\\\__
                _\\/\\\\\\______________/\\\\\\/////\\\\\\__\\/\\\\\\____________\\/\\\\\\_/\\\\___/\\\\_/\\\\\\_____\\/\\\\\\_______/\\\\\\__\\/\\\\\\_______\\/\\\\\\__\\///\\\\\\__/\\\\\\____
                 _\\/\\\\\\_____________\\//\\\\\\\\\\\\\\\\/\\\\_\\/\\\\\\____________\\//\\\\\\\\\\___\\//\\\\\\\\/______\\/\\\\\\\\\\\\\\\\\\\\\\\\/___\\/\\\\\\_______\\/\\\\\\____\\///\\\\\\\\\\/_____
                  _\\///_______________\\////////\\//__\\///______________\\/////_____\\////________\\////////////_____\\///________\\///_______\\/////_______
          Anna Carroll for PartyDAO
          */
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.5;
          // ============ External Imports: Inherited Contracts ============
          // NOTE: we inherit from OpenZeppelin upgradeable contracts
          // because of the proxy structure used for cheaper deploys
          // (the proxies are NOT actually upgradeable)
          import {
          ReentrancyGuardUpgradeable
          } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
          import {
          ERC721HolderUpgradeable
          } from "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol";
          // ============ External Imports: External Contracts & Contract Interfaces ============
          import {
          IERC721VaultFactory
          } from "./external/interfaces/IERC721VaultFactory.sol";
          import {ITokenVault} from "./external/interfaces/ITokenVault.sol";
          import {IWETH} from "./external/interfaces/IWETH.sol";
          import {
          IERC721Metadata
          } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
          import {
          IERC20
          } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
          // ============ Internal Imports ============
          import {Structs} from "./Structs.sol";
          contract Party is ReentrancyGuardUpgradeable, ERC721HolderUpgradeable {
              // ============ Enums ============
              // State Transitions:
              //   (1) ACTIVE on deploy
              //   (2) WON if the Party has won the token
              //   (2) LOST if the Party is over & did not win the token
              enum PartyStatus {ACTIVE, WON, LOST}
              // ============ Structs ============
              struct Contribution {
                  uint256 amount;
                  uint256 previousTotalContributedToParty;
              }
              // ============ Internal Constants ============
              // tokens are minted at a rate of 1 ETH : 1000 tokens
              uint16 internal constant TOKEN_SCALE = 1000;
              // PartyDAO receives an ETH fee equal to 2.5% of the amount spent
              uint16 internal constant ETH_FEE_BASIS_POINTS = 250;
              // PartyDAO receives a token fee equal to 2.5% of the total token supply
              uint16 internal constant TOKEN_FEE_BASIS_POINTS = 250;
              // token is relisted on Fractional with an
              // initial reserve price equal to 2x the price of the token
              uint8 internal constant RESALE_MULTIPLIER = 2;
              // ============ Immutables ============
              address public immutable partyFactory;
              address public immutable partyDAOMultisig;
              IERC721VaultFactory public immutable tokenVaultFactory;
              IWETH public immutable weth;
              // ============ Public Not-Mutated Storage ============
              // NFT contract
              IERC721Metadata public nftContract;
              // ID of token within NFT contract
              uint256 public tokenId;
              // Fractionalized NFT vault responsible for post-purchase experience
              ITokenVault public tokenVault;
              // the address that will receive a portion of the tokens
              // if the Party successfully buys the token
              address public splitRecipient;
              // percent of the total token supply
              // taken by the splitRecipient
              uint256 public splitBasisPoints;
              // address of token that users need to hold to contribute
              // address(0) if party is not token gated
              IERC20 public gatedToken;
              // amount of token that users need to hold to contribute
              // 0 if party is not token gated
              uint256 public gatedTokenAmount;
              // ERC-20 name and symbol for fractional tokens
              string public name;
              string public symbol;
              // ============ Public Mutable Storage ============
              // state of the contract
              PartyStatus public partyStatus;
              // total ETH deposited by all contributors
              uint256 public totalContributedToParty;
              // the total spent buying the token;
              // 0 if the NFT is not won; price of token + 2.5% PartyDAO fee if NFT is won
              uint256 public totalSpent;
              // contributor => array of Contributions
              mapping(address => Contribution[]) public contributions;
              // contributor => total amount contributed
              mapping(address => uint256) public totalContributed;
              // contributor => true if contribution has been claimed
              mapping(address => bool) public claimed;
              // ============ Events ============
              event Contributed(
                  address indexed contributor,
                  uint256 amount,
                  uint256 previousTotalContributedToParty,
                  uint256 totalFromContributor
              );
              event Claimed(
                  address indexed contributor,
                  uint256 totalContributed,
                  uint256 excessContribution,
                  uint256 tokenAmount
              );
              // ======== Modifiers =========
              modifier onlyPartyDAO() {
                  require(
                      msg.sender == partyDAOMultisig,
                      "Party:: only PartyDAO multisig"
                  );
                  _;
              }
              // ======== Constructor =========
              constructor(
                  address _partyDAOMultisig,
                  address _tokenVaultFactory,
                  address _weth
              ) {
                  partyFactory = msg.sender;
                  partyDAOMultisig = _partyDAOMultisig;
                  tokenVaultFactory = IERC721VaultFactory(_tokenVaultFactory);
                  weth = IWETH(_weth);
              }
              // ======== Internal: Initialize =========
              function __Party_init(
                  address _nftContract,
                  uint256 _tokenId,
                  Structs.AddressAndAmount calldata _split,
                  Structs.AddressAndAmount calldata _tokenGate,
                  string memory _name,
                  string memory _symbol
              ) internal {
                  require(msg.sender == partyFactory, "Party::__Party_init: only factory can init");
                  // validate token exists (must set nftContract & tokenId before _getOwner)
                  nftContract = IERC721Metadata(_nftContract);
                  tokenId = _tokenId;
                  require(_getOwner() != address(0), "Party::__Party_init: NFT getOwner failed");
                  // if split is non-zero,
                  if (_split.addr != address(0) && _split.amount != 0) {
                      // validate that party split won't retain the total token supply
                      uint256 _remainingBasisPoints = 10000 - TOKEN_FEE_BASIS_POINTS;
                      require(_split.amount < _remainingBasisPoints, "Party::__Party_init: basis points can't take 100%");
                      splitBasisPoints = _split.amount;
                      splitRecipient = _split.addr;
                  }
                  // if token gating is non-zero
                  if (_tokenGate.addr != address(0) && _tokenGate.amount != 0) {
                      // call totalSupply to verify that address is ERC-20 token contract
                      IERC20(_tokenGate.addr).totalSupply();
                      gatedToken = IERC20(_tokenGate.addr);
                      gatedTokenAmount = _tokenGate.amount;
                  }
                  // initialize ReentrancyGuard and ERC721Holder
                  __ReentrancyGuard_init();
                  __ERC721Holder_init();
                  // set storage variables
                  name = _name;
                  symbol = _symbol;
              }
              // ======== Internal: Contribute =========
              /**
               * @notice Contribute to the Party's treasury
               * while the Party is still active
               * @dev Emits a Contributed event upon success; callable by anyone
               */
              function _contribute() internal {
                  require(
                      partyStatus == PartyStatus.ACTIVE,
                      "Party::contribute: party not active"
                  );
                  address _contributor = msg.sender;
                  uint256 _amount = msg.value;
                  // if token gated, require that contributor has balance of gated tokens
                  if (address(gatedToken) != address(0)) {
                      require(gatedToken.balanceOf(_contributor) >= gatedTokenAmount, "Party::contribute: must hold tokens to contribute");
                  }
                  require(_amount > 0, "Party::contribute: must contribute more than 0");
                  // get the current contract balance
                  uint256 _previousTotalContributedToParty = totalContributedToParty;
                  // add contribution to contributor's array of contributions
                  Contribution memory _contribution =
                      Contribution({
                          amount: _amount,
                          previousTotalContributedToParty: _previousTotalContributedToParty
                      });
                  contributions[_contributor].push(_contribution);
                  // add to contributor's total contribution
                  totalContributed[_contributor] = totalContributed[_contributor] + _amount;
                  // add to party's total contribution & emit event
                  totalContributedToParty = _previousTotalContributedToParty + _amount;
                  emit Contributed(
                      _contributor,
                      _amount,
                      _previousTotalContributedToParty,
                      totalContributed[_contributor]
                  );
              }
              // ======== External: Claim =========
              /**
               * @notice Claim the tokens and excess ETH owed
               * to a single contributor after the party has ended
               * @dev Emits a Claimed event upon success
               * callable by anyone (doesn't have to be the contributor)
               * @param _contributor the address of the contributor
               */
              function claim(address _contributor) external nonReentrant {
                  // ensure party has finalized
                  require(
                      partyStatus != PartyStatus.ACTIVE,
                      "Party::claim: party not finalized"
                  );
                  // ensure contributor submitted some ETH
                  require(
                      totalContributed[_contributor] != 0,
                      "Party::claim: not a contributor"
                  );
                  // ensure the contributor hasn't already claimed
                  require(
                      !claimed[_contributor],
                      "Party::claim: contribution already claimed"
                  );
                  // mark the contribution as claimed
                  claimed[_contributor] = true;
                  // calculate the amount of fractional NFT tokens owed to the user
                  // based on how much ETH they contributed towards the party,
                  // and the amount of excess ETH owed to the user
                  (uint256 _tokenAmount, uint256 _ethAmount) =
                  getClaimAmounts(_contributor);
                  // transfer tokens to contributor for their portion of ETH used
                  _transferTokens(_contributor, _tokenAmount);
                  // if there is excess ETH, send it back to the contributor
                  _transferETHOrWETH(_contributor, _ethAmount);
                  emit Claimed(
                      _contributor,
                      totalContributed[_contributor],
                      _ethAmount,
                      _tokenAmount
                  );
              }
              // ======== External: Emergency Escape Hatches (PartyDAO Multisig Only) =========
              /**
               * @notice Escape hatch: in case of emergency,
               * PartyDAO can use emergencyWithdrawEth to withdraw
               * ETH stuck in the contract
               */
              function emergencyWithdrawEth(uint256 _value)
                  external
                  onlyPartyDAO
              {
                  _transferETHOrWETH(partyDAOMultisig, _value);
              }
              /**
               * @notice Escape hatch: in case of emergency,
               * PartyDAO can use emergencyCall to call an external contract
               * (e.g. to withdraw a stuck NFT or stuck ERC-20s)
               */
              function emergencyCall(address _contract, bytes memory _calldata)
                  external
                  onlyPartyDAO
                  returns (bool _success, bytes memory _returnData)
              {
                  (_success, _returnData) = _contract.call(_calldata);
                  require(_success, string(_returnData));
              }
              /**
               * @notice Escape hatch: in case of emergency,
               * PartyDAO can force the Party to finalize with status LOST
               * (e.g. if finalize is not callable)
               */
              function emergencyForceLost()
                  external
                  onlyPartyDAO
              {
                  // set partyStatus to LOST
                  partyStatus = PartyStatus.LOST;
              }
              // ======== Public: Utility Calculations =========
              /**
               * @notice Convert ETH value to equivalent token amount
               */
              function valueToTokens(uint256 _value)
                  public
                  pure
                  returns (uint256 _tokens)
              {
                  _tokens = _value * TOKEN_SCALE;
              }
              /**
               * @notice The maximum amount that can be spent by the Party
               * while paying the ETH fee to PartyDAO
               * @return _maxSpend the maximum spend
               */
              function getMaximumSpend() public view returns (uint256 _maxSpend) {
                  _maxSpend = (totalContributedToParty * 10000) / (10000 + ETH_FEE_BASIS_POINTS);
              }
              /**
               * @notice Calculate the amount of fractional NFT tokens owed to the contributor
               * based on how much ETH they contributed towards buying the token,
               * and the amount of excess ETH owed to the contributor
               * based on how much ETH they contributed *not* used towards buying the token
               * @param _contributor the address of the contributor
               * @return _tokenAmount the amount of fractional NFT tokens owed to the contributor
               * @return _ethAmount the amount of excess ETH owed to the contributor
               */
              function getClaimAmounts(address _contributor)
                  public
                  view
                  returns (uint256 _tokenAmount, uint256 _ethAmount)
              {
                  require(partyStatus != PartyStatus.ACTIVE, "Party::getClaimAmounts: party still active; amounts undetermined");
                  uint256 _totalContributed = totalContributed[_contributor];
                  if (partyStatus == PartyStatus.WON) {
                      // calculate the amount of this contributor's ETH
                      // that was used to buy the token
                      uint256 _totalEthUsed = totalEthUsed(_contributor);
                      if (_totalEthUsed > 0) {
                          _tokenAmount = valueToTokens(_totalEthUsed);
                      }
                      // the rest of the contributor's ETH should be returned
                      _ethAmount = _totalContributed - _totalEthUsed;
                  } else {
                      // if the token wasn't bought, no ETH was spent;
                      // all of the contributor's ETH should be returned
                      _ethAmount = _totalContributed;
                  }
              }
              /**
               * @notice Calculate the total amount of a contributor's funds
               * that were used towards the buying the token
               * @dev always returns 0 until the party has been finalized
               * @param _contributor the address of the contributor
               * @return _total the sum of the contributor's funds that were
               * used towards buying the token
               */
              function totalEthUsed(address _contributor)
                  public
                  view
                  returns (uint256 _total)
              {
                  require(partyStatus != PartyStatus.ACTIVE, "Party::totalEthUsed: party still active; amounts undetermined");
                  // load total amount spent once from storage
                  uint256 _totalSpent = totalSpent;
                  // get all of the contributor's contributions
                  Contribution[] memory _contributions = contributions[_contributor];
                  for (uint256 i = 0; i < _contributions.length; i++) {
                      // calculate how much was used from this individual contribution
                      uint256 _amount = _ethUsed(_totalSpent, _contributions[i]);
                      // if we reach a contribution that was not used,
                      // no subsequent contributions will have been used either,
                      // so we can stop calculating to save some gas
                      if (_amount == 0) break;
                      _total = _total + _amount;
                  }
              }
              // ============ Internal ============
              function _closeSuccessfulParty(uint256 _nftCost) internal returns (uint256 _ethFee) {
                  // calculate PartyDAO fee & record total spent
                  _ethFee = _getEthFee(_nftCost);
                  totalSpent = _nftCost + _ethFee;
                  // transfer ETH fee to PartyDAO
                  _transferETHOrWETH(partyDAOMultisig, _ethFee);
                  // deploy fractionalized NFT vault
                  // and mint fractional ERC-20 tokens
                  _fractionalizeNFT(_nftCost);
              }
              /**
               * @notice Calculate ETH fee for PartyDAO
               * NOTE: Remove this fee causes a critical vulnerability
               * allowing anyone to exploit a Party via price manipulation.
               * See Security Review in README for more info.
               * @return _fee the portion of _amount represented by scaling to ETH_FEE_BASIS_POINTS
               */
              function _getEthFee(uint256 _amount) internal pure returns (uint256 _fee) {
                  _fee = (_amount * ETH_FEE_BASIS_POINTS) / 10000;
              }
              /**
               * @notice Calculate token amount for specified token recipient
               * @return _totalSupply the total token supply
               * @return _partyDAOAmount the amount of tokens for partyDAO fee,
               * which is equivalent to TOKEN_FEE_BASIS_POINTS of total supply
               * @return _splitRecipientAmount the amount of tokens for the token recipient,
               * which is equivalent to splitBasisPoints of total supply
               */
              function _getTokenInflationAmounts(uint256 _amountSpent)
                  internal
                  view
                  returns (uint256 _totalSupply, uint256 _partyDAOAmount, uint256 _splitRecipientAmount)
              {
                  // the token supply will be inflated to provide a portion of the
                  // total supply for PartyDAO, and a portion for the splitRecipient
                  uint256 inflationBasisPoints = TOKEN_FEE_BASIS_POINTS + splitBasisPoints;
                  _totalSupply = valueToTokens((_amountSpent * 10000) / (10000 - inflationBasisPoints));
                  // PartyDAO receives TOKEN_FEE_BASIS_POINTS of the total supply
                  _partyDAOAmount = (_totalSupply * TOKEN_FEE_BASIS_POINTS) / 10000;
                  // splitRecipient receives splitBasisPoints of the total supply
                  _splitRecipientAmount = (_totalSupply * splitBasisPoints) / 10000;
              }
              /**
              * @notice Query the NFT contract to get the token owner
              * @dev nftContract must implement the ERC-721 token standard exactly:
              * function ownerOf(uint256 _tokenId) external view returns (address);
              * See https://eips.ethereum.org/EIPS/eip-721
              * @dev Returns address(0) if NFT token or NFT contract
              * no longer exists (token burned or contract self-destructed)
              * @return _owner the owner of the NFT
              */
              function _getOwner() internal view returns (address _owner) {
                  (bool _success, bytes memory _returnData) =
                      address(nftContract).staticcall(
                          abi.encodeWithSignature(
                              "ownerOf(uint256)",
                              tokenId
                          )
                  );
                  if (_success && _returnData.length > 0) {
                      _owner = abi.decode(_returnData, (address));
                  }
              }
              /**
               * @notice Upon winning the token, transfer the NFT
               * to fractional.art vault & mint fractional ERC-20 tokens
               */
              function _fractionalizeNFT(uint256 _amountSpent) internal {
                  // approve fractionalized NFT Factory to withdraw NFT
                  nftContract.approve(address(tokenVaultFactory), tokenId);
                  // Party "votes" for a reserve price on Fractional
                  // equal to 2x the price of the token
                  uint256 _listPrice = RESALE_MULTIPLIER * _amountSpent;
                  // users receive tokens at a rate of 1:TOKEN_SCALE for each ETH they contributed that was ultimately spent
                  // partyDAO receives a percentage of the total token supply equivalent to TOKEN_FEE_BASIS_POINTS
                  // splitRecipient receives a percentage of the total token supply equivalent to splitBasisPoints
                  (uint256 _tokenSupply, uint256 _partyDAOAmount, uint256 _splitRecipientAmount) = _getTokenInflationAmounts(totalSpent);
                  // deploy fractionalized NFT vault
                  uint256 vaultNumber =
                      tokenVaultFactory.mint(
                          name,
                          symbol,
                          address(nftContract),
                          tokenId,
                          _tokenSupply,
                          _listPrice,
                          0
                      );
                  // store token vault address to storage
                  tokenVault = ITokenVault(tokenVaultFactory.vaults(vaultNumber));
                  // transfer curator to null address (burn the curator role)
                  tokenVault.updateCurator(address(0));
                  // transfer tokens to PartyDAO multisig
                  _transferTokens(partyDAOMultisig, _partyDAOAmount);
                  // transfer tokens to token recipient
                  if (splitRecipient != address(0)) {
                      _transferTokens(splitRecipient, _splitRecipientAmount);
                  }
              }
              // ============ Internal: Claim ============
              /**
               * @notice Calculate the amount of a single Contribution
               * that was used towards buying the token
               * @param _contribution the Contribution struct
               * @return the amount of funds from this contribution
               * that were used towards buying the token
               */
              function _ethUsed(uint256 _totalSpent, Contribution memory _contribution)
                  internal
                  pure
                  returns (uint256)
              {
                  if (
                      _contribution.previousTotalContributedToParty +
                      _contribution.amount <=
                      _totalSpent
                  ) {
                      // contribution was fully used
                      return _contribution.amount;
                  } else if (
                      _contribution.previousTotalContributedToParty < _totalSpent
                  ) {
                      // contribution was partially used
                      return _totalSpent - _contribution.previousTotalContributedToParty;
                  }
                  // contribution was not used
                  return 0;
              }
              // ============ Internal: TransferTokens ============
              /**
              * @notice Transfer tokens to a recipient
              * @param _to recipient of tokens
              * @param _value amount of tokens
              */
              function _transferTokens(address _to, uint256 _value) internal {
                  // skip if attempting to send 0 tokens
                  if (_value == 0) {
                      return;
                  }
                  // guard against rounding errors;
                  // if token amount to send is greater than contract balance,
                  // send full contract balance
                  uint256 _partyBalance = tokenVault.balanceOf(address(this));
                  if (_value > _partyBalance) {
                      _value = _partyBalance;
                  }
                  tokenVault.transfer(_to, _value);
              }
              // ============ Internal: TransferEthOrWeth ============
              /**
               * @notice Attempt to transfer ETH to a recipient;
               * if transferring ETH fails, transfer WETH insteads
               * @param _to recipient of ETH or WETH
               * @param _value amount of ETH or WETH
               */
              function _transferETHOrWETH(address _to, uint256 _value) internal {
                  // skip if attempting to send 0 ETH
                  if (_value == 0) {
                      return;
                  }
                  // guard against rounding errors;
                  // if ETH amount to send is greater than contract balance,
                  // send full contract balance
                  if (_value > address(this).balance) {
                      _value = address(this).balance;
                  }
                  // Try to transfer ETH to the given recipient.
                  if (!_attemptETHTransfer(_to, _value)) {
                      // If the transfer fails, wrap and send as WETH
                      weth.deposit{value: _value}();
                      weth.transfer(_to, _value);
                      // At this point, the recipient can unwrap WETH.
                  }
              }
              /**
               * @notice Attempt to transfer ETH to a recipient
               * @dev Sending ETH is not guaranteed to succeed
               * this method will return false if it fails.
               * We will limit the gas used in transfers, and handle failure cases.
               * @param _to recipient of ETH
               * @param _value amount of ETH
               */
              function _attemptETHTransfer(address _to, uint256 _value)
                  internal
                  returns (bool)
              {
                  // Here increase the gas limit a reasonable amount above the default, and try
                  // to send ETH to the recipient.
                  // NOTE: This might allow the recipient to attempt a limited reentrancy attack.
                  (bool success, ) = _to.call{value: _value, gas: 30000}("");
                  return success;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.5;
          /**
           * @title IMarketWrapper
           * @author Anna Carroll
           * @notice IMarketWrapper provides a common interface for
           * interacting with NFT auction markets.
           * Contracts can abstract their interactions with
           * different NFT markets using IMarketWrapper.
           * NFT markets can become compatible with any contract
           * using IMarketWrapper by deploying a MarketWrapper contract
           * that implements this interface using the logic of their Market.
           *
           * WARNING: MarketWrapper contracts should NEVER write to storage!
           * When implementing a MarketWrapper, exercise caution; a poorly implemented
           * MarketWrapper contract could permanently lose access to the NFT or user funds.
           */
          interface IMarketWrapper {
              /**
               * @notice Given the auctionId, nftContract, and tokenId, check that:
               * 1. the auction ID matches the token
               * referred to by tokenId + nftContract
               * 2. the auctionId refers to an *ACTIVE* auction
               * (e.g. an auction that will accept bids)
               * within this market contract
               * 3. any additional validation to ensure that
               * a PartyBid can bid on this auction
               * (ex: if the market allows arbitrary bidding currencies,
               * check that the auction currency is ETH)
               * Note: This function probably should have been named "isValidAuction"
               * @dev Called in PartyBid.sol in `initialize` at line 174
               * @return TRUE if the auction is valid
               */
              function auctionIdMatchesToken(
                  uint256 auctionId,
                  address nftContract,
                  uint256 tokenId
              ) external view returns (bool);
              /**
               * @notice Calculate the minimum next bid for this auction.
               * PartyBid contracts always submit the minimum possible
               * bid that will be accepted by the Market contract.
               * usually, this is either the reserve price (if there are no bids)
               * or a certain percentage increase above the current highest bid
               * @dev Called in PartyBid.sol in `bid` at line 251
               * @return minimum bid amount
               */
              function getMinimumBid(uint256 auctionId) external view returns (uint256);
              /**
               * @notice Query the current highest bidder for this auction
               * It is assumed that there is always 1 winning highest bidder for an auction
               * This is used to ensure that PartyBid cannot outbid itself if it is already winning
               * @dev Called in PartyBid.sol in `bid` at line 241
               * @return highest bidder
               */
              function getCurrentHighestBidder(uint256 auctionId)
                  external
                  view
                  returns (address);
              /**
               * @notice Submit bid to Market contract
               * @dev Called in PartyBid.sol in `bid` at line 259
               */
              function bid(uint256 auctionId, uint256 bidAmount) external;
              /**
               * @notice Determine whether the auction has been finalized
               * Used to check if it is still possible to bid
               * And to determine whether the PartyBid should finalize the auction
               * @dev Called in PartyBid.sol in `bid` at line 247
               * @dev and in `finalize` at line 288
               * @return TRUE if the auction has been finalized
               */
              function isFinalized(uint256 auctionId) external view returns (bool);
              /**
               * @notice Finalize the results of the auction
               * on the Market contract
               * It is assumed  that this operation is performed once for each auction,
               * that after it is done the auction is over and the NFT has been
               * transferred to the auction winner.
               * @dev Called in PartyBid.sol in `finalize` at line 289
               */
              function finalize(uint256 auctionId) external;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.5;
          interface Structs {
              struct AddressAndAmount {
                  address addr;
                  uint256 amount;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "../proxy/utils/Initializable.sol";
          /**
           * @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 ReentrancyGuardUpgradeable is Initializable {
              // 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;
              function __ReentrancyGuard_init() internal initializer {
                  __ReentrancyGuard_init_unchained();
              }
              function __ReentrancyGuard_init_unchained() internal initializer {
                  _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;
              }
              uint256[49] private __gap;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "../IERC721ReceiverUpgradeable.sol";
          import "../../../proxy/utils/Initializable.sol";
            /**
             * @dev Implementation of the {IERC721Receiver} interface.
             *
             * Accepts all token transfers.
             * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
             */
          contract ERC721HolderUpgradeable is Initializable, IERC721ReceiverUpgradeable {
              function __ERC721Holder_init() internal initializer {
                  __ERC721Holder_init_unchained();
              }
              function __ERC721Holder_init_unchained() internal initializer {
              }
              /**
               * @dev See {IERC721Receiver-onERC721Received}.
               *
               * Always returns `IERC721Receiver.onERC721Received.selector`.
               */
              function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) {
                  return this.onERC721Received.selector;
              }
              uint256[50] private __gap;
          }
          //SPDX-License-Identifier: MIT
          pragma solidity 0.8.5;
          interface IERC721VaultFactory {
              /// @notice the mapping of vault number to vault address
              function vaults(uint256) external returns (address);
              /// @notice the function to mint a new vault
              /// @param _name the desired name of the vault
              /// @param _symbol the desired sumbol of the vault
              /// @param _token the ERC721 token address fo the NFT
              /// @param _id the uint256 ID of the token
              /// @param _listPrice the initial price of the NFT
              /// @return the ID of the vault
              function mint(string memory _name, string memory _symbol, address _token, uint256 _id, uint256 _supply, uint256 _listPrice, uint256 _fee) external returns(uint256);
          }
          //SPDX-License-Identifier: MIT
          pragma solidity 0.8.5;
          interface ITokenVault {
              /// @notice allow curator to update the curator address
              /// @param _curator the new curator
              function updateCurator(address _curator) external;
              /**
               * @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) external returns (bool);
              /**
               * @dev See {IERC20-balanceOf}.
               */
              function balanceOf(address account) external view returns (uint256);
          }// SPDX-License-Identifier: MIT
          pragma solidity 0.8.5;
          interface IWETH {
              function deposit() external payable;
              function transfer(address to, uint256 value) external returns (bool);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "../IERC721.sol";
          /**
           * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
           * @dev See https://eips.ethereum.org/EIPS/eip-721
           */
          interface IERC721Metadata is IERC721 {
              /**
               * @dev Returns the token collection name.
               */
              function name() external view returns (string memory);
              /**
               * @dev Returns the token collection symbol.
               */
              function symbol() external view returns (string memory);
              /**
               * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
               */
              function tokenURI(uint256 tokenId) external view returns (string memory);
          }
          // 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);
          }
          // SPDX-License-Identifier: MIT
          // solhint-disable-next-line compiler-version
          pragma solidity ^0.8.0;
          /**
           * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
           * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
           * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
           * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
           *
           * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
           * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
           *
           * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
           * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
           */
          abstract contract Initializable {
              /**
               * @dev Indicates that the contract has been initialized.
               */
              bool private _initialized;
              /**
               * @dev Indicates that the contract is in the process of being initialized.
               */
              bool private _initializing;
              /**
               * @dev Modifier to protect an initializer function from being invoked twice.
               */
              modifier initializer() {
                  require(_initializing || !_initialized, "Initializable: contract is already initialized");
                  bool isTopLevelCall = !_initializing;
                  if (isTopLevelCall) {
                      _initializing = true;
                      _initialized = true;
                  }
                  _;
                  if (isTopLevelCall) {
                      _initializing = false;
                  }
              }
          }
          // 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 IERC721ReceiverUpgradeable {
              /**
               * @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);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "../../utils/introspection/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;
          }
          // 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 4 of 6: ZoraMarketWrapper
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.5;
          // ============ External Imports ============
          import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol";
          import {IZoraAuctionHouse} from "../external/interfaces/IZoraAuctionHouse.sol";
          // ============ Internal Imports ============
          import {IMarketWrapper} from "./IMarketWrapper.sol";
          /**
           * @title ZoraMarketWrapper
           * @author Anna Carroll
           * @notice MarketWrapper contract implementing IMarketWrapper interface
           * according to the logic of Zora's Auction Houses
           * Original Zora Auction House code: https://github.com/ourzora/auction-house/blob/main/contracts/AuctionHouse.sol
           */
          contract ZoraMarketWrapper is IMarketWrapper {
              using SafeMath for uint256;
              // ============ Internal Immutables ============
              IZoraAuctionHouse internal immutable market;
              uint8 internal immutable minBidIncrementPercentage;
              // ======== Constructor =========
              constructor(address _zoraAuctionHouse) {
                  market = IZoraAuctionHouse(_zoraAuctionHouse);
                  minBidIncrementPercentage = IZoraAuctionHouse(_zoraAuctionHouse)
                      .minBidIncrementPercentage();
              }
              // ======== External Functions =========
              /**
               * @notice Determine whether there is an existing auction
               * for this token on the market
               * @return TRUE if the auction exists
               */
              function auctionExists(uint256 auctionId)
                  public
                  view
                  override
                  returns (bool)
              {
                  // line 375 of Zora Auction House, _exists() function (not exposed publicly)
                  IZoraAuctionHouse.Auction memory _auction = market.auctions(auctionId);
                  return _auction.tokenOwner != address(0);
              }
              /**
               * @notice Determine whether the given auctionId is
               * an auction for the tokenId + nftContract
               * @return TRUE if the auctionId matches the tokenId + nftContract
               */
              function auctionIdMatchesToken(
                  uint256 auctionId,
                  address nftContract,
                  uint256 tokenId
              ) public view override returns (bool) {
                  IZoraAuctionHouse.Auction memory _auction = market.auctions(auctionId);
                  return
                      _auction.tokenId == tokenId &&
                      _auction.tokenContract == nftContract &&
                      _auction.auctionCurrency == address(0);
              }
              /**
               * @notice Calculate the minimum next bid for this auction
               * @return minimum bid amount
               */
              function getMinimumBid(uint256 auctionId)
                  external
                  view
                  override
                  returns (uint256)
              {
                  // line 173 of Zora Auction House, calculation within createBid() function (calculation not exposed publicly)
                  IZoraAuctionHouse.Auction memory _auction = market.auctions(auctionId);
                  if (_auction.bidder == address(0)) {
                      // if there are NO bids, the minimum bid is the reserve price
                      return _auction.reservePrice;
                  } else {
                      // if there ARE bids, the minimum bid is the current bid plus the increment buffer
                      return _auction.amount.add(_auction.amount.mul(minBidIncrementPercentage).div(100));
                  }
              }
              /**
               * @notice Query the current highest bidder for this auction
               * @return highest bidder
               */
              function getCurrentHighestBidder(uint256 auctionId)
                  external
                  view
                  override
                  returns (address)
              {
                  // line 279 of NFTMarketReserveAuction, getMinBidAmount() function
                  IZoraAuctionHouse.Auction memory _auction = market.auctions(auctionId);
                  return _auction.bidder;
              }
              /**
               * @notice Submit bid to Market contract
               */
              function bid(uint256 auctionId, uint256 bidAmount) external override {
                  // line 153 of Zora Auction House, createBid() function
                  (bool success, bytes memory returnData) =
                      address(market).call{value: bidAmount}(
                          abi.encodeWithSignature(
                              "createBid(uint256,uint256)",
                              auctionId,
                              bidAmount
                          )
                      );
                  require(success, string(returnData));
              }
              /**
               * @notice Determine whether the auction has been finalized
               * @return TRUE if the auction has been finalized
               */
              function isFinalized(uint256 auctionId)
                  external
                  view
                  override
                  returns (bool)
              {
                  // line 302 of Zora Auction House,
                  // the auction is deleted at the end of the endAuction() function
                  // since we checked that the auction DID exist when we deployed the partyBid,
                  // if it no longer exists that means the auction has been finalized.
                  return !auctionExists(auctionId);
              }
              /**
               * @notice Finalize the results of the auction
               */
              function finalize(uint256 auctionId) external override {
                  // line 249 of Zora Auction House, endAuction() function
                  market.endAuction(auctionId);
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          // CAUTION
          // This version of SafeMath should only be used with Solidity 0.8 or later,
          // because it relies on the compiler's built in overflow checks.
          /**
           * @dev Wrappers over Solidity's arithmetic operations.
           *
           * NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler
           * now has built in overflow checking.
           */
          library SafeMath {
              /**
               * @dev Returns the addition of two unsigned integers, with an overflow flag.
               *
               * _Available since v3.4._
               */
              function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                  unchecked {
                      uint256 c = a + b;
                      if (c < a) return (false, 0);
                      return (true, c);
                  }
              }
              /**
               * @dev Returns the substraction of two unsigned integers, with an overflow flag.
               *
               * _Available since v3.4._
               */
              function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                  unchecked {
                      if (b > a) return (false, 0);
                      return (true, a - b);
                  }
              }
              /**
               * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
               *
               * _Available since v3.4._
               */
              function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                  unchecked {
                      // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                      // benefit is lost if 'b' is also tested.
                      // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                      if (a == 0) return (true, 0);
                      uint256 c = a * b;
                      if (c / a != b) return (false, 0);
                      return (true, c);
                  }
              }
              /**
               * @dev Returns the division of two unsigned integers, with a division by zero flag.
               *
               * _Available since v3.4._
               */
              function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                  unchecked {
                      if (b == 0) return (false, 0);
                      return (true, a / b);
                  }
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
               *
               * _Available since v3.4._
               */
              function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                  unchecked {
                      if (b == 0) return (false, 0);
                      return (true, a % b);
                  }
              }
              /**
               * @dev Returns the addition of two unsigned integers, reverting on
               * overflow.
               *
               * Counterpart to Solidity's `+` operator.
               *
               * Requirements:
               *
               * - Addition cannot overflow.
               */
              function add(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a + b;
              }
              /**
               * @dev Returns the subtraction of two unsigned integers, reverting on
               * overflow (when the result is negative).
               *
               * Counterpart to Solidity's `-` operator.
               *
               * Requirements:
               *
               * - Subtraction cannot overflow.
               */
              function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a - b;
              }
              /**
               * @dev Returns the multiplication of two unsigned integers, reverting on
               * overflow.
               *
               * Counterpart to Solidity's `*` operator.
               *
               * Requirements:
               *
               * - Multiplication cannot overflow.
               */
              function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a * b;
              }
              /**
               * @dev Returns the integer division of two unsigned integers, reverting on
               * division by zero. The result is rounded towards zero.
               *
               * Counterpart to Solidity's `/` operator.
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function div(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a / b;
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
               * reverting when dividing by zero.
               *
               * Counterpart to Solidity's `%` operator. This function uses a `revert`
               * opcode (which leaves remaining gas untouched) while Solidity uses an
               * invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a % b;
              }
              /**
               * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
               * overflow (when the result is negative).
               *
               * CAUTION: This function is deprecated because it requires allocating memory for the error
               * message unnecessarily. For custom revert reasons use {trySub}.
               *
               * Counterpart to Solidity's `-` operator.
               *
               * Requirements:
               *
               * - Subtraction cannot overflow.
               */
              function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  unchecked {
                      require(b <= a, errorMessage);
                      return a - b;
                  }
              }
              /**
               * @dev Returns the integer division of two unsigned integers, reverting with custom message on
               * division by zero. The result is rounded towards zero.
               *
               * Counterpart to Solidity's `%` operator. This function uses a `revert`
               * opcode (which leaves remaining gas untouched) while Solidity uses an
               * invalid opcode to revert (consuming all remaining gas).
               *
               * Counterpart to Solidity's `/` operator. Note: this function uses a
               * `revert` opcode (which leaves remaining gas untouched) while Solidity
               * uses an invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  unchecked {
                      require(b > 0, errorMessage);
                      return a / b;
                  }
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
               * reverting with custom message when dividing by zero.
               *
               * CAUTION: This function is deprecated because it requires allocating memory for the error
               * message unnecessarily. For custom revert reasons use {tryMod}.
               *
               * Counterpart to Solidity's `%` operator. This function uses a `revert`
               * opcode (which leaves remaining gas untouched) while Solidity uses an
               * invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  unchecked {
                      require(b > 0, errorMessage);
                      return a % b;
                  }
              }
          }
          // SPDX-License-Identifier: GPL-3.0
          // Reproduced from https://github.com/ourzora/auction-house/blob/main/contracts/interfaces/IAuctionHouse.sol under terms of GPL-3.0
          // Modified slightly
          pragma solidity 0.8.5;
          /**
           * @title Interface for Zora Auction Houses
           */
          interface IZoraAuctionHouse {
              struct Auction {
                  // ID for the ERC721 token
                  uint256 tokenId;
                  // Address for the ERC721 contract
                  address tokenContract;
                  // Whether or not the auction curator has approved the auction to start
                  bool approved;
                  // The current highest bid amount
                  uint256 amount;
                  // The length of time to run the auction for, after the first bid was made
                  uint256 duration;
                  // The time of the first bid
                  uint256 firstBidTime;
                  // The minimum price of the first bid
                  uint256 reservePrice;
                  // The sale percentage to send to the curator
                  uint8 curatorFeePercentage;
                  // The address that should receive the funds once the NFT is sold.
                  address tokenOwner;
                  // The address of the current highest bid
                  address payable bidder;
                  // The address of the auction's curator.
                  // The curator can reject or approve an auction
                  address payable curator;
                  // The address of the ERC-20 currency to run the auction with.
                  // If set to 0x0, the auction will be run in ETH
                  address auctionCurrency;
              }
              event AuctionCreated(
                  uint256 indexed auctionId,
                  uint256 indexed tokenId,
                  address indexed tokenContract,
                  uint256 duration,
                  uint256 reservePrice,
                  address tokenOwner,
                  address curator,
                  uint8 curatorFeePercentage,
                  address auctionCurrency
              );
              event AuctionApprovalUpdated(
                  uint256 indexed auctionId,
                  uint256 indexed tokenId,
                  address indexed tokenContract,
                  bool approved
              );
              event AuctionReservePriceUpdated(
                  uint256 indexed auctionId,
                  uint256 indexed tokenId,
                  address indexed tokenContract,
                  uint256 reservePrice
              );
              event AuctionBid(
                  uint256 indexed auctionId,
                  uint256 indexed tokenId,
                  address indexed tokenContract,
                  address sender,
                  uint256 value,
                  bool firstBid,
                  bool extended
              );
              event AuctionDurationExtended(
                  uint256 indexed auctionId,
                  uint256 indexed tokenId,
                  address indexed tokenContract,
                  uint256 duration
              );
              event AuctionEnded(
                  uint256 indexed auctionId,
                  uint256 indexed tokenId,
                  address indexed tokenContract,
                  address tokenOwner,
                  address curator,
                  address winner,
                  uint256 amount,
                  uint256 curatorFee,
                  address auctionCurrency
              );
              event AuctionCanceled(
                  uint256 indexed auctionId,
                  uint256 indexed tokenId,
                  address indexed tokenContract,
                  address tokenOwner
              );
              function createAuction(
                  uint256 tokenId,
                  address tokenContract,
                  uint256 duration,
                  uint256 reservePrice,
                  address payable curator,
                  uint8 curatorFeePercentages,
                  address auctionCurrency
              ) external returns (uint256);
              function auctions(uint256 auctionId) external view returns (Auction memory);
              function minBidIncrementPercentage() external view returns (uint8);
              function setAuctionApproval(uint256 auctionId, bool approved) external;
              function setAuctionReservePrice(uint256 auctionId, uint256 reservePrice)
                  external;
              function createBid(uint256 auctionId, uint256 amount) external payable;
              function endAuction(uint256 auctionId) external;
              function cancelAuction(uint256 auctionId) external;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.5;
          /**
           * @title IMarketWrapper
           * @author Anna Carroll
           * @notice IMarketWrapper provides a common interface for
           * interacting with NFT auction markets.
           * Contracts can abstract their interactions with
           * different NFT markets using IMarketWrapper.
           * NFT markets can become compatible with any contract
           * using IMarketWrapper by deploying a MarketWrapper contract
           * that implements this interface using the logic of their Market.
           *
           * WARNING: MarketWrapper contracts should NEVER write to storage!
           * When implementing a MarketWrapper, exercise caution; a poorly implemented
           * MarketWrapper contract could permanently lose access to the NFT or user funds.
           */
          interface IMarketWrapper {
              /**
               * @notice Determine whether there is an existing auction
               * for this token on the underlying market
               * @return TRUE if the auction exists
               */
              function auctionExists(uint256 auctionId) external view returns (bool);
              /**
               * @notice Determine whether the given auctionId is
               * an auction for the tokenId + nftContract
               * @return TRUE if the auctionId matches the tokenId + nftContract
               */
              function auctionIdMatchesToken(
                  uint256 auctionId,
                  address nftContract,
                  uint256 tokenId
              ) external view returns (bool);
              /**
               * @notice Calculate the minimum next bid for this auction
               * @return minimum bid amount
               */
              function getMinimumBid(uint256 auctionId) external view returns (uint256);
              /**
               * @notice Query the current highest bidder for this auction
               * @return highest bidder
               */
              function getCurrentHighestBidder(uint256 auctionId)
                  external
                  view
                  returns (address);
              /**
               * @notice Submit bid to Market contract
               */
              function bid(uint256 auctionId, uint256 bidAmount) external;
              /**
               * @notice Determine whether the auction has been finalized
               * @return TRUE if the auction has been finalized
               */
              function isFinalized(uint256 auctionId) external view returns (bool);
              /**
               * @notice Finalize the results of the auction
               */
              function finalize(uint256 auctionId) external;
          }
          

          File 5 of 6: AuctionHouse
          // SPDX-License-Identifier: GPL-3.0
          pragma solidity 0.6.8;
          pragma experimental ABIEncoderV2;
          import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
          import { IERC721, IERC165 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
          import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
          import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
          import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
          import {Counters} from "@openzeppelin/contracts/utils/Counters.sol";
          import { IMarket, Decimal } from "@zoralabs/core/dist/contracts/interfaces/IMarket.sol";
          import { IMedia } from "@zoralabs/core/dist/contracts/interfaces/IMedia.sol";
          import { IAuctionHouse } from "./interfaces/IAuctionHouse.sol";
          interface IWETH {
              function deposit() external payable;
              function withdraw(uint wad) external;
              function transfer(address to, uint256 value) external returns (bool);
          }
          interface IMediaExtended is IMedia {
              function marketContract() external returns(address);
          }
          /**
           * @title An open auction house, enabling collectors and curators to run their own auctions
           */
          contract AuctionHouse is IAuctionHouse, ReentrancyGuard {
              using SafeMath for uint256;
              using SafeERC20 for IERC20;
              using Counters for Counters.Counter;
              // The minimum amount of time left in an auction after a new bid is created
              uint256 public timeBuffer;
              // The minimum percentage difference between the last bid amount and the current bid.
              uint8 public minBidIncrementPercentage;
              // The address of the zora protocol to use via this contract
              address public zora;
              // / The address of the WETH contract, so that any ETH transferred can be handled as an ERC-20
              address public wethAddress;
              // A mapping of all of the auctions currently running.
              mapping(uint256 => IAuctionHouse.Auction) public auctions;
              bytes4 constant interfaceId = 0x80ac58cd; // 721 interface id
              Counters.Counter private _auctionIdTracker;
              /**
               * @notice Require that the specified auction exists
               */
              modifier auctionExists(uint256 auctionId) {
                  require(_exists(auctionId), "Auction doesn't exist");
                  _;
              }
              /*
               * Constructor
               */
              constructor(address _zora, address _weth) public {
                  require(
                      IERC165(_zora).supportsInterface(interfaceId),
                      "Doesn't support NFT interface"
                  );
                  zora = _zora;
                  wethAddress = _weth;
                  timeBuffer = 15 * 60; // extend 15 minutes after every bid made in last 15 minutes
                  minBidIncrementPercentage = 5; // 5%
              }
              /**
               * @notice Create an auction.
               * @dev Store the auction details in the auctions mapping and emit an AuctionCreated event.
               * If there is no curator, or if the curator is the auction creator, automatically approve the auction.
               */
              function createAuction(
                  uint256 tokenId,
                  address tokenContract,
                  uint256 duration,
                  uint256 reservePrice,
                  address payable curator,
                  uint8 curatorFeePercentage,
                  address auctionCurrency
              ) public override nonReentrant returns (uint256) {
                  require(
                      IERC165(tokenContract).supportsInterface(interfaceId),
                      "tokenContract does not support ERC721 interface"
                  );
                  require(curatorFeePercentage < 100, "curatorFeePercentage must be less than 100");
                  address tokenOwner = IERC721(tokenContract).ownerOf(tokenId);
                  require(msg.sender == IERC721(tokenContract).getApproved(tokenId) || msg.sender == tokenOwner, "Caller must be approved or owner for token id");
                  uint256 auctionId = _auctionIdTracker.current();
                  auctions[auctionId] = Auction({
                      tokenId: tokenId,
                      tokenContract: tokenContract,
                      approved: false,
                      amount: 0,
                      duration: duration,
                      firstBidTime: 0,
                      reservePrice: reservePrice,
                      curatorFeePercentage: curatorFeePercentage,
                      tokenOwner: tokenOwner,
                      bidder: address(0),
                      curator: curator,
                      auctionCurrency: auctionCurrency
                  });
                  IERC721(tokenContract).transferFrom(tokenOwner, address(this), tokenId);
                  _auctionIdTracker.increment();
                  emit AuctionCreated(auctionId, tokenId, tokenContract, duration, reservePrice, tokenOwner, curator, curatorFeePercentage, auctionCurrency);
                  if(auctions[auctionId].curator == address(0) || curator == tokenOwner) {
                      _approveAuction(auctionId, true);
                  }
                  return auctionId;
              }
              /**
               * @notice Approve an auction, opening up the auction for bids.
               * @dev Only callable by the curator. Cannot be called if the auction has already started.
               */
              function setAuctionApproval(uint256 auctionId, bool approved) external override auctionExists(auctionId) {
                  require(msg.sender == auctions[auctionId].curator, "Must be auction curator");
                  require(auctions[auctionId].firstBidTime == 0, "Auction has already started");
                  _approveAuction(auctionId, approved);
              }
              function setAuctionReservePrice(uint256 auctionId, uint256 reservePrice) external override auctionExists(auctionId) {
                  require(msg.sender == auctions[auctionId].curator || msg.sender == auctions[auctionId].tokenOwner, "Must be auction curator or token owner");
                  require(auctions[auctionId].firstBidTime == 0, "Auction has already started");
                  auctions[auctionId].reservePrice = reservePrice;
                  emit AuctionReservePriceUpdated(auctionId, auctions[auctionId].tokenId, auctions[auctionId].tokenContract, reservePrice);
              }
              /**
               * @notice Create a bid on a token, with a given amount.
               * @dev If provided a valid bid, transfers the provided amount to this contract.
               * If the auction is run in native ETH, the ETH is wrapped so it can be identically to other
               * auction currencies in this contract.
               */
              function createBid(uint256 auctionId, uint256 amount)
              external
              override
              payable
              auctionExists(auctionId)
              nonReentrant
              {
                  address payable lastBidder = auctions[auctionId].bidder;
                  require(auctions[auctionId].approved, "Auction must be approved by curator");
                  require(
                      auctions[auctionId].firstBidTime == 0 ||
                      block.timestamp <
                      auctions[auctionId].firstBidTime.add(auctions[auctionId].duration),
                      "Auction expired"
                  );
                  require(
                      amount >= auctions[auctionId].reservePrice,
                          "Must send at least reservePrice"
                  );
                  require(
                      amount >= auctions[auctionId].amount.add(
                          auctions[auctionId].amount.mul(minBidIncrementPercentage).div(100)
                      ),
                      "Must send more than last bid by minBidIncrementPercentage amount"
                  );
                  // For Zora Protocol, ensure that the bid is valid for the current bidShare configuration
                  if(auctions[auctionId].tokenContract == zora) {
                      require(
                          IMarket(IMediaExtended(zora).marketContract()).isValidBid(
                              auctions[auctionId].tokenId,
                              amount
                          ),
                          "Bid invalid for share splitting"
                      );
                  }
                  // If this is the first valid bid, we should set the starting time now.
                  // If it's not, then we should refund the last bidder
                  if(auctions[auctionId].firstBidTime == 0) {
                      auctions[auctionId].firstBidTime = block.timestamp;
                  } else if(lastBidder != address(0)) {
                      _handleOutgoingBid(lastBidder, auctions[auctionId].amount, auctions[auctionId].auctionCurrency);
                  }
                  _handleIncomingBid(amount, auctions[auctionId].auctionCurrency);
                  auctions[auctionId].amount = amount;
                  auctions[auctionId].bidder = msg.sender;
                  bool extended = false;
                  // at this point we know that the timestamp is less than start + duration (since the auction would be over, otherwise)
                  // we want to know by how much the timestamp is less than start + duration
                  // if the difference is less than the timeBuffer, increase the duration by the timeBuffer
                  if (
                      auctions[auctionId].firstBidTime.add(auctions[auctionId].duration).sub(
                          block.timestamp
                      ) < timeBuffer
                  ) {
                      // Playing code golf for gas optimization:
                      // uint256 expectedEnd = auctions[auctionId].firstBidTime.add(auctions[auctionId].duration);
                      // uint256 timeRemaining = expectedEnd.sub(block.timestamp);
                      // uint256 timeToAdd = timeBuffer.sub(timeRemaining);
                      // uint256 newDuration = auctions[auctionId].duration.add(timeToAdd);
                      uint256 oldDuration = auctions[auctionId].duration;
                      auctions[auctionId].duration =
                          oldDuration.add(timeBuffer.sub(auctions[auctionId].firstBidTime.add(oldDuration).sub(block.timestamp)));
                      extended = true;
                  }
                  emit AuctionBid(
                      auctionId,
                      auctions[auctionId].tokenId,
                      auctions[auctionId].tokenContract,
                      msg.sender,
                      amount,
                      lastBidder == address(0), // firstBid boolean
                      extended
                  );
                  if (extended) {
                      emit AuctionDurationExtended(
                          auctionId,
                          auctions[auctionId].tokenId,
                          auctions[auctionId].tokenContract,
                          auctions[auctionId].duration
                      );
                  }
              }
              /**
               * @notice End an auction, finalizing the bid on Zora if applicable and paying out the respective parties.
               * @dev If for some reason the auction cannot be finalized (invalid token recipient, for example),
               * The auction is reset and the NFT is transferred back to the auction creator.
               */
              function endAuction(uint256 auctionId) external override auctionExists(auctionId) nonReentrant {
                  require(
                      uint256(auctions[auctionId].firstBidTime) != 0,
                      "Auction hasn't begun"
                  );
                  require(
                      block.timestamp >=
                      auctions[auctionId].firstBidTime.add(auctions[auctionId].duration),
                      "Auction hasn't completed"
                  );
                  address currency = auctions[auctionId].auctionCurrency == address(0) ? wethAddress : auctions[auctionId].auctionCurrency;
                  uint256 curatorFee = 0;
                  uint256 tokenOwnerProfit = auctions[auctionId].amount;
                  if(auctions[auctionId].tokenContract == zora) {
                      // If the auction is running on zora, settle it on the protocol
                      (bool success, uint256 remainingProfit) = _handleZoraAuctionSettlement(auctionId);
                      tokenOwnerProfit = remainingProfit;
                      if(success != true) {
                          _handleOutgoingBid(auctions[auctionId].bidder, auctions[auctionId].amount, auctions[auctionId].auctionCurrency);
                          _cancelAuction(auctionId);
                          return;
                      }
                  } else {
                      // Otherwise, transfer the token to the winner and pay out the participants below
                      try IERC721(auctions[auctionId].tokenContract).safeTransferFrom(address(this), auctions[auctionId].bidder, auctions[auctionId].tokenId) {} catch {
                          _handleOutgoingBid(auctions[auctionId].bidder, auctions[auctionId].amount, auctions[auctionId].auctionCurrency);
                          _cancelAuction(auctionId);
                          return;
                      }
                  }
                  if(auctions[auctionId].curator != address(0)) {
                      curatorFee = tokenOwnerProfit.mul(auctions[auctionId].curatorFeePercentage).div(100);
                      tokenOwnerProfit = tokenOwnerProfit.sub(curatorFee);
                      _handleOutgoingBid(auctions[auctionId].curator, curatorFee, auctions[auctionId].auctionCurrency);
                  }
                  _handleOutgoingBid(auctions[auctionId].tokenOwner, tokenOwnerProfit, auctions[auctionId].auctionCurrency);
                  emit AuctionEnded(
                      auctionId,
                      auctions[auctionId].tokenId,
                      auctions[auctionId].tokenContract,
                      auctions[auctionId].tokenOwner,
                      auctions[auctionId].curator,
                      auctions[auctionId].bidder,
                      tokenOwnerProfit,
                      curatorFee,
                      currency
                  );
                  delete auctions[auctionId];
              }
              /**
               * @notice Cancel an auction.
               * @dev Transfers the NFT back to the auction creator and emits an AuctionCanceled event
               */
              function cancelAuction(uint256 auctionId) external override nonReentrant auctionExists(auctionId) {
                  require(
                      auctions[auctionId].tokenOwner == msg.sender || auctions[auctionId].curator == msg.sender,
                      "Can only be called by auction creator or curator"
                  );
                  require(
                      uint256(auctions[auctionId].firstBidTime) == 0,
                      "Can't cancel an auction once it's begun"
                  );
                  _cancelAuction(auctionId);
              }
              /**
               * @dev Given an amount and a currency, transfer the currency to this contract.
               * If the currency is ETH (0x0), attempt to wrap the amount as WETH
               */
              function _handleIncomingBid(uint256 amount, address currency) internal {
                  // If this is an ETH bid, ensure they sent enough and convert it to WETH under the hood
                  if(currency == address(0)) {
                      require(msg.value == amount, "Sent ETH Value does not match specified bid amount");
                      IWETH(wethAddress).deposit{value: amount}();
                  } else {
                      // We must check the balance that was actually transferred to the auction,
                      // as some tokens impose a transfer fee and would not actually transfer the
                      // full amount to the market, resulting in potentally locked funds
                      IERC20 token = IERC20(currency);
                      uint256 beforeBalance = token.balanceOf(address(this));
                      token.safeTransferFrom(msg.sender, address(this), amount);
                      uint256 afterBalance = token.balanceOf(address(this));
                      require(beforeBalance.add(amount) == afterBalance, "Token transfer call did not transfer expected amount");
                  }
              }
              function _handleOutgoingBid(address to, uint256 amount, address currency) internal {
                  // If the auction is in ETH, unwrap it from its underlying WETH and try to send it to the recipient.
                  if(currency == address(0)) {
                      IWETH(wethAddress).withdraw(amount);
                      // If the ETH transfer fails (sigh), rewrap the ETH and try send it as WETH.
                      if(!_safeTransferETH(to, amount)) {
                          IWETH(wethAddress).deposit{value: amount}();
                          IERC20(wethAddress).safeTransfer(to, amount);
                      }
                  } else {
                      IERC20(currency).safeTransfer(to, amount);
                  }
              }
              function _safeTransferETH(address to, uint256 value) internal returns (bool) {
                  (bool success, ) = to.call{value: value}(new bytes(0));
                  return success;
              }
              function _cancelAuction(uint256 auctionId) internal {
                  address tokenOwner = auctions[auctionId].tokenOwner;
                  IERC721(auctions[auctionId].tokenContract).safeTransferFrom(address(this), tokenOwner, auctions[auctionId].tokenId);
                  emit AuctionCanceled(auctionId, auctions[auctionId].tokenId, auctions[auctionId].tokenContract, tokenOwner);
                  delete auctions[auctionId];
              }
              function _approveAuction(uint256 auctionId, bool approved) internal {
                  auctions[auctionId].approved = approved;
                  emit AuctionApprovalUpdated(auctionId, auctions[auctionId].tokenId, auctions[auctionId].tokenContract, approved);
              }
              function _exists(uint256 auctionId) internal view returns(bool) {
                  return auctions[auctionId].tokenOwner != address(0);
              }
              function _handleZoraAuctionSettlement(uint256 auctionId) internal returns (bool, uint256) {
                  address currency = auctions[auctionId].auctionCurrency == address(0) ? wethAddress : auctions[auctionId].auctionCurrency;
                  IMarket.Bid memory bid = IMarket.Bid({
                      amount: auctions[auctionId].amount,
                      currency: currency,
                      bidder: address(this),
                      recipient: auctions[auctionId].bidder,
                      sellOnShare: Decimal.D256(0)
                  });
                  IERC20(currency).approve(IMediaExtended(zora).marketContract(), bid.amount);
                  IMedia(zora).setBid(auctions[auctionId].tokenId, bid);
                  uint256 beforeBalance = IERC20(currency).balanceOf(address(this));
                  try IMedia(zora).acceptBid(auctions[auctionId].tokenId, bid) {} catch {
                      // If the underlying NFT transfer here fails, we should cancel the auction and refund the winner
                      IMediaExtended(zora).removeBid(auctions[auctionId].tokenId);
                      return (false, 0);
                  }
                  uint256 afterBalance = IERC20(currency).balanceOf(address(this));
                  // We have to calculate the amount to send to the token owner here in case there was a
                  // sell-on share on the token
                  return (true, afterBalance.sub(beforeBalance));
              }
              // TODO: consider reverting if the message sender is not WETH
              receive() external payable {}
              fallback() external payable {}
          }// SPDX-License-Identifier: MIT
          pragma solidity >=0.6.0 <0.8.0;
          /**
           * @dev Wrappers over Solidity's arithmetic operations with added overflow
           * checks.
           *
           * Arithmetic operations in Solidity wrap on overflow. This can easily result
           * in bugs, because programmers usually assume that an overflow raises an
           * error, which is the standard behavior in high level programming languages.
           * `SafeMath` restores this intuition by reverting the transaction when an
           * operation overflows.
           *
           * Using this library instead of the unchecked operations eliminates an entire
           * class of bugs, so it's recommended to use it always.
           */
          library SafeMath {
              /**
               * @dev Returns the addition of two unsigned integers, reverting on
               * overflow.
               *
               * Counterpart to Solidity's `+` operator.
               *
               * Requirements:
               *
               * - Addition cannot overflow.
               */
              function add(uint256 a, uint256 b) internal pure returns (uint256) {
                  uint256 c = a + b;
                  require(c >= a, "SafeMath: addition overflow");
                  return c;
              }
              /**
               * @dev Returns the subtraction of two unsigned integers, reverting on
               * overflow (when the result is negative).
               *
               * Counterpart to Solidity's `-` operator.
               *
               * Requirements:
               *
               * - Subtraction cannot overflow.
               */
              function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                  return sub(a, b, "SafeMath: subtraction overflow");
              }
              /**
               * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
               * overflow (when the result is negative).
               *
               * Counterpart to Solidity's `-` operator.
               *
               * Requirements:
               *
               * - Subtraction cannot overflow.
               */
              function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  require(b <= a, errorMessage);
                  uint256 c = a - b;
                  return c;
              }
              /**
               * @dev Returns the multiplication of two unsigned integers, reverting on
               * overflow.
               *
               * Counterpart to Solidity's `*` operator.
               *
               * Requirements:
               *
               * - Multiplication cannot overflow.
               */
              function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                  // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                  // benefit is lost if 'b' is also tested.
                  // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                  if (a == 0) {
                      return 0;
                  }
                  uint256 c = a * b;
                  require(c / a == b, "SafeMath: multiplication overflow");
                  return c;
              }
              /**
               * @dev Returns the integer division of two unsigned integers. Reverts on
               * division by zero. The result is rounded towards zero.
               *
               * Counterpart to Solidity's `/` operator. Note: this function uses a
               * `revert` opcode (which leaves remaining gas untouched) while Solidity
               * uses an invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function div(uint256 a, uint256 b) internal pure returns (uint256) {
                  return div(a, b, "SafeMath: division by zero");
              }
              /**
               * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
               * division by zero. The result is rounded towards zero.
               *
               * Counterpart to Solidity's `/` operator. Note: this function uses a
               * `revert` opcode (which leaves remaining gas untouched) while Solidity
               * uses an invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  require(b > 0, errorMessage);
                  uint256 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                  return c;
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
               * Reverts when dividing by zero.
               *
               * Counterpart to Solidity's `%` operator. This function uses a `revert`
               * opcode (which leaves remaining gas untouched) while Solidity uses an
               * invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                  return mod(a, b, "SafeMath: modulo by zero");
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
               * Reverts with custom message when dividing by zero.
               *
               * Counterpart to Solidity's `%` operator. This function uses a `revert`
               * opcode (which leaves remaining gas untouched) while Solidity uses an
               * invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  require(b != 0, errorMessage);
                  return a % b;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity >=0.6.2 <0.8.0;
          import "../../introspection/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;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity >=0.6.0 <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 () internal {
                  _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;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity >=0.6.0 <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);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity >=0.6.0 <0.8.0;
          import "./IERC20.sol";
          import "../../math/SafeMath.sol";
          import "../../utils/Address.sol";
          /**
           * @title SafeERC20
           * @dev Wrappers around ERC20 operations that throw on failure (when the token
           * contract returns false). Tokens that return no value (and instead revert or
           * throw on failure) are also supported, non-reverting calls are assumed to be
           * successful.
           * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
           * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
           */
          library SafeERC20 {
              using SafeMath for uint256;
              using Address for address;
              function safeTransfer(IERC20 token, address to, uint256 value) internal {
                  _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
              }
              function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                  _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
              }
              /**
               * @dev Deprecated. This function has issues similar to the ones found in
               * {IERC20-approve}, and its usage is discouraged.
               *
               * Whenever possible, use {safeIncreaseAllowance} and
               * {safeDecreaseAllowance} instead.
               */
              function safeApprove(IERC20 token, address spender, uint256 value) internal {
                  // safeApprove should only be called when setting an initial allowance,
                  // or when resetting it to zero. To increase and decrease it, use
                  // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                  // solhint-disable-next-line max-line-length
                  require((value == 0) || (token.allowance(address(this), spender) == 0),
                      "SafeERC20: approve from non-zero to non-zero allowance"
                  );
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
              }
              function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                  uint256 newAllowance = token.allowance(address(this), spender).add(value);
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
              }
              function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                  uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
              }
              /**
               * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
               * on the return value: the return value is optional (but if data is returned, it must not be false).
               * @param token The token targeted by the call.
               * @param data The call data (encoded using abi.encode or one of its variants).
               */
              function _callOptionalReturn(IERC20 token, bytes memory data) private {
                  // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                  // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                  // the target address contains contract code and also asserts for success in the low-level call.
                  bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                  if (returndata.length > 0) { // Return data is optional
                      // solhint-disable-next-line max-line-length
                      require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity >=0.6.0 <0.8.0;
          import "../math/SafeMath.sol";
          /**
           * @title Counters
           * @author Matt Condon (@shrugs)
           * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
           * of elements in a mapping, issuing ERC721 ids, or counting request ids.
           *
           * Include with `using Counters for Counters.Counter;`
           * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the {SafeMath}
           * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
           * directly accessed.
           */
          library Counters {
              using SafeMath for uint256;
              struct Counter {
                  // This variable should never be directly accessed by users of the library: interactions must be restricted to
                  // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
                  // this feature: see https://github.com/ethereum/solidity/issues/4637
                  uint256 _value; // default: 0
              }
              function current(Counter storage counter) internal view returns (uint256) {
                  return counter._value;
              }
              function increment(Counter storage counter) internal {
                  // The {SafeMath} overflow check can be skipped here, see the comment at the top
                  counter._value += 1;
              }
              function decrement(Counter storage counter) internal {
                  counter._value = counter._value.sub(1);
              }
          }
          // SPDX-License-Identifier: GPL-3.0
          pragma solidity 0.6.8;
          pragma experimental ABIEncoderV2;
          import {Decimal} from "../Decimal.sol";
          /**
           * @title Interface for Zora Protocol's Market
           */
          interface IMarket {
              struct Bid {
                  // Amount of the currency being bid
                  uint256 amount;
                  // Address to the ERC20 token being used to bid
                  address currency;
                  // Address of the bidder
                  address bidder;
                  // Address of the recipient
                  address recipient;
                  // % of the next sale to award the current owner
                  Decimal.D256 sellOnShare;
              }
              struct Ask {
                  // Amount of the currency being asked
                  uint256 amount;
                  // Address to the ERC20 token being asked
                  address currency;
              }
              struct BidShares {
                  // % of sale value that goes to the _previous_ owner of the nft
                  Decimal.D256 prevOwner;
                  // % of sale value that goes to the original creator of the nft
                  Decimal.D256 creator;
                  // % of sale value that goes to the seller (current owner) of the nft
                  Decimal.D256 owner;
              }
              event BidCreated(uint256 indexed tokenId, Bid bid);
              event BidRemoved(uint256 indexed tokenId, Bid bid);
              event BidFinalized(uint256 indexed tokenId, Bid bid);
              event AskCreated(uint256 indexed tokenId, Ask ask);
              event AskRemoved(uint256 indexed tokenId, Ask ask);
              event BidShareUpdated(uint256 indexed tokenId, BidShares bidShares);
              function bidForTokenBidder(uint256 tokenId, address bidder)
                  external
                  view
                  returns (Bid memory);
              function currentAskForToken(uint256 tokenId)
                  external
                  view
                  returns (Ask memory);
              function bidSharesForToken(uint256 tokenId)
                  external
                  view
                  returns (BidShares memory);
              function isValidBid(uint256 tokenId, uint256 bidAmount)
                  external
                  view
                  returns (bool);
              function isValidBidShares(BidShares calldata bidShares)
                  external
                  pure
                  returns (bool);
              function splitShare(Decimal.D256 calldata sharePercentage, uint256 amount)
                  external
                  pure
                  returns (uint256);
              function configure(address mediaContractAddress) external;
              function setBidShares(uint256 tokenId, BidShares calldata bidShares)
                  external;
              function setAsk(uint256 tokenId, Ask calldata ask) external;
              function removeAsk(uint256 tokenId) external;
              function setBid(
                  uint256 tokenId,
                  Bid calldata bid,
                  address spender
              ) external;
              function removeBid(uint256 tokenId, address bidder) external;
              function acceptBid(uint256 tokenId, Bid calldata expectedBid) external;
          }
          // SPDX-License-Identifier: GPL-3.0
          pragma solidity 0.6.8;
          pragma experimental ABIEncoderV2;
          import {IMarket} from "./IMarket.sol";
          /**
           * @title Interface for Zora Protocol's Media
           */
          interface IMedia {
              struct EIP712Signature {
                  uint256 deadline;
                  uint8 v;
                  bytes32 r;
                  bytes32 s;
              }
              struct MediaData {
                  // A valid URI of the content represented by this token
                  string tokenURI;
                  // A valid URI of the metadata associated with this token
                  string metadataURI;
                  // A SHA256 hash of the content pointed to by tokenURI
                  bytes32 contentHash;
                  // A SHA256 hash of the content pointed to by metadataURI
                  bytes32 metadataHash;
              }
              event TokenURIUpdated(uint256 indexed _tokenId, address owner, string _uri);
              event TokenMetadataURIUpdated(
                  uint256 indexed _tokenId,
                  address owner,
                  string _uri
              );
              /**
               * @notice Return the metadata URI for a piece of media given the token URI
               */
              function tokenMetadataURI(uint256 tokenId)
                  external
                  view
                  returns (string memory);
              /**
               * @notice Mint new media for msg.sender.
               */
              function mint(MediaData calldata data, IMarket.BidShares calldata bidShares)
                  external;
              /**
               * @notice EIP-712 mintWithSig method. Mints new media for a creator given a valid signature.
               */
              function mintWithSig(
                  address creator,
                  MediaData calldata data,
                  IMarket.BidShares calldata bidShares,
                  EIP712Signature calldata sig
              ) external;
              /**
               * @notice Transfer the token with the given ID to a given address.
               * Save the previous owner before the transfer, in case there is a sell-on fee.
               * @dev This can only be called by the auction contract specified at deployment
               */
              function auctionTransfer(uint256 tokenId, address recipient) external;
              /**
               * @notice Set the ask on a piece of media
               */
              function setAsk(uint256 tokenId, IMarket.Ask calldata ask) external;
              /**
               * @notice Remove the ask on a piece of media
               */
              function removeAsk(uint256 tokenId) external;
              /**
               * @notice Set the bid on a piece of media
               */
              function setBid(uint256 tokenId, IMarket.Bid calldata bid) external;
              /**
               * @notice Remove the bid on a piece of media
               */
              function removeBid(uint256 tokenId) external;
              function acceptBid(uint256 tokenId, IMarket.Bid calldata bid) external;
              /**
               * @notice Revoke approval for a piece of media
               */
              function revokeApproval(uint256 tokenId) external;
              /**
               * @notice Update the token URI
               */
              function updateTokenURI(uint256 tokenId, string calldata tokenURI) external;
              /**
               * @notice Update the token metadata uri
               */
              function updateTokenMetadataURI(
                  uint256 tokenId,
                  string calldata metadataURI
              ) external;
              /**
               * @notice EIP-712 permit method. Sets an approved spender given a valid signature.
               */
              function permit(
                  address spender,
                  uint256 tokenId,
                  EIP712Signature calldata sig
              ) external;
          }
          // SPDX-License-Identifier: GPL-3.0
          pragma solidity 0.6.8;
          pragma experimental ABIEncoderV2;
          /**
           * @title Interface for Auction Houses
           */
          interface IAuctionHouse {
              struct Auction {
                  // ID for the ERC721 token
                  uint256 tokenId;
                  // Address for the ERC721 contract
                  address tokenContract;
                  // Whether or not the auction curator has approved the auction to start
                  bool approved;
                  // The current highest bid amount
                  uint256 amount;
                  // The length of time to run the auction for, after the first bid was made
                  uint256 duration;
                  // The time of the first bid
                  uint256 firstBidTime;
                  // The minimum price of the first bid
                  uint256 reservePrice;
                  // The sale percentage to send to the curator
                  uint8 curatorFeePercentage;
                  // The address that should receive the funds once the NFT is sold.
                  address tokenOwner;
                  // The address of the current highest bid
                  address payable bidder;
                  // The address of the auction's curator.
                  // The curator can reject or approve an auction
                  address payable curator;
                  // The address of the ERC-20 currency to run the auction with.
                  // If set to 0x0, the auction will be run in ETH
                  address auctionCurrency;
              }
              event AuctionCreated(
                  uint256 indexed auctionId,
                  uint256 indexed tokenId,
                  address indexed tokenContract,
                  uint256 duration,
                  uint256 reservePrice,
                  address tokenOwner,
                  address curator,
                  uint8 curatorFeePercentage,
                  address auctionCurrency
              );
              event AuctionApprovalUpdated(
                  uint256 indexed auctionId,
                  uint256 indexed tokenId,
                  address indexed tokenContract,
                  bool approved
              );
              event AuctionReservePriceUpdated(
                  uint256 indexed auctionId,
                  uint256 indexed tokenId,
                  address indexed tokenContract,
                  uint256 reservePrice
              );
              event AuctionBid(
                  uint256 indexed auctionId,
                  uint256 indexed tokenId,
                  address indexed tokenContract,
                  address sender,
                  uint256 value,
                  bool firstBid,
                  bool extended
              );
              event AuctionDurationExtended(
                  uint256 indexed auctionId,
                  uint256 indexed tokenId,
                  address indexed tokenContract,
                  uint256 duration
              );
              event AuctionEnded(
                  uint256 indexed auctionId,
                  uint256 indexed tokenId,
                  address indexed tokenContract,
                  address tokenOwner,
                  address curator,
                  address winner,
                  uint256 amount,
                  uint256 curatorFee,
                  address auctionCurrency
              );
              event AuctionCanceled(
                  uint256 indexed auctionId,
                  uint256 indexed tokenId,
                  address indexed tokenContract,
                  address tokenOwner
              );
              function createAuction(
                  uint256 tokenId,
                  address tokenContract,
                  uint256 duration,
                  uint256 reservePrice,
                  address payable curator,
                  uint8 curatorFeePercentages,
                  address auctionCurrency
              ) external returns (uint256);
              function setAuctionApproval(uint256 auctionId, bool approved) external;
              function setAuctionReservePrice(uint256 auctionId, uint256 reservePrice) external;
              function createBid(uint256 auctionId, uint256 amount) external payable;
              function endAuction(uint256 auctionId) external;
              function cancelAuction(uint256 auctionId) external;
          }// SPDX-License-Identifier: MIT
          pragma solidity >=0.6.0 <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);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity >=0.6.2 <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;
                  // solhint-disable-next-line no-inline-assembly
                  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");
                  // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                  (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");
                  // solhint-disable-next-line avoid-low-level-calls
                  (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");
                  // solhint-disable-next-line avoid-low-level-calls
                  (bool success, bytes memory returndata) = target.staticcall(data);
                  return _verifyCallResult(success, returndata, errorMessage);
              }
              function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              let returndata_size := mload(returndata)
                              revert(add(32, returndata), returndata_size)
                          }
                      } else {
                          revert(errorMessage);
                      }
                  }
              }
          }
          /*
              Copyright 2019 dYdX Trading Inc.
              Licensed under the Apache License, Version 2.0 (the "License");
              you may not use this file except in compliance with the License.
              You may obtain a copy of the License at
              http://www.apache.org/licenses/LICENSE-2.0
              Unless required by applicable law or agreed to in writing, software
              distributed under the License is distributed on an "AS IS" BASIS,
              WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
              See the License for the specific language governing permissions and
              limitations under the License.
          */
          pragma solidity 0.6.8;
          pragma experimental ABIEncoderV2;
          /**
           * NOTE: This file is a clone of the dydx protocol's Decimal.sol contract. It was forked from https://github.com/dydxprotocol/solo
           * at commit 2d8454e02702fe5bc455b848556660629c3cad36
           *
           * It has not been modified other than to use a newer solidity in the pragma to match the rest of the contract suite of this project
           */
          import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
          import {Math} from "./Math.sol";
          /**
           * @title Decimal
           *
           * Library that defines a fixed-point number with 18 decimal places.
           */
          library Decimal {
              using SafeMath for uint256;
              // ============ Constants ============
              uint256 constant BASE_POW = 18;
              uint256 constant BASE = 10**BASE_POW;
              // ============ Structs ============
              struct D256 {
                  uint256 value;
              }
              // ============ Functions ============
              function one() internal pure returns (D256 memory) {
                  return D256({value: BASE});
              }
              function onePlus(D256 memory d) internal pure returns (D256 memory) {
                  return D256({value: d.value.add(BASE)});
              }
              function mul(uint256 target, D256 memory d)
                  internal
                  pure
                  returns (uint256)
              {
                  return Math.getPartial(target, d.value, BASE);
              }
              function div(uint256 target, D256 memory d)
                  internal
                  pure
                  returns (uint256)
              {
                  return Math.getPartial(target, BASE, d.value);
              }
          }
          pragma solidity 0.6.8;
          pragma experimental ABIEncoderV2;
          import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
          /**
           * @title Math
           *
           * Library for non-standard Math functions
           * NOTE: This file is a clone of the dydx protocol's Decimal.sol contract.
           * It was forked from https://github.com/dydxprotocol/solo at commit
           * 2d8454e02702fe5bc455b848556660629c3cad36. It has not been modified other than to use a
           * newer solidity in the pragma to match the rest of the contract suite of this project.
           */
          library Math {
              using SafeMath for uint256;
              // ============ Library Functions ============
              /*
               * Return target * (numerator / denominator).
               */
              function getPartial(
                  uint256 target,
                  uint256 numerator,
                  uint256 denominator
              ) internal pure returns (uint256) {
                  return target.mul(numerator).div(denominator);
              }
              /*
               * Return target * (numerator / denominator), but rounded up.
               */
              function getPartialRoundUp(
                  uint256 target,
                  uint256 numerator,
                  uint256 denominator
              ) internal pure returns (uint256) {
                  if (target == 0 || numerator == 0) {
                      // SafeMath will check for zero denominator
                      return SafeMath.div(0, denominator);
                  }
                  return target.mul(numerator).sub(1).div(denominator).add(1);
              }
              function to128(uint256 number) internal pure returns (uint128) {
                  uint128 result = uint128(number);
                  require(result == number, "Math: Unsafe cast to uint128");
                  return result;
              }
              function to96(uint256 number) internal pure returns (uint96) {
                  uint96 result = uint96(number);
                  require(result == number, "Math: Unsafe cast to uint96");
                  return result;
              }
              function to32(uint256 number) internal pure returns (uint32) {
                  uint32 result = uint32(number);
                  require(result == number, "Math: Unsafe cast to uint32");
                  return result;
              }
              function min(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a < b ? a : b;
              }
              function max(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a > b ? a : b;
              }
          }
          

          File 6 of 6: Media
          pragma solidity 0.6.8;
          /**
           * NOTE: This contract only exists to serve as a testing utility. It is not recommended to be used outside of a testing environment
           */
          import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
          import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
          import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
          /**
           * @title ERC20 Token
           *
           * Basic ERC20 Implementation
           */
          contract BaseERC20 is IERC20, Ownable {
              using SafeMath for uint256;
              // ============ Variables ============
              string internal _name;
              string internal _symbol;
              uint256 internal _supply;
              uint8 internal _decimals;
              mapping(address => uint256) private _balances;
              mapping(address => mapping(address => uint256)) private _allowances;
              // ============ Constructor ============
              constructor(
                  string memory name,
                  string memory symbol,
                  uint8 decimals
              ) public {
                  _name = name;
                  _symbol = symbol;
                  _decimals = decimals;
              }
              // ============ Public Functions ============
              function symbol() public view returns (string memory) {
                  return _symbol;
              }
              function name() public view returns (string memory) {
                  return _name;
              }
              function decimals() public view virtual returns (uint8) {
                  return _decimals;
              }
              function totalSupply() public view override returns (uint256) {
                  return _supply;
              }
              function balanceOf(address who) public view override returns (uint256) {
                  return _balances[who];
              }
              function allowance(address owner, address spender)
                  public
                  view
                  override
                  returns (uint256)
              {
                  return _allowances[owner][spender];
              }
              // ============ Internal Functions ============
              function _mint(address to, uint256 value) internal {
                  require(to != address(0), "Cannot mint to zero address");
                  _balances[to] = _balances[to].add(value);
                  _supply = _supply.add(value);
                  emit Transfer(address(0), to, value);
              }
              function _burn(address from, uint256 value) internal {
                  require(from != address(0), "Cannot burn to zero");
                  _balances[from] = _balances[from].sub(value);
                  _supply = _supply.sub(value);
                  emit Transfer(from, address(0), value);
              }
              // ============ Token Functions ============
              function transfer(address to, uint256 value)
                  public
                  virtual
                  override
                  returns (bool)
              {
                  if (_balances[msg.sender] >= value) {
                      _balances[msg.sender] = _balances[msg.sender].sub(value);
                      _balances[to] = _balances[to].add(value);
                      emit Transfer(msg.sender, to, value);
                      return true;
                  } else {
                      return false;
                  }
              }
              function transferFrom(
                  address from,
                  address to,
                  uint256 value
              ) public virtual override returns (bool) {
                  if (
                      _balances[from] >= value && _allowances[from][msg.sender] >= value
                  ) {
                      _balances[to] = _balances[to].add(value);
                      _balances[from] = _balances[from].sub(value);
                      _allowances[from][msg.sender] = _allowances[from][msg.sender].sub(
                          value
                      );
                      emit Transfer(from, to, value);
                      return true;
                  } else {
                      return false;
                  }
              }
              function approve(address spender, uint256 value)
                  public
                  override
                  returns (bool)
              {
                  return _approve(msg.sender, spender, value);
              }
              function _approve(
                  address owner,
                  address spender,
                  uint256 value
              ) internal returns (bool) {
                  _allowances[owner][spender] = value;
                  emit Approval(owner, spender, value);
                  return true;
              }
              function mint(address to, uint256 value) public onlyOwner {
                  _mint(to, value);
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          /**
           * @dev Wrappers over Solidity's arithmetic operations with added overflow
           * checks.
           *
           * Arithmetic operations in Solidity wrap on overflow. This can easily result
           * in bugs, because programmers usually assume that an overflow raises an
           * error, which is the standard behavior in high level programming languages.
           * `SafeMath` restores this intuition by reverting the transaction when an
           * operation overflows.
           *
           * Using this library instead of the unchecked operations eliminates an entire
           * class of bugs, so it's recommended to use it always.
           */
          library SafeMath {
              /**
               * @dev Returns the addition of two unsigned integers, reverting on
               * overflow.
               *
               * Counterpart to Solidity's `+` operator.
               *
               * Requirements:
               *
               * - Addition cannot overflow.
               */
              function add(uint256 a, uint256 b) internal pure returns (uint256) {
                  uint256 c = a + b;
                  require(c >= a, "SafeMath: addition overflow");
                  return c;
              }
              /**
               * @dev Returns the subtraction of two unsigned integers, reverting on
               * overflow (when the result is negative).
               *
               * Counterpart to Solidity's `-` operator.
               *
               * Requirements:
               *
               * - Subtraction cannot overflow.
               */
              function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                  return sub(a, b, "SafeMath: subtraction overflow");
              }
              /**
               * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
               * overflow (when the result is negative).
               *
               * Counterpart to Solidity's `-` operator.
               *
               * Requirements:
               *
               * - Subtraction cannot overflow.
               */
              function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  require(b <= a, errorMessage);
                  uint256 c = a - b;
                  return c;
              }
              /**
               * @dev Returns the multiplication of two unsigned integers, reverting on
               * overflow.
               *
               * Counterpart to Solidity's `*` operator.
               *
               * Requirements:
               *
               * - Multiplication cannot overflow.
               */
              function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                  // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                  // benefit is lost if 'b' is also tested.
                  // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                  if (a == 0) {
                      return 0;
                  }
                  uint256 c = a * b;
                  require(c / a == b, "SafeMath: multiplication overflow");
                  return c;
              }
              /**
               * @dev Returns the integer division of two unsigned integers. Reverts on
               * division by zero. The result is rounded towards zero.
               *
               * Counterpart to Solidity's `/` operator. Note: this function uses a
               * `revert` opcode (which leaves remaining gas untouched) while Solidity
               * uses an invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function div(uint256 a, uint256 b) internal pure returns (uint256) {
                  return div(a, b, "SafeMath: division by zero");
              }
              /**
               * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
               * division by zero. The result is rounded towards zero.
               *
               * Counterpart to Solidity's `/` operator. Note: this function uses a
               * `revert` opcode (which leaves remaining gas untouched) while Solidity
               * uses an invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  require(b > 0, errorMessage);
                  uint256 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                  return c;
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
               * Reverts when dividing by zero.
               *
               * Counterpart to Solidity's `%` operator. This function uses a `revert`
               * opcode (which leaves remaining gas untouched) while Solidity uses an
               * invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                  return mod(a, b, "SafeMath: modulo by zero");
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
               * Reverts with custom message when dividing by zero.
               *
               * Counterpart to Solidity's `%` operator. This function uses a `revert`
               * opcode (which leaves remaining gas untouched) while Solidity uses an
               * invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                  require(b != 0, errorMessage);
                  return a % b;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.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);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          import "../GSN/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.
           */
          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 () internal {
                  address msgSender = _msgSender();
                  _owner = msgSender;
                  emit OwnershipTransferred(address(0), msgSender);
              }
              /**
               * @dev Returns the address of the current owner.
               */
              function owner() public view 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 {
                  emit OwnershipTransferred(_owner, address(0));
                  _owner = 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");
                  emit OwnershipTransferred(_owner, newOwner);
                  _owner = newOwner;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.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 GSN 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 payable) {
                  return msg.sender;
              }
              function _msgData() internal view virtual returns (bytes memory) {
                  this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                  return msg.data;
              }
          }
          // SPDX-License-Identifier: MIT
          /**
           * NOTE: This file is a clone of the OpenZeppelin ERC721Burnable.sol contract. It was forked from https://github.com/OpenZeppelin/openzeppelin-contracts
           * at commit 1ada3b633e5bfd9d4ffe0207d64773a11f5a7c40
           *
           * It was cloned in order to ensure it imported from the cloned ERC721.sol file. No other modifications have been made.
           */
          pragma solidity 0.6.8;
          import "@openzeppelin/contracts/GSN/Context.sol";
          import "./ERC721.sol";
          /**
           * @title ERC721 Burnable Token
           * @dev ERC721 Token that can be irreversibly burned (destroyed).
           */
          abstract contract ERC721Burnable is Context, ERC721 {
              /**
               * @dev Burns `tokenId`. See {ERC721-_burn}.
               *
               * Requirements:
               *
               * - The caller must own `tokenId` or be an approved operator.
               */
              function burn(uint256 tokenId) public virtual {
                  //solhint-disable-next-line max-line-length
                  require(
                      _isApprovedOrOwner(_msgSender(), tokenId),
                      "ERC721Burnable: caller is not owner nor approved"
                  );
                  _burn(tokenId);
              }
          }
          // SPDX-License-Identifier: MIT
          /**
           * NOTE: This file is a clone of the OpenZeppelin ERC721.sol contract. It was forked from https://github.com/OpenZeppelin/openzeppelin-contracts
           * at commit 1ada3b633e5bfd9d4ffe0207d64773a11f5a7c40
           *
           *
           * The following functions needed to be modified, prompting this clone:
           *  - `_tokenURIs` visibility was changed from private to internal to support updating URIs after minting
           *  - `_baseURI` visibiility was changed from private to internal to support fetching token URI even after the token was burned
           *  - `_INTERFACE_ID_ERC721_METADATA` is no longer registered as an interface because _tokenURI now returns raw content instead of a JSON file, and supports updatable URIs
           *  - `_approve` visibility was changed from private to internal to support EIP-2612 flavored permits and approval revocation by an approved address
           */
          pragma solidity 0.6.8;
          import "@openzeppelin/contracts/GSN/Context.sol";
          import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
          import "@openzeppelin/contracts/token/ERC721/IERC721Metadata.sol";
          import "@openzeppelin/contracts/token/ERC721/IERC721Enumerable.sol";
          import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
          import "@openzeppelin/contracts/introspection/ERC165.sol";
          import "@openzeppelin/contracts/math/SafeMath.sol";
          import "@openzeppelin/contracts/utils/Address.sol";
          import "@openzeppelin/contracts/utils/EnumerableSet.sol";
          import "@openzeppelin/contracts/utils/EnumerableMap.sol";
          import "@openzeppelin/contracts/utils/Strings.sol";
          /**
           * @title ERC721 Non-Fungible Token Standard basic implementation
           * @dev see https://eips.ethereum.org/EIPS/eip-721
           */
          contract ERC721 is
              Context,
              ERC165,
              IERC721,
              IERC721Metadata,
              IERC721Enumerable
          {
              using SafeMath for uint256;
              using Address for address;
              using EnumerableSet for EnumerableSet.UintSet;
              using EnumerableMap for EnumerableMap.UintToAddressMap;
              using Strings for uint256;
              // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
              // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
              bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
              // Mapping from holder address to their (enumerable) set of owned tokens
              mapping(address => EnumerableSet.UintSet) private _holderTokens;
              // Enumerable mapping from token ids to their owners
              EnumerableMap.UintToAddressMap private _tokenOwners;
              // Mapping from token ID to approved address
              mapping(uint256 => address) private _tokenApprovals;
              // Mapping from owner to operator approvals
              mapping(address => mapping(address => bool)) private _operatorApprovals;
              // Token name
              string private _name;
              // Token symbol
              string private _symbol;
              // Optional mapping for token URIs
              mapping(uint256 => string) internal _tokenURIs;
              // Base URI
              string internal _baseURI;
              /*
               *     bytes4(keccak256('balanceOf(address)')) == 0x70a08231
               *     bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
               *     bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
               *     bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
               *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
               *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
               *     bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
               *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
               *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
               *
               *     => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
               *        0xa22cb465 ^ 0xe985e9c5 ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
               */
              bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
              /*
               *     bytes4(keccak256('totalSupply()')) == 0x18160ddd
               *     bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59
               *     bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7
               *
               *     => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63
               */
              bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
              /**
               * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
               */
              constructor(string memory name, string memory symbol) public {
                  _name = name;
                  _symbol = symbol;
                  // register the supported interfaces to conform to ERC721 via ERC165
                  _registerInterface(_INTERFACE_ID_ERC721);
                  _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
              }
              /**
               * @dev See {IERC721-balanceOf}.
               */
              function balanceOf(address owner) public view override returns (uint256) {
                  require(
                      owner != address(0),
                      "ERC721: balance query for the zero address"
                  );
                  return _holderTokens[owner].length();
              }
              /**
               * @dev See {IERC721-ownerOf}.
               */
              function ownerOf(uint256 tokenId) public view override returns (address) {
                  return
                      _tokenOwners.get(
                          tokenId,
                          "ERC721: owner query for nonexistent token"
                      );
              }
              /**
               * @dev See {IERC721Metadata-name}.
               */
              function name() public view override returns (string memory) {
                  return _name;
              }
              /**
               * @dev See {IERC721Metadata-symbol}.
               */
              function symbol() public view override returns (string memory) {
                  return _symbol;
              }
              /**
               * @dev See {IERC721Metadata-tokenURI}.
               */
              function tokenURI(uint256 tokenId)
                  public
                  view
                  virtual
                  override
                  returns (string memory)
              {
                  require(
                      _exists(tokenId),
                      "ERC721Metadata: URI query for nonexistent token"
                  );
                  string memory _tokenURI = _tokenURIs[tokenId];
                  // If there is no base URI, return the token URI.
                  if (bytes(_baseURI).length == 0) {
                      return _tokenURI;
                  }
                  // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
                  if (bytes(_tokenURI).length > 0) {
                      return string(abi.encodePacked(_baseURI, _tokenURI));
                  }
                  // If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
                  return string(abi.encodePacked(_baseURI, tokenId.toString()));
              }
              /**
               * @dev Returns the base URI set via {_setBaseURI}. This will be
               * automatically added as a prefix in {tokenURI} to each token's URI, or
               * to the token ID if no specific URI is set for that token ID.
               */
              function baseURI() public view returns (string memory) {
                  return _baseURI;
              }
              /**
               * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
               */
              function tokenOfOwnerByIndex(address owner, uint256 index)
                  public
                  view
                  override
                  returns (uint256)
              {
                  return _holderTokens[owner].at(index);
              }
              /**
               * @dev See {IERC721Enumerable-totalSupply}.
               */
              function totalSupply() public view override returns (uint256) {
                  // _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds
                  return _tokenOwners.length();
              }
              /**
               * @dev See {IERC721Enumerable-tokenByIndex}.
               */
              function tokenByIndex(uint256 index)
                  public
                  view
                  override
                  returns (uint256)
              {
                  (uint256 tokenId, ) = _tokenOwners.at(index);
                  return tokenId;
              }
              /**
               * @dev See {IERC721-approve}.
               */
              function approve(address to, uint256 tokenId) public virtual override {
                  address owner = ownerOf(tokenId);
                  require(to != owner, "ERC721: approval to current owner");
                  require(
                      _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
                      "ERC721: approve caller is not owner nor approved for all"
                  );
                  _approve(to, tokenId);
              }
              /**
               * @dev See {IERC721-getApproved}.
               */
              function getApproved(uint256 tokenId)
                  public
                  view
                  override
                  returns (address)
              {
                  require(
                      _exists(tokenId),
                      "ERC721: approved query for nonexistent token"
                  );
                  return _tokenApprovals[tokenId];
              }
              /**
               * @dev See {IERC721-setApprovalForAll}.
               */
              function setApprovalForAll(address operator, bool approved)
                  public
                  virtual
                  override
              {
                  require(operator != _msgSender(), "ERC721: approve to caller");
                  _operatorApprovals[_msgSender()][operator] = approved;
                  emit ApprovalForAll(_msgSender(), operator, approved);
              }
              /**
               * @dev See {IERC721-isApprovedForAll}.
               */
              function isApprovedForAll(address owner, address operator)
                  public
                  view
                  override
                  returns (bool)
              {
                  return _operatorApprovals[owner][operator];
              }
              /**
               * @dev See {IERC721-transferFrom}.
               */
              function transferFrom(
                  address from,
                  address to,
                  uint256 tokenId
              ) public virtual override {
                  //solhint-disable-next-line max-line-length
                  require(
                      _isApprovedOrOwner(_msgSender(), tokenId),
                      "ERC721: transfer caller is not owner nor approved"
                  );
                  _transfer(from, to, tokenId);
              }
              /**
               * @dev See {IERC721-safeTransferFrom}.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 tokenId
              ) public virtual override {
                  safeTransferFrom(from, to, tokenId, "");
              }
              /**
               * @dev See {IERC721-safeTransferFrom}.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 tokenId,
                  bytes memory _data
              ) public virtual override {
                  require(
                      _isApprovedOrOwner(_msgSender(), tokenId),
                      "ERC721: transfer caller is not owner nor approved"
                  );
                  _safeTransfer(from, to, tokenId, _data);
              }
              /**
               * @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.
               *
               * `_data` is additional data, it has no specified format and it is sent in call to `to`.
               *
               * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
               * implement alternative mechanisms to perform token transfer, such as signature-based.
               *
               * Requirements:
               *
               * - `from` cannot be the zero address.
               * - `to` cannot be the zero address.
               * - `tokenId` token must exist and be owned by `from`.
               * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
               *
               * Emits a {Transfer} event.
               */
              function _safeTransfer(
                  address from,
                  address to,
                  uint256 tokenId,
                  bytes memory _data
              ) internal virtual {
                  _transfer(from, to, tokenId);
                  require(
                      _checkOnERC721Received(from, to, tokenId, _data),
                      "ERC721: transfer to non ERC721Receiver implementer"
                  );
              }
              /**
               * @dev Returns whether `tokenId` exists.
               *
               * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
               *
               * Tokens start existing when they are minted (`_mint`),
               * and stop existing when they are burned (`_burn`).
               */
              function _exists(uint256 tokenId) internal view returns (bool) {
                  return _tokenOwners.contains(tokenId);
              }
              /**
               * @dev Returns whether `spender` is allowed to manage `tokenId`.
               *
               * Requirements:
               *
               * - `tokenId` must exist.
               */
              function _isApprovedOrOwner(address spender, uint256 tokenId)
                  internal
                  view
                  returns (bool)
              {
                  require(
                      _exists(tokenId),
                      "ERC721: operator query for nonexistent token"
                  );
                  address owner = ownerOf(tokenId);
                  return (spender == owner ||
                      getApproved(tokenId) == spender ||
                      isApprovedForAll(owner, spender));
              }
              /**
               * @dev Safely mints `tokenId` and transfers it to `to`.
               *
               * Requirements:
               d*
               * - `tokenId` must not exist.
               * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
               *
               * Emits a {Transfer} event.
               */
              function _safeMint(address to, uint256 tokenId) internal virtual {
                  _safeMint(to, tokenId, "");
              }
              /**
               * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
               * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
               */
              function _safeMint(
                  address to,
                  uint256 tokenId,
                  bytes memory _data
              ) internal virtual {
                  _mint(to, tokenId);
                  require(
                      _checkOnERC721Received(address(0), to, tokenId, _data),
                      "ERC721: transfer to non ERC721Receiver implementer"
                  );
              }
              /**
               * @dev Mints `tokenId` and transfers it to `to`.
               *
               * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
               *
               * Requirements:
               *
               * - `tokenId` must not exist.
               * - `to` cannot be the zero address.
               *
               * Emits a {Transfer} event.
               */
              function _mint(address to, uint256 tokenId) internal virtual {
                  require(to != address(0), "ERC721: mint to the zero address");
                  require(!_exists(tokenId), "ERC721: token already minted");
                  _beforeTokenTransfer(address(0), to, tokenId);
                  _holderTokens[to].add(tokenId);
                  _tokenOwners.set(tokenId, to);
                  emit Transfer(address(0), to, tokenId);
              }
              /**
               * @dev Destroys `tokenId`.
               * The approval is cleared when the token is burned.
               *
               * Requirements:
               *
               * - `tokenId` must exist.
               *
               * Emits a {Transfer} event.
               */
              function _burn(uint256 tokenId) internal virtual {
                  address owner = ownerOf(tokenId);
                  _beforeTokenTransfer(owner, address(0), tokenId);
                  // Clear approvals
                  _approve(address(0), tokenId);
                  // Clear metadata (if any)
                  if (bytes(_tokenURIs[tokenId]).length != 0) {
                      delete _tokenURIs[tokenId];
                  }
                  _holderTokens[owner].remove(tokenId);
                  _tokenOwners.remove(tokenId);
                  emit Transfer(owner, address(0), tokenId);
              }
              /**
               * @dev Transfers `tokenId` from `from` to `to`.
               *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
               *
               * Requirements:
               *
               * - `to` cannot be the zero address.
               * - `tokenId` token must be owned by `from`.
               *
               * Emits a {Transfer} event.
               */
              function _transfer(
                  address from,
                  address to,
                  uint256 tokenId
              ) internal virtual {
                  require(
                      ownerOf(tokenId) == from,
                      "ERC721: transfer of token that is not own"
                  );
                  require(to != address(0), "ERC721: transfer to the zero address");
                  _beforeTokenTransfer(from, to, tokenId);
                  // Clear approvals from the previous owner
                  _approve(address(0), tokenId);
                  _holderTokens[from].remove(tokenId);
                  _holderTokens[to].add(tokenId);
                  _tokenOwners.set(tokenId, to);
                  emit Transfer(from, to, tokenId);
              }
              /**
               * @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
               *
               * Requirements:
               *
               * - `tokenId` must exist.
               */
              function _setTokenURI(uint256 tokenId, string memory _tokenURI)
                  internal
                  virtual
              {
                  require(
                      _exists(tokenId),
                      "ERC721Metadata: URI set of nonexistent token"
                  );
                  _tokenURIs[tokenId] = _tokenURI;
              }
              /**
               * @dev Internal function to set the base URI for all token IDs. It is
               * automatically added as a prefix to the value returned in {tokenURI},
               * or to the token ID if {tokenURI} is empty.
               */
              function _setBaseURI(string memory baseURI_) internal virtual {
                  _baseURI = baseURI_;
              }
              /**
               * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
               * The call is not executed if the target address is not a contract.
               *
               * @param from address representing the previous owner of the given token ID
               * @param to target address that will receive the tokens
               * @param tokenId uint256 ID of the token to be transferred
               * @param _data bytes optional data to send along with the call
               * @return bool whether the call correctly returned the expected magic value
               */
              function _checkOnERC721Received(
                  address from,
                  address to,
                  uint256 tokenId,
                  bytes memory _data
              ) private returns (bool) {
                  if (!to.isContract()) {
                      return true;
                  }
                  bytes memory returndata =
                      to.functionCall(
                          abi.encodeWithSelector(
                              IERC721Receiver(to).onERC721Received.selector,
                              _msgSender(),
                              from,
                              tokenId,
                              _data
                          ),
                          "ERC721: transfer to non ERC721Receiver implementer"
                      );
                  bytes4 retval = abi.decode(returndata, (bytes4));
                  return (retval == _ERC721_RECEIVED);
              }
              function _approve(address to, uint256 tokenId) internal {
                  _tokenApprovals[tokenId] = to;
                  emit Approval(ownerOf(tokenId), to, tokenId);
              }
              /**
               * @dev Hook that is called before any token transfer. This includes minting
               * and burning.
               *
               * Calling conditions:
               *
               * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
               * transferred to `to`.
               * - When `from` is zero, `tokenId` will be minted for `to`.
               * - When `to` is zero, ``from``'s `tokenId` will be burned.
               * - `from` cannot be the zero address.
               * - `to` cannot be the zero address.
               *
               * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
               */
              function _beforeTokenTransfer(
                  address from,
                  address to,
                  uint256 tokenId
              ) internal virtual {}
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.2;
          import "../../introspection/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;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.2;
          import "./IERC721.sol";
          /**
           * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
           * @dev See https://eips.ethereum.org/EIPS/eip-721
           */
          interface IERC721Metadata is IERC721 {
              /**
               * @dev Returns the token collection name.
               */
              function name() external view returns (string memory);
              /**
               * @dev Returns the token collection symbol.
               */
              function symbol() external view returns (string memory);
              /**
               * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
               */
              function tokenURI(uint256 tokenId) external view returns (string memory);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.2;
          import "./IERC721.sol";
          /**
           * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
           * @dev See https://eips.ethereum.org/EIPS/eip-721
           */
          interface IERC721Enumerable is IERC721 {
              /**
               * @dev Returns the total amount of tokens stored by the contract.
               */
              function totalSupply() external view returns (uint256);
              /**
               * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
               * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
               */
              function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);
              /**
               * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
               * Use along with {totalSupply} to enumerate all tokens.
               */
              function tokenByIndex(uint256 index) external view returns (uint256);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.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);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          import "./IERC165.sol";
          /**
           * @dev Implementation of the {IERC165} interface.
           *
           * Contracts may inherit from this and call {_registerInterface} to declare
           * their support of an interface.
           */
          contract ERC165 is IERC165 {
              /*
               * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
               */
              bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
              /**
               * @dev Mapping of interface ids to whether or not it's supported.
               */
              mapping(bytes4 => bool) private _supportedInterfaces;
              constructor () internal {
                  // Derived contracts need only register support for their own interfaces,
                  // we register support for ERC165 itself here
                  _registerInterface(_INTERFACE_ID_ERC165);
              }
              /**
               * @dev See {IERC165-supportsInterface}.
               *
               * Time complexity O(1), guaranteed to always use less than 30 000 gas.
               */
              function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
                  return _supportedInterfaces[interfaceId];
              }
              /**
               * @dev Registers the contract as an implementer of the interface defined by
               * `interfaceId`. Support of the actual ERC165 interface is automatic and
               * registering its interface id is not required.
               *
               * See {IERC165-supportsInterface}.
               *
               * Requirements:
               *
               * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
               */
              function _registerInterface(bytes4 interfaceId) internal virtual {
                  require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
                  _supportedInterfaces[interfaceId] = true;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.2;
          /**
           * @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 in extcodesize, which returns 0 for contracts in
                  // construction, since the code is only stored at the end of the
                  // constructor execution.
                  uint256 size;
                  // solhint-disable-next-line no-inline-assembly
                  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");
                  // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                  (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");
                  return _functionCallWithValue(target, data, value, errorMessage);
              }
              function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
                  require(isContract(target), "Address: call to non-contract");
                  // solhint-disable-next-line avoid-low-level-calls
                  (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
                  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
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              let returndata_size := mload(returndata)
                              revert(add(32, returndata), returndata_size)
                          }
                      } else {
                          revert(errorMessage);
                      }
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          /**
           * @dev Library for managing
           * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
           * types.
           *
           * Sets have the following properties:
           *
           * - Elements are added, removed, and checked for existence in constant time
           * (O(1)).
           * - Elements are enumerated in O(n). No guarantees are made on the ordering.
           *
           * ```
           * contract Example {
           *     // Add the library methods
           *     using EnumerableSet for EnumerableSet.AddressSet;
           *
           *     // Declare a set state variable
           *     EnumerableSet.AddressSet private mySet;
           * }
           * ```
           *
           * As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`
           * (`UintSet`) are supported.
           */
          library EnumerableSet {
              // To implement this library for multiple types with as little code
              // repetition as possible, we write it in terms of a generic Set type with
              // bytes32 values.
              // The Set implementation uses private functions, and user-facing
              // implementations (such as AddressSet) are just wrappers around the
              // underlying Set.
              // This means that we can only create new EnumerableSets for types that fit
              // in bytes32.
              struct Set {
                  // Storage of set values
                  bytes32[] _values;
                  // Position of the value in the `values` array, plus 1 because index 0
                  // means a value is not in the set.
                  mapping (bytes32 => uint256) _indexes;
              }
              /**
               * @dev Add a value to a set. O(1).
               *
               * Returns true if the value was added to the set, that is if it was not
               * already present.
               */
              function _add(Set storage set, bytes32 value) private returns (bool) {
                  if (!_contains(set, value)) {
                      set._values.push(value);
                      // The value is stored at length-1, but we add 1 to all indexes
                      // and use 0 as a sentinel value
                      set._indexes[value] = set._values.length;
                      return true;
                  } else {
                      return false;
                  }
              }
              /**
               * @dev Removes a value from a set. O(1).
               *
               * Returns true if the value was removed from the set, that is if it was
               * present.
               */
              function _remove(Set storage set, bytes32 value) private returns (bool) {
                  // We read and store the value's index to prevent multiple reads from the same storage slot
                  uint256 valueIndex = set._indexes[value];
                  if (valueIndex != 0) { // Equivalent to contains(set, value)
                      // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                      // the array, and then remove the last element (sometimes called as 'swap and pop').
                      // This modifies the order of the array, as noted in {at}.
                      uint256 toDeleteIndex = valueIndex - 1;
                      uint256 lastIndex = set._values.length - 1;
                      // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
                      // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
                      bytes32 lastvalue = set._values[lastIndex];
                      // Move the last value to the index where the value to delete is
                      set._values[toDeleteIndex] = lastvalue;
                      // Update the index for the moved value
                      set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based
                      // Delete the slot where the moved value was stored
                      set._values.pop();
                      // Delete the index for the deleted slot
                      delete set._indexes[value];
                      return true;
                  } else {
                      return false;
                  }
              }
              /**
               * @dev Returns true if the value is in the set. O(1).
               */
              function _contains(Set storage set, bytes32 value) private view returns (bool) {
                  return set._indexes[value] != 0;
              }
              /**
               * @dev Returns the number of values on the set. O(1).
               */
              function _length(Set storage set) private view returns (uint256) {
                  return set._values.length;
              }
             /**
              * @dev Returns the value stored at position `index` in the set. O(1).
              *
              * Note that there are no guarantees on the ordering of values inside the
              * array, and it may change when more values are added or removed.
              *
              * Requirements:
              *
              * - `index` must be strictly less than {length}.
              */
              function _at(Set storage set, uint256 index) private view returns (bytes32) {
                  require(set._values.length > index, "EnumerableSet: index out of bounds");
                  return set._values[index];
              }
              // AddressSet
              struct AddressSet {
                  Set _inner;
              }
              /**
               * @dev Add a value to a set. O(1).
               *
               * Returns true if the value was added to the set, that is if it was not
               * already present.
               */
              function add(AddressSet storage set, address value) internal returns (bool) {
                  return _add(set._inner, bytes32(uint256(value)));
              }
              /**
               * @dev Removes a value from a set. O(1).
               *
               * Returns true if the value was removed from the set, that is if it was
               * present.
               */
              function remove(AddressSet storage set, address value) internal returns (bool) {
                  return _remove(set._inner, bytes32(uint256(value)));
              }
              /**
               * @dev Returns true if the value is in the set. O(1).
               */
              function contains(AddressSet storage set, address value) internal view returns (bool) {
                  return _contains(set._inner, bytes32(uint256(value)));
              }
              /**
               * @dev Returns the number of values in the set. O(1).
               */
              function length(AddressSet storage set) internal view returns (uint256) {
                  return _length(set._inner);
              }
             /**
              * @dev Returns the value stored at position `index` in the set. O(1).
              *
              * Note that there are no guarantees on the ordering of values inside the
              * array, and it may change when more values are added or removed.
              *
              * Requirements:
              *
              * - `index` must be strictly less than {length}.
              */
              function at(AddressSet storage set, uint256 index) internal view returns (address) {
                  return address(uint256(_at(set._inner, index)));
              }
              // UintSet
              struct UintSet {
                  Set _inner;
              }
              /**
               * @dev Add a value to a set. O(1).
               *
               * Returns true if the value was added to the set, that is if it was not
               * already present.
               */
              function add(UintSet storage set, uint256 value) internal returns (bool) {
                  return _add(set._inner, bytes32(value));
              }
              /**
               * @dev Removes a value from a set. O(1).
               *
               * Returns true if the value was removed from the set, that is if it was
               * present.
               */
              function remove(UintSet storage set, uint256 value) internal returns (bool) {
                  return _remove(set._inner, bytes32(value));
              }
              /**
               * @dev Returns true if the value is in the set. O(1).
               */
              function contains(UintSet storage set, uint256 value) internal view returns (bool) {
                  return _contains(set._inner, bytes32(value));
              }
              /**
               * @dev Returns the number of values on the set. O(1).
               */
              function length(UintSet storage set) internal view returns (uint256) {
                  return _length(set._inner);
              }
             /**
              * @dev Returns the value stored at position `index` in the set. O(1).
              *
              * Note that there are no guarantees on the ordering of values inside the
              * array, and it may change when more values are added or removed.
              *
              * Requirements:
              *
              * - `index` must be strictly less than {length}.
              */
              function at(UintSet storage set, uint256 index) internal view returns (uint256) {
                  return uint256(_at(set._inner, index));
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          /**
           * @dev Library for managing an enumerable variant of Solidity's
           * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
           * type.
           *
           * Maps have the following properties:
           *
           * - Entries are added, removed, and checked for existence in constant time
           * (O(1)).
           * - Entries are enumerated in O(n). No guarantees are made on the ordering.
           *
           * ```
           * contract Example {
           *     // Add the library methods
           *     using EnumerableMap for EnumerableMap.UintToAddressMap;
           *
           *     // Declare a set state variable
           *     EnumerableMap.UintToAddressMap private myMap;
           * }
           * ```
           *
           * As of v3.0.0, only maps of type `uint256 -> address` (`UintToAddressMap`) are
           * supported.
           */
          library EnumerableMap {
              // To implement this library for multiple types with as little code
              // repetition as possible, we write it in terms of a generic Map type with
              // bytes32 keys and values.
              // The Map implementation uses private functions, and user-facing
              // implementations (such as Uint256ToAddressMap) are just wrappers around
              // the underlying Map.
              // This means that we can only create new EnumerableMaps for types that fit
              // in bytes32.
              struct MapEntry {
                  bytes32 _key;
                  bytes32 _value;
              }
              struct Map {
                  // Storage of map keys and values
                  MapEntry[] _entries;
                  // Position of the entry defined by a key in the `entries` array, plus 1
                  // because index 0 means a key is not in the map.
                  mapping (bytes32 => uint256) _indexes;
              }
              /**
               * @dev Adds a key-value pair to a map, or updates the value for an existing
               * key. O(1).
               *
               * Returns true if the key was added to the map, that is if it was not
               * already present.
               */
              function _set(Map storage map, bytes32 key, bytes32 value) private returns (bool) {
                  // We read and store the key's index to prevent multiple reads from the same storage slot
                  uint256 keyIndex = map._indexes[key];
                  if (keyIndex == 0) { // Equivalent to !contains(map, key)
                      map._entries.push(MapEntry({ _key: key, _value: value }));
                      // The entry is stored at length-1, but we add 1 to all indexes
                      // and use 0 as a sentinel value
                      map._indexes[key] = map._entries.length;
                      return true;
                  } else {
                      map._entries[keyIndex - 1]._value = value;
                      return false;
                  }
              }
              /**
               * @dev Removes a key-value pair from a map. O(1).
               *
               * Returns true if the key was removed from the map, that is if it was present.
               */
              function _remove(Map storage map, bytes32 key) private returns (bool) {
                  // We read and store the key's index to prevent multiple reads from the same storage slot
                  uint256 keyIndex = map._indexes[key];
                  if (keyIndex != 0) { // Equivalent to contains(map, key)
                      // To delete a key-value pair from the _entries array in O(1), we swap the entry to delete with the last one
                      // in the array, and then remove the last entry (sometimes called as 'swap and pop').
                      // This modifies the order of the array, as noted in {at}.
                      uint256 toDeleteIndex = keyIndex - 1;
                      uint256 lastIndex = map._entries.length - 1;
                      // When the entry to delete is the last one, the swap operation is unnecessary. However, since this occurs
                      // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
                      MapEntry storage lastEntry = map._entries[lastIndex];
                      // Move the last entry to the index where the entry to delete is
                      map._entries[toDeleteIndex] = lastEntry;
                      // Update the index for the moved entry
                      map._indexes[lastEntry._key] = toDeleteIndex + 1; // All indexes are 1-based
                      // Delete the slot where the moved entry was stored
                      map._entries.pop();
                      // Delete the index for the deleted slot
                      delete map._indexes[key];
                      return true;
                  } else {
                      return false;
                  }
              }
              /**
               * @dev Returns true if the key is in the map. O(1).
               */
              function _contains(Map storage map, bytes32 key) private view returns (bool) {
                  return map._indexes[key] != 0;
              }
              /**
               * @dev Returns the number of key-value pairs in the map. O(1).
               */
              function _length(Map storage map) private view returns (uint256) {
                  return map._entries.length;
              }
             /**
              * @dev Returns the key-value pair stored at position `index` in the map. O(1).
              *
              * Note that there are no guarantees on the ordering of entries inside the
              * array, and it may change when more entries are added or removed.
              *
              * Requirements:
              *
              * - `index` must be strictly less than {length}.
              */
              function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) {
                  require(map._entries.length > index, "EnumerableMap: index out of bounds");
                  MapEntry storage entry = map._entries[index];
                  return (entry._key, entry._value);
              }
              /**
               * @dev Returns the value associated with `key`.  O(1).
               *
               * Requirements:
               *
               * - `key` must be in the map.
               */
              function _get(Map storage map, bytes32 key) private view returns (bytes32) {
                  return _get(map, key, "EnumerableMap: nonexistent key");
              }
              /**
               * @dev Same as {_get}, with a custom error message when `key` is not in the map.
               */
              function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) {
                  uint256 keyIndex = map._indexes[key];
                  require(keyIndex != 0, errorMessage); // Equivalent to contains(map, key)
                  return map._entries[keyIndex - 1]._value; // All indexes are 1-based
              }
              // UintToAddressMap
              struct UintToAddressMap {
                  Map _inner;
              }
              /**
               * @dev Adds a key-value pair to a map, or updates the value for an existing
               * key. O(1).
               *
               * Returns true if the key was added to the map, that is if it was not
               * already present.
               */
              function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
                  return _set(map._inner, bytes32(key), bytes32(uint256(value)));
              }
              /**
               * @dev Removes a value from a set. O(1).
               *
               * Returns true if the key was removed from the map, that is if it was present.
               */
              function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
                  return _remove(map._inner, bytes32(key));
              }
              /**
               * @dev Returns true if the key is in the map. O(1).
               */
              function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
                  return _contains(map._inner, bytes32(key));
              }
              /**
               * @dev Returns the number of elements in the map. O(1).
               */
              function length(UintToAddressMap storage map) internal view returns (uint256) {
                  return _length(map._inner);
              }
             /**
              * @dev Returns the element stored at position `index` in the set. O(1).
              * Note that there are no guarantees on the ordering of values inside the
              * array, and it may change when more values are added or removed.
              *
              * Requirements:
              *
              * - `index` must be strictly less than {length}.
              */
              function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
                  (bytes32 key, bytes32 value) = _at(map._inner, index);
                  return (uint256(key), address(uint256(value)));
              }
              /**
               * @dev Returns the value associated with `key`.  O(1).
               *
               * Requirements:
               *
               * - `key` must be in the map.
               */
              function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
                  return address(uint256(_get(map._inner, bytes32(key))));
              }
              /**
               * @dev Same as {get}, with a custom error message when `key` is not in the map.
               */
              function get(UintToAddressMap storage map, uint256 key, string memory errorMessage) internal view returns (address) {
                  return address(uint256(_get(map._inner, bytes32(key), errorMessage)));
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          /**
           * @dev String operations.
           */
          library Strings {
              /**
               * @dev Converts a `uint256` to its ASCII `string` representation.
               */
              function toString(uint256 value) internal pure returns (string memory) {
                  // Inspired by OraclizeAPI's implementation - MIT licence
                  // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
                  if (value == 0) {
                      return "0";
                  }
                  uint256 temp = value;
                  uint256 digits;
                  while (temp != 0) {
                      digits++;
                      temp /= 10;
                  }
                  bytes memory buffer = new bytes(digits);
                  uint256 index = digits - 1;
                  temp = value;
                  while (temp != 0) {
                      buffer[index--] = byte(uint8(48 + temp % 10));
                      temp /= 10;
                  }
                  return string(buffer);
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.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);
          }
          // SPDX-License-Identifier: GPL-3.0
          pragma solidity 0.6.8;
          pragma experimental ABIEncoderV2;
          import {ERC721Burnable} from "./ERC721Burnable.sol";
          import {ERC721} from "./ERC721.sol";
          import {EnumerableSet} from "@openzeppelin/contracts/utils/EnumerableSet.sol";
          import {Counters} from "@openzeppelin/contracts/utils/Counters.sol";
          import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
          import {Math} from "@openzeppelin/contracts/math/Math.sol";
          import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
          import {
              ReentrancyGuard
          } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
          import {Decimal} from "./Decimal.sol";
          import {IMarket} from "./interfaces/IMarket.sol";
          import "./interfaces/IMedia.sol";
          /**
           * @title A media value system, with perpetual equity to creators
           * @notice This contract provides an interface to mint media with a market
           * owned by the creator.
           */
          contract Media is IMedia, ERC721Burnable, ReentrancyGuard {
              using Counters for Counters.Counter;
              using SafeMath for uint256;
              /* *******
               * Globals
               * *******
               */
              // Address for the market
              address public marketContract;
              // Mapping from token to previous owner of the token
              mapping(uint256 => address) public previousTokenOwners;
              // Mapping from token id to creator address
              mapping(uint256 => address) public tokenCreators;
              // Mapping from creator address to their (enumerable) set of created tokens
              mapping(address => EnumerableSet.UintSet) private _creatorTokens;
              // Mapping from token id to sha256 hash of content
              mapping(uint256 => bytes32) public tokenContentHashes;
              // Mapping from token id to sha256 hash of metadata
              mapping(uint256 => bytes32) public tokenMetadataHashes;
              // Mapping from token id to metadataURI
              mapping(uint256 => string) private _tokenMetadataURIs;
              // Mapping from contentHash to bool
              mapping(bytes32 => bool) private _contentHashes;
              //keccak256("Permit(address spender,uint256 tokenId,uint256 nonce,uint256 deadline)");
              bytes32 public constant PERMIT_TYPEHASH =
                  0x49ecf333e5b8c95c40fdafc95c1ad136e8914a8fb55e9dc8bb01eaa83a2df9ad;
              //keccak256("MintWithSig(bytes32 contentHash,bytes32 metadataHash,uint256 creatorShare,uint256 nonce,uint256 deadline)");
              bytes32 public constant MINT_WITH_SIG_TYPEHASH =
                  0x2952e482b8e2b192305f87374d7af45dc2eafafe4f50d26a0c02e90f2fdbe14b;
              // Mapping from address to token id to permit nonce
              mapping(address => mapping(uint256 => uint256)) public permitNonces;
              // Mapping from address to mint with sig nonce
              mapping(address => uint256) public mintWithSigNonces;
              /*
               *     bytes4(keccak256('name()')) == 0x06fdde03
               *     bytes4(keccak256('symbol()')) == 0x95d89b41
               *     bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
               *     bytes4(keccak256('tokenMetadataURI(uint256)')) == 0x157c3df9
               *
               *     => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd ^ 0x157c3df9 == 0x4e222e66
               */
              bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x4e222e66;
              Counters.Counter private _tokenIdTracker;
              /* *********
               * Modifiers
               * *********
               */
              /**
               * @notice Require that the token has not been burned and has been minted
               */
              modifier onlyExistingToken(uint256 tokenId) {
                  require(_exists(tokenId), "Media: nonexistent token");
                  _;
              }
              /**
               * @notice Require that the token has had a content hash set
               */
              modifier onlyTokenWithContentHash(uint256 tokenId) {
                  require(
                      tokenContentHashes[tokenId] != 0,
                      "Media: token does not have hash of created content"
                  );
                  _;
              }
              /**
               * @notice Require that the token has had a metadata hash set
               */
              modifier onlyTokenWithMetadataHash(uint256 tokenId) {
                  require(
                      tokenMetadataHashes[tokenId] != 0,
                      "Media: token does not have hash of its metadata"
                  );
                  _;
              }
              /**
               * @notice Ensure that the provided spender is the approved or the owner of
               * the media for the specified tokenId
               */
              modifier onlyApprovedOrOwner(address spender, uint256 tokenId) {
                  require(
                      _isApprovedOrOwner(spender, tokenId),
                      "Media: Only approved or owner"
                  );
                  _;
              }
              /**
               * @notice Ensure the token has been created (even if it has been burned)
               */
              modifier onlyTokenCreated(uint256 tokenId) {
                  require(
                      _tokenIdTracker.current() > tokenId,
                      "Media: token with that id does not exist"
                  );
                  _;
              }
              /**
               * @notice Ensure that the provided URI is not empty
               */
              modifier onlyValidURI(string memory uri) {
                  require(
                      bytes(uri).length != 0,
                      "Media: specified uri must be non-empty"
                  );
                  _;
              }
              /**
               * @notice On deployment, set the market contract address and register the
               * ERC721 metadata interface
               */
              constructor(address marketContractAddr) public ERC721("Zora", "ZORA") {
                  marketContract = marketContractAddr;
                  _registerInterface(_INTERFACE_ID_ERC721_METADATA);
              }
              /* **************
               * View Functions
               * **************
               */
              /**
               * @notice return the URI for a particular piece of media with the specified tokenId
               * @dev This function is an override of the base OZ implementation because we
               * will return the tokenURI even if the media has been burned. In addition, this
               * protocol does not support a base URI, so relevant conditionals are removed.
               * @return the URI for a token
               */
              function tokenURI(uint256 tokenId)
                  public
                  view
                  override
                  onlyTokenCreated(tokenId)
                  returns (string memory)
              {
                  string memory _tokenURI = _tokenURIs[tokenId];
                  return _tokenURI;
              }
              /**
               * @notice Return the metadata URI for a piece of media given the token URI
               * @return the metadata URI for the token
               */
              function tokenMetadataURI(uint256 tokenId)
                  external
                  view
                  override
                  onlyTokenCreated(tokenId)
                  returns (string memory)
              {
                  return _tokenMetadataURIs[tokenId];
              }
              /* ****************
               * Public Functions
               * ****************
               */
              /**
               * @notice see IMedia
               */
              function mint(MediaData memory data, IMarket.BidShares memory bidShares)
                  public
                  override
                  nonReentrant
              {
                  _mintForCreator(msg.sender, data, bidShares);
              }
              /**
               * @notice see IMedia
               */
              function mintWithSig(
                  address creator,
                  MediaData memory data,
                  IMarket.BidShares memory bidShares,
                  EIP712Signature memory sig
              ) public override nonReentrant {
                  require(
                      sig.deadline == 0 || sig.deadline >= block.timestamp,
                      "Media: mintWithSig expired"
                  );
                  bytes32 domainSeparator = _calculateDomainSeparator();
                  bytes32 digest =
                      keccak256(
                          abi.encodePacked(
                              "\\x19\\x01",
                              domainSeparator,
                              keccak256(
                                  abi.encode(
                                      MINT_WITH_SIG_TYPEHASH,
                                      data.contentHash,
                                      data.metadataHash,
                                      bidShares.creator.value,
                                      mintWithSigNonces[creator]++,
                                      sig.deadline
                                  )
                              )
                          )
                      );
                  address recoveredAddress = ecrecover(digest, sig.v, sig.r, sig.s);
                  require(
                      recoveredAddress != address(0) && creator == recoveredAddress,
                      "Media: Signature invalid"
                  );
                  _mintForCreator(recoveredAddress, data, bidShares);
              }
              /**
               * @notice see IMedia
               */
              function auctionTransfer(uint256 tokenId, address recipient)
                  external
                  override
              {
                  require(msg.sender == marketContract, "Media: only market contract");
                  previousTokenOwners[tokenId] = ownerOf(tokenId);
                  _safeTransfer(ownerOf(tokenId), recipient, tokenId, "");
              }
              /**
               * @notice see IMedia
               */
              function setAsk(uint256 tokenId, IMarket.Ask memory ask)
                  public
                  override
                  nonReentrant
                  onlyApprovedOrOwner(msg.sender, tokenId)
              {
                  IMarket(marketContract).setAsk(tokenId, ask);
              }
              /**
               * @notice see IMedia
               */
              function removeAsk(uint256 tokenId)
                  external
                  override
                  nonReentrant
                  onlyApprovedOrOwner(msg.sender, tokenId)
              {
                  IMarket(marketContract).removeAsk(tokenId);
              }
              /**
               * @notice see IMedia
               */
              function setBid(uint256 tokenId, IMarket.Bid memory bid)
                  public
                  override
                  nonReentrant
                  onlyExistingToken(tokenId)
              {
                  require(msg.sender == bid.bidder, "Market: Bidder must be msg sender");
                  IMarket(marketContract).setBid(tokenId, bid, msg.sender);
              }
              /**
               * @notice see IMedia
               */
              function removeBid(uint256 tokenId)
                  external
                  override
                  nonReentrant
                  onlyTokenCreated(tokenId)
              {
                  IMarket(marketContract).removeBid(tokenId, msg.sender);
              }
              /**
               * @notice see IMedia
               */
              function acceptBid(uint256 tokenId, IMarket.Bid memory bid)
                  public
                  override
                  nonReentrant
                  onlyApprovedOrOwner(msg.sender, tokenId)
              {
                  IMarket(marketContract).acceptBid(tokenId, bid);
              }
              /**
               * @notice Burn a token.
               * @dev Only callable if the media owner is also the creator.
               */
              function burn(uint256 tokenId)
                  public
                  override
                  nonReentrant
                  onlyExistingToken(tokenId)
                  onlyApprovedOrOwner(msg.sender, tokenId)
              {
                  address owner = ownerOf(tokenId);
                  require(
                      tokenCreators[tokenId] == owner,
                      "Media: owner is not creator of media"
                  );
                  _burn(tokenId);
              }
              /**
               * @notice Revoke the approvals for a token. The provided `approve` function is not sufficient
               * for this protocol, as it does not allow an approved address to revoke it's own approval.
               * In instances where a 3rd party is interacting on a user's behalf via `permit`, they should
               * revoke their approval once their task is complete as a best practice.
               */
              function revokeApproval(uint256 tokenId) external override nonReentrant {
                  require(
                      msg.sender == getApproved(tokenId),
                      "Media: caller not approved address"
                  );
                  _approve(address(0), tokenId);
              }
              /**
               * @notice see IMedia
               * @dev only callable by approved or owner
               */
              function updateTokenURI(uint256 tokenId, string calldata tokenURI)
                  external
                  override
                  nonReentrant
                  onlyApprovedOrOwner(msg.sender, tokenId)
                  onlyTokenWithContentHash(tokenId)
                  onlyValidURI(tokenURI)
              {
                  _setTokenURI(tokenId, tokenURI);
                  emit TokenURIUpdated(tokenId, msg.sender, tokenURI);
              }
              /**
               * @notice see IMedia
               * @dev only callable by approved or owner
               */
              function updateTokenMetadataURI(
                  uint256 tokenId,
                  string calldata metadataURI
              )
                  external
                  override
                  nonReentrant
                  onlyApprovedOrOwner(msg.sender, tokenId)
                  onlyTokenWithMetadataHash(tokenId)
                  onlyValidURI(metadataURI)
              {
                  _setTokenMetadataURI(tokenId, metadataURI);
                  emit TokenMetadataURIUpdated(tokenId, msg.sender, metadataURI);
              }
              /**
               * @notice See IMedia
               * @dev This method is loosely based on the permit for ERC-20 tokens in  EIP-2612, but modified
               * for ERC-721.
               */
              function permit(
                  address spender,
                  uint256 tokenId,
                  EIP712Signature memory sig
              ) public override nonReentrant onlyExistingToken(tokenId) {
                  require(
                      sig.deadline == 0 || sig.deadline >= block.timestamp,
                      "Media: Permit expired"
                  );
                  require(spender != address(0), "Media: spender cannot be 0x0");
                  bytes32 domainSeparator = _calculateDomainSeparator();
                  bytes32 digest =
                      keccak256(
                          abi.encodePacked(
                              "\\x19\\x01",
                              domainSeparator,
                              keccak256(
                                  abi.encode(
                                      PERMIT_TYPEHASH,
                                      spender,
                                      tokenId,
                                      permitNonces[ownerOf(tokenId)][tokenId]++,
                                      sig.deadline
                                  )
                              )
                          )
                      );
                  address recoveredAddress = ecrecover(digest, sig.v, sig.r, sig.s);
                  require(
                      recoveredAddress != address(0) &&
                          ownerOf(tokenId) == recoveredAddress,
                      "Media: Signature invalid"
                  );
                  _approve(spender, tokenId);
              }
              /* *****************
               * Private Functions
               * *****************
               */
              /**
               * @notice Creates a new token for `creator`. Its token ID will be automatically
               * assigned (and available on the emitted {IERC721-Transfer} event), and the token
               * URI autogenerated based on the base URI passed at construction.
               *
               * See {ERC721-_safeMint}.
               *
               * On mint, also set the sha256 hashes of the content and its metadata for integrity
               * checks, along with the initial URIs to point to the content and metadata. Attribute
               * the token ID to the creator, mark the content hash as used, and set the bid shares for
               * the media's market.
               *
               * Note that although the content hash must be unique for future mints to prevent duplicate media,
               * metadata has no such requirement.
               */
              function _mintForCreator(
                  address creator,
                  MediaData memory data,
                  IMarket.BidShares memory bidShares
              ) internal onlyValidURI(data.tokenURI) onlyValidURI(data.metadataURI) {
                  require(data.contentHash != 0, "Media: content hash must be non-zero");
                  require(
                      _contentHashes[data.contentHash] == false,
                      "Media: a token has already been created with this content hash"
                  );
                  require(
                      data.metadataHash != 0,
                      "Media: metadata hash must be non-zero"
                  );
                  uint256 tokenId = _tokenIdTracker.current();
                  _safeMint(creator, tokenId);
                  _tokenIdTracker.increment();
                  _setTokenContentHash(tokenId, data.contentHash);
                  _setTokenMetadataHash(tokenId, data.metadataHash);
                  _setTokenMetadataURI(tokenId, data.metadataURI);
                  _setTokenURI(tokenId, data.tokenURI);
                  _creatorTokens[creator].add(tokenId);
                  _contentHashes[data.contentHash] = true;
                  tokenCreators[tokenId] = creator;
                  previousTokenOwners[tokenId] = creator;
                  IMarket(marketContract).setBidShares(tokenId, bidShares);
              }
              function _setTokenContentHash(uint256 tokenId, bytes32 contentHash)
                  internal
                  virtual
                  onlyExistingToken(tokenId)
              {
                  tokenContentHashes[tokenId] = contentHash;
              }
              function _setTokenMetadataHash(uint256 tokenId, bytes32 metadataHash)
                  internal
                  virtual
                  onlyExistingToken(tokenId)
              {
                  tokenMetadataHashes[tokenId] = metadataHash;
              }
              function _setTokenMetadataURI(uint256 tokenId, string memory metadataURI)
                  internal
                  virtual
                  onlyExistingToken(tokenId)
              {
                  _tokenMetadataURIs[tokenId] = metadataURI;
              }
              /**
               * @notice Destroys `tokenId`.
               * @dev We modify the OZ _burn implementation to
               * maintain metadata and to remove the
               * previous token owner from the piece
               */
              function _burn(uint256 tokenId) internal override {
                  string memory tokenURI = _tokenURIs[tokenId];
                  super._burn(tokenId);
                  if (bytes(tokenURI).length != 0) {
                      _tokenURIs[tokenId] = tokenURI;
                  }
                  delete previousTokenOwners[tokenId];
              }
              /**
               * @notice transfer a token and remove the ask for it.
               */
              function _transfer(
                  address from,
                  address to,
                  uint256 tokenId
              ) internal override {
                  IMarket(marketContract).removeAsk(tokenId);
                  super._transfer(from, to, tokenId);
              }
              /**
               * @dev Calculates EIP712 DOMAIN_SEPARATOR based on the current contract and chain ID.
               */
              function _calculateDomainSeparator() internal view returns (bytes32) {
                  uint256 chainID;
                  /* solium-disable-next-line */
                  assembly {
                      chainID := chainid()
                  }
                  return
                      keccak256(
                          abi.encode(
                              keccak256(
                                  "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
                              ),
                              keccak256(bytes("Zora")),
                              keccak256(bytes("1")),
                              chainID,
                              address(this)
                          )
                      );
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          import "../math/SafeMath.sol";
          /**
           * @title Counters
           * @author Matt Condon (@shrugs)
           * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
           * of elements in a mapping, issuing ERC721 ids, or counting request ids.
           *
           * Include with `using Counters for Counters.Counter;`
           * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the {SafeMath}
           * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
           * directly accessed.
           */
          library Counters {
              using SafeMath for uint256;
              struct Counter {
                  // This variable should never be directly accessed by users of the library: interactions must be restricted to
                  // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
                  // this feature: see https://github.com/ethereum/solidity/issues/4637
                  uint256 _value; // default: 0
              }
              function current(Counter storage counter) internal view returns (uint256) {
                  return counter._value;
              }
              function increment(Counter storage counter) internal {
                  // The {SafeMath} overflow check can be skipped here, see the comment at the top
                  counter._value += 1;
              }
              function decrement(Counter storage counter) internal {
                  counter._value = counter._value.sub(1);
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          /**
           * @dev Standard math utilities missing in the Solidity language.
           */
          library Math {
              /**
               * @dev Returns the largest of two numbers.
               */
              function max(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a >= b ? a : b;
              }
              /**
               * @dev Returns the smallest of two numbers.
               */
              function min(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a < b ? a : b;
              }
              /**
               * @dev Returns the average of two numbers. The result is rounded towards
               * zero.
               */
              function average(uint256 a, uint256 b) internal pure returns (uint256) {
                  // (a + b) / 2 can overflow, so we distribute
                  return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.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].
           */
          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 () internal {
                  _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;
              }
          }
          /*
              Copyright 2019 dYdX Trading Inc.
              Licensed under the Apache License, Version 2.0 (the "License");
              you may not use this file except in compliance with the License.
              You may obtain a copy of the License at
              http://www.apache.org/licenses/LICENSE-2.0
              Unless required by applicable law or agreed to in writing, software
              distributed under the License is distributed on an "AS IS" BASIS,
              WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
              See the License for the specific language governing permissions and
              limitations under the License.
          */
          pragma solidity 0.6.8;
          pragma experimental ABIEncoderV2;
          /**
           * NOTE: This file is a clone of the dydx protocol's Decimal.sol contract. It was forked from https://github.com/dydxprotocol/solo
           * at commit 2d8454e02702fe5bc455b848556660629c3cad36
           *
           * It has not been modified other than to use a newer solidity in the pragma to match the rest of the contract suite of this project
           */
          import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
          import {Math} from "./Math.sol";
          /**
           * @title Decimal
           *
           * Library that defines a fixed-point number with 18 decimal places.
           */
          library Decimal {
              using SafeMath for uint256;
              // ============ Constants ============
              uint256 constant BASE_POW = 18;
              uint256 constant BASE = 10**BASE_POW;
              // ============ Structs ============
              struct D256 {
                  uint256 value;
              }
              // ============ Functions ============
              function one() internal pure returns (D256 memory) {
                  return D256({value: BASE});
              }
              function onePlus(D256 memory d) internal pure returns (D256 memory) {
                  return D256({value: d.value.add(BASE)});
              }
              function mul(uint256 target, D256 memory d)
                  internal
                  pure
                  returns (uint256)
              {
                  return Math.getPartial(target, d.value, BASE);
              }
              function div(uint256 target, D256 memory d)
                  internal
                  pure
                  returns (uint256)
              {
                  return Math.getPartial(target, BASE, d.value);
              }
          }
          // SPDX-License-Identifier: GPL-3.0
          pragma solidity 0.6.8;
          pragma experimental ABIEncoderV2;
          import {Decimal} from "../Decimal.sol";
          /**
           * @title Interface for Zora Protocol's Market
           */
          interface IMarket {
              struct Bid {
                  // Amount of the currency being bid
                  uint256 amount;
                  // Address to the ERC20 token being used to bid
                  address currency;
                  // Address of the bidder
                  address bidder;
                  // Address of the recipient
                  address recipient;
                  // % of the next sale to award the current owner
                  Decimal.D256 sellOnShare;
              }
              struct Ask {
                  // Amount of the currency being asked
                  uint256 amount;
                  // Address to the ERC20 token being asked
                  address currency;
              }
              struct BidShares {
                  // % of sale value that goes to the _previous_ owner of the nft
                  Decimal.D256 prevOwner;
                  // % of sale value that goes to the original creator of the nft
                  Decimal.D256 creator;
                  // % of sale value that goes to the seller (current owner) of the nft
                  Decimal.D256 owner;
              }
              event BidCreated(uint256 indexed tokenId, Bid bid);
              event BidRemoved(uint256 indexed tokenId, Bid bid);
              event BidFinalized(uint256 indexed tokenId, Bid bid);
              event AskCreated(uint256 indexed tokenId, Ask ask);
              event AskRemoved(uint256 indexed tokenId, Ask ask);
              event BidShareUpdated(uint256 indexed tokenId, BidShares bidShares);
              function bidForTokenBidder(uint256 tokenId, address bidder)
                  external
                  view
                  returns (Bid memory);
              function currentAskForToken(uint256 tokenId)
                  external
                  view
                  returns (Ask memory);
              function bidSharesForToken(uint256 tokenId)
                  external
                  view
                  returns (BidShares memory);
              function isValidBid(uint256 tokenId, uint256 bidAmount)
                  external
                  view
                  returns (bool);
              function isValidBidShares(BidShares calldata bidShares)
                  external
                  pure
                  returns (bool);
              function splitShare(Decimal.D256 calldata sharePercentage, uint256 amount)
                  external
                  pure
                  returns (uint256);
              function configure(address mediaContractAddress) external;
              function setBidShares(uint256 tokenId, BidShares calldata bidShares)
                  external;
              function setAsk(uint256 tokenId, Ask calldata ask) external;
              function removeAsk(uint256 tokenId) external;
              function setBid(
                  uint256 tokenId,
                  Bid calldata bid,
                  address spender
              ) external;
              function removeBid(uint256 tokenId, address bidder) external;
              function acceptBid(uint256 tokenId, Bid calldata expectedBid) external;
          }
          // SPDX-License-Identifier: GPL-3.0
          pragma solidity 0.6.8;
          pragma experimental ABIEncoderV2;
          import {IMarket} from "./IMarket.sol";
          /**
           * @title Interface for Zora Protocol's Media
           */
          interface IMedia {
              struct EIP712Signature {
                  uint256 deadline;
                  uint8 v;
                  bytes32 r;
                  bytes32 s;
              }
              struct MediaData {
                  // A valid URI of the content represented by this token
                  string tokenURI;
                  // A valid URI of the metadata associated with this token
                  string metadataURI;
                  // A SHA256 hash of the content pointed to by tokenURI
                  bytes32 contentHash;
                  // A SHA256 hash of the content pointed to by metadataURI
                  bytes32 metadataHash;
              }
              event TokenURIUpdated(uint256 indexed _tokenId, address owner, string _uri);
              event TokenMetadataURIUpdated(
                  uint256 indexed _tokenId,
                  address owner,
                  string _uri
              );
              /**
               * @notice Return the metadata URI for a piece of media given the token URI
               */
              function tokenMetadataURI(uint256 tokenId)
                  external
                  view
                  returns (string memory);
              /**
               * @notice Mint new media for msg.sender.
               */
              function mint(MediaData calldata data, IMarket.BidShares calldata bidShares)
                  external;
              /**
               * @notice EIP-712 mintWithSig method. Mints new media for a creator given a valid signature.
               */
              function mintWithSig(
                  address creator,
                  MediaData calldata data,
                  IMarket.BidShares calldata bidShares,
                  EIP712Signature calldata sig
              ) external;
              /**
               * @notice Transfer the token with the given ID to a given address.
               * Save the previous owner before the transfer, in case there is a sell-on fee.
               * @dev This can only be called by the auction contract specified at deployment
               */
              function auctionTransfer(uint256 tokenId, address recipient) external;
              /**
               * @notice Set the ask on a piece of media
               */
              function setAsk(uint256 tokenId, IMarket.Ask calldata ask) external;
              /**
               * @notice Remove the ask on a piece of media
               */
              function removeAsk(uint256 tokenId) external;
              /**
               * @notice Set the bid on a piece of media
               */
              function setBid(uint256 tokenId, IMarket.Bid calldata bid) external;
              /**
               * @notice Remove the bid on a piece of media
               */
              function removeBid(uint256 tokenId) external;
              function acceptBid(uint256 tokenId, IMarket.Bid calldata bid) external;
              /**
               * @notice Revoke approval for a piece of media
               */
              function revokeApproval(uint256 tokenId) external;
              /**
               * @notice Update the token URI
               */
              function updateTokenURI(uint256 tokenId, string calldata tokenURI) external;
              /**
               * @notice Update the token metadata uri
               */
              function updateTokenMetadataURI(
                  uint256 tokenId,
                  string calldata metadataURI
              ) external;
              /**
               * @notice EIP-712 permit method. Sets an approved spender given a valid signature.
               */
              function permit(
                  address spender,
                  uint256 tokenId,
                  EIP712Signature calldata sig
              ) external;
          }
          pragma solidity 0.6.8;
          pragma experimental ABIEncoderV2;
          import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
          /**
           * @title Math
           *
           * Library for non-standard Math functions
           * NOTE: This file is a clone of the dydx protocol's Decimal.sol contract.
           * It was forked from https://github.com/dydxprotocol/solo at commit
           * 2d8454e02702fe5bc455b848556660629c3cad36. It has not been modified other than to use a
           * newer solidity in the pragma to match the rest of the contract suite of this project.
           */
          library Math {
              using SafeMath for uint256;
              // ============ Library Functions ============
              /*
               * Return target * (numerator / denominator).
               */
              function getPartial(
                  uint256 target,
                  uint256 numerator,
                  uint256 denominator
              ) internal pure returns (uint256) {
                  return target.mul(numerator).div(denominator);
              }
              /*
               * Return target * (numerator / denominator), but rounded up.
               */
              function getPartialRoundUp(
                  uint256 target,
                  uint256 numerator,
                  uint256 denominator
              ) internal pure returns (uint256) {
                  if (target == 0 || numerator == 0) {
                      // SafeMath will check for zero denominator
                      return SafeMath.div(0, denominator);
                  }
                  return target.mul(numerator).sub(1).div(denominator).add(1);
              }
              function to128(uint256 number) internal pure returns (uint128) {
                  uint128 result = uint128(number);
                  require(result == number, "Math: Unsafe cast to uint128");
                  return result;
              }
              function to96(uint256 number) internal pure returns (uint96) {
                  uint96 result = uint96(number);
                  require(result == number, "Math: Unsafe cast to uint96");
                  return result;
              }
              function to32(uint256 number) internal pure returns (uint32) {
                  uint32 result = uint32(number);
                  require(result == number, "Math: Unsafe cast to uint32");
                  return result;
              }
              function min(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a < b ? a : b;
              }
              function max(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a > b ? a : b;
              }
          }
          // SPDX-License-Identifier: GPL-3.0
          pragma solidity 0.6.8;
          pragma experimental ABIEncoderV2;
          import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
          import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
          import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
          import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
          import {Decimal} from "./Decimal.sol";
          import {Media} from "./Media.sol";
          import {IMarket} from "./interfaces/IMarket.sol";
          /**
           * @title A Market for pieces of media
           * @notice This contract contains all of the market logic for Media
           */
          contract Market is IMarket {
              using SafeMath for uint256;
              using SafeERC20 for IERC20;
              /* *******
               * Globals
               * *******
               */
              // Address of the media contract that can call this market
              address public mediaContract;
              // Deployment Address
              address private _owner;
              // Mapping from token to mapping from bidder to bid
              mapping(uint256 => mapping(address => Bid)) private _tokenBidders;
              // Mapping from token to the bid shares for the token
              mapping(uint256 => BidShares) private _bidShares;
              // Mapping from token to the current ask for the token
              mapping(uint256 => Ask) private _tokenAsks;
              /* *********
               * Modifiers
               * *********
               */
              /**
               * @notice require that the msg.sender is the configured media contract
               */
              modifier onlyMediaCaller() {
                  require(mediaContract == msg.sender, "Market: Only media contract");
                  _;
              }
              /* ****************
               * View Functions
               * ****************
               */
              function bidForTokenBidder(uint256 tokenId, address bidder)
                  external
                  view
                  override
                  returns (Bid memory)
              {
                  return _tokenBidders[tokenId][bidder];
              }
              function currentAskForToken(uint256 tokenId)
                  external
                  view
                  override
                  returns (Ask memory)
              {
                  return _tokenAsks[tokenId];
              }
              function bidSharesForToken(uint256 tokenId)
                  public
                  view
                  override
                  returns (BidShares memory)
              {
                  return _bidShares[tokenId];
              }
              /**
               * @notice Validates that the bid is valid by ensuring that the bid amount can be split perfectly into all the bid shares.
               *  We do this by comparing the sum of the individual share values with the amount and ensuring they are equal. Because
               *  the splitShare function uses integer division, any inconsistencies with the original and split sums would be due to
               *  a bid splitting that does not perfectly divide the bid amount.
               */
              function isValidBid(uint256 tokenId, uint256 bidAmount)
                  public
                  view
                  override
                  returns (bool)
              {
                  BidShares memory bidShares = bidSharesForToken(tokenId);
                  require(
                      isValidBidShares(bidShares),
                      "Market: Invalid bid shares for token"
                  );
                  return
                      bidAmount != 0 &&
                      (bidAmount ==
                          splitShare(bidShares.creator, bidAmount)
                              .add(splitShare(bidShares.prevOwner, bidAmount))
                              .add(splitShare(bidShares.owner, bidAmount)));
              }
              /**
               * @notice Validates that the provided bid shares sum to 100
               */
              function isValidBidShares(BidShares memory bidShares)
                  public
                  pure
                  override
                  returns (bool)
              {
                  return
                      bidShares.creator.value.add(bidShares.owner.value).add(
                          bidShares.prevOwner.value
                      ) == uint256(100).mul(Decimal.BASE);
              }
              /**
               * @notice return a % of the specified amount. This function is used to split a bid into shares
               * for a media's shareholders.
               */
              function splitShare(Decimal.D256 memory sharePercentage, uint256 amount)
                  public
                  pure
                  override
                  returns (uint256)
              {
                  return Decimal.mul(amount, sharePercentage).div(100);
              }
              /* ****************
               * Public Functions
               * ****************
               */
              constructor() public {
                  _owner = msg.sender;
              }
              /**
               * @notice Sets the media contract address. This address is the only permitted address that
               * can call the mutable functions. This method can only be called once.
               */
              function configure(address mediaContractAddress) external override {
                  require(msg.sender == _owner, "Market: Only owner");
                  require(mediaContract == address(0), "Market: Already configured");
                  require(
                      mediaContractAddress != address(0),
                      "Market: cannot set media contract as zero address"
                  );
                  mediaContract = mediaContractAddress;
              }
              /**
               * @notice Sets bid shares for a particular tokenId. These bid shares must
               * sum to 100.
               */
              function setBidShares(uint256 tokenId, BidShares memory bidShares)
                  public
                  override
                  onlyMediaCaller
              {
                  require(
                      isValidBidShares(bidShares),
                      "Market: Invalid bid shares, must sum to 100"
                  );
                  _bidShares[tokenId] = bidShares;
                  emit BidShareUpdated(tokenId, bidShares);
              }
              /**
               * @notice Sets the ask on a particular media. If the ask cannot be evenly split into the media's
               * bid shares, this reverts.
               */
              function setAsk(uint256 tokenId, Ask memory ask)
                  public
                  override
                  onlyMediaCaller
              {
                  require(
                      isValidBid(tokenId, ask.amount),
                      "Market: Ask invalid for share splitting"
                  );
                  _tokenAsks[tokenId] = ask;
                  emit AskCreated(tokenId, ask);
              }
              /**
               * @notice removes an ask for a token and emits an AskRemoved event
               */
              function removeAsk(uint256 tokenId) external override onlyMediaCaller {
                  emit AskRemoved(tokenId, _tokenAsks[tokenId]);
                  delete _tokenAsks[tokenId];
              }
              /**
               * @notice Sets the bid on a particular media for a bidder. The token being used to bid
               * is transferred from the spender to this contract to be held until removed or accepted.
               * If another bid already exists for the bidder, it is refunded.
               */
              function setBid(
                  uint256 tokenId,
                  Bid memory bid,
                  address spender
              ) public override onlyMediaCaller {
                  BidShares memory bidShares = _bidShares[tokenId];
                  require(
                      bidShares.creator.value.add(bid.sellOnShare.value) <=
                          uint256(100).mul(Decimal.BASE),
                      "Market: Sell on fee invalid for share splitting"
                  );
                  require(bid.bidder != address(0), "Market: bidder cannot be 0 address");
                  require(bid.amount != 0, "Market: cannot bid amount of 0");
                  require(
                      bid.currency != address(0),
                      "Market: bid currency cannot be 0 address"
                  );
                  require(
                      bid.recipient != address(0),
                      "Market: bid recipient cannot be 0 address"
                  );
                  Bid storage existingBid = _tokenBidders[tokenId][bid.bidder];
                  // If there is an existing bid, refund it before continuing
                  if (existingBid.amount > 0) {
                      removeBid(tokenId, bid.bidder);
                  }
                  IERC20 token = IERC20(bid.currency);
                  // We must check the balance that was actually transferred to the market,
                  // as some tokens impose a transfer fee and would not actually transfer the
                  // full amount to the market, resulting in locked funds for refunds & bid acceptance
                  uint256 beforeBalance = token.balanceOf(address(this));
                  token.safeTransferFrom(spender, address(this), bid.amount);
                  uint256 afterBalance = token.balanceOf(address(this));
                  _tokenBidders[tokenId][bid.bidder] = Bid(
                      afterBalance.sub(beforeBalance),
                      bid.currency,
                      bid.bidder,
                      bid.recipient,
                      bid.sellOnShare
                  );
                  emit BidCreated(tokenId, bid);
                  // If a bid meets the criteria for an ask, automatically accept the bid.
                  // If no ask is set or the bid does not meet the requirements, ignore.
                  if (
                      _tokenAsks[tokenId].currency != address(0) &&
                      bid.currency == _tokenAsks[tokenId].currency &&
                      bid.amount >= _tokenAsks[tokenId].amount
                  ) {
                      // Finalize exchange
                      _finalizeNFTTransfer(tokenId, bid.bidder);
                  }
              }
              /**
               * @notice Removes the bid on a particular media for a bidder. The bid amount
               * is transferred from this contract to the bidder, if they have a bid placed.
               */
              function removeBid(uint256 tokenId, address bidder)
                  public
                  override
                  onlyMediaCaller
              {
                  Bid storage bid = _tokenBidders[tokenId][bidder];
                  uint256 bidAmount = bid.amount;
                  address bidCurrency = bid.currency;
                  require(bid.amount > 0, "Market: cannot remove bid amount of 0");
                  IERC20 token = IERC20(bidCurrency);
                  emit BidRemoved(tokenId, bid);
                  delete _tokenBidders[tokenId][bidder];
                  token.safeTransfer(bidder, bidAmount);
              }
              /**
               * @notice Accepts a bid from a particular bidder. Can only be called by the media contract.
               * See {_finalizeNFTTransfer}
               * Provided bid must match a bid in storage. This is to prevent a race condition
               * where a bid may change while the acceptBid call is in transit.
               * A bid cannot be accepted if it cannot be split equally into its shareholders.
               * This should only revert in rare instances (example, a low bid with a zero-decimal token),
               * but is necessary to ensure fairness to all shareholders.
               */
              function acceptBid(uint256 tokenId, Bid calldata expectedBid)
                  external
                  override
                  onlyMediaCaller
              {
                  Bid memory bid = _tokenBidders[tokenId][expectedBid.bidder];
                  require(bid.amount > 0, "Market: cannot accept bid of 0");
                  require(
                      bid.amount == expectedBid.amount &&
                          bid.currency == expectedBid.currency &&
                          bid.sellOnShare.value == expectedBid.sellOnShare.value &&
                          bid.recipient == expectedBid.recipient,
                      "Market: Unexpected bid found."
                  );
                  require(
                      isValidBid(tokenId, bid.amount),
                      "Market: Bid invalid for share splitting"
                  );
                  _finalizeNFTTransfer(tokenId, bid.bidder);
              }
              /**
               * @notice Given a token ID and a bidder, this method transfers the value of
               * the bid to the shareholders. It also transfers the ownership of the media
               * to the bid recipient. Finally, it removes the accepted bid and the current ask.
               */
              function _finalizeNFTTransfer(uint256 tokenId, address bidder) private {
                  Bid memory bid = _tokenBidders[tokenId][bidder];
                  BidShares storage bidShares = _bidShares[tokenId];
                  IERC20 token = IERC20(bid.currency);
                  // Transfer bid share to owner of media
                  token.safeTransfer(
                      IERC721(mediaContract).ownerOf(tokenId),
                      splitShare(bidShares.owner, bid.amount)
                  );
                  // Transfer bid share to creator of media
                  token.safeTransfer(
                      Media(mediaContract).tokenCreators(tokenId),
                      splitShare(bidShares.creator, bid.amount)
                  );
                  // Transfer bid share to previous owner of media (if applicable)
                  token.safeTransfer(
                      Media(mediaContract).previousTokenOwners(tokenId),
                      splitShare(bidShares.prevOwner, bid.amount)
                  );
                  // Transfer media to bid recipient
                  Media(mediaContract).auctionTransfer(tokenId, bid.recipient);
                  // Calculate the bid share for the new owner,
                  // equal to 100 - creatorShare - sellOnShare
                  bidShares.owner = Decimal.D256(
                      uint256(100)
                          .mul(Decimal.BASE)
                          .sub(_bidShares[tokenId].creator.value)
                          .sub(bid.sellOnShare.value)
                  );
                  // Set the previous owner share to the accepted bid's sell-on fee
                  bidShares.prevOwner = bid.sellOnShare;
                  // Remove the accepted bid
                  delete _tokenBidders[tokenId][bidder];
                  emit BidShareUpdated(tokenId, bidShares);
                  emit BidFinalized(tokenId, bid);
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.6.0;
          import "./IERC20.sol";
          import "../../math/SafeMath.sol";
          import "../../utils/Address.sol";
          /**
           * @title SafeERC20
           * @dev Wrappers around ERC20 operations that throw on failure (when the token
           * contract returns false). Tokens that return no value (and instead revert or
           * throw on failure) are also supported, non-reverting calls are assumed to be
           * successful.
           * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
           * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
           */
          library SafeERC20 {
              using SafeMath for uint256;
              using Address for address;
              function safeTransfer(IERC20 token, address to, uint256 value) internal {
                  _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
              }
              function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                  _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
              }
              /**
               * @dev Deprecated. This function has issues similar to the ones found in
               * {IERC20-approve}, and its usage is discouraged.
               *
               * Whenever possible, use {safeIncreaseAllowance} and
               * {safeDecreaseAllowance} instead.
               */
              function safeApprove(IERC20 token, address spender, uint256 value) internal {
                  // safeApprove should only be called when setting an initial allowance,
                  // or when resetting it to zero. To increase and decrease it, use
                  // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                  // solhint-disable-next-line max-line-length
                  require((value == 0) || (token.allowance(address(this), spender) == 0),
                      "SafeERC20: approve from non-zero to non-zero allowance"
                  );
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
              }
              function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                  uint256 newAllowance = token.allowance(address(this), spender).add(value);
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
              }
              function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                  uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
              }
              /**
               * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
               * on the return value: the return value is optional (but if data is returned, it must not be false).
               * @param token The token targeted by the call.
               * @param data The call data (encoded using abi.encode or one of its variants).
               */
              function _callOptionalReturn(IERC20 token, bytes memory data) private {
                  // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                  // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                  // the target address contains contract code and also asserts for success in the low-level call.
                  bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                  if (returndata.length > 0) { // Return data is optional
                      // solhint-disable-next-line max-line-length
                      require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                  }
              }
          }