ETH Price: $1,883.92 (+1.27%)

Transaction Decoder

Block:
7400538 at Mar-19-2019 05:12:54 PM +UTC
Transaction Fee:
0.00042764 ETH $0.81
Gas Used:
53,455 Gas / 8 Gwei

Emitted Events:

4 0x0aa36a98574bfcecda92d7815c14657b3f35e63b.0x101214446435ebbb29893f3348e3aae5ea070b63037a3df346d09d3396a34aee( 0x101214446435ebbb29893f3348e3aae5ea070b63037a3df346d09d3396a34aee, 0f96be2b35968c0b5d351ef67477bb8ff84dac3b47c7f4a630a38984669f46cb, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000001 )

Account State Difference:

  Address   Before After State Difference Code
0x0Aa36a98...B3f35e63b
(Spark Pool)
6,995.56750864124395383 Eth6,995.56793628124395383 Eth0.00042764
0xE2fFab08...CB44a5c75
0.17898213075505 Eth
Nonce: 88
0.17855449075505 Eth
Nonce: 89
0.00042764

Execution Trace

0x0aa36a98574bfcecda92d7815c14657b3f35e63b.91aeeedc( )
  • Null: 0x000...001.0f96be2b( )
  • ETH 0.02 SaleClockAuction.bid( _tokenId=685527 )
    pragma solidity ^0.4.11;
    
    
    /**
     * @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;
    
    
      /**
       * @dev The Ownable constructor sets the original `owner` of the contract to the sender
       * account.
       */
      function Ownable() {
        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) onlyOwner {
        if (newOwner != address(0)) {
          owner = newOwner;
        }
      }
    
    }
    
    
    
    /// @title Interface for contracts conforming to ERC-721: Non-Fungible Tokens
    /// @author Dieter Shirley <[email protected]> (https://github.com/dete)
    contract ERC721 {
        // Required methods
        function totalSupply() public view returns (uint256 total);
        function balanceOf(address _owner) public view returns (uint256 balance);
        function ownerOf(uint256 _tokenId) external view returns (address owner);
        function approve(address _to, uint256 _tokenId) external;
        function transfer(address _to, uint256 _tokenId) external;
        function transferFrom(address _from, address _to, uint256 _tokenId) external;
    
        // Events
        event Transfer(address from, address to, uint256 tokenId);
        event Approval(address owner, address approved, uint256 tokenId);
    
        // Optional
        // function name() public view returns (string name);
        // function symbol() public view returns (string symbol);
        // function tokensOfOwner(address _owner) external view returns (uint256[] tokenIds);
        // function tokenMetadata(uint256 _tokenId, string _preferredTransport) public view returns (string infoUrl);
    
        // ERC-165 Compatibility (https://github.com/ethereum/EIPs/issues/165)
        function supportsInterface(bytes4 _interfaceID) external view returns (bool);
    }
    
    
    
    
    
    
    
    
    
    /// @title Auction Core
    /// @dev Contains models, variables, and internal methods for the auction.
    /// @notice We omit a fallback function to prevent accidental sends to this contract.
    contract ClockAuctionBase {
    
        // Represents an auction on an NFT
        struct Auction {
            // 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;
        }
    
        // Reference to contract tracking NFT ownership
        ERC721 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 tokenId, uint256 startingPrice, uint256 endingPrice, uint256 duration);
        event AuctionSuccessful(uint256 tokenId, uint256 totalPrice, address winner);
        event AuctionCancelled(uint256 tokenId);
    
        /// @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.transferFrom(_owner, 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.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(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);
    
            tokenIdToAuction[_tokenId] = _auction;
    
            AuctionCreated(
                uint256(_tokenId),
                uint256(_auction.startingPrice),
                uint256(_auction.endingPrice),
                uint256(_auction.duration)
            );
        }
    
        /// @dev Cancels an auction unconditionally.
        function _cancelAuction(uint256 _tokenId, address _seller) internal {
            _removeAuction(_tokenId);
            _transfer(_seller, _tokenId);
            AuctionCancelled(_tokenId);
        }
    
        /// @dev Computes the price and transfers winnings.
        /// Does NOT transfer ownership of token.
        function _bid(uint256 _tokenId, uint256 _bidAmount)
            internal
            returns (uint256)
        {
            // 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(_isOnAuction(auction));
    
            // Check that the bid is greater than or equal to 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(_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);
            }
    
            // Calculate any excess funds included with the bid. If the excess
            // is anything worth worrying about, transfer it back to bidder.
            // NOTE: We checked above that the bid amount is greater than or
            // equal to the price so this cannot underflow.
            uint256 bidExcess = _bidAmount - price;
    
            // Return the funds. Similar to the previous transfer, this is
            // not susceptible to a re-entry attack because the auction is
            // removed before any transfers occur.
            msg.sender.transfer(bidExcess);
    
            // Tell the world!
            AuctionSuccessful(_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(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);
        }
    
        /// @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;
        }
    
    }
    
    
    
    
    
    
    
    /**
     * @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 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() onlyOwner whenNotPaused returns (bool) {
        paused = true;
        Pause();
        return true;
      }
    
      /**
       * @dev called by the owner to unpause, returns to normal state
       */
      function unpause() onlyOwner whenPaused returns (bool) {
        paused = false;
        Unpause();
        return true;
      }
    }
    
    
    /// @title Clock auction for non-fungible tokens.
    /// @notice We omit a fallback function to prevent accidental sends to this contract.
    contract ClockAuction is Pausable, ClockAuctionBase {
    
        /// @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(0x9a20483d);
    
        /// @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.
        function ClockAuction(address _nftAddress, uint256 _cut) public {
            require(_cut <= 10000);
            ownerCut = _cut;
    
            ERC721 candidateContract = ERC721(_nftAddress);
            require(candidateContract.supportsInterface(InterfaceSignature_ERC721));
            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 withdrawBalance() external {
            address nftAddress = address(nonFungibleContract);
    
            require(
                msg.sender == owner ||
                msg.sender == nftAddress
            );
            // We are using this boolean method to make sure that even if one fails it will still work
            bool res = nftAddress.send(this.balance);
        }
    
        /// @dev Creates and begins a new auction.
        /// @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(
            uint256 _tokenId,
            uint256 _startingPrice,
            uint256 _endingPrice,
            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(_startingPrice == uint256(uint128(_startingPrice)));
            require(_endingPrice == uint256(uint128(_endingPrice)));
            require(_duration == uint256(uint64(_duration)));
    
            require(_owns(msg.sender, _tokenId));
            _escrow(msg.sender, _tokenId);
            Auction memory auction = Auction(
                _seller,
                uint128(_startingPrice),
                uint128(_endingPrice),
                uint64(_duration),
                uint64(now)
            );
            _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);
            _transfer(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 _tokenId - ID of token on auction
        function cancelAuction(uint256 _tokenId)
            external
        {
            Auction storage auction = tokenIdToAuction[_tokenId];
            require(_isOnAuction(auction));
            address seller = auction.seller;
            require(msg.sender == seller);
            _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));
            _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 startingPrice,
            uint256 endingPrice,
            uint256 duration,
            uint256 startedAt
        ) {
            Auction storage auction = tokenIdToAuction[_tokenId];
            require(_isOnAuction(auction));
            return (
                auction.seller,
                auction.startingPrice,
                auction.endingPrice,
                auction.duration,
                auction.startedAt
            );
        }
    
        /// @dev Returns the current price of an auction.
        /// @param _tokenId - ID of the token price we are checking.
        function getCurrentPrice(uint256 _tokenId)
            external
            view
            returns (uint256)
        {
            Auction storage auction = tokenIdToAuction[_tokenId];
            require(_isOnAuction(auction));
            return _currentPrice(auction);
        }
    
    }
    
    
    /// @title Clock auction modified for sale of kitties
    /// @notice We omit a fallback function to prevent accidental sends to this contract.
    contract SaleClockAuction is ClockAuction {
    
        // @dev Sanity check that allows us to ensure that we are pointing to the
        //  right auction in our setSaleAuctionAddress() call.
        bool public isSaleClockAuction = true;
        
        // Tracks last 5 sale price of gen0 kitty sales
        uint256 public gen0SaleCount;
        uint256[5] public lastGen0SalePrices;
    
        // Delegate constructor
        function SaleClockAuction(address _nftAddr, uint256 _cut) public
            ClockAuction(_nftAddr, _cut) {}
    
        /// @dev Creates and begins a new auction.
        /// @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).
        /// @param _seller - Seller, if not the message sender
        function createAuction(
            uint256 _tokenId,
            uint256 _startingPrice,
            uint256 _endingPrice,
            uint256 _duration,
            address _seller
        )
            external
        {
            // Sanity check that no inputs overflow how many bits we've allocated
            // to store them in the auction struct.
            require(_startingPrice == uint256(uint128(_startingPrice)));
            require(_endingPrice == uint256(uint128(_endingPrice)));
            require(_duration == uint256(uint64(_duration)));
    
            require(msg.sender == address(nonFungibleContract));
            _escrow(_seller, _tokenId);
            Auction memory auction = Auction(
                _seller,
                uint128(_startingPrice),
                uint128(_endingPrice),
                uint64(_duration),
                uint64(now)
            );
            _addAuction(_tokenId, auction);
        }
    
        /// @dev Updates lastSalePrice if seller is the nft contract
        /// Otherwise, works the same as default bid method.
        function bid(uint256 _tokenId)
            external
            payable
        {
            // _bid verifies token ID size
            address seller = tokenIdToAuction[_tokenId].seller;
            uint256 price = _bid(_tokenId, msg.value);
            _transfer(msg.sender, _tokenId);
    
            // If not a gen0 auction, exit
            if (seller == address(nonFungibleContract)) {
                // Track gen0 sale prices
                lastGen0SalePrices[gen0SaleCount % 5] = price;
                gen0SaleCount++;
            }
        }
    
        function averageGen0SalePrice() external view returns (uint256) {
            uint256 sum = 0;
            for (uint256 i = 0; i < 5; i++) {
                sum += lastGen0SalePrices[i];
            }
            return sum / 5;
        }
    
    }