pragma solidity 0.4.24;
/**
* @title ERC-721 Non-Fungible Token Standard
* @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contract ERC721 {
// Required methods
function totalSupply() public view returns (uint256 total);
function balanceOf(address _owner) public view returns (uint256 balance);
function ownerOf(uint256 _tokenId) external view returns (address owner);
function approve(address _to, uint256 _tokenId) external;
function transfer(address _to, uint256 _tokenId) external;
function transferFrom(address _from, address _to, uint256 _tokenId) external;
// Events
event Transfer(address from, address to, uint256 tokenId);
event Approval(address owner, address approved, uint256 tokenId);
}
contract AccessControl {
event ContractUpgrade(address newContract);
address public addressDev;
address public addressFin;
address public addressOps;
modifier onlyDeveloper() {
require(msg.sender == addressDev);
_;
}
modifier onlyFinance() {
require(msg.sender == addressFin);
_;
}
modifier onlyOperation() {
require(msg.sender == addressOps);
_;
}
modifier onlyTeamMembers() {
require(
msg.sender == addressDev ||
msg.sender == addressFin ||
msg.sender == addressOps
);
_;
}
function setDeveloper(address _newDeveloper) external onlyDeveloper {
require(_newDeveloper != address(0));
addressDev = _newDeveloper;
}
function setFinance(address _newFinance) external onlyDeveloper {
require(_newFinance != address(0));
addressFin = _newFinance;
}
function setOperation(address _newOperation) external onlyDeveloper {
require(_newOperation != address(0));
addressOps = _newOperation;
}
}
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function Ownable() public {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
contract Pausable is Ownable {
event Pause();
event Unpause();
bool public paused = false;
modifier whenNotPaused() {
require(!paused);
_;
}
modifier whenPaused() {
require(paused);
_;
}
function pause() onlyOwner whenNotPaused public {
paused = true;
Pause();
}
function unpause() onlyOwner whenPaused public {
paused = false;
Unpause();
}
}
// Basic mineral operations and counters, defines the constructor
contract MineralBase is AccessControl, Pausable {
bool public isPresale = true;
uint16 public discounts = 10000;
uint32 constant TOTAL_SUPPLY = 8888888;
uint32 public oresLeft;
uint32 gemsLeft;
// Price of ORE (50 pieces in presale, only 1 afterwards)
uint64 public orePrice = 1e16;
mapping(address => uint) internal ownerOreCount;
// Constructor
function MineralBase() public {
// Assign ownership to the creator
owner = msg.sender;
addressDev = owner;
addressFin = owner;
addressOps = owner;
// Initializing counters
oresLeft = TOTAL_SUPPLY;
gemsLeft = TOTAL_SUPPLY;
// Transfering ORES to the team
ownerOreCount[msg.sender] += oresLeft / 2;
oresLeft = oresLeft / 2;
}
function balanceOfOre(address _owner) public view returns (uint256 _balance) {
return ownerOreCount[_owner];
}
function sendOre(address _recipient, uint _amount) external payable {
require(balanceOfOre(msg.sender) >= _amount);
ownerOreCount[msg.sender] -= _amount;
ownerOreCount[_recipient] += _amount;
}
function endPresale() onlyTeamMembers external {
isPresale = false;
discounts = 0;
}
}
// The Factory holds the defined counts and provides an exclusive operations
contract MineralFactory is MineralBase {
uint8 constant MODULUS = 100;
uint8 constant CATEGORY_COUNT = 50;
uint64 constant EXTRACT_PRICE = 1e16;
uint32[] mineralCounts = [
8880, 9768, 10744, 11819, 13001,
19304, 21234, 23358, 25694, 28263,
28956, 31852, 35037, 38541, 42395,
43434, 47778, 52556, 57811, 63592,
65152, 71667, 78834, 86717, 95389,
97728, 107501, 118251, 130076, 143084,
146592, 161251, 177377, 195114, 214626,
219888, 241877, 266065, 292672, 321939,
329833, 362816, 399098, 439008, 482909,
494750, 544225, 598647, 658512, 724385];
uint64[] polishingPrice = [
200e16, 180e16, 160e16, 130e16, 100e16,
80e16, 60e16, 40e16, 20e16, 5e16];
mapping(address => uint) internal ownerGemCount;
mapping (uint256 => address) public gemIndexToOwner;
mapping (uint256 => address) public gemIndexToApproved;
Gemstone[] public gemstones;
struct Gemstone {
uint category;
string name;
uint256 colour;
uint64 extractionTime;
uint64 polishedTime;
uint256 price;
}
function _getRandomMineralId() private view returns (uint32) {
return uint32(uint256(keccak256(block.timestamp, block.difficulty))%oresLeft);
}
function _getPolishingPrice(uint _category) private view returns (uint) {
return polishingPrice[_category / 5];
}
function _generateRandomHash(string _str) private view returns (uint) {
uint rand = uint(keccak256(_str));
return rand % MODULUS;
}
function _getCategoryIdx(uint position) private view returns (uint8) {
uint32 tempSum = 0;
//Chosen category index, 255 for no category selected - when we are out of minerals
uint8 chosenIdx = 255;
for (uint8 i = 0; i < mineralCounts.length; i++) {
uint32 value = mineralCounts[i];
tempSum += value;
if (tempSum > position) {
//Mineral counts is 50, so this is safe to do
chosenIdx = i;
break;
}
}
return chosenIdx;
}
function extractOre(string _name) external payable returns (uint8, uint256) {
require(gemsLeft > 0);
require(msg.value >= EXTRACT_PRICE);
require(ownerOreCount[msg.sender] > 0);
uint32 randomNumber = _getRandomMineralId();
uint8 categoryIdx = _getCategoryIdx(randomNumber);
require(categoryIdx < CATEGORY_COUNT);
//Decrease the mineral count for the category
mineralCounts[categoryIdx] = mineralCounts[categoryIdx] - 1;
//Decrease total mineral count
gemsLeft = gemsLeft - 1;
Gemstone memory _stone = Gemstone({
category : categoryIdx,
name : _name,
colour : _generateRandomHash(_name),
extractionTime : uint64(block.timestamp),
polishedTime : 0,
price : 0
});
uint256 newStoneId = gemstones.push(_stone) - 1;
ownerOreCount[msg.sender]--;
ownerGemCount[msg.sender]++;
gemIndexToOwner[newStoneId] = msg.sender;
return (categoryIdx, _stone.colour);
}
function polishRoughStone(uint256 _gemId) external payable {
uint gainedWei = msg.value;
require(gemIndexToOwner[_gemId] == msg.sender);
Gemstone storage gem = gemstones[_gemId];
require(gem.polishedTime == 0);
require(gainedWei >= _getPolishingPrice(gem.category));
gem.polishedTime = uint64(block.timestamp);
}
}
// The Ownership contract makes sure the requirements of the NFT are met
contract MineralOwnership is MineralFactory, ERC721 {
string public constant name = "CryptoMinerals";
string public constant symbol = "GEM";
function _owns(address _claimant, uint256 _gemId) internal view returns (bool) {
return gemIndexToOwner[_gemId] == _claimant;
}
// Assigns ownership of a specific gem to an address.
function _transfer(address _from, address _to, uint256 _gemId) internal {
require(_from != address(0));
require(_to != address(0));
ownerGemCount[_from]--;
ownerGemCount[_to]++;
gemIndexToOwner[_gemId] = _to;
Transfer(_from, _to, _gemId);
}
function _approvedFor(address _claimant, uint256 _gemId) internal view returns (bool) {
return gemIndexToApproved[_gemId] == _claimant;
}
function _approve(uint256 _gemId, address _approved) internal {
gemIndexToApproved[_gemId] = _approved;
}
// Required for ERC-721 compliance
function balanceOf(address _owner) public view returns (uint256 count) {
return ownerGemCount[_owner];
}
// Required for ERC-721 compliance.
function transfer(address _to, uint256 _gemId) external whenNotPaused {
require(_to != address(0));
require(_to != address(this));
require(_owns(msg.sender, _gemId));
_transfer(msg.sender, _to, _gemId);
}
// Required for ERC-721 compliance.
function approve(address _to, uint256 _gemId) external whenNotPaused {
require(_owns(msg.sender, _gemId));
_approve(_gemId, _to);
Approval(msg.sender, _to, _gemId);
}
// Required for ERC-721 compliance.
function transferFrom(address _from, address _to, uint256 _gemId) external whenNotPaused {
require(_to != address(0));
require(_to != address(this));
require(_approvedFor(msg.sender, _gemId));
require(_owns(_from, _gemId));
_transfer(_from, _to, _gemId);
}
// Required for ERC-721 compliance.
function totalSupply() public view returns (uint) {
return TOTAL_SUPPLY - gemsLeft;
}
// Required for ERC-721 compliance.
function ownerOf(uint256 _gemId) external view returns (address owner) {
owner = gemIndexToOwner[_gemId];
require(owner != address(0));
}
// Required for ERC-721 compliance.
function implementsERC721() public view returns (bool implementsERC721) {
return true;
}
function gemsOfOwner(address _owner) external view returns(uint256[] ownerGems) {
uint256 gemCount = balanceOf(_owner);
if (gemCount == 0) {
return new uint256[](0);
} else {
uint256[] memory result = new uint256[](gemCount);
uint256 totalGems = totalSupply();
uint256 resultIndex = 0;
uint256 gemId;
for (gemId = 0; gemId <= totalGems; gemId++) {
if (gemIndexToOwner[gemId] == _owner) {
result[resultIndex] = gemId;
resultIndex++;
}
}
return result;
}
}
}
// This contract introduces functionalities for the basic trading
contract MineralMarket is MineralOwnership {
function buyOre() external payable {
require(msg.sender != address(0));
require(msg.value >= orePrice);
require(oresLeft > 0);
uint8 amount;
if (isPresale) {
require(discounts > 0);
amount = 50;
discounts--;
} else {
amount = 1;
}
oresLeft -= amount;
ownerOreCount[msg.sender] += amount;
}
function buyGem(uint _gemId) external payable {
uint gainedWei = msg.value;
require(msg.sender != address(0));
require(_gemId < gemstones.length);
require(gemIndexToOwner[_gemId] == address(this));
Gemstone storage gem = gemstones[_gemId];
require(gainedWei >= gem.price);
_transfer(address(this), msg.sender, _gemId);
}
function mintGem(uint _categoryIdx, string _name, uint256 _colour, bool _polished, uint256 _price) onlyTeamMembers external {
require(gemsLeft > 0);
require(_categoryIdx < CATEGORY_COUNT);
//Decrease the mineral count for the category if not PROMO gem
if (_categoryIdx < CATEGORY_COUNT){
mineralCounts[_categoryIdx] = mineralCounts[_categoryIdx] - 1;
}
uint64 stamp = 0;
if (_polished) {
stamp = uint64(block.timestamp);
}
//Decrease counters
gemsLeft = gemsLeft - 1;
oresLeft--;
Gemstone memory _stone = Gemstone({
category : _categoryIdx,
name : _name,
colour : _colour,
extractionTime : uint64(block.timestamp),
polishedTime : stamp,
price : _price
});
uint256 newStoneId = gemstones.push(_stone) - 1;
ownerGemCount[address(this)]++;
gemIndexToOwner[newStoneId] = address(this);
}
function setPrice(uint256 _gemId, uint256 _price) onlyTeamMembers external {
require(_gemId < gemstones.length);
Gemstone storage gem = gemstones[_gemId];
gem.price = uint64(_price);
}
function setMyPrice(uint256 _gemId, uint256 _price) external {
require(_gemId < gemstones.length);
require(gemIndexToOwner[_gemId] == msg.sender);
Gemstone storage gem = gemstones[_gemId];
gem.price = uint64(_price);
}
function withdrawBalance() onlyTeamMembers external {
bool res = owner.send(address(this).balance);
}
}
{
"compilationTarget": {
"MineralMarket.sol": "MineralMarket"
},
"evmVersion": "byzantium",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":false,"inputs":[],"name":"buyOre","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"addressFin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"gemIndexToApproved","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"addressDev","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_gemId","type":"uint256"}],"name":"approve","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"gemstones","outputs":[{"name":"category","type":"uint256"},{"name":"name","type":"string"},{"name":"colour","type":"uint256"},{"name":"extractionTime","type":"uint64"},{"name":"polishedTime","type":"uint64"},{"name":"price","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"implementsERC721","outputs":[{"name":"implementsERC721","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_gemId","type":"uint256"}],"name":"transferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_gemId","type":"uint256"},{"name":"_price","type":"uint256"}],"name":"setMyPrice","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_gemId","type":"uint256"}],"name":"polishRoughStone","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_gemId","type":"uint256"}],"name":"buyGem","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOfOre","outputs":[{"name":"_balance","type":"uint256"}],"payable":false,"stateMutability":"view","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":true,"inputs":[{"name":"_gemId","type":"uint256"}],"name":"ownerOf","outputs":[{"name":"owner","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"string"}],"name":"extractOre","outputs":[{"name":"","type":"uint8"},{"name":"","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"count","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"discounts","outputs":[{"name":"","type":"uint16"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oresLeft","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","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":true,"inputs":[],"name":"isPresale","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_recipient","type":"address"},{"name":"_amount","type":"uint256"}],"name":"sendOre","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"orePrice","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newFinance","type":"address"}],"name":"setFinance","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"endPresale","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_gemId","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"addressOps","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"gemsOfOwner","outputs":[{"name":"ownerGems","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"gemIndexToOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_categoryIdx","type":"uint256"},{"name":"_name","type":"string"},{"name":"_colour","type":"uint256"},{"name":"_polished","type":"bool"},{"name":"_price","type":"uint256"}],"name":"mintGem","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":"_gemId","type":"uint256"},{"name":"_price","type":"uint256"}],"name":"setPrice","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_newOperation","type":"address"}],"name":"setOperation","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_newDeveloper","type":"address"}],"name":"setDeveloper","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"from","type":"address"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"approved","type":"address"},{"indexed":false,"name":"tokenId","type":"uint256"}],"name":"Approval","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"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newContract","type":"address"}],"name":"ContractUpgrade","type":"event"}]