ERC balanceOf function
An account address' ERC 20 balance is simply whatever the balanceOf
function says it is.
As you can define the function, you can decide what it says the balance is for each account.
This means that you can do all kinds of wacky things, based on however you decide the contract should operate.
There is nothing special or mystical about an ERC20 token, other than the specification for the required function names.
"Minting" coins
For most contracts minting coins is nothing but adding a number to one variable without subtracting a number from another variable. Conversely burning coins is subtracting a number from one variable without adding it to another variable. Generally the variables that handle the balances are a mapping between the account address and the balance.
Example
/// @notice Will cause a certain `_value` of coins minted for `_to`.
/// @param _to The address that will receive the coin.
/// @param _value The amount of coin they will receive.
function mint(address _to, uint _value) public {
require(msg.sender == owner); // assuming you have a contract owner
balances[_to] += value;
supply += value;
require(balances[_to] >= value && supply >= value); // overflow checks
emit Transfer(address(0), _to, _value);
}
But this is not set in stone and your contract can do anything from within the balanceOf
function that you can possibly think of.
To answer your question specifically, yes, you can have a function which only you can call that will create new tokens out of thin air based upon user actions external to the contract.
Gas costly?
Be aware that calling the mint()
function as defined about will cost you (or someone) gas each time you execute the function to create the new tokens.
Often to alleviate this problem, people will put in "batch" or "airdrop" functionality into the contract so that multiple updates can be done within a single call and thus save gas.
Example:
/// @notice Will cause a certain `_value` of coins minted for `_to`.
/// @param _to The address that will receive the coin.
/// @param _value The amount of coin they will receive.
function mint(address _to, uint _value) public {
require(msg.sender == owner); // assuming you have a contract owner
mintToken(_to, _value);
}
/// @notice Will allow multiple minting within a single call to save gas.
/// @param _to_list A list of addresses to mint for.
/// @param _values The list of values for each respective `_to` address.
function airdropMinting([]address _to_list, []uint _values) public {
require(msg.sender == owner); // assuming you have a contract owner
require(_to_list.length == _values.length);
for (uint i = 0; i < _to_list.length; i++) {
mintToken(_to_list[i], _values[i]);
}
}
/// Internal method shared by `mint()` and `airdropMinting()`.
function mintToken(address _to, uint _value) internal {
balances[_to] += value;
supply += value;
require(balances[_to] >= value && supply >= value); // overflow checks
emit Transfer(address(0), _to, _value);
}
In this example, you can call the airdropMinting function once a day (or whatever interval you feel is adequate) with a list of minting items and it will execute each in turn, saving about 21,000 gas per additional item.
Note: we have redefined mint() to call an internal minting function, and while we could have called the original mint()
function from within mintToken()
, the calling of an external function from internally comes with an additional gas cost.
Note 2: there is a limit on the number of items that you can mint at one time, and this is based off the global gas limit of 8 million gas.