I'm trying to understand exactly how all these fee on swap tokens work and how they are deployed so I am trying to go through the process of deploying a test one myself and scripting the whole process using hardhat and a local testnet.
I have created a deployment script that deploys the original HATE contracts and it works fine until the point that I try to add liquidity to the Uniswap pair that was created on contract creation. From the looks of it, during deployment createPair is called and an LP pair with no liquidity is created and stored in the contract – you're able to get the address from the UniswapV2Pair
function – this all works fine with no problems and I'm able to get the pair address.
The relevant section of the deploy code looks like this:
//Deploy the contract
const shitcoin = await deploy(name, [routerAddress,feeDecimals, feePercentage, minTokensBeforeSwap, false]);
// BURN 50% of the supply
const pairAddress = await shitcoin.uniswapV2Pair();
const supply = await shitcoin.totalSupply();
const halfSupply = supply.div(2);
const remainingSupply = supply.sub(halfSupply);
await shitcoin.transfer(constants.AddressZero, halfSupply);
//Connect uniswap router to deploying wallet
const router = await uRouter.connect(signer);
//Approve token for use by Uniswap router so that liq can be added
const approved = await shitcoin.approve(routerAddress, constants.MaxUint256);
//Approving the LP token itself also makes no difference
//Tried this bc of SE Q&A - made no diff
const approved2 = await shitcoin.approve(pairAddress, constants.MaxUint256);
const baseCurToAdd = ethers.utils.parseEther("5.0")
const lpAddTx = await router.populateTransaction.addLiquidityETH(
shitcoin.address,
remainingSupply,
0,
baseCurToAdd,
signer.address,
(Date.now() + 100000)
);
await signer.call(lpAddTx);
However I find that lpAddTx causes this error:
- From `hardhat chain`
eth_call Contract call: <UnrecognizedContract> From: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 To: 0x7a250d5630b4cf539739df2c5dacb4c659f2488d
Error: VM Exception while processing transaction: reverted with reason string 'ds-math-sub-underflow'
at <UnrecognizedContract>.<unknown> (0x65556fc49c3a0664e7fbde761fc2ba5d0e6cbbed)
at <UnrecognizedContract>.<unknown> (0x7a250d5630b4cf539739df2c5dacb4c659f2488d)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at HardhatNode.runCall (/Users/vegtam/devspace/crypto-bots/rugger/node_modules/hardhat/src/internal/hardhat-network/provider/node.ts:511:20)
at EthModule._callAction (/Users/vegtam/devspace/crypto-bots/rugger/node_modules/hardhat/src/internal/hardhat-network/provider/modules/eth.ts:353:9)
at HardhatNetworkProvider._sendWithLogging (/Users/vegtam/devspace/crypto-bots/rugger/node_modules/hardhat/src/internal/hardhat-network/provider/provider.ts:127:22)
at HardhatNetworkProvider.request (/Users/vegtam/devspace/crypto-bots/rugger/node_modules/hardhat/src/internal/hardhat-network/provider/provider.ts:104:18)
at JsonRpcHandler._handleRequest (/Users/vegtam/devspace/crypto-bots/rugger/node_modules/hardhat/src/internal/hardhat-network/jsonrpc/handler.ts:188:20)
at JsonRpcHandler._handleSingleRequest (/Users/vegtam/devspace/crypto-bots/rugger/node_modules/hardhat/src/internal/hardhat-network/jsonrpc/handler.ts:167:17)
and from the script itself:
pair 0x65556Fc49C3A0664E7FBDE761FC2ba5d0E6CbBed { data: '0xf305d7190000000000000000000000000e801d84fa97b50751dbf25036d067dcf18858bf00000000000000000000000000000000000000000000001b1ae4d6e2ef50000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000004563918244f40000000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000017af8d63bc4', to: '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D', from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' } ProviderError: Error: VM Exception while processing transaction: reverted with reason string 'ds-math-sub-underflow'
at HttpProvider.request (/Users/vegtam/devspace/crypto-bots/rugger/node_modules/hardhat/internal/core/providers/http.js:50:23)
at GanacheGasMultiplierProvider.request (/Users/vegtam/devspace/crypto-bots/rugger/node_modules/hardhat/internal/core/providers/gas-providers.js:215:38)
at processTicksAndRejections (node:internal/process/task_queues:96:5) error Command failed with exit code 1. info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Where I'm printing the address of the pair and the unsigned transaction for verification (looks correct to me). At first I believed that this may be caused by the fee on transfer being enabled as that caused some problems with the token burn but disabling fees until the final step made no difference.
I also saw this question + answer suggesting that it may be caused because I did not approve the pair token for spending so I added the approve2
call so I tried that but it also made no difference and gave the same error. That said, I'm also a bit confused why this would even be the case because when adding liquidity the actual token is the thing being spend not the LP token. I couldn't really find any more info about this in the Uniswap docs
I'm thinking it could be related to the fact that I am trying to do this on a hardhat local blockchain being forked from mainnet but it seems that if that was the issue that I wouldn't be able to create a uniswap pair in the first place.
I have also tried using the addLiquidity
function instead of addLiquidityETH
and passing in the wETH address but ran into the same error.
Has anyone experienced this problem before or know what I could be doing to debug the issue?
Best Answer
The
UniswapV2Pair.sol
fails in themint
method, called by the contractUniswapV2Router02.sol
during theaddLiquidityETH
/addLiquidity
method. The problem is that the pair requires a small amount of balance of both tokens, such that a minimum amount of liquidity can be added.In short, just transfer more than 1000 wei of each token to the pair address and
addLiquidityETH
/addLiquidity
will work.Longer version:
The failing line is marked with
XXX
:when deploying new tokens, their reserve is 0 and also the balance of the token held by the pair address is 0. This means in the above
mint
function, the variablesamount0
andamount1
are also 0.But
sqrt(amount0 * amount1) - MINIMUM_LIQUIDITY > 0
, otherwise one gets thatds-math-sub-underflow
error.With
MINIMUM_LIQUIDITY = 10**3
andamount0 * amount1 > MINIMUM_LIQUIDITY**2
, one can calculate that this requirement is fulfilled when the pair address holds just more of 1000 wei of each token.