1

I want to create an ERC20 token that prevent all wallets from holding more than 1%, the reason for this is to prevent holders from holding big quantities in one single wallet(they can hold in more than one wallet, it's ok).

This can be done through checking the balance of the receiver before the transaction.

But i certainly need to create some exceptions for this? Because if i want to send a certain exchange let's say 5% of the total supply, or lock 5% in a locker wallet, then i need to make exceptions for those wallets.

1- What is the best way to go through this ?

2- Is it recommended ?

3- Do you think it is safe ?

Thanks for helping

Edit: I think i fond a solution, which is to give only the owner the exception to overcome the 1% rule, this way he can send the other wallets the required amounts.

All opinions is welcome.

1 Answer 1

0

You can certainly do that.

What you will need to do is to add whitelist logic to your contract.

As an admin of your contract, you will keep a whitelist mapping of addresses than can have more than 1% of tokens for any reason. You would need to be able to add/remove addresses from that whitelist at any time.

I will provide you with an idea.

MyERC20 contract extends from ERC20, so it is an ERC20 token contract.

I override the transfer and transferFrom functions to add the ensureNoMoreThan1PercentIfNotWhitelisted modifier on them, so, before sending tokens from one account to another, it checks if the final amount for the receiving account would exceed 1% of the total supply. If it's a whitelisted address, it will allow it to have more than 1% of the total supply. If it's not whitelisted, it will not allow it.

Feel free to do any modifications.

// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol";

contract MyERC20 is ERC20("Abc", "ABC") {

    mapping(address => bool) public whitelist;
    address public admin;
    uint256 public constant initialSupply = 100;

    constructor() {
         admin = msg.sender;
        _mint(admin, initialSupply);
    }

    modifier onlyAdmin() {
        require(msg.sender == admin);
        _;
    }

    // modifier to make sure that only whitelisted addresses can have more than 1% of the total supply.
    modifier ensureNoMoreThan1PercentIfNotWhitelisted(address to, uint256 amount) {

        uint256 toBalance = balanceOf(to);

        uint256 onePercentOfTotalSupply = totalSupply() / 100;

        // If it's not whitelisted to have more than 1% of the total supply and is trying to have more than allowed, revert.
        if(!whitelist[msg.sender]) {
            require((toBalance + amount) <= onePercentOfTotalSupply, "Illegal operation. Account owould have more than 1% of the total supply.");
        }

        _;

    }

    // Overriding `transfer` and `transferFrom` so they cannot be called externally directly
    // and all `transfer` and `transferFrom` calls should pass through the `ensureNoMoreThan1PercentIfNotWhitelisted` modifier.
    function transfer(address to, uint256 amount) public override ensureNoMoreThan1PercentIfNotWhitelisted(to, amount) returns (bool) {
        return super.transfer(to, amount);
    }

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

    function addToWhitelist(address addr) public onlyAdmin {
        whitelist[addr] = true;
    }

    function removeFromWhitelist(address addr) public onlyAdmin {
        delete whitelist[addr];
    }

    // more logic...

}

One problem with this approach would be if you implement burning logic. Because the total supply would reduce and then users could end up with more tokens than the 1% allowed because the calculation of the 1% would be affected by the reduced tokens in circulation due to burning.

For minting I don't think there would be an issue, because when minting users would now have more room to get more tokens and still be below 1%.

Feel free to change anything and add your own logic.

With separate logic for whitelisted users:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol";

contract MyERC20 is ERC20("Abc", "ABC") {

    mapping(address => bool) public whitelist;
    address public admin;
    uint256 public constant initialSupply = 100;

    constructor() {
         admin = msg.sender;
        _mint(admin, initialSupply);
    }

    modifier onlyAdmin() {
        require(msg.sender == admin);
        _;
    }

    modifier whitelisted() {
        require(whitelist[msg.sender], "User is not whitelisted to access this funcionality");
        _;
    }

    // modifier to make sure that only whitelisted addresses can have more than 1% of the total supply.
    modifier ensureNoMoreThan1PercentIfNotWhitelisted(address to, uint256 amount) {

        uint256 toBalance = balanceOf(to);

        uint256 onePercentOfTotalSupply = totalSupply() / 100;

        require((toBalance + amount) <= onePercentOfTotalSupply, "Illegal operation. Account owould have more than 1% of the total supply.");

        _;

    }

    // Overriding `transfer` and `transferFrom` so they cannot be called externally directly
    // and all `transfer` and `transferFrom` calls should pass through the `ensureNoMoreThan1PercentIfNotWhitelisted` modifier.
    function transfer(address to, uint256 amount) public override ensureNoMoreThan1PercentIfNotWhitelisted(to, amount) returns (bool) {
        return super.transfer(to, amount);
    }

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

    function addToWhitelist(address addr) public onlyAdmin {
        whitelist[addr] = true;
    }

    function removeFromWhitelist(address addr) public onlyAdmin {
        delete whitelist[addr];
    }

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

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

    // more logic...

}

Remember this is just an example. More logic should be added, like making sure only whitelisted could get more than 1% of the supply when using any of the transfer functions, etc.

5
  • Thank you for your solution, it is totally clear. And thanks for mentioning the burning logic problem, I'll avoid it. I want please to ask you one question: With the whitelist solution that you propose, isn't this going to use more gas fees for all transactions (because of going through the checking) ? While if i only give the owner the ability to overcome the 1% rule, there will be no checking at all. Am i right in my guessing ? Thanks.
    – Mary
    Sep 20, 2022 at 21:09
  • You are going to let your users trade freely right? Besides the 1%, they should be able to trade their tokens with each other. But, how do you prevent them from getting more than 1% of the total amount? Somehow we need to check and make sure it does not happen on every transfer and transferFrom operation. Sep 20, 2022 at 21:31
  • yes off course the traders need to trade freely, and i know that the transactions need to check for the 1% each time. what i meant in the previous question is the check for the whitelist, let's say there is 10 wallets in the whitelist, doesn't this mean the compiler should go through those 10 wallets in the whitelist on each transaction ? and this could elevate the gas fees. I'm not sure about it, and thanks in advance if you have an answer.
    – Mary
    Sep 20, 2022 at 21:54
  • Aahhh. Yes. Gotcha! You are right. I updated the answer. Take a look. Sep 20, 2022 at 22:02
  • 1
    Thanks for the help, you are a legend!
    – Mary
    Sep 20, 2022 at 22:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.