ERC-721 collectible card game of historical figures. mineCard() costs 0.001 ETH and randomly assigns a figure via weak block.timestamp randomness.
Token Information
Key Facts
Description
HumanityCards (HCX) is an ERC-721 collectible card game in which every card is a historical figure from human history. It was deployed on March 13, 2018 (block 5,249,869) and is still live.
Mining cards
New cards are created by calling mineCard() and paying the card price, currently 0.001 ETH (1 finney). Each mint randomly assigns one historical figure and forwards the 0.001 ETH directly to the contract owner. Every figure has a fixed maximum supply set when it was added: some are effectively 1-of-1 (Moses, Jesus, Muhammad), while others allow a small number of copies. Once a figure reaches its cap it can no longer be mined and can only be acquired from an existing holder.
Weak randomness
The figure assigned by mineCard() is chosen with int(keccak256(block.timestamp)) % remaining. Because block.timestamp is visible to any contract executing in the same block, the outcome is fully predictable: a wrapper contract can compute the same hash, learn exactly which figure the next mint will produce, and either mint it or revert. This makes targeted sniping of specific rare figures (for example the 1-of-1 Moses, Jesus, or Muhammad) trivial. Deriving game randomness from block.timestamp was a known 2018-era anti-pattern. The flaw has been present since deployment, and the contract still functions exactly as written for ordinary minters.
The figures
The roster spans religious, political, and military figures from across history, including Moses, Jesus, Muhammad, Abraham, Alexander, Napoleon, and Satoshi Nakamoto, among 239 figures in total. New figures can be added by the owner through addHuman(name, max), which also raises the collection's total card supply.
Pre-standard ERC-721
HumanityCards predates the finalized ERC-721 standard. It implements the early draft interface, using the takeOwnership(tokenId) claim pattern alongside approve and transferFrom, and exposes isERC721() and implementsERC721() marker methods. There is no safeTransferFrom, no tokenURI (the only metadata is the figure's name, returned by tokenMetadata), and no approval-for-all. Ownership and balances are tracked through the contract's own card and index structures rather than a standard owner mapping.
Built-in marketplace
The contract includes its own on-chain marketplace. A holder lists a card with createSellOrder(tokenId, price), which escrows the card inside the contract. A buyer fills the order with processSellOrder(id, tokenId) by sending the exact price, which forwards the funds to the seller and transfers the card. A seller can withdraw an unfilled listing with cancelSellOrder.
Current state
As of June 2026, 337 of a possible 18,190 cards have been mined across 239 figures. The mint price is 0.001 ETH and mineCard() remains callable. The deploying account's last activity was January 2019.
Technical details
- Contract name: HumanityCard
- Symbol: HCX
- Compiler: v0.4.21+commit.dfe3193c, optimizer disabled
- Deployer: 0x6b94a6b94b4689c8d1cc0e76bb5090330df44fad
- Deployment tx: 0xb945f973473d197bbd04123589e14ad81160b747caf31af5823d8589217c237c
- Verified on Etherscan
Source Verified
Source verified on Etherscan. Compiler v0.4.21+commit.dfe3193c, optimizer disabled. Pre-final ERC-721 draft interface (takeOwnership pattern, no safeTransferFrom).
Historian Categories
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 on Etherscan.
View Verification ProofShow source code (Solidity)
pragma solidity ^0.4.2;
contract ERC721 {
function isERC721() public pure returns (bool b);
function implementsERC721() public pure returns (bool b);
function name() public pure returns (string name);
function symbol() public pure returns (string symbol);
function totalSupply() public view returns (uint256 totalSupply);
function balanceOf(address _owner) public view returns (uint256 balance);
function ownerOf(uint256 _tokenId) public view returns (address owner);
function approve(address _to, uint256 _tokenId) public;
function takeOwnership(uint256 _tokenId) public;
function transferFrom(address _from, address _to, uint256 _tokenId) public;
function transfer(address _to, uint256 _tokenId) public;
function tokenOfOwnerByIndex(address _owner, uint256 _index) constant returns (uint tokenId);
function tokenMetadata(uint256 _tokenId) constant returns (string infoUrl);
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
}
contract HumanityCard is ERC721 {
///////////////////////////////////////////////////////////////
/// Modifiers
modifier onlyOwner {
require(msg.sender == owner);
_;
}
event Mined(address indexed owner, uint16 human);
///////////////////////////////////////////////////////////////
/// Structures
struct Human {
string name;
uint8 max;
uint mined;
}
struct Card {
uint16 human;
address owner;
uint indexUser;
}
struct SellOrder {
address seller;
uint card;
uint price;
}
///////////////////////////////////////////////////////////////
/// Constants
string constant NAME = "HumanityCards";
string constant SYMBOL = "HCX";
///////////////////////////////////////////////////////////////
/// Attributes
address owner;
uint cardPrice;
uint humanNumber;
Human[] humanArray;
uint cardNumber;
uint cardMined;
Card[] cardArray;
mapping (address => uint256) cardCount;
mapping (uint256 => address) approveMap;
SellOrder[] sellOrderList;
// Index of the card for the user
mapping (address => mapping (uint => uint)) indexCard;
///////////////////////////////////////////////////////////////
/// Constructor
function HumanityCard() public {
owner = msg.sender;
cardPrice = 1 finney;
humanNumber = 0;
cardNumber = 0;
cardMined = 0;
}
///////////////////////////////////////////////////////////////
/// Admin functions
function addHuman(string name, uint8 max) public onlyOwner {
Human memory newHuman = Human(name, max, 0);
humanArray.push(newHuman);
humanNumber += 1;
cardNumber += max;
}
// Used only if ether price increase (decrease the price card)
function changeCardPrice(uint newPrice) public onlyOwner {
cardPrice = newPrice;
}
///////////////////////////////////////////////////////////////
/// Implementation ERC721
function isERC721() public pure returns (bool b) {
return true;
}
function implementsERC721() public pure returns (bool b) {
return true;
}
function name() public pure returns (string _name) {
return NAME;
}
function symbol() public pure returns (string _symbol) {
return SYMBOL;
}
function totalSupply() public view returns (uint256 _totalSupply) {
return cardMined;
}
function balanceOf(address _owner) public view returns (uint256 balance) {
return cardCount[_owner];
}
function ownerOf(uint256 _tokenId) public view returns (address _owner) {
require(_tokenId < cardMined);
Card c = cardArray[_tokenId];
return c.owner;
}
function approve(address _to, uint256 _tokenId) public {
require(msg.sender == ownerOf(_tokenId));
require(msg.sender != _to);
approveMap[_tokenId] = _to;
Approval(msg.sender, _to, _tokenId);
}
function transferFrom(address _from, address _to, uint256 _tokenId) public {
require(_tokenId < cardMined);
require(_from == ownerOf(_tokenId));
require(_from != _to);
require(approveMap[_tokenId] == _to);
cardCount[_from] -= 1;
// Change the indexCard of _from
indexCard[_from][cardArray[_tokenId].indexUser] = indexCard[_from][cardCount[_from]];
cardArray[indexCard[_from][cardCount[_from]]].indexUser = cardArray[_tokenId].indexUser;
// This card is the last one for the new owner
cardArray[_tokenId].indexUser = cardCount[_to];
indexCard[_to][cardCount[_to]] = _tokenId;
cardArray[_tokenId].owner = _to;
cardCount[_to] += 1;
Transfer(_from, _to, _tokenId);
}
function takeOwnership(uint256 _tokenId) public {
require(_tokenId < cardMined);
address oldOwner = ownerOf(_tokenId);
address newOwner = msg.sender;
require(newOwner != oldOwner);
require(approveMap[_tokenId] == msg.sender);
cardCount[oldOwner] -= 1;
// Change the indexCard of _from
indexCard[oldOwner][cardArray[_tokenId].indexUser] = indexCard[oldOwner][cardCount[oldOwner]];
cardArray[indexCard[oldOwner][cardCount[oldOwner]]].indexUser = cardArray[_tokenId].indexUser;
// This card is the last one for the new owner
cardArray[_tokenId].indexUser = cardCount[newOwner];
indexCard[newOwner][cardCount[newOwner]] = _tokenId;
cardArray[_tokenId].owner = newOwner;
cardCount[newOwner] += 1;
Transfer(oldOwner, newOwner, _tokenId);
}
function transfer(address _to, uint256 _tokenId) public {
require(_tokenId < cardMined);
address oldOwner = msg.sender;
address newOwner = _to;
require(oldOwner == ownerOf(_tokenId));
require(oldOwner != newOwner);
require(newOwner != address(0));
cardCount[oldOwner] -= 1;
// Change the indexCard of _from
indexCard[oldOwner][cardArray[_tokenId].indexUser] = indexCard[oldOwner][cardCount[oldOwner]];
cardArray[indexCard[oldOwner][cardCount[oldOwner]]].indexUser = cardArray[_tokenId].indexUser;
// This card is the last one for the new owner
cardArray[_tokenId].indexUser = cardCount[newOwner];
indexCard[newOwner][cardCount[newOwner]] = _tokenId;
cardArray[_tokenId].owner = newOwner;
cardCount[newOwner] += 1;
Transfer(oldOwner, newOwner, _tokenId);
}
function tokenOfOwnerByIndex(address _owner, uint256 _index) constant returns (uint tokenId) {
require(_index < cardCount[_owner]);
return indexCard[_owner][_index];
}
// For this case the only metadata is the name of the human
function tokenMetadata(uint256 _tokenId) constant returns (string infoUrl) {
require(_tokenId < cardMined);
uint16 humanId = cardArray[_tokenId].human;
return humanArray[humanId].name;
}
///////////////////////////////////////////////////////////////
/// HumanityCard functions
// Mine a new card
function mineCard() public payable returns(bool success) {
require(msg.value == cardPrice);
require(cardMined < cardNumber);
int remaining = (int)(cardNumber - cardMined);
// Choosing the card
int numero = int(keccak256(block.timestamp))%remaining;
if(numero < 0) {
numero *= -1;
}
uint16 chosenOne = 0;
while (numero >= 0) {
numero -= (int)(humanArray[chosenOne].max-humanArray[chosenOne].mined);
if (numero >= 0) {
chosenOne += 1;
}
}
// Adding the card to the user
address newOwner = msg.sender;
Card memory newCard = Card(chosenOne, newOwner, cardCount[newOwner]);
cardArray.push(newCard);
// This card is the last one
indexCard[newOwner][cardCount[newOwner]] = cardMined;
cardCount[newOwner] += 1;
// Updating cards informations
cardMined += 1;
humanArray[chosenOne].mined += 1;
// Sending the fund to the owner
if(!owner.send(cardPrice)) {
revert();
}
Mined(newOwner, chosenOne);
return true;
}
// Sale functions
function createSellOrder(uint256 _tokenId, uint price) public {
require(_tokenId < cardMined);
require(msg.sender == ownerOf(_tokenId));
SellOrder memory newOrder = SellOrder(msg.sender, _tokenId, price);
sellOrderList.push(newOrder);
cardArray[_tokenId].owner = address(0);
cardCount[msg.sender] -= 1;
// Change the indexCard of sender
indexCard[msg.sender][cardArray[_tokenId].indexUser] = indexCard[msg.sender][cardCount[msg.sender]];
cardArray[indexCard[msg.sender][cardCount[msg.sender]]].indexUser = cardArray[_tokenId].indexUser;
}
function processSellOrder(uint id, uint256 _tokenId) payable public {
require(id < sellOrderList.length);
SellOrder memory order = sellOrderList[id];
require(order.card == _tokenId);
require(msg.value == order.price);
require(msg.sender != order.seller);
// Sending fund to the seller
if(!order.seller.send(msg.value)) {
revert();
}
// Adding card to the buyer
cardArray[_tokenId].owner = msg.sender;
// This card is the last one for the new owner
cardArray[_tokenId].indexUser = cardCount[msg.sender];
indexCard[msg.sender][cardCount[msg.sender]] = _tokenId;
cardCount[msg.sender] += 1;
// Update list
sellOrderList[id] = sellOrderList[sellOrderList.length-1];
delete sellOrderList[sellOrderList.length-1];
sellOrderList.length--;
}
function cancelSellOrder(uint id, uint256 _tokenId) public {
require(id < sellOrderList.length);
SellOrder memory order = sellOrderList[id];
require(order.seller == msg.sender);
require(order.card == _tokenId);
// Give back card to seller
cardArray[_tokenId].owner = msg.sender;
// This card is the last one for the new owner
cardArray[_tokenId].indexUser = cardCount[msg.sender];
indexCard[msg.sender][cardCount[msg.sender]] = _tokenId;
cardCount[msg.sender] += 1;
// Update list
sellOrderList[id] = sellOrderList[sellOrderList.length-1];
delete sellOrderList[sellOrderList.length-1];
sellOrderList.length--;
}
function getSellOrder(uint id) public view returns(address seller, uint card, uint price) {
require(id < sellOrderList.length);
SellOrder memory ret = sellOrderList[id];
return(ret.seller, ret.card, ret.price);
}
function getNbSellOrder() public view returns(uint nb) {
return sellOrderList.length;
}
// Get functions
function getOwner() public view returns(address ret) {
return owner;
}
function getCardPrice() public view returns(uint ret) {
return cardPrice;
}
function getHumanNumber() public view returns(uint ret) {
return humanNumber;
}
function getHumanInfo(uint i) public view returns(string name, uint8 max, uint mined) {
require(i < humanNumber);
Human memory h = humanArray[i];
return (h.name, h.max, h.mined);
}
function getCardNumber() public view returns(uint ret) {
return cardNumber;
}
function getCardInfo(uint256 _tokenId) public view returns(uint16 human, address owner) {
require(_tokenId < cardMined);
Card memory c = cardArray[_tokenId];
return (c.human, c.owner);
}
}