---
description: >-
  This page details some the important process in the PingPong exchange as well
  as some key formulas , interfaces and general contract structure
---

# Architecture

## Conceptual Overview

This overview might be at odds with the code. If you spot something like that please let us know so we can fix it.

### Variable Definitions

For simplicity and as reference point here is a more detailed definition of all variables and short names used throughout the description.

* Market - A contract that facilitates swaps between ETH and an ERC20 token and vice versa.
* $$res_A$$  - The amount that the AMM stores of a particular asset A
* k - The invariant of the AMM
* S - The percentage difference between a price level N and the one above it
* $$P_n$$ - The nth price level, equal to $$(1 + S)^n$$ from eth to token i.e. 1ETH = $$(1 + S)^n$$ TOKEN&#x20;
* SwapFee - The fee (in percentages) payed buy order takers/traders
* _floor_  - The nearest price level below the current rate of the AMM
* _ciel_ - The nearest price level above the current rate of the AMM&#x20;
* $$Liq_A$$ - Traditional liquidity shares as they appear in an AMM

### Depositing Liquidity to AMM&#x20;

This part is identical to how existing AMMs work. \
Liquidity providers deposit amounts of ETH and TOKEN that maintain the existing ratio in the exchange.\
In return they get liquidity shares equal to:

$$
MintedShares = ExistingShares\times \frac{ETH_{Deposited}}{ETH_{Deposited}+ res_{ETH}}
$$

### Setting Up Limit Orders

A harder consideration is how do you set up limit orders.\
The current design enables users to send ETH to the _floor_ level and TOKEN to the _ciel_ level. In return they get withdrawal tokens at a 1 to 1 ratio to the tokens they need to receive as a result from the trade. minted to them. These tokens guarantee that after a level has been completely passed then anyone who deposited an asset can withdraw the appropriate amount of the other asset. It's up to the user to actualize his withdrawal tokens and move them to her own wallet. Alternatively, the user can call watchers that can withdraw user tokens on their behalf.

```typescript
/**
@notice Takes a level number and deposits either tokens or ether to it.
Determining which should it be is done according to the current AMM ratio.
If sent to level below would expect ETH and if sent to a level above would expect TOKEN
@param level Level that the sender wishes to fill
@param tokenAmount Amount of tokens if applicable the sender wishes to put in
@param watcher Address of watcher who can complete the depositors trade on his behalf
@param watcherFee Fee to pay the watcher to compensate him for withdrawl
*/
function depositToLevel(uint256 level, uint256 tokenAmount, address watcher, uint32 watcherFee) public payable returns(bool);

/**
@notice Enables a user or an approved watcher to withdraw the assets after a trade is 
@param level Level that the sender wishes to withdraw from
@param address Beneficiary of the withdrawl
@param withdrawlTokenAmount Amount of tokens to withdraw
*/
function withdrawFromLevel(uint256 level, address beneficiery, uint256 withdrawlTokenAmount) public returns(bool);
```

### Performing Trades

Performing a trade has a few stages, first the trader goes to the AMM and pays a fee on the whole trade. The trader then buys tokens/eth in the AMM until they hit a price wall. They then buy tokens/eth from the price wall until the wall is dry after which they go back to the AMM. This process can continue until the desired take amount is fulfilled.

In the following section we'll provide a flow of performing a sell of X tokens to eth.

{% tabs %}
{% tab title="Selling tokens for eth" %}
1. When initially accessing the contract it first determines the fee the user must pay for his trade (a constant percentage). $$SellAmount = X*(1 - SwapFee)$$&#x20;
2. After the actual _SellAmount_ is calculated the exchange calculates the amount of tokens that are to be sold in the AMM and which are to be passed to the nearest ciel level.\
   It does so using the formula (derivation provided below):\
   \
   &#x20;$$TokensSoldInAMM =SellAmount - (\sqrt{k\times{(1+s)^{n+1}}} - res_{token})$$ \
   \
   Note that we are using n+1 when calculating EthToTokenPrice because n refers to the floor and not the ciel.
3. It then sells TokensSoldInAMM according to:\
   &#x20;$$EthFromAMM = TokensSoldInAMM \times \frac{res_{eth}}{res_{token} + TokensSoldInAmm}$$&#x20;
4. If there are still tokens left to sell it sells them at:\
   &#x20;$$EthFromOrderbook = \frac{SellAmount - TokensSoldInAMM}{(1+s)^{n+1}}$$&#x20;
5. If the ciel is not  enough to fill the _SellAmount_ then repeat steps 2-4

