Upgradeable ERC20 token with partial burn on transfer

Hi @costech,


[UPDATE]

First of all, I suggest looking at Points to consider when creating a fungible token (ERC20, ERC777)

Tokens should be appropriately tested and audited, along with ensuring regulatory compliance.

:warning: A deflationary token can cause issues such as this recent issue: https://medium.com/balancer-protocol/incident-with-non-standard-erc20-deflationary-tokens-95a0f6d46dea


I have done a simple example of an upgradeable version of a contract that burns 3% of tokens on transfer that is also mintable and pausable, 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: Test smart contracts like a rockstar

MyToken.sol

pragma solidity ^0.5.0;

import "@openzeppelin/upgrades/contracts/Initializable.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Detailed.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Mintable.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Pausable.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 Initializable, ERC20, ERC20Detailed, ERC20Mintable, ERC20Pausable {

    uint256 private _minimumSupply;

    /**
     * @dev Gives holder all of existing tokens.
     */
    function initialize(address holder, address minter, address pauser) public initializer {
        ERC20Detailed.initialize("MyToken", "MYT", 18);
        ERC20Mintable.initialize(minter);
        ERC20Pausable.initialize(pauser);

        _minimumSupply = 2000 * (10 ** 18);

        _mint(holder, 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.mul(3).div(100);
            uint256 availableBurn = totalSupply().sub(_minimumSupply);
            if (burnAmount > availableBurn) {
                burnAmount = availableBurn;
            }
        }

        return burnAmount;
    }
}

Create

$ oz create
✓ Compiled contracts with solc 0.5.11 (commit.c082d0b4)
? Pick a contract to instantiate MyToken
? Pick a network development
✓ Deploying @openzeppelin/contracts-ethereum-package dependency to network dev-1569294282448
✓ Contract MyToken deployed
All contracts have been deployed
? Do you want to call a function on the instance after creating it? Yes
? Select which function * initialize(holder: address, minter: address, pauser: address)
? holder (address): 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1
? minter (address): 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1
? pauser (address): 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1
✓ Setting everything up to create contract instances
✓ Instance created at 0xA57B8a5584442B467b4689F1144D269d096A3daF
0xA57B8a5584442B467b4689F1144D269d096A3daF

Transfer

$ oz send-tx
? Pick a network development
? Pick an instance MyToken at 0xA57B8a5584442B467b4689F1144D269d096A3daF
? Select which function transfer(to: address, amount: uint256)
? to (address): 0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0
? amount (uint256): 100
✓ Transaction successful. Transaction hash: 0x59452556d29f2452ab9452dc939edc4a59337b399510c29424602c6bc15a5ae8
$ oz call
? Pick a network development
? Pick an instance MyToken at 0xA57B8a5584442B467b4689F1144D269d096A3daF
? Select which function balanceOf(owner: address)
? owner (address): 0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0
✓ Method 'balanceOf(address)' returned: 97
97
1 Like