pragma solidity ^0.4.18;
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 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 c;
}
/**
* @dev Substracts 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) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
/**
* @title Pausable
* @dev Base contract which allows children to implement an emergency stop mechanism.
*/
contract Pausable is Ownable {
event Pause();
event Unpause();
bool public paused = false;
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*/
modifier whenNotPaused() {
require(!paused);
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*/
modifier whenPaused() {
require(paused);
_;
}
/**
* @dev called by the owner to pause, triggers stopped state
*/
function pause() onlyOwner whenNotPaused public {
paused = true;
Pause();
}
/**
* @dev called by the owner to unpause, returns to normal state
*/
function unpause() onlyOwner whenPaused public {
paused = false;
Unpause();
}
}
/**
* @title ERC721 interface
* @dev see https://github.com/ethereum/eips/issues/721
*/
contract ERC721 {
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
function balanceOf(address _owner) public view returns (uint256 _balance);
function ownerOf(uint256 _tokenId) public view returns (address _owner);
function transfer(address _to, uint256 _tokenId) public;
function approve(address _to, uint256 _tokenId) public;
function takeOwnership(uint256 _tokenId) public;
}
/// @title CryptoCelebritiesMarket
/// @dev Contains models, variables, and internal methods for sales.
contract CelebrityMarket is Pausable{
ERC721 ccContract;
// Represents a sale item on an NFT
struct Sale {
// Current owner of NFT
address seller;
// Price (in wei) at beginning of a sale item
uint256 salePrice;
// Time when sale started
// NOTE: 0 if this sale has been concluded
uint64 startedAt;
}
// Owner of this contract
address public owner;
// Map from token ID to their corresponding sale.
mapping (uint256 => Sale) tokenIdToSale;
event SaleCreated(address seller,uint256 tokenId, uint256 salePrice, uint256 startedAt);
event SaleSuccessful(address seller, uint256 tokenId, uint256 totalPrice, address winner);
event SaleCancelled(address seller, uint256 tokenId);
event SaleUpdated(address seller, uint256 tokenId, uint256 oldPrice, uint256 newPrice);
/// @dev Constructor registers the nft address (CCAddress)
/// @param _ccAddress - Address of the CryptoCelebrities contract
function CelebrityMarket(address _ccAddress) public {
ccContract = ERC721(_ccAddress);
owner = msg.sender;
}
/// @dev DON'T give me your money.
function() external {}
/// @dev Remove all Ether from the contract, which is the owner's cuts
/// as well as any Ether sent directly to the contract address.
/// Always transfers to the NFT contract, but can be called either by
/// the owner or the NFT contract.
function withdrawBalance() external {
require(
msg.sender == owner
);
msg.sender.transfer(address(this).balance);
}
/// @dev Creates and begins a new sale.
/// @param _tokenId - ID of token to sell, sender must be owner.
/// @param _salePrice - Sale Price of item (in wei).
function createSale(
uint256 _tokenId,
uint256 _salePrice
)
public
whenNotPaused
{
require(_owns(msg.sender, _tokenId));
_escrow(_tokenId);
Sale memory sale = Sale(
msg.sender,
_salePrice,
uint64(now)
);
_addSale(_tokenId, sale);
}
/// @dev Update sale price of a sale item that hasn't been completed yet.
/// @notice This is a state-modifying function that can
/// be called while the contract is paused.
/// @param _tokenId - ID of token on sale
/// @param _newPrice - new sale price
function updateSalePrice(uint256 _tokenId, uint256 _newPrice)
public
{
Sale storage sale = tokenIdToSale[_tokenId];
require(_isOnSale(sale));
address seller = sale.seller;
require(msg.sender == seller);
_updateSalePrice(_tokenId, _newPrice, seller);
}
/// @dev Allows to buy a sale item, completing the sale and transferring
/// ownership of the NFT if enough Ether is supplied.
/// @param _tokenId - ID of token to buy.
function buy(uint256 _tokenId)
public
payable
whenNotPaused
{
// _bid will throw if the bid or funds transfer fails
_buy(_tokenId, msg.value);
_transfer(msg.sender, _tokenId);
}
/// @dev Cancels a sale that hasn't been completed yet.
/// Returns the NFT to original owner.
/// @notice This is a state-modifying function that can
/// be called while the contract is paused.
/// @param _tokenId - ID of token on sale
function cancelSale(uint256 _tokenId)
public
{
Sale storage sale = tokenIdToSale[_tokenId];
require(_isOnSale(sale));
address seller = sale.seller;
require(msg.sender == seller);
_cancelSale(_tokenId, seller);
}
/// @dev Cancels a sale when the contract is paused.
/// Only the owner may do this, and NFTs are returned to
/// the seller. This should only be used in emergencies.
/// @param _tokenId - ID of the NFT on sale to cancel.
function cancelSaleWhenPaused(uint256 _tokenId)
whenPaused
onlyOwner
public
{
Sale storage sale = tokenIdToSale[_tokenId];
require(_isOnSale(sale));
_cancelSale(_tokenId, sale.seller);
}
/// @dev Returns sale info for an NFT on sale.
/// @param _tokenId - ID of NFT on sale.
function getSale(uint256 _tokenId)
public
view
returns
(
address seller,
uint256 salePrice,
uint256 startedAt
) {
Sale storage sale = tokenIdToSale[_tokenId];
require(_isOnSale(sale));
return (
sale.seller,
sale.salePrice,
sale.startedAt
);
}
/// @dev Returns the current price of a sale item.
/// @param _tokenId - ID of the token price we are checking.
function getSalePrice(uint256 _tokenId)
public
view
returns (uint256)
{
Sale storage sale = tokenIdToSale[_tokenId];
require(_isOnSale(sale));
return sale.salePrice;
}
/// @dev Returns true if the claimant owns the token.
/// @param _claimant - Address claiming to own the token.
/// @param _tokenId - ID of token whose ownership to verify.
function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) {
return (ccContract.ownerOf(_tokenId) == _claimant);
}
/// @dev Escrows the CCToken, assigning ownership to this contract.
/// Throws if the escrow fails.
/// @param _tokenId - ID of token whose approval to verify.
function _escrow(uint256 _tokenId) internal {
// it will throw if transfer fails
ccContract.takeOwnership(_tokenId);
}
/// @dev Transfers a CCToken owned by this contract to another address.
/// Returns true if the transfer succeeds.
/// @param _receiver - Address to transfer NFT to.
/// @param _tokenId - ID of token to transfer.
function _transfer(address _receiver, uint256 _tokenId) internal {
// it will throw if transfer fails
ccContract.transfer(_receiver, _tokenId);
}
/// @dev Adds a sale to the list of open sales. Also fires the
/// SaleCreated event.
/// @param _tokenId The ID of the token to be put on sale.
/// @param _sale Sale to add.
function _addSale(uint256 _tokenId, Sale _sale) internal {
tokenIdToSale[_tokenId] = _sale;
SaleCreated(
address(_sale.seller),
uint256(_tokenId),
uint256(_sale.salePrice),
uint256(_sale.startedAt)
);
}
/// @dev Cancels a sale unconditionally.
function _cancelSale(uint256 _tokenId, address _seller) internal {
_removeSale(_tokenId);
_transfer(_seller, _tokenId);
SaleCancelled(_seller, _tokenId);
}
/// @dev updates sale price of item
function _updateSalePrice(uint256 _tokenId, uint256 _newPrice, address _seller) internal {
// Get a reference to the sale struct
Sale storage sale = tokenIdToSale[_tokenId];
uint256 oldPrice = sale.salePrice;
sale.salePrice = _newPrice;
SaleUpdated(_seller, _tokenId, oldPrice, _newPrice);
}
/// @dev Computes the price and transfers winnings.
/// Does NOT transfer ownership of token.
function _buy(uint256 _tokenId, uint256 _amount)
internal
returns (uint256)
{
// Get a reference to the sale struct
Sale storage sale = tokenIdToSale[_tokenId];
// Explicitly check that this sale is currently live.
// (Because of how Ethereum mappings work, we can't just count
// on the lookup above failing. An invalid _tokenId will just
// return an sale object that is all zeros.)
require(_isOnSale(sale));
// Check that the incoming bid is higher than the current
// price
uint256 price = sale.salePrice;
require(_amount >= price);
// Grab a reference to the seller before the sale struct
// gets deleted.
address seller = sale.seller;
// The bid is good! Remove the sale before sending the fees
// to the sender so we can't have a reentrancy attack.
_removeSale(_tokenId);
// Transfer proceeds to seller (if there are any!)
if (price > 0) {
// Calculate the market owner's cut.
// (NOTE: _computeCut() is guaranteed to return a
// value <= price, so this subtraction can't go negative.)
uint256 ownerCut = _computeCut(price);
uint256 sellerProceeds = price - ownerCut;
// NOTE: Doing a transfer() in the middle of a complex
// method like this is generally discouraged because of
// reentrancy attacks and DoS attacks if the seller is
// a contract with an invalid fallback function. We explicitly
// guard against reentrancy attacks by removing the sale item
// before calling transfer(), and the only thing the seller
// can DoS is the sale of their own asset! (And if it's an
// accident, they can call cancelSale(). )
seller.transfer(sellerProceeds);
}
// Calculate any excess funds included with the bid. If the excess
// is anything worth worrying about, transfer it back to bidder.
// NOTE: We checked above that the bid amount is greater than or
// equal to the price so this cannot underflow.
uint256 amountExcess = _amount - price;
// Return the funds. Similar to the previous transfer, this is
// not susceptible to a re-entry attack because the sale is
// removed before any transfers occur.
msg.sender.transfer(amountExcess);
// Tell the world!
SaleSuccessful(seller, _tokenId, price, msg.sender);
return price;
}
/// @dev Removes a sale item from the list of open sales.
/// @param _tokenId - ID of NFT on sale.
function _removeSale(uint256 _tokenId) internal {
delete tokenIdToSale[_tokenId];
}
/// @dev Returns true if the NFT is on sale.
/// @param _sale - Sale to check.
function _isOnSale(Sale storage _sale) internal view returns (bool) {
return (_sale.startedAt > 0);
}
/// @dev Computes owner's cut of a sale.
/// @param _price - Sale price of NFT.
function _computeCut(uint256 _price) internal pure returns (uint256) {
return uint256(SafeMath.div(SafeMath.mul(_price, 6), 100));
}
}
{
"compilationTarget": {
"CelebrityMarket.sol": "CelebrityMarket"
},
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":false,"inputs":[{"name":"_tokenId","type":"uint256"}],"name":"cancelSaleWhenPaused","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_tokenId","type":"uint256"},{"name":"_newPrice","type":"uint256"}],"name":"updateSalePrice","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"withdrawBalance","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_tokenId","type":"uint256"},{"name":"_salePrice","type":"uint256"}],"name":"createSale","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_tokenId","type":"uint256"}],"name":"cancelSale","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_tokenId","type":"uint256"}],"name":"getSale","outputs":[{"name":"seller","type":"address"},{"name":"salePrice","type":"uint256"},{"name":"startedAt","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_tokenId","type":"uint256"}],"name":"buy","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_tokenId","type":"uint256"}],"name":"getSalePrice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_ccAddress","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":false,"stateMutability":"nonpayable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"seller","type":"address"},{"indexed":false,"name":"tokenId","type":"uint256"},{"indexed":false,"name":"salePrice","type":"uint256"},{"indexed":false,"name":"startedAt","type":"uint256"}],"name":"SaleCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"seller","type":"address"},{"indexed":false,"name":"tokenId","type":"uint256"},{"indexed":false,"name":"totalPrice","type":"uint256"},{"indexed":false,"name":"winner","type":"address"}],"name":"SaleSuccessful","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"seller","type":"address"},{"indexed":false,"name":"tokenId","type":"uint256"}],"name":"SaleCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"seller","type":"address"},{"indexed":false,"name":"tokenId","type":"uint256"},{"indexed":false,"name":"oldPrice","type":"uint256"},{"indexed":false,"name":"newPrice","type":"uint256"}],"name":"SaleUpdated","type":"event"},{"anonymous":false,"inputs":[],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}]