POW NFT — Technical Details


bytes32 hash = keccak256(abi.encodePacked(address miner_address,bytes32 prev_hash, uint nonce));
difficulty = BASE_DIFFICULTY / (DIFFICULTY_RAMP**generation);
generation > 13){
difficulty /= (tokenId — 2**14 + 1);
BASE_DIFFICULTY = uint(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)/uint(300);
generation = floor(log2(tokenId))

Generational demand curve

function mine(uint nonce) payable{
uint BASE_COST = 0.000045 ether;

Previous hash


Sample code for writing your own miner

const BN = ethers.BigNumber;
result = var1.mul(var2);
const BASE_DIFFICULTY = BN.from('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff').div(BN.from(300));const DIFFICULTY_RAMP = 3;const BASE_COST = BN.from(ethers.utils.parseEther('0.000045'));
//Get the generation of a given tokenId
return Math.floor(Math.log2(_tokenId));
//Get the difficulty target to mine a given tokenId
const generation = generationOf(tokenId);
let difficulty = BASE_DIFFICULTY.div(BN.from(Math.pow(DIFFICULTY_RAMP,generation)));
if(generation > 13){
BN.from(parseInt(tokenId) - Math.pow(2,14) + 1)
return difficulty;
const calculate_cost = (_tokenId) => {
const generation = generationOf(_tokenId);
return (BN.from(Math.pow(2,generation) - 1)).mul(BASE_COST);
function Hash(address,prev,nonce){
return ethers.utils.solidityKeccak256(["address","bytes32","uint256"],[address,prev,nonce]);
function Attempt(address,prev_hash,nonce,difficulty){
const _hash = Hash(address,prev_hash,nonce);
const hash = BN.from(_hash);

return hash.lt(difficulty);
event Mined(uint indexed _tokenId, bytes32 hash);
contract.on( "Mined" , (author,oldValue,newValue,event,e,f,g,h)=>{
if(typeof event === "undefined"){
//This catches weird stuff with ethers.js that their
// docs don't explain

event = newValue;

//Do stuff with the event
//TokenId of token minted
tokenId = Number(event.args._tokenId._hex);
//Hash of token minted
hash = event.args.hash;

Go forth and build

Human readable ABI for ethers.js

contract = new ethers.Contract(contractAddress, contractAbi, provider);
contractWithSigner = contract.connect(signer);
const contractAbi = ["event Approval(address indexed _owner,address indexed _approved,uint256 indexed _tokenId)","event ApprovalForAll(address indexed _owner,address indexed _operator,bool _approved)","event Migrate(uint256 indexed _tokenId)","event Mined(uint256 indexed _tokenId,bytes32 hash)","event Transfer(address indexed _from,address indexed _to,uint256 indexed _tokenId)","event Withdraw(uint256 indexed _tokenId,uint256 value)","function PREV_CHAIN_LAST_HASH() view returns(bytes32 )","function UNMIGRATED() view returns(uint256 )","function V2_TOTAL() view returns(uint256 )","function approve(address _approved,uint256 _tokenId) nonpayable","function balanceOf(address _owner) view returns(uint256 )","function getApproved(uint256 _tokenId) view returns(address )","function hashOf(uint256 _tokenId) view returns(bytes32 )","function isApprovedForAll(address _owner,address _operator) view returns(bool )","function migrate(uint256 _tokenId,uint256 _withdrawEthUntil) nonpayable","function migrateMultiple(uint256[] _tokenIds,uint256[] _withdrawUntil) nonpayable","function mine(uint256 nonce) payable","function name() view returns(string _name)","function ownerOf(uint256 _tokenId) view returns(address )","function safeTransferFrom(address _from,address _to,uint256 _tokenId) nonpayable","function safeTransferFrom(address _from,address _to,uint256 _tokenId,bytes data) nonpayable","function setApprovalForAll(address _operator,bool _approved) nonpayable","function supportsInterface(bytes4 interfaceID) view returns(bool )","function symbol() view returns(string _symbol)","function tokenByIndex(uint256 _index) view returns(uint256 )","function tokenOfOwnerByIndex(address _owner,uint256 _index) view returns(uint256 )","function tokenURI(uint256 _tokenId) view returns(string )","function totalSupply() view returns(uint256 )","function transferFrom(address _from,address _to,uint256 _tokenId) nonpayable","function withdraw(uint256 _tokenId,uint256 _withdrawUntil) nonpayable","function withdrawMultiple(uint256[] _tokenIds,uint256[] _withdrawUntil) nonpayable"];



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store