github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/ammswap/handler.go (about) 1 package ammswap 2 3 import ( 4 "github.com/fibonacci-chain/fbc/x/ammswap/keeper" 5 "github.com/fibonacci-chain/fbc/x/ammswap/types" 6 "github.com/fibonacci-chain/fbc/x/common" 7 "github.com/fibonacci-chain/fbc/x/common/perf" 8 9 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 10 ) 11 12 // NewHandler creates an sdk.Handler for all the ammswap type messages 13 func NewHandler(k Keeper) sdk.Handler { 14 return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { 15 ctx.SetEventManager(sdk.NewEventManager()) 16 var handlerFun func() (*sdk.Result, error) 17 var name string 18 switch msg := msg.(type) { 19 case types.MsgAddLiquidity: 20 name = "handleMsgAddLiquidity" 21 handlerFun = func() (*sdk.Result, error) { 22 return handleMsgAddLiquidity(ctx, k, msg) 23 } 24 case types.MsgRemoveLiquidity: 25 name = "handleMsgRemoveLiquidity" 26 handlerFun = func() (*sdk.Result, error) { 27 return handleMsgRemoveLiquidity(ctx, k, msg) 28 } 29 case types.MsgCreateExchange: 30 name = "handleMsgCreateExchange" 31 handlerFun = func() (*sdk.Result, error) { 32 return handleMsgCreateExchange(ctx, k, msg) 33 } 34 case types.MsgTokenToToken: 35 name = "handleMsgTokenToToken" 36 handlerFun = func() (*sdk.Result, error) { 37 return handleMsgTokenToToken(ctx, k, msg) 38 } 39 default: 40 return nil, types.ErrSwapUnknownMsgType() 41 } 42 seq := perf.GetPerf().OnDeliverTxEnter(ctx, types.ModuleName, name) 43 defer perf.GetPerf().OnDeliverTxExit(ctx, types.ModuleName, name, seq) 44 45 res, err := handlerFun() 46 common.SanityCheckHandler(res, err) 47 return res, err 48 } 49 } 50 51 func handleMsgTokenToToken(ctx sdk.Context, k Keeper, msg types.MsgTokenToToken) (*sdk.Result, error) { 52 _, err := k.GetSwapTokenPair(ctx, msg.GetSwapTokenPairName()) 53 if err != nil { 54 return swapTokenByRouter(ctx, k, msg) 55 } else { 56 return swapToken(ctx, k, msg) 57 } 58 } 59 60 func handleMsgCreateExchange(ctx sdk.Context, k Keeper, msg types.MsgCreateExchange) (*sdk.Result, error) { 61 event := sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName)) 62 63 // 0. check if 2 tokens exist 64 err := k.IsTokenExist(ctx, msg.Token0Name) 65 if err != nil { 66 return nil, err 67 } 68 69 err = k.IsTokenExist(ctx, msg.Token1Name) 70 if err != nil { 71 return nil, err 72 } 73 74 // 1. check if the token pair exists 75 tokenPairName := msg.GetSwapTokenPairName() 76 _, err = k.GetSwapTokenPair(ctx, tokenPairName) 77 if err == nil { 78 return types.ErrSwapTokenPairExist().Result() 79 } 80 81 // 2. check if the pool token exists 82 poolTokenName := types.GetPoolTokenName(msg.Token0Name, msg.Token1Name) 83 _, err = k.GetPoolTokenInfo(ctx, poolTokenName) 84 if err == nil { 85 return types.ErrPoolTokenPairExist().Result() 86 } 87 88 // 3. create the pool token 89 k.NewPoolToken(ctx, poolTokenName) 90 91 // 4. create the token pair 92 swapTokenPair := types.NewSwapPair(msg.Token0Name, msg.Token1Name) 93 k.SetSwapTokenPair(ctx, tokenPairName, swapTokenPair) 94 95 // 5. notify backend module 96 k.OnCreateExchange(ctx, swapTokenPair) 97 98 event = event.AppendAttributes(sdk.NewAttribute("pool-token-name", poolTokenName)) 99 event = event.AppendAttributes(sdk.NewAttribute("token-pair", tokenPairName)) 100 ctx.EventManager().EmitEvent(event) 101 return &sdk.Result{Events: ctx.EventManager().Events()}, nil 102 } 103 104 func handleMsgAddLiquidity(ctx sdk.Context, k Keeper, msg types.MsgAddLiquidity) (*sdk.Result, error) { 105 event := sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName)) 106 if msg.Deadline < ctx.BlockTime().Unix() { 107 return types.ErrBlockTimeBigThanDeadline().Result() 108 } 109 swapTokenPair, err := k.GetSwapTokenPair(ctx, msg.GetSwapTokenPairName()) 110 if err != nil { 111 return nil, err 112 } 113 baseTokens := sdk.NewDecCoinFromDec(msg.MaxBaseAmount.Denom, sdk.ZeroDec()) 114 var liquidity sdk.Dec 115 poolToken, err := k.GetPoolTokenInfo(ctx, swapTokenPair.PoolTokenName) 116 if err != nil { 117 return nil, err 118 } 119 if swapTokenPair.QuotePooledCoin.Amount.IsZero() && swapTokenPair.BasePooledCoin.Amount.IsZero() { 120 baseTokens.Amount = msg.MaxBaseAmount.Amount 121 liquidity = sdk.NewDec(1) 122 } else if swapTokenPair.BasePooledCoin.IsPositive() && swapTokenPair.QuotePooledCoin.IsPositive() { 123 baseTokens.Amount = common.MulAndQuo(msg.QuoteAmount.Amount, swapTokenPair.BasePooledCoin.Amount, swapTokenPair.QuotePooledCoin.Amount) 124 totalSupply := k.GetPoolTokenAmount(ctx, swapTokenPair.PoolTokenName) 125 if baseTokens.IsZero() { 126 baseTokens.Amount = sdk.NewDecWithPrec(1, sdk.Precision) 127 } 128 if totalSupply.IsZero() { 129 return types.ErrIsZeroValue("totalSupply").Result() 130 } 131 liquidity = common.MulAndQuo(msg.QuoteAmount.Amount, totalSupply, swapTokenPair.QuotePooledCoin.Amount) 132 if liquidity.IsZero() { 133 return types.ErrIsZeroValue("liquidity").Result() 134 } 135 } else { 136 return types.ErrInvalidTokenPair(swapTokenPair.String()).Result() 137 } 138 if baseTokens.Amount.GT(msg.MaxBaseAmount.Amount) { 139 return types.ErrBaseTokensAmountBiggerThanMax().Result() 140 } 141 if liquidity.LT(msg.MinLiquidity) { 142 return types.ErrLessThan("liquidity", "min liquidity").Result() 143 } 144 145 // transfer coins 146 coins := sdk.SysCoins{ 147 msg.QuoteAmount, 148 baseTokens, 149 } 150 151 coins = coinSort(coins) 152 153 err = k.SendCoinsToPool(ctx, coins, msg.Sender) 154 if err != nil { 155 return types.ErrSendCoinsFailed(err).Result() 156 } 157 // update swapTokenPair 158 swapTokenPair.QuotePooledCoin = swapTokenPair.QuotePooledCoin.Add(msg.QuoteAmount) 159 swapTokenPair.BasePooledCoin = swapTokenPair.BasePooledCoin.Add(baseTokens) 160 k.SetSwapTokenPair(ctx, msg.GetSwapTokenPairName(), swapTokenPair) 161 162 // update poolToken 163 poolCoins := sdk.NewDecCoinFromDec(poolToken.Symbol, liquidity) 164 err = k.MintPoolCoinsToUser(ctx, sdk.SysCoins{poolCoins}, msg.Sender) 165 if err != nil { 166 return types.ErrMintPoolTokenFailed(err).Result() 167 } 168 169 event.AppendAttributes(sdk.NewAttribute("liquidity", liquidity.String())) 170 event.AppendAttributes(sdk.NewAttribute("baseAmount", baseTokens.String())) 171 ctx.EventManager().EmitEvent(event) 172 return &sdk.Result{Events: ctx.EventManager().Events()}, nil 173 } 174 175 func handleMsgRemoveLiquidity(ctx sdk.Context, k Keeper, msg types.MsgRemoveLiquidity) (*sdk.Result, error) { 176 event := sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName)) 177 178 if msg.Deadline < ctx.BlockTime().Unix() { 179 return types.ErrMsgDeadlineLessThanBlockTime().Result() 180 } 181 swapTokenPair, err := k.GetSwapTokenPair(ctx, msg.GetSwapTokenPairName()) 182 if err != nil { 183 return nil, err 184 } 185 186 liquidity := msg.Liquidity 187 poolTokenAmount := k.GetPoolTokenAmount(ctx, swapTokenPair.PoolTokenName) 188 if poolTokenAmount.LT(liquidity) { 189 return types.ErrLessThan("pool token amount", "liquidity").Result() 190 } 191 192 baseDec := common.MulAndQuo(swapTokenPair.BasePooledCoin.Amount, liquidity, poolTokenAmount) 193 quoteDec := common.MulAndQuo(swapTokenPair.QuotePooledCoin.Amount, liquidity, poolTokenAmount) 194 195 baseAmount := sdk.NewDecCoinFromDec(swapTokenPair.BasePooledCoin.Denom, baseDec) 196 quoteAmount := sdk.NewDecCoinFromDec(swapTokenPair.QuotePooledCoin.Denom, quoteDec) 197 198 if baseAmount.IsLT(msg.MinBaseAmount) { 199 return types.ErrLessThan("base amount", "min base amount").Result() 200 } 201 if quoteAmount.IsLT(msg.MinQuoteAmount) { 202 return types.ErrLessThan("quote amount", "min quote amount").Result() 203 } 204 205 // transfer coins 206 coins := sdk.SysCoins{ 207 baseAmount, 208 quoteAmount, 209 } 210 coins = coinSort(coins) 211 err = k.SendCoinsFromPoolToAccount(ctx, coins, msg.Sender) 212 if err != nil { 213 return types.ErrSendCoinsFromPoolToAccountFailed(err.Error()).Result() 214 } 215 // update swapTokenPair 216 swapTokenPair.QuotePooledCoin = swapTokenPair.QuotePooledCoin.Sub(quoteAmount) 217 swapTokenPair.BasePooledCoin = swapTokenPair.BasePooledCoin.Sub(baseAmount) 218 k.SetSwapTokenPair(ctx, msg.GetSwapTokenPairName(), swapTokenPair) 219 220 // update poolToken 221 poolCoins := sdk.NewDecCoinFromDec(swapTokenPair.PoolTokenName, liquidity) 222 err = k.BurnPoolCoinsFromUser(ctx, sdk.SysCoins{poolCoins}, msg.Sender) 223 if err != nil { 224 return types.ErrBurnPoolTokenFailed(err).Result() 225 } 226 227 event.AppendAttributes(sdk.NewAttribute("quoteAmount", quoteAmount.String())) 228 event.AppendAttributes(sdk.NewAttribute("baseAmount", baseAmount.String())) 229 ctx.EventManager().EmitEvent(event) 230 return &sdk.Result{Events: ctx.EventManager().Events()}, nil 231 } 232 233 func swapToken(ctx sdk.Context, k Keeper, msg types.MsgTokenToToken) (*sdk.Result, error) { 234 event := sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName)) 235 236 if err := common.HasSufficientCoins(msg.Sender, k.GetTokenKeeper().GetCoins(ctx, msg.Sender), 237 sdk.SysCoins{msg.SoldTokenAmount}); err != nil { 238 return common.ErrInsufficientCoins(DefaultParamspace, err.Error()).Result() 239 } 240 if msg.Deadline < ctx.BlockTime().Unix() { 241 return types.ErrBlockTimeBigThanDeadline().Result() 242 } 243 swapTokenPair, err := k.GetSwapTokenPair(ctx, msg.GetSwapTokenPairName()) 244 if err != nil { 245 return nil, err 246 } 247 if swapTokenPair.BasePooledCoin.IsZero() || swapTokenPair.QuotePooledCoin.IsZero() { 248 return types.ErrIsZeroValue("base pooled coin or quote pooled coin").Result() 249 } 250 params := k.GetParams(ctx) 251 tokenBuy := keeper.CalculateTokenToBuy(swapTokenPair, msg.SoldTokenAmount, msg.MinBoughtTokenAmount.Denom, params) 252 if tokenBuy.IsZero() { 253 return types.ErrIsZeroValue("token buy").Result() 254 } 255 if tokenBuy.Amount.LT(msg.MinBoughtTokenAmount.Amount) { 256 return types.ErrLessThan("token buy amount", "min bought token amount").Result() 257 } 258 259 res, err := swapTokenNativeToken(ctx, k, swapTokenPair, tokenBuy, msg) 260 if err != nil { 261 return res, err 262 } 263 event.AppendAttributes(sdk.NewAttribute("bought_token_amount", tokenBuy.String())) 264 event.AppendAttributes(sdk.NewAttribute("recipient", msg.Recipient.String())) 265 ctx.EventManager().EmitEvent(event) 266 return &sdk.Result{Events: ctx.EventManager().Events()}, nil 267 } 268 269 func swapTokenByRouter(ctx sdk.Context, k Keeper, msg types.MsgTokenToToken) (*sdk.Result, error) { 270 event := sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName)) 271 272 if msg.Deadline < ctx.BlockTime().Unix() { 273 return types.ErrBlockTimeBigThanDeadline().Result() 274 } 275 if err := common.HasSufficientCoins(msg.Sender, k.GetTokenKeeper().GetCoins(ctx, msg.Sender), 276 sdk.SysCoins{msg.SoldTokenAmount}); err != nil { 277 return common.ErrInsufficientCoins(DefaultParamspace, err.Error()).Result() 278 } 279 tokenPairOne := types.GetSwapTokenPairName(msg.SoldTokenAmount.Denom, sdk.DefaultBondDenom) 280 swapTokenPairOne, err := k.GetSwapTokenPair(ctx, tokenPairOne) 281 if err != nil { 282 return nil, types.ErrNonExistSwapTokenPair(msg.GetSwapTokenPairName()) 283 } 284 if swapTokenPairOne.BasePooledCoin.IsZero() || swapTokenPairOne.QuotePooledCoin.IsZero() { 285 return types.ErrIsZeroValue("base pooled coin or quote pooled coin").Result() 286 } 287 tokenPairTwo := types.GetSwapTokenPairName(msg.MinBoughtTokenAmount.Denom, sdk.DefaultBondDenom) 288 swapTokenPairTwo, err := k.GetSwapTokenPair(ctx, tokenPairTwo) 289 if err != nil { 290 return nil, types.ErrNonExistSwapTokenPair(msg.GetSwapTokenPairName()) 291 } 292 if swapTokenPairTwo.BasePooledCoin.IsZero() || swapTokenPairTwo.QuotePooledCoin.IsZero() { 293 return types.ErrIsZeroValue("base pooled coin or quote pooled coin").Result() 294 } 295 296 nativeAmount := sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.MustNewDecFromStr("0")) 297 params := k.GetParams(ctx) 298 msgOne := msg 299 msgOne.MinBoughtTokenAmount = nativeAmount 300 tokenNative := keeper.CalculateTokenToBuy(swapTokenPairOne, msgOne.SoldTokenAmount, msgOne.MinBoughtTokenAmount.Denom, params) 301 if tokenNative.IsZero() { 302 return types.ErrIsZeroValue("token native").Result() 303 } 304 msgTwo := msg 305 msgTwo.SoldTokenAmount = tokenNative 306 tokenBuy := keeper.CalculateTokenToBuy(swapTokenPairTwo, msgTwo.SoldTokenAmount, msgTwo.MinBoughtTokenAmount.Denom, params) 307 // sanity check. user may set MinBoughtTokenAmount to zero on front end. 308 // if set zero,this will not return err 309 if tokenBuy.IsZero() { 310 return types.ErrIsZeroValue("token to buy amount").Result() 311 } 312 if tokenBuy.Amount.LT(msg.MinBoughtTokenAmount.Amount) { 313 return types.ErrLessThan("token buy amount", "min bought token amount").Result() 314 } 315 316 res, err := swapTokenNativeToken(ctx, k, swapTokenPairOne, tokenNative, msgOne) 317 if err != nil { 318 return res, err 319 } 320 res, err = swapTokenNativeToken(ctx, k, swapTokenPairTwo, tokenBuy, msgTwo) 321 if err != nil { 322 return res, err 323 } 324 325 event.AppendAttributes(sdk.NewAttribute("bought_token_amount", tokenBuy.String())) 326 event.AppendAttributes(sdk.NewAttribute("recipient", msg.Recipient.String())) 327 ctx.EventManager().EmitEvent(event) 328 return &sdk.Result{Events: ctx.EventManager().Events()}, nil 329 } 330 331 func swapTokenNativeToken( 332 ctx sdk.Context, k Keeper, swapTokenPair SwapTokenPair, tokenBuy sdk.SysCoin, 333 msg types.MsgTokenToToken, 334 ) (*sdk.Result, error) { 335 // transfer coins 336 err := k.SendCoinsToPool(ctx, sdk.SysCoins{msg.SoldTokenAmount}, msg.Sender) 337 if err != nil { 338 return types.ErrSendCoinsToPoolFailed(err.Error()).Result() 339 } 340 341 err = k.SendCoinsFromPoolToAccount(ctx, sdk.SysCoins{tokenBuy}, msg.Recipient) 342 if err != nil { 343 return types.ErrSendCoinsFromPoolToAccountFailed(err.Error()).Result() 344 } 345 346 // update swapTokenPair 347 if msg.MinBoughtTokenAmount.Denom < msg.SoldTokenAmount.Denom { 348 swapTokenPair.QuotePooledCoin = swapTokenPair.QuotePooledCoin.Add(msg.SoldTokenAmount) 349 swapTokenPair.BasePooledCoin = swapTokenPair.BasePooledCoin.Sub(tokenBuy) 350 } else { 351 swapTokenPair.QuotePooledCoin = swapTokenPair.QuotePooledCoin.Sub(tokenBuy) 352 swapTokenPair.BasePooledCoin = swapTokenPair.BasePooledCoin.Add(msg.SoldTokenAmount) 353 } 354 k.SetSwapTokenPair(ctx, msg.GetSwapTokenPairName(), swapTokenPair) 355 k.OnSwapToken(ctx, msg.Recipient, swapTokenPair, msg.SoldTokenAmount, tokenBuy) 356 return &sdk.Result{}, nil 357 } 358 359 func coinSort(coins sdk.SysCoins) sdk.SysCoins { 360 var newCoins sdk.SysCoins 361 for _, coin := range coins { 362 if coin.Amount.IsPositive() { 363 newCoins = append(newCoins, coin) 364 } 365 } 366 newCoins = newCoins.Sort() 367 return newCoins 368 }