A similar process can be performed for Selling eth and buying eth and tokens
{% endtab %}

{% tab title="Deriving TokensSoldInAMM" %}
We want to find the amount of tokens that need to be in the reserve of the AMM so it will reach the price limit of the wall _TokenReserveAtCiel_, that naturally follows: $$TokenReserveAtCiel = \sqrt{res_{token}^2}$$

Now because we are talking about the price of the token at the ciel level we know that\
&#x20;$$res_{token} = res_{eth} \times (1+s)^{n+1}$$&#x20;

This leads to $$\sqrt{res_{token}^2}  = \sqrt{res_{token}\times res_{eth} \times (1+s)^{n+1}}$$&#x20;

No if we recall that _K_ is the product of the two reserves at the AMM and the equations above we get:f $$TokenReserveAtCiel = \sqrt{k\times (1+s)^{n+1}}$$

From here if know the _SellAmount_ the amount sold in the AMM is simply:\
&#x20;$$TokensSoldInAMM =SellAmount - (\sqrt{k\times{(1+s)^{n+1}}} - res_{token})$$&#x20;
{% endtab %}
{% endtabs %}

### Order of entering a limit order

The current design is that ignores the order in which market makers set up limit orders. It is entirely possible that someone who put their order first will end up withdrawing the trade proceeds last as because they were late to respond.

We could potentially compnsate early entrants by minting less withdraw tokens the more tokens already exist at the the level:

$$
tokensMinted = addedAssets \times \frac{existingTokens}{existingAssets} \times max(1- \frac{existingAssets}{res_{asset}}, maxPenalty)
$$

The tradeoffs of increased complexity versus potentially better incentive are still being explored.

## Contract Structuring

Each TOKEN/ETH pair is a market consisting of an unlimited number of contracts.

### Automated Market Maker&#x20;

This is a single regular contract (not a library, due to the high amount of transactions to the contract not worth the delegate call overhead) serving as the entry point to the DEX.

It shares a lot of it's functionality with the traditional AMM with some of the main changes outlined below.

{% code title="PingPongAMM.sol" %}
```javascript
contract AutomatedMarketMaker{

uint256 ciel;
uint256 floor;
mapping(uint256 => address) ciels;
mapping (uint256 => address) floors;

/**
@notice As an example of a trade function
This function receives eth and a desired number of tokens as return.
It returns the tokens desired and the remainder of the eth. Making sure do go through the designated ciels.
@param outputAmount Number of tokens to return
@param deadline max time to execute the trade
*/
function ethToTokenOutput(uint256 outputAmount, uint256 deadline) public payable; 

/**
@notice If a ciel for paticular rate doesn't exist create it
Optionally if msg.value > 0 automaticall mint sender withdrawl tokens
@param rate rate to create the ciel for
@param watcher address for newly minted withdrawl tokens watcher
@param watcherFee fee for watcher
*/
function createNewCiel(uint rate, address watcher, uint watcherFee) public payable;

}
```
{% endcode %}

It stores AMM reserves and liquidity tokens and shares most of it's functionality with existing AMM. The exepction of course is that the trading takes into account the EthTillCiel/TokenTillFloor amounts defined earlier and calls the Ciel/Floor contracts accordingly.

### Floor/Ciel Contracts

Each market has multiple Floor/Ciel contracts that proxy their logic into a shared master contract.\
The core of each logic an be summed into the following

{% code title="CielLevel.sol" %}
```javascript
contract CielLevel{
    address token;
    uint256 rate;
    mapping(address => uint256) withdrawlTokens;
    mapping(address => address => uint) watcherFees;
    
    /**
    @notice Mints rate * msg.value withdrawal tokens to the sender
    @param watcher watcher for user tokens
    @param watcherFee fee for watcher
    */
    function makeOrder(address watcher, uint watcherFee) public payable;
    
    /**
    @notice Cancels ethAmount out of the order and returns the eth to the msg.sender
    @param ethAmount Amount ot cancel 
    */
    function cancelOrder(uint ethAmount) public;
    
    /**
    @notice Exchanges the withdraw tokens for actual tokens
    @param amount amount of tokens to exchange
    */
    function exchangeTokens(amount) public;
}
```
{% endcode %}

A similar contract exists for the Floor levels.

### Gas Cost Estimation

Other than some relatively cheap calculations the potential additional gas costs are:

* 20K more for each level involved
* 40K (or maybe 20K depends on cost of exponentiation) for each time a level is passed

