How to create an ERC20 Token with burn and stop burn feature?

[EDIT @abcoathup] :warning: If you create a fee on transfer or deflationary token (burn a percentage on transfer) this can cause issues when used with other contracts such as: https://medium.com/balancer-protocol/incident-with-non-standard-erc20-deflationary-tokens-95a0f6d46dea
_From: https://forum.openzeppelin.com/t/points-to-consider-when-creating-a-fungible-token-erc20-erc777/2915_

Hi @makerz

I would suggest that the token inherits from the OpenZeppelin implementation of ERC20 and only has to override transfer and transferFrom functions to calculate the amount to burn and then perform the burn prior to doing the transfer of the remaining amount.

I have done a simple example below, though please note I have not done any of the required automated testing.

I recommend that any such contract be fully tested and audited.
For testing, I suggest using the following guide:

MyToken.sol

pragma solidity ^0.5.0;

import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
import "openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol";

/**
 * @title MyToken
 * @dev ERC20 Token example, where all tokens are pre-assigned to the creator.
 * Note they can later distribute these tokens as they wish using `transfer` and other
 * `ERC20` functions.
 */
contract MyToken is ERC20, ERC20Detailed {

    uint256 private _minimumSupply = 2000 * (10 ** 18);

    /**
     * @dev Constructor that gives msg.sender all of existing tokens.
     */
    constructor () public ERC20Detailed("MyToken", "MYT", 18) {
        _mint(msg.sender, 10000 * (10 ** uint256(decimals())));
    }

    function transfer(address to, uint256 amount) public returns (bool) {
        return super.transfer(to, _partialBurn(amount));
    }

    function transferFrom(address from, address to, uint256 amount) public returns (bool) {
        return super.transferFrom(from, to, _partialBurn(amount));
    }

    function _partialBurn(uint256 amount) internal returns (uint256) {
        uint256 burnAmount = _calculateBurnAmount(amount);

        if (burnAmount > 0) {
            _burn(msg.sender, burnAmount);
        }

        return amount.sub(burnAmount);
    }

    function _calculateBurnAmount(uint256 amount) internal view returns (uint256) {
        uint256 burnAmount = 0;

        // burn amount calculations
        if (totalSupply() > _minimumSupply) {
            burnAmount = amount.div(100);
            uint256 availableBurn = totalSupply().sub(_minimumSupply);
            if (burnAmount > availableBurn) {
                burnAmount = availableBurn;
            }
        }

        return burnAmount;
    }
}
2 Likes