ETH Price: $2,532.39 (+0.36%)

Transaction Decoder

Block:
8627313 at Sep-26-2019 10:53:56 PM +UTC
Transaction Fee:
0.0007869904 ETH $1.99
Gas Used:
70,267 Gas / 11.2 Gwei

Emitted Events:

64 OriginToken.Approval( owner=[Sender] 0x343a3c7f789335c9ea60932d34be258f643678d9, spender=V00_Marketplace, value=2000000000000000000 )
65 OriginToken.Transfer( from=[Sender] 0x343a3c7f789335c9ea60932d34be258f643678d9, to=V00_Marketplace, value=2000000000000000000 )
66 V00_Marketplace.ListingUpdated( party=[Sender] 0x343a3c7f789335c9ea60932d34be258f643678d9, listingID=2794, ipfsHash=7382CEE1DB153B9E21A778E1343BEC3A5A1FBCB9D802E24A1C8968AD52A8039D )

Account State Difference:

  Address   Before After State Difference Code
0x343a3C7F...F643678d9
0.012121290681576316 Eth
Nonce: 277
0.011334300281576316 Eth
Nonce: 278
0.0007869904
0x819Bb996...B921510f9
(Origin: Marketplace v0)
0x8207c1Ff...c3541Ae26
(F2Pool Old)
2,281.093542539375516752 Eth2,281.094329529775516752 Eth0.0007869904

Execution Trace

OriginToken.approveAndCallWithSender( _spender=0x819Bb9964B6eBF52361F1ae42CF4831B921510f9, _value=2000000000000000000, _selector=System.Byte[], _callParams=0x0000000000000000000000000000000000000000000000000000000000000AEA7382CEE1DB153B9E21A778E1343BEC3A5A1FBCB9D802E24A1C8968AD52A8039D0000000000000000000000000000000000000000000000001BC16D674EC80000 ) => ( True )
  • V00_Marketplace.updateListingWithSender( _seller=0x343a3C7F789335C9EA60932D34bE258F643678d9, listingID=2794, _ipfsHash=7382CEE1DB153B9E21A778E1343BEC3A5A1FBCB9D802E24A1C8968AD52A8039D, _additionalDeposit=2000000000000000000 ) => ( True )
    • OriginToken.transferFrom( _from=0x343a3C7F789335C9EA60932D34bE258F643678d9, _to=0x819Bb9964B6eBF52361F1ae42CF4831B921510f9, _value=2000000000000000000 ) => ( True )
      File 1 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;
          }
      }

      File 2 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);
          }
      }