Calculate ERC20 token buy/sell tax

1.6k Views Asked by At

I am trying to implement a method for calculating token buy/sell tax. The one that devs implement it in the transfer() function.

I've done some research and there are 2 options.

  1. eth_call - simulate a swap, calculate the difference
  2. deploy the ERC20 smart contract on a local hardhat/ganache, execute swap and see the difference

The eth_call seems to be better for me but I'm facing some issues.

 // Encode function data
        const encodedData = router.interface.encodeFunctionData('swapExactETHForTokens', [
            0,
            path,
            to,
            deadline,
        ]);

        // Get amountsOut using eth_call, performs a SIMULATION
        const callResult = await this.provider.call({
            data: encodedData,
            value: parseUnits('2', 18),
            to: constants.UNISWAP_ROUTER_ADDRESS,
        });

        console.log('callResult', callResult);

        // Decode result
        const decodedResult = router.interface.decodeFunctionResult(
            'swapExactETHForTokens',
            callResult
        );

it does not return the actual amount including taxes, the response is just an array of the amounts out first is amount of ETH and second is the token amount. if I set the amountIn 0, then I get 0% tax, if I increase it then the amountOut of token decreases.

3

There are 3 best solutions below

0
Uğur Özpınar On BEST ANSWER

To get a more accurate representation of the tax, you might need to make use of the Uniswap V2 Router's getAmountsOut function instead of swapExactETHForTokens. getAmountsOut is designed to get the expected output amounts given an input amount and the token path. You can then compare the estimated output amount with the actual output amount after the swap to determine the tax.

const uniswapV2Router = new ethers.Contract(
  constants.UNISWAP_ROUTER_ADDRESS,
  ['function getAmountsOut(uint amountIn, address[] memory path) public view returns (uint[] memory amounts)'],
  this.provider
);

// Replace 'tokenAmount' and 'path' with appropriate values
const amountsOut = await uniswapV2Router.getAmountsOut(tokenAmount, path);

console.log('Estimated output amount:', amountsOut[amountsOut.length - 1]);
0
biscuit_delicious On

You're trying to guess how many tokens you'll get when you swap on Uniswap (or something like it), but you also want to include any transfer fees in your calculations. However: you've found that using an eth_call doesn't always give you the right answer. This is because eth_call is like a practice run - it doesn't make any actual changes, so it may not correctly show what happens when tokens have a transfer fee.

If the token includes a fee automatically, the result from a practice call won't match the real transaction result, since the eth_call doesn't practice the fee deduction.

Here are a few approaches:

i Set the tax rate yourself: If you know the token tax rate and it doesn't change, you could just subtract it from your swap simulation result. But remember, this could break if the token contract changes its tax rates.

ii Try it out locally: You could deploy the contract to a local version of Ethereum, like Ganache or Hardhat. Then you could run the swap function and look at the balance changes. This could give you a more accurate answer, but it might be slower and need more resources.

iii Use event data: Some token contracts produce events that could help. For example, they might make a Transfer event with the actual number of tokens transferred, including the tax.

iiii Do the math yourself: If the tax is a percentage, you could first calculate the amount of tokens received without tax and then subtract the tax.

const { ethers } = require("hardhat");

async function main() {
  const Token = await ethers.getContractFactory("yourToken");
  const token = await Token.deploy();
  await token.deployed();
  
  const initialBalance = await token.balanceOf(account);

  // Swap
  await token.swapExactETHForTokens(...args);
  
  const finalBalance = await token.balanceOf(account);

  // The difference is the amount of tokens received, accounting for tax
  const actualTokensReceived = finalBalance.sub(initialBalance);

  console.log('Tokens received:', ethers.utils.formatUnits(actualTokensReceived, 'ether'));
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

0
Lucas Hendren On

Prior to researching this, to my knowledge it wasnt fully possible to use eth_call to calculate the token tax because it doesnt actually transfer the token so you cant see the final result. Though you maybe able to use web3.eth.call or do some more complicated calls involving dexes but i have not verified this yet.

That being said, while researching it I came across a few places and a specific blog that have claimed to do it but its very complicated, complex and behind a paywall so I was not able to fully test it, you can take a look and use at your discretion

Overall I would suggest option 2, setting up a local hardhat/ganache. Im providing a link to code in python that you can use to base any solution off of