Skip to content

Commit

Permalink
GAMM module spec improvements (#3654)
Browse files Browse the repository at this point in the history
## What is the purpose of the change

This PR is created during the Informal Systems audit, after analysis of the existing specification and code inspection
Auditing is performed on commit hash: 42d73f1

## Brief Changelog
- GAMM/README.md is expanded with certain details that we found important to know and understand during the creation of the TLA spec for the GAMM module
- Diagrams added for easier understanding of code structure of GAMM module base and two types of pool styles (balancer and stableswap)
- minor changes in function descriptions to hold the explanation of the actual implementation

## Testing and Verifying

*(Please pick one of the following options)*

This change is a trivial rework / code cleanup without any test coverage.

## Documentation and Release Note

  - Does this pull request introduces a new feature or user-facing behavior changes?  no
  - Is a relevant changelog entry added to the `Unreleased` section in `CHANGELOG.md`?  no
  - How is the feature or change documented?  specification x/GAMM/README.md

(cherry picked from commit a942009)
  • Loading branch information
dalmirel authored and mergify[bot] committed Dec 14, 2022
1 parent 77d1060 commit adc6ffb
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 16 deletions.
Binary file added x/gamm/GAMM_ExitPoolMsgs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added x/gamm/GAMM_JoinPoolMsgs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
63 changes: 53 additions & 10 deletions x/gamm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ The ``GAMM`` module (**G**eneralized **A**utomated **M**arket **M**aker) provide

## Concepts

The `x/gamm` module implements an AMM using Balancer style pools with
varying amounts and weights of assets in pools.
The `x/gamm` module implements an AMM using:
- Balancer style pools with varying amounts and weights of assets in pools.
- Stableswap pools - still WIP.

Here we will explain basic GAMM concepts and give an overview of how GAMM module's code is organized to support both type of pools.

### Pool

Expand All @@ -23,22 +26,41 @@ varying amounts and weights of assets in pools.
At an initial creation of the pool, a fixed amount of 100 share token is
minted in the pool and sent to the creator of the pool's account. The
pool share denom is in the format of `gamm/pool/{poolID}` and is
displayed in the format of `GAMM-{poolID}` to the user. Pool assets are
sorted in alphabetical order by default.
displayed in the format of `GAMM-{poolID}` to the user.
Pool assets are sorted in alphabetical order by default.
Pool creation is possible only for at least 2 and no more than 8 denominations.

`PoolCreationFee` needs to be paid to create the pool. This also keeps
us safe when it comes to the malicious creation of unneeded pools.


#### Joining Pool

When joining a pool, a user provides the maximum amount of tokens
they're willing to deposit, while the front end takes care of the
When joining a pool without swapping - with `JoinPool`, a user can provide the maximum amount of tokens `TokenInMaxs'
they're willing to deposit. This argument must contain all the denominations from the pool or no tokens at all,
otherwise, the tx will be aborted.
If `TokenInMaxs` contains no tokens, the calculations are done based on the user's balance as the only constraint.

The front end takes care of the
calculation of how many share tokens the user is eligible for at the
specific moment of sending the transaction.

Calculation of exactly how many tokens are needed to get the designated
share is done at the moment of processing the transaction, validating
that it does not exceed the maximum amount of tokens the user is willing
to deposit. After the validation, GAMM share tokens of the pool are
minted and sent to the user's account. Joining the pool using a single
asset is also possible.
minted and sent to the user's account.

Joining the pool using a single asset is also possible with `JoinSwapExternAmountIn`.

Existing Join types:
- JoinPool
- JoinSwapExternAmountIn
- JoinSwapShareAmountOut

#### Join types code call stack and structure:
<img src="GAMM_JoinPoolMsgs.png" height="500"/>
</br>

#### Exiting Pool

Expand All @@ -49,7 +71,22 @@ the exit fee, which is set as a param of the pool. The user's share
tokens burnt as result. Exiting the pool using a single asset is also
possible.

[Exiting pool](https://github.com/osmosis-labs/osmosis/blob/main/x/gamm/keeper/pool_service.go)
Exiting a pool is possible only if user will leave a positive balance for a certain denomination after exiting
or positive number of LP shares.
Otherwise transaction will be aborted and user will not be able to exit a pool.
Therefore, it is not possible to "drain out" a pool.

When exiting a pool with a swap, both exit and swap fees are paid.

Existing Exit types:
- ExitPool
- ExitSwapExternAmountOut
- ExitSwapShareAmountIn

#### Exit types code call stack and structure:
<img src="GAMM_ExitPoolMsgs.png" height="500"/>
</br>


### Swap

Expand All @@ -69,6 +106,10 @@ user should be putting in is done through the following formula:

`tokenBalanceIn * [{tokenBalanceOut / (tokenBalanceOut - tokenAmountOut)} ^ (tokenWeightOut / tokenWeightIn) -1] / tokenAmountIn`

Existing Swap types:
- SwapExactAmountIn
- SwapExactAmountOut

#### Spot Price

Meanwhile, calculation of the spot price with a swap fee is done using
Expand All @@ -84,7 +125,9 @@ the following formula:

All tokens are swapped using a multi-hop mechanism. That is, all swaps
are routed via the most cost-efficient way, swapping in and out from
multiple pools in the process.
multiple pools in the process.
The most cost-efficient route is determined offline and the list of the pools is provided externally, by user, during the broadcasting of the swapping transaction.
In the moment of the execution the provided route may not be the most cost efficient one anymore.

When a trade consists of just two OSMO-included routes during a single transaction,
the swap fees on each hop would be automatically halved.
Expand Down
12 changes: 6 additions & 6 deletions x/gamm/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,8 @@ func (server msgServer) CreatePool(goCtx context.Context, msg types.CreatePoolMs
// This can result in negotiable difference between the number of shares provided within the msg
// and the actual number of share amount resulted from joining pool.
// Internal logic flow for each pool model is as follows:
// Balancer: TokensIn provided as the argument must be either a single token or tokens containing all assets in the pool.
// * For the case of a single token, we simply perform single asset join (balancer notation: pAo, pool shares amount out,
// given single asset in).
// * For the case of multi-asset join, we first calculate the maximal amount of tokens that can be joined whilst maintaining
// pool asset's ratio without swap. We then iterate through the remaining coins that couldn't be joined
// and perform single asset join on each token.
// Balancer: TokensInMaxs provided as the argument must either contain no tokens or containing all assets in the pool.
// * For the case of a not containing tokens, we simply perform calculation of sharesOut and needed amount of tokens for joining the pool
func (server msgServer) JoinPool(goCtx context.Context, msg *types.MsgJoinPool) (*types.MsgJoinPoolResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

Expand Down Expand Up @@ -208,6 +204,10 @@ func (server msgServer) SwapExactAmountOut(goCtx context.Context, msg *types.Msg
return &types.MsgSwapExactAmountOutResponse{TokenInAmount: tokenInAmount}, nil
}

// JoinSwapExactAmountIn is an LP transaction, that will LP all of the provided tokensIn coins.
// * For the case of a single token, we simply perform single asset join (balancer notation: pAo, pool shares amount out,
// given single asset in).
// For more details on the calculation of the number of shares look at the CalcJoinPoolShares function for the appropriate pool style
func (server msgServer) JoinSwapExternAmountIn(goCtx context.Context, msg *types.MsgJoinSwapExternAmountIn) (*types.MsgJoinSwapExternAmountInResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

Expand Down

0 comments on commit adc6ffb

Please sign in to comment.