DEV Community

Cover image for How to create a BEP-20 tax token with marketing and liquidity fees
d joker
d joker

Posted on Edited on

How to create a BEP-20 tax token with marketing and liquidity fees

A tax token's marketing wallet is usually described as a percentage, not a schedule: "2% of every trade goes to marketing." That tells you how much leaves a trade. It does not tell you when the wallet receives anything. The Marketing + liquidity token type on GTokenTool answers the second question in the contract, and that answer is the most interesting part of the design: fees accumulate as tokens inside the contract, and a threshold decides when they move.

This is a field-by-field walkthrough of the deploy form, followed by the swap routine itself — because the routine is what separates this type from a token that just advertises a wallet address.

What the type says it is

The chip reads Marketing + liquidity token. The panel under it is short:

"Buy and sell fees flow into the marketing wallet and liquidity pool, with an optional burn allocation."

Best for is "Community and project tokens that need an ongoing marketing budget or automatic liquidity support." The two Keep in mind lines are the ones that decide whether it fits:

"High taxes can discourage trading. Test market acceptance before launch."
"Fees do not guarantee returns. The project team still needs to run the token."

The English form also puts a warning under the type chips: "Automated risk scanners often flag this token type as high risk after launch. Proceed only if you understand the risks and have tested the contract." That banner appears for every non-standard type, so it is not specific to this one. It is there because a token that taxes trades and holds an owner key looks, to a scanner, exactly like a token that taxes trades and holds an owner key. The only way to argue with that is to show the contract and the settings, which is what this article does.

Underneath the chip, the form notes the constructor takes 5 argument groups. That is accurate: name/symbol, the numeric group, the address group, the flags, and the referral ladder. The form hides most of that behind switches.

Three destinations per side, not four

Turn on Buy / sell fee settings and you get two columns — Buy fee, Sell fee — with three percentages each:

Field Hint on the form What actually happens
Marketing fee "Automatically sends to the marketing wallet" Accumulates as tokens, later swapped to the quote token, then sent to the marketing wallet
Burn fee "Sends to the burn address, making the supply deflationary" Sent to address(0xdead) during the trade itself — it never reaches the contract and never gets swapped
Liquidity fee "Automatically adds to the liquidity pool" Accumulates, later half of that slice is paired with quote tokens, and the LP is minted to the marketing wallet

There is no fourth rewards column. The holder-rewards and burn types pay people in the quote token or a reward token; this type pays no holders. Its output is a marketing balance and a deeper pool, which is a different promise and worth saying plainly.

The burn column is the one that behaves most differently. It is taken at trade time:

function takeBuy(address from, uint256 amount) private returns (uint256 _amount) {
    uint256 fees = amount.mul(getBuyFee()).div(10000);
    _basicTransfer(from, address(this),
        fees.sub(amount.mul(feeSettings.buyBurnFee).div(10000)));
    if (feeSettings.buyBurnFee > 0) {
        _basicTransfer(from, address(0xdead),
            amount.mul(feeSettings.buyBurnFee).div(10000));
    }
    _amount = amount.sub(fees);
}
Enter fullscreen mode Exit fullscreen mode

One detail worth knowing before you promise "deflation" to anyone: totalSupply() in this contract returns the original supply forever. The burn moves tokens to 0xdead, which takes them out of circulation but does not change the number the contract reports. A scanner that subtracts the dead address balance will show the reduced figure; the contract's own supply getter will not.

The trigger is the design

The marketing and liquidity shares stay in the contract as tokens. Every transfer checks whether the contract's balance has reached swapTokensAtAmount:

uint256 contractTokenBalance = balanceOf(address(this));
bool canSwap = contractTokenBalance >= feeSettings.swapTokensAtAmount;
if (canSwap && from != address(this) && from != _uniswapV2Pair
    && from != owner() && to != owner() && _poolReady()) {
    (bool ok,) = address(this).call(
        abi.encodeWithSelector(this.transferSwap.selector, contractTokenBalance)
    );
    if (!ok) _sendFailedFeesToMarketing();
}
Enter fullscreen mode Exit fullscreen mode

Five things fall out of that condition, and each one explains something you will see after launch:

  1. The default threshold is 0.01% of supply. initialize sets swapTokensAtAmount to totalSupply / 10,000. For a 1,000,000-token supply that is 100 tokens. Below it, nothing moves.
  2. Buys do not trigger the swap. from is the pair on a buy, and the pair is excluded. Sells can trigger it, and so can ordinary wallet-to-wallet transfers.
  3. The owner's own transfers never trigger it. Both from and to are checked.
  4. The pool has to exist first. _poolReady() means both reserves are greater than zero. Before liquidity is added, fees just pile up.
  5. The swap runs before the current trade is charged. The balance is read at the top of _transfer, so the fee from the trade that triggered the swap is processed on a later trade.

