# Introduction

![Liquidswap UI](/files/8ozntPDZjV9m8x6R2JeD)

[Liquidswap](https://liquidswap.com) is the first AMM (Automated Market Maker) on the Aptos blockchain, created to enable safe and decentralized token swaps. The protocol uses smart contracts developed by the Pontem Network team, written in the Move language, and published on the Aptos mainnet.

### Features

Supported features and benefits:

* Uncorrelated token swaps (similar to Uniswap V2);
* Stable swaps for correlated assets, using a different liquidity curve;
* A DAO treasury that receives a part of every swap transaction fee;
* Dynamic fees: managed by treasury multisig and dao in the future.
* Written in Move: a new smart contract language designed with security in mind;
* High speed thanks to Aptos' parallel transaction execution engine;
* Formal verification partially complete: full formal verification is coming in the future.

## Mainnet :tada:

The current version of Liquidswap is deployed on the Aptos mainnet and located at <https://liquidswap.com>.

The mainnet has been available since 19.10.2022; the [contracts](https://github.com/pontem-network/liquidswap) since v0.4.2 deployed to mainnet.

Please test Liquidswap for yourself and share your feedback - it will be greatly appreciated 😊

## Security audits

We've done 2 security audits and another one in progress currently.

### Ottersec

Website - [OtterSec.io](https://osec.io/)

Audit report:

{% file src="/files/2KbUyxiZcCFiCxl4bF2z" %}

The rest of the changes, like flashloans and dynamic fees and configuration, are also reviewed by the Ottersec team but not added to the report.

### Halborn

Website - [Halborn.com](https://halborn.com/)

Audit reports:

**General**

{% file src="/files/Vdd4jT43MNWZYXLOGTPP" %}

**Flashloan**

{% file src="/files/Eco3nGKIlNcNPT8rmfwx" %}

**Dynamic Fees and Config**

{% file src="/files/qTsuFQeUIgVqbFbqKLLD" %}

### Zellic

Website - <https://www.zellic.io/>

{% file src="/files/8RmkM4i3Q3mXorIaiyEo" %}

### Movebit

Website - <https://www.movebit.xyz>

{% file src="/files/aOBwlUuWKCcMTTSmecHw" %}

## Bounty Program

![](/files/JNceNrEPKMIPRvhPNTsR)

Liquidswap has launched a bug bounty program on the [Immunefi](https://immunefi.com/) platform recently.&#x20;

See program overview impacts in scope, and participate - <https://immunefi.com/bounty/liquidswap/>

## Developer Links

For a general idea about Liquidswap, start with the [Protocol Overview](/protocol-overview).

To get hands-on with the DEX, check out the tutorials:

* [Smart contracts usage & integrations](/integration)
* [Frontend integration](/typescript-sdk)

Looking for source code? Visit our [Github](https://github.com/pontem-network):

* [Liquidswap Smart Contracts](https://github.com/pontem-network/liquidswap)
* [Test coins](https://github.com/pontem-network/test-coins)&#x20;
* [Typescript SDK](https://github.com/pontem-network/liquidswap-sdk)

Need help integrating your service with Liquidswap? Feel free to contact us:

* [Telegram](https://t.me/pontemnetworkchat)
* [Discord](https://discord.gg/44QgPFHYqs)


# Protocol Overview

The design of a decentralized AMM was first proposed by Vitalik Buterin in 2016-2017 and later implemented by Uniswap and Bancor. Unlike centralized crypto exchanges with their order books, AMMs allows users to create liquidity pools with tokens of X and Y, where the initial ratio of the two tokens determines their starting relative price and the so-called liquidity curve (the change in price that results from each swap transaction).

Users can swap tokens by providing one side asset in exchange for another and paying a small fee. By contrast, liquidity providers provide both X and Y tokens and earn a share of the transaction fees. When depositing tokens X and Y in a pool, a liquidity provider receives special LP tokens in exchange, which represent their share in the pool and are needed to withdraw the deposited liquidity.

Most AMMs utilize a so-called **Constant Function** - a formula for calculating two tokens' relative prices that ensures that a pool will not be drained as a result of any liquidity event. The standard liquidity curve function, introduced by Uniswap v2, looks as follows:

```
x * y = k
```

In the formula, X and Y are the amounts of the two tokens in a pool, and their product k is a constant. Any trade (swap) changes the amounts X and Y, but k remains the same. This formula is suitable for uncorrelated swaps (assets whose prices aren't correlated with each other) and works well in most cases, including on Liquidswap.

*For swaps between stablecoins and other correlated assets, however, the simple constant product formula is not very effective. In such cases, Liquidswap uses a different, more complex formula to minimize slippage even for large transactions.*

## User Flow

Like most DEXes, Liquidswap features two types of users: liquidity providers and traders.

### Liquidity providers

Liquidity providers create new pools and provide liquidity for them (tokens X and Y) so that traders can execute swaps. In return, liquidity providers receive LP tokens. To redeem the liquidity together with the earned fees, they need to burn LP tokens.

Types of liquidity providers:

* Traders who want to earn a passive income;
* Project teams that wish to support their token's price and trading volume by providing initial liquidity;
* DeFi protocols focused on passive income, experimental strategies, etc.

### Traders

Traders interact with a pool's smart contract to swap tokens X for Y or vice versa using the liquidity in the pool. For each swap, a trader pays a fee to the liquidity providers.

*Any AMM (DEX) transaction can be executed using the* [*Liquidswap Dapp*](https://liquidswap.com) *or direct calls to the deployed smart contracts.*

## Fees & Treasury

Liquidswap fees are configurable by treasury multisig and later by DAO.&#x20;

In the current version, all uncorrelated pools are created with default 0.3% fees, and stable pools create with default 0.04% fees.

Out of this, 66.7% goes to the liquidity providers, while the remaining 33.3% is sent to the treasury contract created for each liquidity pool on the DEX by default. Still, DAO fees are also configurable and can differ pool to pool.

The treasury multisignature can change the fee percent for individual pools.&#x20;

To redeem their accumulated fee rewards, liquidity providers can burn their LP tokens (and redeem the deposited liquidity in the process).

The treasury itself will be initially managed by a multisignature of Pontem and trusted 3rd parties (e.g. Pontem investors, prominent community members, etc.) and will later be migrated to a full-fledged treasury contract managed by the Pontem DAO.

#### Configuration

Current configuration:

* Default fee for uncorrelated pools - 0.3%
* Default fee for stable pools - 0.04%
* Default DAO fee - 33% from general fee.

The treasury or DAO always can change default fees and fees per pool if it follows the next rules:

* Minimum fee for both curves - 0.01%.
* Maximum fee for both curves - 1%.
* Minimum DAO fee - 0%.
* Maximum DAO fee - 100%.

## Stable swaps

Decentralized swaps between stablecoins like USDT, USDC, and DAI have always been a challenge for popular DEXes like Uniswap or SushiSwap because of high slippage – the change in price that occurs as a result of a swap. The problem lies in the standard formula used to calculate the relative prices of two assets in a pool: `x * y = k`, also known as the constant product formula described above.

Without going into too much technical detail, we can say that a single large swap can significantly affect a token’s price, especially when liquidity in the pool is shallow. For stablecoins this is unacceptable, as they should always trade 1:1 (or almost) to each other.

The same goes for pairs like BTC/WBTC and ETH/WETH. They are also called correlated assets, as their relative prices are (or should be) almost ideally correlated.

The solution is to use a different formula for swaps between correlated assets – one that can keep slippage minimal even for large transactions. So far, one of the most successful formulas was proposed by Solidly, a stablecoin DEX on Fantom built by Andre Cronje. It looks as follows:

`x^3*y + x*y^3 = k`.

![Stable Curve vs Uncorrelated One](/files/Jl6VIYRePR8EcRIDGzEa)

By default, Liquidswap supports both curves and allows for the creation of two types of pools: uncorrelated pairs and stables.

#### Decimals  of stable pairs:warning:

If coins used in a stable pool have more than eight decimals, the liquidity operations in the pool can lead to rounding errors and loss of precisions. The Liquidswap DApp would show a warning for such pools, yet we are pretty sure eight decimals is enough for any coin on Aptos.

## Emergency brake

As the Aptos blockchain, the Move language, and Move VM are all very new technologies, it will take time for them to be fully verified and tested. For this reason, we have implemented an emergency brake to stop all liquidity minting and swaps, while simultaneously allowing liquidity providers to burn their LP tokens.

We hope that we will never have to activate the emergency brake, as we have also performed several security audits. Yet, we still want to keep the 'emergency button' in case of an unlikely event of a protocol-level or virtual machine-level issue.

The emergency feature is currently managed by the Pontem team [MSafe](https://github.com/Momentum-Safe) multisignature and can later be transferred to a DAO.

Once we have verified that the protocol is stable and secure enough, we may disable the emergency brake indefinitely.

## Formal verification

Formal verification is a powerful and promising feature of the Move language, but it is still little tested and not perfectly adapted for some use cases.

A formal verification of Liquidswap presents a number of challenges. The team is working on it, but for the time being there is no fixed deadline for its completion.


# Smart Contracts

The contracts are published in the Liquidswap GitHub repository at <https://github.com/pontem-network/liquidswap>.

Liquidswap uses smart contracts written in the Move language and executed by Move VM. The core contracts containing the protocol's logic and safety guarantees and are separated from the periphery contracts, allowing for a well-tested, reasonably small core layer and multiple versions of the periphery that can work with  the core utilizing their own custom logic.

For example, the basic `Router.move`, used by Liquidswap's frontend, proxys all Liquidity Pool module methods with additional safety and price checks. At the same time, traders who want to minimize gas usage can write their own custom routers or call the core contracts directly.

## Branches and versions

The current `main` branch is the development branch and always contains the latest changes.

Release branches contain the latest changes created for the specific release.

The current release tag `v0.4.4` are deployed and released on Aptos mainnet.

You can always find the latest release in the [Releases](https://github.com/pontem-network/liquidswap/releases) section.

### Routers/scripts versions

As router and scripts are part of periphery we can update router/scripts and release new version time to time, so there can be many routers versions and releases. All official routers/scripts are part of Liquidswap core repository.&#x20;

#### Router v1

**Not recommended to use!** :warning:

Source code - [./sources/swap/router.move](https://github.com/pontem-network/liquidswap/blob/main/sources/swap/router.move)

It contains a u64 overflow bug in `get_amount_in` and `get_amount_out`, so not advised to use, as both functions can return overflow errors in case numbers are too high.

#### Router v2

Source code - [./liquidswap\_router\_v2](https://github.com/pontem-network/liquidswap/tree/main/liquidswap_router_v2)

The Router v2 contains bug fixes for functions in router v1, also scripts are updated too.&#x20;

#### Optional routers

* Router v3 - contains bug fixes with accuracy (1-2 units) for stable swaps (see [Zellic audit](/#zellic)): functions to get the amount in or out. Yet, we aren't going to migrate as the solution would eat more gas. The router available in [branch](https://github.com/pontem-network/liquidswap/tree/router-v3) and deployed to mainnet/testnet/devnet. &#x20;

### Addresses

All liquidity pools resources and LP coins currently placed at the following resource account:

```
0x05a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c948
```

Liquidswap modules are deployed at the following address:

```
0x190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12
```

**All smart contracts and dependencies (exclude that placed on `0x1` address) are immutable.**

#### **Devnet**

For Aptos Devnet support, we used specific different addresses, to be able to deploy contracts each devnet reset.

Use the [devnet-addresses](https://github.com/pontem-network/liquidswap/tree/devnet-addresses) branch, look at [Move.toml](https://github.com/pontem-network/liquidswap/tree/devnet-addresses) to get details: dependencies also has it's own devnet branch, while test coins has the same addresses from testnet.

## Integrations

This document explains how Liquidswap contracts are organized at the top level.

If you are already familiar with this material, you can proceed to the [integrations](/integration) docs.

## Liquidity Pool

Source Code: [./sources/swap/liquidity\_pool.move](https://github.com/pontem-network/liquidswap/blob/main/sources/swap/liquidity_pool.move).

The Liquidity Pool module implements all swap logic, LP logic, math (constant product formula), and core checks.

The core part of the Liquidity Pool contract is a resource that describes the liquidity pools themselves and contains all the required information:

{% code lineNumbers="true" %}

```rust
struct LiquidityPool<phantom X, phantom Y, phantom Curve> has key {
    coin_x_reserve: Coin<X>,
    coin_y_reserve: Coin<Y>,
    last_block_timestamp: u64,
    last_price_x_cumulative: u128,
    last_price_y_cumulative: u128,
    lp_mint_cap: coin::MintCapability<LP<X, Y, Curve>>,
    lp_burn_cap: coin::BurnCapability<LP<X, Y, Curve>>,
    // Scales are pow(10, token_decimals).
    x_scale: u64,
    y_scale: u64,
    locked: bool,
    fee: u64,           // 1 - 100 (0.01% - 1%)
    dao_fee: u64,       // 0 - 100 (0% - 100%)
}
```

{% endcode %}

* Reserves of both tokens `X` and `Y`.
* Curve type: `phantom Curve`.
* Cumulative price information.
* Mint and burn capabilities for `LP` coins.
* Token decimal scales (used for stable swaps).
* If a pool is locked (needed for the flashloans).

Almost like Uniswap the Liquidswap creates pools on the reserved address:

```
0x05a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c948
```

All pools are unique, so there can't be two pools containing the same coins, and it also works for LP and LP generics.

To avoid confusion, Liquidswap uses a frontend interface with a list of pools already pre-filled and a pools registry to show the users mostly verified pools.

### Generics

A set of `(X, Y, Curve)` uniquely identifies a liquidity pool on the blockchain.

Because of that, all functions that refer to pools have three generic parameters:

```
public entry fun swap<X, Y, Curve>(...) {} 
```

* `X, Y` - the tokens to be swapped in a pool, for example, `0x1::aptos_coin::AptosCoin` and `test_coins::coins::USDT`(wrapped USDT)
* `Curve` - type of the Curve corresponding to the pool, explained below.
* Note: **Generics X and Y must be sorted**, see [Coin Sorting](#coins-sorting).

### Functions

**In most cases, you won't need to use the `LiquidityPool` module itself. The `Router` module provides high-level wrappers around `LiquidityPool`, which simplify most of the tasks.**

Operations with liquidity:

* `register<X, Y, Curve>` - creates fora new liquidity pool on reserved account.
* `mint<X, Y, Curve>` - mints new LP coins in exchange for `Coin<X>` and `Coin<Y>`.
* `burn<X, Y, Curve>` - burns some LP coins in exchange for `Coin<X>` and `Coin<Y>`.
* `swap<X, Y, Curve>` - swaps `Coin<X>` or `Coin<Y>` or both to get `Coin<X>` or `Coin<Y>` or both in exchange.
* `flashloan<X, Y, Curve>` - flashloan reserves `Coin<X>` and/or `Coin<Y>` from the pool, also returns `Flashloan` object.&#x20;
* `pay_flashloan<X, Y, Curve>` - pay flashloan by returning `Coin<X>`, `Coin<Y>` and `Flashloan` object.

Getters:

* `is_pool_locked<X, Y, Curve>` - returns bool determining if the pool is locked (flashloaned).
* `get_reserves_size<X, Y, Curve>` - returns the current reserves size for both `Coin<X>` and `Coin<Y>`.
* `get_curve_type<X, Y, Curve>` - returns the curve type for the pool.
* `get_decimals_scales<X, Y, Curve>` - returns the decimal scales for both `Coin<X>` and `Coin<Y> - but` correct values are returned only for stable pools.
* `pool_exists_at<X, Y, Curve>` - returns bool determining if the pool exists.
* `get_cumulative_prices<X, Y, Curve>` - return cumulative prices and the last block's timestamp.
* `get_fees_config<X, Y, Curve>` - returns fees config for the pool (both nominator fee and denominator).
* `get_fee<X, Y, Curve>` - return the fee nominator for pool.
* `get_dao_fees_config<X, Y, Curve>` - returns dao fees config for the pool (both nominator fee and denominator).
* `get_dao_fee<X, Y, Curve>` - return the dao fee nominator for pool.&#x20;

#### Coin Sorting

To work with Liquidity Pool module functions, you must sort the coins you pass on as generics.

Sorting is important as it brings in the rules for creating liquidity pools. All functions in the Liquidity Pool module accept only sorted generics; otherwise, it reverts.

For the Router and Scripts modules, **only part** of the functions requires generics to be sorted.

The current sorting algorithm takes the types of provided coins, e.g., `address::module::struct_name`, and compares the struct name of both coins. If it's equal, it continues with the module name and, in the end, with the address.

You always can just use the implementation in Coin Helper module: [coin\_helper.move](https://github.com/pontem-network/liquidswap/blob/main/sources/libs/coin_helper.move#L44).

Other languages:

* Implementation in Javascript: [Coin Sorting In JS](https://gist.github.com/borispovod/2809728c8959649d42c5cef15b4cedb7).
* Also, it's supported in [Typescript SDK](/typescript-sdk).

### Curve types

Source code: [sources/swap/curves.move](https://github.com/pontem-network/liquidswap/blob/main/sources/swap/curves.move)

When creating a pool, the curve type can be provided; it's as simple as using a generic to determine the pool type.

The types themself:

```rust
/// For pairs like BTC, Aptos, ETH.
struct Uncorrelated {}

/// For stablecoins like USDC, USDT.
struct Stable {}
```

* `liquidswap::curves::Uncorrelated`- Uncorrelated curve type.
* `liquidswap::curves::Curve` - Stable curve type.

The following types can be imported into code and should be used as the last generic argument in almost all modules functions.

Functions:

* `is_uncorrelated<Curve>` - returns `true` if provided type is `Uncorrelated`.
* `is_stable<Curve>` - returns true if provided type is `Stable`.
* `is_valid_curve<Curve>` - returns true if provided type is `Stable` or `Uncorrelated`.
* `assert_valid_curve<Curve>` - abort if provided type is not a curve type.

You can read more about Liquidswap's curve formulas on the [Protocol Overview](/protocol-overview) page.

### LP coins

Source code: - [liquidswap\_lp/sources/lp\_coin.move](https://github.com/pontem-network/liquidswap/blob/main/liquidswap_lp/sources/lp_coin.move)

*Consider that LiquidswapLP is an independent module inside the liquiswap repository, so it must be imported as a separate dependency.*

The `LP` coins type for pairs `X` and `Y` is represented as `LP<X, Y, Curve>` and deployed on the following resource account:

```
0x05a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c948
```

The LP coin registers automatically for each new liquidity pool, and generics are always sorted.

For `APT/BTC` uncorrelated pool, the LP coin would look so (*the coins addresses are testnet*) :

```

0x05a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c948::lp_coin::LP<
    0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC,
    0x1::aptos_coin::AptosCoin,
    0x190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12::curves::Uncorrelated,
>
```

### Flashloans

The implementation is based on Move's loan concept, where a Move object containing the loan data is issued but cannot be stored, copied, cloned or dropped, the only available action being to return the object back to the `pay_flashloan` function, which will verify the resulting constant product function.

The concept was explained early on by the Pontem team in this [Medium article](https://medium.com/p/bbc48a48d93c).

Read more in the [integration section](/integration/flashloans).

## Routers

Source code - [./liquidswap\_router\_v2/sources/router\_v2.move](https://github.com/pontem-network/liquidswap/blob/main/liquidswap_router_v2/sources/router_v2.move)

**It's recommended to use Router v2** :warning: **Read about** [**router versions**](#routers-versions)**.**

The Router module is a periphery layer on top of the Liquidity Pool module.

The Router contains additional checks to verify that the amount that a developer, trader, or liquidity provider wants to exchange/mint/burn is reasonable.

Also, the module sorts tokens or coins automatically for **part of functions** and has several useful getters that help estimating the swap price.

In most cases, we recommend using a Router if you want to work with the Liquidity Pool module.

Third-party teams can provide their own routers, but Liquidswap's standard Router should be enough for most cases. An added advantage is that it's already audited.

### Functions

The router functions accept the resources `Coin<X>,` `Coin<Y>`, `Curve` similar to Liquidity Pool module.&#x20;

However, the functions cannot be called directly from a transaction; if you need entry points, refer to [Scripts](#scripts).

Developers interacting with **swap functions and getters** can order generics in any way they wish. For example, if one wants to swap `aptos_framework::aptos_coin::AptosCoin` to `test_coins::coins::USDT, one`can just use the function:

```
router::swap_exact_coin_for_coin<
    aptos_framework::aptos_coin::AptosCoin,
    test_coins::coins::USDT,
    curves::Uncorrelated,
>
```

A reverse swap (`USDT` -> `APTOS`) can be done by reordering the generics:

```
router::swap_exact_coin_for_coin<
    test_coins::coins::USDT,
    aptos_framework::aptos_coin::AptosCoin,
    curves::Uncorrelated,
>
```

**Important note:** the rest of functions (`register_pool`,`add_liquidity`, `remove_liquidity`) requires **sorted generics!**

Liquidity operations:

* **Requires sorted generics:**
  * `register_pool<X, Y, Curve>` - register a new pool with `Coin<X>` and `Coin<Y>` as reserves and provided curve.
  * `add_liquidity<X, Y, Curve>` - add liquidity (`Coin<X>` and `Coin<Y>`) to an existing pool.
  * `remove_liquidity<X, Y, Curve>` - burn `Coin<LP<X ,Y, Curve>>` and get `Coin<X>` and `Coin<Y>` back with checks.
* **Generics can be sorted in any way:**&#x20;
  * `swap_exact_coin_for_coin` - swap an exact amount of `Coin<X>` to get no less than the specified amount of `Coin<Y>`.
  * `swap_coin_for_exact_coin` - swap no more than the specified maximum amount of `Coin<X>` to get an exact amount of `Coin<Y>`.
  * `swap_coin_for_coin_unchecked` - simply swaps tokens without any checks.

**Getters:**

* `get_amount_out<X, Y, Curve>` - estimate the amount of `Y` tokens resulting from a swap of a specified amount in`X` tokens. Can consume a lot of gas when used for stable pools.
* `get_amount_in<X, Y, Curve>` - estimate the amount of `X` tokens needed to get a specified amount in`Y` tokens.
* `calc_optimal_coin_values<X, Y, Curve>` - calculates the optimal amounts of `Coin<X>` and `Coin<Y>` needed to add liquidity and get a fair amount of LP coins.
* `get_reserves_size<X, Y, Curve>` - returns the reserves for both coin X and coin Y held by the pool.
* `pool_exists_at<X, Y, Curve>` - returns bool determining if the pool exists.
* `get_decimals_scales<X, Y, Curve>` - returns the pool's decimals scale. The resulting values will be correct only for stable pools.
* `get_cumulative_prices<X, Y, Curve>` - returns cumulative prices and the last block's timestamp.
* `get_reserves_for_lp_coins<X, Y, Curve>` - returns the amounts in `Coin<X>` and `Coin<Y>` that a user will receive after burning LP coins.
* `get_fees_config<X, Y, Curve>` - returns fees config for the pool (both nominator fee and denominator).
* `get_fee<X, Y, Curve>` - return the fee nominator for pool.
* `get_dao_fees_config<X, Y, Curve>` - returns dao fees config for the pool (both nominator fee and denominator).
* `get_dao_fee<X, Y, Curve>` - return the dao fee nominator for pool.

## Scripts

Source code - [./liquidswaprouter\_v2/sources/scripts\_v2.move](https://github.com/pontem-network/liquidswap/blob/main/liquidswap_router_v2/sources/scripts_v2.move)

The top-level module contains the entry functions that users can execute directly by sending transactions and that third-party modules can use via `&signer`.

Summary:

* Requires a signer;
* Accepts numbers as arguments representing coins' values;
* Extracts coins/tokens directly from the signer account;
* Registers a LP token on the account if it's not registered;
* Works with default [Router](#router) v2.

This is the optimal way to interact with Liquidswap if you want to call a swap from the CLI or the UI using the standard router.

### Functions

All the functions have 'slippage' amount arguments, like `coin_x_val_min`, `coin_y_val_min` or `min_x_out_val`, etc.

* **Requires sorted generics:**
  * `register_pool<X, Y, Curve>` - register a pool with the specified curve type.
  * `register_pool_and_add_liquidity<X, Y, Curve>` - register a pool with the specified curve type and add liquidity immediately (extracted from the signer's balance), with the resulting LP tokens deposited to the signer.
  * `add_liquidity<X, Y, LP>` - add liquidity to an existing pool, with the resulting LP coins deposited to the signer's address.
  * `remove_liquidity<X, Y, Curve>` - burns users LP coins and deposit received coin X and coin Y on user account.
* **Generics can be sorted in any way:**&#x20;
  * `swap<X, Y, Curve>` - swap `Coin<X>` for `Coin<Y>;` the developer has to provide the amount of `X` coins `coin_val` to get the minimum amount `coin_out_min_val` of Y.
  * `swap_into<X, Y, Curve>` - swap no more than the specified maximum amount of `Coin<X>` for an exact amount in `Coin<Y>`. The remainder of `Coin<X>` will be deposited back to the account.
  * `swap_unchecked<X, Y, Curve>` - simply swaps tokens without any additional checks (best price, etc).

## Helpers

Source Code: [.sources/libs](https://github.com/pontem-network/liquidswap/tree/main/sources/libs)

The helpers library improves code readability by extracting parts of the math, token interactions, and other things to separate files.

### Coin Helper

Source Code: [.sources/libs/coin\_helper.move](https://github.com/pontem-network/liquidswap/tree/main/sources/libs/coin_helper.move)

Mostly helpers on top of `aptos_framework::coin`.

#### Functions

* `assert_is_coin<CoinType>` - aborts if the provided `CoinType` is not a coin/token.
* `compare<X, Y>` - compares `X` coin symbols and `Y` coin symbols.
* `is_sorted<X, Y>` - returns bool determining if `X` and `Y` tokens are sorted.
* `supply<CoinType>` - extracts supply from `LP`, ignoring `Option`.
* `generate_lp_name<X, Y, Curve>` - generates a name and symbol for the `LP` token from `X` and `Y` symbols and `Curve`.

### Math

Source Code: [.sources/libs/math.move](https://github.com/pontem-network/liquidswap/tree/main/sources/libs/math.move)

Implements basic math helpers.

#### Functions

* `overflow_add` - adding two `u128` and just overflowing the numbers if needed without aborting.
* `mul_div` - `x * y / z` for `u64` numbers.
* `mul_div_u128` - `x * y / z` for `u128` numbers but returns `u64`.
* `mul_to_u128` - muls two `u64` to `u128`.
* `sqrt` - get the square root using the Babylonian method.
* `pow_10` - returns `10^degree`.

### Deps libraries

The smart contracts utilize the following math libraries created by the Pontem team:

* [u256](https://github.com/pontem-network/u256) - a pure Move `u256` implementation because some numbers don't fit in `u128`.
* [uq64x64](https://github.com/pontem-network/uq64x64) - the Q number format for cumulative price calculations.

### Advanced topics

#### VE(3,3)

The `ve(3,3)` logic is currently under active development and can undergo significant changes; stay tuned.

#### Oracle

Liquidswap's oracle model is based on the cumulative price algorithm used by Uniswap v2. For this reason, token price variables on Liquidswap can overflow, just like on Uniswap.

The following fields in the Liquidity Pool resource are responsible for storing prices:

```
last_block_timestamp: u64,
last_price_x_cumulative: u128,
last_price_y_cumulative: u128,
```

Meanwhile, the Liquidity Pool module's function `get_cumulative_prices<X, Y, Curve>` extracts prices from the pool.

If you are interested in the Oracle implementation, refer to our [integration](/integration) guides.

#### Treasury

Source Code: [.sources/swap/dao\_storage.move](https://github.com/pontem-network/liquidswap/blob/main/sources/swap/dao_storage.move)

The DAO Treasury receives a fee from each swap transaction in every liquidity pool on the protocol.

The treasury is currently managed by Treasury multisig and will be eventually transferred to a full-fledged Pontem DAO.

#### Config & Dynamic Fees

Source code: [./sources/swap/global\_config.move](https://github.com/pontem-network/liquidswap/blob/main/sources/swap/global_config.move)

Most of the functions mostly needed to migrate Treasury accounts from one to another and change default fees.&#x20;

The [liquidity pool](https://github.com/pontem-network/liquidswap/blob/main/sources/swap/liquidity_pool.move) module contains functions to change individual pools fees.

Read more fees concept: [Fees & Treasury](/protocol-overview#fees-and-treasury).

#### Emergency

Source Code: [.sources/swap/emergency.move](https://github.com/pontem-network/liquidswap/blob/main/sources/swap/emergency.move)

Can be used to pause/resume all swaps via an emergency account.

The contract itself can be disabled forever once the team can verify that the protocol is fully stable and will not be derailed by any liquidity event, attack etc.


# V0.5 Update

In March 2023, a new version of the Liquidswap contracts - v0.5 - was released.

The main reasons for the protocol upgrade are as follows:

* The latest version of Move now supports `u256` types, which allows for cheaper gas-wise stable swaps.
* As our v0 upgrade policy is `immutable`, we couldn't upgrade v0, so we deployed v0.5 on both the testnet and mainnet and upgraded the UI.
* Stable pools liquidity will be migrated to v0.5 over time.
* We strongly recommend that new pools be created using v0.5.
* The current UI creates new pools using v0.5.
* Update has been reviewed by Ottersec team.

The contracts have been deployed on both the mainnet and testnet at the following address:

```
0x0163df34fccbf003ce219d3f1d9e70d140b60622cb9dd47599c25fb2f797ba6e
```

Pools stored on the following resource account:

```
0x61d2c22a6cb7831bee0f48363b0eec92369357aece0d1142062f7d5d85c7bef8
```

LP coins module deployed on the following resource account:

```
0x61d2c22a6cb7831bee0f48363b0eec92369357aece0d1142062f7d5d85c7bef8
```

#### Smart Contracts

The smart contract logic remains mostly the same for v0.5, so all examples and logic should work in the same way. However, for v0.5, there are no different versions of the router, as the default router has already been deployed with all the fixes we made during v0.

Additionally, since gas usage has been reduced  for v0.5, you are welcome to use router best price swap functions, getters, etc. for stable swaps again.

**LP name & Symbols**

LP coins of v0.5 version has the following name and symbol format:

* Name - `LS05 LP-{coin_x_symbol}-{coin_y_symbol}-{curve: S or U}`
* Symbol - `{coin_x_symbol}-{coin_y_symbol}{curve: S or U}`


# Integration

* The smart contracts' source code can be found in the [Liquidswap](https://github.com/pontem-network/liquidswap) repository.
* The current version of Liquidswap is deployed on the address&#x20;

  ```
  0x190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12
  ```
* The latest release tag can be found on the[ Releases](https://github.com/pontem-network/liquidswap/releases) page.

Before we start, create a new Move project so that you can repeat the steps as described here.

#### Supported networks

The Aptos mainnet and testnet, devnet are currently supported.&#x20;

For both testnet and mainnet you can use latest tags, for [devnet](/smart-contracts#devnet) see details.

### Add as dependency

To integrate Liquidswap into your project, you first need to add a dependency to your `Move.toml`

{% code lineNumbers="true" %}

```toml
[dependencies.Liquidswap]
git = 'https://github.com/pontem-network/liquidswap.git'
rev = 'latest version'
```

{% endcode %}

Import [Router v2:](/smart-contracts#router-v2)

```rust
[dependencies.LiquidswapRouterV2]
git = 'https://github.com/pontem-network/liquidswap.git'
subdir = 'liquidswap_router_v2/'
rev = 'latest version'
```

Add the LP coin module as a dependency too:

```toml
[dependencies.LiquidswapLP]
git = 'https://github.com/pontem-network/liquidswap.git'
subdir = 'liquidswap_lp/'
rev = 'latest version'
```

**Replace `'latest version'` with the actual ones from the repositories.**

Next, try compile; you shouldn't get any errors.

*Liquidswap's contracts always point to the latest `AptosFramework` devnet revision. If you encounter issues after adding our dependencies, remove `dependencies.AptosFramework` completely from your project.*


# Test Coins

**This part works only for testnet/devnet, as on mainnet there is production ready coins should be used!**

During the integration, you probably would need test coins, so we prepared a repository containing several coins (USDT, BTC, USDC, DAI, ETH) just for tests.

The existing pools on Liquidswap use our test coins, so it's a good idea to follow standard test coins during integration. &#x20;

Repository - [test-coins](https://github.com/pontem-network/test-coins)

The following test coins are already deployed on the testnet/devnet and can be used in your project:

```
// Test BTC
0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC

// Test USDT
0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::USDT
```

Add as dependency:&#x20;

```toml
[dependencies.TestCoins]
git = 'https://github.com/pontem-network/test-coins.git'
rev = 'latest version'
```

Use in your code:

```rust
use test_coins::coins::{USDT, BTC};
use test_coins_extended::extended_coins::{USDC, ETH, DAI};
...
```

#### Faucet

Repository - [faucet](https://github.com/pontem-network/faucet)

Tests coins are available on Aptos testnet/devnet, to get free coins just do calls to faucet or visit [Liquidswap DApp](https://testnet.liquidswap.com).&#x20;


# Let's Swap

**Always practice solid risk management rules when experimenting with swaps: use the correct slippage values and trusted currency pairs, double-check the numbers before confirming, and compare external price sources.**

Throughout this guide, we will use [Router](/smart-contracts#router) and try to swap Aptos coins to test BTC.

Let's create a new module and import the router into it:

{% embed url="<https://gist.github.com/borispovod/62ba3b63a335267221922db3eaa05891>" %}
Basic module
{% endembed %}

As you try to compile the code above, you will get a warning; don't worry, it's fine.&#x20;

Now let's add the dependencies we will need for this experiment, as well as a basic entry function that we can call in a transaction later:

{% embed url="<https://gist.github.com/borispovod/1a1ec9b2ae21c0e0d75775f4fd9e6ecd>" %}

Let's withdraw `1000` Aptos coins from an account and see how many BTC we can get in exchange:

{% embed url="<https://gist.github.com/borispovod/c0d434a0c944009d83e18b2b7187b98f>" %}

The provided code above wouldn't still compile as we can't drop Aptos coins; at the same time, it already shows how much BTC we can get back for 1000 Aptos coins because of `get_amount_out` function.

Now let's indeed do a swap and finally deposit the swapped coins on our account:

{% embed url="<https://gist.github.com/borispovod/019a3b99214845278d2339bef40aaf2c>" %}

Everything worked out well; using `swap_exact_coin_for_coin,` we have swapped Aptos coins for BTC.

This looks simple, but what if someone decided to front-run us? Let's add some basic slippage checks for the minimum amount of BTC we want to receive (afterwards we can remove `get_amount_out`).

{% embed url="<https://gist.github.com/borispovod/871dfeddbffba5396e5a605a46d7ef9c>" %}

All done. In the same way, you can try another function: `router::swap_coin_for_exact_coin` (see [Router](/smart-contracts#router) docs).

*An important note regarding decentralized AMM securities and user transactions:*

* *Always use safety checks.*
* *Never swap tokens without a slippage value provided offline.*
* *Use external price feeds or oracles (even the* [*Basic Oracle*](/integration/basic-oracle) *can be a good option).*


# Create Pool

Now let's create a new pool utilizing Router. First of all, deploy your new own coin just for test:

{% embed url="<https://gist.github.com/borispovod/a57e4de806dea23195412eb08f731f70>" %}
Test coin for new pool
{% endembed %}

Extend the example from the previous guide and add a new function:

{% embed url="<https://gist.github.com/borispovod/5caea21cc3777372ce18cbb03cd6cac0>" %}
Create a new pool
{% endembed %}

The function above creates a new pool for your coin: `MyCoin/AptosCoin`. As Uncorrelated generic provided, the formula for the pool would be standard one: `x*y=k`.

It's very important to follow the rule about generic sorting as otherwise, the function would abort.

#### Creating a stable pool

In the same way, you can create a stable pool by use `Stable` generic:

{% embed url="<https://gist.github.com/borispovod/1fbb2f7c3fd76a50371241ea89700c64>" %}
Create stable pool
{% endembed %}

### Add liquidity to your pool

Let's add liquidity to new pool:

{% embed url="<https://gist.github.com/borispovod/c6abe785342fd3a18059d7e155647dd4>" %}
Adding liquidity to our new pool
{% endembed %}

As you can see above, `add_liquidity` allows to add liquidity to the newly created pool and then deposit the remainders of both `X` and `Y` coins back in the account, together with the new `LP` coins.

Similarly, you can add liquidity to any other pool: replace `MyCoin` and `AptosCoin` with other coins.

*When working with real-world examples, it's better to deposit liquidity immediately during the same transaction with which you create a pool. We also recommend using slippage values for the amount of the LP tokens you'll get in exchange for your liquidity.*

### Adding liquidity to an existing pool

Let's say we want to add liquidity to an existing `BTC/Aptos` pool created by the Pontem team:

{% embed url="<https://gist.github.com/borispovod/cc613eb511c4025641f18c7060fa9a60>" %}
Adding liquidity to an existing pool
{% endembed %}

*When working with real-world examples, it's better to deposit liquidity immediately during the same transaction with which you create a pool. We also recommend using slippage values for the amount of the LP tokens you'll get in exchange for your liquidity.*


# Burn Liquidity

The following example shows how to burn liquidity in the BTC / AptosCoin pool deployed by the Pontem team, where slippage can be provided as an argument:

{% embed url="<https://gist.github.com/borispovod/faf89d87841302c08f71d439283b1f56>" %}


# Basic Oracle

As Liquidswap accumulates cumulative prices for each pair at the start of each block, in case liquidity operation happens on the pool, developers can build their oracle with it.

First of all, add [overflowing math](https://github.com/pontem-network/overflowing-math) as dependency to your project:

```toml
[dependencies.OverflowingMath]
git = "https://github.com/pontem-network/overflowing-math.git"
rev = "v0.1.0"
```

It's important to use overflowing math when works with cumulative prices of Liquidswap, as cumulative prices can overflow and it's ok.&#x20;

The basic example of oracle would be the following contract:

{% embed url="<https://gist.github.com/borispovod/a11b1a9019ef26d7f23c7297a68c0066>" %}


# Flashloans

The Liquidswap allows to loan liquidity from pools, use that liquidity in any 3rd party DeFi protocol or even in another Liquidswap pool itself, and return liquidity to the pool by paying the pool's standard fee.

The loan must happen in one transaction **(and it's impossible to loan and then return the loan in a separate transaction!)** and be returned in one transaction, which means Liquidswap liquidity never drains.

### Smart Contracts

Source code - .[/sources/swap/liquidity\_pool.move](https://github.com/pontem-network/liquidswap/blob/main/sources/swap/liquidity_pool.move)

The idea of Flashloan is the **"Hot Potato"** concept; read more in [the medium article](https://medium.com/@borispovod/move-hot-potato-pattern-bbc48a48d93c); in a nutshell: because Move language structs have abilities, we can create a such structure that can't be copied, dropped, stored, but only can be destructed.&#x20;

In same time Move language allows to objects only in modules where initial structure defined. &#x20;

So by creating and returning the Flashloan object to the loaner, we force the loaner to return the loan together with the Flashloan object by calling the function used to pay the loan.

```rust
struct Flashloan<phantom X, phantom Y, phantom Curve> {
    x_loan: u64,
    y_loan: u64
}
```

Functions to implement flashloan in your code:

**Most of the functions of the specific LiquidityPool\<X, Y, Curve> wouldn't work during flashloan on that pool.**

The list of flashloan functions

* `flashloan<X, Y, Curve>(x_loan, y_loan)` - to loan X and Y coins from the pool.
  * **Generics X and Y must be sorted.**
  * `x_loan` - amount of X coins to loan.
  * `y_loan` - amount of Y coins to loan.
  * Returns `Coin<X>`, `Coin<Y>`and `Flashloan<X,Y, Curve>` object. &#x20;
* `pay_flashloan<X, Y, Curve>(x_in, y_in, loan):`
  * **Generics X and Y must be sorted.**
  * `x_in` - `Coin<X>` to return to pool.
  * `y_in` - `Coin<Y>` to return to the pool.

### Example

{% embed url="<https://gist.github.com/borispovod/1269f2a70e0963425ca62892f521f667>" %}
Flashloan Example
{% endembed %}


# Unit Testing

This part explains how to write unit tests during integration with Liquidswap Smart Contracts.

### Testing integration

To be able to run tests, you need to prepare the Liquidswap state in your test. There's a couple of utilities available to help you with that:

* `public fun initialize_liquidity_pool()` \[[source](https://github.com/pontem-network/liquidswap/blob/main/sources/test_helpers/test_pool.move#L30)]

It correctly initialize both Liquidswap and `LP<X, Y, Curve>` token of the Liquidswap. That process is pretty involved, so we provide this helper function to make it easier. Run it after the `genesis::setup()` call from Aptos Framework.

* `public fun mint_liquidity<X, Y, Curve>(lp_owner: &signer, coin_x: Coin<X>, coin_y: Coin<Y>): u64` \[[source](https://github.com/pontem-network/liquidswap/blob/main/sources/test_helpers/test_pool.move#L54)]

Adds `coin_x`, `coin_y` to the pool, and automatically deposits returning lp coins to the `lp_owner` account, handling the edge cases.

An example of the test would be:

{% embed url="<https://gist.github.com/borispovod/41d239593acd6d1b8b1f4ad5110ae130>" %}
Example of unit test
{% endembed %}

#### More examples

To learn Unit Tests more look at our Liquidswap [Unit Tests](https://github.com/pontem-network/liquidswap/tree/main/tests) and [Test Helpers](https://github.com/pontem-network/liquidswap/tree/main/sources/test_helpers).


# Staking / Harvest

<figure><img src="/files/bd9NtrK2ABK36VwafQFF" alt=""><figcaption></figcaption></figure>

**Why launch Harvest?**

The objective of Liquidswap’s Harvest staking program is to incentivize liquidity providers by rewarding them with APT and third-party coins (tokens) issued on Aptos.&#x20;

Staking is easy to use, fully permissionless, and independent at the same time. Other projects can use the Harvest solution to configure and launch their own staking pools. You don’t even have to use Liquidswap LPs: any assets issued on Aptos can be used for staking and as rewards.

The staking contracts are written in Move. The key features are:

* The contracts are permissionless: anyone can create a new staking pool.
* Any coins\* can be used as the staking or reward asset.
* NFTs can be used as an additional parameter for the pool to provide reward boosts to the users holding specified NFTs.
* Rewards can be deposited into the pool at any time to extend the harvesting period.
* Users can unstake their coins without losing any of the profit after 1 week of staking - or once the harvesting period is complete.
* Users can harvest (withdraw) the accumulated rewards at any time.

The easiest way to use the staking protocol is via the[ Liquidswap Dapp](https://liquidswap.com/). Alternatively, continue reading the documentation to learn more about the protocol and its smart contracts.

### Security audits

The staking contracts have been reviewed and audited by the Ottersec and Halborn security teams.

**Ottersec**

{% file src="/files/ixtSL3iK2mag3r4yhHDF" %}

**Halborn**

{% file src="/files/ptIaXGaEeQu4qbNfoKor" %}

*\*In Aptos, the term ‘coin’ is used to denote both what is called ‘coins’ and ‘tokens’ in other blockchain ecosystems. Thus, we use ‘LP coins’ here as the correct term in the context of the Aptos architecture, though the meaning is the same as in ‘LP tokens’ on other chains.*


# Overview

Liquidswap staking allows developers to create new staking pools, configuring the stake and reward coin types and the duration of the harvest period.

A staking pool stores all the information regarding staking itself, such as rewards, stakes, duration, etc. Any new staking pool will be created and stored on the pool creator's account.

At the point of creation, the pool will calculate the number of reward coins that can be distributed per second using the following formula:

`reward_per_second = rewards / duration`&#x20;

All accumulated rewards will be split between all the stakers during harvesting. If there are no stakers, all accumulated rewards will go to the [treasury](#treasury).&#x20;

In the first step, the accumulated reward is calculated and divided by the total staked amount:

`accum_reward = (reward_per_sec * time_passed) / total_stake`

In the second step, we calculate the user's reward:

`user_reward = (accum_reward * user_stake) - unobtainable_reward`

The 'unobtainable reward' part is used to fix the rewards that were accumulated before the user joined the pool and prevents the user from claiming excessive rewards in specific cases.

### Weekly Lock

All stakes are locked for one week by default. After that initial week elapses, stakers can unlock their coins. If the harvest period finishes early, it's also possible to unlock early.

### Harvest

To earn rewards, users should perfrom harvesting from time to time by sending a transaction to call the harvest function from. It can be done via the dApp user interface.

### Extending the harvest period

Depositing more rewards into the pool becomes possible immediately after the pool is created. Anyone who holds reward coins can do it before the end of the harvest period.

Whenever additional rewards are deposited into the pool, the harvest period is extended by the amount of time calculated according to the formula:

`extend_seconds = extended_rewards / rewards_per_sec`

Therefore, if you want to extend the harvest period, simply deposit more rewards into the pool.

### After the harvest period

After the harvest period (once the end timestamp is reached), it is still possible for a user to harvest their remaining rewards and unstake their coins/NFT. Yet, it will not be possible for them to accumulate new rewards, stake more, or extend the pool duration.

### Staking Coin & Reward Coin

Any coin issued on the Aptos blockchain (and registered) can be used as the reward coin or staking coin, and both the rewards and stakes can be denominated in the same coin.

Decimals from the Staking coin and Rewards coin are used to calculate scale. The scale is a variable used to get quite accurate results for rewards, share:

```
scale = (10e12 / pow(10, reward_decimals)) * pow(10, stake_decimals)
```

**Important:** It may not work with exotic coins (large decimal numbers, extremely large supply), so use this feature at your own risk and double-check that everything works (e.g. by forking and running tests with your coin, etc.). Also, it is very important to be sure that the reward amount (including the amount that will be distributed per second) is enough to be distributed among all the potential stakers :warning:

### NFT staking

The NFT collection for boosting can be configured during the pool's creation, and NFTs from that collection can be used as a stake in addition to the staking coins themselves to boost the staking power.

Each pool configured for an NFT boost has its own boost percentage rate, and user stakes are boosted according to the formula:

```
boosted_stake = stake * boost_percent / 100
```

### Treasury

The treasury account is a multisignature account created with [Momentum Safe](https://github.com/Momentum-Safe).

The treasury withdraws any rewards left three months after the harvest period is completed - or immediately in case of an emergency.

In the future, the treasury will be transferred under the control of the DAO account.

### Emergency

Staking contracts can be stopped in case of an emergency, while the emergency account controls the emergency mechanism. The account is a multisignature account created with [Momentum Safe](https://github.com/Momentum-Safe).

Contracts have two kinds of emergency coded into them: pool-specific and global. In case of a global event, all pools will be stopped. Otherwise, only a specific pool will be stopped. Emergency stoppage can't be undone, as it breaks the staking math.

In an emergency, stakers can withdraw their stakes (coins/NFT) immediately, and the treasury account can withdraw the remaining rewards.

We will transfer the emergency role under the control of the DAO account in the future.


# Smart Contracts

Repository: [https://github.com/pontem-network/harvest](https://github.com/pontem-network/harvest/tree/main)

The harvest repository contains the main staking code core, entry functions, and different tests.

### Branches and Versions

Release tags are to be used for deployment on production (mainnet).

You can always find the latest release in the [Releases](https://github.com/pontem-network/harvest/releases) section and use it for the relevant dependencies in your project.&#x20;

### Addresses & Networks

The deployed contracts are on both mainnet and testnet.&#x20;

The **mainnet** address used to deploy is as follows:

```
0xbaaf53f7b9017ec4be9623c60b6182f2ffba1f01bc67f292295bcfe17f327701
```

The **testnet** addresses used:

```
0xc4d5cb0e84c7dfc3498d38dcbe1a898b694c2a8c274fbc99696b7773fe75b381
```

### Staking contract

Source code: [./sources/stake.move](https://github.com/pontem-network/harvest/blob/main/sources/stake.move)

It's the core contract of the Liquidswap staking protocol. It manages staking pools, stakes, and reward distribution, and contains a rich list of getters.

The key part of the staking contracts is the `StakingPool<S, R>` resource, which contains all the information about a pool, rewards, events, and stakers:

```rust
// Stake pool, stores stake, reward coins and related info.
struct StakePool<phantom S, phantom R> has key {
   reward_per_sec: u64,
   // pool reward ((reward_per_sec * time) / total_staked) + accum_reward (previous period)
   accum_reward: u128,
   // last accum_reward update time
   last_updated: u64,
   // start timestamp.
   start_timestamp: u64,
   // when harvest will be finished.
   end_timestamp: u64,

   stakes: table::Table<address, UserStake>,
   stake_coins: Coin<S>,
   reward_coins: Coin<R>,
   // multiplier to handle decimals
   scale: u128,

   total_boosted: u128,

   /// This field can contain pool boost configuration.
   /// Pool creator can give ability for users to increase their stake profitability
   /// by staking nft's from specified collection.
   nft_boost_config: Option<NFTBoostConfig>,

   /// This field set to `true` only in case of emergency:
   /// * only `emergency_unstake()` operation is available in the state of emergency
   emergency_locked: bool,

   stake_events: EventHandle<StakeEvent>,
   unstake_events: EventHandle<UnstakeEvent>,
   deposit_events: EventHandle<DepositRewardEvent>,
   harvest_events: EventHandle<HarvestEvent>,
   boost_events: EventHandle<BoostEvent>,
   remove_boost_events: EventHandle<RemoveBoostEvent>,
}
```

* `phantom S` generic: the coin used as the staking asset
* `phantom R` generic: coin used the reward
* Reward amount distributed per second
* Some of the technical fields contain information about accumulated rewards, total stakes, and the coins' decimal scales
* Timestamps for the point when the harvest period started and when it ends
* Optional NFT boost configuration and the total boosted staking amount
* User's staking positions in the table
* Events
* Local emergency switch (paused while there is only one pool)

New staking pools are created on the registrar account, and the corresponding resource is placed in that account's storage. The registrar account interacts with the pools created on that account, so it's important. The pools on the same account can't be the same.

Liquidswap uses a front-end interface with a list of pre-filled staking pools to avoid confusion.

#### Generics

Almost all functions in the staking module use two generics, `S` and `R`, as already mentioned:

* `phantom S` generic - coin used to be a stake.
* `phantom R` generic - coin used to be a reward.

So for a staking pool that allows staking Liquidswap LPs for the APT/USDC (LayerZero) pair on the mainnet, the `S` generic will look like this:

```rust
0x05a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c948::lp_coin::LP<
    0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC,
    0x1::aptos_coin::AptosCoin,
    0x190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12::curves::Uncorrelated
>
```

And the `R` generic will be:

```rust
0x1::aptos_coin::AptosCoin
```

To make this work with your Move smart contract, see [Integration Examples](/staking-harvest/integration-examples).

#### Common logic

It's important to mention that accumulated rewards, user-earned rewards, and other technical variables are updated each time a user stakes, unstakes, harvests, or boosts/removes a boost.

This way, we don't need to recalculate the rewards for a specific user; we can execute the required mathematical operations at once for everyone, which makes the contract less expensive and as functional as possible.

#### NFT config

The NFT config is presented as the following resource and is optional for a staking pool. However, if a config is provided, users can boost their staking rewards using NFTs from a specified collection.

The config itself looks like this:

```rust
struct NFTBoostConfig has store {
    boost_percent: u128,
    collection_owner: address,
    collection_name: String,
}
```

* Contains the collection's creator/owner and name to determine the collection for the pool.
* Stake Boost percentage: should be between 1 and 100.

To create a config, you can use the function `stake::create_boost_config`.

#### Functions

In most cases, you can use [Scripts](/staking-harvest/smart-contracts#scripts) entry functions, but if you want to work directly with the staking core, the following would be helpful.

Almost all functions require the address of the pool (`pool_addr`) and the address of the account that created the pool:

* `create_boost_config` - creates and returns a new boost NFT config used in the `register_pool` function.
* `register_pool<S, R>` - creates a new pool on the owner address; also requires the values for rewards and duration. The boost config `nft_boost_config` is optional.
* `deposit_reward_coins<S, R>` - allows to deposit more rewards in the pool, requires rewards coins, extends harvest periods.
* `stake<S, R>` - stakes user coins. If a user stake already exists, it updates the current stake and relocks it again for one week.
* `unstake<S, R>` - unstakes the coins staked by the user; requires a value for the amount to unstake and returns the value for unstaked coins.
* `harvest<S, R>` - harvests rewards for user stake and returns reward coin.
* `boost<S, R>` - bosts user stake with the provided NFT; requires `Token` as the argument.
* `remove_boost<S, R>` - removes the NFT boost from the user stake and returns `Token`.&#x20;

**Getters**

* `get_boost_config<S, R>` - get the NFT boost configuration parameters: collection creator, name, boost percentage.
* `is_finished<S, R>` - get true if the harvest period is finished.
* `get_end_timestamp<S, R>` - get the end timestamp of the harvest period.
* `pool_exists<S, R>`  - get true if the pool exists.
* `stake_exists<S, R>` - get true if the user stake exists.
* `get_pool_total_stake<S, R>` - returns the total staked amount for the pool.
* `get_pool_total_boosted<S, R>` - returns the total boosted stake amount for the pool.
* `get_user_stake<S, R>` - get the user stake amount in the pool.
* `get_user_boosted<S, R>` - get the user's boosted stake amount in the pool.
* `get_pending_user_rewards<S, R>` - get the amount the user's rewards that are pending (awaiting harvest).
* `is_emergency` / `is_local_emergency` - get true if a global or local emergency happens.
* `is_boostable` - get true if the pool stake can be boosted with an NFT.
* `get_start_timestamp` - get the timestamp for when the pool was created.
* `is_boosted` - get true if the user stake is boosted.
* `get_unlock_time` - get the timestamp for the point when a user stake can be unstaked.
* `is_unlocked` - get true if a user stake can be unstaked.

The rest of the functions are related to emergency or admin accounts. If you'd like a more detailed explanation, check the detailed comments in the[ source code](https://github.com/pontem-network/harvest/blob/main/sources/stake.move) section.

### Scripts

Source code: [./sources/scripts.move](https://github.com/pontem-network/harvest/blob/main/sources/scripts.move)

The scripts module is an entry function that can be called diretly in a transaction and used by the Liquidswap dApp.

It makes the development task easier by hiding most of the complexity. Also, it works directly with user balances/NFTs while the core accepts NFTs and coins as part of the arguments.

#### Functions

* `register_pool<S, R>` - creates a new pool without an NFT boost config and extracts reward coins from the user account.
* `register_pool_with_collection<S, R>` - creates a new pool with an NFT boost config enabled; extracts reward coins from the user account.
* `stake<S, R>` - stakes the user's coins and extracts stake coins from the user account.
* `stake_and_boost<S, R>` - stakes the user's coins and boosts them with the provided NFT. Both the staking coins and the NFT are extracted from the user account.
* `unstake<S, R>` - unstakes the user's coins and deposits them back into the account.
* `unstake_and_remove_boost<S, R>` - unstakes the user's coins and the NFT boost, deposits both the coins and the NFT back on the account.
* `harvest<S, R>` - harvests user rewards and deposits them on the account.
* `deposit_rewards_coins<S, R>` - extract rewards from the user account and deposit them into the pool: extends the pool's duration.
* `boost<S, R>` - boosts the user's stakes with an NFT, extracting the NFT from the user account.
* `remove_boost<S, R>` - removes the NFT boost from the user's staking positions, depositing the NFT back to the user account.

### Config

Source code: [./sources/stake\_config.move](https://github.com/pontem-network/harvest/blob/main/sources/stake_config.move)

This is in case of a global emergency: if it occurs, all the pools will be stopped. We don't believe this will ever happen, but we still want to cover the possible extreme cases.

Also, the contracts allow changing the emergency and treasury admin accounts.


# Integration Examples

**Important:** you need to have a very good idea of what you are doing, because there are some risks.&#x20;

Use only test coins for test purposes. Your rewards or NFTs can get stuck in the staking pool if you use a wrong configuration, and you won't be able to withdraw them. First check that everything works on testnet and only then switch to the production environment! :warning:

We have prepared a few integration examples in case you want to integrate staking contracts into your Move smart contracts.

### Add as dependency

Add the Harvest package as a dependency to your project updating `Move.toml`:

```toml
[dependencies.Harvest]
git = 'https://github.com/pontem-network/harvest.git'
rev = 'latest version'
```

Replace `latest version` with the actual one - see the latest version on the [Releases](https://github.com/pontem-network/harvest/releases) page.

As we are going to use Liquidswap LP coins in the example (though it's not mandatory for harvest contracts), add another dependency:

```toml
[dependencies.LiquidswapLP]
git = 'https://github.com/pontem-network/liquidswap.git'
subdir = 'liquidswap_lp/'
rev = 'v0.4.4'
```

### Register pool / stake

**Important:** The following examples show how you can iterate with staking contracts in your smart contracts/product. The example is designed for educational purposes only; your code will probably be very different.&#x20;

Let's say we want to register a pool that uses Liquidswap LP coins as staking coins and reward coins as an unknown generic. After that, we will immediately stake.

{% embed url="<https://gist.github.com/borispovod/84bb62d9160db6f0863a9f7604464d3f>" %}
Register pool & stake
{% endembed %}

### Harvest

If we want to harvest the rewards, we can extend the module above with the harvest function:

{% embed url="<https://gist.github.com/borispovod/53ba39f725f0ac968ee101e4a6a8c278>" %}
Harvest
{% endembed %}

### Unstake

Once one week has elapsed from the moment of staking or once the established harvesting period is complete, you can unstake:

{% embed url="<https://gist.github.com/borispovod/0f46c0a3164f5bb3a4ab5d71600b974d>" %}
Unstake
{% endembed %}

### Boost

To boost or remove a boost, look at the following example where we extract boost collection from config:

{% embed url="<https://gist.github.com/borispovod/9d312f1ec40638beeddd3bac4b52c0b5>" %}

### More

You can find more examples in our [tests](https://github.com/pontem-network/harvest/tree/main/tests).


# DApp

<figure><img src="/files/JgxOQTR531VUgljkwgly" alt=""><figcaption></figcaption></figure>

The staking paltform itself is available in the [Liquidswap DApp](https://liquidswap.com) in the Hydroponics Farm section.

The UI supports the following features:

* Staking / unstaking
* Harvesting
* NFT-enabled boosting

If you wish to create a new pool or extend an existing pool, please use the deployed [smart contracts](/staking-harvest/smart-contracts) directly.

#### Listing a pool

The dApp contains only whitelisted staking pools. If you wish your staking pool to be included on the list, please [contact us](https://t.me/pontemnetworkchat).


# Create your own farming pool

**Important:** It is crucial to have a comprehensive understanding of the tasks involved, as there are inherent risks, including the potential loss of funds.

If you are interested in creating a new farming pool on Liquidswap and providing rewards, this is the page you should study. The current example demonstrates the process using the Aptos CLI, but alternative methods can be employed with the Aptos SDK or a custom user interface (note that a default interface is not currently provided and there are no plans to develop one at this time).

## Preparation

Before creating a farming pool, if you want it to be visible on the Liquidswap DApp, please [contact us](https://t.me/pontemnetworkchat) first and communicate your intentions. Otherwise, we will not be able to whitelist your reward pool.

To create a farming pool, you will need:

* [Aptos CLI](https://github.com/aptos-labs/aptos-core/releases)
* A new account funded with Aptos to cover gas fees and containing the reward coins you plan to use for the pool.
* **Optional:** If you want to enable users to stake NFTs for a staking boost, you will also need information about the staking collection:
  2\. Address of the collection creator
  3\. Name of the collection

Before creating the pool, prepare a folder that will store the Aptos profile containing the private key from the account you plan to use for pool creation.

If you're unsure how to do this, simply create a new directory and run the `aptos init` command there. As a result, you can either create a new account and transfer tokens to it or use the private key from an existing account.

### Pool Creation

To create a pool without an NFT collection booster, use the following command from the directory containing your Aptos CLI profile.&#x20;

Make sure to replace the placeholder values with your own information:

```sh
aptos move run --function-id 0xb247ddeee87e848315caf9a33b8e4c71ac53db888cb88143d62d2370cca0ead2::scripts::register_pool \
  --args u64:0 \ # Amount of rewards.
  --args u64:0 \ # Duration of the pool in seconds.
  --type-args "" \ # Coin type of coin which will be used to stake.
  --type-args "" \ # Reward coin type, used as reward, e.g. 0x1::aptos_coin::AptosCoin.
```

Example of a stake coin type if you want to allow people to stake lzUSDC / APT LP coins:

```
0x05a97986a9d031c4567e15b797be516910cfcb4156312482efc6a19c0a30c948::lp_coin::LP<0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC, 0x1::aptos_coin::AptosCoin, 0x190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12::curves::Uncorrelated>
```

If you want to create a pool with NFT collection use another command:

```sh
aptos move run --function-id 0xb247ddeee87e848315caf9a33b8e4c71ac53db888cb88143d62d2370cca0ead2::scripts::register_pool_with_collection \
  --args u64:0 \ # Amount of rewards.
  --args u64:0 \ # Duration of the pool in seconds.
  --args address:0x1 \ # Creator of the collection.
  --args string:"" \ # Name of the collection.
  --args u128:0 \ # Booster, from 1 to 100.
  --type-args "" \ # Coin type of coin which will be used to stake.
  --type-args "" \ # Reward coin type, used as reward, e.g. 0x1::aptos_coin::AptosCoin.
```

After creating the new pool, please provide us with the following information:

* A link to the transaction in the blockchain explorer.
* Account address that created the pool (so we can verify).<br>


# SDKs

### Typescript

Developed by Pontem team.

**Important note:** the SDK is in active development: we are changing API from time to time, updating it with missed functions, etc. So before running a local update for your project – better review changes. :warning:

The Liquidswap Frontend SDK is written using TypeScript and can be used to integrate Liquidswap into your frontend and DApp.

Link to the repository:

{% embed url="<https://github.com/pontem-network/liquidswap-sdk>" %}

For documentation, refer to the [Typedoc](https://pontem-network.github.io/liquidswap-sdk/).

### 3rd party SDKs

**IMPORTANT:** Use 3rd party SDKs on your own risk. :warning:

#### Golang SDK

Developed by [OmniBTC](https://www.omnibtc.finance/) team.

Repository - <https://github.com/OmniBTC/go-aptos-liquidswap>


# Liquidswap Widget

A web component custom element that can be embedded in any frontend application, or even plain HTML/JS/CSS. Using the full strength of the [SDK](/typescript-sdk), the widget can provide swap operations from any other custom dApp, wallet, etc.

## Installation

```bash
yarn add @pontem/liquidswap-widget
```

or

```bash
npm install @pontem/liquidswap-widget
```

## Usage

Function `loadWidget` accepts widget HTML tag name as string. It can be custom name but must be in kebab case. We recommend to use `liquidswap-widget` name. Passed name must be exactly the same as tag name.

* React

  ```tsx
  import React, { useLayoutEffect } from 'react';
  import { loadWidget } from '@pontem/liquidswap-widget';

  export const Widget = () => {
    useLayoutEffect(() => {
      loadWidget('liquidswap-widget');
    }, []);
    return (
      <div className="'Your Wrapper className'">
        <liquidswap-widget/>
      </div>
    );
  };
  ```
* Vue

  ```typescript
  <template>
    <div class="'Your Wrapper class name'">
      <liquidswap-widget>
    </div>
  </template>

  <script setup lang="ts">
    import { loadWidget } from '@pontem/liquidswap-widget';
    loadWidget('liquidswap-widget');
  </script>
  ```
* Any other framework / lib

  1. Make sure you added html tag `liquidswap-widget` into app.

  ```html
  <liquidswap-widget></liquidswap-widget>
  ```

  1. Import `loadWidget` function from npm and run with passing html tag name in kebab case;

  ```
  import { loadWidget } from '@pontem/liquidswap-widget';loadWidget('liquidswap-widget');
  ```

## When Loaded

You can listen widget and add some logic (if necessary) when widget is embedded:

```jsx
// customElements defined at global window object. 
customElements.whenDefined('liquidswap-widget').then(() => {
     // here custom logic
});
```

## Live Example

Widget example published to github pages:

<https://pontem-network.github.io/liquidswap-widget/>

## Liquidswap Widget as Dapp (native wallet application) <a href="#wallet-integration" id="wallet-integration"></a>

Liquidswap Widget could be used as Dapp inside wallet.

In that case web-custom-element needs several properties:

* Account address of wallet;

```tsx
const dataAccount = '0x15fd61229f6e12b51adbff45b7b74310c7eaf9c24ef8c13b653c8f2a07bc1d14';
```

* Network information: name and chainId;

```tsx
const dataNetwork = { name: 'mainnet', chainId: '1' };
```

* Transaction info: status and hash;

```tsx
interface ITransactionStatus {
  status: 'pending' | 'success' | 'error' | 'rejected';
  hash: string | null
};
const transactionStatus: ITransactionStatus = { status: 'pending', hash: null };
```

* Properties should be passed as strings (data-attributes), so to pass `Object` variable you need to use `JSON.stringify()` on passing `Object`.
* Properties will be extruded with `JSON.parse()` inside widget;
* Properties are reactive, so any change to props will update widget’s internal store;

Also, widget will dispatch to Custom Events:

1. ‘**signAndSubmitTransaction**’ - this event will return CustomEvent with ‘detail’ property containing ready for wallet transaction payload. Just accept props inside handler and you will find prop ‘detail’ with full transaction payload;
2. ‘**transactionProcessed**’ - this event will be fired after widget accepts hash and status of transaction and correctly resolves it with.

<details>

<summary>Example in React-JSX</summary>

```jsx
import React, { useLayoutEffect, useRef, useState } from 'react';
import { loadWidget } from '@pontem/liquidswap-widget';

export const Widget = () => {
  const [dataNetwork, setDataNetwork] = useState(
    { name: 'mainnet', chainId: '1' }
  );
  const [dataAccount, setDataAccount] = useState(
    '0x019b68599dd727829dfc5036dec02464abeacdf76e5d17ce43352533b1b212b8'
  );
  const [transactionStatus, setTransactionStatus] = useState<{ 
    status: string,
    hash: string | null
  }>({ status: 'pending', hash: null });
  
  const ref = useRef();
  const transactionHandler = (props: CustomEvent) => {
    // props.detail will be contain payload. 
  };
  const processedHandler = (event: CustomEvent) => {
    // this event will be fired when user close modal with transaction status
    // neither it's success or error. After that point we do not need to 
    // provide hash or status. So it should be set to initial State: 
    setTransactionStatus({ status: 'pending', hash: null });
  };
  useLayoutEffect(() => {
    loadWidget('liquidswap-widget');
    // customElements available as window.customElements 
    customElements.whenDefined('liquidswap-widget').then(() => {
      if (ref.current) {
        const nodeElement = ref.current as unknown as Element;
        nodeElement.addEventListener(
	  'signAndSubmitTransaction',
	  transactionHandler
	);
        nodeElement.addEventListener(
	  'transactionProcessed',
          processedHandler
	);
      }
    });
  }, []);
  
  return (
     <liquidswap-widget
        ref={ref}
        data-network={JSON.stringify(dataNetwork)}
	data-account={dataAccount}
	data-transaction={JSON.stringify(transactionStatus)}
     />
   )
};
```

</details>

Note: When this properties are passed to widget - it treats Parent element as ‘wallet environment’, so connect / disconnect wallet flow will be skipped / hidden.


# Liquidswap DApp

<figure><img src="/files/SCefn5hkovFVg92ARsI6" alt=""><figcaption><p>Liquidswap UI/UX</p></figcaption></figure>

The user interface for interacting with the Liquidswap protocol is published at <https://liquidswap.com/>

It supports most of Liquidswap's core features:

* Pool creation
* Adding liquidity
* Burning liquidity
* Swaps
* Basic statistic

### Bridge

The Liquidswap DApp allows to transfer your coins (USDC, USDT, many others) from other chains to Aptos chain. The bridge developed and maintained by [LayerZero](https://layerzero.network/) team, the DApp itself integrated with bridge widget.

The bridge is available at <https://bridge.liquidswap.com>

### Testnet

The testnet version DApp is available at <https://testnet.liquidswap.com>


# Coins Registry

We are developing our own [Coin Registry](https://github.com/pontem-network/coins-registry) file formats for [LiquidSwap](https://liquidswap.com/), which contains all the information about tokens and pools in which currencies can be exchanged.

Currently, we use two files for data:

* [coins.json](https://github.com/pontem-network/coins-registry/blob/main/src/coins.json)
* [pools.json](https://github.com/pontem-network/coins-registry/blob/main/src/pools.json)

And an [index.js](https://github.com/pontem-network/coins-registry/blob/main/src/index.js) file for easier usage of coins-registry in other projects. Through the interface, it is easy to get tokens or pools lists for the selected chain type. It provides two methods: `getCoinsFor` and `getPoolsFor`. Each of these methods requires the network type as an argument. Possible values for network types: `mainnet` and `testnet`.

### Coins Data Structure

There is a coins list in coins.json. One coin can be presented as follows:

```
{
    "source": "aptos",
    "name": "Aptos Coin",
    "chainId": 1,
    "decimals": 8,
    "symbol": "APT",
    "type": "0x1::aptos_coin::AptosCoin",
    "caution": false,
    "order": 1
}
```

#### Fields Description

`source` - enum data type with strict value check. If you’d like to add the new value into it, fill the form. Usually source is the name of a partner's company to add many coins into the list. We support now following sources:

* `aptos` - for the native Aptos Coin.
* `partners` - for other tokens.
* `celer` - for tokens provided by Celer.
* `layerzero` - for tokens provided by Layer Zero.
* `wormhole` - for tokens provided by Wormhole.

`name` - tokens' full name. We update this value by a request to the node. It is used on Picture 1 as a bottom string after the dot (on the picture it is Bitcoin).

`chainId` - for mainnet value is 1. Currently, testnet tokens stored in the `testnet` branch of this repo.

`decimals` - decimals how supported by your token. We update this value by a request to the node.

`symbol` - it is used to construct a token alias, which can be equal to symbol as on Picture 1 in the top string.

`type` - full type of token. String with following structure ADDRESS::MODULE::COIN, e.g. `0x1000000fa32d122c18a6a31c009ce5e71674f22d06a581bb0a15575e6addadcc::usda::USDA`.

`caution` - if we need to show warning icon near the token - we will add the caution field.

`order` - order of a token in the tokens list. Current logic:

```
1 Apt
10 USDC
20 USDT
30 DAI
40 BTC
50 WETH
60 BUSD / BNB
1000 other tokens
```

### Pools Data Structure

```
{
    "coinX": "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDT",
    "coinY": "0x1::aptos_coin::AptosCoin",
    "curve": "uncorrelated",
    "networkId": 1
}
```

#### Fields Description

`coinX` - a full type of coin. String with the following structure ADDRESS::MODULE::COIN, e.g. `0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDT`.

`coinY` - a full type of coin. String with the following structure ADDRESS::MODULE::COIN, e.g. `0x1::aptos_coin::AptosCoin`.

> ⚠️ Coins should be sorted.

`curve` - to strictly point curve type use the following values:

* selectable - will keep a switcher for a curve type available for declared coins pair.
* uncorrelated - keep only an uncorrelated curve pool on UI.
* stable - keep only a stable curve pool on UI.


# Introduction v1

Liquidswap V1 represents the inaugural Concentrated Liquidity Automated Market Maker (AMM) on the Aptos blockchain. It marks a significant evolution from the previous iterations of the Liquidswap protocol, namely versions V0 and V0.5

<figure><img src="/files/Tq6PWHUuV50hk6noayON" alt=""><figcaption><p>Liquidswap V1 Dapp</p></figcaption></figure>

Built on a liquidity book model, Liquidswap V1 offers heightened efficiency and flexibility compared to its predecessors. This advancement significantly reduces impermanent loss and enables zero slippage. Additionally, it opens up a myriad of strategic possibilities for both traders and liquidity providers.

## Features

Key Features and Advantages of Liquidswap V1 Compared to Versions V0/V0.5:

1. **No Slippage**: Liquidswap V1 employs a unique system where liquidity is divided into 'bins', each representing a specific price range. This structure effectively eliminates slippage.
2. **Bins**: In this system, liquidity is organized into bins. This allows for the existence of the same trading pairs with varying bin steps, which are essentially the price changes between bins. Users have the flexibility to add liquidity at any price and in any configuration.
3. **Fees**: Fees in Liquidswap V1 are dynamic and tailored to the volatility of each pair. Pools with high frequency and significant volatility attract higher fees, aligning costs with market dynamics.
4. **Liquidity Positions**: Instead of traditional LP (Liquidity Provider) tokens, providers in Liquidswap V1 receive fungible tokens that represent their share in a specific bin. This offers a more precise representation of their stake.
5. **Unified Curve Approach**: With V1, there's no longer a need for different curves for stable and uncorrelated pairs. Stable pairs function efficiently with the smallest bin steps, simplifying the overall process.

And more:

1. **Written in Move Language**: Liquidswap V1 is developed using the Move language, renowned for its flexibility and inherent safety features. This choice underscores the protocol's commitment to security and adaptability.
2. **High Speed**: Leveraging the BlockSTM feature of the Aptos consensus, Liquidswap V1 offers exceptionally high transaction speeds. This efficiency is a key advantage in the fast-paced world of cryptocurrency trading.
3. **Security Review**: The source code of Liquidswap V1 has undergone rigorous reviews by several authoritative security teams, ensuring a high level of security and reliability for its users.

## Decentralized Application :tada:

The current version of Liquidswap is actively deployed on both the Aptos mainnet and testnet. DApps utilizing Liquidswap can be accessed through the following links:

* <https://cl.liquidswap.com> - on the Aptos mainnet.
* [https://testnet.cl.liquidswap.com](https://testnet.cl.liquidswap.com/#/pools) - on the Aptos testnet.

Please test Liquidswap v0 for yourself and share your feedback - it will be greatly appreciated 😊

## Security audits

[Ottersec](https://osec.io/)

{% file src="/files/phnsFxBLaCGx9GcfFCeP" %}

[Zellic](https://www.zellic.io/)

{% file src="/files/yuWgR5J3KFSxIjQS9YuP" %}

## Bounty Program

We are currently collaborating with Immunefi to initiate and implement a bounty program, akin to those established for previous versions of our platform.

## Developer Links

For a general idea about Liquidswap v1, continue with the [Protocol Overview](/liquidswap-v1-docs/protocol-overview).

To get hands-on with the DEX, check out the tutorials:

* [Smart contracts usage & integrations](/liquidswap-v1-docs/integration)

Looking for source code? Visit our [Github](https://github.com/pontem-network):&#x20;

* [Liquidswap v1](https://github.com/pontem-network/liquidswap_v1)

Need help integrating your service with Liquidswap? Feel free to contact us:

* [Telegram](https://t.me/pontemnetworkchat)
* [Discord](https://discord.gg/44QgPFHYqs)


# Protocol Overview

Liquidswap V1 introduces a novel approach to decentralized exchange (DEX) mechanisms, moving away from the traditional Automated Market Maker (AMM) model governed by a constant product formula (e.g., X\*Y=K). Instead, it employs a liquidity book model, offering enhanced flexibility for liquidity providers and traders. This design allows users to specify exact prices for buying or selling assets, akin to placing limit orders, thereby enabling precise control over their liquidity positions.

This model also mitigates concerns about price slippage for traders. Since liquidity is supplied at specific prices without reliance on a constant formula for swaps, assets can be exchanged at the stated price, ensuring predictability and stability. For instance, stablecoins can be swapped at a one-to-one ratio, barring transaction fees, with each bin maintaining a fixed price.

A 'bin' functions similarly to a mini liquidity pool within the broader global pool, designated for a specific price point of a pair of assets (X and Y). Each bin possesses a unique ID, enabling liquidity providers to target their contributions to specific price levels. The 'bin step' is a critical concept, representing the minimum price increment between adjacent bins, typically measured in basis points. This metric facilitates the precise calculation of price movements within the pool.

The arrangement within the pool is such that bins on the left side contain liquidity primarily for asset Y, whereas bins on the right are reserved for asset X. The 'active bin' signifies the bin that matches the current market price of the asset pair, serving as the starting point for swaps. As trades occur and the price shifts, the active bin may change, with the bin corresponding to the new price becoming the focal point for subsequent transactions.

<figure><img src="/files/vw7qiGwJR7l2erPGRwog" alt=""><figcaption></figcaption></figure>

Liquidswap's architecture is influenced by innovative models seen in the DeFi space, notably similar to approaches like those introduced in Trader Joe V2. As we refine our [documentation](https://docs.traderjoexyz.com/concepts/concentrated-liquidity), we recommend exploring available resources from leading platforms for a broader perspective on the strategies and mechanics that inspire our system's functionality.&#x20;

## Bins

In Liquidswap, each bin serves a dual function: it not only holds liquidity but also defines the price at which this liquidity is available. The organization of bins is directional—bins allocated for coin X extend towards the right, while bins designated for coin Y liquidity extend in the opposite direction. Central to this system is the 'active bin', which at any given moment contains liquidity for both X and Y coins, reflecting the pair's current market price.

The movement of the active bin is dynamic; it shifts to correspond with the bin that accurately reflects the post-swap market price, ensuring that trades are executed at prevailing rates.

Bins are uniquely identified by a numeric ID (represented as a `u32`), facilitating easy navigation and operations within the pool. The bin ID system is designed around a central pivot—the 'zero bin id' is set at 8388608, representing an equilibrium price ratio of 1:1 between X and Y coins. This central value is strategically chosen halfway through the maximum range, which caps at 16777215, equivalent to $$224224$$. This arrangement allows for a wide spectrum of price representations within the pool.

To translate a bin ID into a price, you can apply the following formula:

```
(1 + BinStep / 10000) ** (BinID - 8388608)
```

Learn more in [Trader Joe v2 doc](https://docs.traderjoexyz.com/concepts/bin-math).

## Fees & Treasury

In Liquidswap V1, liquidity providers earn fees not from the entire pool but specifically from the liquidity they've contributed to bins actively used in swaps. This targeted rewards system ensures that providers who directly facilitate trading activity are compensated. Furthermore, the fee structure in V1 is dynamic, varying with the volatility of the asset pair. This means liquidity providers can potentially earn more from pairs with higher volatility compared to those with stable valuations.

Additionally, Liquidswap V1 incorporates a protocol fee. This fee is collected separately and directed to the treasury, where it is governed by the DAO. This mechanism not only supports the operational sustainability of the platform but also empowers the community through DAO oversight

Learn more about [fees](/liquidswap-v1-docs/smart-contracts/fees).&#x20;

## LB Positions

In Liquidswap V1, liquidity providers no longer receive traditional LP tokens. Instead, they are awarded LB position tokens—Aptos Token v1—which signify their proportional share in the liquidity provided to a specific bin or pool.&#x20;

Each LB position token includes properties such as the bin ID and the amount of liquidity contributed, rendering these tokens fungible. This fungibility enables liquidity providers to seamlessly transfer their entire position or a portion thereof to another account, engage with other DeFi protocols, or consolidate positions. Such flexibility enhances the utility and versatility of liquidity contributions within the ecosystem.

Learn more about[ LB tokens.](/liquidswap-v1-docs/smart-contracts/lb-tokens)


# Smart Contracts

The contracts are published in the Liquidswap GitHub repository at <https://github.com/pontem-network/liquidswap_v1>

Liquidswap V1 employs smart contracts written in the Move language, executed on the Move VM. This setup ensures the core contracts, which encompass the protocol's logic and safety guarantees, are distinctly separated from the peripheral contracts, such as routers or entry points (scripts). While the current implementation of the periphery is quite basic, it's designed to accommodate multiple peripherals in the future. Importantly, these enhancements will not impact the core codebase.

## Branches and versions

The current `main` branch is the development branch and always contains the latest changes. Release branches contain the latest changes created for the specific release.\
\
You can always find the latest release in the [Releases](https://github.com/pontem-network/liquidswap_v1/releases) section.

## Deployments

**Aptos Mainnet**

```
# Module addresses (deployer)
0x54cb0bb2c18564b86e34539b9f89cfe1186e39d89fce54e1cd007b8e61673a85

# Resource account
0xa0d8702b7c696d989675cd2f894f44e79361531cff115c0063390922f5463883

# Emergency resource account
0x9aa8dec24fe7a592de107b4622069c4ed60567900fa3eddade6326d92e9b751c
```

#### **Aptos Testnet**

:warning:If you wish to experiment with Liquidswap V1 on the testnet, please utilize the `testnet` branch of the Liquidswap V1 repository.

```
# Module addresses (deployer)
0xc9ccc585c8e1455a5c0ae4e068897a47e7c16cf16f14e0655e3573c2bbc76d48

# Resource account
0x62ff269698a76cf6bccf256d93fc1963926935f74e72d89f5af653ad882568d8

# Emergency resource account
0x370b9a4b15952b33784662e92493b7a78bc93ea2a8d8569028acab49196c2128
```

## Coin Generics Sorting

:warning:I**t is crucial to ensure that coins are sorted correctly in Liquidswap V1. Unlike versions V0/V0.5, automatic sorting is not functional in this version, and this applies even at the Router level.**&#x20;

When interacting with the Pool module functions in Liquidswap V1, it's imperative to sort the coins passed as generics. Proper sorting is crucial because it establishes the rules for creating liquidity pools. It's important to note that all functions within the Liquidity Pool module and others require sorted generics to function correctly; failing to do so will result in the transaction being reverted.

The current sorting algorithm takes the types of provided coins, e.g., `address::module::struct_name`, and compares the struct name of both coins. If it's equal, it continues with the module name and, in the end, with the address.

You always can just use the [implementation](https://github.com/pontem-network/liquidswap_v1/blob/main/sources/pool.move#L1292) in Move language.

Other languages:

* Implementation in Javascript: [Coin Sorting In JS](https://gist.github.com/borispovod/2809728c8959649d42c5cef15b4cedb7).
* Also, it's supported in [Typescript SDK](broken://pages/og7X13d9lbmVeq9iKz0I).


# Pool

Source Code: [sources/pool.move](https://github.com/pontem-network/liquidswap_v1/blob/main/sources/pool.move)

The Pool module stands as the cornerstone of Liquidswap V1, encompassing the essential functionalities such as swap, mint, burn logic, and core checks. These operations are efficiently executed with the assistance of helper libraries. Central to this module is the 'Pool resource', a pivotal component of the contracts. It serves as a repository for crucial data including reserves, bin information, fee parameters, and events.&#x20;

This resource is defined with three generics, a design choice that facilitates the determination of the appropriate pool during iterative processes:

<pre class="language-rust"><code class="lang-rust"><strong>/// Pool itself.
</strong><strong>struct Pool&#x3C;phantom X, phantom Y, phantom BinStep> has key {
</strong><strong>    // Current active bin id.
</strong><strong>    active_bin_id: u32,
</strong><strong>    
</strong><strong>    // Current bin step.
</strong><strong>    bin_step: u32,
</strong><strong>    
</strong><strong>    // Bins tree and table.
</strong><strong>    tree: BinsTree,
</strong><strong>    bins: Table&#x3C;u32, Bin>,
</strong><strong>    
</strong><strong>    // Current coin reserves.
</strong><strong>    coins_x: Coin&#x3C;X>,
</strong><strong>    coins_y: Coin&#x3C;Y>,
</strong><strong>    
</strong><strong>    // Gas optimization, just store instead of generating.
</strong><strong>    collection_name: String,
</strong><strong>    
</strong><strong>    // The pool must be locked for swap/mint/burn operations during flashloan.
</strong><strong>    is_locked: bool,
</strong><strong>    
</strong><strong>    // Fees.
</strong><strong>    fee_params: StaticFeeParams,
</strong><strong>    
</strong><strong>    // Events.
</strong><strong>    swap_event_handler: event::EventHandle&#x3C;SwapEvent>,
</strong><strong>    mint_event_handler: event::EventHandle&#x3C;MintEvent>,
</strong><strong>    composition_fees_event_handler: event::EventHandle&#x3C;CompositionFeesEvent>,
</strong><strong>    burn_event_handler: event::EventHandle&#x3C;BurnEvent>,
</strong><strong>    flashloan_event_handler: event::EventHandle&#x3C;FlashloanEvent>,
</strong><strong>    change_fee_params_event_handler: event::EventHandle&#x3C;ChangeFeeParamsEvent>,
</strong><strong>}
</strong></code></pre>

The generics are primarily defined by two coins, X and Y, which establish the trading pair.

For instance, X could be represented as

```
0x1::aptos_coin::AptosCoin
```

&#x20;And Y could be represented as:

```
0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC
```

* BinStep: This type is crucial as it determines the step between bins in Liquidswap V1, essentially representing the price difference between adjacent bins
* it is important to note that the coin generics X and Y must be sorted correctly. This sorting ensures proper functioning within the protocol's framework.

Liquidswap V1 operates by creating pools within a designated resource account. Each pool is unique, ensuring there are no duplicates with the same combination of coins and bin size. This principle of uniqueness also applies to LB tokens, where each collection is distinct and non-replicable.

## Bins

Source Code: [bin\_steps/](https://github.com/pontem-network/liquidswap_v1/tree/main/bin_steps)

All potential bin steps, ranging from 1 to 100, are defined within the 'BinSteps' module of Liquidswap V1. They can be utilized in the following manner:&#x20;

```
use liquidswap_v1::bin_steps::X1;
use liquidswap_v1::bin_steps::X5;
use liquidswap_v1::bin_steps::X10;
```

Each bin step is indicative of the price difference between bins, measured in basis points. For instance, one basis point, which is equivalent to 0.0001 or 0.01%, corresponds to the X1 bin step. For a more comprehensive understanding, please refer to the [Protocol Overview](/liquidswap-v1-docs/protocol-overview) section.

## Functions

:warning: For swap, liquidity mint and burn operations we recommend to use [Router](/liquidswap-v1-docs/smart-contracts/router-and-entry) module as otherwise it could be risky: the pool contract itself doesn't have slippage checks.

The module offers an extensive array of functions and getters that can be highly useful. We recommend exploring the source code to gain a deeper understanding.

These functions enable you to retrieve information about bins, the active bin step, current fee parameters, calculate fees, perform flashloans, and more. Some functions are marked as `#[view]`, indicating that they can be queried off-chain

## Pool Creation

At present, pool creation in Liquidswap V1 is a permissioned process. This decision is driven by the fact that the project is in its early stages, and there is a need for time to comprehensively evaluate and determine the most optimal pre-configurations to offer. Misconfigurations during pool creation can potentially lead to unpredictable consequences, and this precautionary approach helps mitigate such risks.

## Add Liquidity

To contribute liquidity to existing pools, you can utilize the `mint` function:

```javascript
/// Provide liquidity to pair and mint new LB tokens.
/// Would return mitned LB tokens and X, Y leftovers.
public fun mint<X, Y, BinStep>(
    coins_x: Coin<X>,
    coins_y: Coin<Y>,
    bin_ids: vector<u32>,
    liq_x_coins: vector<u64>,
    liq_y_coins: vector<u64>,
): (vector<Token>, Coin<X>, Coin<Y>) acquires Pool, InitConfiguration {
...
```

To add liquidity, you'll need to specify the IDs of the bins where you intend to contribute liquidity, along with the respective amounts for each bin. It's important to note that when adding liquidity, you can only add both X and Y coins to the active bin. Specifically, on the left side of the active bin, you can only use X coins, and on the right side, you can only use Y coins

After you would get the LB tokens which would represent your position in bins of the pools, and leftovers coins if there is something left.

### Remove Liquidity

It's much pretty simple, just use the function `burn` and  provide LB tokens you are willing to burn:

```javascript
/// Burns provided tokens, releasing corresponding `X` and `Y` coins liquidity.
/// Returns released `X` and `Y` coins.
public fun burn<X, Y, BinStep>(
    liq_nfts: vector<Token>,
): (Coin<X>, Coin<Y>) acquires Pool, InitConfiguration {
```

You will get reedemed liquidity and collected fees.

## Swap

You can swap coin X in pair for coin Y or vise versa. To do swap use the following two functions:

```java
/// Swap X to Y.
public fun swap_x_for_y<X, Y, BinStep>(
    coin_in: Coin<X>,
): Coin<Y> acquires Pool {
...

/// Swap Y for X.
public fun swap_y_for_x<X, Y, BinStep>(
    coin_in: Coin<Y>,
): Coin<X> acquires Pool {
```

If the amount to swap is greater than provided liquidity in the pool, the function would abort, so always be sure your swap can be covered.

## Flashloans

The implementation is based on Move's loan concept, where a Move object containing the loan data is issued by `flashloan` function but cannot be stored, copied, cloned or dropped, the only available action being to return the object back to the `pay_flashloan` function, which would verify that payout contains enough fees.

The concept was explained early on by the Pontem team in this [Medium article](https://medium.com/p/bbc48a48d93c).

Take a flashloan:

```javascript
public fun flashloan<X, Y, BinStep>(
    amount_x: u64,
    amount_y: u64,
): (Coin<X>, Coin<Y>, Flashloan<X, Y, BinStep>) acquires Pool {
....
```

Pay flashloan:

<pre class="language-javascript"><code class="lang-javascript">public fun pay_flashloan&#x3C;X, Y, BinStep>(
<strong>  coin_x_loan: Coin&#x3C;X>,
</strong>  coin_y_loan: Coin&#x3C;Y>,
  flashloan: Flashloan&#x3C;X, Y, BinStep>
) acquires Pool {
.....
</code></pre>

The dynamic fees is not applying to the flashloan, the flashloan fee can be changed by DAO account.

Read more in the [integration section](/liquidswap-v1-docs/integration/flashloans).

## Getters

Almost all getters are marked as `#[view]` functions which allows to query data of the pools, include query active bin, current bin step as number, fees, get amount you will get after swap, or get amount you need to perfom a swap, pools parameters and many more.

For more details look at source code.

## Events

Each operation, include pool creation, covered by events.


# LB Tokens

Source code:  [sources/lb\_token.move](https://github.com/pontem-network/liquidswap_v1/blob/main/sources/lb_token.move)

The LB token is a fungible asset token (Aptos token version 1) that represents a liquidity provider's position in a specific pool and bin.&#x20;

When a new pool is created on the protocol, it is assigned a unique collection that belongs to that pool. Each time someone adds liquidity to a specific bin, the token associated with that bin is released in the collection

The collection name can be generated using the following function:

```javascript
/// Create collection name by coin symbols and bin step.
/// Returns string.
public fun create_collection_name(
    pool_id: u128,
    x_coin_symbol: String,
    y_coin_symbol: String,
    bin_step_symbol: String,
): String {
    string_utils::format4(&b"Liquidswap v1 #{} {}-{}-{}", pool_id, x_coin_symbol, y_coin_symbol, bin_step_symbol)
}
```

:warning: **Caution**: When using the collection name to identify that a token belongs to the protocol, exercise caution. This is because there can be coins with the same symbols. The pool id, which serves as a counter of pools created on the protocol, helps determine which pool is in use. Additionally, the creation of the collection should be handled by the minter account.

Each LB token issued under a collection has a property that represents the Bin ID. This Bin ID can be extracted using the 'Bin ID' field as a u64 using BCS (Binary Canonical Serialization). The most crucial aspect is that each token issued has an associated amount, which makes it fungible. This amount precisely represents the share of liquidity provided to the specific bin.&#x20;

Tokens can only be burned by the creator (minter) account, which is represented by the resource account of the protocol. On the mainnet, it's:&#x20;

```
0xa0d8702b7c696d989675cd2f894f44e79361531cff115c0063390922f5463883
```


# Oracles

Source code: [sources/oracle.move](https://github.com/pontem-network/liquidswap_v1/blob/main/sources/oracle.move)

In the protocol, Oracles are responsible for storing historical time-weighted average values. Whenever a new pool is created, an oracle is also created for that specific pool. Each oracle comprises samples, with a default of 2 samples per oracle. Each sample contains the following data:

* Timestamp when the sample was created.
* Seconds elapsed since the last update.
* Cumulative active bin ID.
* Cumulative value accumulated.
* Cumulative amount of bins crossed.

The structures created during pool creation are as follows:

```rust
 /// Contains information about oracle sample.
struct Sample has store, copy, drop {
    // Timestamp when sample created.
    filled_at: u64,
    // Seconds elapsed from creation to last update.
    lifetime_in_secs: u64,

    // Cumulative data.
    cum_active_id: u256,
    cum_vol_accum: u256,
    cum_bins_crossed: u256,
}

/// Oracle data itself.
struct Oracle<phantom X, phantom Y, phantom BinStep> has key {
    // Current active sample id.
    active_sample_id: u64,
    // Length of oracle (e.g. amount of samples), counts all samples: even which never filled.
    length: u64,
    // Active length of oracle (e.g. amount of samples filled at least once).
    active_length: u64,
    // Samples itself.
    samples: table::Table<u64, Sample>,

    // Events.
    increase_length_event_handler: event::EventHandle<IncreaseLengthEvent>
}
```

Each new Oracle instance created on resource account and contains a table full of samples.&#x20;

Amounts of samples only limited to `65535` and default 2 samples can be resized to max amount or just by adding another one sample or several of them by anyone. The idea is to save a gas and if someone needs to get access to large historical data the storage can be always resized. If new sample created and doesn't have any data yet, such sample wouldn't be able to query.

The sample filled go one after one in sequence, once the latest sample filled, the 0 sampel will become active.

<figure><img src="/files/MP6Dwf4Uopt72DVSQzLV" alt=""><figcaption></figcaption></figure>

Each sample has a 120 seconds lifetime, during this lifetime every swap would fill the current sample, once lifetime is passed the new sample will be filled. In case of composition fee event during liquidity provision the oracle can be updated too.

You can query each sample by an ID use the module, it would return your data provided in the sample. As each sampel contains previous cumulative data, it's simple allows you to get averaged values, e.g.:

```
timestamp_1 = ...
timestamp_2 = ...
sample_1 = binary_search(timestamp_1)
sample_2 = binary_search(timestamp_2)

time weighted active bin = (sample_1.cum_active_id - sample_2.cum_active_bin_id) / timestamp_1 - timestamp_2
```

The `binary_search` funcs allows you to effective find an sample which nearest to the provided timestamp. It never fails and if sample timestamp is greater than recent saved it would return the latest timestamp, and for past timestamp which already is overwritten it would return the oldest sample.

See more example in [tests](https://github.com/pontem-network/liquidswap_v1/blob/main/tests/oracle_tests.move) and try yourself in our integration tutorials.


# Fees

Liquidswap V1 revolutionizes fee structures by introducing dynamic fees that adjust based on the volatility of each trading pair. This system builds upon the foundations laid by versions V0 and V0.5, where fees are distributed among liquidity providers who have contributed to bins directly involved in swaps. This means that if a swap does not utilize your provided liquidity, you will not earn fees from that transaction. Fees are accumulated within the pool and can be claimed by liquidity providers upon withdrawing their liquidity.

The fee model for each pair in Liquidswap V1 is multifaceted, comprising a base fee, a volatility-dependent dynamic fee, and a cap on the maximum fee to ensure fairness and predictability. The base fee is a mandatory charge applied to all transactions if enabled and is calculated using a 'base factor' established during pool creation, in conjunction with the bin step. This structured approach ensures a balanced and responsive fee system that rewards providers based on market dynamics and their contribution to the liquidity pool:

```
base_fee = base_factor * BinStep
total_fee = base_fee + variable_fee
```

The variable fee in Liquidswap V1 is designed to reflect market volatility, essentially determined by the number of bins crossed during a swap and the time elapsed since the last transaction. This fee is calculated using a 'volatility accumulator', which in turn is influenced by several factors:

1. **Volatility Reference**: A benchmark for assessing market volatility, updated with each swap. It accounts for the amount of market movement, represented by the bins crossed during a swap.
2. **Volatility Control**: This mechanism adjusts the variable fee by considering the frequency of transactions within the pool and the extent of market fluctuations.
   * After each swap, the system tallies the bins crossed to gauge recent market activity.
   * The volatility reference is updated based on the 'volatility accumulator', adjusted downwards by a reduction factor to reflect ongoing market conditions.
   * For pools with low transaction frequency, the volatility reference is reset to zero after a specific 'decay period', acknowledging periods of low market activity.
   * Pools with high transaction frequency maintain their volatility reference, ensuring that the variable fee accurately reflects sustained market activity.
   * In cases of standard transaction frequency ('filter period'), the volatility reference is methodically reduced according to the predefined reduction factor.

This mechanism is designed to adjust fees dynamically, ensuring they align with the prevailing market conditions. It does this by being sensitive to both the frequency of transactions and the extent of price fluctuations within the pool. The 'volatility accumulator' is a key component in this process, calculated by tallying the number of bins traversed during a swap and integrating this with the 'volatility reference'. This calculation ensures that the variable fee remains within the maximum volatility accumulator threshold.

It's important to note that all parameters influencing this fee model are set during pool creation. With future updates aimed at making pool creation permissionless, these settings will be guided by a selection of pre-defined options. However, not all fees within the protocol are subject to this dynamic model:

* **Treasury Fees**: These remain constant and may only be adjusted by the DAO within predefined limits, ensuring a stable contribution to the treasury regardless of market volatility.
* **Flashloan Fees**: Similarly, fees for flashloans are fixed, pre-configured, and do not fluctuate with market conditions. This ensures clarity and predictability for users utilizing flashloan services

## Module

Source code: [sources/libs/fees\_helper.move](https://github.com/pontem-network/liquidswap_v1/blob/main/sources/libs/fees_helper.move)

The fee calculation module plays a crucial role in Liquidswap. Upon the creation of each pool, a `StaticFeeParams` struct is established within the fees helper module. This struct encapsulates the pool's fee parameters, including the base fee, dynamic fee, and other relevant variables.

Additionally, the module offers getter functions designed to access these fee parameters, allowing users to retrieve detailed information for any specific pool. For an in-depth understanding of how fee parameters are managed and utilized within the protocol, we recommend consulting the source code.


# Router & Entry

Source code: [router/](https://github.com/pontem-network/liquidswap_v1/tree/main/router)

The router module encompasses both router and entry points for the protocol. Initially, it enables the invocation of functions external to smart contracts (utilizing CLI, wallets, libraries, etc.), while concurrently offering functions for securely swapping, minting, and burning liquidity with specified slippage.

Since both the router and entry points function as peripheral contracts and do not necessitate updates to the core system, new versions of the modules can be deployed, or alternative routers with different methodologies can be introduced.

## Router

Source code:  [router/sources/router.move](https://github.com/pontem-network/liquidswap_v1/blob/main/router/sources/router.move)

It's crucial to note that the router does not encompass all functionalities of the pool, as this is unnecessary. Many getter functions are already available within the pool module itself. Additionally, while entry functions interact directly with account balances, the router continues to accept Aptos Coin objects.&#x20;

The primary role of the router is to facilitate operations involving slippage with the pool.

### Swap

To perform a swap from a specific quantity of X coins to Y coins, utilize the function outlined below:

```javascript
public fun swap_exact_x_for_y<X, Y, BinStep>(
    x_coins_in: Coin<X>,
    y_coins_out_min_val: u64,
): Coin<Y> {
...
```

The parameter `y_coins_out_min_val` specifies the minimum quantity of Y coins you expect to receive in exchange for X coins. If the actual amount received is less than this minimum value, the function will abort the transaction. Upon successful execution, the function returns a `Coin` object representing the amount of Y coins exchanged.

The same logic applies when exchanging Y coins for X coins:

```javascript
public fun swap_exact_y_for_x<X, Y, BinStep>(
    y_coins_in: Coin<Y>,
    x_coins_out_min_val: u64,
): Coin<X> {
...
```

To swap any amount of X for an exact amount of Y coins and receive leftovers, or to do the reverse, use the functions below. These allow precise exchanges with potential surplus returns:

```javascript
public fun swap_x_for_exact_y<X, Y, BinStep>(
    x_coins_in: Coin<X>,
    y_coins_required_out: u64,
): (Coin<Y>, Coin<X>) {
...
}

public fun swap_y_for_exact_x<X, Y, BinStep>(
    y_coins_in: Coin<Y>,
    x_coins_required_out: u64,
): (Coin<X>, Coin<Y>) {
...
}
```

### Add Liquidity

Adding liquidity involves complexity due to the protocol's use of price ranges and bins. When you add liquidity within a specific price range, corresponding to the current active bin, this action can shift the price, potentially activating a different bin.

The function's arguments are structured as follows:

```javascript
/// Adds liquidity to bins in flexible way.
/// Applies active bin id change as shift to liquidity distribution `bin_ids`.
/// This way of adding liqudity should prevent txs failure in case of active bin change
/// during tx pending.
///
/// Ensures that pool `active_bin_id` and minted shares satisfies requirements.
///
/// Returns minted liquidity Tokens, `X` and `Y` coins leftovers.
public fun add_liquidity<X, Y, BinStep>(
    coins_x: Coin<X>,
    coins_y: Coin<Y>,
    bin_ids: vector<u32>,
    active_bin_id_desired: u32,
    active_bin_id_slippage: u32,
    liq_x_coins: vector<u64>,
    liq_y_coins: vector<u64>,
    amount_x_min: u64,
    amount_y_min: u64,
): (vector<Token>, Coin<X>, Coin<Y>) {
...
```

So basically:

* `coins_x`: The amount of X coins you wish to add as liquidity.
* `coins_y`: The amount of Y coins you wish to add as liquidity.
* `bin_ids`: A vector containing the IDs of bins where you intend to allocate your liquidity.
* `active_bin_id_desired`: The ID of the active bin at the time of transaction submission.
* `active_bin_id_slippage`: The maximum number of bins the active bin can shift, accommodating for potential price changes.
* `liq_x_coins`: A vector detailing the exact amount of X coins to be added to each specified bin, corresponding to `bin_ids`.
* `liq_y_coins`: A vector detailing the exact amount of Y coins to be added to each specified bin, also matching `bin_ids`.
* `amount_x_min`: The minimum amount of X coin liquidity you're willing to add to the pool, serving as a form of slippage control.
* `amount_y_min`: The minimum amount of Y coin liquidity you're willing to add to the pool, similarly acting as slippage control.

If the pool's price shifts by an amount less than or equal to `active_bin_id_slippage` in either direction, all specified `bin_ids` will adjust accordingly, mirroring the price movement's bin shift. Should the slippage exceed the specified `active_bin_id_slippage` or the minimum X, Y amounts, the function will revert. Upon successful execution, the function issues LB tokens, which you should either store in a contract or deposit into an account.&#x20;

Given the complexity, we recommend reviewing our [integration](/liquidswap-v1-docs/integration) examples for clearer understanding.

### Remove Liquidity

To remove or redeem your liquidity, utilize the function outlined below:

```javascript
public fun remove_liquidity<X, Y, BinStep>(
    liq_nfts: vector<Token>,
    amount_x_min: u64,
    amount_y_min: u64,
): (Coin<X>, Coin<Y>) {
...
```

The function for removing or redeeming liquidity requires the following parameters:

* `liq_nfts`: A vector containing your LB tokens.
* `amount_x_min`: The minimum amount of X coins you expect to receive.
* `amount_y_min`: The minimum amount of Y coins you expect to receive.

If the actual amounts received are below these minimums, the function will revert. Upon successful execution, the function returns the redeemed values of X and Y coins.

## Entry

Source code: [router/sources/entry.move](https://github.com/pontem-network/liquidswap_v1/blob/main/router/sources/entry.move)

This function operates similarly to the router but is designed for direct transaction calls, interacting with user balances.&#x20;

The main difference lies in the input requirements: instead of LB token or `Coin` objects, users simply specify amounts using primitive types. For detailed insights into the function's specifics, refer to the source code available at the provided link.


# Other

## BinSteps

Source code: [bin\_steps/](https://github.com/pontem-network/liquidswap_v1/tree/main/bin_steps)

The concept has been previously detailed in the Pools documentation.&#x20;

Within the module, there are functions specifically designed to retrieve both the symbolic and numeric representations of a BinStep. For example, for a BinStep labeled 'X10', the function returns the string 'X10' for its symbolic representation, and numerically, it would return the value 10.

## Math Helpers

Source code: [math\_helpers/](https://github.com/pontem-network/liquidswap_v1/tree/main/math_helpers)

The protocol leverages helper functions that implement tree math, optimizing the performance of coin swaps across Bins. At its core, the protocol utilizes fixed-point arithmetic, specifically 128-bit and 64-bit math, to ensure accurate and efficient calculations.

## Config

Source code: [sources/config.move](https://github.com/pontem-network/liquidswap_v1/blob/main/sources/config.move)

The component houses the current global configuration, primarily encompassing accounts designated for DAO activities, pool creation, and emergency operations.

## Treasury

Source code: [sources/treasury.move](https://github.com/pontem-network/liquidswap_v1/blob/main/sources/treasury.move)

Keeps the protocol collected fees and allows to withdraw them by DAO account.

## Emergency

Source code: [sources/emergency.move](https://github.com/pontem-network/liquidswap_v1/blob/main/sources/emergency.move)

The protocol includes a provision for an emergency pause of all swap and mint operations, which can be activated through a transaction initiated by the Emergency account.


# Integration

Before we begin, please create a new Move project. This initial step will enable you to follow along with the subsequent instructions more effectively.

## Deployments

The code for [Liquidswap v1](https://github.com/pontem-network/liquidswap_v1) is available in our GitHub repository. You can access it to review, download, or contribute to the project.

### Mainnet

The contracts for our project have been deployed on the Aptos mainnet at the following addresses:

<pre><code><strong># Module addresses (deployer)
</strong>0x54cb0bb2c18564b86e34539b9f89cfe1186e39d89fce54e1cd007b8e61673a85

# Resource account
0xa0d8702b7c696d989675cd2f894f44e79361531cff115c0063390922f5463883

# Emergency resource account
0x9aa8dec24fe7a592de107b4622069c4ed60567900fa3eddade6326d92e9b751c
</code></pre>

### Testnet

If you are working with the Aptos testnet, please use the following addresses:

```
# Module addresses (deployer)
0xc9ccc585c8e1455a5c0ae4e068897a47e7c16cf16f14e0655e3573c2bbc76d48

# Resource account
0x62ff269698a76cf6bccf256d93fc1963926935f74e72d89f5af653ad882568d8

# Emergency resource account
0x370b9a4b15952b33784662e92493b7a78bc93ea2a8d8569028acab49196c2128
```

:warning: **Caution:** ensure you switch to the testnet branch when conducting experiments on the Aptos testnet.

## Dependency

To integrate Liquidswap into your project, begin by adding the Liquidswap dependency to your `Move.toml` file:

{% code lineNumbers="true" %}

```toml
[dependencies.LiquidswapV1]
git = 'https://github.com/pontem-network/liquidswap_v1.git'
rev = 'latest version or testnet'
```

{% endcode %}

Continue the setup by importing the rest of the required dependencies into your project. Ensure each dependency is correctly listed in your 'Move.toml' to facilitate seamless integration:

```toml

[dependencies.BinSteps]
git = 'https://github.com/pontem-network/liquidswap_v1.git'
subdir = 'bin_steps/'
rev = 'latest version or testnet'

[dependencies.Router]
git = 'https://github.com/pontem-network/liquidswap_v1.git'
subdir = 'router/'
rev = 'latest version or testnet'
```

Ensure to replace \`latest version or testnet' with the actual version numbers sourced from the repositories. After updating these details, proceed to compile your project. Ideally, this step should complete without any errors.


# Test Coins

:warning:**This part works only for testnet, as on mainnet there is production ready coins should be used!**

During the integration, you probably would need test coins, so we prepared a repository containing several coins (USDT, BTC, USDC, DAI, ETH) just for tests.

The existing pools on Liquidswap V1 use our test coins, so it's a good idea to follow standard test coins during integration. &#x20;

Repository - [test-coins](https://github.com/pontem-network/test-coins)

The following test coins are already deployed on the testnet and can be used in your project:

```
// Test BTC
0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC

// Test USDT
0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::USDT
```

Add as dependency:&#x20;

```toml
[dependencies.TestCoins]
git = 'https://github.com/pontem-network/test-coins.git'
rev = 'latest version'
```

Use in your code:

```rust
use test_coins::coins::{USDT, BTC};
use test_coins_extended::extended_coins::{USDC, ETH, DAI};
...
```

#### Faucet

Repository - [faucet](https://github.com/pontem-network/faucet)

Tests coins are available on Aptos testnet, to get free coins just do calls to faucet or visit [Liquidswap V1 DApp](https://testnet.cl.liquidswap.com/#/).&#x20;


# Let's Swap

**Always practice solid risk management rules when experimenting with swaps: use the correct slippage values and trusted currency pairs, double-check the numbers before confirming, and compare external price sources.**

In this guide, we'll focus on using the Router to perform a swap from Aptos coins to test USDT. To begin, let's create a new module and then import the Router into this module:

{% embed url="<https://gist.github.com/borispovod/1fbdef36000268a18c975e157118ae23>" %}

When you compile the code provided above, you may encounter a warning. This is expected and not a cause for concern. Next, we'll create a function to engage with a test pool for APT/USDT with a BinStep of 5. This function will facilitate the purchase of APT using the USDT available in your account.

&#x20;Let's start by calculating the expected amount of APT we can receive through this swap:

{% embed url="<https://gist.github.com/borispovod/1173c1ac604886c306766ef6446bc4ee>" %}
Get amount we will get after swap
{% endembed %}

Now, let's proceed to withdraw the specified amount and perform the swap:

{% embed url="<https://gist.github.com/borispovod/c8f3110e625057d670eb2c26158253f7>" %}
Perfom swap
{% endembed %}

Everything proceeded successfully; through the `buy_apt` function, we've swapped test USDT for APT. Importantly, we utilized the leftover value to precisely calculate the amount needed for the swap. However, it's crucial to note that the code provided here should not be used in a production environment, as it's vulnerable to being front-run. This example is for demonstration purposes only, and you must configure slippage settings appropriately before sending a transaction for production use.&#x20;

A more suitable approach for production environments would include:

{% embed url="<https://gist.github.com/borispovod/af0dfb79fc616729d7cf232d3c67597c>" %}
Example with offchain slippage
{% endembed %}

All done. Similarly, you are encouraged to explore and experiment with other router functions detailed in the [Router](/liquidswap-v1-docs/smart-contracts#router) documentation. This exploration can provide further insights and capabilities within your projects.

*An important note regarding decentralized AMM securities and user transactions:*

* *Always use safety checks.*
* *Never swap tokens without a slippage value provided offline.*
* *Use external price feeds or oracles.*


# Add Liquidity

Given that pool creation on Liquidswap requires specific permissions, this tutorial will focus on adding liquidity as the accessible action for users. We will be working with APT and test USDT, utilizing a BinStep of X5:

{% embed url="<https://gist.github.com/borispovod/b33938f8ae517822edfe2618334c3500>" %}

In the example above, we first withdraw a fixed amount of coins from the account and prepare the necessary information regarding bin IDs and liquidity. Next, we specify the slippage for the active bin along with the active bin itself as transaction parameters.&#x20;

Following this, we proceed to add liquidity. Finally, any leftovers and LB tokens are deposited back into the account.


# Burn Liquidity

This example demonstrates the process of burning liquidity from the test USDT / AptosCoin pool:

{% embed url="<https://gist.github.com/borispovod/855762b8a1b95762d212430817d7cd38>" %}


# Flashloans

Liquidswap V1 enables users to borrow liquidity from pools, utilize it within any third-party DeFi protocol or even another Liquidswap pool, and then return the borrowed liquidity along with the pool's standard fee.&#x20;

This entire process—loaning and returning liquidity—must be completed within a single transaction. This ensures that Liquidswap's liquidity remains intact, as it's not possible to separate the loan and return actions into different transactions.

The Flashloan feature in Liquidswap V1 is inspired by the 'Hot Potato' concept, which is detailed further in a [Medium article](https://medium.com/@borispovod/move-hot-potato-pattern-bbc48a48d93c). At its core, this concept leverages the capabilities of the Move language, allowing the creation of a special kind of structure. This structure is designed with specific abilities, making it impossible to copy, drop, or store; it can only be destructed.

Here's how a Flashloan operates in Liquidswap V1:

{% embed url="<https://gist.github.com/borispovod/0bfbc1c019d93277a0a703b6003306fe>" %}


# Liquidswap DApp

<figure><img src="/files/npWBJeNdT29msrDruJGN" alt=""><figcaption></figcaption></figure>

The user interface for CL (Concentrated Liquidity) currently published under the liquidswap subdomain - [https://cl.liquidswap.com ](https://cl.liquidswap.com)

Currently CL inteface allows the following:

* Adding liquidity to existing pools.
* Removing liquidity.
* Swaps.
* Contains the list with known pools and coins from registry.
* Statistic.

### Bridge

Same as previous versions The Liquidswap DApp allows to transfer your coins (USDC, USDT, many others) from other chains to Aptos chain. The bridge developed and maintained by [LayerZero](https://layerzero.network/) team, the DApp itself integrated with bridge widget.

The bridge is available at <https://bridge.liquidswap.com>

### Testnet

The testnet version DApp is available at <https://testnet.cl.liquidswap.com/>


