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

I have been working to develop a self-destructive erc20 token with a certain burning rate for two days. I have succeeded in adding the burn function but I would like to add stop burn function to prevent it from burning to 0 because it would be used as a utility token. I need the community to help me achieve this.

1 Like

@makerz Hi, welcome to OpenZeppelin! I am not sure, but how about adding a modifier stop() for your burn function, could you please paste your code, so we can give a more specific solution.

2 Likes

Welcome to the community @makerz :wave:

Can you provide more details on how you intend for the burn/burn rate to work?
It could be helpful if you could also share your contract.

If you are creating a new token, then you may want to consider ERC777. Also if you want to support upgrading you may want to consider ZeppelinOS.

EDIT: I posted my reply and found @Skyge got their first, awesome. Thanks @Skyge

2 Likes

@abcoathup & @Skyge thanks for your willingness to help. here is my code:

pragma solidity ^0.5.0;

interface IERC20 {
  function totalSupply() external view returns (uint256);
  function balanceOf(address who) external view returns (uint256);
  function allowance(address owner, address spender) external view returns (uint256);
  function transfer(address to, uint256 value) external returns (bool);
  function approve(address spender, uint256 value) external returns (bool);
  function transferFrom(address from, address to, uint256 value) external returns (bool);

  event Transfer(address indexed from, address indexed to, uint256 value);
  event Approval(address indexed owner, address indexed spender, uint256 value);
}

library SafeMath {
  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    if (a == 0) {
      return 0;
    }
    uint256 c = a * b;
    assert(c / a == b);
    return c;
  }

  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a / b;
    return c;
  }

  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b <= a);
    return a - b;
  }

  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    assert(c >= a);
    return c;
  }

  function ceil(uint256 a, uint256 m) internal pure returns (uint256) {
    uint256 c = add(a,m);
    uint256 d = sub(c,1);
    return mul(div(d,m),m);
  }
}

contract ERC20Detailed is IERC20 {

  string private _name;
  string private _symbol;
  uint8 private _decimals;

  constructor(string memory name, string memory symbol, uint8 decimals) public {
    _name = name;
    _symbol = symbol;
    _decimals = decimals;
  }

  function name() public view returns(string memory) {
    return _name;
  }

  function symbol() public view returns(string memory) {
    return _symbol;
  }

  function decimals() public view returns(uint8) {
    return _decimals;
  }
}

contract BUNA is ERC20Detailed {

  using SafeMath for uint256;
  mapping (address => uint256) private _balances;
  mapping (address => mapping (address => uint256)) private _allowed;

  string constant tokenName = "BUNA";
  string constant tokenSymbol = "BNA";
  uint8  constant tokenDecimals = 18;
  uint256 _totalSupply = 500000*10**18;
  uint256 public basePercent = 100;
  uint256 public _burnStopAmount;
  uint256 public _lastTokenSupply;
  
  constructor() public payable ERC20Detailed(tokenName, tokenSymbol, tokenDecimals) {

    _mint(msg.sender, _totalSupply);
    _burnStopAmount = 0;
    _lastTokenSupply = 200000 * 10**18;
  }

  function totalSupply() public view returns (uint256) {
    return _totalSupply;
  }

  function balanceOf(address owner) public view returns (uint256) {
    return _balances[owner];
  }

  function allowance(address owner, address spender) public view returns (uint256) {
    return _allowed[owner][spender];
  }

  function findOnePercent(uint256 value) public view returns (uint256)  {
    uint256 roundValue = value.ceil(basePercent);
    uint256 onePercent = roundValue.mul(basePercent).div(10000);
    return onePercent;
  }

  function transfer(address to, uint256 value) public returns (bool) {
    require(value <= _balances[msg.sender]);
    require(to != address(0));

    uint256 tokensToBurn = findOnePercent(value);
    uint256 tokensToTransfer = value.sub(tokensToBurn);

    _balances[msg.sender] = _balances[msg.sender].sub(value);
    _balances[to] = _balances[to].add(tokensToTransfer);

    _totalSupply = _totalSupply.sub(tokensToBurn);

    emit Transfer(msg.sender, to, tokensToTransfer);
    emit Transfer(msg.sender, address(0), tokensToBurn);
    return true;
  }

  function multiTransfer(address[] memory receivers, uint256[] memory amounts) public {
    for (uint256 i = 0; i < receivers.length; i++) {
      transfer(receivers[i], amounts[i]);
    }
  }

  function approve(address spender, uint256 value) public returns (bool) {
    require(spender != address(0));
    _allowed[msg.sender][spender] = value;
    emit Approval(msg.sender, spender, value);
    return true;
  }

  function transferFrom(address from, address to, uint256 value) public returns (bool) {
    require(value <= _balances[from]);
    require(value <= _allowed[from][msg.sender]);
    require(to != address(0));

    _balances[from] = _balances[from].sub(value);

    uint256 tokensToBurn = findOnePercent(value);
    uint256 tokensToTransfer = value.sub(tokensToBurn);

    _balances[to] = _balances[to].add(tokensToTransfer);
    _totalSupply = _totalSupply.sub(tokensToBurn);

    _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value);

    emit Transfer(from, to, tokensToTransfer);
    emit Transfer(from, address(0), tokensToBurn);

    return true;
  }

  function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
    require(spender != address(0));
    _allowed[msg.sender][spender] = (_allowed[msg.sender][spender].add(addedValue));
    emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
    return true;
  }

  function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
    require(spender != address(0));
    _allowed[msg.sender][spender] = (_allowed[msg.sender][spender].sub(subtractedValue));
    emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
    return true;
  }

  function _mint(address account, uint256 amount) internal {
    require(amount != 0);
    _balances[account] = _balances[account].add(amount);
    emit Transfer(address(0), account, amount);
  }

  function burn(uint256 amount) external {
    _burn(msg.sender, amount);
  }
  function _burn(address account, uint256 amount) internal {
    require(amount != 0);
    require(amount <= _balances[account]);
    _totalSupply = _totalSupply.sub(amount);
    _balances[account] = _balances[account].sub(amount);
    emit Transfer(account, address(0), amount);
  }

  function burnFrom(address account, uint256 amount) external {
    require(amount <= _allowed[account][msg.sender]);
    _allowed[account][msg.sender] = _allowed[account][msg.sender].sub(amount);
    _burn(account, amount);
  }
}
1 Like

