Back to Blog 

UniswapDeFiSolidityWeb3 Security
Uniswap V2 Router Explained — addLiquidity Line by Line
10 min
In today's article, we'll deep dive into the heart of Uniswap's V2 Router smart contract, exploring the
addLiquidity function line by line.But there's more — after the in-depth analysis of the code, we'll raise some potential risks of the protocol and their impact on users.
As part of the Uniswap ecosystem, the V2 Router plays a crucial role in facilitating liquidity provision. Our focus today, the
addLiquidity function, is what enables users to supply assets into the liquidity pool — an essential action for the operation of the Uniswap exchange. For a broader look at the full Uniswap V2 architecture, security pitfalls, and fork risks, see our companion article on Uniswap V2 Security.Video walkthrough
You can watch this content on YouTube if you prefer a video format:
The addLiquidity function
Let's jump into the code and dissect the
addLiquidity function found in the UniswapV2Router02.sol contract.Parameters
tokenA,tokenB(address): The addresses of the ERC-20 tokens being provided as liquidity.amountADesired,amountBDesired(uint): The desired amounts oftokenAandtokenBthe liquidity provider wants to add to the pool.amountAMin,amountBMin(uint): The minimum amounts oftokenAandtokenBthe liquidity provider is willing to add. These values serve as a safeguard against price slippage between the initiation of the transaction and its execution.to(address): The address where liquidity tokens will be sent. Typically, this is the address of the liquidity provider.deadline(uint): A timestamp after which the transaction will revert, ensuring the transaction is executed in a timely manner.
Modifiers
ensure(deadline): A custom modifier declared in the contract that checks the currentblock.timestampagainst the provideddeadline, reverting if the deadline has passed.
Return values
amountA,amountB(uint): The actual amounts oftokenAandtokenBdeposited into the liquidity pool.liquidity(uint): The amount of LP tokens minted and sent to thetoaddress.
Function execution: step by step
Step 1 — Call the internal _addLiquidity
The first line of the external
addLiquidity function calls an internal _addLiquidity function. Let's look inside.Step 2 — Check if the pair exists
Inside
_addLiquidity(), the contract checks whether a pair for these two tokens already exists. If it doesn't, it creates a new one:1if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {2 IUniswapV2Factory(factory).createPair(tokenA, tokenB);3}
Step 3 — Get the current reserves
Next, the contract fetches the existing reserves (
reserveA and reserveB) from the UniswapV2Pair contract:1IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves()
These reserves represent the current balance of each token in the pool and are critical for calculating the optimal amounts to deposit.
Step 4 — Calculate the optimal amounts
After retrieving the reserves through
UniswapV2Library, the contract runs a series of checks to determine how much of each token to actually deposit.If the pair is newly created (reserves are zero),
amountA and amountB are simply the desired amounts passed as parameters:1(amountA, amountB) = (amountADesired, amountBDesired);
If the pair already exists, the contract calculates the optimal
amountB based on the current ratio:1amountBOptimal = amountADesired.mul(reserveB) / reserveA;
If
amountBOptimal is less than or equal to amountBDesired, it returns:1(uint amountA, uint amountB) = (amountADesired, amountBOptimal);
Otherwise, it calculates
amountAOptimal the same way and returns:1(uint amountA, uint amountB) = (amountAOptimal, amountBDesired);
This logic ensures the tokens are deposited in the correct ratio to match the pool's current price, protecting against unnecessary slippage.
How LP token liquidity is calculated
Now we know how
amountA and amountB are determined. Let's see how the liquidity return value is calculated.Deploying the pair with CREATE2
The contract uses the
pairFor function from UniswapV2Library to deterministically compute the address of the pair contract using the CREATE2 opcode — without making any external calls:1pair = address(uint(keccak256(abi.encodePacked(2 hex'ff',3 factory,4 keccak256(abi.encodePacked(token0, token1)),5 hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code hash6))));
This
pairFor function returns the address of the deployed (or to-be-deployed) pair contract.Minting LP tokens
When you add liquidity, the pair contract mints LP tokens. When you remove liquidity, LP tokens get burned.
First, we get the pair address:
1address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
Then, we mint the ERC-20 LP tokens and calculate the liquidity returned:
Get the DeFi Protocol Security Checklist
15 vulnerabilities every DeFi team should check before mainnet. Used by 30+ protocols.
No spam. Unsubscribe anytime.
1liquidity = IUniswapV2Pair(pair).mint(to);
Inside the
mint function, the pair contract stores balances as standard ERC-20 state:1uint balance0 = IERC20(token0).balanceOf(address(this));2uint balance1 = IERC20(token1).balanceOf(address(this));
The LP tokens you receive represent your proportional share of the pool. These are fully transferable ERC-20 tokens that can be redeemed later for the underlying assets.
Practical implications
Now that we understand the mechanics, let's explore how adding liquidity affects the broader Uniswap ecosystem, what benefits liquidity providers receive, and what risks they face.
Impact on the liquidity pool
Liquidity addition: The
addLiquidity function is the gateway for funding liquidity pools. When called, it updates the reserves by adding the specified amounts of both tokens, increasing the total liquidity available. This is essential for enabling trades and reducing slippage.
Price stability: Well-funded pools can absorb larger trades without significant price impact. By allowing users to continuously add liquidity, the function contributes to more stable exchange rates.

