# AMM pools

Tapp's Automated Market Maker (AMM) implements a classic constant product pool similar to Uniswap v2. Each pool holds two fungible assets and issues **pool shares** that track a provider's proportional ownership of those reserves. The logic lives in the `amm::amm` Move module.

This document explains both the Move code and the high‑level economics behind the AMM so liquidity providers (LPs) understand how trades affect the pool, how fees accrue and the risks involved.

## Key data types

### Config

* `authority`: Address allowed to manage global settings.
* `pending_authority`: Temporary storage while transferring authority.
* `fee_tiers`: Supported trading fees expressed in parts per million (`FEE_DENOMINATOR = 1_000_000`).

### Pool

A pool is represented by the `Pool` struct:

```move
struct Pool has key, store {
    pool_addr: address,       // address holding the pool resources
    asset_a: address,         // coin type of token A
    asset_b: address,         // coin type of token B
    fee: u64,                 // fee in ppm
    reserve_a: u64,           // amount of token A in the pool
    reserve_b: u64,           // amount of token B in the pool
    total_shares: u128,       // total liquidity tokens minted
    positions: BigOrderedMap<u64, Position>,
    position_index: u64,
}
```

### Position

When users add liquidity they receive a `Position` record which tracks the number of shares owned. A pool can have many positions and users may hold several of them.

## Creating a pool

Pools are created with `create_pool(pool_signer, assets, fee, creator)`.

* `pool_signer` is the signer of the newly created account that will store the `Pool` resource.
* `assets` is a vector with two addresses representing the coin types of token A and token B.
* `fee` must match one of the configured fee tiers (0.01%, 0.05%, 0.3% or 1%).
* `creator` is recorded in the `PoolCreated` event.

The function initializes reserves to zero and sets `total_shares` to zero. No liquidity exists until someone calls `add_liquidity`.

## Adding liquidity

`add_liquidity(pool_signer, position_idx, stream, creator)` accepts a `BCSStream` that encodes the following arguments:

1. `amount_a` – desired amount of token A to deposit
2. `amount_b` – desired amount of token B
3. `min_amount_a` – minimum A that must be deposited
4. `min_amount_b` – minimum B that must be deposited

If `position_idx` is `some`, the existing position is topped up. Otherwise a new position is created.

The function calculates the optimal deposit amounts using `calc_optimal_lp_amount`, mints shares and updates reserves. The first liquidity provider permanently locks `MINIMUM_LIQUIDITY` (1000 shares) to protect against division by zero attacks. A `LiquidityAdded` event is emitted.

Returned values are:

* A vector containing the final amounts of A and B deposited.
* `some(index)` if a new position was created; otherwise `none`.

## Removing liquidity

`remove_liquidity(pool_signer, position_idx, stream, creator)` burns shares and returns the underlying tokens. The stream encodes:

1. `burned_shares` – amount of liquidity tokens to burn
2. `amount_a_min` – minimum A expected
3. `amount_b_min` – minimum B expected

The amounts withdrawn are computed proportionally to the pool reserves using `compute_lp_from_shares`. If the resulting amounts are below the minimum thresholds the transaction aborts. If a position’s remaining share balance is zero it is removed from the map. A `LiquidityRemoved` event logs the operation.

## Swapping tokens

`swap(pool_signer, stream, creator)` performs trades in either direction. The stream encodes:

1. `a2b` – `true` to swap token A for token B; `false` for B → A
2. `fixed_amount_in` – `true` if the provided amount is the input; `false` when specifying the desired output
3. `amount_in` – amount of tokens sent (if `fixed_amount_in`)
4. `amount_out` – minimum amount expected (when `fixed_amount_in`) or the exact amount requested (when swapping for a fixed output)

Depending on `fixed_amount_in` the module routes to `swap_exact_tokens_for_tokens` or `swap_tokens_for_exact_tokens`. In either case the pool uses the constant product model to determine the price impact. A `Swapped` event records the trade.

At any moment the marginal price of token A relative to B is simply`reserve_b / reserve_a`. Trading against the pool shifts this ratio: buying A\
reduces its reserve and therefore raises its price, while selling A increases\
its reserve and lowers the price. This automatic price discovery lets the AMM\
function without relying on order books.

### Constant product formula

Each pool maintains the invariant `x * y = k` where `x` and `y` are the reserves\
of token A and B. When a trade occurs the product of the reserves after fees\
must be at least as large as before.

For an input amount `dx`, reserves `x0` and `y0`, and fee `f` (ppm):

```
amount_out = (y0 * dx * (1 - f/FEE_DENOMINATOR)) / (x0 + dx * (1 - f/FEE_DENOMINATOR))
```

Where:

* `x0`, `y0` – reserves of token A and B before the swap
* `dx` – the amount of token A being traded in
* `f` – the fee applied to the swap (parts per million)
* `FEE_DENOMINATOR` – constant 1,000,000 used to express `f`
* `k` – the constant product `x * y` that remains after the swap

The result `amount_out` is the amount of token B returned while keeping the\
constant product `k = x * y` from decreasing after fees, where `x` and `y` are\
the new reserves after the swap.

This equation keeps the pool on the same constant product curve. The inverse\
formula computes the input needed for a specific output.

The spot price of token A in terms of B is the ratio `reserve_b / reserve_a`.\
As trades move this ratio the price adjusts automatically.

### Fees and LP incentives

The fee `f` is deducted from every trade and added to the reserves. Because\
liquidity providers own a share of the reserves, these fees accumulate\
proportionally to their holdings. Over time LPs earn yield from trading volume.

A simple approximation for APR is:

```
APR ≈ (daily volume × fee × 365) / total_liquidity
```

Pools with high volume relative to their liquidity tend to generate more fees.\
However, volatile assets can experience larger price swings which lead to\
impermanent loss as described below.

### Impermanent loss

Impermanent loss (IL) is the difference between holding tokens in the pool\
versus simply holding them in a wallet. If the price of either asset moves\
significantly, the value of your pool position may end up lower than if you had\
held the tokens outright. IL is "impermanent" because it may diminish if prices\
return to their original ratio. Fees earned can offset IL, especially for stable\
pairs with large trading volume.

## Utility functions

* `fee_rate(address)` – fetch the fee tier of a pool.
* `reserve_a`, `reserve_b` – current reserves.
* `total_shares` – total minted liquidity shares.
* `position_shares` / `get_position` – inspect individual liquidity positions.

These helpers can be used off‑chain to display pool info or compute prices.

## Events

Every action emits an event so indexers can track pool activity:

* `PoolCreated`
* `LiquidityAdded`
* `LiquidityRemoved`
* `Swapped`

## Security checks

The Move code performs numerous assertions such as ensuring supported fee tiers, verifying liquidity amounts, preventing arithmetic underflow, and checking that swap invariants hold. These checks are designed to safeguard liquidity providers and traders from invalid state transitions.

***

This AMM forms the foundation for Tapp’s more advanced pool types. Understanding its mechanics is essential for anyone integrating directly with the contracts or building hooks that extend the protocol.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://tapp-exchange.gitbook.io/tapp-exchange/developer-docs/amm.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
