Skip to content

Uniswap V2 AMM

Uniswap V2 fork deployed on Celo Sepolia for token-to-token swaps.

Deployed Contracts

ContractAddress
UniswapV2Factory0xe3FB7Da331c5420eac2D4c55c41B75Fc5B897c40
UniswapV2Router020x5b724eEe9a2D4d76Ca0edFbAcf1B36749Eb7ed9b

Active Pools

PairToken0Token1Reserve0Reserve1
IMC/USDT0xF150...d5A20x3C8E...DDDF1000 IMC500 USDT
IMC/USDC0xF150...d5A20x82DD...62A71000 IMC500 USDC
USDT/USDC0x3C8E...DDDF0x82DD...62A7500 USDT500 USDC

Fee

0.3% per swap (997/1000 multiplier). Configured in UniswapV2Library.sol.

Source Code

Located at:

stablecoin-contracts/contracts/uniswap/
├── UniswapV2Factory.sol
├── UniswapV2Router02.sol
├── UniswapV2Pair.sol
├── UniswapV2Library.sol
├── interfaces/
│   ├── IUniswapV2Factory.sol
│   ├── IUniswapV2Pair.sol
│   ├── IUniswapV2Router02.sol
│   ├── IFactoryForRouter.sol
│   └── IWETH9.sol
└── test/
    ├── ERC20.sol
    └── MockWETH.sol

Architecture Decision: factory.getPair() vs pairFor()

The standard Uniswap V2 Router uses UniswapV2Library.pairFor() which hashes tokenA + tokenB + init code hash. This approach requires the factory's INIT_CODE_PAIR_HASH to be baked into the library at compile time.

We chose factory.getPair() instead:

  • Simpler: no init code hash management
  • More flexible: works even if pair creation logic changes
  • Tradeoff: slightly more gas per swap (extra external call)
  • For testnet/dev this is acceptable

Modified in UniswapV2Router02.sol:

solidity
function _getPair(address tokenA, tokenB) internal view returns (address pair) {
    pair = IFactoryForRouter(factory).getPair(tokenA, tokenB);
    require(pair != address(0), "UniswapV2: PAIR_NOT_FOUND");
}

Adding New Pools

  1. Deploy two ERC-20 tokens
  2. Call createPair(tokenA, tokenB) on the Factory
  3. Approve the Router to spend both tokens
  4. Call addLiquidity() to seed the pool
  5. Register CELO_<SYMBOL>_CONTRACT in comet-engine env

Price Calculation

amountOut = amountIn * 997 * reserveOut / (reserveIn * 1000 + amountIn * 997)

Released under the MIT License.