pragma solidity ^0.4.24;
/*
* Origin Protocol
* https://originprotocol.com
*
* Released under the MIT license
* https://github.com/OriginProtocol
*
* Copyright 2019 Origin Protocol, Inc
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
event OwnershipRenounced(address indexed previousOwner);
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* @notice Renouncing to ownership will leave the contract without an owner.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipRenounced(owner);
owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param _newOwner The address to transfer ownership to.
*/
function transferOwnership(address _newOwner) public onlyOwner {
_transferOwnership(_newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param _newOwner The address to transfer ownership to.
*/
function _transferOwnership(address _newOwner) internal {
require(_newOwner != address(0));
emit OwnershipTransferred(owner, _newOwner);
owner = _newOwner;
}
}
contract ERC20 {
function transfer(address _to, uint256 _value) external returns (bool);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool);
}
/**
* @title A Marketplace contract for managing listings, offers, payments, escrow and arbitration
*
* Listings may be priced in ETH or ERC20.
*/
contract V01_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) {
require(
tokenAddr.transferFrom(_seller, this, _deposit), // Transfer Origin Token
"transferFrom failed"
);
}
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) {
listing.deposit += _additionalDeposit;
require(
tokenAddr.transferFrom(_seller, this, _additionalDeposit),
"transferFrom failed"
);
}
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");
uint deposit = listing.deposit;
listing.deposit = 0; // Prevent multiple deposit withdrawals
require(tokenAddr.transfer(_target, deposit), "transfer failed"); // 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 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 memory offer = offers[listingID][offerID];
require(
msg.sender == offer.buyer || msg.sender == listing.seller,
"Restricted to buyer or seller"
);
require(offer.status == 1, "status != created");
delete offers[listingID][offerID];
refundBuyer(offer.buyer, offer.currency, offer.value);
emit OfferWithdrawn(msg.sender, listingID, offerID, _ipfsHash);
}
// @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");
offer.value += _value;
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"
);
}
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 memory 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");
delete offers[listingID][offerID];
if (msg.sender != offer.buyer) {
listing.deposit += offer.commission; // Refund commission to seller
} else {
// Only pay commission if buyer is finalizing
payCommission(offer.affiliate, offer.commission);
}
paySeller(listing.seller, offer.buyer, offer.currency, offer.value, offer.refund); // Pay seller
emit OfferFinalized(msg.sender, listingID, offerID, _ipfsHash);
}
// @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 {
Listing storage listing = listings[listingID];
Offer memory 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");
delete offers[listingID][offerID];
if (_ruling & 2 == 2) {
payCommission(offer.affiliate, offer.commission);
} else { // Refund commission to seller
listings[listingID].deposit += offer.commission;
}
if (_ruling & 1 == 1) {
refundBuyer(offer.buyer, offer.currency, offer.value);
} else {
paySeller(listing.seller, offer.buyer, offer.currency, offer.value, _refund); // Pay seller
}
emit OfferRuling(offer.arbitrator, listingID, offerID, _ipfsHash, _ruling);
}
// @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(address buyer, ERC20 currency, uint value) private {
if (address(currency) == 0x0) {
require(buyer.send(value), "ETH refund failed");
} else {
require(
currency.transfer(buyer, value),
"Refund failed"
);
}
}
// @dev Pay seller in ETH or ERC20
function paySeller(address seller, address buyer, ERC20 currency, uint offerValue, uint offerRefund) private {
uint value = offerValue - offerRefund;
if (address(currency) == 0x0) {
require(buyer.send(offerRefund), "ETH refund failed");
require(seller.send(value), "ETH send failed");
} else {
require(
currency.transfer(buyer, offerRefund),
"Refund failed"
);
require(
currency.transfer(seller, value),
"Transfer failed"
);
}
}
// @dev Pay commission to affiliate
function payCommission(address affiliate, uint commission) private {
if (affiliate != 0x0) {
require(
tokenAddr.transfer(affiliate, 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);
}
}
{
"compilationTarget": {
"V01_Marketplace.sol": "V01_Marketplace"
},
"evmVersion": "byzantium",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":false,"inputs":[{"name":"listingID","type":"uint256"},{"name":"offerID","type":"uint256"},{"name":"_refund","type":"uint256"},{"name":"_ipfsHash","type":"bytes32"}],"name":"updateRefund","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"ipfsHash","type":"bytes32"}],"name":"addData","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_tokenAddr","type":"address"}],"name":"setTokenAddr","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"name":"offers","outputs":[{"name":"value","type":"uint256"},{"name":"commission","type":"uint256"},{"name":"refund","type":"uint256"},{"name":"currency","type":"address"},{"name":"buyer","type":"address"},{"name":"affiliate","type":"address"},{"name":"arbitrator","type":"address"},{"name":"finalizes","type":"uint256"},{"name":"status","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"listingID","type":"uint256"},{"name":"_ipfsHash","type":"bytes32"},{"name":"_finalizes","type":"uint256"},{"name":"_affiliate","type":"address"},{"name":"_commission","type":"uint256"},{"name":"_value","type":"uint256"},{"name":"_currency","type":"address"},{"name":"_arbitrator","type":"address"}],"name":"makeOffer","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"listingID","type":"uint256"},{"name":"target","type":"address"},{"name":"value","type":"uint256"},{"name":"ipfsHash","type":"bytes32"}],"name":"sendDeposit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"tokenAddr","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"listingID","type":"uint256"},{"name":"offerID","type":"uint256"},{"name":"_ipfsHash","type":"bytes32"}],"name":"dispute","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_affiliate","type":"address"},{"name":"ipfsHash","type":"bytes32"}],"name":"addAffiliate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"listingID","type":"uint256"},{"name":"offerID","type":"uint256"},{"name":"_ipfsHash","type":"bytes32"},{"name":"_value","type":"uint256"}],"name":"addFunds","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"listingID","type":"uint256"}],"name":"totalOffers","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"allowedAffiliates","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"listingID","type":"uint256"},{"name":"ipfsHash","type":"bytes32"}],"name":"addData","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"listingID","type":"uint256"},{"name":"offerID","type":"uint256"},{"name":"_ipfsHash","type":"bytes32"}],"name":"acceptOffer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_affiliate","type":"address"},{"name":"ipfsHash","type":"bytes32"}],"name":"removeAffiliate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"listingID","type":"uint256"},{"name":"_target","type":"address"},{"name":"_ipfsHash","type":"bytes32"}],"name":"withdrawListing","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"listingID","type":"uint256"},{"name":"_ipfsHash","type":"bytes32"},{"name":"_additionalDeposit","type":"uint256"}],"name":"updateListing","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"listingID","type":"uint256"},{"name":"offerID","type":"uint256"},{"name":"_ipfsHash","type":"bytes32"},{"name":"_ruling","type":"uint256"},{"name":"_refund","type":"uint256"}],"name":"executeRuling","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalListings","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_ipfsHash","type":"bytes32"},{"name":"_deposit","type":"uint256"},{"name":"_depositManager","type":"address"}],"name":"createListing","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_seller","type":"address"},{"name":"listingID","type":"uint256"},{"name":"_ipfsHash","type":"bytes32"},{"name":"_additionalDeposit","type":"uint256"}],"name":"updateListingWithSender","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"listingID","type":"uint256"},{"name":"offerID","type":"uint256"},{"name":"ipfsHash","type":"bytes32"}],"name":"addData","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_seller","type":"address"},{"name":"_ipfsHash","type":"bytes32"},{"name":"_deposit","type":"uint256"},{"name":"_depositManager","type":"address"}],"name":"createListingWithSender","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"listings","outputs":[{"name":"seller","type":"address"},{"name":"deposit","type":"uint256"},{"name":"depositManager","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"listingID","type":"uint256"},{"name":"offerID","type":"uint256"},{"name":"_ipfsHash","type":"bytes32"}],"name":"withdrawOffer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"listingID","type":"uint256"},{"name":"offerID","type":"uint256"},{"name":"_ipfsHash","type":"bytes32"}],"name":"finalize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_tokenAddr","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"party","type":"address"},{"indexed":false,"name":"ipfsHash","type":"bytes32"}],"name":"MarketplaceData","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"party","type":"address"},{"indexed":false,"name":"ipfsHash","type":"bytes32"}],"name":"AffiliateAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"party","type":"address"},{"indexed":false,"name":"ipfsHash","type":"bytes32"}],"name":"AffiliateRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"party","type":"address"},{"indexed":true,"name":"listingID","type":"uint256"},{"indexed":false,"name":"ipfsHash","type":"bytes32"}],"name":"ListingCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"party","type":"address"},{"indexed":true,"name":"listingID","type":"uint256"},{"indexed":false,"name":"ipfsHash","type":"bytes32"}],"name":"ListingUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"party","type":"address"},{"indexed":true,"name":"listingID","type":"uint256"},{"indexed":false,"name":"ipfsHash","type":"bytes32"}],"name":"ListingWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"party","type":"address"},{"indexed":true,"name":"listingID","type":"uint256"},{"indexed":false,"name":"ipfsHash","type":"bytes32"}],"name":"ListingArbitrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"party","type":"address"},{"indexed":true,"name":"listingID","type":"uint256"},{"indexed":false,"name":"ipfsHash","type":"bytes32"}],"name":"ListingData","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"party","type":"address"},{"indexed":true,"name":"listingID","type":"uint256"},{"indexed":true,"name":"offerID","type":"uint256"},{"indexed":false,"name":"ipfsHash","type":"bytes32"}],"name":"OfferCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"party","type":"address"},{"indexed":true,"name":"listingID","type":"uint256"},{"indexed":true,"name":"offerID","type":"uint256"},{"indexed":false,"name":"ipfsHash","type":"bytes32"}],"name":"OfferAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"party","type":"address"},{"indexed":true,"name":"listingID","type":"uint256"},{"indexed":true,"name":"offerID","type":"uint256"},{"indexed":false,"name":"ipfsHash","type":"bytes32"}],"name":"OfferFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"party","type":"address"},{"indexed":true,"name":"listingID","type":"uint256"},{"indexed":true,"name":"offerID","type":"uint256"},{"indexed":false,"name":"ipfsHash","type":"bytes32"}],"name":"OfferWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"party","type":"address"},{"indexed":true,"name":"listingID","type":"uint256"},{"indexed":true,"name":"offerID","type":"uint256"},{"indexed":false,"name":"ipfsHash","type":"bytes32"}],"name":"OfferFundsAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"party","type":"address"},{"indexed":true,"name":"listingID","type":"uint256"},{"indexed":true,"name":"offerID","type":"uint256"},{"indexed":false,"name":"ipfsHash","type":"bytes32"}],"name":"OfferDisputed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"party","type":"address"},{"indexed":true,"name":"listingID","type":"uint256"},{"indexed":true,"name":"offerID","type":"uint256"},{"indexed":false,"name":"ipfsHash","type":"bytes32"},{"indexed":false,"name":"ruling","type":"uint256"}],"name":"OfferRuling","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"party","type":"address"},{"indexed":true,"name":"listingID","type":"uint256"},{"indexed":true,"name":"offerID","type":"uint256"},{"indexed":false,"name":"ipfsHash","type":"bytes32"}],"name":"OfferData","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"}],"name":"OwnershipRenounced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}]