changed 2 years ago
Published Linked with GitHub

EPF Week 13 Updates

Topic: PEPC-Boost Research

Two weeks ago we decided that I should focus on implementation of the following specs:

Objects

  • Swap
    • tokenIn: Number of tokens to swap
    • tokenInType: Can be X or Y
    • minTokenOut: ~ Slippage rate, minimum amount of token expected to be received after the swap
  • AMM
    • reserve_x: Number of tokens X provided by LPs to the pool
    • reserve_y: Number of tokens Y provided by LPs to the pool
    • Bonding curve, e.g., reserve_x * reserve_y == k for some parameter k
    • Assume no fee to LPs for now, we write the AMM state as \((R_x, R_y)\) = (reserve_x, reserve_y)

Dynamics

  • Outside price process \(P_t\) is available continuously, price of X in Y
    • e.g., \(P_t \sim \text{GBM}(0, \sigma)\), with some initial \(P_0\)
  • User swaps are received randomly throughout the 12 seconds
    • For slot \(i\), draw a random number of transactions \(N_i \sim \text{Poisson}(12 \lambda)\), where \(\lambda\) is the average number of swaps received per second
    • Poisson process property: the swaps are uniformly distributed over \([12(i-1), 12i)\) (the duration of slot \(i\)). So draw \(N_i\) samples from \(U \sim \text{Uniform}(0, 12)\), one sample per transaction.
    • Transaction \(j\) is drawn for time \(t_j\), the price at the time the swap is made is \(P_{t_j}\). Assume the user wants to swap \(C_j\) tokens, with slippage rate \(r_j\), then we set minTokenOut to:
      • \(C_j \cdot P_{t_j} \cdot r_j\) if tokenInType == "X"
      • \(\frac{C_j}{P_{t_j}} \cdot r_j\) if tokenInType == "Y"
      • This encodes the assumption that the user does not trade at the AMM price (which is stale after the previous block is built), but trades virtually at the price of the continuous exchange.
      • If we do not like this assumption, we can also replace \(P_{t_j}\) with \(P_{i-1} = \frac{R_y}{R_x}\). We could try with either one.
    • Question: How to set \(C_j\)? Could use historical data of swaps, or just draw from some power law distribution (many small swaps, few big swaps) and draw at random whether the tokenInType is X or Y.
  • Swap execution
    • Starting from AMM state \((R_x, R_y)\), with user swap \((C_j, r_j)\), we compute \(T_o\) (token out) as
      • \(T_o = \frac{R_y \cdot C_j}{R_x + C_j}\) (for tokenInType == "X", inverting \(R_x\) and \(R_y\) otherwise)
      • If \(T_o \leq\) minTokenOut, then the trade does not execute and simply reverts
      • If the swap executes successfully, then update the reserves
        • \(R_x \leftarrow R_x + C_j\)
        • \(R_y \leftarrow R_y - T_o\)
        • Also inverting if tokenInType == "Y"
  • Block construction
    • A single ToB searcher makes a swap to arbitrage the AMM against the CEX. They read the price \(P_{12 \cdot i}\) (CEX price at the time of the release of block \(i\)), and make the swap which exactly moves the AMM state to \((R_x, R_y)\) such that \(P_{12 \cdot i} = \frac{R_y}{R_x}\), i.e., price in the AMM is the same as the outside price \(P_{12 \cdot i}\)
      • The ToB swap is added to the block, as the first transaction
    • User swaps are then added as RoB
      • Question: What should the ordering be? We could just use FCFS for now
      • Question: Should user swaps be tampered with? e.g., user gets sandwiched by the builder
        • And if so, should bundles be placed at the top of the RoB?

Metrics

  • We measure how many user swaps revert in the RoB

I have a working simulation written in Python that I'm going to be presenting next Wednesday on the 25th October on our PEPC meeting with Barnabé, Diego and Bharath. I currently spend time on answering all the related questions to this simulation.

Related materials

Select a repo