contract CheezyExchange is CheezyExchangeAdmin {

    // OpenZeppelin's SafeMath library is used for all arithmetic operations to
    // avoid overflows/underflows.
    using SafeMath for uint256;

	/* ********** */
    /* DATA TYPES */
    /* ********** */

    /// @notice The main Order struct. The struct fits into two 256-bit words
    ///  due to Solidity's rules for struct packing, saving gas.
    struct Order {
        // The tokenId for the seller's wizard. This is the wizard that will
        // relinquish its power if an order is fulfilled.
        uint256 wizardId;
        // An order creator specifies how much wei they are willing to sell
        // their wizard's power for. Note that this price is per-power, so
        // the buyer will actually have to pay this price multiplied by the
        // amount of power that the wizard has.
        uint128 pricePerPower;
        // The address of the creator of the order, the seller, the owner
        // of the wizard whose power will be relinquished.
		address makerAddress;
        // The address of the tournament that this order pertains to. Note that
        // each Wizard has an independent power value for each tournament, and
        // call sell each one separately, so orders can exist simultaneously
        // for the same wizard for different tournaments.
		address basicTournamentAddress;
        // We save the dev fee at the time of order creation. This prevents an
        // attack where the dev could frontrun an order acceptance with a
        // change to the dev fee.
        uint16 savedSuccessfulTradeFeeInBasisPoints;
    }

    /* ****** */
    /* EVENTS */
    /* ****** */

    /// @notice This event is fired a user creates an order selling their wizard's
    ///  power, but not the NFT itself.
    /// @param wizardId  The tokenId for the seller's wizard. This is the
    ///  wizard that will relinquish its power if an order is fulfilled.
    /// @param pricePerPower  An order creator specifies how much wei they are
    ///  willing to sell their wizard's power for. Note that this price is
    ///  per-power, so the buyer will actually have to pay this price
    ///  multiplied by the amount of power that the wizard has.
    /// @param makerAddress  The address of the creator of the order, the
    ///  seller, the owner of the wizard whose power will be relinquished.
    /// @param basicTournamentAddress  The address of the tournament that this
    ///  order pertains to. Note that each Wizard has an independent power
    ///  value for each tournament, and call sell each one separately, so
    ///  orders can exist simultaneously for the same wizard for different
    ///  tournaments.
    /// @param savedSuccessfulTradeFeeInBasisPoints  We save the dev fee at the
    ///  time of order creation. This prevents an attack where the dev could
    ///  frontrun an order acceptance with a change to the dev fee.
    event CreateSellOrder(
    	uint256 wizardId,
        uint256 pricePerPower,
		address makerAddress,
        address basicTournamentAddress,
        uint256 savedSuccessfulTradeFeeInBasisPoints
    );

    /// @notice This event is fired a user updates an existing order to have a
    ///  different pricePerPower. It saves a small amount of gas to only change
    ///  that one part of the oder.
    /// @param wizardId  The tokenId for the seller's wizard. This is the
    ///  wizard that will relinquish its power if an order is fulfilled.
    /// @param oldPricePerPower  This is the old pricePerPower, the price that
    ///  is being replaced in this update() call. An order creator specifies
    ///  how much wei they are willing to sell their wizard's power for. Note
    ///  that this price is per-power, so the buyer will actually have to pay
    ///  this price multiplied by the amount of power that the wizard has.
    /// @param newPricePerPower  This is the new pricePerPower, the price that
    ///  will now be valid after this update() call. An order creator specifies
    ///  how much wei they are willing to sell their wizard's power for. Note
    ///  that this price is per-power, so the buyer will actually have to pay
    ///  this price multiplied by the amount of power that the wizard has.
    /// @param makerAddress  The address of the creator of the order, the
    ///  seller, the owner of the wizard whose power will be relinquished.
    /// @param basicTournamentAddress  The address of the tournament that this
    ///  order pertains to. Note that each Wizard has an independent power
    ///  value for each tournament, and call sell each one separately, so
    ///  orders can exist simultaneously for the same wizard for different
    ///  tournaments.
    /// @param savedSuccessfulTradeFeeInBasisPoints  We save the dev fee at the
    ///  time of order creation. This prevents an attack where the dev could
    ///  frontrun an order acceptance with a change to the dev fee.
    event UpdateSellOrder(
    	uint256 wizardId,
        uint256 oldPricePerPower,
        uint256 newPricePerPower,
		address makerAddress,
        address basicTournamentAddress,
        uint256 savedSuccessfulTradeFeeInBasisPoints
    );

    /// @notice This event is fired a user cancels an existing order. It is also
    ///  fired when a user calls createSellOrder() for a wizard that already
    ///  has a (potentially invalid) order on the books.
    /// @dev The reason that this contract emits the CancelSellOrder event
    ///  when a user calls createSellOrder() for a wizard that already
    ///  has a (potentially invalid) order on the books is illustrated by the
    ///  following scenario: if Alice creates an order for Wizard1 on this
    ///  contract, but then sells Wizard1 to Bob, then this contract will still
    ///  have an order on the books for Wizard1. However, this contract will
    ///  not allow that order to be fulfilled, since the owner of Wizard1 has
    ///  changed, and this contract will only view an order as valid if the
    ///  wizard is still owned by the original order creator. Thus, if Bob is
    ///  now trying to create a sell order for Wizard1, we need to delete
    ///  Alice's old order.
    /// @param wizardId  The tokenId for the seller's wizard. This is the
    ///  wizard that will relinquish its power if an order is fulfilled.
    /// @param pricePerPower  An order creator specifies how much wei they are
    ///  willing to sell their wizard's power for. Note that this price is
    ///  per-power, so the buyer will actually have to pay this price
    ///  multiplied by the amount of power that the wizard has.
    /// @param makerAddress  The address of the creator of the order, the
    ///  seller, the owner of the wizard whose power will be relinquished.
    /// @param basicTournamentAddress  The address of the tournament that this
    ///  order pertains to. Note that each Wizard has an independent power
    ///  value for each tournament, and call sell each one separately, so
    ///  orders can exist simultaneously for the same wizard for different
    ///  tournaments.
    /// @param savedSuccessfulTradeFeeInBasisPoints  We save the dev fee at the
    ///  time of order creation. This prevents an attack where the dev could
    ///  frontrun an order acceptance with a change to the dev fee.
    event CancelSellOrder(
    	uint256 wizardId,
        uint256 pricePerPower,
		address makerAddress,
        address basicTournamentAddress,
        uint256 savedSuccessfulTradeFeeInBasisPoints
    );

    /// @notice This event is fired a buyer successfully fills a seller's order.
    /// @param makerWizardId  The tokenId for the seller's wizard. This is the
    ///  wizard that will relinquish its power if an order is fulfilled.
    /// @param takerWizardId  The tokenId for the buyer's wizard. This is the
    ///  wizard that will receive its power if an order is fulfilled.
    /// @param pricePerPower  An order creator specifies how much wei they are
    ///  willing to sell their wizard's power for. Note that this price is
    ///  per-power, so the buyer will actually have to pay this price
    ///  multiplied by the amount of power that the wizard has.
    /// @param makerAddress  The address of the creator of the order, the
    ///  seller, the owner of the wizard whose power will be relinquished.
    /// @param takerAddress  The address of the fulfiller of the order, the
    ///  buyer, the owner of the wizard who will receive power.
    /// @param basicTournamentAddress  The address of the tournament that this
    ///  order pertains to. Note that each Wizard has an independent power
    ///  value for each tournament, and call sell each one separately, so
    ///  orders can exist simultaneously for the same wizard for different
    ///  tournaments.
    /// @param savedSuccessfulTradeFeeInBasisPoints  We save the dev fee at the
    ///  time of order creation. This prevents an attack where the dev could
    ///  frontrun an order acceptance with a change to the dev fee.
    event FillSellOrder(
    	uint256 makerWizardId,
        uint256 takerWizardId,
        uint256 pricePerPower,
		address makerAddress,
        address takerAddress,
        address basicTournamentAddress,
        uint256 savedSuccessfulTradeFeeInBasisPoints
    );

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

    /// @notice A mapping that tracks current order structs, indexed first by
    ///  tournament address and then by wizardId.
    /// @dev This contract is generalized to accept orders for multiple
    ///  cheezewizard tournaments, potentially simultaneously, potentially even
    ///  for the same wizard. This is because power is tournament-specific, so
    ///  the same wizard can have different amounts of power in different
    ///  tournaments.
    mapping(address => mapping(uint256 => Order)) internal orderForWizardIdAndTournamentAddress;

    /// @notice A simple ticker keeping track of the pricePerPower of the last
    ///  successful sale for this particular tournament. This could be useful
    ///  for displaying on a frontend UI as a guide for new orders, or the
    ///  current state of the market.
    mapping(address => uint256) public lastSuccessfulPricePerPowerForTournamentAddress;

    /* ********* */
    /* FUNCTIONS */
    /* ********* */

    /// @notice After calling approve() in the CheezeWizards Core contract
    ///  (called WizardGuild.sol), a seller can post an order to sell the power
    ///  of one of their wizards for a particular tournament (found in the
    ///  contract BasicTournament.sol). Note that this only sells the wizard's
    ///  power, but the seller will retain the NFT itself.
    /// @dev The reason that this contract emits the CancelSellOrder event
    ///  when a user calls createSellOrder() for a wizard that already
    ///  has a (potentially invalid) order on the books is illustrated by the
    ///  following scenario: if Alice creates an order for Wizard1 on this
    ///  contract, but then sells Wizard1 to Bob, then this contract will still
    ///  have an order on the books for Wizard1. However, this contract will
    ///  not allow that order to be fulfilled, since the owner of Wizard1 has
    ///  changed, and this contract will only view an order as valid if the
    ///  wizard is still owned by the original order creator. Thus, if Bob is
    ///  now trying to create a sell order for Wizard1, we need to delete
    ///  Alice's old order.
    /// @param _wizardId  The tokenId for the seller's wizard. This is the
    ///  wizard that will relinquish its power if an order is fulfilled.
    /// @param _pricePerPower  An order creator specifies how much wei they are
    ///  willing to sell their wizard's power for. Note that this price is
    ///  per-power, so the buyer will actually have to pay this price
    ///  multiplied by the amount of power that the wizard has.
    /// @param _basicTournamentAddress  The address of the tournament that this
    ///  order pertains to. Note that each Wizard has an independent power
    ///  value for each tournament, and call sell each one separately, so
    ///  orders can exist simultaneously for the same wizard for different
    ///  tournaments.
    function createSellOrder(uint256 _wizardId, uint256 _pricePerPower, address _basicTournamentAddress) external whenNotPaused nonReentrant {
        require(WizardGuild(wizardGuildAddress).ownerOf(_wizardId) == msg.sender, 'only the owner of the wizard can create a sell order');
        require(WizardGuild(wizardGuildAddress).isApprovedOrOwner(address(this), _wizardId), 'you must call the approve() function on WizardGuild before you can create a sell order');
        require(_pricePerPower <= uint256(~uint128(0)), 'you cannot specify a _pricePerPower greater than uint128_max');

        // Fetch wizard's stats from BasicTournament contract
        bool molded;
        bool ready;
        ( , , , , , , , molded, ready) = BasicTournament(_basicTournamentAddress).getWizard(_wizardId);
        require(molded == false, 'you cannot sell the power from a molded wizard');
        require(ready == true, 'you cannot sell the power from a wizard that is not ready');

        Order memory previousOrder = orderForWizardIdAndTournamentAddress[_basicTournamentAddress][_wizardId];

        // Check if an order already exists for this wizard within this
        // tournament
        if(previousOrder.makerAddress != address(0)){
            // If an order already exists, delete it before we create a new one
            emit CancelSellOrder(
                uint256(previousOrder.wizardId),
                uint256(previousOrder.pricePerPower),
                previousOrder.makerAddress,
                previousOrder.basicTournamentAddress,
                uint256(previousOrder.savedSuccessfulTradeFeeInBasisPoints)
            );
            delete orderForWizardIdAndTournamentAddress[_basicTournamentAddress][_wizardId];
        }

        // Save the new order to storage
        Order memory order = Order({
            wizardId: uint256(_wizardId),
            pricePerPower: uint128(_pricePerPower),
            makerAddress: msg.sender,
            basicTournamentAddress: _basicTournamentAddress,
            savedSuccessfulTradeFeeInBasisPoints: uint16(successfulTradeFeeInBasisPoints)
        });
        orderForWizardIdAndTournamentAddress[_basicTournamentAddress][_wizardId] = order;
        emit CreateSellOrder(
            _wizardId,
            _pricePerPower,
            msg.sender,
            _basicTournamentAddress,
            successfulTradeFeeInBasisPoints
        );
    }

    /// @notice A seller/maker calls this function to update the pricePerPower
    ///  of an order that they already have stored in this contract. This saves
    ///  some gas for the seller by allowing them to just update the
    ///  pricePerPower parameter of their order.
    /// @param _wizardId  The tokenId for the seller's wizard. This is the
    ///  wizard that will relinquish its power if an order is fulfilled.
    /// @param _newPricePerPower  An order creator specifies how much wei they
    ///  are willing to sell their wizard's power for. Note that this price is
    ///  per-power, so the buyer will actually have to pay this price
    ///  multiplied by the amount of power that the wizard has.
    /// @param _basicTournamentAddress  The address of the tournament that this
    ///  order pertains to. Note that each Wizard has an independent power
    ///  value for each tournament, and call sell each one separately, so
    ///  orders can exist simultaneously for the same wizard for different
    ///  tournaments.
    function updateSellOrder(uint256 _wizardId, uint256 _newPricePerPower, address _basicTournamentAddress) external whenNotPaused nonReentrant {
        require(WizardGuild(wizardGuildAddress).ownerOf(_wizardId) == msg.sender, 'only the owner of the wizard can update a sell order');
        require(WizardGuild(wizardGuildAddress).isApprovedOrOwner(address(this), _wizardId), 'you must call the approve() function on WizardGuild before you can update a sell order');
        require(_newPricePerPower <= uint256(~uint128(0)), 'you cannot specify a _newPricePerPower greater than uint128_max');

        // Fetch order
        Order storage order = orderForWizardIdAndTournamentAddress[_basicTournamentAddress][_wizardId];

        // Check that order is not from a previous owner
        require(msg.sender == order.makerAddress, 'you can only update a sell order that you created');

        // Emit event
        emit UpdateSellOrder(
            _wizardId,
            uint256(order.pricePerPower),
            _newPricePerPower,
            msg.sender,
            _basicTournamentAddress,
            uint256(order.savedSuccessfulTradeFeeInBasisPoints)
        );

        // Update price
        order.pricePerPower = uint128(_newPricePerPower);
    }

    /// @notice A seller/maker calls this function to cancel an existing order
    ///  that they have posted to this contract.
    /// @dev Unlike the other Order functions, this function can be called even
    ///  if the contract is paused, so that users can clear out their orderbook
    ///  if they would like.
    /// @param _wizardId  The tokenId for the seller's wizard. This is the
    ///  wizard that will relinquish its power if an order is fulfilled.
    /// @param _basicTournamentAddress  The address of the tournament that this
    ///  order pertains to. Note that each Wizard has an independent power
    ///  value for each tournament, and call sell each one separately, so
    ///  orders can exist simultaneously for the same wizard for different
    ///  tournaments.
    function cancelSellOrder(uint256 _wizardId, address _basicTournamentAddress) external nonReentrant {
        require(WizardGuild(wizardGuildAddress).ownerOf(_wizardId) == msg.sender, 'only the owner of the wizard can cancel a sell order');

        // Wait until after emitting event to delete order so that event data
        // can be pulled from soon-to-be-deleted order
        Order memory order = orderForWizardIdAndTournamentAddress[_basicTournamentAddress][_wizardId];
        emit CancelSellOrder(
            uint256(order.wizardId),
            uint256(order.pricePerPower),
            msg.sender,
            _basicTournamentAddress,
            uint256(order.savedSuccessfulTradeFeeInBasisPoints)
        );

        // Delete order
        delete orderForWizardIdAndTournamentAddress[_basicTournamentAddress][_wizardId];
    }

    /// @notice A buyer/taker calls this function to complete an order, paying
    ///  pricePerPower multiplied by the makerWizard's current power in order
    ///  to transfer the makerWizard's power to the takerWizard.
    /// @dev The buyer/taker must send enough wei along with this function call
    ///  to cover pricePerPower multiplied by the makerWizard's current power.
    /// @param _makerWizardId  The tokenId for the seller's wizard. This is the
    ///  wizard that will relinquish its power if an order is fulfilled.
    /// @param _takerWizardId  The tokenId for the buyer's wizard. This is the
    ///  wizard that will receive its power if an order is fulfilled.
    /// @param _basicTournamentAddress  The address of the tournament that this
    ///  order pertains to. Note that each Wizard has an independent power
    ///  value for each tournament, and call sell each one separately, so
    ///  orders can exist simultaneously for the same wizard for different
    ///  tournaments.
    /// @param _referrer  This address gets half of the successful trade fees.
    ///  This encourages third party developers to develop their own front-end
    ///  UI's for this contract in order to receive half of the rewards.
    function fillSellOrder(uint256 _makerWizardId, uint256 _takerWizardId, address _basicTournamentAddress, address _referrer) external payable whenNotPaused nonReentrant {
        require(WizardGuild(wizardGuildAddress).ownerOf(_takerWizardId) == msg.sender, 'you can only purchase power for a wizard that you own');
        Order memory order = orderForWizardIdAndTournamentAddress[_basicTournamentAddress][_makerWizardId];
        require(WizardGuild(wizardGuildAddress).ownerOf(_makerWizardId) == order.makerAddress, 'an order is only valid while the order creator owns the wizard');

        // Fetch wizard's stats from BasicTournament contract
        uint256 power;
        bool molded;
        bool ready;
        ( , power, , , , , , molded, ready) = BasicTournament(_basicTournamentAddress).getWizard(_makerWizardId);
        require(molded == false, 'you cannot sell the power from a molded wizard');
        require(ready == true, 'you cannot sell the power from a wizard that is not ready');

        // Update the global ticker for the last successful pricePerPower sale
        // price
        lastSuccessfulPricePerPowerForTournamentAddress[_basicTournamentAddress] = uint256(order.pricePerPower);

        // Calculate seller proceeds and contractCreator fees
        uint256 priceToFillOrder = (uint256(order.pricePerPower)).mul(power);
        require(msg.value >= priceToFillOrder, 'you did not send enough wei to fulfill this order');
        uint256 sellerProceeds = _computeSellerProceeds(priceToFillOrder, uint256(order.savedSuccessfulTradeFeeInBasisPoints));
        uint256 fees = priceToFillOrder.sub(sellerProceeds);
        uint256 excess = (msg.value).sub(priceToFillOrder);

        // Save the seller's address before we delete the order
        address payable orderMakerAddress = address(uint160(order.makerAddress));

        // Emit event
        emit FillSellOrder(
            uint256(order.wizardId),
            _takerWizardId,
            uint256(order.pricePerPower),
            address(order.makerAddress),
            msg.sender,
            order.basicTournamentAddress,
            uint256(order.savedSuccessfulTradeFeeInBasisPoints)
        );

        // Delete the order prior to sending any funds
        delete orderForWizardIdAndTournamentAddress[_basicTournamentAddress][_makerWizardId];

        // Transfer Power from maker wizard to taker wizard
        BasicTournament(_basicTournamentAddress).giftPower(_makerWizardId, _takerWizardId);

        // Send proceeds to seller/maker
        orderMakerAddress.transfer(sellerProceeds);

        // Store fees earned by contract creator in contract until creator calls
        // withdrawsEarnings()
        // This prevents a halting condition that would occur if one of the fee
        // earners rejects incoming transfers
        if(_referrer != address(0)){
            uint256 halfOfFees = fees.div(uint256(2));
            addressToFeeEarnings[_referrer] = addressToFeeEarnings[_referrer].add(halfOfFees);
            addressToFeeEarnings[owner()] = addressToFeeEarnings[owner()].add(halfOfFees);
        } else {
            addressToFeeEarnings[owner()] = addressToFeeEarnings[owner()].add(fees);
        }

        // Send any excess wei back to buyer/taker
        if(excess > 0){
            msg.sender.transfer(excess);
        }
    }

    /// @notice A simple getter function to return an order for a given wizard
    ///  and tournament address.
    /// @dev The reason that we cannot simply use Solidity's automatically
    ///  created getter functions is that the orders are stored in a nested
    ///  mapping, which messes with Solidity's automatically created getters.
    /// @param _wizardId The id of the wizard NFT whose order we would like to
    ///  see the details of.
    /// @param _basicTournamentAddress The address of the tournament that this
    ///  order pertains to. Note that each Wizard has an independent power
    ///  value for each tournament, and call sell each one separately.
    /// @return The values from the Order struct, returned in tuple-form.
    function getOrder(uint256 _wizardId, address _basicTournamentAddress) external view returns (uint256 wizardId, uint256 pricePerPower, address makerAddress, address basicTournamentAddress, uint256 savedSuccessfulTradeFeeInBasisPoints){
        Order memory order = orderForWizardIdAndTournamentAddress[_basicTournamentAddress][_wizardId];
        return (uint256(order.wizardId), uint256(order.pricePerPower), address(order.makerAddress), address(order.basicTournamentAddress), uint256(order.savedSuccessfulTradeFeeInBasisPoints));
    }

    /// @notice A convenience function providing the caller with the current
    ///  amount needed (in wei) to fulfill this order.
    /// @param _wizardId The id of the wizard NFT whose order we would like to
    ///  see the details of.
    /// @param _basicTournamentAddress The address of the tournament that this
    ///  order pertains to. Note that each Wizard has an independent power
    ///  value for each tournament, and call sell each one separately.
    /// @return The amount needed (in wei) to fulfill this order
    function getCurrentPriceForOrder(uint256 _wizardId, address _basicTournamentAddress) external view returns (uint256){
        Order memory order = orderForWizardIdAndTournamentAddress[_basicTournamentAddress][_wizardId];
        uint256 power;
        ( , power, , , , , , , ) = BasicTournament(_basicTournamentAddress).getWizard(_wizardId);
        uint256 price = power.mul(uint256(order.pricePerPower));
        return price;
    }

    /// @notice A convenience function checking whether the order is currently
    ///  valid. Note that an order can become invalid for a period of time, but
    ///  then become valid again (for example, during the time that a wizard
    ///  is dueling).
    /// @param _wizardId The id of the wizard NFT whose order we would like to
    ///  see the details of.
    /// @param _basicTournamentAddress The address of the tournament that this
    ///  order pertains to. Note that each Wizard has an independent power
    ///  value for each tournament, and call sell each one separately.
    /// @return Whether the order is currently valid or not.
    function getIsOrderCurrentlyValid(uint256 _wizardId, address _basicTournamentAddress) external view returns (bool){
        Order memory order = orderForWizardIdAndTournamentAddress[_basicTournamentAddress][_wizardId];
        if(order.makerAddress == address(0)){
            // Order is not valid if order does not exist
            return false;
        } else {

            if(WizardGuild(wizardGuildAddress).ownerOf(_wizardId) != order.makerAddress){
                // Order is not valid if order's creator no longer owns wizard
                return false;
            }
            bool molded;
            bool ready;
            ( , , , , , , , molded, ready) = BasicTournament(_basicTournamentAddress).getWizard(_wizardId);
            if(molded == true || ready == false){
                // Order is not valid if makerWizard is molded or is dueling
                return false;
            } else {
                return true;
            }
        }
    }

    /// @notice Computes the seller proceeds given a total value sent,
    ///  and the successfulTradeFee in percentage basis points that was saved
    ///  at the time of the order's creation.
    /// @dev 10000 is not a magic number, but is the maximum number of basis
    ///  points that can exist (with basis points being hundredths of a
    ///  percent).
    /// @param _totalValueIncludingFees The amount of ether (in wei) that was
    ///  sent to complete the trade
    /// @param _successfulTradeFeeInBasisPoints The percentage (in basis points)
    ///  of that total amount that will be taken as a fee if the trade is
    ///  successfully completed.
    /// @return The amount of ether (in wei) that will be sent to the seller if
    ///  the trade is successfully completed
    function _computeSellerProceeds(uint256 _totalValueIncludingFees, uint256 _successfulTradeFeeInBasisPoints) internal pure returns (uint256) {
    	return (_totalValueIncludingFees.mul(uint256(10000).sub(_successfulTradeFeeInBasisPoints))).div(uint256(10000));
    }

    /// @dev By calling 'revert' in the fallback function, we prevent anyone from
    ///  accidentally sending funds directly to this contract.
    function() external payable {
        revert();
    }
}
/// @title Main contract for CheezyExchange. This contract manages the creation,
///  editing, and fulfillment of trades of CheezeWizard power only, not the NFT
///  itself.
/// @notice Consider each CheezeWizard NFT as a vessel that holds power. This
///  contract allows a seller to list their Wizard's power for a specified
///  price. If a buyer accepts their order, then they pay the specified price
///  multiplied by the amount of power that the Wizard has, and all of the
///  wizard's power is transferred from the seller's wizard to the buyer's
///  wizard. This leaves the seller's wizard with no power, but the seller
///  still possesses the wizard NFT.
/// @notice Power is tournament-specific. Multiple CheezeWizard tournaments can
///  exist, even at the same time. A single wizard can be entered into
///  multiple tournaments at once, and can have different power levels in each
///  tournament. This contract contains a different orderbook for each
///  tournament. This means that this contract can simultaneously accept orders
///  for multiple tournaments at the same time, even for the same wizard.
function getCurrentPriceForOrder(uint256 _wizardId, address _basicTournamentAddress) external view returns (uint256){
    Order memory order = orderForWizardIdAndTournamentAddress[_basicTournamentAddress][_wizardId];
    uint256 power;
    ( , power, , , , , , , ) = BasicTournament(_basicTournamentAddress).getWizard(_wizardId);
    uint256 price = power.mul(uint256(order.pricePerPower));
    return price;
}

/// @notice A convenience function providing the caller with the current
///  amount needed (in wei) to fulfill this order.
/// @param _wizardId The id of the wizard NFT whose order we would like to
///  see the details of.
/// @param _basicTournamentAddress The address of the tournament that this
///  order pertains to. Note that each Wizard has an independent power
///  value for each tournament, and call sell each one separately.
/// @return The amount needed (in wei) to fulfill this order