diff --git a/x/gamm/GAMM_ExitPoolMsgs.png b/x/gamm/GAMM_ExitPoolMsgs.png new file mode 100644 index 00000000000..6f73d4ee43b Binary files /dev/null and b/x/gamm/GAMM_ExitPoolMsgs.png differ diff --git a/x/gamm/GAMM_JoinPoolMsgs.png b/x/gamm/GAMM_JoinPoolMsgs.png new file mode 100644 index 00000000000..99608f4116e Binary files /dev/null and b/x/gamm/GAMM_JoinPoolMsgs.png differ diff --git a/x/gamm/README.md b/x/gamm/README.md index d1d4b43f49b..0b5be0da411 100644 --- a/x/gamm/README.md +++ b/x/gamm/README.md @@ -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 @@ -23,13 +26,22 @@ 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. @@ -37,8 +49,18 @@ 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: + +
#### Exiting Pool @@ -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: + +
+ ### Swap @@ -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 @@ -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. diff --git a/x/gamm/keeper/msg_server.go b/x/gamm/keeper/msg_server.go index 0b755013b58..ab0b7434842 100644 --- a/x/gamm/keeper/msg_server.go +++ b/x/gamm/keeper/msg_server.go @@ -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) @@ -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)