Simple ERC20 Contract

Hello, first off, i'm new around here so thanks ahead of time for everything! I'm a very new, learning developer. I'm sure you guys hear it all the time these days but I'm putting things together with chatgpt and it seems to be working but I want to confirm before deploying and adding liquidity that things are proper. Looking to deploy on AVAX.

I'm curious if this very simple contract looks safe to deploy and also if the sales tax of 3% is setup properly and sending to the designated address. Here's the code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyToken is ERC20 {
    uint256 private constant _initialSupply = 100e12; // 100 trillion tokens
    uint256 private constant _salesTaxRate = 3; // 3%

    constructor() ERC20("testing84", "84test") {
        _mint(msg.sender, _initialSupply);
    }

    function transfer(address recipient, uint256 amount) public override returns (bool) {
        uint256 taxAmount = (amount * _salesTaxRate) / 100;
        uint256 netAmount = amount - taxAmount;

        _transfer(_msgSender(), recipient, netAmount);
        _transfer(_msgSender(), PASTE ADDRESS TO RECEIVE SALES TAX HERE, taxAmount); // Sending tax to the burn address or another designated address

        return true;
    }
}

I would greatly appreciate you skilled professionals advice. Thanks ahead of time!

I did have a little more "advanced" code that included some more functionality like adding the ownable import and ability to mint more tokens for a fork but I was afraid to deploy it because I don't know if the contract and the investors money would be safe. Here's that code if anyone wants to take a gander please?

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MyToken is ERC20, Ownable {
    uint256 private constant _initialSupply = 69e9; // 69 billion tokens
    uint256 private constant _salesTaxRate = 7; // 7%

    constructor(address initialOwner) ERC20("Eggplant", "EGGP") Ownable(initialOwner) {
        _mint(initialOwner, _initialSupply * 10**decimals());
    }

    function transfer(address recipient, uint256 amount) public override returns (bool) {
        uint256 taxAmount = (amount * _salesTaxRate) / 100;
        uint256 netAmount = amount - taxAmount;

        _transfer(_msgSender(), recipient, netAmount);
        _transfer(_msgSender(), owner(), taxAmount);

        return true;
    }

    // Function to allow the owner to mint more tokens (for potential forking)
    function mint(uint256 amount) external onlyOwner {
        _mint(owner(), amount);
    }
}

For starters, that's not 100 trillion tokens, it is 100 trillion wei tokens, which is equivalent to 1 millionth of a token, i.e., 0.00001 token.

You need to multiply this value by 10 ** 18, or more generally, by 10 ** decimals().

Second, once you implement a tax-mechanism in your token, it will likely to rejected by most if not all DEXs, which means that your token will not be tradable anywhere.

The way to work around this is by allowing yourself (and yourself only) to exclude those DEXs from being subjected to this tax-mechanism.

Wow, that's interesting. So the original code that I actually deployed and came from chatgpt did have it right. This is code that I put together today and it seems it did it incorrectly. Happy I came in here and really appreciate your response!!

This is the original code and it looks correct... Does this look better?

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

contract Token {
    mapping(address => uint) public balances;
    mapping(address => mapping(address => uint)) public allowance;
    uint public totalSupply = 99000000000000 * 10 ** 18;
    string public name = "Test89";
    string public symbol = "Test89";
    uint public decimals = 18;
    
    
    event Transfer(address indexed from, address indexed to, uint value);
    event Approval(address indexed owner, address indexed spender, uint value);
    
    constructor() {
        balances[msg.sender] = totalSupply;
    }
    
    function balanceOf(address owner) public view returns(uint) {
   return balances[owner];
    }
    
    function transfer(address to, uint value) public returns(bool) {
        require(balanceOf(msg.sender) >= value, 'balance too low');
        balances[to] += value;
        balances[msg.sender] -= value;
       emit Transfer(msg.sender, to, value);
        return true;
    }
    
    function transferFrom(address from, address to, uint value) public returns(bool) {
        require(balanceOf(from) >= value, 'balance too low');
        require(allowance[from][msg.sender] >= value, 'allowance too low');
        balances[to] += value;
        balances[from] -= value;
        emit Transfer(from, to, value);
        return true;   
    }
    
    function approve(address spender, uint value) public returns (bool) {
        allowance[msg.sender][spender] = value;
        emit Approval(msg.sender, spender, value);
        return true;   
    }
}

Well, you've changed your original goal, of deploying a taxed-token, so I don't quite understand the purpose of this thread.

In either case, this contract looks like a standard ERC20 token contract, which means that you may as well just take the one on the OpenZeppelin's GitHub repository, and slightly extend it:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyToken is ERC20 {
    constructor() ERC20("Test89", "Test89") {
        _mint(msg.sender, 99000000000000 * 10 ** decimals());
    }
}
1 Like

The goal of this thread was achieved and I highly appreciate your input. After speaking with the community it sounds like sales tax is hated on by pretty much everyone in every case. Now that the goal is changed I will be using your very clean code taken from the repository. Thanks again. As mentioned Im very new to all this and your input helped a ton.

1 Like

For a first-timer, you seem to be doing pretty well (compared with what I've seen round here and all).

So, apparently the community NEEDS me to be able to 'renoune ownership' of the contract so it is a truly decentralized and community driven project. I found this code snippet for doing so but I want to confirm this is correct..

Could I add this snippet to the bottom of the code like so?

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyToken is ERC20 {
    constructor() ERC20("Test89", "Test89") {
        _mint(msg.sender, 99000000000000 * 10 ** decimals());
    }
}
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}

There is nothing to renounce (and nothing centralized in the example that I gave you on my previous comment), unless your contract is ownable in one way or another (and if you inherit this specific contract, then function renounceOwnership is already implemented).

Note that most if not all of the basic stuff that you're gradually finding via ChatGPT and similar, is already implemented (and thoroughly verified by the community) in OpenZeppelin's GitHub repository.

1 Like

So if i understand correctly.. by not having the ownable import the contract is basically already renounced.. Like, i have no control over the contract once it's deployed anyway? Is that right?

I guess they just want to make sure I can't do anything to the contract down the road like make changes to max supply and things like that, would you agree?

Youre a life saver. Thank you SO much!

Do you do consulting on this stuff? Or, perhaps you do more advanced development for people? I'd be very interested to know more.

It sounds like the community would like some protection from whales coming in and buying up all the supply. Curious if this whale protection code would work ok?? Not sure if we can message each other but im not against paying for your expertise!

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyToken is ERC20 {
    uint256 private constant MAX_BUY_LIMIT_PERCENT = 1; // 1% of the total supply

    mapping(address => uint256) private buyLimits;

    constructor() ERC20("TEST", "TEST") {
        _mint(msg.sender, 99000000000000 * 10 ** decimals());
    }

    modifier checkBuyLimit(address recipient, uint256 amount) {
        require(
            (balanceOf(recipient) + amount) <= (totalSupply() * MAX_BUY_LIMIT_PERCENT) / 100,
            "Buyer protection: exceeding maximum buy limit"
        );
        _;
    }

    function transfer(address recipient, uint256 amount) public override checkBuyLimit(recipient, amount) returns (bool) {
        return super.transfer(recipient, amount);
    }
}

By not having your contract inheriting from the ownable contract.
The import statement itself is just a compilation requirement.

Yup, that is a very accurate description!

NP.

I have never heard of such concern, and I don't think that there is one, to be honest.

Perhaps a whale buying up the entire supply is an indication of the token not being financially robust, but that would be at a "different layer of concerns" (meaning that preventing it in the code is probably not the right way to go).