// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; interface IERC721Receiver { //cannot use the one from openzeppelin as we need payable function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) payable external returns (bytes4); } contract Market { mapping(address => mapping(uint256 => mapping(address => uint256))) private _offers; event Offer(address nftContract, uint256 nftId, address offerer, uint256 value); event Withdraw(address nftContract, uint256 nftId, address offerer, uint256 value); event Accepted(address nftContract, uint256 nftId, address from, address to, uint256 value); function makeOffer(address nftContract, uint256 nftId) payable external { _offers[nftContract][nftId][msg.sender] = msg.value; emit Offer(nftContract, nftId, msg.sender, msg.value); } function withdrawOffer(address nftContract, uint256 nftId) payable external { uint256 val = _offers[nftContract][nftId][msg.sender]; delete(_offers[nftContract][nftId][msg.sender]); payable(msg.sender).transfer(val); emit Withdraw(nftContract, nftId, msg.sender, val); } function offers(address nftContract, uint256 nftId, address offerer) external view returns (uint256) { return _offers[nftContract][nftId][offerer]; } function onERC721Received(address, address from, uint256 nftId, bytes calldata data) payable external returns (bytes4) { address offerer = bytesToAddress(data); uint256 val = _offers[msg.sender][nftId][offerer]; require(val > 0, "Offer invalid"); delete(_offers[msg.sender][nftId][offerer]); payable(from).transfer(val); IERC721(msg.sender).transferFrom(address(this), offerer, nftId); emit Accepted(msg.sender, nftId, from, offerer, val); return IERC721Receiver.onERC721Received.selector; } function bytesToAddress(bytes memory bs) private pure returns (address) { require(bs.length >= 20, "slicing out of range"); address addr; assembly { addr := mload(add(bs,20)) } return addr; } }