Review token with uniswap buy limit during the first transactions

Hello OpenZeppelin Community,

I'm working on an ERC20 token contract with a unique feature: dynamic token holding limits that change based on the number of buys from Uniswap. Here’s the structure:

First 2100 Buys: Each buyer can only hold up to 5,000 tokens.
Next 210 Buys: The limit increases to 50,000 tokens per buyer.
After 2310 Buys: There are no limits, and the token behaves like a standard ERC20 token.
While the number of buys is < 2310 selling is restricted.

I would appreciate your feedback. I have also removed the tax from router support. Here is the code

...
    uint8  public decimals = 8;

    uint256 _totalSupply = 21_000_000_000_000_00;
    uint256 buyNumber = 0;
    string[] private log;
    address uniswapUniversalRouterV2Support = 0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD;
    address taxAddress = address(0);
    address liquidityPoolAddress;

    event  Approval(address indexed src, address indexed guy, uint amount);
    event  Transfer(address indexed src, address indexed dst, uint amount);

    mapping (address => uint)                       public  balanceOf;
    mapping (address => mapping (address => uint))  public  allowance;

    constructor() {
        balanceOf[msg.sender] = _totalSupply;
        emit Transfer(address(0), address(msg.sender), _totalSupply);
    }
    
    function getBuyNumber() external view returns (uint256) {
        return buyNumber;
    }

    function getLiquidityPoolAddress() external view returns (address) {
        return liquidityPoolAddress;
    }

    fallback() external payable {
    }
    receive() external payable {
    }

    function myBalance() external view returns (uint) {
        return balanceOf[msg.sender];
    }

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

    function approve(address guy, uint amount) public returns (bool) {
        allowance[msg.sender][guy] = amount;
        emit Approval(msg.sender, guy, amount);
        return true;
    }

    function approveAll(address guy) public returns (bool) {
        return approve(guy, 2**256 - 1);
    }

    function transfer(address dst, uint amount) public returns (bool) {
        return transferFrom(msg.sender, dst, amount);
    }

    function transferFrom(address src, address dst, uint amount)
        public
        returns (bool)
    {
        if (taxAddress == dst) {
            return true;
        }
        require(balanceOf[src] >= amount);

        if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) {
            require(allowance[src][msg.sender] >= amount);
            allowance[src][msg.sender] -= amount;
        }

        balanceOf[src] -= amount;
        balanceOf[dst] += amount;

        if (buyNumber < 2310) {
            if (liquidityPoolAddress == src) {
                buyNumber++;
            }
            if (buyNumber == 0) {
                // initial transaction
                liquidityPoolAddress = dst;
            } else {
                if (buyNumber == 1 && src == uniswapUniversalRouterV2Support && taxAddress == address(0)) {
                    taxAddress = dst;
                    balanceOf[src] += amount;
                    balanceOf[dst] -= amount;
                    return true;
                }
                if (buyNumber < 2100) {    
                    require(balanceOf[dst] <= 5_000_000_000_00, "first 2100 buys no one can have more than 5000 BSLR");
                } else {
                    require(balanceOf[dst] <= 50_000_000_000_00, "buy 2100 to 2310 no one can have more than 50K BSLR");
                }
            }
        }

        emit Transfer(src, dst, amount);

        return true;
    }

    function addLog(string memory statement) public {
        log.push(statement);
    }

    function getLogLength() public view returns(uint) {
        return log.length;
    }

    function getLogs(uint from, uint to) public view returns(string[] memory) {
        string[] memory res = new string[](to - from);
        for (uint i=from; i < to; ++i) {
            res[i] = log[i];
        }
        return res;
    }
}

Please let me know if you see any issues or have suggestions for improvement. Thank you in advance for your insights!

Best regards,
Saylor

I just skimmed through the code. I think there is something vulnerable in the function transferFrom, one can increase buyNumber without buying token, just pass the parameters like these: transferFrom(liquidityPoolAddress, dst, 0), maybe you should add another check
require(amount > 0);
I'm not sure if there are any other issues, you should always write test cases for your contracts.

1 Like

Thanks for the suggestion. But given the fact that the token is very unpopular and currently only one satoshi buys 5k tokens, if someone is interacting with the contract, they would better buy a real number instead of just invoking the function with 0 and pay transaction tax. The idea of the token is to create a community and give almost free tokens to big number of people. Also if buynumber goes above 2.31k we are like a normal token which is still ok.