A specification for a standardized Mineable Token that uses a Proof of Work algorithm for distribution.
Abstract
This specification describes a method for initially locking tokens within a token contract and slowly dispensing them with a mint() function which acts like a faucet. This mint() function uses a Proof of Work algorithm in order to minimize gas fees and control the distribution rate. Additionally, standardization of mineable tokens will give rise to standardized CPU and GPU token mining software, token mining pools and other external tools in the token mining ecosystem.
Motivation
Token distribution via the ICO model and its derivatives is susceptible to illicit behavior by human actors. Furthermore, new token projects are centralized because a single entity must handle and control all of the initial coins and all of the raised ICO money. By distributing tokens via an ‘Initial Mining Offering’ (or IMO), the ownership of the token contract no longer belongs with the deployer at all and the deployer is ‘just another user.’ As a result, investor risk exposure utilizing a mined token distribution model is significantly diminished. This standard is intended to be standalone, allowing maximum interoperability with ERC20, ERC721, and others.
Specification
Interface
The general behavioral specification includes a primary function that defines the token minting operation, an optional merged minting operation for issuing multiple tokens, getters for challenge number, mining difficulty, mining target and current reward, and finally a Mint event, to be emitted upon successful solution validation and token issuance. At a minimum, contracts must adhere to this interface (save the optional merge operation). It is recommended that contracts interface with the more behaviorally defined Abstract Contract described below, in order to leverage a more defined construct, allowing for easier external implementations via overridden phased functions. (see ‘Abstract Contract’ below)
The Abstract Contract adheres to the EIP918 Interface and extends behavioral definition through the introduction of 4 internal phases of token mining and minting: hash, reward, epoch and adjust difficulty, all called during the mint() operation. This construct provides a balance between being too general for use while providing ample room for multiple mined implementation types.
Fields
adjustmentInterval
The amount of time between difficulty adjustments in seconds.
bytes32publicadjustmentInterval;
challengeNumber
The current challenge number. It is expected that a new challenge number is generated after a new reward is minted.
bytes32publicchallengeNumber;
difficulty
The current mining difficulty which should be adjusted via the _adjustDifficulty minting phase
uintpublicdifficulty;
tokensMinted
Cumulative counter of the total minted tokens, usually modified during the _reward phase
uintpublictokensMinted;
epochCount
Number of ‘blocks’ mined
uintpublicepochCount;
Mining Operations
mint
Returns a flag indicating a successful hash digest verification, and reward allocation to msg.sender. In order to prevent MiTM attacks, it is recommended that the digest include a recent Ethereum block hash and msg.sender’s address. Once verified, the mint function calculates and delivers a mining reward to the sender and performs internal accounting operations on the contract’s supply.
The mint operation exists as a public function that invokes 4 separate phases, represented as functions hash, _reward, _newEpoch, and _adjustDifficulty. In order to create the most flexible implementation while adhering to a necessary contract protocol, it is recommended that token implementors override the internal methods, allowing the base contract to handle their execution via mint.
This externally facing function is called by miners to validate challenge digests, calculate reward,
populate statistics, mutate epoch variables and adjust the solution difficulty as required. Once complete,
a Mint event is emitted before returning a boolean success flag.
contractAbstractERC918isEIP918Interface{// the amount of time between difficulty adjustments
uintpublicadjustmentInterval;// generate a new challenge number after a new reward is minted
bytes32publicchallengeNumber;// the current mining target
uintpublicminingTarget;// cumulative counter of the total minted tokens
uintpublictokensMinted;// number of blocks per difficulty readjustment
uintpublicblocksPerReadjustment;//number of 'blocks' mined
uintpublicepochCount;/*
* Externally facing mint function that is called by miners to validate challenge digests, calculate reward,
* populate statistics, mutate epoch variables and adjust the solution difficulty as required. Once complete,
* a Mint event is emitted before returning a success indicator.
**/functionmint(uint256nonce)publicreturns(boolsuccess){require(msg.sender!=address(0));// perform the hash function validation
hash(nonce);// calculate the current reward
uintrewardAmount=_reward();// increment the minted tokens amount
tokensMinted+=rewardAmount;epochCount=_epoch();//every so often, readjust difficulty. Don't readjust when deploying
if(epochCount%blocksPerReadjustment==0){_adjustDifficulty();}// send Mint event indicating a successful implementation
emitMint(msg.sender,rewardAmount,epochCount,challengeNumber);returntrue;}}
Mint Event
Upon successful verification and reward the mint method dispatches a Mint Event indicating the reward address, the reward amount, the epoch count and newest challenge number.
Internal interface function _reward, meant to be overridden in implementation to calculate and allocate the reward amount. The reward amount must be returned by this method.
function_reward()internalreturns(uint);
_newEpoch
Internal interface function _newEpoch, meant to be overridden in implementation to define a cutpoint for mutating mining variables in preparation for the next phase of mine.
Internal interface function _adjustDifficulty, meant to be overridden in implementation to adjust the difficulty (via field difficulty) of the mining as required
function_adjustDifficulty()internalreturns(uint);
getAdjustmentInterval
The amount of time, in seconds, between difficulty adjustment operations.
Once the nonce and hash1 are found, these are used to call the mint() function of the smart contract to receive a reward of tokens.
Merged Mining Extension (Optional)
In order to provide support for merge mining multiple tokens, an optional merged mining extension can be implemented as part of the ERC918 standard. It is important to note that the following function will only properly work if the base contracts use tx.origin instead of msg.sender when applying rewards. If not the rewarded tokens will be sent to the calling contract and not the end user.
/**
* @title ERC-918 Mineable Token Standard, optional merged mining functionality
* @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-918.md
*
*/contractERC918MergedisAbstractERC918{/*
* @notice Externally facing merge function that is called by miners to validate challenge digests, calculate reward,
* populate statistics, mutate state variables and adjust the solution difficulty as required. Additionally, the
* merge function takes an array of target token addresses to be used in merged rewards. Once complete,
* a Mint event is emitted before returning a success indicator.
*
* @param _nonce the solution nonce
**/functionmerge(uint256_nonce,address[]_mineTokens)publicreturns(bool){for(uinti=0;i<_mineTokens.length;i++){addresstokenAddress=_mineTokens[i];ERC918Interface(tokenAddress).mint(_nonce);}}/*
* @notice Externally facing merge function kept for backwards compatibility with previous definition
*
* @param _nonce the solution nonce
* @param _challenge_digest the keccak256 encoded challenge number + message sender + solution nonce
**/functionmerge(uint256_nonce,bytes32_challenge_digest,address[]_mineTokens)publicreturns(bool){//the challenge digest must match the expected
bytes32digest=keccak256(abi.encodePacked(challengeNumber,msg.sender,_nonce));require(digest==_challenge_digest,"Challenge digest does not match expected digest on token contract [ ERC918Merged.mint() ]");returnmerge(_nonce,_mineTokens);}}
Delegated Minting Extension (Optional)
In order to facilitate a third party minting submission paradigm, such as the case of miners submitting solutions to a pool operator and/or system, a delegated minting extension can be used to allow pool accounts submit solutions on the behalf of a user, so the miner can avoid directly paying Ethereum transaction costs. This is performed by an off chain mining account packaging and signing a standardized mint solution packet and sending it to a pool or 3rd party to be submitted.
The ERC918 Mineable Mint Packet Metadata should be prepared using following schema:
{"title":"Mineable Mint Packet Metadata","type":"object","properties":{"nonce":{"type":"string","description":"Identifies the target solution nonce",},"origin":{"type":"string","description":"Identifies the original user that mined the solution nonce",},"signature":{"type":"string","description":"The signed hash of tightly packed variables sha3('delegatedMintHashing(uint256,address)')+nonce+origin_account",}}}
The preparation of a mineable mint packet on a JavaScript client would appear as follows:
functionprepareDelegatedMintTxn(nonce,account){varfunctionSig=web3.utils.sha3("delegatedMintHashing(uint256,address)").substring(0,10)vardata=web3.utils.soliditySha3(functionSig,nonce,account.address)varsig=web3.eth.accounts.sign(web3.utils.toHex(data),account.privateKey)// prepare the mint packet
varpacket={}packet.nonce=noncepacket.origin=account.addresspacket.signature=sig.signature// deliver resulting JSON packet to pool or third party
varmineableMintPacket=JSON.stringify(packet,null,4)/* todo: send mineableMintPacket to submitter */...}
Once the packet is prepared and formatted it can then be routed to a third party that will submit the transaction to the contract’s delegatedMint() function, thereby paying for the transaction gas and receiving the resulting tokens. The pool/third party must then manually payback the minted tokens minus fees to the original minter.
The following code sample exemplifies third party packet relaying:
//received by minter
varmineableMintPacket=...varpacket=JSON.parse(mineableMintPacket)erc918MineableToken.delegatedMint(packet.nonce,packet.origin,packet.signature)
The Delegated Mint Extension expands upon ERC918 realized as a sub-contract:
import'openzeppelin-solidity/contracts/contracts/cryptography/ECDSA.sol';contractERC918DelegatedMintisAbstractERC918,ECDSA{/**
* @notice Hash (keccak256) of the payload used by delegatedMint
* @param _nonce the golden nonce
* @param _origin the original minter
* @param _signature the original minter's elliptical curve signature
*/functiondelegatedMint(uint256_nonce,address_origin,bytes_signature)publicreturns(boolsuccess){bytes32hashedTx=delegatedMintHashing(_nonce,_origin);addressminter=recover(hashedTx,_signature);require(minter==_origin,"Origin minter address does not match recovered signature address [ AbstractERC918.delegatedMint() ]");require(minter!=address(0),"Invalid minter address recovered from signature [ ERC918DelegatedMint.delegatedMint() ]");success=mintInternal(_nonce,minter);}/**
* @notice Hash (keccak256) of the payload used by delegatedMint
* @param _nonce the golden nonce
* @param _origin the original minter
*/functiondelegatedMintHashing(uint256_nonce,address_origin)publicpurereturns(bytes32){/* "0x7b36737a": delegatedMintHashing(uint256,address) */returntoEthSignedMessageHash(keccak256(abi.encodePacked(bytes4(0x7b36737a),_nonce,_origin)));}}
Mineable Token Metadata (Optional)
In order to provide for richer and potentially mutable metadata for a particular Mineable Token, it is more viable to offer an off-chain reference to said data. This requires the implementation of a single interface method ‘metadataURI()’ that returns a JSON string encoded with the string fields symbol, name, description, website, image, and type.
Solidity interface for Mineable Token Metadata:
/**
* @title ERC-918 Mineable Token Standard, optional metadata extension
* @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-918.md
*
*/interfaceERC918MetadataisAbstractERC918{/**
* @notice A distinct Uniform Resource Identifier (URI) for a mineable asset.
*/functionmetadataURI()externalviewreturns(string);}
Mineable Token Metadata JSON schema definition:
{"title":"Mineable Token Metadata","type":"object","properties":{"symbol":{"type":"string","description":"Identifies the Mineable Token's symbol",},"name":{"type":"string","description":"Identifies the Mineable Token's name",},"description":{"type":"string","description":"Identifies the Mineable Token's long description",},"website":{"type":"string","description":"Identifies the Mineable Token's homepage URI",},"image":{"type":"string","description":"Identifies the Mineable Token's image URI",},"type":{"type":"string","description":"Identifies the Mineable Token's hash algorithm ( ie.keccak256 ) used to encode the solution",}}}
Rationale
The solidity keccak256 algorithm does not have to be used, but it is recommended since it is a cost effective one-way algorithm to perform in the EVM and simple to perform in solidity. The nonce is the solution that miners try to find and so it is part of the hashing algorithm. A challengeNumber is also part of the hash so that future blocks cannot be mined since it acts like a random piece of data that is not revealed until a mining round starts. The msg.sender address is part of the hash so that a nonce solution is valid only for a particular Ethereum account and so the solution is not susceptible to man-in-the-middle attacks. This also allows pools to operate without being easily cheated by the miners since pools can force miners to mine using the pool’s address in the hash algorithm.
The economics of transferring electricity and hardware into mined token assets offers a flourishing community of decentralized miners the option to be involved in the Ethereum token economy directly. By voting with hash power, an economically pegged asset to real-world resources, miners are incentivized to participate in early token trade to revamp initial costs, providing a bootstrapped stimulus mechanism between miners and early investors.
One community concern for mined tokens has been around energy use without a function for securing a network. Although token mining does not secure a network, it serves the function of securing a community from corruption as it offers an alternative to centralized ICOs. Furthermore, an initial mining offering may last as little as a week, a day, or an hour at which point all of the tokens would have been minted.
Backwards Compatibility
Earlier versions of this standard incorporated a redundant ‘challenge_digest’ parameter on the mint() function that hash-encoded the packed variables challengeNumber, msg.sender and nonce. It was decided that this could be removed from the standard to help minimize processing and thereby gas usage during mint operations. However, in the name of interoperability with existing mining programs and pool software the following contract can be added to the inheritance tree:
/**
* @title ERC-918 Mineable Token Standard, optional backwards compatibility function
* @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-918.md
*
*/contractERC918BackwardsCompatibleisAbstractERC918{/*
* @notice Externally facing mint function kept for backwards compatibility with previous mint() definition
* @param _nonce the solution nonce
* @param _challenge_digest the keccak256 encoded challenge number + message sender + solution nonce
**/functionmint(uint256_nonce,bytes32_challenge_digest)publicreturns(boolsuccess){//the challenge digest must match the expected
bytes32digest=keccak256(abi.encodePacked(challengeNumber,msg.sender,_nonce));require(digest==_challenge_digest,"Challenge digest does not match expected digest on token contract [ AbstractERC918.mint() ]");success=mint(_nonce);}}
Test Cases
(Test cases for an implementation are mandatory for EIPs that are affecting consensus changes. Other EIPs can choose to include links to test cases if applicable.)