1// SPDX-License-Identifier: Apache License2pragma solidity >=0.8.0;3import {UniqueNFT, Dummy, ERC165} from "../api/UniqueNFT.sol";45// Inline6interface ERC20Events {7 event Transfer(address indexed from, address indexed to, uint256 value);8 event Approval(9 address indexed owner,10 address indexed spender,11 uint256 value12 );13}1415interface ERC20 is Dummy, ERC165, ERC20Events {16 // Selector: name() 06fdde0317 function name() external view returns (string memory);1819 // Selector: symbol() 95d89b4120 function symbol() external view returns (string memory);2122 // Selector: totalSupply() 18160ddd23 function totalSupply() external view returns (uint256);2425 // Selector: decimals() 313ce56726 function decimals() external view returns (uint8);2728 // Selector: balanceOf(address) 70a0823129 function balanceOf(address owner) external view returns (uint256);3031 // Selector: transfer(address,uint256) a9059cbb32 function transfer(address to, uint256 amount) external returns (bool);3334 // Selector: transferFrom(address,address,uint256) 23b872dd35 function transferFrom(36 address from,37 address to,38 uint256 amount39 ) external returns (bool);4041 // Selector: approve(address,uint256) 095ea7b342 function approve(address spender, uint256 amount) external returns (bool);4344 // Selector: allowance(address,address) dd62ed3e45 function allowance(address owner, address spender)46 external47 view48 returns (uint256);49}5051interface UniqueFungible is Dummy, ERC165, ERC20 {}5253contract MarketPlace {54 struct Order {55 56 uint256 idNFT;57 address currencyCode; // UNIQ tokens as address address (1); wKSM 58 uint256 price;59 uint256 time;60 address idCollection;61 address ownerAddr;62 uint8 flagActive;63 string name;64 string symbol;65 string tokenURI; 66 }67 Order[] public orders;68 uint test;69 mapping (address => uint256) public balanceKSM; // [ownerAddr][currency] => [KSMs]70 mapping (address => mapping (uint256 => uint256)) public asks ; // [buyer][idCollection][idNFT] => idorder71 mapping (address => mapping (uint => uint[])) public ordersbyNFT; // [addressCollection] =>idNFT =>idorder7273 mapping (address => uint[]) public asksbySeller; // [addressSeller] =>idorder7475 mapping (address =>bool) internal isEscrow;7677 //address escrow;78 address owner;79 address nativecoin;8081 // from abstract contract ReentrancyGuard 82 // Booleans are more expensive than uint256 or any type that takes up a full83 // word because each write operation emits an extra SLOAD to first read the84 // slot's contents, replace the bits taken up by the boolean, and then write85 // back. This is the compiler's defense against contract upgrades and86 // pointer aliasing, and it cannot be disabled.8788 // The values being non-zero value makes deployment a bit more expensive,89 // but in exchange the refund on every call to nonReentrant will be lower in90 // amount. Since refunds are capped to a percentage of the total91 // transaction's gas, it is best to keep them low in cases like this one, to92 // increase the likelihood of the full refund coming into effect.93 uint8 private constant _NOT_ENTERED = 1;94 uint8 private constant _ENTERED = 2;9596 uint8 private _status;9798 struct NFT {99 address collection;100 uint256 id;101 }102 103 //function initialize() public initializer {104 constructor () { // call setEscrow directly105 owner = msg.sender;106107 orders.push(Order( 108 0,109 address(0),110 0,111 0,112 address(0),113 address(0),114 0, "","",""));115 _status = _NOT_ENTERED;116117 }118119 modifier nonReentrant() { // from abstract contract ReentrancyGuard 120 // On the first call to nonReentrant, _notEntered will be true121 require(_status != _ENTERED, "ReentrancyGuard: reentrant call");122123 // Any calls to nonReentrant after this point will fail124 _status = _ENTERED;125126 _;127128 // By storing the original value once again, a refund is triggered (see129 // https://eips.ethereum.org/EIPS/eip-2200)130 _status = _NOT_ENTERED;131 }132133 modifier onlyEscrow () {134 require(isEscrow [msg.sender] , "Only escrow can");135 _;136 }137138 modifier onlyOwner () {139 require(msg.sender == owner, "Only owner can");140 _;141 }142143 /**144 * Make bids (orders) to sell NFTs 145 */146147 148 receive () external payable {149 // revert ("Can't accept payment without collection and IDs, use dApp to send");150 }151 fallback () external payable {152 revert ("No such function");153 } 154155 event AddedAsk (uint256 _price, 156 address _currencyCode, 157 address _idCollection, 158 uint256 _idNFT,159 uint256 orderId160 );161 event EditedAsk (uint256 _price, 162 address _currencyCode, 163 address _idCollection, 164 uint256 _idNFT,165 uint8 _active,166 uint orderId);167168 event CanceledAsk (address _idCollection, 169 uint256 _idNFT, 170 uint orderId171 );172173 event DepositedKSM (uint256 _amount, address _sender);174175 event BoughtNFT4KSM (address _idCollection, uint256 _idNFT, uint orderID, uint orderPrice );176177 event BoughtNFT (address _idCollection, uint256 _idNFT, uint orderID, uint orderPrice );178179 event WithdrawnAllKSM (address _sender, uint256 balance); 180181 event WithdrawnKSM (address _sender, uint256 balance); 182183 event Withdrawn (uint256 _amount, address _currencyCode, address _sender);184 185186 function setOwner (address _newOwner) public onlyOwner {187 owner = _newOwner;188 }189190 function setEscrow (address _escrow, bool _state) public onlyOwner returns (bool) {191 if (isEscrow[_escrow] != _state) {192 isEscrow[_escrow] = _state;193 return true;194 }195 return false;196 }197198 function setNativeCoin (address _coin) public onlyOwner {199 nativecoin = _coin;200 }201202203 function addAsk (uint256 _price, 204 address _currencyCode, 205 address _idCollection, 206 uint256 _idNFT207 ) public { //208 address ownerNFT = UniqueNFT(_idCollection).ownerOf(_idNFT);209 require (ownerNFT == msg.sender, "Only token owner can make ask");210 string memory nameNFT;211 string memory symbolNFT;212 string memory uriNFT;213 try UniqueNFT(_idCollection).name() returns (string memory name_) {214 nameNFT = name_;215 }216 catch {217 nameNFT="";218 } 219 try UniqueNFT(_idCollection).symbol() returns (string memory symbol_) {220 symbolNFT = symbol_;221 }222 catch {223 symbolNFT="";224 } 225 try UniqueNFT(_idCollection).tokenURI(_idNFT) returns (string memory uri_) {226 uriNFT = uri_;227 }228 catch {229 uriNFT="";230 } 231 orders.push(Order( 232 _idNFT,233 _currencyCode,234 _price,235 block.timestamp,236 _idCollection,237 msg.sender,238 1, // 1 = is active239 nameNFT, 240 symbolNFT,241 uriNFT242 ));243 244 uint orderId = orders.length-1;245 asks[_idCollection][_idNFT] = orderId;246 asksbySeller[msg.sender].push(orderId);247 UniqueNFT(_idCollection).transferFrom(msg.sender, address(this), _idNFT);248 emit AddedAsk(_price, _currencyCode, _idCollection, _idNFT, orderId); 249 }250251 function editAsk (uint256 _price, 252 address _currencyCode, 253 address _idCollection, 254 uint256 _idNFT,255 uint8 _active) public {256 257258 uint orderID = asks[_idCollection][_idNFT];259260 require (orders[orderID].ownerAddr == msg.sender, "Only token owner can edit ask");261 require (orders[orderID].flagActive != 0, "This ask is closed");262 if (_price> 0 ) {263 orders[orderID].price = _price ; 264 }265 266 if (_currencyCode != address(0) ) {267 orders[orderID].currencyCode = _currencyCode ; 268 }269270 orders[orderID].time = block.timestamp;271 orders[orderID].flagActive = _active;272 273 emit EditedAsk(_price, _currencyCode, _idCollection, _idNFT, _active, orderID);274 }275 276277 function cancelAsk (address _idCollection, 278 uint256 _idNFT279 ) public {280281 uint orderID = asks[_idCollection][_idNFT];282283 require (orders[orderID].ownerAddr == msg.sender, "Only token owner can edit ask");284 require (orders[orderID].flagActive != 0, "This ask is closed");285286 orders[orderID].time = block.timestamp;287 orders[orderID].flagActive = 0;288 UniqueNFT(_idCollection).transferFrom(address(this),orders[orderID].ownerAddr, _idNFT);289 emit CanceledAsk(_idCollection, _idNFT, orderID);290 }291292293 function depositKSM (uint256 _amount, address _sender) public onlyEscrow {294 balanceKSM[_sender] = balanceKSM[_sender] + _amount;295 emit DepositedKSM(_amount, _sender);296 }297298 function buyKSM (address _idCollection, uint256 _idNFT, address _buyer, address _receiver ) public {299 300 Order memory order = orders[ asks[_idCollection][_idNFT]];301 require(isEscrow[msg.sender] || msg.sender == _buyer, "Only escrow or buyer can call buyKSM" );302 //1. reduce balance303304 balanceKSM[_buyer] = balanceKSM[_buyer] - order.price;305 balanceKSM[order.ownerAddr] = balanceKSM[order.ownerAddr] + order.price;306 // 2. close order307 orders[ asks[_idCollection][_idNFT]].flagActive = 0;308 // 3. transfer NFT to buyer309 UniqueNFT(_idCollection).transferFrom(address(this), _receiver, _idNFT);310 emit BoughtNFT4KSM(_idCollection, _idNFT, asks[_idCollection][_idNFT], order.price);311312 }313 function buy (address _idCollection, uint256 _idNFT ) public payable returns (bool result) { //buing for UNQ like as ethers 314 315 Order memory order = orders[asks[_idCollection][_idNFT]]; 316 //1. check sent amount and send to seller317 require (msg.value == order.price, "Not right amount sent, have to be equal price" ); 318 // 2. close order319 orders[ asks[_idCollection][_idNFT]].flagActive = 0;320 321 // 3. transfer NFT to buyer322 UniqueNFT(_idCollection).transferFrom(address(this), msg.sender, _idNFT);323 //uint balance = address(this).balance;324 result = payable(order.ownerAddr).send (order.price); 325 emit BoughtNFT(_idCollection, _idNFT, asks[_idCollection][_idNFT], order.price);326 327 }328329/* 330 function buyOther (address _idCollection, uint256 _idNFT, address _currencyCode, uint _amount ) public { //buy for sny token if seller wants331 332 Order memory order = orders[ asks[_idCollection][_idNFT]];333 //1. check sent amount and transfer from buyer to seller334 require (order.price == _amount && order.currencyCode == _currencyCode, "Not right amount or currency sent, have to be equal currency and price" );335 // !!! transfer have to be approved to marketplace!336 UniqueFungible(order.currencyCode).transferFrom(msg.sender, address(this), order.price); //to not disclojure buyer's address 337 UniqueFungible(order.currencyCode).transfer(order.ownerAddr, order.price);338 // 2. close order339 orders[ asks[_idCollection][_idNFT]].flagActive = 0;340 // 3. transfer NFT to buyer341 UniqueNFT(_idCollection).transferFrom(address(this), msg.sender, _idNFT);342343344 }345 */346347 function withdrawAllKSM (address _sender) public nonReentrant returns (uint lastBalance ){348 require(isEscrow[msg.sender] || msg.sender == _sender, "Only escrow or balance owner can withdraw all KSM" );349350 lastBalance = balanceKSM[_sender];351 balanceKSM[_sender] =0;352 emit WithdrawnAllKSM(_sender, lastBalance);353 }354355 function withdrawKSM (uint _amount, address _sender) onlyEscrow public {356 balanceKSM[_sender] = balanceKSM[_sender] - _amount;357 emit WithdrawnKSM(_sender, balanceKSM[_sender]);358 359 }360361 function withdraw (uint256 _amount, address _currencyCode) public nonReentrant returns (bool result ){ //onlyOwner362 address payable _sender = payable( msg.sender);363 if (_currencyCode != nativecoin ) { //erc20 compat. tokens on UNIQUE chain364 // uint balance = UniqueFungible(_currencyCode).balanceOf(address(this));365 UniqueFungible(_currencyCode).transfer(_sender, _amount);366 } else {367 // uint balance = address(this).balance;368369 result = (_sender).send(_amount); // for UNQ like as ethers 370 }371 emit Withdrawn(_amount, _currencyCode, _sender);372 return result;373374 }375376 377 // event GettedOrder(uint , Order);378 function getOrder (address _idCollection, uint256 _idNFT) public view returns (Order memory) {379 uint orderId = asks[_idCollection][_idNFT];380 Order memory order = orders[orderId];381 // emit GettedOrder (orderId, order);382 return order;383 }384385 function getOrdersLen () public view returns (uint) {386 return orders.length;387 }388389 function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) public pure returns(bytes4) {390 return bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));391 }392 393 //TODO make destructing function to return all 394 function terminate(address[] calldata tokens, NFT[] calldata nfts) public onlyOwner {395 // Transfer tokens to owner (TODO: error handling)396 for (uint i = 0; i < tokens.length; i++) {397 address addr = tokens[i];398 UniqueFungible token = UniqueFungible(addr);399 uint256 balance = token.balanceOf(address(this));400 token.transfer(owner, balance);401 }402 for (uint i = 0; i < nfts.length; i++) {403 address addr = nfts[i].collection;404 UniqueNFT token = UniqueNFT(addr);405 token.transferFrom(address(this), owner, nfts[i].id);406 } 407 // Transfer Eth to owner and terminate contract408 address payable owner1 = payable (owner);409 uint balance = address(this).balance;410 owner1.transfer(balance);411 412 } 413 414}