pragma solidity ^0.8.0;








contract EncV1Market is IEncV1Market, ERC2981, Ownable{
    using SafeCast for uint256;

     address public royaltyReceiver = 0xaa5a6eec8F785F8C4fEeb28057f1f4F37EC33C44;
     uint96 public feeNumerator;


     constructor()  {
        // set royalty of all NFTs to 5%
        _setDefaultRoyalty(royaltyReceiver, 750);
    }

    struct SaleInfo{
        uint256 tokenId;
        address owner; // 160
        uint96  price; 
    }
    mapping(bytes32 => SaleInfo) private saleInfo;


    function setroyaltyReceiver(address _royaltyReceiver) public onlyOwner {
         require(_royaltyReceiver != address(0), "Invalid address");
        royaltyReceiver = _royaltyReceiver;
    }

     function setTokenRoyalty(uint256 tokenId, address _receiver, uint96 _feeNumerator) public onlyOwner {
         royaltyReceiver = _receiver;
         feeNumerator = _feeNumerator;
        _setTokenRoyalty(tokenId, royaltyReceiver, feeNumerator);
    }

  function supportsInterface(bytes4 interfaceId)
    public view virtual override(ERC2981)
    returns (bool) {
      return super.supportsInterface(interfaceId);
  }

    /// @inheritdoc IEncV1Market
    function sellNft(address token, uint256 tokenId, uint256 price) external override{
        require(price >= 0, "Invalid price");
        require(IERC721(token).ownerOf(tokenId) == msg.sender, "Not owner");
        require(
            IERC721(token).getApproved(tokenId) == address(this) ||
            IERC721(token).isApprovedForAll(msg.sender, address(this)),
            "Approve first."
        );
        SaleInfo storage info = saleInfo[keccak256(abi.encodePacked(token,tokenId))];
        (info.tokenId, info.owner, info.price) = (tokenId, msg.sender, price.toUint96());

        emit SaleInfoChangedEvt(token, tokenId, price);
    }

    /// @inheritdoc IEncV1Market
    function buyNft(address token, uint256 tokenId, address to) external payable override {
        SaleInfo storage info = saleInfo[keccak256(abi.encodePacked(token,tokenId))];
        SaleInfo memory _info = info;
        require(msg.value >= _info.price, "Value not enough.");
        require(_info.price > 0, "Not on sale.");
        // The buyer pays fees.
        (, uint256 royaltyAmount) = royaltyInfo(tokenId, info.price);
        // transfer nft
        TransferHelper.safeTransferNFTFrom(token, _info.owner, to, tokenId);
        // pay eth
        TransferHelper.safeTransferETH(_info.owner, _info.price - royaltyAmount);
        // update value
        (info.owner, info.price) = (to ,0);
        

        emit SaleInfoChangedEvt(token, tokenId, 0);
        emit NFTSoldEvt(token, tokenId, _info.price - royaltyAmount);
    }

        function withdraw() public payable onlyOwner {
        (bool success, ) = payable(msg.sender).call{
            value: address(this).balance
        }("");
        require(success);
    }

    /// @inheritdoc IEncV1Market
    function tokenSaleInfo(
        address token, 
        uint256 tokenId
    ) external override view returns(bool onSale, uint256 price){
        SaleInfo memory info = saleInfo[keccak256(abi.encodePacked(token,tokenId))];
        if(
            // Check still on sale
            info.price > 0 &&
            // check owner is still correct
            IERC721(token).ownerOf(tokenId) == info.owner && 
            (
                // Check approve
                IERC721(token).getApproved(tokenId) == address(this) ||
                IERC721(token).isApprovedForAll(info.owner, address(this))
            )
        ){
            return (true, info.price);
        }
        else{
            return (false, 0);
        }
    }

}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"NFTSoldEvt","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"SaleInfoChangedEvt","type":"event"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"buyNft","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"feeNumerator","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"royaltyReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"name":"sellNft","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"uint96","name":"_feeNumerator","type":"uint96"}],"name":"setTokenRoyalty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_royaltyReceiver","type":"address"}],"name":"setroyaltyReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenSaleInfo","outputs":[{"internalType":"bool","name":"onSale","type":"bool"},{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"payable","type":"function"}]