POW NFT — Technical Details

This is a short technical write-up of POW NFT mining, for people making their own mining software or are interested in adding mining to their own NFT projects.

Hashing

The hash function used to mine POW NFTs is keccak256, because it’s built right into Solidity. A successful hash will be below the block target and takes the following form:

A POW NFT can be minted if a hash (cast to uint) is found below the difficulty, which follows the following rule:

Where tokenId increments by 1 with each new token,

And (pseudocode):

Note: base difficulty is 1/300 the max value of a uint.

Generational demand curve

Once a valid hash is found, the mine function must be executed on the POW NFT smart contract. It takes only one argument, and also requires msg.value equal to the current minting price according to the demand curve.

This function uses msg.sender and the stored previous hash to validate the nonce, and if the hash is valid a token is minted.

The demand curve is described as:

cost = (2**generation — 1)* BASE_COST;

Where

Essentially, the minting cost jumps discretely every time the number of tokens doubles.

Previous hash

When a token is minted, the hash is re-hashed with block.timestamp to provide the prev_hash for the next token.

This has a dual purpose of preventing miners from pre-mining several tokens at once and submitting them all at once, but also ensure that hash data used to render POW NFT Atoms isn’t affected by the changing difficulty level.

Sample code for writing your own miner

There has been a lot of enthusiasm around this project since launch last week, and everyone is eager to maximise their hash rate. Although I’ve made some marginal improvements to the in-site miner (and hopefully more are on their way), there are plenty more speed gains to be had for someone writing their own.

The in-site miner is written in Javascript, so I’m providing some functions here that should help people interested in writing their own, either using the code for a JS miner, or for writing in their preferred language.

If you want to see a finished product, you can see a bare-bones web miner on GitHub.

Although I’m a big fan of web3.js, the site uses ethers.js for it’s web3 stuff. This is because the JS framework (svelte.js) wouldn’t play nice with web3.js, but don’t get me started on JS frameworks and the headaches they cause. Vanilla 4 lyf.

Anyway, the only dependency you need for the following code is ethers.js. It’s mostly for sending transactions, but I also make use of its BN (big number) class.

If you haven’t used it before, its pretty simple. It can handle 256 bit integers (unlike pure JS), but syntax is a little different. To multiply two BN variables we do something like this:

ethers.js also lets you use a human-readable ABI for contract instances. I’ll paste this at the end of this article.

There’s a few key variables which you’ll need for your code, the BASE_DIFFICULTY, DIFFICULTY_RAMP and BASE_COST (for calculating minting cost). These all mirror values in the smart contract.

Your miner needs to know the current generation of your token in order to work out the target difficulty as well as the current minting cost, so you’ll want the following functions too

and

And our cost calculation function is:

Now that we have all of these parts ready, we need an actual hashing function. Luckily ethers.js has a keccak256 function built in, so this is pretty easy:

Where address is your address, prev is the hash of the previously minted token, and nonce is the current nonce.

However, this returns a value in bytes, and we need a BN to be able to compare it to our current difficulty, so you can wrap it all in a function with the current difficulty and let it tell you whether it’s a successful nonce.

Note: lt is a “less than” comparison function. This will return true if hash is less than difficulty. So basically you just need to iterate nonce and test if the hash was successful. If not, iterate again and repeat.

Obviously you need to know the hash of the latest token as soon as it’s minted, because otherwise you’re hashing garbage. The site monitors events emitted by the contract to do this. The relevant event in the smart contract is:

This will tell you the tokenId and hash of the token that has been mined. So your miner just needs to watch for these events, and adjust itself accordingly, the prev hash is hash and the difficulty and cost can be calculated based on _tokenId + 1.

Subscribing to events with ethers.js is pretty easy, it basically takes this form:

And the components of event that you’ll want are:

Just let that event update your stored current tokenId and hash (and probably reset your nonce) and you should be good to go. The rest of it is design choices.

Go forth and build

That should give you a head-start on writing your own miner. I’m currently investigating using a web-assembly based keccak256 instead of the js one in ethers.js. Hopefully that will add to performance.

If you want to recompile the smart contract for whatever reason, you can see the Solidity code at:

https://etherscan.io/address/0x9abb7bddc43fa67c76a62d8c016513827f59be1b#code

The address of the contract is
0x9Abb7BdDc43FA67c76a62d8C016513827f59bE1b
But if you’re playing around and want to test, there’s an instance on Ropsten at
0x88066567a7F90409Ced36D4030C47909Eb910926

If you are deploying a version on your local RPC you’ll probably want the pre-migration contract code too, which can be found here:

https://etherscan.io/address/0x7d4e35A2090b3ba805ddB39B2c4b83612890Df87#code

The new contract takes the previous contract address as its only argument (because it took a snapshot when it was launched).

If there is any more information you want, please don’t hesitate to ask. The only reason I haven’t provided the full miner code is because its tangled up with frontend code and is a little incomprehensible at a glance.

Human readable ABI for ethers.js

Rather than making you recompile the contract, I’ve provided the human-readable ABI that you can use to create your contract instance, ie, like this:

Here it is for you to copy/paste:

Codeslinger. Melbourne based Solidity developer for hire.