Hi @makerz
If I understand correctly you burn 1% of tokens being transferred. Is that correct?
At what point do you want to stop burning?
You also have a multiTransfer function.

I added syntax highlighting to your code to make it easier to read.

1 Like

you are right, i want the burning to stop when the total supply reaches 200,000.

1 Like

Maybe you should use two variables, one records the current burning value, and the other one records the reserved value, every time you burn, just computing currentBrunedValue + burnValue , if it is less than or equal to totalSupply - reservedValue, just burn, if not, you only can burn totalSupply - reservedValue - currentBrunedValue.

3 Likes

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;
    }
}
1 Like

thanks @abcoathup I’m going to try this now.

1 Like

the function partial burn and calculate burn amount doesn’t justify the 1% burn per transaction. I’ve been trying to find a way to implement it in the code,I still can’t find a way to do it perfectly.

1 Like

Hi @makerz

Can you elaborate more on what you mean by justify? Do you mean the gas cost?

I mean it doesn’t fulfill the 1% token burn per every transaction.

1 Like

Hi @makerz

The sample code burns 1% of the amount being transferred using either transfer or transferFrom (up until the minimum supply limit is reached).

What other transactions do you have?

Is there something that I have misunderstood?

1 Like

you are awesome!!! I missed somethings up for myself.
so sorry for the confusion.

1 Like

Hi @makerz that’s great.

Please note as I recommended above the contract needs to be fully tested and audited. The testing guide is a great place to start: Test smart contracts like a rockstar

I have marked my reply as the solution.

Feel free to ask (and answer) all the questions that you need.

This is my first time of importing library contracts, is there a way I get the library contract address for of ether scan verification.

1 Like

Hi @makerz

There is no linked library for the contract above as SafeMath only has internal library functions.

https://solidity.readthedocs.io/en/latest/contracts.html#libraries
Libraries can be seen as implicit base contracts of the contracts that use them. They will not be explicitly visible in the inheritance hierarchy, but calls to library functions look just like calls to functions of explicit base contracts ( L.f() if L is the name of the library). Furthermore, internal functions of libraries are visible in all contracts, just as if the library were a base contract. Of course, calls to internal functions use the internal calling convention, which means that all internal types can be passed and types stored in memory will be passed by reference and not copied. To realize this in the EVM, code of internal library functions and all functions called from therein will at compile time be pulled into the calling contract, and a regular JUMP call will be used instead of a DELEGATECALL .

I verified the contract above on Etherscan as a single flat file.
First I flattened using https://github.com/nomiclabs/truffle-flattener then I verified the flattened contract on Etherscan

To make it easier for the rest of the community to answer questions (and for people searching for existing answers) it is best to create a new topic for each question.

If you need more information on verifying do you mind creating a new topic.

A post was split to a new topic: Upgradeable ERC20 token with mint, burn and pause