ETH Price: $2,509.13 (+0.01%)

Transaction Decoder

Block:
8821370 at Oct-27-2019 11:41:34 AM +UTC
Transaction Fee:
0.000502007 ETH $1.26
Gas Used:
45,637 Gas / 11 Gwei

Emitted Events:

38 OriginToken.Transfer( from=[Receiver] V00_Marketplace, to=[Sender] 0x343a3c7f789335c9ea60932d34be258f643678d9, value=0 )
39 V00_Marketplace.ListingWithdrawn( party=[Sender] 0x343a3c7f789335c9ea60932d34be258f643678d9, listingID=2316, ipfsHash=17A7D0B206F30ACD861773AACE102AB35F41D2A0916AEF975B2809D0B3CED1A8 )

Account State Difference:

  Address   Before After State Difference Code
0x343a3C7F...F643678d9
0.011334300281576316 Eth
Nonce: 278
0.010832293281576316 Eth
Nonce: 279
0.000502007
(Hiveon: Old Pool)
333.438354608528521858 Eth333.438856615528521858 Eth0.000502007

Execution Trace

V00_Marketplace.withdrawListing( listingID=2316, _target=0x343a3C7F789335C9EA60932D34bE258F643678d9, _ipfsHash=17A7D0B206F30ACD861773AACE102AB35F41D2A0916AEF975B2809D0B3CED1A8 )
  • OriginToken.transfer( _to=0x343a3C7F789335C9EA60932D34bE258F643678d9, _value=0 ) => ( True )
    File 1 of 2: V00_Marketplace
    pragma solidity ^0.4.24;
    // produced by the Solididy File Flattener (c) David Appleton 2018
    // contact : [email protected]
    // released under Apache 2.0 licence
    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.
       */
      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 ERC20 {
        function transfer(address _to, uint256 _value) external returns (bool);
        function transferFrom(address _from, address _to, uint256 _value) external returns (bool);
    }
    
    
    contract V00_Marketplace is Ownable {
    
        /**
        * @notice All events have the same indexed signature offsets for easy filtering
        */
        event MarketplaceData  (address indexed party, bytes32 ipfsHash);
        event AffiliateAdded   (address indexed party, bytes32 ipfsHash);
        event AffiliateRemoved (address indexed party, bytes32 ipfsHash);
        event ListingCreated   (address indexed party, uint indexed listingID, bytes32 ipfsHash);
        event ListingUpdated   (address indexed party, uint indexed listingID, bytes32 ipfsHash);
        event ListingWithdrawn (address indexed party, uint indexed listingID, bytes32 ipfsHash);
        event ListingArbitrated(address indexed party, uint indexed listingID, bytes32 ipfsHash);
        event ListingData      (address indexed party, uint indexed listingID, bytes32 ipfsHash);
        event OfferCreated     (address indexed party, uint indexed listingID, uint indexed offerID, bytes32 ipfsHash);
        event OfferAccepted    (address indexed party, uint indexed listingID, uint indexed offerID, bytes32 ipfsHash);
        event OfferFinalized   (address indexed party, uint indexed listingID, uint indexed offerID, bytes32 ipfsHash);
        event OfferWithdrawn   (address indexed party, uint indexed listingID, uint indexed offerID, bytes32 ipfsHash);
        event OfferFundsAdded  (address indexed party, uint indexed listingID, uint indexed offerID, bytes32 ipfsHash);
        event OfferDisputed    (address indexed party, uint indexed listingID, uint indexed offerID, bytes32 ipfsHash);
        event OfferRuling      (address indexed party, uint indexed listingID, uint indexed offerID, bytes32 ipfsHash, uint ruling);
        event OfferData        (address indexed party, uint indexed listingID, uint indexed offerID, bytes32 ipfsHash);
    
        struct Listing {
            address seller;     // Seller wallet / identity contract / other contract
            uint deposit;       // Deposit in Origin Token
            address depositManager; // Address that decides token distribution
        }
    
        struct Offer {
            uint value;         // Amount in Eth or ERC20 buyer is offering
            uint commission;    // Amount of commission earned if offer is finalized
            uint refund;        // Amount to refund buyer upon finalization
            ERC20 currency;     // Currency of listing
            address buyer;      // Buyer wallet / identity contract / other contract
            address affiliate;  // Address to send any commission
            address arbitrator; // Address that settles disputes
            uint finalizes;     // Timestamp offer finalizes
            uint8 status;       // 0: Undefined, 1: Created, 2: Accepted, 3: Disputed
        }
    
        Listing[] public listings;
        mapping(uint => Offer[]) public offers; // listingID => Offers
        mapping(address => bool) public allowedAffiliates;
    
        ERC20 public tokenAddr; // Origin Token address
    
        constructor(address _tokenAddr) public {
            owner = msg.sender;
            setTokenAddr(_tokenAddr); // Origin Token contract
            allowedAffiliates[0x0] = true; // Allow null affiliate by default
        }
    
        // @dev Return the total number of listings
        function totalListings() public view returns (uint) {
            return listings.length;
        }
    
        // @dev Return the total number of offers
        function totalOffers(uint listingID) public view returns (uint) {
            return offers[listingID].length;
        }
    
        // @dev Seller creates listing
        function createListing(bytes32 _ipfsHash, uint _deposit, address _depositManager)
            public
        {
            _createListing(msg.sender, _ipfsHash, _deposit, _depositManager);
        }
    
        // @dev Can only be called by token
        function createListingWithSender(
            address _seller,
            bytes32 _ipfsHash,
            uint _deposit,
            address _depositManager
        )
            public returns (bool)
        {
            require(msg.sender == address(tokenAddr), "Token must call");
            _createListing(_seller, _ipfsHash, _deposit, _depositManager);
            return true;
        }
    
        // Private
        function _createListing(
            address _seller,
            bytes32 _ipfsHash,  // IPFS JSON with details, pricing, availability
            uint _deposit,      // Deposit in Origin Token
            address _depositManager // Address of listing depositManager
        )
            private
        {
            /* require(_deposit > 0); // Listings must deposit some amount of Origin Token */
            require(_depositManager != 0x0, "Must specify depositManager");
    
            listings.push(Listing({
                seller: _seller,
                deposit: _deposit,
                depositManager: _depositManager
            }));
    
            if (_deposit > 0) {
                tokenAddr.transferFrom(_seller, this, _deposit); // Transfer Origin Token
            }
            emit ListingCreated(_seller, listings.length - 1, _ipfsHash);
        }
    
        // @dev Seller updates listing
        function updateListing(
            uint listingID,
            bytes32 _ipfsHash,
            uint _additionalDeposit
        ) public {
            _updateListing(msg.sender, listingID, _ipfsHash, _additionalDeposit);
        }
    
        function updateListingWithSender(
            address _seller,
            uint listingID,
            bytes32 _ipfsHash,
            uint _additionalDeposit
        )
            public returns (bool)
        {
            require(msg.sender == address(tokenAddr), "Token must call");
            _updateListing(_seller, listingID, _ipfsHash, _additionalDeposit);
            return true;
        }
    
        function _updateListing(
            address _seller,
            uint listingID,
            bytes32 _ipfsHash,      // Updated IPFS hash
            uint _additionalDeposit // Additional deposit to add
        ) private {
            Listing storage listing = listings[listingID];
            require(listing.seller == _seller, "Seller must call");
    
            if (_additionalDeposit > 0) {
                tokenAddr.transferFrom(_seller, this, _additionalDeposit);
                listing.deposit += _additionalDeposit;
            }
    
            emit ListingUpdated(listing.seller, listingID, _ipfsHash);
        }
    
        // @dev Listing depositManager withdraws listing. IPFS hash contains reason for withdrawl.
        function withdrawListing(uint listingID, address _target, bytes32 _ipfsHash) public {
            Listing storage listing = listings[listingID];
            require(msg.sender == listing.depositManager, "Must be depositManager");
            require(_target != 0x0, "No target");
            tokenAddr.transfer(_target, listing.deposit); // Send deposit to target
            emit ListingWithdrawn(_target, listingID, _ipfsHash);
        }
    
        // @dev Buyer makes offer.
        function makeOffer(
            uint listingID,
            bytes32 _ipfsHash,   // IPFS hash containing offer data
            uint _finalizes,     // Timestamp an accepted offer will finalize
            address _affiliate,  // Address to send any required commission to
            uint256 _commission, // Amount of commission to send in Origin Token if offer finalizes
            uint _value,         // Offer amount in ERC20 or Eth
            ERC20 _currency,     // ERC20 token address or 0x0 for Eth
            address _arbitrator  // Escrow arbitrator
        )
            public
            payable
        {
            bool affiliateWhitelistDisabled = allowedAffiliates[address(this)];
            require(
                affiliateWhitelistDisabled || allowedAffiliates[_affiliate],
                "Affiliate not allowed"
            );
    
            if (_affiliate == 0x0) {
                // Avoid commission tokens being trapped in marketplace contract.
                require(_commission == 0, "commission requires affiliate");
            }
    
            offers[listingID].push(Offer({
                status: 1,
                buyer: msg.sender,
                finalizes: _finalizes,
                affiliate: _affiliate,
                commission: _commission,
                currency: _currency,
                value: _value,
                arbitrator: _arbitrator,
                refund: 0
            }));
    
            if (address(_currency) == 0x0) { // Listing is in ETH
                require(msg.value == _value, "ETH value doesn't match offer");
            } else { // Listing is in ERC20
                require(msg.value == 0, "ETH would be lost");
                require(
                    _currency.transferFrom(msg.sender, this, _value),
                    "transferFrom failed"
                );
            }
    
            emit OfferCreated(msg.sender, listingID, offers[listingID].length-1, _ipfsHash);
        }
    
        // @dev Make new offer after withdrawl
        function makeOffer(
            uint listingID,
            bytes32 _ipfsHash,
            uint _finalizes,
            address _affiliate,
            uint256 _commission,
            uint _value,
            ERC20 _currency,
            address _arbitrator,
            uint _withdrawOfferID
        )
            public
            payable
        {
            withdrawOffer(listingID, _withdrawOfferID, _ipfsHash);
            makeOffer(listingID, _ipfsHash, _finalizes, _affiliate, _commission, _value, _currency, _arbitrator);
        }
    
        // @dev Seller accepts offer
        function acceptOffer(uint listingID, uint offerID, bytes32 _ipfsHash) public {
            Listing storage listing = listings[listingID];
            Offer storage offer = offers[listingID][offerID];
            require(msg.sender == listing.seller, "Seller must accept");
            require(offer.status == 1, "status != created");
            require(
                listing.deposit >= offer.commission,
                "deposit must cover commission"
            );
            if (offer.finalizes < 1000000000) { // Relative finalization window
                offer.finalizes = now + offer.finalizes;
            }
            listing.deposit -= offer.commission; // Accepting an offer puts Origin Token into escrow
            offer.status = 2; // Set offer to 'Accepted'
            emit OfferAccepted(msg.sender, listingID, offerID, _ipfsHash);
        }
    
        // @dev Buyer withdraws offer. IPFS hash contains reason for withdrawl.
        function withdrawOffer(uint listingID, uint offerID, bytes32 _ipfsHash) public {
            Listing storage listing = listings[listingID];
            Offer storage offer = offers[listingID][offerID];
            require(
                msg.sender == offer.buyer || msg.sender == listing.seller,
                "Restricted to buyer or seller"
            );
            require(offer.status == 1, "status != created");
            refundBuyer(listingID, offerID);
            emit OfferWithdrawn(msg.sender, listingID, offerID, _ipfsHash);
            delete offers[listingID][offerID];
        }
    
        // @dev Buyer adds extra funds to an accepted offer.
        function addFunds(uint listingID, uint offerID, bytes32 _ipfsHash, uint _value) public payable {
            Offer storage offer = offers[listingID][offerID];
            require(msg.sender == offer.buyer, "Buyer must call");
            require(offer.status == 2, "status != accepted");
            if (address(offer.currency) == 0x0) { // Listing is in ETH
                require(
                    msg.value == _value,
                    "sent != offered value"
                );
            } else { // Listing is in ERC20
                require(msg.value == 0, "ETH must not be sent");
                require(
                    offer.currency.transferFrom(msg.sender, this, _value),
                    "transferFrom failed"
                );
            }
            offer.value += _value;
            emit OfferFundsAdded(msg.sender, listingID, offerID, _ipfsHash);
        }
    
        // @dev Buyer must finalize transaction to receive commission
        function finalize(uint listingID, uint offerID, bytes32 _ipfsHash) public {
            Listing storage listing = listings[listingID];
            Offer storage offer = offers[listingID][offerID];
            if (now <= offer.finalizes) { // Only buyer can finalize before finalization window
                require(
                    msg.sender == offer.buyer,
                    "Only buyer can finalize"
                );
            } else { // Allow both seller and buyer to finalize if finalization window has passed
                require(
                    msg.sender == offer.buyer || msg.sender == listing.seller,
                    "Seller or buyer must finalize"
                );
            }
            require(offer.status == 2, "status != accepted");
            paySeller(listingID, offerID); // Pay seller
            if (msg.sender == offer.buyer) { // Only pay commission if buyer is finalizing
                payCommission(listingID, offerID);
            }
            emit OfferFinalized(msg.sender, listingID, offerID, _ipfsHash);
            delete offers[listingID][offerID];
        }
    
        // @dev Buyer or seller can dispute transaction during finalization window
        function dispute(uint listingID, uint offerID, bytes32 _ipfsHash) public {
            Listing storage listing = listings[listingID];
            Offer storage offer = offers[listingID][offerID];
            require(
                msg.sender == offer.buyer || msg.sender == listing.seller,
                "Must be seller or buyer"
            );
            require(offer.status == 2, "status != accepted");
            require(now <= offer.finalizes, "Already finalized");
            offer.status = 3; // Set status to "Disputed"
            emit OfferDisputed(msg.sender, listingID, offerID, _ipfsHash);
        }
    
        // @dev Called by arbitrator
        function executeRuling(
            uint listingID,
            uint offerID,
            bytes32 _ipfsHash,
            uint _ruling, // 0: Seller, 1: Buyer, 2: Com + Seller, 3: Com + Buyer
            uint _refund
        ) public {
            Offer storage offer = offers[listingID][offerID];
            require(msg.sender == offer.arbitrator, "Must be arbitrator");
            require(offer.status == 3, "status != disputed");
            require(_refund <= offer.value, "refund too high");
            offer.refund = _refund;
            if (_ruling & 1 == 1) {
                refundBuyer(listingID, offerID);
            } else  {
                paySeller(listingID, offerID);
            }
            if (_ruling & 2 == 2) {
                payCommission(listingID, offerID);
            } else  { // Refund commission to seller
                listings[listingID].deposit += offer.commission;
            }
            emit OfferRuling(offer.arbitrator, listingID, offerID, _ipfsHash, _ruling);
            delete offers[listingID][offerID];
        }
    
        // @dev Sets the amount that a seller wants to refund to a buyer.
        function updateRefund(uint listingID, uint offerID, uint _refund, bytes32 _ipfsHash) public {
            Offer storage offer = offers[listingID][offerID];
            Listing storage listing = listings[listingID];
            require(msg.sender == listing.seller, "Seller must call");
            require(offer.status == 2, "status != accepted");
            require(_refund <= offer.value, "Excessive refund");
            offer.refund = _refund;
            emit OfferData(msg.sender, listingID, offerID, _ipfsHash);
        }
    
        // @dev Refunds buyer in ETH or ERC20 - used by 1) executeRuling() and 2) to allow a seller to refund a purchase
        function refundBuyer(uint listingID, uint offerID) private {
            Offer storage offer = offers[listingID][offerID];
            if (address(offer.currency) == 0x0) {
                require(offer.buyer.send(offer.value), "ETH refund failed");
            } else {
                require(
                    offer.currency.transfer(offer.buyer, offer.value),
                    "Refund failed"
                );
            }
        }
    
        // @dev Pay seller in ETH or ERC20
        function paySeller(uint listingID, uint offerID) private {
            Listing storage listing = listings[listingID];
            Offer storage offer = offers[listingID][offerID];
            uint value = offer.value - offer.refund;
    
            if (address(offer.currency) == 0x0) {
                require(offer.buyer.send(offer.refund), "ETH refund failed");
                require(listing.seller.send(value), "ETH send failed");
            } else {
                require(
                    offer.currency.transfer(offer.buyer, offer.refund),
                    "Refund failed"
                );
                require(
                    offer.currency.transfer(listing.seller, value),
                    "Transfer failed"
                );
            }
        }
    
        // @dev Pay commission to affiliate
        function payCommission(uint listingID, uint offerID) private {
            Offer storage offer = offers[listingID][offerID];
            if (offer.affiliate != 0x0) {
                require(
                    tokenAddr.transfer(offer.affiliate, offer.commission),
                    "Commission transfer failed"
                );
            }
        }
    
        // @dev Associate ipfs data with the marketplace
        function addData(bytes32 ipfsHash) public {
            emit MarketplaceData(msg.sender, ipfsHash);
        }
    
        // @dev Associate ipfs data with a listing
        function addData(uint listingID, bytes32 ipfsHash) public {
            emit ListingData(msg.sender, listingID, ipfsHash);
        }
    
        // @dev Associate ipfs data with an offer
        function addData(uint listingID, uint offerID, bytes32 ipfsHash) public {
            emit OfferData(msg.sender, listingID, offerID, ipfsHash);
        }
    
        // @dev Allow listing depositManager to send deposit
        function sendDeposit(uint listingID, address target, uint value, bytes32 ipfsHash) public {
            Listing storage listing = listings[listingID];
            require(listing.depositManager == msg.sender, "depositManager must call");
            require(listing.deposit >= value, "Value too high");
            listing.deposit -= value;
            require(tokenAddr.transfer(target, value), "Transfer failed");
            emit ListingArbitrated(target, listingID, ipfsHash);
        }
    
        // @dev Set the address of the Origin token contract
        function setTokenAddr(address _tokenAddr) public onlyOwner {
            tokenAddr = ERC20(_tokenAddr);
        }
    
        // @dev Add affiliate to whitelist. Set to address(this) to disable.
        function addAffiliate(address _affiliate, bytes32 ipfsHash) public onlyOwner {
            allowedAffiliates[_affiliate] = true;
            emit AffiliateAdded(_affiliate, ipfsHash);
        }
    
        // @dev Remove affiliate from whitelist.
        function removeAffiliate(address _affiliate, bytes32 ipfsHash) public onlyOwner {
            delete allowedAffiliates[_affiliate];
            emit AffiliateRemoved(_affiliate, ipfsHash);
        }
    }

    File 2 of 2: OriginToken
    pragma solidity ^0.4.24;
    // produced by the Solididy File Flattener (c) David Appleton 2018
    // contact : [email protected]
    // released under Apache 2.0 licence
    contract ERC20Basic {
      function totalSupply() public view returns (uint256);
      function balanceOf(address who) public view returns (uint256);
      function transfer(address to, uint256 value) public returns (bool);
      event Transfer(address indexed from, address indexed to, uint256 value);
    }
    
    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.
       */
      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;
      }
    }
    
    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;
      }
    }
    
    contract ERC20 is ERC20Basic {
      function allowance(address owner, address spender)
        public view returns (uint256);
    
      function transferFrom(address from, address to, uint256 value)
        public returns (bool);
    
      function approve(address spender, uint256 value) public returns (bool);
      event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
      );
    }
    
    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;
        emit Pause();
      }
    
      /**
       * @dev called by the owner to unpause, returns to normal state
       */
      function unpause() onlyOwner whenPaused public {
        paused = false;
        emit Unpause();
      }
    }
    
    contract DetailedERC20 is ERC20 {
      string public name;
      string public symbol;
      uint8 public decimals;
    
      constructor(string _name, string _symbol, uint8 _decimals) public {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;
      }
    }
    
    contract BasicToken is ERC20Basic {
      using SafeMath for uint256;
    
      mapping(address => uint256) balances;
    
      uint256 totalSupply_;
    
      /**
      * @dev total number of tokens in existence
      */
      function totalSupply() public view returns (uint256) {
        return totalSupply_;
      }
    
      /**
      * @dev transfer token for a specified address
      * @param _to The address to transfer to.
      * @param _value The amount to be transferred.
      */
      function transfer(address _to, uint256 _value) public returns (bool) {
        require(_to != address(0));
        require(_value <= balances[msg.sender]);
    
        balances[msg.sender] = balances[msg.sender].sub(_value);
        balances[_to] = balances[_to].add(_value);
        emit Transfer(msg.sender, _to, _value);
        return true;
      }
    
      /**
      * @dev Gets the balance of the specified address.
      * @param _owner The address to query the the balance of.
      * @return An uint256 representing the amount owned by the passed address.
      */
      function balanceOf(address _owner) public view returns (uint256) {
        return balances[_owner];
      }
    
    }
    
    contract BurnableToken is BasicToken {
    
      event Burn(address indexed burner, uint256 value);
    
      /**
       * @dev Burns a specific amount of tokens.
       * @param _value The amount of token to be burned.
       */
      function burn(uint256 _value) public {
        _burn(msg.sender, _value);
      }
    
      function _burn(address _who, uint256 _value) internal {
        require(_value <= balances[_who]);
        // no need to require value <= totalSupply, since that would imply the
        // sender's balance is greater than the totalSupply, which *should* be an assertion failure
    
        balances[_who] = balances[_who].sub(_value);
        totalSupply_ = totalSupply_.sub(_value);
        emit Burn(_who, _value);
        emit Transfer(_who, address(0), _value);
      }
    }
    
    contract StandardToken is ERC20, BasicToken {
    
      mapping (address => mapping (address => uint256)) internal allowed;
    
    
      /**
       * @dev Transfer tokens from one address to another
       * @param _from address The address which you want to send tokens from
       * @param _to address The address which you want to transfer to
       * @param _value uint256 the amount of tokens to be transferred
       */
      function transferFrom(
        address _from,
        address _to,
        uint256 _value
      )
        public
        returns (bool)
      {
        require(_to != address(0));
        require(_value <= balances[_from]);
        require(_value <= allowed[_from][msg.sender]);
    
        balances[_from] = balances[_from].sub(_value);
        balances[_to] = balances[_to].add(_value);
        allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
        emit Transfer(_from, _to, _value);
        return true;
      }
    
      /**
       * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
       *
       * Beware that changing an allowance with this method brings the risk that someone may use both the old
       * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
       * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
       * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
       * @param _spender The address which will spend the funds.
       * @param _value The amount of tokens to be spent.
       */
      function approve(address _spender, uint256 _value) public returns (bool) {
        allowed[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
      }
    
      /**
       * @dev Function to check the amount of tokens that an owner allowed to a spender.
       * @param _owner address The address which owns the funds.
       * @param _spender address The address which will spend the funds.
       * @return A uint256 specifying the amount of tokens still available for the spender.
       */
      function allowance(
        address _owner,
        address _spender
       )
        public
        view
        returns (uint256)
      {
        return allowed[_owner][_spender];
      }
    
      /**
       * @dev Increase the amount of tokens that an owner allowed to a spender.
       *
       * approve should be called when allowed[_spender] == 0. To increment
       * allowed value is better to use this function to avoid 2 calls (and wait until
       * the first transaction is mined)
       * From MonolithDAO Token.sol
       * @param _spender The address which will spend the funds.
       * @param _addedValue The amount of tokens to increase the allowance by.
       */
      function increaseApproval(
        address _spender,
        uint _addedValue
      )
        public
        returns (bool)
      {
        allowed[msg.sender][_spender] = (
          allowed[msg.sender][_spender].add(_addedValue));
        emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
      }
    
      /**
       * @dev Decrease the amount of tokens that an owner allowed to a spender.
       *
       * approve should be called when allowed[_spender] == 0. To decrement
       * allowed value is better to use this function to avoid 2 calls (and wait until
       * the first transaction is mined)
       * From MonolithDAO Token.sol
       * @param _spender The address which will spend the funds.
       * @param _subtractedValue The amount of tokens to decrease the allowance by.
       */
      function decreaseApproval(
        address _spender,
        uint _subtractedValue
      )
        public
        returns (bool)
      {
        uint oldValue = allowed[msg.sender][_spender];
        if (_subtractedValue > oldValue) {
          allowed[msg.sender][_spender] = 0;
        } else {
          allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
        }
        emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
      }
    
    }
    
    contract PausableToken is StandardToken, Pausable {
    
      function transfer(
        address _to,
        uint256 _value
      )
        public
        whenNotPaused
        returns (bool)
      {
        return super.transfer(_to, _value);
      }
    
      function transferFrom(
        address _from,
        address _to,
        uint256 _value
      )
        public
        whenNotPaused
        returns (bool)
      {
        return super.transferFrom(_from, _to, _value);
      }
    
      function approve(
        address _spender,
        uint256 _value
      )
        public
        whenNotPaused
        returns (bool)
      {
        return super.approve(_spender, _value);
      }
    
      function increaseApproval(
        address _spender,
        uint _addedValue
      )
        public
        whenNotPaused
        returns (bool success)
      {
        return super.increaseApproval(_spender, _addedValue);
      }
    
      function decreaseApproval(
        address _spender,
        uint _subtractedValue
      )
        public
        whenNotPaused
        returns (bool success)
      {
        return super.decreaseApproval(_spender, _subtractedValue);
      }
    }
    
    contract MintableToken is StandardToken, Ownable {
      event Mint(address indexed to, uint256 amount);
      event MintFinished();
    
      bool public mintingFinished = false;
    
    
      modifier canMint() {
        require(!mintingFinished);
        _;
      }
    
      modifier hasMintPermission() {
        require(msg.sender == owner);
        _;
      }
    
      /**
       * @dev Function to mint tokens
       * @param _to The address that will receive the minted tokens.
       * @param _amount The amount of tokens to mint.
       * @return A boolean that indicates if the operation was successful.
       */
      function mint(
        address _to,
        uint256 _amount
      )
        hasMintPermission
        canMint
        public
        returns (bool)
      {
        totalSupply_ = totalSupply_.add(_amount);
        balances[_to] = balances[_to].add(_amount);
        emit Mint(_to, _amount);
        emit Transfer(address(0), _to, _amount);
        return true;
      }
    
      /**
       * @dev Function to stop minting new tokens.
       * @return True if the operation was successful.
       */
      function finishMinting() onlyOwner canMint public returns (bool) {
        mintingFinished = true;
        emit MintFinished();
        return true;
      }
    }
    
    contract WhitelistedPausableToken is PausableToken {
        // UNIX timestamp (in seconds) after which this whitelist no longer applies
        uint256 public whitelistExpiration;
        // While the whitelist is active, either the sender or recipient must be
        // in allowedTransactors.
        mapping (address => bool) public allowedTransactors;
    
        event SetWhitelistExpiration(uint256 expiration);
        event AllowedTransactorAdded(address sender);
        event AllowedTransactorRemoved(address sender);
    
        //
        // Functions for maintaining whitelist
        //
    
        modifier allowedTransfer(address _from, address _to) {
            require(
                // solium-disable-next-line operator-whitespace
                !whitelistActive() ||
                allowedTransactors[_from] ||
                allowedTransactors[_to],
                "neither sender nor recipient are allowed"
            );
            _;
        }
    
        function whitelistActive() public view returns (bool) {
            return block.timestamp < whitelistExpiration;
        }
    
        function addAllowedTransactor(address _transactor) public onlyOwner {
            emit AllowedTransactorAdded(_transactor);
            allowedTransactors[_transactor] = true;
        }
    
        function removeAllowedTransactor(address _transactor) public onlyOwner {
            emit AllowedTransactorRemoved(_transactor);
            delete allowedTransactors[_transactor];
        }
    
        /**
        * @dev Set the whitelist expiration, after which the whitelist no longer
        * applies.
        */
        function setWhitelistExpiration(uint256 _expiration) public onlyOwner {
            // allow only if whitelist expiration hasn't yet been set, or if the
            // whitelist expiration hasn't passed yet
            require(
                whitelistExpiration == 0 || whitelistActive(),
                "an expired whitelist cannot be extended"
            );
            // prevent possible mistakes in calling this function
            require(
                _expiration >= block.timestamp + 1 days,
                "whitelist expiration not far enough into the future"
            );
            emit SetWhitelistExpiration(_expiration);
            whitelistExpiration = _expiration;
        }
    
        //
        // ERC20 transfer functions that have been overridden to enforce the
        // whitelist.
        //
    
        function transfer(
            address _to,
            uint256 _value
        )
            public
            allowedTransfer(msg.sender, _to)
            returns (bool)
        {
            return super.transfer(_to, _value);
        }
    
        function transferFrom(
            address _from,
            address _to,
            uint256 _value
        )
        public
            allowedTransfer(_from, _to)
        returns (bool)
        {
            return super.transferFrom(_from, _to, _value);
        }
    }
    
    contract OriginToken is BurnableToken, MintableToken, WhitelistedPausableToken, DetailedERC20 {
        event AddCallSpenderWhitelist(address enabler, address spender);
        event RemoveCallSpenderWhitelist(address disabler, address spender);
    
        mapping (address => bool) public callSpenderWhitelist;
    
        // @dev Constructor that gives msg.sender all initial tokens.
        constructor(uint256 _initialSupply) DetailedERC20("OriginToken", "OGN", 18) public {
            owner = msg.sender;
            mint(owner, _initialSupply);
        }
    
        //
        // Burn methods
        //
    
        // @dev Burns tokens belonging to the sender
        // @param _value Amount of token to be burned
        function burn(uint256 _value) public onlyOwner {
            // TODO: add a function & modifier to enable for all accounts without doing
            // a contract migration?
            super.burn(_value);
        }
    
        // @dev Burns tokens belonging to the specified address
        // @param _who The account whose tokens we're burning
        // @param _value Amount of token to be burned
        function burn(address _who, uint256 _value) public onlyOwner {
            _burn(_who, _value);
        }
    
        //
        // approveAndCall methods
        //
    
        // @dev Add spender to whitelist of spenders for approveAndCall
        // @param _spender Address to add
        function addCallSpenderWhitelist(address _spender) public onlyOwner {
            callSpenderWhitelist[_spender] = true;
            emit AddCallSpenderWhitelist(msg.sender, _spender);
        }
    
        // @dev Remove spender from whitelist of spenders for approveAndCall
        // @param _spender Address to remove
        function removeCallSpenderWhitelist(address _spender) public onlyOwner {
            delete callSpenderWhitelist[_spender];
            emit RemoveCallSpenderWhitelist(msg.sender, _spender);
        }
    
        // @dev Approve transfer of tokens and make a contract call in a single
        // @dev transaction. This allows a DApp to avoid requiring two MetaMask
        // @dev approvals for a single logical action, such as creating a listing,
        // @dev which requires the seller to approve a token transfer and the
        // @dev marketplace contract to transfer tokens from the seller.
        //
        // @dev This is based on the ERC827 function approveAndCall and avoids
        // @dev security issues by only working with a whitelisted set of _spender
        // @dev addresses. The other difference is that the combination of this
        // @dev function ensures that the proxied function call receives the
        // @dev msg.sender for this function as its first parameter.
        //
        // @param _spender The address that will spend the funds.
        // @param _value The amount of tokens to be spent.
        // @param _selector Function selector for function to be called.
        // @param _callParams Packed, encoded parameters, omitting the first parameter which is always msg.sender
        function approveAndCallWithSender(
            address _spender,
            uint256 _value,
            bytes4 _selector,
            bytes _callParams
        )
            public
            payable
            returns (bool)
        {
            require(_spender != address(this), "token contract can't be approved");
            require(callSpenderWhitelist[_spender], "spender not in whitelist");
    
            require(super.approve(_spender, _value), "approve failed");
    
            bytes memory callData = abi.encodePacked(_selector, uint256(msg.sender), _callParams);
            // solium-disable-next-line security/no-call-value
            require(_spender.call.value(msg.value)(callData), "proxied call failed");
            return true;
        }
    }