Uniswap: A Deep Dive Into Its Data and Control Flows

Anirudh Makhana
Coinmonks
4 min readAug 24, 2023

--

Uniswap has taken the DeFi sector by storm, offering a decentralized protocol for token exchange on Ethereum. For the uninitiated, the intricate workings of Uniswap might seem perplexing. In this article, we will break down the data and control flow of its three main actions: swapping tokens, adding liquidity, and removing liquidity.

Swapping tokens on Uniswap

Swapping tokens is a frequent action on Uniswap and the most common flow, mainly executed by traders. Here’s a step-by-step breakdown:

The Caller’s Action

  1. Allow the periphery to access the amount you’re swapping.
  2. Choose a swap function from the periphery. The choice depends on factors like if ETH’s part of the deal, how many tokens you’re giving, or what you expect in return. Each function uses a ‘path’, a list of the exchanges involved.

The Periphery Contract: UniswapV2Router02.sol’s Role

  1. Figure out how many tokens need to be swapped at each step.
  2. Walk through the path. At each step, it sends the input tokens and triggers the swap. Tokens usually move to the next exchange, but at the last step, they go to the trader’s address.

In the Core (UniswapV2Pair.sol):

  1. Check to ensure enough liquidity remains after the swap.
  2. Count any tokens over the known reserves, which is the swap’s input.
  3. Dispatch the swapped tokens.
  4. Update the reserve counts.

Back in the Periphery:

Clean up any remaining tasks, like converting WETH tokens back to ETH.

Talk is cheap, show me the code!

const tx = await router
.connect(owner)
.swapExactTokensForTokens(
ethers.parseUnits("1", 18),
0,
[USDT_ADDRESS, USDC_ADDRESS],
owner.address,
Math.floor(Date.now() / 1000) + 60 * 10,
{
gasLimit: 1000000,
}
);
await tx.wait();

Here is a sample code that demonstrates how the actual swap happens. The user is trying to swap an exact amount of USDT for USDC.

  • The first parameter ethers.parseUnits("1", 18) denotes the exact amount of USDT to be swapped (1 USDT in this case).
  • The second parameter 0 is the minimum amount of USDC expected to be received in return. It's set to 0, indicating any amount of USDC is acceptable.
  • The next two parameters [USDT_ADDRESS, USDC_ADDRESS] specify the trading path.
  • The subsequent parameter is the recipient’s address, in this case, the owner.
  • The last parameter in the list is a deadline (current timestamp + 10 minutes), after which the transaction will not go through.

Adding Liquidity on Uniswap

The Caller’s Action

  1. Let the periphery access the tokens you’re putting into the liquidity pool.
  2. Use a function from the periphery to add those tokens.

Within the Periphery (UniswapV2Router02.sol):

  1. Make a new token pair if needed.
  2. For an existing pair, figure out how many of each token to add, keeping the same ratio.
  3. Ensure the amount you’re adding meets any specified minimum.
  4. Engage the core contract.

In the Core (UniswapV2Pair.sol):

  1. Mint new liquidity tokens and give them to the caller.
  2. Refresh the reserve counts.

Talk is cheap, show me the code!

const addLiquidityTx = await router
.connect(owner)
.addLiquidity(
usdtAddress,
usdcAddress,
token0Amount,
token1Amount,
0,
0,
owner,
deadline
);

The addLiquidity function requires the addresses of the two tokens, the amount of each token to be added, the minimum amounts of each token to be added (set to 0 for simplicity), the address receiving the liquidity provider (LP) tokens, and a deadline after which the transaction will not execute.

Removing Liquidity on Uniswap

The Caller’s Action

  1. Allow the periphery to take the liquidity tokens you’re cashing out.
  2. Use a function from the periphery to remove liquidity.

Within the Periphery (UniswapV2Router02.sol):

Pass on the liquidity tokens to the pair exchange.

In the Core (UniswapV2Pair.sol):

  1. Based on the liquidity tokens given up, send back the equivalent underlying tokens. For instance, if giving up 10% of your liquidity tokens, you get 10% of each token in the pool.
  2. Destroy (burn) the cashed-out liquidity tokens.
  3. Update the reserve counts.

Talk is cheap, show me the code!

    const removeLiquidityTx = await router.removeLiquidity(
usdtAddress,
usdcAddress,
lpTokenBalance, // Removing all LP tokens
minToken0Amount,
minToken1Amount,
owner.address,
deadline
);

Remove Liquidity: Using the removeLiquidity function, we specify the token addresses, the amount of LP tokens we want to burn, the minimum amounts of each token we want in return (set to 0 for the example), the address that will receive the tokens, and a deadline.

Uniswap is a standout player in the DeFi world, making decentralized token exchanges on Ethereum straightforward and efficient. Through this article, we’ve broken down its inner workings, from token swaps to adding or removing liquidity. Alongside the explanations, we provided hands-on code examples to showcase how these operations play out in real life. Whether you’re a developer, trader, or just curious about DeFi, we hope this guide has made Uniswap a bit easier to grasp. Remember, understanding tools like Uniswap is a stepping stone to navigating the broader crypto ecosystem with confidence.

Toodles! :)

--

--