Point 5 is the reason a marketing wallet can look silent right after launch: the first trade that crosses the threshold pays to process the fees that were already there, not its own.

If the swap reverts, the contract does not retry forever. It calls _sendFailedFeesToMarketing(), which hands the accumulated tokens plus any quote token it already holds to the marketing wallet. Practical translation: on a thin pool, your marketing wallet gets tokens instead of the quote token, and the liquidity share does not become LP. That is the failure mode behind "marketing stopped receiving funds even though fees keep accruing."

What the swap does with the balance

The routine, cut down to the arithmetic that matters:

uint _denominator = feeSettings.buyMarketingFee.add(feeSettings.sellMarketingFee)
    .add(feeSettings.buyLiquidityFee).add(feeSettings.sellLiquidityFee);

uint256 tokensForLP = contractTokenBalance
    .mul(feeSettings.buyLiquidityFee.add(feeSettings.sellLiquidityFee))
    .div(_denominator)
    .div(2);

swapTokensForTokens(contractTokenBalance.sub(tokensForLP));
...
addLiquidity(tokensForLP,
    tokenBal * (feeSettings.buyLiquidityFee.add(feeSettings.sellLiquidityFee)) / _denominator);
TokenSafe.tryTransfer(_token, _marketAddr, IERC20(_token).balanceOf(address(this)));
Enter fullscreen mode Exit fullscreen mode

In words: the accumulated balance is split by the combined marketing and liquidity rates, with buy and sell added together; the liquidity slice is halved again, and only that half is paired with fresh quote tokens to mint LP. The rest of the tokens are swapped, and the quote token left over goes to the marketing wallet. The LP is minted to the marketing wallet, not locked in the contract — so the liquidity fee grows a position the team controls, which is either the point or a governance question you should answer before launch.

A worked example

Take a 1,000,000 supply, which sets the default threshold at 100 tokens. Set both sides to 3%: 2% marketing, 0.5% burn, 0.5% liquidity.

A 20,000-token buy pays 600 tokens in tax: 400 marketing, 100 burn, 100 liquidity.

  • The 100 burn tokens go to 0xdead immediately. Circulating supply drops; the contract's totalSupply does not.
  • The remaining 500 sit in the contract. That is already above the 100-token threshold, so the next eligible transfer — a sell or a wallet-to-wallet move, not the next buy — processes them.
  • Of those 500, one fifth (100 tokens) is the liquidity slice. Half of that slice — 50 tokens — stays as tokens and is paired with quote tokens to mint LP to the marketing wallet.
  • The other 450 tokens are swapped, and the quote proceeds are split 4:1 between marketing and the LP pairing. The marketing wallet receives the 4/5 in quote tokens.

After that trade, the marketing wallet holds quote tokens and a little more LP, the pool is slightly deeper, and 100 tokens are gone for good. None of it happens on the buy that generated the fees.

The numbers the form will not let you submit

Three rules, each enforced before the wallet is asked to sign:

  • A side cannot reach 100%. The form counts the three fee columns plus any referral rewards, and blocks the side at 100% — "Base buy tax plus referral rewards must stay below 100%". At 100% the trader receives nothing, which is why the form stops you before you sign.
  • At least one fee must be non-zero when the switch is on. Opening the switch and leaving all six fields at 0 ships a token with a tax switch and no tax. The error is "Set at least one fee when fees are enabled".
  • Two decimals of precision. Fees are entered in percent and written on-chain as basis points, so 0.01% is the smallest step. The input takes two decimals; don't design a fee around 0.005%.

The buy and sell columns are independent. A 3% buy and a 5% sell is a valid setup, and sometimes a deliberate one — but it means the pool gets fed at two different rates depending on direction.

The other five switches

This type comes with six switches. Buy / sell fee settings is the one above; the other five behave like this on this token.

Anti-bot blacklist — "Blacklists addresses that trade within N seconds of launch — stops sniping bots." It only works when the buy fee is greater than 0. The countdown starts at the launch time; with an immediate launch, it starts at deployment. The form's own warning: "If liquidity is added after that window ends, the protection will no longer stop snipers after the pool opens." A 60-second window and a pool added twenty minutes later protect nothing — schedule the launch for when you will actually add liquidity.

Grow the holder count — "Sends a share of every trade to a few random addresses." In the contract, each of those addresses receives one base unit of the token, and the addresses come from a counter-derived sequence rather than a random draw. It is a holder-count tool, not a distribution: on an 18-decimal token each address gets 0.000000000000000001. Set it for the optics, not for the economics.

Enable trading limits — max buy, max holding and max sell, in whole tokens. When the switch is off, all three default to the total supply, which is why turning the switch off does not mean "no limit" in the contract. Setting all three to 0 freezes trading in both directions.

