├── V2 ├── Interface │ ├── IFeeStorage.sol │ ├── IERC1155.sol │ └── ISellStorage.sol ├── Lib │ └── SellLib.sol ├── Validator │ └── Validator.sol ├── Storage │ ├── FeeStorage.sol │ └── SellStorage.sol └── Proxy │ └── SellProxyContract.sol ├── Readme.md ├── ERC1155Contract.sol └── V1 └── SellContract.sol /V2/Interface/IFeeStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @title IFeeStorage 7 | * @dev Implements NFT sell process 8 | * @author Hebys development team 9 | **/ 10 | 11 | interface IFeeStorage { 12 | 13 | function getFeeWalletAddress() external view returns (address); 14 | 15 | function getFee(address _address) external view returns (uint256); 16 | } 17 | -------------------------------------------------------------------------------- /V2/Interface/IERC1155.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | 3 | pragma solidity ^0.8.0; 4 | import "../Lib/SellLib.sol"; 5 | 6 | /** 7 | * @title IERC1155 8 | * @dev Implements NFT sell process 9 | * @author Hebys development team 10 | **/ 11 | 12 | interface IERC1155 { 13 | 14 | function isApprovedForAll(address _tokenOwnerAddress ,address _proxyContractAddress) external returns (bool); 15 | 16 | function balanceOf(address _tokenOwnerAddress,uint256 _tokenId) external view returns (uint256) ; 17 | 18 | function getRoyaltyAddress( uint256 _tokenId) external view returns (address) ; 19 | 20 | function getRoyalty( uint256 _tokenId) external view returns (SellLib.Royalty memory) ; 21 | 22 | function getRoyaltyRate(uint256 _tokenId ) external view returns (uint256) ; 23 | 24 | function safeTransferFrom(address _fromAddress,address _toAdress,uint256 _tokenId,uint256 _amount, bytes memory data) external ; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /V2/Interface/ISellStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | pragma solidity ^0.8.0; 3 | 4 | import "../Lib/SellLib.sol"; 5 | 6 | /** 7 | * @title ISellStorage 8 | * @dev Implements NFT sell process 9 | * @author Hebys development team 10 | **/ 11 | 12 | interface ISellStorage { 13 | 14 | function sellRegister(SellLib.Sell memory _sell) external returns (bool); 15 | 16 | function setSaleStatus(uint256 _sellId, bool _isSale) external returns (bool); 17 | 18 | function setAuctionBid(uint256 _sellId, address _bidderAddress,uint256 _bidPrice) external returns (bool); 19 | 20 | function getIsDefinedSellId(uint256 _sellId) external view returns (bool); 21 | 22 | function getSellItem(uint256 _sellId) external view returns (SellLib.Sell memory); 23 | 24 | function getAuctionTopBid(uint256 _sellId) external view returns (uint256); 25 | 26 | function getAuctionTopBidderAddress(uint256 _sellId) external view returns (address); 27 | 28 | function getAuctionTopBidAndBidderAddress(uint256 _sellId) external view returns (SellLib.Bid memory); 29 | 30 | function updateSaleQuantity(uint256 _sellId, uint256 _quantity) external returns (uint256); 31 | 32 | function updateUnitPrice(uint256 _sellId, uint256 _unitPrice) external returns (uint256); 33 | 34 | } -------------------------------------------------------------------------------- /V2/Lib/SellLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @title SellRepositoryContract 7 | * @dev Implements NFT sell process 8 | * @author Hebys development team 9 | **/ 10 | 11 | contract SellLib { 12 | 13 | enum SellTypeEnum { 14 | DirectSell, 15 | EnglishAuction, 16 | SpanishAuction 17 | } 18 | 19 | struct Sell { 20 | uint256 sellId; // OffChainDataSell ID 21 | address payable ownerAddress; // NFT owner address 22 | uint256 tokenId; // NFT tokenId 23 | uint256 unitPrice; // NFT Unit Price 24 | uint256 saleQuantity; // NFT Sale Quantity 25 | address nftCreatorContract; // NFT Craetor contractAddress 26 | bool isSale; // NFT sale true/false 27 | bool isPrivateSell; // NFT Private Sell 28 | address privateSellAddress; // NFT Private Sell 29 | SellTypeEnum sellTypeEnum; 30 | uint256 minumumBid; 31 | uint256 topBid; 32 | uint256 auctionEndTime; 33 | uint256 incrementQty; 34 | } 35 | 36 | struct Bid { 37 | uint256 sellId; 38 | uint256 topBid; 39 | address topBidAddress; 40 | } 41 | 42 | struct ProxyRegistry { 43 | uint256 sellId; // OffChainDataSell ID 44 | address sellStorageAddress; 45 | address feeStorageAddress; 46 | } 47 | 48 | struct Metadata { 49 | uint256 sellId; // OffChainDataSell ID 50 | bytes key; 51 | bytes value; 52 | } 53 | 54 | struct Royalty { 55 | uint256 id; // NFT Id 56 | address to; // NFT Creator Address 57 | uint256 royaltyRate; // NFT Royalty rate 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ## Hebys Smart Contracts 2 | 3 | ![HebysV2ContractDesign](https://user-images.githubusercontent.com/16240508/132330253-bc9da3a9-42dd-49d7-90c1-2e43f691b669.png) 4 | 5 | The structure adopted in Hebys Contracts V2 version was created as stated above. Each Contract interacts with the contract with which it communicates in accordance with the Interface. 6 | Communication states are separated for Read/Write roles according to the contract's property and the information it contains. 7 | The arrow signs above show the communication status of the contracts with each other. 8 | 9 | ### Storage 10 | Storage Contracts in Hebys Contracts were created by adopting the eternal storage structure. 11 | Due to their nature, Smart Contracts cannot be updated and are redeployed, so keeping the data on ProxyContract means that the data will be lost in an unusual scenario. 12 | In this created structure, in any unusual situation that may occur on ProxyContract, ProxyContract, which does not keep any information on it, is redeployed and allows the same data to be processed again. 13 | 14 | #### Sell Storage 15 | Sell Storage Contract contains the sell data. It is available only to the Read/Write authorization of the SellProxy contract. 16 | #### Fee Storage 17 | Fee Storage Contract contains the fee data. It is available only to the Read authorization of the SellProxy contract. 18 | 19 | ### Proxy 20 | Sell Proxy undertakes the task of interpreting the data and methods in other related Contracts that do not contain any data on the Contract. 21 | While other contracts do not specify a business logic in themselves, Proxy Contract undertakes this main business task. Various requests are made special to Proxy Contract only with Read/Write authorization. 22 | After the Proxy Contract makes the necessary validations, the incoming data or request makes a request to the contract that it will interact with in accordance with the Interface. 23 | 24 | ### Hebys ERC1155 25 | Hebys ERC1155 Contract essentially complies with the ERC1155 Standard it has adopted. Keeping the royalty management on the Hebys ERC1155 Contract in V2 version provides a trust mechanism for the Creator, because the royalty he will receive is directly stated on the NFT that he mints. 26 | 27 | 28 | >This structure is open to be updated in different versions with necessary arrangements as a result of different approaches in necessary situations and scenarios. 29 | -------------------------------------------------------------------------------- /V2/Validator/Validator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | 3 | pragma solidity ^0.8.0; 4 | import "../Lib/SellLib.sol"; 5 | 6 | /** 7 | * @title Validator 8 | * @dev Implements NFT sell process 9 | * @author Hebys development team 10 | **/ 11 | 12 | contract Validator { 13 | 14 | function validateSell(SellLib.Sell memory _sell) internal view returns (bool) { 15 | require( 16 | _sell.saleQuantity != uint256(0), 17 | "SALE_QUANTITY_CAN_NOT_BE_ZERO" 18 | ); 19 | 20 | require(msg.sender == _sell.ownerAddress,"NOT_TOKEN_OWNER"); 21 | if ( 22 | _sell.isPrivateSell == true && 23 | _sell.privateSellAddress == address(0) 24 | ) { 25 | revert("INVALID_PRIVATE_SELL_ADDRESS"); 26 | } 27 | 28 | if ( 29 | _sell.sellTypeEnum != SellLib.SellTypeEnum.DirectSell && 30 | block.timestamp >= _sell.auctionEndTime 31 | ) { 32 | revert("PAST_AUCTION_DATE"); 33 | } 34 | return true; 35 | } 36 | 37 | function validateBid(uint256 _sellId, address _bidderAddress) internal view returns (bool) { 38 | require(msg.sender == _bidderAddress,"THE_SENDER_IS_NOT_BIDDER_ADDRESS"); 39 | require(msg.value != uint256(0),"BID_PRICE_CAN_NOT_BE_ZERO"); 40 | require(_sellId != uint256(0),"SELL_ID_CAN_NOT_BE_ZERO"); 41 | 42 | return true; 43 | } 44 | 45 | function validateWithdraw(uint256 _sellId, address _to) internal view returns (bool) { 46 | require(_sellId != uint256(0),"SELL_ID_CAN_NOT_BE_ZERO"); 47 | require(_to == msg.sender, "THE_SENDER_IS_NOT_TO_ADDRESS"); 48 | require(_to != address(0), "INVALID_ADDRESS"); 49 | require(msg.value != uint256(0), "SALE_AMOUNT_CAN_NOT_BE_ZERO"); 50 | 51 | 52 | return true; 53 | } 54 | 55 | function validateGetSellItem(SellLib.Sell memory _sell,address _to, uint256 _amount) internal view returns (bool) { 56 | require(_sell.isSale != false, "NOT_FOR_SALE"); 57 | require(_sell.ownerAddress != address(0), "SELL_ID_NOT_FOUND"); 58 | require( 59 | _sell.unitPrice <= msg.value, 60 | "INSUFFICIENT_SALE_AMOUNT" 61 | ); 62 | if ( 63 | _sell.isPrivateSell == true && 64 | _sell.privateSellAddress != _to 65 | ) { 66 | revert("NO_PURCHASE_AUTHORITY"); 67 | } 68 | 69 | require( 70 | _sell.saleQuantity >= _amount, 71 | "MORE_SALES_THAN_TOKEN_BALANCE" 72 | ); 73 | 74 | return true; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /V2/Storage/FeeStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | 3 | pragma solidity ^0.8.0; 4 | import "@openzeppelin/contracts/access/AccessControl.sol"; 5 | import "../Lib/SellLib.sol"; 6 | 7 | /** 8 | * @title FeeStorageContract 9 | * @dev Implements NFT sell process 10 | * @author Hebys development team 11 | **/ 12 | 13 | contract FeeStorageContract is AccessControl { 14 | bytes32 public constant WRITER_ROLE = keccak256("WRITER_ROLE"); 15 | bytes32 public constant READER_ROLE = keccak256("READER_ROLE"); 16 | bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); 17 | 18 | modifier onlyWriter() { 19 | require(hasRole(WRITER_ROLE, msg.sender), "NOT_WRITER_ADDRESS"); 20 | _; 21 | } 22 | 23 | modifier onlyReader() { 24 | require(hasRole(READER_ROLE, msg.sender), "NOT_READER_ADDRESS"); 25 | _; 26 | } 27 | 28 | constructor() { 29 | _setupRole(MINTER_ROLE, msg.sender); 30 | } 31 | 32 | event LOGFeeOverride(FeeOverride _fee); 33 | 34 | struct FeeOverride { 35 | address _address; 36 | uint256 feeRate; 37 | address providerAddress; 38 | } 39 | 40 | address public minter; 41 | address public feeWalletAddress; 42 | uint256 public defaultFeeRate; 43 | 44 | mapping(address => FeeOverride) public feeOverrides; 45 | 46 | function setFeeOverride( 47 | address _address, 48 | uint256 _feeRate, 49 | address _providerAddress 50 | ) public onlyWriter returns (FeeOverride memory) { 51 | feeOverrides[_address]._address = _address; 52 | feeOverrides[_address].feeRate = _feeRate; 53 | feeOverrides[_address].providerAddress = _providerAddress; 54 | return feeOverrides[_address]; 55 | } 56 | 57 | function setFeeOverrideBatch( 58 | address[] memory _address, 59 | uint256[] memory _feeRates, 60 | address[] memory _providerAddress 61 | ) public onlyWriter returns (bool) { 62 | for (uint256 i = 0; i < _address.length; i++) { 63 | feeOverrides[_address[i]]._address = _address[i]; 64 | feeOverrides[_address[i]].feeRate = _feeRates[i]; 65 | feeOverrides[_address[i]].providerAddress = _providerAddress[i]; 66 | emit LOGFeeOverride(feeOverrides[_address[i]]); 67 | } 68 | 69 | return true; 70 | } 71 | 72 | function setWriterRole(address _writerAddress) public { 73 | require(hasRole(MINTER_ROLE, msg.sender), "NOT_MINTER_ADDRESS"); 74 | _setupRole(WRITER_ROLE, _writerAddress); 75 | } 76 | 77 | function setReaderRole(address _writerAddress) public { 78 | require(hasRole(MINTER_ROLE, msg.sender), "NOT_MINTER_ADDRESS"); 79 | _setupRole(READER_ROLE, _writerAddress); 80 | } 81 | 82 | function getContractAddress() public view returns (address) { 83 | return address(this); 84 | } 85 | 86 | function setFeeWalletAddress(address _feeWalletAddress) public onlyWriter { 87 | feeWalletAddress = _feeWalletAddress; 88 | } 89 | 90 | function setDefaultFeeRate(uint256 _feeRate) public onlyWriter { 91 | defaultFeeRate = _feeRate; 92 | } 93 | 94 | function getFeeWalletAddress() public view onlyReader returns (address) { 95 | return feeWalletAddress; 96 | } 97 | 98 | function getFee(address _address) 99 | external 100 | view 101 | onlyReader 102 | returns (uint256) 103 | { 104 | uint256 _fee = defaultFeeRate; 105 | if (feeOverrides[_address]._address != address(0)) { 106 | _fee = feeOverrides[_address].feeRate; 107 | } 108 | 109 | return _fee; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /V2/Storage/SellStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | 3 | pragma solidity ^0.8.0; 4 | import "@openzeppelin/contracts/access/AccessControl.sol"; 5 | import "../Lib/SellLib.sol"; 6 | 7 | /** 8 | * @title SellStorageContract 9 | * @dev Implements NFT sell process 10 | * @author Hebys development team 11 | **/ 12 | 13 | contract SellStorageContract is AccessControl { 14 | bytes32 public constant WRITER_ROLE = keccak256("WRITER_ROLE"); 15 | bytes32 public constant READER_ROLE = keccak256("READER_ROLE"); 16 | bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); 17 | 18 | modifier onlyWriter() { 19 | require(hasRole(WRITER_ROLE, msg.sender), "NOT_WRITER_ADDRESS"); 20 | _; 21 | } 22 | 23 | modifier onlyReader() { 24 | require(hasRole(READER_ROLE, msg.sender), "NOT_WRITER_ADDRESS"); 25 | _; 26 | } 27 | 28 | constructor() { 29 | _setupRole(MINTER_ROLE, msg.sender); 30 | } 31 | 32 | event LOGSellItem(SellLib.Sell _sell); 33 | event LOGAuctionBid(SellLib.Bid _bid); 34 | 35 | address public minter; 36 | address payable public ownerAddress; 37 | 38 | mapping(uint256 => SellLib.Sell) public sales; 39 | mapping(uint256 => SellLib.Bid) public bids; 40 | mapping(uint256 => SellLib.Metadata) public metadataList; 41 | 42 | function sellRegister(SellLib.Sell memory _sell) 43 | external 44 | onlyWriter 45 | returns (bool) 46 | { 47 | setSaleItem(_sell); 48 | return true; 49 | } 50 | 51 | function setSaleItem(SellLib.Sell memory _sell) 52 | internal 53 | returns (SellLib.Sell memory) 54 | { 55 | sales[_sell.sellId].sellId = _sell.sellId; 56 | sales[_sell.sellId].ownerAddress = _sell.ownerAddress; 57 | sales[_sell.sellId].nftCreatorContract = _sell.nftCreatorContract; 58 | sales[_sell.sellId].tokenId = _sell.tokenId; 59 | sales[_sell.sellId].saleQuantity = _sell.saleQuantity; 60 | sales[_sell.sellId].unitPrice = _sell.unitPrice; 61 | sales[_sell.sellId].isPrivateSell = _sell.isPrivateSell; 62 | sales[_sell.sellId].privateSellAddress = _sell.privateSellAddress; 63 | sales[_sell.sellId].isSale = true; 64 | sales[_sell.sellId].sellTypeEnum = _sell.sellTypeEnum; 65 | sales[_sell.sellId].minumumBid = _sell.minumumBid; 66 | sales[_sell.sellId].topBid = _sell.topBid; 67 | sales[_sell.sellId].auctionEndTime = _sell.auctionEndTime; 68 | sales[_sell.sellId].incrementQty = _sell.incrementQty; 69 | emit LOGSellItem(_sell); 70 | return sales[_sell.sellId]; 71 | } 72 | 73 | function setWriterRole(address _writerAddress) public { 74 | require(hasRole(MINTER_ROLE, msg.sender), "NOT_MINTER_ADDRESS"); 75 | _setupRole(WRITER_ROLE, _writerAddress); 76 | } 77 | 78 | function setReaderRole(address _writerAddress) public { 79 | require(hasRole(MINTER_ROLE, msg.sender), "NOT_MINTER_ADDRESS"); 80 | _setupRole(READER_ROLE, _writerAddress); 81 | } 82 | 83 | function getSellItem(uint256 _sellId) 84 | public 85 | view 86 | onlyReader 87 | returns (SellLib.Sell memory) 88 | { 89 | return sales[_sellId]; 90 | } 91 | 92 | function getContractAddress() public view returns (address) { 93 | return address(this); 94 | } 95 | 96 | function getIsDefinedSellId(uint256 _sellId) external view returns (bool) { 97 | return sales[_sellId].ownerAddress == address(0); 98 | } 99 | 100 | function updateUnitPrice(uint256 _sellId, uint256 _unitPrice) 101 | external 102 | onlyWriter 103 | returns (uint256) 104 | { 105 | sales[_sellId].unitPrice = _unitPrice; 106 | return sales[_sellId].unitPrice; 107 | } 108 | 109 | function setSaleStatus(uint256 _sellId, bool _isSale) 110 | external 111 | onlyWriter 112 | returns (bool) 113 | { 114 | sales[_sellId].isSale = _isSale; 115 | return sales[_sellId].isSale; 116 | } 117 | 118 | function updateSaleQuantity(uint256 _sellId, uint256 _quantity) 119 | external 120 | onlyWriter 121 | onlyWriter 122 | returns (uint256) 123 | { 124 | sales[_sellId].saleQuantity = _quantity; 125 | return sales[_sellId].saleQuantity; 126 | } 127 | 128 | function setAuctionBid( 129 | uint256 _sellId, 130 | address _bidderAddress, 131 | uint256 _bidPrice 132 | ) external onlyWriter returns (bool) { 133 | bids[_sellId].sellId = _sellId; 134 | bids[_sellId].topBidAddress = _bidderAddress; 135 | bids[_sellId].topBid = _bidPrice; 136 | emit LOGAuctionBid(bids[_sellId]); 137 | return true; 138 | } 139 | 140 | function getAuctionTopBid(uint256 _sellId) 141 | external 142 | view 143 | onlyReader 144 | returns (uint256) 145 | { 146 | return bids[_sellId].topBid; 147 | } 148 | 149 | function getAuctionTopBidderAddress(uint256 _sellId) 150 | external 151 | view 152 | onlyReader 153 | returns (address) 154 | { 155 | return bids[_sellId].topBidAddress; 156 | } 157 | 158 | function getAuctionTopBidAndBidderAddress(uint256 _sellId) 159 | external 160 | view 161 | onlyReader 162 | returns (SellLib.Bid memory) 163 | { 164 | return bids[_sellId]; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /ERC1155Contract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; 6 | import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol"; 7 | import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Pausable.sol"; 8 | import "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; 9 | import "@openzeppelin/contracts/utils/Context.sol"; 10 | 11 | /** 12 | * @author Hebys development team 13 | * @dev {ERC1155} token, including: 14 | * 15 | * - ability for holders to burn (destroy) their tokens 16 | * - a minter role that allows for token minting (creation) 17 | * - a pauser role that allows to stop all token transfers 18 | * 19 | * This contract uses {AccessControl} to lock permissioned functions using the 20 | * different roles - head to its documentation for details. 21 | * 22 | * The account that deploys the contract will be granted the minter and pauser 23 | * roles, as well as the default admin role, which will let it grant both minter 24 | * and pauser roles to other accounts. 25 | */ 26 | contract ERC1155Contract is 27 | Context, 28 | AccessControlEnumerable, 29 | ERC1155Burnable, 30 | ERC1155Pausable 31 | { 32 | bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); 33 | bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); 34 | 35 | /** 36 | * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE`, and `PAUSER_ROLE` to the account that 37 | * deploys the contract. 38 | */ 39 | constructor(string memory uri) ERC1155(uri) { 40 | _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); 41 | 42 | _setupRole(MINTER_ROLE, _msgSender()); 43 | _setupRole(PAUSER_ROLE, _msgSender()); 44 | } 45 | 46 | struct Royalty { 47 | uint256 id; // NFT Id 48 | address to; // NFT Creator Address 49 | uint256 royaltyRate; // NFT Royalty rate 50 | } 51 | 52 | mapping(uint256 => Royalty) public royalties; 53 | uint256 public maximumRoyaltyRate; //Maximum Royalty Rate 54 | 55 | /** 56 | * @dev Creates `amount` new tokens for `to`, of token type `id`. 57 | * 58 | * See {ERC1155-_mint}. 59 | * 60 | * Requirements: 61 | * 62 | * - the caller must have the `MINTER_ROLE`. 63 | */ 64 | 65 | function mint( 66 | address to, 67 | uint256 id, 68 | uint256 amount, 69 | uint256 royaltyRate, 70 | bytes32 r, 71 | bytes32 s, 72 | uint8 v, 73 | bytes memory data 74 | ) public virtual { 75 | // TODO Figure out signature that wanted next line. contract address + token id ? 76 | // require(_msgSender() == ecrecover(keccak256(abi.encodePacked(this, id)), v, r, s), "owner should sign tokenId"); 77 | require(to != address(0), "ERC1155: mint to the zero address"); 78 | // require(hasRole(MINTER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have minter role to mint"); 79 | 80 | _mint(to, id, amount, data); 81 | setRoyalty(to, id, royaltyRate); 82 | } 83 | 84 | /** 85 | * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] variant of {mint}. 86 | */ 87 | function mintBatch( 88 | address to, 89 | uint256[] memory ids, 90 | uint256[] memory amounts, 91 | uint256[] memory royaltyRates, 92 | bytes memory data 93 | ) public virtual { 94 | require( 95 | hasRole(MINTER_ROLE, _msgSender()), 96 | "ERC1155PresetMinterPauser: must have minter role to mint" 97 | ); 98 | require( 99 | ids.length == royaltyRates.length, 100 | "ERC1155: ids and royalties length mismatch" 101 | ); 102 | 103 | _mintBatch(to, ids, amounts, data); 104 | setRoyaltyBatch(to, ids, royaltyRates); 105 | } 106 | 107 | /** 108 | * @dev Pauses all token transfers. 109 | * 110 | * See {ERC1155Pausable} and {Pausable-_pause}. 111 | * 112 | * Requirements: 113 | * 114 | * - the caller must have the `PAUSER_ROLE`. 115 | */ 116 | function pause() public virtual { 117 | require( 118 | hasRole(PAUSER_ROLE, _msgSender()), 119 | "ERC1155PresetMinterPauser: must have pauser role to pause" 120 | ); 121 | _pause(); 122 | } 123 | 124 | /** 125 | * @dev Unpauses all token transfers. 126 | * 127 | * See {ERC1155Pausable} and {Pausable-_unpause}. 128 | * 129 | * Requirements: 130 | * 131 | * - the caller must have the `PAUSER_ROLE`. 132 | */ 133 | function unpause() public virtual { 134 | require( 135 | hasRole(PAUSER_ROLE, _msgSender()), 136 | "ERC1155PresetMinterPauser: must have pauser role to unpause" 137 | ); 138 | _unpause(); 139 | } 140 | 141 | /** 142 | * @dev See {IERC165-supportsInterface}. 143 | */ 144 | function supportsInterface(bytes4 interfaceId) 145 | public 146 | view 147 | virtual 148 | override(AccessControlEnumerable, ERC1155) 149 | returns (bool) 150 | { 151 | return super.supportsInterface(interfaceId); 152 | } 153 | 154 | function _beforeTokenTransfer( 155 | address operator, 156 | address from, 157 | address to, 158 | uint256[] memory ids, 159 | uint256[] memory amounts, 160 | bytes memory data 161 | ) internal virtual override(ERC1155, ERC1155Pausable) { 162 | super._beforeTokenTransfer(operator, from, to, ids, amounts, data); 163 | } 164 | 165 | function setMaximumRoyaltyRate(uint256 royaltyRate) public virtual { 166 | require(hasRole(MINTER_ROLE, _msgSender()), "NOT_AUTHORIZED"); 167 | 168 | maximumRoyaltyRate = royaltyRate; 169 | } 170 | 171 | function setRoyalty( 172 | address to, 173 | uint256 id, 174 | uint256 royaltyRate 175 | ) internal returns (Royalty memory) { 176 | require( 177 | maximumRoyaltyRate >= royaltyRate, 178 | "ROYALTY_RATE_CAN_NOT_THAN_MAXIMUM_RATE" 179 | ); 180 | royalties[id].to = to; 181 | royalties[id].id = id; 182 | royalties[id].royaltyRate = royaltyRate; 183 | return royalties[id]; 184 | } 185 | 186 | function setRoyaltyBatch( 187 | address to, 188 | uint256[] memory ids, 189 | uint256[] memory royaltyRates 190 | ) internal returns (bool) { 191 | for (uint256 i = 0; i < ids.length; i++) { 192 | require( 193 | maximumRoyaltyRate >= royaltyRates[i], 194 | "ROYALTY_RATE_CAN_NOT_THAN_MAXIMUM_RATE" 195 | ); 196 | royalties[ids[i]].to = to; 197 | royalties[ids[i]].id = ids[i]; 198 | royalties[ids[i]].royaltyRate = royaltyRates[i]; 199 | } 200 | 201 | return true; 202 | } 203 | 204 | function getRoyalty(uint256 id) public view returns (Royalty memory) { 205 | return royalties[id]; 206 | } 207 | 208 | function getRoyaltyAddress(uint256 id) public view returns (address) { 209 | return royalties[id].to; 210 | } 211 | 212 | function getRoyaltyRate(uint256 id) public view returns (uint256) { 213 | return royalties[id].royaltyRate; 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /V2/Proxy/SellProxyContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | 3 | pragma solidity ^0.8.0; 4 | import "@openzeppelin/contracts/utils/math/SafeMath.sol"; 5 | import "../Lib/SellLib.sol"; 6 | import "../Validator/Validator.sol"; 7 | import "../Interface/ISellStorage.sol"; 8 | import "../Interface/IERC1155.sol"; 9 | import "../Interface/IFeeStorage.sol"; 10 | 11 | /** 12 | * @title SellProxy 13 | * @dev Implements NFT sell process 14 | * @author Hebys development team 15 | */ 16 | 17 | contract SellProxyContract is Validator { 18 | modifier pauseControl() { 19 | require(pause != true, "CONTRACT_PAUSED"); 20 | _; 21 | } 22 | 23 | modifier onlyMinter() { 24 | require(msg.sender == minter, "NOT_MINTER_ADDRESS"); 25 | _; 26 | } 27 | 28 | constructor(ISellStorage _sellStorage, IFeeStorage _feeStorage) { 29 | minter = msg.sender; 30 | sellStorage = _sellStorage; 31 | feeStorage = _feeStorage; 32 | pause = false; 33 | } 34 | 35 | using SafeMath for uint256; 36 | event BalanceOf(address _ownerAddress, uint256 _tokenId, bool success); 37 | 38 | address public minter; 39 | ISellStorage public sellStorage; 40 | IFeeStorage public feeStorage; 41 | address payable public ownerAddress; 42 | bool public pause; 43 | 44 | function register(SellLib.Sell memory _sell) 45 | public 46 | pauseControl 47 | returns (bool) 48 | { 49 | //Sell Data Validator 50 | Validator.validateSell(_sell); 51 | //Previously Sell Id Check 52 | { 53 | bool isDefinedSellId = sellStorage.getIsDefinedSellId(_sell.sellId); 54 | require(isDefinedSellId == true, "SELL_ID_PREVIOUSLY_DEFINED"); 55 | } 56 | 57 | //Proxy Authorıze Check 58 | { 59 | bool isApproved = IERC1155(_sell.nftCreatorContract) 60 | .isApprovedForAll(_sell.ownerAddress, address(this)); 61 | require(isApproved == true, "NOT_AUTHORIZED_CREATOR_CONTRACT"); 62 | } 63 | 64 | //Token balance Check 65 | { 66 | uint256 tokenBalance = IERC1155(_sell.nftCreatorContract).balanceOf( 67 | _sell.ownerAddress, 68 | _sell.tokenId 69 | ); 70 | require(tokenBalance != 0, "INSUFFICIENT_TOKEN_BALANCE"); 71 | require( 72 | tokenBalance >= _sell.saleQuantity, 73 | "MORE_SALES_THAN_TOKEN_BALANCE" 74 | ); 75 | } 76 | 77 | //register SellRegistry 78 | bool registerySellSuccess = sellStorage.sellRegister(_sell); 79 | require(registerySellSuccess == true, "SELL_ITEM_NOT_SAVED"); 80 | 81 | return true; 82 | } 83 | 84 | function bid(uint256 _sellId, address bidderAddress) 85 | public 86 | payable 87 | pauseControl 88 | returns (bool) 89 | { 90 | //Bid Data Validator 91 | Validator.validateBid(_sellId, bidderAddress); 92 | 93 | //SellIdCheck Sell Id Check 94 | { 95 | bool isDefinedSellId = sellStorage.getIsDefinedSellId(_sellId); 96 | require(isDefinedSellId == false, "SELL_ID_NOT_FOUND"); 97 | } 98 | //BidPrice Check 99 | { 100 | SellLib.Sell memory sellItem = sellStorage.getSellItem(_sellId); 101 | require(sellItem.isSale != false, "NOT_FOR_SALE"); 102 | require( 103 | sellItem.minumumBid <= msg.value, 104 | "AMOUNT_LESS_THAN_THE_MINIMUM_BID_AMOUNT" 105 | ); 106 | require( 107 | sellItem.sellTypeEnum != SellLib.SellTypeEnum.DirectSell, 108 | "THIS_SALE_CANNOT_BE_OFFERED" 109 | ); 110 | if ( 111 | sellItem.sellTypeEnum != SellLib.SellTypeEnum.DirectSell && 112 | block.timestamp >= sellItem.auctionEndTime 113 | ) { 114 | //TODO: revert to require 115 | revert("PAST_AUCTION_DATE"); 116 | } 117 | //BidPrice Check 118 | SellLib.Bid memory topAuction = sellStorage 119 | .getAuctionTopBidAndBidderAddress(_sellId); 120 | require( 121 | topAuction.topBid < msg.value, 122 | "YOU_MUST_EXCEED_THE_PREVIOUS_OFFER" 123 | ); 124 | if (topAuction.topBid == uint256(0)) {} else { 125 | uint256 incrementTopBid = SafeMath.add( 126 | topAuction.topBid, 127 | sellItem.incrementQty 128 | ); 129 | require( 130 | incrementTopBid <= msg.value, 131 | "YOU_MUST_EXCEED_THE_PREVIOUS_OFFER" 132 | ); 133 | } 134 | 135 | //Refund of previous top bid 136 | bool successRevertTopBidPrice = sendPrice( 137 | topAuction.topBidAddress, 138 | topAuction.topBid 139 | ); 140 | require(successRevertTopBidPrice, "BID_PRICE_REVERT_ERROR"); 141 | } 142 | 143 | uint256 bidPrice = msg.value; 144 | bool setAuctionBidSuccess = sellStorage.setAuctionBid( 145 | _sellId, 146 | bidderAddress, 147 | bidPrice 148 | ); 149 | require(setAuctionBidSuccess == true, "SET_AUCTION_NOT_SAVED"); 150 | 151 | return true; 152 | } 153 | 154 | function closeAuction(uint256 _sellId) public pauseControl { 155 | SellLib.Sell memory sellItem = sellStorage.getSellItem(_sellId); 156 | require( 157 | sellItem.ownerAddress == msg.sender, 158 | "THE_SENDER_IS_NOT_TO_ADDRESS" 159 | ); 160 | 161 | SellLib.Bid memory topAuction = sellStorage 162 | .getAuctionTopBidAndBidderAddress(_sellId); 163 | withdraw( 164 | sellItem, 165 | topAuction.topBidAddress, 166 | uint256(1), 167 | topAuction.topBid, 168 | topAuction.topBid 169 | ); 170 | sellStorage.setSaleStatus(sellItem.sellId, false); 171 | } 172 | 173 | function updateUnitPrice(uint256 _sellId, uint256 _unitPrice) 174 | public 175 | pauseControl 176 | { 177 | SellLib.Sell memory sellItem = sellStorage.getSellItem(_sellId); 178 | require( 179 | sellItem.ownerAddress == msg.sender, 180 | "THE_SENDER_IS_NOT_TO_ADDRESS" 181 | ); 182 | 183 | sellStorage.updateUnitPrice(sellItem.sellId, _unitPrice); 184 | } 185 | 186 | function closeAuctionForSystem(uint256[] memory _sellIds) 187 | public 188 | pauseControl 189 | onlyMinter 190 | { 191 | for (uint256 i = 0; i < _sellIds.length; i++) { 192 | SellLib.Sell memory sellItem = sellStorage.getSellItem(_sellIds[i]); 193 | SellLib.Bid memory topAuction = sellStorage 194 | .getAuctionTopBidAndBidderAddress(_sellIds[i]); 195 | withdraw( 196 | sellItem, 197 | topAuction.topBidAddress, 198 | uint256(1), 199 | topAuction.topBid, 200 | topAuction.topBid 201 | ); 202 | sellStorage.setSaleStatus(sellItem.sellId, false); 203 | } 204 | } 205 | 206 | function purchase( 207 | uint256 _sellId, 208 | uint256 _amount, 209 | address payable _to 210 | ) public payable pauseControl returns (bool) { 211 | //Bid Data Validator 212 | Validator.validateWithdraw(_sellId, _to); 213 | 214 | //BidPrice Check 215 | { 216 | SellLib.Sell memory sellItem = sellStorage.getSellItem(_sellId); 217 | require( 218 | sellItem.sellTypeEnum == SellLib.SellTypeEnum.DirectSell, 219 | "NOT_PURCHASE_ITEM" 220 | ); 221 | Validator.validateGetSellItem(sellItem, msg.sender, _amount); 222 | withdraw(sellItem, _to, _amount, sellItem.unitPrice, msg.value); 223 | } 224 | return true; 225 | } 226 | 227 | function calculateRoyaltyPrice(uint256 _totalPrice, uint256 _royaltyRate) 228 | internal 229 | pure 230 | returns (uint256) 231 | { 232 | uint256 calculatedRoyaltyPrice = SafeMath.div( 233 | SafeMath.mul(_totalPrice, _royaltyRate), 234 | 100 235 | ); 236 | 237 | return calculatedRoyaltyPrice; 238 | } 239 | 240 | function sendPrice(address _to, uint256 _price) internal returns (bool) { 241 | (bool successSendPrice, ) = _to.call{value: _price}(""); 242 | return successSendPrice; 243 | } 244 | 245 | function withdraw( 246 | SellLib.Sell memory sellItem, 247 | address _to, 248 | uint256 _amount, 249 | uint256 _unitPrice, 250 | uint256 _price 251 | ) internal returns (bool) { 252 | uint256 totalPrice = SafeMath.mul(_amount, _unitPrice); 253 | require(totalPrice <= _price, "INSUFFICIENT_SALE_AMOUNT"); 254 | 255 | //getRoyaltyRate 256 | SellLib.Royalty memory royalty = IERC1155(sellItem.nftCreatorContract) 257 | .getRoyalty(sellItem.tokenId); 258 | { 259 | require(royalty.to != address(0), "INVALID_ROYALTY_ADDRESS"); 260 | } 261 | //Calculate NFT Creator Address RoyaltyPrice 262 | uint256 calculatedRoyaltyPrice = calculateRoyaltyPrice( 263 | totalPrice, 264 | royalty.royaltyRate 265 | ); 266 | 267 | uint256 ownerPrice = totalPrice; 268 | //NFT Owner Price (OwnerPrice - calculatedRoyaltyPrice) 269 | ownerPrice = SafeMath.sub(ownerPrice, calculatedRoyaltyPrice); 270 | 271 | uint256 feeRate = feeStorage.getFee(sellItem.ownerAddress); 272 | //NFT Marketplace Price (totalPrice * feeRate) / 1000 273 | uint256 calculatedFeePrice = SafeMath.div( 274 | SafeMath.mul(totalPrice, feeRate), 275 | 1000 276 | ); 277 | 278 | //NFT Owner Price (OwnerPrice - calculatedRoyaltyPrice) 279 | ownerPrice = SafeMath.sub(ownerPrice, calculatedFeePrice); 280 | { 281 | address marketplaceFeeWalletAddress = feeStorage 282 | .getFeeWalletAddress(); 283 | require( 284 | marketplaceFeeWalletAddress != address(0), 285 | "FEE_WALLET_ADDRESS_NOT_FOUND" 286 | ); 287 | 288 | //Marketplace Fee price Send 289 | bool successSendFeePrice = sendPrice( 290 | marketplaceFeeWalletAddress, 291 | calculatedFeePrice 292 | ); 293 | require(successSendFeePrice, "FEE_PRICE_TRANSFER_FAILED"); 294 | } 295 | { 296 | //Creator Royalty Price Send 297 | bool successSendRoyaltyPrice = sendPrice( 298 | royalty.to, 299 | calculatedRoyaltyPrice 300 | ); 301 | require(successSendRoyaltyPrice, "ROYALTY_PRICE_TRANSFER_FAILED"); 302 | } 303 | { 304 | bool successSendOwnerPrice = sendPrice( 305 | sellItem.ownerAddress, 306 | ownerPrice 307 | ); 308 | require(successSendOwnerPrice, "OWNER_PRICE_TRANSFER_FAILED"); 309 | } 310 | 311 | //Send NFT 312 | IERC1155(sellItem.nftCreatorContract).safeTransferFrom( 313 | sellItem.ownerAddress, 314 | _to, 315 | sellItem.tokenId, 316 | _amount, 317 | "0x0" 318 | ); 319 | { 320 | uint256 calculatedSellQuantity = SafeMath.sub( 321 | sellItem.saleQuantity, 322 | _amount 323 | ); 324 | sellStorage.updateSaleQuantity( 325 | sellItem.sellId, 326 | calculatedSellQuantity 327 | ); 328 | if (calculatedSellQuantity == 0) { 329 | sellStorage.setSaleStatus(sellItem.sellId, false); 330 | } 331 | } 332 | return true; 333 | } 334 | 335 | function setSellStorageAddress(ISellStorage _sellRegisterAddress) 336 | public 337 | pauseControl 338 | onlyMinter 339 | { 340 | sellStorage = _sellRegisterAddress; 341 | } 342 | 343 | function pauseContract() public onlyMinter { 344 | pause = true; 345 | } 346 | 347 | function unPauseContract() public onlyMinter { 348 | pause = false; 349 | } 350 | 351 | function setFeeStorageAddress(IFeeStorage _feeRegisterAddress) 352 | public 353 | onlyMinter 354 | { 355 | feeStorage = _feeRegisterAddress; 356 | } 357 | 358 | function getBalance() public view pauseControl returns (uint256) { 359 | return address(this).balance; 360 | } 361 | } 362 | -------------------------------------------------------------------------------- /V1/SellContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | 3 | pragma solidity ^0.8.0; 4 | import "@openzeppelin/contracts/utils/math/SafeMath.sol"; 5 | 6 | /** 7 | * @title Sell 8 | * @dev Implements NFT sell process 9 | * @author Hebys development team 10 | */ 11 | contract SellContract { 12 | constructor() { 13 | minter = msg.sender; 14 | } 15 | 16 | using SafeMath for uint256; 17 | event balanceOf(address _ownerAddress, uint256 _tokenId, bool success); 18 | event creatorRoyaltyAddress( 19 | address _royaltyAddress, 20 | uint256 _tokenId, 21 | bool success 22 | ); 23 | event creatorRoyaltyRate( 24 | uint256 _royaltyRate, 25 | uint256 _tokenId, 26 | bool success 27 | ); 28 | 29 | event LOGSellItem(uint256 sellId, address ownerAddress, uint256 tokenId); 30 | 31 | struct Seller { 32 | uint256 sellId; // OffChainDataSell ID 33 | address payable ownerAddress; // NFT owner address 34 | uint256 tokenId; // NFT tokenId 35 | uint256 unitPrice; // NFT Unit Price 36 | uint256 saleQuantity; // NFT Sale Quantity 37 | address nftCreatorContract; // NFT Craetor contractAddress 38 | bool isSale; // NFT sale true/false 39 | bool isPrivateSell; // NFT Private Sell 40 | address privateSellAddress; // NFT Private Sell 41 | } 42 | 43 | address public minter; 44 | address payable public feeWalletAddress; 45 | address payable public ownerAddress; 46 | uint256 public feeRate; 47 | 48 | mapping(uint256 => Seller) public sales; 49 | 50 | Seller[] public sellers; 51 | 52 | function setForSale( 53 | uint256 _sellId, 54 | address payable _ownerAddress, 55 | uint256 _tokenId, 56 | uint256 _unitPrice, 57 | uint256 _saleQuantity, 58 | address _nftCreatorContractAddress, 59 | bool _isPrivateSell, 60 | address _privateSellAddress 61 | ) public returns (Seller memory) { 62 | require(_saleQuantity != uint256(0), "SALE_QUANTITY_CAN_NOT_BE_ZERO"); 63 | require(msg.sender == _ownerAddress, "NOT_TOKEN_OWNER"); 64 | if (_isPrivateSell == true && _privateSellAddress == address(0)) { 65 | revert("INVALID_PRIVATE_SELL_ADDRESS"); 66 | } 67 | 68 | //sell Id Check 69 | require( 70 | sales[_sellId].ownerAddress == address(0), 71 | "SELL_ID_PREVIOUSLY_DEFINED" 72 | ); 73 | 74 | address creatorContractAddress = _nftCreatorContractAddress; 75 | address contractAddress = address(this); 76 | 77 | //Operator authorization inquiry for sales contract from creator contract 78 | (bool success, bytes memory result) = 79 | creatorContractAddress.call( 80 | abi.encodeWithSignature( 81 | "isApprovedForAll(address,address)", 82 | _ownerAddress, 83 | contractAddress 84 | ) 85 | ); 86 | 87 | require(success, "NOT_AUTHORIZED_CREATOR_CONTRACT_CALL_ERROR"); 88 | bool isApproved = abi.decode(result, (bool)); 89 | require(isApproved != false, "NOT_AUTHORIZED_CREATOR_CONTRACT"); 90 | 91 | //token balance control 92 | uint256 tokenBalance = 93 | getBalanceOfCallContract( 94 | _ownerAddress, 95 | creatorContractAddress, 96 | _tokenId 97 | ); 98 | require(tokenBalance != 0, "INSUFFICIENT_TOKEN_BALANCE"); 99 | require(tokenBalance >= _saleQuantity, "MORE_SALES_THAN_TOKEN_BALANCE"); 100 | return 101 | setSaleItem( 102 | _sellId, 103 | _ownerAddress, 104 | _tokenId, 105 | _unitPrice, 106 | _saleQuantity, 107 | _nftCreatorContractAddress, 108 | _isPrivateSell, 109 | _privateSellAddress 110 | ); 111 | } 112 | 113 | function setSaleItem( 114 | uint256 _sellId, 115 | address payable _ownerAddress, 116 | uint256 _tokenId, 117 | uint256 _unitPrice, 118 | uint256 _saleQuantity, 119 | address _nftCreatorContractAddress, 120 | bool _isPrivateSell, 121 | address _privateSellAddress 122 | ) internal returns (Seller memory) { 123 | sales[_sellId].sellId = _sellId; 124 | sales[_sellId].ownerAddress = _ownerAddress; 125 | sales[_sellId].nftCreatorContract = _nftCreatorContractAddress; 126 | sales[_sellId].tokenId = _tokenId; 127 | sales[_sellId].saleQuantity = _saleQuantity; 128 | sales[_sellId].unitPrice = _unitPrice; 129 | sales[_sellId].isPrivateSell = _isPrivateSell; 130 | sales[_sellId].privateSellAddress = _privateSellAddress; 131 | sales[_sellId].isSale = true; 132 | 133 | return sales[_sellId]; 134 | } 135 | 136 | function setFeeWalletAddress(address payable _feeWalletAddress) public { 137 | //Contract generating address control 138 | require(msg.sender == minter, "NOT_MINTER_ADDRESS"); 139 | feeWalletAddress = _feeWalletAddress; 140 | } 141 | 142 | function setFeeRate(uint256 _feeRate) public { 143 | //Contract generating address control 144 | require(msg.sender == minter, "NOT_MINTER_ADDRESS"); 145 | feeRate = _feeRate; 146 | } 147 | 148 | function getSellItem(uint256 _sellId) public view returns (Seller memory) { 149 | return sales[_sellId]; 150 | } 151 | 152 | function getContractAddress() public view returns (address) { 153 | return address(this); 154 | } 155 | 156 | function getAllSellItem() public view returns (Seller[] memory) { 157 | return sellers; 158 | } 159 | 160 | function getBalanceOfCallContract( 161 | address _ownerAddress, 162 | address _creatorContractAddress, 163 | uint256 _tokenId 164 | ) internal returns (uint256) { 165 | (bool success, bytes memory result) = 166 | _creatorContractAddress.call( 167 | abi.encodeWithSignature( 168 | "balanceOf(address,uint256)", 169 | _ownerAddress, 170 | _tokenId 171 | ) 172 | ); 173 | 174 | require(success, "BALANCE_CALL_ERROR"); 175 | 176 | emit balanceOf(_ownerAddress, _tokenId, success); 177 | return abi.decode(result, (uint256)); 178 | } 179 | 180 | function getCreatorRoyaltyAddress( 181 | address _creatorContractAddress, 182 | uint256 _tokenId 183 | ) internal returns (address) { 184 | (bool success, bytes memory result) = 185 | _creatorContractAddress.call( 186 | abi.encodeWithSignature("getRoyaltyAddress(uint256)", _tokenId) 187 | ); 188 | 189 | require(success, "CREATOR_FEE_ADDRESS_CALL_ERROR"); 190 | 191 | emit creatorRoyaltyAddress( 192 | abi.decode(result, (address)), 193 | _tokenId, 194 | success 195 | ); 196 | return abi.decode(result, (address)); 197 | } 198 | 199 | function getCreatorRoyaltyRate( 200 | address _creatorContractAddress, 201 | uint256 _tokenId 202 | ) internal returns (uint256) { 203 | (bool success, bytes memory result) = 204 | _creatorContractAddress.call( 205 | abi.encodeWithSignature("getRoyaltyRate(uint256)", _tokenId) 206 | ); 207 | 208 | require(success, "CREATOR_ROYALTY_RATE_CALL_ERROR"); 209 | 210 | emit creatorRoyaltyRate( 211 | abi.decode(result, (uint256)), 212 | _tokenId, 213 | success 214 | ); 215 | return abi.decode(result, (uint256)); 216 | } 217 | 218 | function updateUnitPrice(uint256 _sellId, uint256 _unitPrice) 219 | public 220 | returns (Seller memory) 221 | { 222 | require(sales[_sellId].ownerAddress != msg.sender, "NOT_AUTHORIZED"); 223 | sales[_sellId].unitPrice = _unitPrice; 224 | return sales[_sellId]; 225 | } 226 | 227 | function setSaleStatus(uint256 _sellId, bool _isSale) 228 | public 229 | returns (Seller memory) 230 | { 231 | require(sales[_sellId].ownerAddress != msg.sender, "NOT_AUTHORIZED"); 232 | sales[_sellId].isSale = _isSale; 233 | return sales[_sellId]; 234 | } 235 | 236 | function updateSaleQuantity(uint256 _sellId, uint256 _saleQuantity, uint256 quantity) 237 | internal 238 | 239 | { 240 | sales[_sellId].saleQuantity = SafeMath.sub(sales[_sellId].saleQuantity,quantity ); 241 | } 242 | 243 | function withdraw( 244 | uint256 _sellId, 245 | uint256 _amount, 246 | address payable _to 247 | ) public payable { 248 | require(_to == msg.sender, "NOT_AUTHORIZED"); 249 | require(_to != address(0), "INVALID_ADDRESS"); 250 | require(sales[_sellId].isSale != false, "NOT_FOR_SALE"); 251 | require(msg.value != uint256(0), "SALE_AMOUNT_CAN_NOT_BE_ZERO"); 252 | if ( 253 | sales[_sellId].isPrivateSell == true && 254 | sales[_sellId].privateSellAddress != _to 255 | ) { 256 | revert("NO_PURCHASE_AUTHORITY"); 257 | } 258 | 259 | uint256 _saleQuantity = sales[_sellId].saleQuantity; 260 | require(_saleQuantity >= _amount, "MORE_SALES_THAN_TOKEN_BALANCE"); 261 | //Calculate Total Price (amount * unitPrice) 262 | uint256 totalPrice = SafeMath.mul(_amount, sales[_sellId].unitPrice); 263 | require(totalPrice <= msg.value, "INSUFFICIENT_SALE_AMOUNT"); 264 | 265 | //token balance control 266 | uint256 tokenBalance = 267 | getBalanceOfCallContract( 268 | sales[_sellId].ownerAddress, 269 | sales[_sellId].nftCreatorContract, 270 | sales[_sellId].tokenId 271 | ); 272 | require(tokenBalance != 0, "INSUFFICIENT_TOKEN_BALANCE"); 273 | require(tokenBalance >= _amount, "LESS_TOKEN_BALANCE_THAN_AMOUNT"); 274 | 275 | address creatorContractAddress = sales[_sellId].nftCreatorContract; 276 | //getRoyaltyRate 277 | uint256 royaltyRate = 278 | getCreatorRoyaltyRate( 279 | creatorContractAddress, 280 | sales[_sellId].tokenId 281 | ); 282 | require(royaltyRate != uint256(0), "ROYALTY_RATE_CAN_NOT_BE_ZERO"); 283 | 284 | //getRoyaltyAddress NFT creator Address 285 | address creatorAddress = 286 | getCreatorRoyaltyAddress( 287 | creatorContractAddress, 288 | sales[_sellId].tokenId 289 | ); 290 | require(creatorAddress != address(0), "INVALID_ROYALTY_ADDRESS"); 291 | 292 | address _ownerAddress = sales[_sellId].ownerAddress; 293 | require(_ownerAddress != address(0), "INVALID_ROYALTY_ADDRESS"); 294 | 295 | uint256 _tokenId = sales[_sellId].tokenId; 296 | 297 | //Calculate NFT Creator Address RoyaltyPrice 298 | uint256 calculatedRoyaltyPrice = 299 | SafeMath.div(SafeMath.mul(totalPrice, royaltyRate), 100); 300 | 301 | uint256 OwnerPrice = totalPrice; 302 | //NFT Owner Price (OwnerPrice - calculatedRoyaltyPrice) 303 | OwnerPrice = SafeMath.sub(OwnerPrice, calculatedRoyaltyPrice); 304 | 305 | updateSaleQuantity(_sellId, _saleQuantity,_amount); 306 | 307 | //NFT Marketplace Price (totalPrice * feeRate) / 1000 308 | uint256 calculatedFeePrice = 309 | SafeMath.div(SafeMath.mul(totalPrice, feeRate), 1000); 310 | address toAddress = _to; 311 | //NFT Owner Price (OwnerPrice - calculatedRoyaltyPrice) 312 | OwnerPrice = SafeMath.sub(OwnerPrice, calculatedFeePrice); 313 | uint256 quantity = _amount; 314 | 315 | //Marketplace Fee price Send 316 | (bool successSendFeePrice, ) = 317 | feeWalletAddress.call{value: calculatedFeePrice}(""); 318 | require(successSendFeePrice, "FEE_PRICE_TRANSFER_FAILED"); 319 | //Creator Royalty Price Send 320 | (bool successSendRoyaltyPrice, ) = 321 | creatorAddress.call{value: calculatedRoyaltyPrice}(""); 322 | require(successSendRoyaltyPrice, "ROYALTY_PRICE_TRANSFER_FAILED"); 323 | //Owner Sale Price Send 324 | (bool successSendOwnerPrice, ) = 325 | creatorAddress.call{value: OwnerPrice}(""); 326 | require(successSendOwnerPrice, "OWNER_PRICE_TRANSFER_FAILED"); 327 | 328 | //Send NFT 329 | (bool succesNFTTransfer, bytes memory returnData) = 330 | sendTokenTransfer( 331 | creatorContractAddress, 332 | _ownerAddress, 333 | toAddress, 334 | _tokenId, 335 | quantity 336 | ); 337 | 338 | require(succesNFTTransfer, "TOKEN_TRANSFER_FAILED"); 339 | } 340 | 341 | function sendTokenTransfer( 342 | address creatorContractAddress, 343 | address _fromAddress, 344 | address _toAdress, 345 | uint256 _tokenId, 346 | uint256 _amount 347 | ) internal returns (bool, bytes memory) { 348 | (bool succesNFTTransfer, bytes memory returnData) = 349 | creatorContractAddress.call( 350 | abi.encodeWithSignature( 351 | "safeTransferFrom(address,address,uint256,uint256,bytes)", 352 | _fromAddress, 353 | _toAdress, 354 | _tokenId, 355 | _amount, 356 | "0x0" 357 | ) 358 | ); 359 | 360 | require(succesNFTTransfer, "TOKEN_TRANSFER_FAILED"); 361 | return (succesNFTTransfer, returnData); 362 | } 363 | } 364 | --------------------------------------------------------------------------------