Token pair availability: The function also facilitates the availability of different token pairs on Uniswap by enabling users to create entirely new pools or add liquidity to existing ones.
Benefits for liquidity providers
- LP tokens: Liquidity providers receive LP tokens representing their share of the pool. These tokens can be redeemed for the underlying assets at any time.
- Trading fees: LPs earn a portion of the 0.3% trading fee on every swap that occurs in their pool, proportional to their share.
- Arbitrage opportunities: LPs can potentially benefit from arbitrage opportunities arising from price differences between Uniswap and other exchanges.
Potential risks
- Impermanent loss: This is the most significant risk. It occurs when the price ratio of the tokens in the pool changes compared to when they were deposited. If one token's price rises significantly relative to the other, LPs may incur a loss when withdrawing — they end up with more of the depreciated token and less of the appreciated one. For a deeper analysis of AMM-specific risks, see our AMM Security Foundations series.
- Smart contract risks: There is always a risk of bugs in smart contract code. Even though Uniswap's contracts have been audited extensively, the risk can never be entirely eliminated. This is why comprehensive smart contract audits are critical for any DeFi protocol.
- Regulatory risks: The regulatory environment around DeFi and liquidity provision is still evolving, which could introduce compliance risks for participants.
- Price volatility: High price volatility directly affects the amounts of assets LPs receive upon withdrawal, compounding the effects of impermanent loss.
Conclusion
We've walked through exactly how adding liquidity works under the hood on Uniswap V2 — from the initial parameter validation, through the reserve-based amount calculations, all the way to the
CREATE2 pair address derivation and LP token minting. Understanding these internals is essential for anyone building on, integrating with, or auditing DeFi protocols.If you're interested in the broader Uniswap architecture across versions, check out our full series: V1, V2, V3, and V4.
Get in touch
At Zealynx, we specialize in auditing DeFi protocols — from AMM routers and liquidity pool logic to complex token integrations. Whether you're building a new DEX, forking Uniswap, or need a security review of your liquidity management contracts, our team is ready to assist — reach out.
Want to stay ahead with more in-depth analyses like this? Subscribe to our newsletter and ensure you don't miss out on future insights.
FAQ: Uniswap V2 Router — addLiquidity
1. What does the addLiquidity function actually do?
The
addLiquidity function in the Uniswap V2 Router allows users to deposit two ERC-20 tokens into a liquidity pool. It calculates the optimal amounts based on the pool's current reserves, transfers the tokens, and mints LP tokens representing the provider's share of the pool.2. What is the purpose of the amountMin parameters?
The
amountAMin and amountBMin parameters protect against price slippage. Between the time a transaction is submitted and when it's executed, the pool's price ratio may change. These minimum values ensure the transaction reverts if the actual deposit amounts fall below the provider's acceptable threshold.3. Why does Uniswap use CREATE2 for pair addresses?
CREATE2 allows the contract to deterministically compute the address of a pair contract without making any external calls. This means any contract or user can calculate a pair's address off-chain from just the factory address and the two token addresses, which is more gas-efficient than calling the factory's
getPair function on every interaction.4. What is impermanent loss and when does it occur?
Impermanent loss occurs when the price ratio of the two tokens in a pool changes relative to when liquidity was deposited. The larger the divergence, the greater the loss. It's called "impermanent" because the loss only becomes realized when liquidity is withdrawn — if prices return to the original ratio, the loss disappears. However, in practice, prices rarely return exactly, making it a real cost for liquidity providers.
5. What happens if a pair doesn't exist yet when addLiquidity is called?
If the pair doesn't exist, the internal
_addLiquidity function calls IUniswapV2Factory(factory).createPair(tokenA, tokenB) to deploy a new pair contract. Since no reserves exist yet, the desired amounts are used directly without any ratio adjustment.6. How are LP tokens different from regular ERC-20 tokens?
LP tokens are standard ERC-20 tokens — they can be transferred, traded, and used in other DeFi protocols. What makes them special is that they represent a proportional claim on the underlying assets in a liquidity pool. When you burn (redeem) LP tokens, you receive back your share of both tokens in the pool, plus any accumulated trading fees.
Glossary
| Term | Definition |
|---|---|
| Liquidity Pool | A smart contract holding reserves of two tokens that enables automated trading via a constant product formula. |
| LP Tokens | ERC-20 tokens representing a liquidity provider's proportional share of a pool's reserves. |
| Impermanent Loss | The temporary loss LPs experience when the price ratio of pooled tokens diverges from the deposit-time ratio. |
| CREATE2 | An EVM opcode that deploys contracts to deterministic addresses based on the deployer, salt, and init code hash. |
| Slippage | The difference between the expected price of a trade and the actual execution price. |
Get the DeFi Protocol Security Checklist
15 vulnerabilities every DeFi team should check before mainnet. Used by 30+ protocols.
No spam. Unsubscribe anytime.


