/** *Submitted for verification at Etherscan.io on 2018-01-10 */ pragma solidity ^0.4.18; /** * Interface for required functionality in the ERC721 standard * for non-fungible tokens. * * Author: Nadav Hollander (nadav at dharma.io) */ contract ERC721 { // Function function totalSupply() public view returns (uint256 _totalSupply); function balanceOf(address _owner) public view returns (uint256 _balance); function ownerOf(uint _tokenId) public view returns (address _owner); function approve(address _to, uint _tokenId) public; function transferFrom(address _from, address _to, uint _tokenId) public; function transfer(address _to, uint _tokenId) public; function implementsERC721() public view returns (bool _implementsERC721); // Events event Transfer(address indexed _from, address indexed _to, uint256 _tokenId); event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId); } /// @title Auction Core /// @dev Contains models, variables, and internal methods for the auction. contract ClockAuctionBase { // Represents an auction on an NFT struct Auction { // Address of the NFT address nftAddress; // Current owner of NFT address seller; // Price (in wei) at beginning of auction uint128 startingPrice; // Price (in wei) at end of auction uint128 endingPrice; // Duration (in seconds) of auction uint64 duration; // Time when auction started // NOTE: 0 if this auction has been concluded uint64 startedAt; } // 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 (address => mapping(uint256 => Auction)) nftToTokenIdToAuction; event AuctionCreated(address nftAddress, uint256 tokenId, uint256 startingPrice, uint256 endingPrice, uint256 duration); event AuctionSuccessful(address nftAddress, uint256 tokenId, uint256 totalPrice, address winner); event AuctionCancelled(address nftAddress, uint256 tokenId); /// @dev DON'T give me your money. function() external {} // Modifiers to check that inputs can be safely stored with a certain // number of bits. We use constants and multiple modifiers to save gas. modifier canBeStoredWith64Bits(uint256 _value) { require(_value <= 18446744073709551615); _; } modifier canBeStoredWith128Bits(uint256 _value) { require(_value < 340282366920938463463374607431768211455); _; } /// @dev Returns true if the claimant owns the token. /// @param _nft - The address of the NFT. /// @param _claimant - Address claiming to own the token. /// @param _tokenId - ID of token whose ownership to verify. function _owns(address _nft, address _claimant, uint256 _tokenId) internal view returns (bool) { ERC721 nonFungibleContract = _getNft(_nft); return (nonFungibleContract.ownerOf(_tokenId) == _claimant); } /// @dev Escrows the NFT, assigning ownership to this contract. /// Throws if the escrow fails. /// @param _nft - The address of the NFT. /// @param _owner - Current owner address of token to escrow. /// @param _tokenId - ID of token whose approval to verify. function _escrow(address _nft, address _owner, uint256 _tokenId) internal { ERC721 nonFungibleContract = _getNft(_nft); // it will throw if transfer fails nonFungibleContract.transferFrom(_owner, this, _tokenId); } /// @dev Transfers an NFT owned by this contract to another address. /// Returns true if the transfer succeeds. /// @param _nft - The address of the NFT. /// @param _receiver - Address to transfer NFT to. /// @param _tokenId - ID of token to transfer. function _transfer(address _nft, address _receiver, uint256 _tokenId) internal { ERC721 nonFungibleContract = _getNft(_nft); // it will throw if transfer fails nonFungibleContract.transfer(_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(address _nft, uint256 _tokenId, Auction _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); nftToTokenIdToAuction[_nft][_tokenId] = _auction; AuctionCreated( address(_nft), uint256(_tokenId), uint256(_auction.startingPrice), uint256(_auction.endingPrice), uint256(_auction.duration) ); } /// @dev Cancels an auction unconditionally. function _cancelAuction(address _nft, uint256 _tokenId, address _seller) internal { _removeAuction(_nft, _tokenId); _transfer(_nft, _seller, _tokenId); AuctionCancelled(_nft, _tokenId); } /// @dev Computes the price and transfers winnings. /// Does NOT transfer ownership of token. function _bid(address _nft, uint256 _tokenId, uint256 _bidAmount) internal returns (uint256) { // Get a reference to the auction struct Auction storage auction = nftToTokenIdToAuction[_nft][_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(_isOnAuction(auction)); // Check that the incoming bid is higher than the current // price uint256 price = _currentPrice(auction); require(_bidAmount >= price); // Grab a reference to the seller before the auction struct // gets deleted. address seller = auction.seller; // The bid is good! Remove the auction before sending the fees // to the sender so we can't have a reentrancy attack. _removeAuction(_nft, _tokenId); // Transfer proceeds to seller (if there are any!) if (price > 0) { // Calculate the auctioneer's cut. // (NOTE: _computeCut() is guaranteed to return a // value <= price, so this subtraction can't go negative.) uint256 auctioneerCut = _computeCut(price); uint256 sellerProceeds = price - auctioneerCut; // NOTE: Doing a transfer() in the middle of a complex // method like this is generally discouraged because of // reentrancy attacks and DoS attacks if the seller is // a contract with an invalid fallback function. We explicitly // guard against reentrancy attacks by removing the auction // before calling transfer(), and the only thing the seller // can DoS is the sale of their own asset! (And if it's an // accident, they can call cancelAuction(). ) seller.transfer(sellerProceeds); } // Tell the world! AuctionSuccessful(_nft, _tokenId, price, msg.sender); return price; } /// @dev Removes an auction from the list of open auctions. /// @param _tokenId - ID of NFT on auction. function _removeAuction(address _nft, uint256 _tokenId) internal { delete nftToTokenIdToAuction[_nft][_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); } /// @dev Returns current price of an NFT on auction. Broken into two /// functions (this one, that computes the duration from the auction /// structure, and the other that does the price computation) so we /// can easily test that the price computation works correctly. function _currentPrice(Auction storage _auction) internal view returns (uint256) { uint256 secondsPassed = 0; // A bit of insurance against negative values (or wraparound). // Probably not necessary (since Ethereum guarnatees that the // now variable doesn't ever go backwards). if (now > _auction.startedAt) { secondsPassed = now - _auction.startedAt; } return _computeCurrentPrice( _auction.startingPrice, _auction.endingPrice, _auction.duration, secondsPassed ); } /// @dev Computes the current price of an auction. Factored out /// from _currentPrice so we can run extensive unit tests. /// When testing, make this function public and turn on /// `Current price computation` test suite. function _computeCurrentPrice( uint256 _startingPrice, uint256 _endingPrice, uint256 _duration, uint256 _secondsPassed ) internal pure returns (uint256) { // NOTE: We don't use SafeMath (or similar) in this function because // all of our public functions carefully cap the maximum values for // time (at 64-bits) and currency (at 128-bits). _duration is // also known to be non-zero (see the require() statement in // _addAuction()) if (_secondsPassed >= _duration) { // We've reached the end of the dynamic pricing portion // of the auction, just return the end price. return _endingPrice; } else { // Starting price can be higher than ending price (and often is!), so // this delta can be negative. int256 totalPriceChange = int256(_endingPrice) - int256(_startingPrice); // This multiplication can't overflow, _secondsPassed will easily fit within // 64-bits, and totalPriceChange will easily fit within 128-bits, their product // will always fit within 256-bits. int256 currentPriceChange = totalPriceChange * int256(_secondsPassed) / int256(_duration); // currentPriceChange can be negative, but if so, will have a magnitude // less that _startingPrice. Thus, this result will always end up positive. int256 currentPrice = int256(_startingPrice) + currentPriceChange; return uint256(currentPrice); } } /// @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; } /// @dev Gets the NFT object from an address, validating that implementsERC721 is true. /// @param _nft - Address of the NFT. function _getNft(address _nft) internal view returns (ERC721) { ERC721 candidateContract = ERC721(_nft); //require(candidateContract.implementsERC721()); return candidateContract; } } /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address public owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ function Ownable() 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 transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) public onlyOwner { require(newOwner != address(0)); OwnershipTransferred(owner, newOwner); owner = newOwner; } } /** * @title Pausable * @dev Base contract which allows children to implement an emergency stop mechanism. */ contract Pausable is Ownable { event Pause(); event Unpause(); bool public paused = false; /** * @dev Modifier to make a function callable only when the contract is not paused. */ modifier whenNotPaused() { require(!paused); _; } /** * @dev Modifier to make a function callable only when the contract is paused. */ modifier whenPaused() { require(paused); _; } /** * @dev called by the owner to pause, triggers stopped state */ function pause() onlyOwner whenNotPaused public { paused = true; Pause(); } /** * @dev called by the owner to unpause, returns to normal state */ function unpause() onlyOwner whenPaused public { paused = false; Unpause(); } } /// @title Clock auction for non-fungible tokens. contract ClockAuction is Pausable, ClockAuctionBase { /// @dev Constructor creates a reference to the NFT ownership contract /// and verifies the owner cut is in the valid range. /// @param _cut - percent cut the owner takes on each auction, must be /// between 0-10,000. function ClockAuction(uint256 _cut) public { require(_cut <= 10000); ownerCut = _cut; } /// @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 withdrawBalance() external { require( msg.sender == owner ); msg.sender.transfer(this.balance); } /// @dev Creates and begins a new auction. /// @param _nftAddress - address of a deployed contract implementing /// the Nonfungible Interface. /// @param _tokenId - ID of token to auction, sender must be owner. /// @param _startingPrice - Price of item (in wei) at beginning of auction. /// @param _endingPrice - Price of item (in wei) at end 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( address _nftAddress, uint256 _tokenId, uint256 _startingPrice, uint256 _endingPrice, uint256 _duration, address _seller ) public whenNotPaused canBeStoredWith128Bits(_startingPrice) canBeStoredWith128Bits(_endingPrice) canBeStoredWith64Bits(_duration) { require(_owns(_nftAddress, msg.sender, _tokenId)); _escrow(_nftAddress, msg.sender, _tokenId); Auction memory auction = Auction( _nftAddress, _seller, uint128(_startingPrice), uint128(_endingPrice), uint64(_duration), uint64(now) ); _addAuction(_nftAddress, _tokenId, auction); } /// @dev Bids on an open auction, completing the auction and transferring /// ownership of the NFT if enough Ether is supplied. /// @param _nftAddress - address of a deployed contract implementing /// the Nonfungible Interface. /// @param _tokenId - ID of token to bid on. function bid(address _nftAddress, uint256 _tokenId) public payable whenNotPaused { // _bid will throw if the bid or funds transfer fails _bid(_nftAddress, _tokenId, msg.value); _transfer(_nftAddress, msg.sender, _tokenId); } /// @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 _nftAddress - Address of the NFT. /// @param _tokenId - ID of token on auction function cancelAuction(address _nftAddress, uint256 _tokenId) public { Auction storage auction = nftToTokenIdToAuction[_nftAddress][_tokenId]; require(_isOnAuction(auction)); address seller = auction.seller; require(msg.sender == seller); _cancelAuction(_nftAddress, _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 _nftAddress - Address of the NFT. /// @param _tokenId - ID of the NFT on auction to cancel. function cancelAuctionWhenPaused(address _nftAddress, uint256 _tokenId) whenPaused onlyOwner public { Auction storage auction = nftToTokenIdToAuction[_nftAddress][_tokenId]; require(_isOnAuction(auction)); _cancelAuction(_nftAddress, _tokenId, auction.seller); } /// @dev Returns auction info for an NFT on auction. /// @param _nftAddress - Address of the NFT. /// @param _tokenId - ID of NFT on auction. function getAuction(address _nftAddress, uint256 _tokenId) public view returns ( address seller, uint256 startingPrice, uint256 endingPrice, uint256 duration, uint256 startedAt ) { Auction storage auction = nftToTokenIdToAuction[_nftAddress][_tokenId]; require(_isOnAuction(auction)); return ( auction.seller, auction.startingPrice, auction.endingPrice, auction.duration, auction.startedAt ); } /// @dev Returns the current price of an auction. /// @param _nftAddress - Address of the NFT. /// @param _tokenId - ID of the token price we are checking. function getCurrentPrice(address _nftAddress, uint256 _tokenId) public view returns (uint256) { Auction storage auction = nftToTokenIdToAuction[_nftAddress][_tokenId]; require(_isOnAuction(auction)); return _currentPrice(auction); } } /// @title Clock auction modified for sale of kitties contract SaleClockAuction is ClockAuction { // Delegate constructor function SaleClockAuction(uint256 _cut) public ClockAuction(_cut) {} /// @dev Creates and begins a new auction. /// @param _nftAddress - The address of the NFT. /// @param _tokenId - ID of token to auction, sender must be owner. /// @param _startingPrice - Price of item (in wei) at beginning of auction. /// @param _endingPrice - Price of item (in wei) at end of auction. /// @param _duration - Length of auction (in seconds). function createAuction( address _nftAddress, uint256 _tokenId, uint256 _startingPrice, uint256 _endingPrice, uint256 _duration ) public canBeStoredWith128Bits(_startingPrice) canBeStoredWith128Bits(_endingPrice) canBeStoredWith64Bits(_duration) { address seller = msg.sender; _escrow(_nftAddress, seller, _tokenId); Auction memory auction = Auction( _nftAddress, seller, uint128(_startingPrice), uint128(_endingPrice), uint64(_duration), uint64(now) ); _addAuction(_nftAddress, _tokenId, auction); } /// @dev Updates lastSalePrice if seller is the nft contract /// Otherwise, works the same as default bid method. function bid(address _nftAddress, uint256 _tokenId) public payable { // _bid verifies token ID size address seller = nftToTokenIdToAuction[_nftAddress][_tokenId].seller; uint256 price = _bid(_nftAddress, _tokenId, msg.value); _transfer(_nftAddress, msg.sender, _tokenId); } }