@sentre/senswap - v1.0.7

SenSwap

SenSwap is a Balancer-like AMM on Solana. The AMM implementation is heavily relying on Balancer's Whitepaper.

Beside that, we also add some extra features to help leverage others on-top application in the future. You can find details in the Implementation.

Preresiquite

Install anchor-lang

Please follow this link https://www.anchor-lang.com/docs/installation to setup anchor-lang on your machine.

pnpm

Due to some conlicts between yarn and anchor-lang, we decided to migrate yarn to pnpm within this project. Follow https://pnpm.io/installation to setup pnpm and run the below commands to install the project's dependencies

pnpm install

Build

To build the program,

pnpm build

Test

To run the testcase,

pnpm test

Folder Structure

Folder name Description
src/balancer-amm The smart contract implementation
src/balancer-amm/instructions The smart contract's processors. Each instruction from users will be navigated to the relevant processor.
src/balancer-amm/math Contains math utitilies such as safe f64 and Balancer's formulas in oracle subfolder.
src/balancer-amm/schema Defines program account schema.
lib The SenSwap Javascript SDK to help developers to interact with the smart contract.
migrations Contains smart contract deployment scripts.
tests Contains testcases for both the smart contract and the SDK.
.github Defines Github workflows

Implementation

This section will require the reader to understand both the current documentation and the source code also.

senswap.jpg Fig.1. This is flowchart-like diagram depicting available features of SenSwap corresponding to its state. Note that the circle is just a diagram connector and has no semantic meaning.

How to initialize a pool?

To initialize a pool, the pool owner needs to call initialize_pool first. The function will ask for basic info like mints, treasuries, weights, etc., to create a pool account and store these info.

Note that initialize_pool won't create any accounts (execept the pool account) and keep the status of PoolState::Uninitialized until the pool when owner calls initialize_join.

To create the treasuries (aka token accounts) corresponding to the mints, and also deposit the initial amount of tokens into the pool, the pool owner must call initialize_join for all mints one by one. After all, the pool will transmit the status from PoolState::Uninitialized to PoolState::Initializing and there are a number LP initialized for the pool owner aka the first liquidity provider.

In case of incorrect configs, the pool owner can cancel the pool by close_pool. This function is only possible when the pool is PoolState::Uninitialized.

There current pool limit is 8 mints.

How to finalize a pool?

Once the pool is initialized, only the pool owner is able to update weights, add/remove liquidity. This limit is to avoid rug pull and build a foundation for liquidity boostrapping (aka. launchpad).

To open the pool to the publish, the pool owner must finalized the pool state from PoolState::Initializing to PoolState::Initialized. At that time, the publish can join and add/remove liquidity to the pool.

How to add liquidity?

By adding liquidity, the user will become an liquidity provider. However, add_liquidity differs from initialize_pool that it requires all token deposited at once. In exchange, the liquidity provider will receive a corresponding number of LP tokens.

How to remove liquidity?

The liquidity providers can return LP tokens via remove_liquidity or remove_sided_liqudity to get back their deposited tokens.

The different between remove_liquidity and remove_sided_liqudity is that remove_liquidity will return all mints respectively to the current proportion while remove_sided_liqudity will allow liquidity providers to select their single favorite mint to withdraw.

Senswap now only supports remove_liquidity for full liquidity withdrawals.

Swap

Users can run a swap by calling swap. There exists fees for each transaction. You can find more in Fee & Tax.

Route

route is a high-level abstract function of swap. Users can call multiple swap-s in a monolithic transaction of route. The basic idea is that route will verify params for each swap then self-invoke the program by calling swap. This function is really helpful for AMM aggregators.

Fee & Tax

On each swap, there are fees that the trader must to pay:

  • Liquidity Provision Fee (aka fee):

    • IT'S COLLECTED OVER BIDDING TOKENS.
    • This fee is to incentivize people to provide liquidity into the pool and avoid permanent loss.
  • Platform Fee (aka tax):

    • IT'S COLLECTED OVER ASKING TOKENS.
    • This tax is to help SenSwap maintain the system and develop more features to the platform.
    • However, the tax is not only for the SenSwap foundation, but also being structured for the referral system. When referrer addresses are injected in a transaction, the tax will splitted equally for the platform fee and referral fees. For example, there are 2 referrer addresses in a swap transaction and 90 tokens for the tax, then the platform fee will be 30 tokens, and 30 tokens for each referrer.
    • There is MAXIMUM 2 referral addresses.
    • We skip the tax in term of inbalance liquidity provision.

As you may notice, SenSwap allows liquidity providers add/remove liquidity asymmetrically while the system will make an auto swap to balance the structure. Because of the auto swap, the fee & tax is available in add_liquidity, remove_liquidity, or remove_sided_liquidity as well. However, the referal system is disabled in those transaction types.

Computing Precision

In regard to the Balancer's multiple-weighted-tokens AMM, the computation is heavily relies on fractional exponentiation. To keep the implementation effective and reliable, we employ directly the float system f64 in Rust to all relevant computation.

Audited

Copyright

SenSwap © 2023 Sentizen Foundation.

Generated using TypeDoc