March 21, 2018 — genesis EtherMinerals NFT deploy by anonymous developer 'Elias', funded via ShapeShift, scaffolded from a Coursetro.com Solidity tutorial. 17 n
Token Information
Key Facts
Description
EtherMinerals is the genesis deploy of a small, hand-crafted NFT collectible from March 21, 2018 — the first of three near-identical EtherMinerals contracts an anonymous developer deployed over five days in late March 2018. Seventeen named minerals on a CryptoCelebrities/CelebrityToken-derived ERC-721-draft template, each starting at 0.01 ETH, never bought by anyone in the seven years since.
THE COLLECTION (17 minerals)
Real common gems and metals — Emerald, Opal, Diamond, Bismuth, Amethyst, Gold, Fluorite, Ruby, Sapphire, Malachite, Silver. Obscure real mineralogy — Pascoite (a rare orange vanadium mineral first identified in Pasco County, Utah), Karpatite (an aromatic-hydrocarbon mineral from the Carpathian Mountains), Uvarovite (a green chromium garnet named for a Russian count). Specific provenance — Burmese Tourmaline (the Myanmar-origin gem-grade designation). Pop culture — Kryptonite (DC Comics) and "Good ol' Rock" (the Charlie Brown Halloween rock from the 1966 Peanuts special).
THE DEPLOYER
Address 0x5e97a9f58076954EF0b4943Ddd09348e188429B2, 12 lifetime transactions, all between March 19-24, 2018, no activity since. Funded once with 0.021157 ETH (~$12 at the time) from ShapeShift's no-KYC hot wallet on March 19 — a privacy-conscious crypto-to-crypto swap rather than a centralized exchange withdrawal. No ENS, no token holdings, no further on-chain trace.
Off-chain footprint (recovered via Wayback Machine): the developer ran etherminerals.com from a Windows PC under the local username "Elias" with a project folder named C:\Users\Elias\code\etherores. The frontend JavaScript copied scaffolding from the Coursetro.com Solidity tutorial series (a popular 2017-2018 YouTube course by Gary Simon), then lightly obfuscated the button handlers. The hosting account was suspended by May 5, 2018 (5 weeks after launch) and the domain dropped to HugeDomains by June. No Reddit, Bitcointalk, GitHub, Twitter, or NFT-listicle mentions of the project exist anywhere.
DEPLOY TIMELINE (UTC)
2018-03-21 21:40 — deploy #1 (this contract, the genesis), block 5,297,442
2018-03-21 21:41 — createAllTokens() mints the 17 minerals to the contract itself
2018-03-21 23:02 — openGame() opens sales
2018-03-22 00:04 — deploy #2 at 0x2efa…bb71 (later activated March 24)
2018-03-24 17:16 — deploy #3 at 0x336a…0061 (activated immediately)
2018-03-24 17:23 — deploy #2 createAllTokens + openGame (3 days after its deployment)
The 3-day gap between deploying #2 and activating it remains unexplained.
THE CONTRACT
A customized CryptoCelebrities template. The verified ERC-721 interface block includes Dieter Shirley's authorship attribution verbatim, confirming the CryptoKitties/CryptoCelebrities lineage. On top of that base the developer added: createMineral / createAllTokens (admin batch minter, not in the base template); openGame / gameOpen boolean (explicit two-phase activation — deploy, mint, then flip the switch); setLast / lastBuyer mapping (vestigial dead code — written by the constructor and an admin setter, but never read anywhere in the contract; only the constructor pre-seeds slots 1-9 of 17, oddly).
Price escalation: 90% reward to previous owner, 10% to contract. New price computed in three tiers — below 0.15 ETH price × 200/90 (2.22×), below 0.564957 ETH price × 118/90 (1.31×), above that price × 113/90 (1.256×). The literal threshold 0.564957 ETH is a non-round value the developer typed in directly; its derivation is unknown.
DIFFERENCE FROM DEPLOY #2
Deploy #1 (this contract) is 75 bytes smaller than deploy #2. The exact difference: deploy #2 added a getBalance() public CEO-only view returning the contract's ETH balance; deploy #1 has no such function. All other code (setLast, lastBuyer mapping, the entire purchase/escalation logic, the 17-mineral list) is byte-for-byte identical.
GENESIS-NESS
This is the first and only EtherMinerals contract where every one of the 17 minerals remained owned by the contract itself for the full seven years between deployment and verification (May 2026). No mineral was ever purchased. The collection sat untouched until a wrapper preserved it as on-chain pixel art.
Source Verified
Source recovered from deploy #2 (0x2efaf60bbd75b9430a8446bd58d12fbc3f5dbb71), which is Etherscan-verified with this same compiler. Deploy #1 differs from deploy #2 only by the omission of the getBalance() function — confirmed by bytecode-region diff against deploy #2's runtime, plus selector-presence verification across all 28 functions in the source. Adapted source compiles (solc 0.4.22-nightly.2018.3.21+commit.8fd53c1c, optimizer ON, 200 runs, byzantium EVM) to byte-for-byte identical runtime (5911 bytes) and creation (6398 bytes) bytecode, with only the 32-byte trailing CBOR metadata IPFS hash differing — expected because the EthereumHistory submission header was added to the source. Sourcify accepted as 'match / creationMatch / runtimeMatch' on the first submission. Etherscan also verified.
Heuristic Analysis
The following characteristics were detected through bytecode analysis and may not be accurate.
Byzantium Era
First Metropolis hard fork. Added zk-SNARK precompiles, REVERT opcode, and staticcall.
Bytecode Overview
Verified Source Available
Source verified through compiler archaeology and exact bytecode matching.
View Verification ProofShow source code (Solidity)
// Submitted by EthereumHistory (ethereumhistory.com)
pragma solidity ^0.4.18;
///EtherMinerals
/// @title Interface for contracts conforming to ERC-721: Non-Fungible Tokens
/// @author Dieter Shirley <dete@axiomzen.co> (https://github.com/dete)
contract ERC721 {
function approve(address _to, uint256 _tokenId) public;
function balanceOf(address _owner) public view returns (uint256 balance);
function implementsERC721() public pure returns (bool);
function ownerOf(uint256 _tokenId) public view returns (address addr);
function takeOwnership(uint256 _tokenId) public;
function totalSupply() public view returns (uint256 total);
function transferFrom(address _from, address _to, uint256 _tokenId) public;
function transfer(address _to, uint256 _tokenId) public;
event Transfer(address indexed from, address indexed to, uint256 tokenId);
event Approval(address indexed owner, address indexed approved, uint256 tokenId);
}
contract EtherMinerals is ERC721 {
/*** EVENTS ***/
event Birth(uint256 tokenId, bytes32 name, address owner);
event TokenSold(uint256 tokenId, uint256 oldPrice, uint256 newPrice, address prevOwner, address winner, bytes32 name);
event Transfer(address from, address to, uint256 tokenId);
/*** STRUCTS ***/
struct Mineral {
bytes32 name;
address owner;
uint256 price;
uint256 last_price;
address approve_transfer_to;
}
/*** CONSTANTS ***/
string public constant NAME = "EtherMinerals";
string public constant SYMBOL = "MINERAL";
uint256 private startingPrice = 0.01 ether;
uint256 private firstStepLimit = 0.15 ether;
uint256 private secondStepLimit = 0.564957 ether;
bool public gameOpen = false;
/*** STORAGE ***/
mapping (address => uint256) private ownerCount;
mapping (uint256 => address) public lastBuyer;
address public ceoAddress;
mapping (uint256 => address) public extra;
uint256 mineral_count;
mapping (uint256 => Mineral) private minerals;
/*** ACCESS MODIFIERS ***/
modifier onlyCEO() { require(msg.sender == ceoAddress); _; }
/*** ACCESS MODIFIES ***/
function setCEO(address _newCEO) public onlyCEO {
require(_newCEO != address(0));
ceoAddress = _newCEO;
}
function setLast(uint256 _id, address _newExtra) public onlyCEO {
require(_newExtra != address(0));
lastBuyer[_id] = _newExtra;
}
/*** DEFAULT METHODS ***/
function symbol() public pure returns (string) { return SYMBOL; }
function name() public pure returns (string) { return NAME; }
function implementsERC721() public pure returns (bool) { return true; }
/*** CONSTRUCTOR ***/
function EtherMinerals() public {
ceoAddress = msg.sender;
lastBuyer[1] = msg.sender;
lastBuyer[2] = msg.sender;
lastBuyer[3] = msg.sender;
lastBuyer[4] = msg.sender;
lastBuyer[5] = msg.sender;
lastBuyer[6] = msg.sender;
lastBuyer[7] = msg.sender;
lastBuyer[8] = msg.sender;
lastBuyer[9] = msg.sender;
}
/*** INTERFACE METHODS ***/
function createMineral(bytes32 _name, uint256 _price) public onlyCEO {
require(msg.sender != address(0));
_create_mineral(_name, address(this), _price, 0);
}
function createPromoMineral(bytes32 _name, address _owner, uint256 _price, uint256 _last_price) public onlyCEO {
require(msg.sender != address(0));
require(_owner != address(0));
_create_mineral(_name, _owner, _price, _last_price);
}
function openGame() public onlyCEO {
require(msg.sender != address(0));
gameOpen = true;
}
function totalSupply() public view returns (uint256 total) {
return mineral_count;
}
function balanceOf(address _owner) public view returns (uint256 balance) {
return ownerCount[_owner];
}
function priceOf(uint256 _mineral_id) public view returns (uint256 price) {
return minerals[_mineral_id].price;
}
function getMineral(uint256 _mineral_id) public view returns (
uint256 id,
bytes32 mineral_name,
address owner,
uint256 price,
uint256 last_price
) {
id = _mineral_id;
mineral_name = minerals[_mineral_id].name;
owner = minerals[_mineral_id].owner;
price = minerals[_mineral_id].price;
last_price = minerals[_mineral_id].last_price;
}
function getMinerals() public view returns (uint256[], bytes32[], address[], uint256[]) {
uint256[] memory ids = new uint256[](mineral_count);
bytes32[] memory names = new bytes32[](mineral_count);
address[] memory owners = new address[](mineral_count);
uint256[] memory prices = new uint256[](mineral_count);
for(uint256 _id = 0; _id < mineral_count; _id++){
ids[_id] = _id;
names[_id] = minerals[_id].name;
owners[_id] = minerals[_id].owner;
prices[_id] = minerals[_id].price;
}
return (ids, names, owners, prices);
}
function purchase(uint256 _mineral_id) public payable {
require(gameOpen == true);
Mineral storage mineral = minerals[_mineral_id];
require(mineral.owner != msg.sender);
require(msg.sender != address(0));
require(msg.value >= mineral.price);
uint256 excess = SafeMath.sub(msg.value, mineral.price);
uint256 reward = uint256(SafeMath.div(SafeMath.mul(mineral.price, 90), 100));
if(mineral.owner != address(this)){
mineral.owner.transfer(reward);
}
mineral.last_price = mineral.price;
address _old_owner = mineral.owner;
if (mineral.price < firstStepLimit) {
// first stage
mineral.price = SafeMath.div(SafeMath.mul(mineral.price, 200), 90);
} else if (mineral.price < secondStepLimit) {
// second stage
mineral.price = SafeMath.div(SafeMath.mul(mineral.price, 118), 90);
} else {
// third stage
mineral.price = SafeMath.div(SafeMath.mul(mineral.price, 113), 90);
}
mineral.owner = msg.sender;
emit Transfer(_old_owner, mineral.owner, _mineral_id);
emit TokenSold(_mineral_id, mineral.last_price, mineral.price, _old_owner, mineral.owner, mineral.name);
msg.sender.transfer(excess);
}
function payout() public onlyCEO {
ceoAddress.transfer(address(this).balance);
}
function tokensOfOwner(address _owner) public view returns(uint256[] ownerTokens) {
uint256 tokenCount = balanceOf(_owner);
if (tokenCount == 0) {
return new uint256[](0);
} else {
uint256[] memory result = new uint256[](tokenCount);
uint256 resultIndex = 0;
for (uint256 mineralId = 0; mineralId <= totalSupply(); mineralId++) {
if (minerals[mineralId].owner == _owner) {
result[resultIndex] = mineralId;
resultIndex++;
}
}
return result;
}
}
/*** ERC-721 compliance. ***/
function approve(address _to, uint256 _mineral_id) public {
require(msg.sender == minerals[_mineral_id].owner);
minerals[_mineral_id].approve_transfer_to = _to;
emit Approval(msg.sender, _to, _mineral_id);
}
function ownerOf(uint256 _mineral_id) public view returns (address owner){
owner = minerals[_mineral_id].owner;
require(owner != address(0));
}
function takeOwnership(uint256 _mineral_id) public {
address oldOwner = minerals[_mineral_id].owner;
require(msg.sender != address(0));
require(minerals[_mineral_id].approve_transfer_to == msg.sender);
_transfer(oldOwner, msg.sender, _mineral_id);
}
function transfer(address _to, uint256 _mineral_id) public {
require(msg.sender != address(0));
require(msg.sender == minerals[_mineral_id].owner);
_transfer(msg.sender, _to, _mineral_id);
}
function transferFrom(address _from, address _to, uint256 _mineral_id) public {
require(_from == minerals[_mineral_id].owner);
require(minerals[_mineral_id].approve_transfer_to == _to);
require(_to != address(0));
_transfer(_from, _to, _mineral_id);
}
function createAllTokens() public onlyCEO{
createMineral("Emerald", 10000000000000000);
createMineral("Opal", 10000000000000000);
createMineral("Diamond", 10000000000000000);
createMineral("Bismuth", 10000000000000000);
createMineral("Amethyst", 10000000000000000);
createMineral("Gold", 10000000000000000);
createMineral("Fluorite", 10000000000000000);
createMineral("Ruby", 10000000000000000);
createMineral("Sapphire", 10000000000000000);
createMineral("Pascoite", 10000000000000000);
createMineral("Karpatite", 10000000000000000);
createMineral("Uvarovite", 10000000000000000);
createMineral("Kryptonite", 10000000000000000);
createMineral("Good ol' Rock", 10000000000000000);
createMineral("Malachite", 10000000000000000);
createMineral("Silver", 10000000000000000);
createMineral("Burmese Tourmaline" ,10000000000000000);
}
/*** PRIVATE METHODS ***/
function _create_mineral(bytes32 _name, address _owner, uint256 _price, uint256 _last_price) private {
// Params: name, owner, price, is_for_sale, is_public, share_price, increase, fee, share_count,
minerals[mineral_count] = Mineral({
name: _name,
owner: _owner,
price: _price,
last_price: _last_price,
approve_transfer_to: address(0)
});
emit Birth(mineral_count, _name, _owner);
emit Transfer(address(this), _owner, mineral_count);
mineral_count++;
}
function _transfer(address _from, address _to, uint256 _mineral_id) private {
minerals[_mineral_id].owner = _to;
minerals[_mineral_id].approve_transfer_to = address(0);
ownerCount[_from] -= 1;
ownerCount[_to] += 1;
emit Transfer(_from, _to, _mineral_id);
}
}
library SafeMath {
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;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a / b;
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}