CS8803: Exploiting Smart Contract and DeFi

Taesoo Kim



CS8803: Decentralized Finance/DeFi

Taesoo Kim

Recap

Timeline

About Part 2

Timeline

A Few Pointers

This Week’s Study

Ethereum Layers

Ref. EI §2

DeFi on Ethereum

DeFi on Ethereum

Market Making with Order Book (CeFi)

Automated Market Makers (AMM)

Example: Uniswap ($4B Market Cap)

Today’s Topic: Uniswap V1 ($8M Market Cap)

Automated Market Makers (AMM)

  1. Decentralized buying and selling of an asset
  2. Automated pricing and price discovery
  3. Incentivise liquidity providers
  4. Incentivise users

1) Liquidity Pool

→ Who can be the liquidity provider? Anyone!

2) Algorithmic Pricing

2) Algorithmic Pricing (given xx, price of yy?)

2) Algorithmic Pricing (given xx, price of yy?)

2) Notes on Algorithmic Pricing

3) Incentivising Liquidity Providers

4) Incentivising Users

Uniswap V1 (AMM)

xy=kxy = k and (x+Δx)(yΔy)=k(x + \Delta x)(y - \Delta y) = k → So, Δy=ykx+Δx\quad \Delta y = y - \frac{k}{x + \Delta x}

xy=kxy = k and (x+(1ρ)Δx)(yΔy)=k(x + (1 - \rho)\Delta x)(y - \Delta y) = k → So, Δy=ykx+(1ρ)Δx\quad \Delta y = y - \frac{k}{x + (1 - \rho)\Delta x}

Uniswap V1 (AMM in Code)

  # Pricing function for converting between ETH and Tokens.
  #
  #@param input_amount Amount of ETH or Tokens being sold.
  #@param input_reserve Amount of ETH or Tokens (input type) in exchange reserves.
  #@param output_reserve Amount of ETH or Tokens (output type) in exchange reserves.
  #@return Amount of ETH or Tokens bought.
  @private
  @constant
  def getInputPrice(input_amount: uint256, input_reserve: uint256, 
                    output_reserve: uint256) => uint256:
      assert input_reserve > 0 and output_reserve > 0
      input_amount_with_fee: uint256 = input_amount * 997
      numerator: uint256 = input_amount_with_fee * output_reserve
      denominator: uint256 = (input_reserve * 1000) + input_amount_with_fee
      return numerator / denominator

Factory

$ git pull
$ brownie run scripts/uniswap.py -I
...
>>> factory
<Factory Contract '0xb5F338E1397D646AFFf707DD47ea94eAacdBa45D'>

Example: Creating a New Exchange for ERC20

>>> token = ERCToken.deploy(1000, "A")
<ERCToken Contract '0xcFf3e49D8947D75Fa04115cFD3f9343c95956eb7'>

>>> factory.createExchange(token.address)
>>> factory.getExchange(token.address)
'0x0bEf07F1fF27538ee5C7D64e7582A42867069f4e'

# or simply
>>> exchange = new_exchange(factory, token)
<Exchange Contract '0x0bEf07F1fF27538ee5C7D64e7582A42867069f4e'>

Example: Adding to Liquidity Pool

# Note.
#   min_liquidity will be ignored for the very first setup
#   max_tokens indicates #tokens to be put
#   msg.value should be provided as part of the invocation
def addLiquidity(min_liquidity: uint256, max_tokens: uint256, 
                 deadline: timestamp) -> uint256:
# allow the exchange to spend 100 tokens
>>> token.approve(exchange.address, 100)

# add liquidity of (100 tokens, 1000000000 wei)
>>> exchange.addLiquidity(0, 100, time.time() + 1000, {"value": 1000000000})

>>> exchange.balance()
1000000000
>>> token.balanceOf(exchange.address)
100

Example: Checking the Price of a ERC20 Token

  # @notice Public price function for Token to ETH trades with an exact input.
  # @param tokens_sold Amount of Tokens sold.
  # @return Amount of ETH that can be bought with input Tokens.
  @public
  @constant
  def getTokenToEthInputPrice(tokens_sold: uint256) -> uint256(wei):
      assert tokens_sold > 0
      token_reserve: uint256 = self.token.balanceOf(self)
      eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance))
      return as_wei_value(eth_bought, 'wei')

Example: Checking the Price of a ERC20 Token

# selling one token (input) and ether as output
>>> exchange.getTokenToEthInputPrice(1)
9871580

>>> 10000000 - 9871580
128420

# getting cheaper when putting more tokens
>>> exchange.getTokenToEthInputPrice(10)/10
9066108.9

Example: Swapping a Token for ETH

def getEthToTokenOutputPrice(tokens_bought) 
    -> uint256(wei)
def ethToTokenTransferInput(min_tokens, deadline, recipient)
    -> uint256
def ethToTokenTransferOutput(tokens_bought, deadline, recipient)
    -> uint256(wei)

Example: Swapping a Token for ETH

>>> token.approve(exchange.address, 1)
>>> exchange.tokenToEthSwapInput(1, ethPerToken, int(time.time())+1000)

├── General ERC20 Token (0xe0aA552A10d7EC8760Fc6c246D391E698a82dDf9)
│   ├── Approval
│   │   ├── owner: 0x66aB6D9362d4F35596279692F0251Db635165871
│   │   ├── spender: 0x87e23022F31814f835C052A9e280608bdE7aE3f9
│   │   └── value: 0
│   └── Transfer
│       ├── from: 0x66aB6D9362d4F35596279692F0251Db635165871
│       ├── to: 0x87e23022F31814f835C052A9e280608bdE7aE3f9
│       └── value: 1

└── 0x87e23022F31814f835C052A9e280608bdE7aE3f9
    └── EthPurchase
        ├── buyer: 0x66aB6D9362d4F35596279692F0251Db635165871
        ├── tokens_sold: 1
        └── eth_bought: 9871580

Tutorial: Pricing on Uniswap V1

Today’s Tutorial

  1. Measuring the price of tokens while buying/selling
  2. ERC777 and ERC1820 on Uniswap

(WARNING. lots of things to digest!)

Lab06!

References