Skip to content

Cyfrin/5-t-swap-audit

Repository files navigation

t-swap

TSwap

This project is meant to be a permissionless way for users to swap assets between each other at a fair price. You can think of T-Swap as a decentralized asset/token exchange (DEX). T-Swap is known as an Automated Market Maker (AMM) because it doesn't use a normal "order book" style exchange, instead it uses "Pools" of an asset. It is similar to Uniswap. To understand Uniswap, please watch this video: Uniswap Explained

TSwap Pools

The protocol starts as simply a PoolFactory contract. This contract is used to create new "pools" of tokens. It helps make sure every pool token uses the correct logic. But all the magic is in each TSwapPool contract.

You can think of each TSwapPool contract as it's own exchange between exactly 2 assets. Any ERC20 and the WETH token. These pools allow users to permissionlessly swap between an ERC20 that has a pool and WETH. Once enough pools are created, users can easily "hop" between supported ERC20s.

For example:

  1. User A has 10 USDC
  2. They want to use it to buy DAI
  3. They swap their 10 USDC -> WETH in the USDC/WETH pool
  4. Then they swap their WETH -> DAI in the DAI/WETH pool

Every pool is a pair of TOKEN X & WETH.

There are 2 functions users can call to swap tokens in the pool.

  • swapExactInput
  • swapExactOutput

We will talk about what those do in a little.

Liquidity Providers

In order for the system to work, users have to provide liquidity, aka, "add tokens into the pool".

Why would I want to add tokens to the pool?

The TSwap protocol accrues fees from users who make swaps. Every swap has a 0.3 fee, represented in getInputAmountBasedOnOutput and getOutputAmountBasedOnInput. Each applies a 997 out of 1000 multiplier. That fee stays in the protocol.

When you deposit tokens into the protocol, you are rewarded with an LP token. You'll notice TSwapPool inherits the ERC20 contract. This is because the TSwapPool gives out an ERC20 when Liquidity Providers (LP)s deposit tokens. This represents their share of the pool, how much they put in. When users swap funds, 0.03% of the swap stays in the pool, netting LPs a small profit.

LP Example

  1. LP A adds 1,000 WETH & 1,000 USDC to the USDC/WETH pool
    1. They gain 1,000 LP tokens
  2. LP B adds 500 WETH & 500 USDC to the USDC/WETH pool
    1. They gain 500 LP tokens
  3. There are now 1,500 WETH & 1,500 USDC in the pool
  4. User A swaps 100 USDC -> 100 WETH.
    1. The pool takes 0.3%, aka 0.3 USDC.
    2. The pool balance is now 1,400.3 WETH & 1,600 USDC
    3. aka: They send the pool 100 USDC, and the pool sends them 99.7 WETH

Note, in practice, the pool would have slightly different values than 1,400.3 WETH & 1,600 USDC due to the math below.

Core Invariant

Our system works because the ratio of Token A & WETH will always stay the same. Well, for the most part. Since we add fees, our invariant technially increases.

x * y = k

  • x = Token Balance X
  • y = Token Balance Y
  • k = The constant ratio between X & Y
y = Token Balance Y
x = Token Balance X
x * y = k
x * y = (x + ∆x) * (y  ∆y)
∆x = Change of token balance X
∆y = Change of token balance Y
β = (∆y / y)
α = (∆x / x)

Final invariant equation without fees:
∆x = (β/(1-β)) * x
∆y = (α/(1+α)) * y

Invariant with fees
ρ = fee (between 0 & 1, aka a percentage)
γ = (1 - p) (pronounced gamma)
∆x = (β/(1-β)) * (1/γ) * x
∆y = (αγ/1+αγ) * y

Our protocol should always follow this invariant in order to keep swapping correctly!

Make a swap

After a pool has liquidity, there are 2 functions users can call to swap tokens in the pool.

  • swapExactInput
  • swapExactOutput

A user can either choose exactly how much to input (ie: I want to use 10 USDC to get however much WETH the market says it is), or they can choose exactly how much they want to get out (ie: I want to get 10 WETH from however much USDC the market says it is.

This codebase is based loosely on Uniswap v1

Getting Started

Requirements

  • git
    • You'll know you did it right if you can run git --version and you see a response like git version x.x.x
  • foundry
    • You'll know you did it right if you can run forge --version and you see a response like forge 0.2.0 (816e00b 2023-03-16T00:05:26.396218Z)

Quickstart

git clone https://github.com/Cyfrin/5-t-swap-audit
cd 5-t-swap-audit
make 

Usage

Testing

forge test

Test Coverage

forge coverage

and for coverage based testing:

forge coverage --report debug

Audit Scope Details

  • Commit Hash: e643a8d4c2c802490976b538dd009b351b1c8dda
  • In Scope:
./src/
#-- PoolFactory.sol
#-- TSwapPool.sol
  • Solc Version: 0.8.20
  • Chain(s) to deploy contract to: Ethereum
  • Tokens:
    • Any ERC20 token

Actors / Roles

  • Liquidity Providers: Users who have liquidity deposited into the pools. Their shares are represented by the LP ERC20 tokens. They gain a 0.3% fee every time a swap is made.
  • Users: Users who want to swap tokens.

Known Issues

  • None