Transfer fee — "Enter 0 to turn it off. Enter 3 for a 3% transfer tax; the tax goes into the contract and is distributed using the trading-fee allocation." Wallet-to-wallet transfers become another source for the same marketing/liquidity split. With the transfer tax on and both marketing and liquidity fees at 0, the contract has no split to apply and sends the accumulated balance to the marketing wallet instead.

Referral rewards — a ladder of up to three levels, and a binding rule that catches people out: a user binds by sending exactly one whole token to their referrer. The direction switch decides whether rewards come off buys, sells or both. Any unclaimed share of the ladder goes to the marketing wallet, and the whole ladder counts toward the per-side 100% cap.

Launch settings ship switched off

"Turn this on to launch immediately or schedule a time. Leave it off to keep trading closed after deployment and open it later from the token console."

Off means what it says: the token deploys, the pair contract is created, and both sides of the pair are blocked until a launch time exists. The two on modes:

  • Launch immediately — "Trading opens as soon as deployment is confirmed. Once liquidity is added, users can trade right away. This cannot be reversed."
  • Schedule a time — "Trading opens automatically then. It cannot be closed after launch, and the launch time can only be moved earlier, never later."

The launch time is written during deployment, inside the same constructor call. There is no second transaction to sign for it, and no way to close trading afterward. If you leave launch off and later renounce ownership, the console refuses: renouncing requires a launch time first, because a token with no launch time and no owner can never trade.

What is fixed, what is editable

The Exchange / router and Quote token are fixed at deploy. The constructor creates the pair against that quote token, and the contract has no setter for either one. Getting them wrong means deploying again.

The hint on the quote token is worth following literally: "Usually WBNB, WETH, or a stablecoin." The routine swaps and pairs in that one token, so a quote token with its own transfer tax changes the amounts that arrive at the pairing step. Use a standard, tax-free ERC-20.

From the token console you can still edit buy and sell fees, the marketing wallet, the swap threshold, limits, transfer tax, the holder-count number, blacklist and whitelist, and the referral ladder while you own the contract. Name, symbol, supply and decimals are not editable. Two warnings from the console copy:

  • After Change marketing wallet, remove the old address from the whitelist yourself. New marketing wallets are whitelisted automatically; old ones are not cleaned up, and they keep trading tax-free until you say otherwise.
  • Renounce ownership is permanent. Fees that have not reached the threshold will keep sitting in the contract, and you will no longer be able to lower the threshold to release them.

The threshold itself lives in the console under Trading controls → Reward trigger threshold. The label says "reward", but on this token the field only decides when the swap runs. If the marketing wallet never receives anything, lower it before you conclude the contract is broken. The console note puts the default at "one ten-thousandth of the total supply."

What deployment costs

On BNB Chain the deploy fee for this type is 0.05 BNB, and it is charged inside the same transaction that deploys the contract — one signature covers the platform fee and the deployment, and gas is paid on top. The page shows the exact figure for whichever network you selected before you sign, and the BNB Chain testnet charges a nominal 0.000001 BNB so you can walk the whole flow.

Before the transaction is sent, the call is simulated with your actual arguments. The FAQ on the page answers the obvious question — "Do I pay if the deployment fails?" — with "No. Invalid settings are shown before your wallet asks you to sign, and no transaction is sent."

Two optional extras:

  • Vanity address. The token is deployed through CREATE2, so the address is known before deployment and you can scan for an address that ends in characters you choose. It costs no gas, and you still deploy once.
  • Automatic source verification. After deployment the platform attempts to verify the contract on the explorer, which is what makes the fee settings and the swap routine readable to anyone who checks.

Before you deploy

A short checklist, in the order the mistakes usually happen:

  1. Decide the quote token and the chain first. Both are fixed at deploy, and the deploy fee is charged on the chain selected in the page header, not in your wallet.
  2. Model the tax against a realistic pool. A 3% tax on a pool with $2,000 of liquidity moves very little money, and the swap itself incurs slippage on the way out.
  3. Set the threshold deliberately. 0.01% of supply is the default; a large supply makes that number large in absolute terms, and a small pool may take a long time to clear it.
  4. Plan the launch around liquidity. If you use the anti-bot window, the launch time should match the moment you add liquidity.
  5. Run it on a testnet once. Deploy with launch off, open it from the console, add a little liquidity, make a trade, and watch the contract balance and the marketing wallet respond. The mechanics in this article are visible on-chain from that run.

The creator is at gtokentool.com/en/tokenfactory, the field-by-field guide for all seven types at /en/guide/create-token, the console you will live in afterward at /en/managetokens, and the liquidity step at /en/createLiquidityAndBuy. Fees and prices above are BNB Chain's at the time of writing; the same form runs on the other networks the site lists, and the number on the page is the one that applies to the chain you selected.

Taxes are a transfer from traders to the destinations you configure. This type automates the transfer; it does not create the volume, the buyers, or the returns.

Top comments (0)

⌂ 换网址