/** * Source Code first verified at https://etherscan.io on Monday, April 22, 2019 (UTC) */ pragma solidity ^0.5.0; // produced by the Solididy File Flattener (c) David Appleton 2018 // contact : [email protected] // released under Apache 2.0 licence // input C:\github\privateCode\retroArt\reactWebsite\contracts\RetroArtTokenAuction.sol // flattened : Monday, 22-Apr-19 22:17:45 UTC contract ERC721Receiver { /** * @dev Magic value to be returned upon successful reception of an NFT * Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`, * which can be also obtained as `ERC721Receiver(0).onERC721Received.selector` */ bytes4 internal constant ERC721_RECEIVED = 0x150b7a02; /** * @notice Handle the receipt of an NFT * @dev The ERC721 smart contract calls this function on the recipient * after a `safetransfer`. This function MAY throw to revert and reject the * transfer. Return of other than the magic value MUST result in the * transaction being reverted. * Note: the contract address is always the message sender. * @param _operator The address which called `safeTransferFrom` function * @param _from The address which previously owned the token * @param _tokenId The NFT identifier which is being transferred * @param _data Additional data with no specified format * @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` */ function onERC721Received( address _operator, address _from, uint256 _tokenId, bytes memory _data ) public returns(bytes4); } library AddressUtils { /** * Returns whether the target address is a contract * @dev This function will return false if invoked during the constructor of a contract, * as the code is not actually created until after the constructor finishes. * @param _addr address to check * @return whether the target address is a contract */ function isContract(address _addr) internal view returns (bool) { uint256 size; // XXX Currently there is no better way to check if there is a contract in an address // than to check the size of the code at that address. // See https://ethereum.stackexchange.com/a/14016/36603 // for more details about how this works. // TODO Check this again before the Serenity release, because all addresses will be // contracts then. // solium-disable-next-line security/no-inline-assembly assembly { size := extcodesize(_addr) } return size > 0; } } interface ERC165 { /** * @notice Query if a contract implements an interface * @param _interfaceId The interface identifier, as specified in ERC-165 * @dev Interface identification is specified in ERC-165. This function * uses less than 30,000 gas. */ function supportsInterface(bytes4 _interfaceId) external view returns (bool); } library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) { // Gas optimization: this is cheaper than asserting 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (_a == 0) { return 0; } c = _a * _b; assert(c / _a == _b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 _a, uint256 _b) internal pure returns (uint256) { // assert(_b > 0); // Solidity automatically throws when dividing by 0 // uint256 c = _a / _b; // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold return _a / _b; } /** * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 _a, uint256 _b) internal pure returns (uint256) { assert(_b <= _a); return _a - _b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) { c = _a + _b; assert(c >= _a); return c; } } library RecordKeeping { struct priceRecord { uint256 price; address owner; uint256 timestamp; } } contract Ownable { address public owner; event OwnershipRenounced(address indexed previousOwner); event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to relinquish control of the contract. * @notice Renouncing to ownership will leave the contract without an owner. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. */ function renounceOwnership() public onlyOwner { emit OwnershipRenounced(owner); owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function transferOwnership(address _newOwner) public onlyOwner { _transferOwnership(_newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function _transferOwnership(address _newOwner) internal { require(_newOwner != address(0)); emit OwnershipTransferred(owner, _newOwner); owner = _newOwner; } } contract Withdrawable is Ownable { // _changeType is used to indicate the type of the transaction // 0 - normal withdraw // 1 - deposit from selling asset // 2 - deposit from profit sharing of new token // 3 - deposit from auction // 4 - failed auction refund // 5 - referral commission event BalanceChanged(address indexed _owner, int256 _change, uint256 _balance, uint8 _changeType); mapping (address => uint256) internal pendingWithdrawals; //total pending amount uint256 internal totalPendingAmount; function _deposit(address addressToDeposit, uint256 amount, uint8 changeType) internal{ if (amount > 0) { _depositWithoutEvent(addressToDeposit, amount); emit BalanceChanged(addressToDeposit, int256(amount), pendingWithdrawals[addressToDeposit], changeType); } } function _depositWithoutEvent(address addressToDeposit, uint256 amount) internal{ pendingWithdrawals[addressToDeposit] += amount; totalPendingAmount += amount; } function getBalance(address addressToCheck) public view returns (uint256){ return pendingWithdrawals[addressToCheck]; } function withdrawOwnFund(address payable recipient_address) public { require(msg.sender==recipient_address); uint amount = pendingWithdrawals[recipient_address]; require(amount > 0); // Remember to zero the pending refund before // sending to prevent re-entrancy attacks pendingWithdrawals[recipient_address] = 0; totalPendingAmount -= amount; recipient_address.transfer(amount); emit BalanceChanged(recipient_address, -1 * int256(amount), 0, 0); } function checkAvailableContractBalance() public view returns (uint256){ if (address(this).balance > totalPendingAmount){ return address(this).balance - totalPendingAmount; } else{ return 0; } } function withdrawContractFund(address payable recipient_address) public onlyOwner { uint256 amountToWithdraw = checkAvailableContractBalance(); if (amountToWithdraw > 0){ recipient_address.transfer(amountToWithdraw); } } } contract ERC721Holder is ERC721Receiver { function onERC721Received( address, address, uint256, bytes memory ) public returns(bytes4) { return ERC721_RECEIVED; } } contract ERC721Basic is ERC165 { bytes4 internal constant InterfaceId_ERC721 = 0x80ac58cd; /* * 0x80ac58cd === * bytes4(keccak256('balanceOf(address)')) ^ * bytes4(keccak256('ownerOf(uint256)')) ^ * bytes4(keccak256('approve(address,uint256)')) ^ * bytes4(keccak256('getApproved(uint256)')) ^ * bytes4(keccak256('setApprovalForAll(address,bool)')) ^ * bytes4(keccak256('isApprovedForAll(address,address)')) ^ * bytes4(keccak256('transferFrom(address,address,uint256)')) ^ * bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^ * bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) */ bytes4 internal constant InterfaceId_ERC721Exists = 0x4f558e79; /* * 0x4f558e79 === * bytes4(keccak256('exists(uint256)')) */ bytes4 internal constant InterfaceId_ERC721Enumerable = 0x780e9d63; /** * 0x780e9d63 === * bytes4(keccak256('totalSupply()')) ^ * bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) ^ * bytes4(keccak256('tokenByIndex(uint256)')) */ bytes4 internal constant InterfaceId_ERC721Metadata = 0x5b5e139f; /** * 0x5b5e139f === * bytes4(keccak256('name()')) ^ * bytes4(keccak256('symbol()')) ^ * bytes4(keccak256('tokenURI(uint256)')) */ event Transfer( address indexed _from, address indexed _to, uint256 indexed _tokenId ); event Approval( address indexed _owner, address indexed _approved, uint256 indexed _tokenId ); event ApprovalForAll( address indexed _owner, address indexed _operator, bool _approved ); function balanceOf(address _owner) public view returns (uint256 _balance); function ownerOf(uint256 _tokenId) public view returns (address _owner); function exists(uint256 _tokenId) public view returns (bool _exists); function approve(address _to, uint256 _tokenId) public; function getApproved(uint256 _tokenId) public view returns (address _operator); function setApprovalForAll(address _operator, bool _approved) public; function isApprovedForAll(address _owner, address _operator) public view returns (bool); function transferFrom(address _from, address _to, uint256 _tokenId) public; function safeTransferFrom(address _from, address _to, uint256 _tokenId) public; function safeTransferFrom( address _from, address _to, uint256 _tokenId, bytes memory _data ) public; } contract Pausable is Ownable { event Pause(); event Unpause(); bool public paused = false; /** * @dev modifier to allow actions only when the contract IS paused */ modifier whenNotPaused() { require(!paused); _; } /** * @dev modifier to allow actions only when the contract IS NOT paused */ modifier whenPaused { require(paused); _; } /** * @dev called by the owner to pause, triggers stopped state */ function pause() public onlyOwner whenNotPaused returns (bool) { paused = true; emit Pause(); return true; } /** * @dev called by the owner to unpause, returns to normal state */ function unpause() public onlyOwner whenPaused returns (bool) { paused = false; emit Unpause(); return true; } } contract SupportsInterfaceWithLookup is ERC165 { bytes4 public constant InterfaceId_ERC165 = 0x01ffc9a7; /** * 0x01ffc9a7 === * bytes4(keccak256('supportsInterface(bytes4)')) */ /** * @dev a mapping of interface id to whether or not it's supported */ mapping(bytes4 => bool) internal supportedInterfaces; /** * @dev A contract implementing SupportsInterfaceWithLookup * implement ERC165 itself */ constructor() public { _registerInterface(InterfaceId_ERC165); } /** * @dev implement supportsInterface(bytes4) using a lookup table */ function supportsInterface(bytes4 _interfaceId) external view returns (bool) { return supportedInterfaces[_interfaceId]; } /** * @dev private method for registering an interface */ function _registerInterface(bytes4 _interfaceId) internal { require(_interfaceId != 0xffffffff); supportedInterfaces[_interfaceId] = true; } } contract TokenAuctionBase is ERC721Holder, Withdrawable { // Represents an auction on an NFT struct Auction { // Current owner of NFT address seller; // Mminimum selling Price (in wei) uint128 minPrice; // Duration (in seconds) of auction uint64 duration; // Time when auction started // NOTE: 0 if this auction has been concluded uint64 startedAt; //current highest Bid price uint128 highestPrice; //current highest Bidder address highestBidder; } // Reference to contract tracking NFT ownership ERC721Basic public nonFungibleContract; // Cut owner takes on each auction, measured in basis points (1/100 of a percent). // Values 0-10,000 map to 0%-100% uint256 public ownerCut; // Map from token ID to their corresponding auction. mapping (uint256 => Auction) tokenIdToAuction; event AuctionCreated(uint256 indexed tokenId, uint128 minPrice, uint64 duration); event AuctionSuccessful(uint256 indexed tokenId, uint128 totalPrice, address winner); event AuctionCancelled(uint256 indexed tokenId); event AuctionExpired(uint256 indexed tokenId); event HighestBidIncreased(uint256 indexed tokenId, address bidder, uint128 amount); /// @dev Returns true if the claimant owns the token. /// @param _claimant - Address claiming to own the token. /// @param _tokenId - ID of token whose ownership to verify. function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) { return (nonFungibleContract.ownerOf(_tokenId) == _claimant); } /// @dev Escrows the NFT, assigning ownership to this contract. /// Throws if the escrow fails. /// @param _owner - Current owner address of token to escrow. /// @param _tokenId - ID of token whose approval to verify. function _escrow(address _owner, uint256 _tokenId) internal { // it will throw if transfer fails nonFungibleContract.safeTransferFrom(_owner, address(this), _tokenId); } /// @dev Transfers an NFT owned by this contract to another address. /// Returns true if the transfer succeeds. /// @param _receiver - Address to transfer NFT to. /// @param _tokenId - ID of token to transfer. function _transfer(address _receiver, uint256 _tokenId) internal { // it will throw if transfer fails nonFungibleContract.safeTransferFrom(address(this), _receiver, _tokenId); } /// @dev Adds an auction to the list of open auctions. Also fires the /// AuctionCreated event. /// @param _tokenId The ID of the token to be put on auction. /// @param _auction Auction to add. function _addAuction(uint256 _tokenId, Auction memory _auction) internal { // Require that all auctions have a duration of // at least one minute. (Keeps our math from getting hairy!) require(_auction.duration >= 1 minutes); tokenIdToAuction[_tokenId] = _auction; emit AuctionCreated( _tokenId, uint128(_auction.minPrice), uint64(_auction.duration) ); } /// @dev Cancels an auction unconditionally. function _cancelAuction(uint256 _tokenId, address _seller) internal { _removeAuction(_tokenId); _transfer(_seller, _tokenId); emit AuctionCancelled(_tokenId); } function _bidWithDeposit (uint256 _tokenId, uint256 _additionalAmount) internal{ uint256 totalAmount = _additionalAmount + pendingWithdrawals[msg.sender]; pendingWithdrawals[msg.sender] = 0; _bid(_tokenId, totalAmount); } /// @dev Computes the price and transfers winnings. /// Does NOT transfer ownership of token. function _bid(uint256 _tokenId, uint256 _bidAmount) internal { // Get a reference to the auction struct Auction storage auction = tokenIdToAuction[_tokenId]; // Explicitly check that this auction is currently live. // (Because of how Ethereum mappings work, we can't just count // on the lookup above failing. An invalid _tokenId will just // return an auction object that is all zeros.) require(_isAuctionActive(auction)); // Check that the bid is greater than the highest price require(_bidAmount > auction.highestPrice && _bidAmount > auction.minPrice); //refund the current highest bidder if (auction.highestPrice != 0){ _deposit(auction.highestBidder, auction.highestPrice, 4); //make sure we unlock the amount from the withheld amount if (totalPendingAmount > auction.highestPrice) { totalPendingAmount -= auction.highestPrice; }else{ totalPendingAmount = 0; } } auction.highestPrice = uint128(msg.value); auction.highestBidder = msg.sender; //make sure we lock the escrow amount of the highest price to prevent withdrawing from the contract totalPendingAmount += auction.highestPrice; emit HighestBidIncreased(_tokenId,auction.highestBidder, auction.highestPrice); } function _concludeAuction(uint256 _tokenId) internal returns (uint256) { Auction storage auction = tokenIdToAuction[_tokenId]; require(_isOnAuction(auction)); uint256 secondsPassed = now - auction.startedAt; require(secondsPassed > auction.duration); if (auction.highestPrice > auction.minPrice && auction.highestPrice > 0) { uint256 auctioneerCut = _computeCut(auction.highestPrice); uint256 sellerProceeds = auction.highestPrice - auctioneerCut; emit AuctionSuccessful(_tokenId, auction.highestPrice, auction.highestBidder); return sellerProceeds; }else{ emit AuctionExpired(_tokenId); return 0; } } /// @dev Removes an auction from the list of open auctions. /// @param _tokenId - ID of NFT on auction. function _removeAuction(uint256 _tokenId) internal { delete tokenIdToAuction[_tokenId]; } /// @dev Returns true if the NFT is on auction. /// @param _auction - Auction to check. function _isOnAuction(Auction storage _auction) internal view returns (bool) { return (_auction.startedAt > 0); } function _isAuctionActive(Auction storage _auction) internal view returns (bool) { return (_auction.startedAt > 0 && now - _auction.startedAt < _auction.duration); } /// @dev Computes owner's cut of a sale. /// @param _price - Sale price of NFT. function _computeCut(uint256 _price) internal view returns (uint256) { // NOTE: We don't use SafeMath (or similar) in this function because // all of our entry functions carefully cap the maximum values for // currency (at 128-bits), and ownerCut <= 10000 (see the require() // statement in the ClockAuction constructor). The result of this // function is always guaranteed to be <= _price. return _price * ownerCut / 10000; } } contract ERC721Enumerable is ERC721Basic { function totalSupply() public view returns (uint256); function tokenOfOwnerByIndex( address _owner, uint256 _index ) public view returns (uint256 _tokenId); function tokenByIndex(uint256 _index) public view returns (uint256); } /** * @title ERC-721 Non-Fungible Token Standard, optional metadata extension * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md */ contract ERC721Metadata is ERC721Basic { function name() external view returns (string memory _name); function symbol() external view returns (string memory _symbol); function tokenURI(uint256 _tokenId) public view returns (string memory); } /** * @title ERC-721 Non-Fungible Token Standard, full implementation interface * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md */ contract ERC721 is ERC721Basic, ERC721Enumerable, ERC721Metadata { } contract ERC721BasicToken is SupportsInterfaceWithLookup, ERC721Basic { using SafeMath for uint256; using AddressUtils for address; // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` // which can be also obtained as `ERC721Receiver(0).onERC721Received.selector` bytes4 private constant ERC721_RECEIVED = 0x150b7a02; // Mapping from token ID to owner mapping (uint256 => address) internal tokenOwner; // Mapping from token ID to approved address mapping (uint256 => address) internal tokenApprovals; // Mapping from owner to number of owned token mapping (address => uint256) internal ownedTokensCount; // Mapping from owner to operator approvals mapping (address => mapping (address => bool)) internal operatorApprovals; constructor() public { // register the supported interfaces to conform to ERC721 via ERC165 _registerInterface(InterfaceId_ERC721); _registerInterface(InterfaceId_ERC721Exists); } /** * @dev Gets the balance of the specified address * @param _owner address to query the balance of * @return uint256 representing the amount owned by the passed address */ function balanceOf(address _owner) public view returns (uint256) { require(_owner != address(0)); return ownedTokensCount[_owner]; } /** * @dev Gets the owner of the specified token ID * @param _tokenId uint256 ID of the token to query the owner of * @return owner address currently marked as the owner of the given token ID */ function ownerOf(uint256 _tokenId) public view returns (address) { address owner = tokenOwner[_tokenId]; require(owner != address(0)); return owner; } /** * @dev Returns whether the specified token exists * @param _tokenId uint256 ID of the token to query the existence of * @return whether the token exists */ function exists(uint256 _tokenId) public view returns (bool) { address owner = tokenOwner[_tokenId]; return owner != address(0); } /** * @dev Approves another address to transfer the given token ID * The zero address indicates there is no approved address. * There can only be one approved address per token at a given time. * Can only be called by the token owner or an approved operator. * @param _to address to be approved for the given token ID * @param _tokenId uint256 ID of the token to be approved */ function approve(address _to, uint256 _tokenId) public { address owner = ownerOf(_tokenId); require(_to != owner); require(msg.sender == owner || isApprovedForAll(owner, msg.sender)); tokenApprovals[_tokenId] = _to; emit Approval(owner, _to, _tokenId); } /** * @dev Gets the approved address for a token ID, or zero if no address set * @param _tokenId uint256 ID of the token to query the approval of * @return address currently approved for the given token ID */ function getApproved(uint256 _tokenId) public view returns (address) { return tokenApprovals[_tokenId]; } /** * @dev Sets or unsets the approval of a given operator * An operator is allowed to transfer all tokens of the sender on their behalf * @param _to operator address to set the approval * @param _approved representing the status of the approval to be set */ function setApprovalForAll(address _to, bool _approved) public { require(_to != msg.sender); operatorApprovals[msg.sender][_to] = _approved; emit ApprovalForAll(msg.sender, _to, _approved); } /** * @dev Tells whether an operator is approved by a given owner * @param _owner owner address which you want to query the approval of * @param _operator operator address which you want to query the approval of * @return bool whether the given operator is approved by the given owner */ function isApprovedForAll( address _owner, address _operator ) public view returns (bool) { return operatorApprovals[_owner][_operator]; } /** * @dev Transfers the ownership of a given token ID to another address * Usage of this method is discouraged, use `safeTransferFrom` whenever possible * Requires the msg sender to be the owner, approved, or operator * @param _from current owner of the token * @param _to address to receive the ownership of the given token ID * @param _tokenId uint256 ID of the token to be transferred */ function transferFrom( address _from, address _to, uint256 _tokenId ) public { require(isApprovedOrOwner(msg.sender, _tokenId)); require(_from != address(0)); require(_to != address(0)); clearApproval(_from, _tokenId); removeTokenFrom(_from, _tokenId); addTokenTo(_to, _tokenId); emit Transfer(_from, _to, _tokenId); } /** * @dev Safely transfers the ownership of a given token ID to another address * If the target address is a contract, it must implement `onERC721Received`, * which is called upon a safe transfer, and return the magic value * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, * the transfer is reverted. * * Requires the msg sender to be the owner, approved, or operator * @param _from current owner of the token * @param _to address to receive the ownership of the given token ID * @param _tokenId uint256 ID of the token to be transferred */ function safeTransferFrom( address _from, address _to, uint256 _tokenId ) public { // solium-disable-next-line arg-overflow safeTransferFrom(_from, _to, _tokenId, ""); } /** * @dev Safely transfers the ownership of a given token ID to another address * If the target address is a contract, it must implement `onERC721Received`, * which is called upon a safe transfer, and return the magic value * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, * the transfer is reverted. * Requires the msg sender to be the owner, approved, or operator * @param _from current owner of the token * @param _to address to receive the ownership of the given token ID * @param _tokenId uint256 ID of the token to be transferred * @param _data bytes data to send along with a safe transfer check */ function safeTransferFrom( address _from, address _to, uint256 _tokenId, bytes memory _data ) public { transferFrom(_from, _to, _tokenId); // solium-disable-next-line arg-overflow require(checkAndCallSafeTransfer(_from, _to, _tokenId, _data)); } /** * @dev Returns whether the given spender can transfer a given token ID * @param _spender address of the spender to query * @param _tokenId uint256 ID of the token to be transferred * @return bool whether the msg.sender is approved for the given token ID, * is an operator of the owner, or is the owner of the token */ function isApprovedOrOwner( address _spender, uint256 _tokenId ) internal view returns (bool) { address owner = ownerOf(_tokenId); // Disable solium check because of // https://github.com/duaraghav8/Solium/issues/175 // solium-disable-next-line operator-whitespace return ( _spender == owner || getApproved(_tokenId) == _spender || isApprovedForAll(owner, _spender) ); } /** * @dev Internal function to mint a new token * Reverts if the given token ID already exists * @param _to The address that will own the minted token * @param _tokenId uint256 ID of the token to be minted by the msg.sender */ function _mint(address _to, uint256 _tokenId) internal { require(_to != address(0)); addTokenTo(_to, _tokenId); emit Transfer(address(0), _to, _tokenId); } /** * @dev Internal function to burn a specific token * Reverts if the token does not exist * @param _tokenId uint256 ID of the token being burned by the msg.sender */ function _burn(address _owner, uint256 _tokenId) internal { clearApproval(_owner, _tokenId); removeTokenFrom(_owner, _tokenId); emit Transfer(_owner, address(0), _tokenId); } /** * @dev Internal function to clear current approval of a given token ID * Reverts if the given address is not indeed the owner of the token * @param _owner owner of the token * @param _tokenId uint256 ID of the token to be transferred */ function clearApproval(address _owner, uint256 _tokenId) internal { require(ownerOf(_tokenId) == _owner); if (tokenApprovals[_tokenId] != address(0)) { tokenApprovals[_tokenId] = address(0); } } /** * @dev Internal function to add a token ID to the list of a given address * @param _to address representing the new owner of the given token ID * @param _tokenId uint256 ID of the token to be added to the tokens list of the given address */ function addTokenTo(address _to, uint256 _tokenId) internal { require(tokenOwner[_tokenId] == address(0)); tokenOwner[_tokenId] = _to; ownedTokensCount[_to] = ownedTokensCount[_to].add(1); } /** * @dev Internal function to remove a token ID from the list of a given address * @param _from address representing the previous owner of the given token ID * @param _tokenId uint256 ID of the token to be removed from the tokens list of the given address */ function removeTokenFrom(address _from, uint256 _tokenId) internal { require(ownerOf(_tokenId) == _from); ownedTokensCount[_from] = ownedTokensCount[_from].sub(1); tokenOwner[_tokenId] = address(0); } /** * @dev Internal function to invoke `onERC721Received` on a target address * The call is not executed if the target address is not a contract * @param _from address representing the previous owner of the given token ID * @param _to target address that will receive the tokens * @param _tokenId uint256 ID of the token to be transferred * @param _data bytes optional data to send along with the call * @return whether the call correctly returned the expected magic value */ function checkAndCallSafeTransfer( address _from, address _to, uint256 _tokenId, bytes memory _data ) internal returns (bool) { if (!_to.isContract()) { return true; } bytes4 retval = ERC721Receiver(_to).onERC721Received( msg.sender, _from, _tokenId, _data); return (retval == ERC721_RECEIVED); } } contract ERC721Token is SupportsInterfaceWithLookup, ERC721BasicToken, ERC721 { // Token name string internal name_; // Token symbol string internal symbol_; // Mapping from owner to list of owned token IDs mapping(address => uint256[]) internal ownedTokens; // Mapping from token ID to index of the owner tokens list mapping(uint256 => uint256) internal ownedTokensIndex; // Array with all token ids, used for enumeration uint256[] internal allTokens; // Mapping from token id to position in the allTokens array mapping(uint256 => uint256) internal allTokensIndex; // Optional mapping for token URIs mapping(uint256 => string) internal tokenURIs; /** * @dev Constructor function */ constructor(string memory _name, string memory _symbol) public { name_ = _name; symbol_ = _symbol; // register the supported interfaces to conform to ERC721 via ERC165 _registerInterface(InterfaceId_ERC721Enumerable); _registerInterface(InterfaceId_ERC721Metadata); } /** * @dev Gets the token name * @return string representing the token name */ function name() external view returns (string memory) { return name_; } /** * @dev Gets the token symbol * @return string representing the token symbol */ function symbol() external view returns (string memory) { return symbol_; } /** * @dev Returns an URI for a given token ID * Throws if the token ID does not exist. May return an empty string. * @param _tokenId uint256 ID of the token to query */ function tokenURI(uint256 _tokenId) public view returns (string memory) { require(exists(_tokenId)); return tokenURIs[_tokenId]; } /** * @dev Gets the token ID at a given index of the tokens list of the requested owner * @param _owner address owning the tokens list to be accessed * @param _index uint256 representing the index to be accessed of the requested tokens list * @return uint256 token ID at the given index of the tokens list owned by the requested address */ function tokenOfOwnerByIndex( address _owner, uint256 _index ) public view returns (uint256) { require(_index < balanceOf(_owner)); return ownedTokens[_owner][_index]; } /** * @dev Gets the total amount of tokens stored by the contract * @return uint256 representing the total amount of tokens */ function totalSupply() public view returns (uint256) { return allTokens.length; } /** * @dev Gets the token ID at a given index of all the tokens in this contract * Reverts if the index is greater or equal to the total number of tokens * @param _index uint256 representing the index to be accessed of the tokens list * @return uint256 token ID at the given index of the tokens list */ function tokenByIndex(uint256 _index) public view returns (uint256) { require(_index < totalSupply()); return allTokens[_index]; } /** * @dev Internal function to set the token URI for a given token * Reverts if the token ID does not exist * @param _tokenId uint256 ID of the token to set its URI * @param _uri string URI to assign */ function _setTokenURI(uint256 _tokenId, string memory _uri) internal { require(exists(_tokenId)); tokenURIs[_tokenId] = _uri; } /** * @dev Internal function to add a token ID to the list of a given address * @param _to address representing the new owner of the given token ID * @param _tokenId uint256 ID of the token to be added to the tokens list of the given address */ function addTokenTo(address _to, uint256 _tokenId) internal { super.addTokenTo(_to, _tokenId); uint256 length = ownedTokens[_to].length; ownedTokens[_to].push(_tokenId); ownedTokensIndex[_tokenId] = length; } /** * @dev Internal function to remove a token ID from the list of a given address * @param _from address representing the previous owner of the given token ID * @param _tokenId uint256 ID of the token to be removed from the tokens list of the given address */ function removeTokenFrom(address _from, uint256 _tokenId) internal { super.removeTokenFrom(_from, _tokenId); // To prevent a gap in the array, we store the last token in the index of the token to delete, and // then delete the last slot. uint256 tokenIndex = ownedTokensIndex[_tokenId]; uint256 lastTokenIndex = ownedTokens[_from].length.sub(1); uint256 lastToken = ownedTokens[_from][lastTokenIndex]; ownedTokens[_from][tokenIndex] = lastToken; // This also deletes the contents at the last position of the array ownedTokens[_from].length--; // Note that this will handle single-element arrays. In that case, both tokenIndex and lastTokenIndex are going to // be zero. Then we can make sure that we will remove _tokenId from the ownedTokens list since we are first swapping // the lastToken to the first position, and then dropping the element placed in the last position of the list ownedTokensIndex[_tokenId] = 0; ownedTokensIndex[lastToken] = tokenIndex; } /** * @dev Internal function to mint a new token * Reverts if the given token ID already exists * @param _to address the beneficiary that will own the minted token * @param _tokenId uint256 ID of the token to be minted by the msg.sender */ function _mint(address _to, uint256 _tokenId) internal { super._mint(_to, _tokenId); allTokensIndex[_tokenId] = allTokens.length; allTokens.push(_tokenId); } /** * @dev Internal function to burn a specific token * Reverts if the token does not exist * @param _owner owner of the token to burn * @param _tokenId uint256 ID of the token being burned by the msg.sender */ function _burn(address _owner, uint256 _tokenId) internal { super._burn(_owner, _tokenId); // Clear metadata (if any) if (bytes(tokenURIs[_tokenId]).length != 0) { delete tokenURIs[_tokenId]; } // Reorg all tokens array uint256 tokenIndex = allTokensIndex[_tokenId]; uint256 lastTokenIndex = allTokens.length.sub(1); uint256 lastToken = allTokens[lastTokenIndex]; allTokens[tokenIndex] = lastToken; allTokens[lastTokenIndex] = 0; allTokens.length--; allTokensIndex[_tokenId] = 0; allTokensIndex[lastToken] = tokenIndex; } } contract TokenAuction is TokenAuctionBase, Pausable { /// @dev The ERC-165 interface signature for ERC-721. /// Ref: https://github.com/ethereum/EIPs/issues/165 /// Ref: https://github.com/ethereum/EIPs/issues/721 bytes4 constant InterfaceSignature_ERC721 = bytes4(0x80ac58cd); /// @dev Constructor creates a reference to the NFT ownership contract /// and verifies the owner cut is in the valid range. /// @param _nftAddress - address of a deployed contract implementing /// the Nonfungible Interface. /// @param _cut - percent cut the owner takes on each auction, must be /// between 0-10,000. constructor (address _nftAddress, uint256 _cut) public { require(_cut <= 10000); ownerCut = _cut; ERC165 testContract = ERC165(_nftAddress); require(testContract.supportsInterface(InterfaceSignature_ERC721)); ERC721Basic candidateContract = ERC721Basic(_nftAddress); nonFungibleContract = candidateContract; } /// @dev Remove all Ether from the contract, which is the owner's cuts /// as well as any Ether sent directly to the contract address. /// Always transfers to the NFT contract, but can be called either by /// the owner or the NFT contract. function withdrawContractBalance(address payable recipient_address) external { address nftAddress = address(nonFungibleContract); require(recipient_address==nftAddress); require( msg.sender == owner || msg.sender == nftAddress ); require (address(this).balance - totalPendingAmount > 0, "no fund"); // We are using this boolean method to make sure that even if one fails it will still work recipient_address.transfer(address(this).balance - totalPendingAmount); } /// @dev Creates and begins a new auction. /// @param _tokenId - ID of token to auction, sender must be owner. /// @param _minPrice - minimum price of auction. /// @param _duration - Length of time to move between starting /// price and ending price (in seconds). /// @param _seller - Seller, if not the message sender function createAuction( uint256 _tokenId, uint256 _minPrice, uint256 _duration, address _seller ) external whenNotPaused { // Sanity check that no inputs overflow how many bits we've allocated // to store them in the auction struct. require(_minPrice == uint256(uint128(_minPrice))); require(_duration == uint256(uint64(_duration))); require(_owns(msg.sender, _tokenId)); _createAuction(_tokenId, _minPrice, _duration, _seller); } function _createAuction( uint256 _tokenId, uint256 _minPrice, uint256 _duration, address _seller ) internal { _escrow(msg.sender, _tokenId); Auction memory auction = Auction( _seller, uint128(_minPrice), uint64(_duration), uint64(now), 0, address(0) ); _addAuction(_tokenId, auction); } /// @dev Bids on an open auction, completing the auction and transferring /// ownership of the NFT if enough Ether is supplied. /// @param _tokenId - ID of token to bid on. function bid(uint256 _tokenId) external payable whenNotPaused { // _bid will throw if the bid or funds transfer fails _bid(_tokenId, msg.value); } /// @dev Cancels an auction that hasn't been won yet. /// Returns the NFT to original owner. /// @notice This is a state-modifying function that can /// be called while the contract is paused. /// @param _tokenId - ID of token on auction function cancelAuction(uint256 _tokenId) external { Auction storage auction = tokenIdToAuction[_tokenId]; require(_isAuctionActive(auction)); address seller = auction.seller; require(msg.sender == seller); if (auction.highestPrice != 0){ _deposit(auction.highestBidder, auction.highestPrice, 4); auction.startedAt = 0; auction.highestPrice = 0; } _cancelAuction(_tokenId, seller); } /// @dev Cancels an auction when the contract is paused. /// Only the owner may do this, and NFTs are returned to /// the seller. This should only be used in emergencies. /// @param _tokenId - ID of the NFT on auction to cancel. function cancelAuctionWhenPaused(uint256 _tokenId) whenPaused onlyOwner external { Auction storage auction = tokenIdToAuction[_tokenId]; require(_isOnAuction(auction)); if (auction.highestPrice != 0){ _deposit(auction.highestBidder, auction.highestPrice, 4); auction.startedAt = 0; auction.highestPrice = 0; } _cancelAuction(_tokenId, auction.seller); } /// @dev Returns auction info for an NFT on auction. /// @param _tokenId - ID of NFT on auction. function getAuction(uint256 _tokenId) external view returns ( address seller, uint256 minPrice, uint256 duration, uint256 startedAt, uint256 highestPrice, address highestBidder ) { Auction storage auction = tokenIdToAuction[_tokenId]; require(_isOnAuction(auction)); return ( auction.seller, auction.minPrice, auction.duration, auction.startedAt, auction.highestPrice, auction.highestBidder ); } function concludeAuction(uint256 _tokenId) public { Auction storage auction = tokenIdToAuction[_tokenId]; uint256 sellerProceeds = _concludeAuction(_tokenId); if (totalPendingAmount > sellerProceeds) { totalPendingAmount -= sellerProceeds; }else{ totalPendingAmount = 0; } if (sellerProceeds > 0) { _transfer(auction.highestBidder, _tokenId); _deposit(auction.seller, sellerProceeds, 3); } else { _transfer(auction.seller, _tokenId); _deposit(auction.highestBidder, auction.highestPrice, 4); } auction.startedAt = 0; _removeAuction(_tokenId); } } contract ERC721WithState is ERC721BasicToken { mapping (uint256 => uint8) internal tokenState; event TokenStateSet(uint256 indexed _tokenId, uint8 _state); function setTokenState(uint256 _tokenId, uint8 _state) public { require(isApprovedOrOwner(msg.sender, _tokenId)); require(exists(_tokenId)); tokenState[_tokenId] = _state; emit TokenStateSet(_tokenId, _state); } function getTokenState(uint256 _tokenId) public view returns (uint8){ require(exists(_tokenId)); return tokenState[_tokenId]; } } contract RetroArt is ERC721Token, Ownable, Withdrawable, ERC721WithState { address public stemTokenContractAddress; uint256 public currentPrice; uint256 constant initiailPrice = 0.03 ether; //new asset price increase at the rate that determined by the variable below //it is caculated from the current price + (current price / ( price rate * totalTokens / slowDownRate )) uint public priceRate = 10; uint public slowDownRate = 7; //Commission will be charged if a profit is made //Commission is the pure profit / profit Commission // measured in basis points (1/100 of a percent) // Values 0-10,000 map to 0%-100% uint public profitCommission = 500; //the referral percentage of the commission of selling of aset // measured in basis points (1/100 of a percent) // Values 0-10,000 map to 0%-100% uint public referralCommission = 3000; //share will be given to all tokens equally if a new asset is acquired. //the amount of total shared value is assetValue/sharePercentage // measured in basis points (1/100 of a percent) // Values 0-10,000 map to 0%-100% uint public sharePercentage = 3000; //number of shares for acquiring new asset. uint public numberOfShares = 10; string public uriPrefix =""; // Mapping from owner to list of owned token IDs mapping (uint256 => string) internal tokenTitles; mapping (uint256 => RecordKeeping.priceRecord) internal initialPriceRecords; mapping (uint256 => RecordKeeping.priceRecord) internal lastPriceRecords; mapping (uint256 => uint256) internal currentTokenPrices; event AssetAcquired(address indexed _owner, uint256 indexed _tokenId, string _title, uint256 _price); event TokenPriceSet(uint256 indexed _tokenId, uint256 _price); event TokenBrought(address indexed _from, address indexed _to, uint256 indexed _tokenId, uint256 _price); event PriceRateChanged(uint _priceRate); event SlowDownRateChanged(uint _slowDownRate); event ProfitCommissionChanged(uint _profitCommission); event MintPriceChanged(uint256 _price); event SharePercentageChanged(uint _sharePercentage); event NumberOfSharesChanged(uint _numberOfShares); event ReferralCommissionChanged(uint _referralCommission); event Burn(address indexed _owner, uint256 _tokenId); bytes4 private constant InterfaceId_RetroArt = 0x94fb30be; /* bytes4(keccak256("buyTokenFrom(address,address,uint256)"))^ bytes4(keccak256("setTokenPrice(uint256,uint256)"))^ bytes4(keccak256("setTokenState(uint256,uint8)"))^ bytes4(keccak256("getTokenState(uint256)")); */ address[] internal auctionContractAddresses; function tokenTitle(uint256 _tokenId) public view returns (string memory) { require(exists(_tokenId)); return tokenTitles[_tokenId]; } function lastPriceOf(uint256 _tokenId) public view returns (uint256) { require(exists(_tokenId)); return lastPriceRecords[_tokenId].price; } function lastTransactionTimeOf(uint256 _tokenId) public view returns (uint256) { require(exists(_tokenId)); return lastPriceRecords[_tokenId].timestamp; } function firstPriceOf(uint256 _tokenId) public view returns (uint256) { require(exists(_tokenId)); return initialPriceRecords[_tokenId].price; } function creatorOf(uint256 _tokenId) public view returns (address) { require(exists(_tokenId)); return initialPriceRecords[_tokenId].owner; } function firstTransactionTimeOf(uint256 _tokenId) public view returns (uint256) { require(exists(_tokenId)); return initialPriceRecords[_tokenId].timestamp; } //problem with current web3.js that can't return an array of struct function lastHistoryOf(uint256 _tokenId) internal view returns (RecordKeeping.priceRecord storage) { require(exists(_tokenId)); return lastPriceRecords[_tokenId]; } function firstHistoryOf(uint256 _tokenId) internal view returns (RecordKeeping.priceRecord storage) { require(exists(_tokenId)); return initialPriceRecords[_tokenId]; } function setPriceRate(uint _priceRate) public onlyOwner { priceRate = _priceRate; emit PriceRateChanged(priceRate); } function setSlowDownRate(uint _slowDownRate) public onlyOwner { slowDownRate = _slowDownRate; emit SlowDownRateChanged(slowDownRate); } function setprofitCommission(uint _profitCommission) public onlyOwner { require(_profitCommission <= 10000); profitCommission = _profitCommission; emit ProfitCommissionChanged(profitCommission); } function setSharePercentage(uint _sharePercentage) public onlyOwner { require(_sharePercentage <= 10000); sharePercentage = _sharePercentage; emit SharePercentageChanged(sharePercentage); } function setNumberOfShares(uint _numberOfShares) public onlyOwner { numberOfShares = _numberOfShares; emit NumberOfSharesChanged(numberOfShares); } function setReferralCommission(uint _referralCommission) public onlyOwner { require(_referralCommission <= 10000); referralCommission = _referralCommission; emit ReferralCommissionChanged(referralCommission); } function setUriPrefix(string memory _uri) public onlyOwner { uriPrefix = _uri; } //use the token name, symbol as usual //this contract create another ERC20 as stemToken, //the constructure takes the stemTokenName and stemTokenSymbol constructor(string memory _name, string memory _symbol , address _stemTokenAddress) ERC721Token(_name, _symbol) Ownable() public { currentPrice = initiailPrice; stemTokenContractAddress = _stemTokenAddress; _registerInterface(InterfaceId_RetroArt); } function getAllAssets() public view returns (uint256[] memory){ return allTokens; } function getAllAssetsForSale() public view returns (uint256[] memory){ uint arrayLength = allTokens.length; uint forSaleCount = 0; for (uint i = 0; i 0) { forSaleCount++; } } uint256[] memory tokensForSale = new uint256[](forSaleCount); uint j = 0; for (uint i = 0; i 0) { tokensForSale[j] = allTokens[i]; j++; } } return tokensForSale; } function getAssetsForSale(address _owner) public view returns (uint256[] memory) { uint arrayLength = allTokens.length; uint forSaleCount = 0; for (uint i = 0; i 0 && tokenOwner[allTokens[i]] == _owner) { forSaleCount++; } } uint256[] memory tokensForSale = new uint256[](forSaleCount); uint j = 0; for (uint i = 0; i 0 && tokenOwner[allTokens[i]] == _owner) { tokensForSale[j] = allTokens[i]; j++; } } return tokensForSale; } function getAssetsByState(uint8 _state) public view returns (uint256[] memory){ uint arrayLength = allTokens.length; uint matchCount = 0; for (uint i = 0; i= currentPrice); uint totalShares = numberOfShares; if (referralAddress != address(0)) totalShares++; uint numberOfTokens = allTokens.length; if (numberOfTokens > 0 && sharePercentage > 0) { uint256 perShareValue = 0; uint256 totalShareValue = msg.value * sharePercentage / 10000 ; if (totalShares > numberOfTokens) { if (referralAddress != address(0)) perShareValue = totalShareValue / (numberOfTokens + 1); else perShareValue = totalShareValue / numberOfTokens; for (uint i = 0; i < numberOfTokens; i++) { //turn off events if there are too many tokens in the loop if (numberOfTokens > 100) { _depositWithoutEvent(tokenOwner[allTokens[i]], perShareValue); }else{ _deposit(tokenOwner[allTokens[i]], perShareValue, 2); } } }else{ if (referralAddress != address(0)) perShareValue = totalShareValue / (totalShares + 1); else perShareValue = totalShareValue / totalShares; uint[] memory randomArray = random(numberOfShares); for (uint i = 0; i < numberOfShares; i++) { uint index = randomArray[i] % numberOfTokens; if (numberOfShares > 100) { _depositWithoutEvent(tokenOwner[allTokens[index]], perShareValue); }else{ _deposit(tokenOwner[allTokens[index]], perShareValue, 2); } } } if (referralAddress != address(0) && perShareValue > 0) _deposit(referralAddress, perShareValue, 5); } _acquireAsset(_tokenId, _title, msg.sender, msg.value); } function _acquireAsset(uint256 _tokenId, string memory _title, address _purchaser, uint256 _value) internal { currentPrice = CalculateNextPrice(); _mint(_purchaser, _tokenId); tokenTitles[_tokenId] = _title; RecordKeeping.priceRecord memory pr = RecordKeeping.priceRecord(_value, _purchaser, block.timestamp); initialPriceRecords[_tokenId] = pr; lastPriceRecords[_tokenId] = pr; emit AssetAcquired(_purchaser,_tokenId, _title, _value); emit TokenBrought(address(0), _purchaser, _tokenId, _value); emit MintPriceChanged(currentPrice); } function CalculateNextPrice() public view returns (uint256){ return currentPrice + currentPrice * slowDownRate / ( priceRate * (allTokens.length + 2)); } function tokensOf(address _owner) public view returns (uint256[] memory){ return ownedTokens[_owner]; } function _buyTokenFromWithReferral(address _from, address _to, uint256 _tokenId, address referralAddress, address _depositTo) internal { require(currentTokenPrices[_tokenId] != 0); require(msg.value >= currentTokenPrices[_tokenId]); tokenApprovals[_tokenId] = _to; safeTransferFrom(_from,_to,_tokenId); uint256 valueTransferToOwner = msg.value; uint256 lastRecordPrice = lastPriceRecords[_tokenId].price; if (msg.value > lastRecordPrice){ uint256 profit = msg.value - lastRecordPrice; uint256 commission = profit * profitCommission / 10000; valueTransferToOwner = msg.value - commission; if (referralAddress != address(0)){ _deposit(referralAddress, commission * referralCommission / 10000, 5); } } if (valueTransferToOwner > 0) _deposit(_depositTo, valueTransferToOwner, 1); writePriceRecordForAssetSold(_depositTo, msg.sender, _tokenId, msg.value); } function buyTokenFromWithReferral(address _from, address _to, uint256 _tokenId, address referralAddress) public payable { _buyTokenFromWithReferral(_from, _to, _tokenId, referralAddress, _from); } function buyTokenFrom(address _from, address _to, uint256 _tokenId) public payable { buyTokenFromWithReferral(_from, _to, _tokenId, address(0)); } function writePriceRecordForAssetSold(address _from, address _to, uint256 _tokenId, uint256 _value) internal { RecordKeeping.priceRecord memory pr = RecordKeeping.priceRecord(_value, _to, block.timestamp); lastPriceRecords[_tokenId] = pr; tokenApprovals[_tokenId] = address(0); currentTokenPrices[_tokenId] = 0; emit TokenBrought(_from, _to, _tokenId, _value); } function recordAuctionPriceRecord(address _from, address _to, uint256 _tokenId, uint256 _value) external { require(findAuctionContractIndex(msg.sender) >= 0); //make sure the sender is from one of the auction addresses writePriceRecordForAssetSold(_from, _to, _tokenId, _value); } function setTokenPrice(uint256 _tokenId, uint256 _newPrice) public { require(isApprovedOrOwner(msg.sender, _tokenId)); currentTokenPrices[_tokenId] = _newPrice; emit TokenPriceSet(_tokenId, _newPrice); } function getTokenPrice(uint256 _tokenId) public view returns(uint256) { return currentTokenPrices[_tokenId]; } function random(uint num) private view returns (uint[] memory) { uint base = uint(keccak256(abi.encodePacked(block.difficulty, now, tokenOwner[allTokens[allTokens.length-1]]))); uint[] memory randomNumbers = new uint[](num); for (uint i = 0; i= 0); for (uint i = uint(index); i < auctionContractAddresses.length-1; i++){ auctionContractAddresses[i] = auctionContractAddresses[i+1]; } auctionContractAddresses.length--; } function setStemTokenContractAddress(address _stemTokenContractAddress) public onlyOwner { stemTokenContractAddress = _stemTokenContractAddress; } function tokenURI(uint256 _tokenId) public view returns (string memory) { require(exists(_tokenId)); return string(abi.encodePacked(uriPrefix, uint256ToString(_tokenId))); } // Functions used for generating the URI function amountOfZeros(uint256 num, uint256 base) public pure returns(uint256){ uint256 result = 0; num /= base; while (num > 0){ num /= base; result += 1; } return result; } function uint256ToString(uint256 num) public pure returns(string memory){ if (num == 0){ return "0"; } uint256 numLen = amountOfZeros(num, 10) + 1; bytes memory result = new bytes(numLen); while(num != 0){ numLen -= 1; result[numLen] = byte(uint8((num - (num / 10 * 10)) + 48)); num /= 10; } return string(result); } // function initialImport(uint256[] memory _tokenIds, // uint256[] memory _lastPrices, address[] memory _owners, uint256[] memory _lastDates, // uint256[] memory _initialPrices, address[] memory _creators, uint256[] memory _initialDates, // string[] memory _titles ) public onlyOwner { // require( _tokenIds.length == _lastPrices.length && // _tokenIds.length == _owners.length && // _tokenIds.length == _lastDates.length && // _tokenIds.length == _initialPrices.length && // _tokenIds.length == _creators.length && // _tokenIds.length == _initialDates.length && // _tokenIds.length == _titles.length // ); // for (uint i = 0; i < _tokenIds.length; i++){ // allTokensIndex[_tokenIds[i]] = allTokens.length; // allTokens.push(_tokenIds[i]); // tokenTitles[_tokenIds[i]] = _titles[i]; // addTokenTo(_owners[i],_tokenIds[i]); // RecordKeeping.priceRecord memory prInitial = RecordKeeping.priceRecord(_initialPrices[i], _creators[i], _initialDates[i]); // initialPriceRecords[_tokenIds[i]] = prInitial; // RecordKeeping.priceRecord memory prLast = RecordKeeping.priceRecord(_lastPrices[i], _owners[i], _lastDates[i]); // lastPriceRecords[_tokenIds[i]] = prLast; // } // } } contract RetroArtTokenAuction is TokenAuction { // Delegate constructor constructor (address _nftAddr, uint256 _cut) public TokenAuction(_nftAddr, _cut) {} event ownerCutChanged(uint256 _ownerCut); /// @dev Creates and begins a new auction. /// @param _tokenId - ID of token to auction, sender must be owner. /// @param _minPrice - minimum price of auction. /// @param _duration - Length of time to move between starting /// price and ending price (in seconds). /// @param _seller - Seller, if not the message sender function createAuction( uint256 _tokenId, uint256 _minPrice, uint256 _duration, address _seller ) external whenNotPaused { require(_minPrice == uint256(uint128(_minPrice))); require(_duration == uint256(uint64(_duration))); require(_owns(msg.sender, _tokenId)); super._createAuction(_tokenId, _minPrice, _duration, _seller); address nonFungibleContractAddress = address(nonFungibleContract); ERC165 nftContract = ERC165(nonFungibleContractAddress); //bytes4 public constant InterfaceSignature_RetroArt = 0x94fb30be; if (nftContract.supportsInterface(0x94fb30be)) { RetroArt retroArt = RetroArt(nonFungibleContractAddress); //state 2 is onAuction retroArt.setTokenState(_tokenId,2); } } /// @dev Removes an auction from the list of open auctions. /// @param _tokenId - ID of NFT on auction. function _removeAuction(uint256 _tokenId) internal { delete tokenIdToAuction[_tokenId]; address nonFungibleContractAddress = address(nonFungibleContract); ERC165 nftContract = ERC165(nonFungibleContractAddress); //bytes4 public constant InterfaceSignature_RetroArt = 0x94fb30be; if (nftContract.supportsInterface(0x94fb30be)) { RetroArt retroArt = RetroArt(nonFungibleContractAddress); //state 0 is the original state retroArt.setTokenState(_tokenId,0); } } function concludeAuction(uint256 _tokenId) public { //todo: should it check who has permission to call this function? address nonFungibleContractAddress = address(nonFungibleContract); ERC165 nftContract = ERC165(nonFungibleContractAddress); Auction memory auction = tokenIdToAuction[_tokenId]; uint256 sellerProceeds = _concludeAuction(_tokenId); if (auction.highestPrice > 0){ if (totalPendingAmount > auction.highestPrice) { totalPendingAmount -= auction.highestPrice; }else{ totalPendingAmount = 0; } } _removeAuction(_tokenId); //bytes4 public constant InterfaceSignature_RetroArt = 0x94fb30be; if (sellerProceeds > 0) { _transfer(auction.highestBidder, _tokenId); _deposit(auction.seller, sellerProceeds, 3); if (nftContract.supportsInterface(0x94fb30be)) { RetroArt retroArt = RetroArt(nonFungibleContractAddress); retroArt.recordAuctionPriceRecord(auction.seller, auction.highestBidder, _tokenId, sellerProceeds); } } if (sellerProceeds == 0){ _transfer(auction.seller, _tokenId); if ( auction.highestPrice > 0 && auction.highestBidder != address(0)) _deposit(auction.highestBidder, auction.highestPrice, 4); } // auction.startedAt = 0; } /// @dev Returns auction info for an NFT on auction. /// @param _tokenId - ID of NFT on auction. function getAuction(uint256 _tokenId) external view returns ( address seller, uint256 minPrice, uint256 duration, uint256 startedAt, uint256 highestPrice, address highestBidder ) { Auction storage auction = tokenIdToAuction[_tokenId]; return ( auction.seller, auction.minPrice, auction.duration, auction.startedAt, auction.highestPrice, auction.highestBidder ); } function setOwnerCut(uint256 _cut) public onlyOwner { require(_cut <= 10000); ownerCut = _cut; emit ownerCutChanged(ownerCut); } }