Late to the question, but since it's still unanswered I'll write a solution; it should help people still struggling with UniswapV2 math.
So, the price impact is usually defined as the relative variation of price due to a swap. If the price initially was pi
and finally pf
, the price impact will be PI = (pf-pi)/pi.
By price we are considering the nominal price (no fees), which is the ratio between the reserves, so:

with in
and out
indicating the reserves of input and output tokens respectively.
The final reserves are given by:


with x_in
the input amount, and x_out
the output amount, which can be given by the following formula (taken from uniswap's code):

Putting all the formulas together we can take x_in
as a function of the price impact and the initial reserves:

with:

Writing it all in solidity this is how it looks:
pragma solidity ^0.8.0;
// https://github.com/Uniswap/v2-core/blob/v1.0.1/contracts/libraries/Math.sol
import '/path/to/Math.sol';
// https://github.com/Uniswap/v2-periphery/blob/master/contracts/libraries/UniswapV2Library.sol
import '/path/to/UniswapV2Library.sol';
contract UniswapV2Helper {
address private constant UniV2Factory = ...;
address private constant WETH = ...;
uint256 private constant PRICE_IMPACT_DECIMALS = 18; // for integer math
// @notice Get max amount of tokens without impacting the price more than maxPriceImpact
// Swap output is WETH
// @param tokenIn Address of ERC20 to swap
// @param maxPriceImpact Price impact with 'PRICE_IMPACT_DECIMALS' decimals
function getMaxAmountIn(address tokenIn, uint256 maxPriceImpact) public returns(uint256) {
require(maxPriceImpact < 10**PRICE_IMPACT_DECIMALS, "maxPriceImpact too many decimals");
// get reserves
(uint256 reserveIn, /*uint256 reserveOut*/) = UniswapV2Library.getReserves(UniV2Factory, tokenIn, WETH);
return _getMaxAmountIn(reserveIn, maxPriceImpact);
}
function _getMaxAmountIn(uint256 R, uint256 maxPriceImpact) public pure returns(uint256) {
uint256 p = 10**(PRICE_IMPACT_DECIMALS*2)/(maxPriceImpact + 10**PRICE_IMPACT_DECIMALS);
uint256 temp = 9*p*p + 4*1000*997*p*(10**PRICE_IMPACT_DECIMALS);
uint256 sqrt = Math.sqrt(temp);
return (sqrt - 1997*p)*R/(2*997*p);
}
}
Hopefully I didn't make some math mistakes. Hope it helps someone!
reserveB - getAmountOut(x) = reserveB B*0.97
whereas reserveB = token reserve. getAmountOut : github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/…