github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/ammswap/keeper/querier.go (about) 1 package keeper 2 3 import ( 4 "encoding/json" 5 6 abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types" 7 "github.com/fibonacci-chain/fbc/x/common" 8 9 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 10 "github.com/fibonacci-chain/fbc/x/ammswap/types" 11 ) 12 13 // NewQuerier creates a new querier for swap clients. 14 func NewQuerier(k Keeper) sdk.Querier { 15 return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { 16 switch path[0] { 17 case types.QueryBuyAmount: 18 res, err = queryBuyAmount(ctx, path[1:], req, k) 19 case types.QuerySwapTokenPair: 20 res, err = querySwapTokenPair(ctx, path[1:], req, k) 21 case types.QueryParams: 22 res, err = queryParams(ctx, path[1:], req, k) 23 case types.QuerySwapTokenPairs: 24 res, err = querySwapTokenPairs(ctx, path[1:], req, k) 25 case types.QueryRedeemableAssets: 26 res, err = queryRedeemableAssets(ctx, path[1:], req, k) 27 case types.QuerySwapQuoteInfo: 28 res, err = querySwapQuoteInfo(ctx, req, k) 29 case types.QuerySwapAddLiquidityQuote: 30 res, err = querySwapAddLiquidityQuote(ctx, req, k) 31 32 default: 33 return nil, types.ErrSwapUnknownQueryType() 34 } 35 36 if err != nil { 37 response := common.GetErrorResponse(types.CodeInternalError, "", err.Error()) 38 res, errJSON := json.Marshal(response) 39 if errJSON != nil { 40 return nil, common.ErrMarshalJSONFailed(errJSON.Error()) 41 } 42 return res, err 43 } 44 45 return res, nil 46 } 47 } 48 49 // nolint 50 func querySwapTokenPair( 51 ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper, 52 ) ([]byte, sdk.Error) { 53 tokenPairName := path[0] 54 var response *common.BaseResponse 55 tokenPair, err := keeper.GetSwapTokenPair(ctx, tokenPairName) 56 // return nil when token pair not exists 57 if err != nil { 58 response = common.GetBaseResponse(nil) 59 } else { 60 response = common.GetBaseResponse(tokenPair) 61 } 62 63 bz, err := json.Marshal(response) 64 if err != nil { 65 return nil, common.ErrMarshalJSONFailed(err.Error()) 66 } 67 return bz, nil 68 } 69 70 // nolint 71 func queryBuyAmount( 72 ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper, 73 ) ([]byte, sdk.Error) { 74 var queryParams types.QueryBuyAmountParams 75 err := keeper.cdc.UnmarshalJSON(req.Data, &queryParams) 76 if err != nil { 77 return nil, common.ErrUnMarshalJSONFailed(err.Error()) 78 } 79 errToken := types.ValidateSwapAmountName(queryParams.TokenToBuy) 80 if errToken != nil { 81 return nil, errToken 82 } 83 errToken = types.ValidateSwapAmountName(queryParams.SoldToken.Denom) 84 if errToken != nil { 85 return nil, errToken 86 } 87 params := keeper.GetParams(ctx) 88 var buyAmount sdk.Dec 89 swapTokenPair := types.GetSwapTokenPairName(queryParams.SoldToken.Denom, queryParams.TokenToBuy) 90 tokenPair, errTokenPair := keeper.GetSwapTokenPair(ctx, swapTokenPair) 91 if errTokenPair == nil { 92 if tokenPair.BasePooledCoin.IsZero() || tokenPair.QuotePooledCoin.IsZero() { 93 return nil, types.ErrIsZeroValue("base pooled coin or quote pooled coin") 94 } 95 buyAmount = CalculateTokenToBuy(tokenPair, queryParams.SoldToken, queryParams.TokenToBuy, params).Amount 96 } else { 97 tokenPairName1 := types.GetSwapTokenPairName(queryParams.SoldToken.Denom, sdk.DefaultBondDenom) 98 tokenPair1, err := keeper.GetSwapTokenPair(ctx, tokenPairName1) 99 if err != nil { 100 return nil, err 101 } 102 if tokenPair1.BasePooledCoin.IsZero() || tokenPair1.QuotePooledCoin.IsZero() { 103 return nil, types.ErrIsZeroValue("base pooled coin or quote pooled coin") 104 } 105 tokenPairName2 := types.GetSwapTokenPairName(queryParams.TokenToBuy, sdk.DefaultBondDenom) 106 tokenPair2, err := keeper.GetSwapTokenPair(ctx, tokenPairName2) 107 if err != nil { 108 return nil, err 109 } 110 if tokenPair2.BasePooledCoin.IsZero() || tokenPair2.QuotePooledCoin.IsZero() { 111 return nil, types.ErrIsZeroValue("base pooled coin or quote pooled coin") 112 } 113 nativeToken := CalculateTokenToBuy(tokenPair1, queryParams.SoldToken, sdk.DefaultBondDenom, params) 114 buyAmount = CalculateTokenToBuy(tokenPair2, nativeToken, queryParams.TokenToBuy, params).Amount 115 } 116 117 bz := keeper.cdc.MustMarshalJSON(buyAmount) 118 119 return bz, nil 120 } 121 122 func queryParams(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { 123 return keeper.cdc.MustMarshalJSON(keeper.GetParams(ctx)), nil 124 } 125 126 // nolint 127 func querySwapTokenPairs(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, 128 err sdk.Error) { 129 return keeper.cdc.MustMarshalJSON(keeper.GetSwapTokenPairs(ctx)), nil 130 } 131 132 // nolinte 133 func queryRedeemableAssets(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { 134 tokenPairName := path[0] 135 swapTokenPair, err := keeper.GetSwapTokenPair(ctx, tokenPairName) 136 if err != nil { 137 return nil, err 138 } 139 140 liquidity, errDec := sdk.NewDecFromStr(path[1]) 141 if errDec != nil { 142 return nil, errDec 143 144 } 145 var tokenList sdk.SysCoins 146 baseToken, quoteToken, err := keeper.GetRedeemableAssets(ctx, swapTokenPair.BasePooledCoin.Denom, swapTokenPair.QuotePooledCoin.Denom, liquidity) 147 if err != nil { 148 return nil, err 149 } 150 tokenList = append(tokenList, baseToken, quoteToken) 151 bz := keeper.cdc.MustMarshalJSON(tokenList) 152 return bz, nil 153 } 154 155 // querySwapQuoteInfo returns infos when swap token 156 func querySwapQuoteInfo(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { 157 var queryParams types.QuerySwapBuyInfoParams 158 err := keeper.cdc.UnmarshalJSON(req.Data, &queryParams) 159 if err != nil { 160 return nil, common.ErrUnMarshalJSONFailed(err.Error()) 161 } 162 if queryParams.SellTokenAmount == "" || queryParams.BuyToken == "" { 163 return nil, types.ErrSellAmountOrBuyTokenIsEmpty() 164 } 165 166 sellAmount, err := sdk.ParseDecCoin(queryParams.SellTokenAmount) 167 if err != nil { 168 return nil, types.ErrConvertSellTokenAmount(queryParams.SellTokenAmount, err) 169 } 170 171 if sellAmount.Denom == queryParams.BuyToken { 172 return nil, types.ErrSellAmountEqualBuyToken() 173 } 174 175 var route string 176 var fee sdk.SysCoin 177 buyAmount := sdk.ZeroDec() 178 marketPrice := sdk.ZeroDec() 179 180 swapParams := keeper.GetParams(ctx) 181 tokenPairName := types.GetSwapTokenPairName(sellAmount.Denom, queryParams.BuyToken) 182 tokenPair, err := keeper.GetSwapTokenPair(ctx, tokenPairName) 183 if err == nil { 184 if tokenPair.BasePooledCoin.Amount.IsZero() || tokenPair.QuotePooledCoin.IsZero() { 185 return nil, types.ErrIsZeroValue("base pooled coin or quote pooled coin") 186 } 187 buyAmount = CalculateTokenToBuy(tokenPair, sellAmount, queryParams.BuyToken, swapParams).Amount 188 // calculate market price 189 if tokenPair.BasePooledCoin.Denom == sellAmount.Denom { 190 marketPrice = tokenPair.QuotePooledCoin.Amount.Quo(tokenPair.BasePooledCoin.Amount) 191 } else { 192 marketPrice = tokenPair.BasePooledCoin.Amount.Quo(tokenPair.QuotePooledCoin.Amount) 193 } 194 // calculate fee 195 fee = sdk.NewDecCoinFromDec(sellAmount.Denom, sellAmount.Amount.Mul(swapParams.FeeRate)) 196 } else { 197 tokenPairName1 := types.GetSwapTokenPairName(sellAmount.Denom, common.NativeToken) 198 tokenPair1, err := keeper.GetSwapTokenPair(ctx, tokenPairName1) 199 if err != nil { 200 return nil, err 201 } 202 if tokenPair1.BasePooledCoin.Amount.IsZero() || tokenPair1.QuotePooledCoin.IsZero() { 203 return nil, types.ErrIsZeroValue("base pooled coin or quote pooled coin") 204 } 205 tokenPairName2 := types.GetSwapTokenPairName(queryParams.BuyToken, common.NativeToken) 206 tokenPair2, err := keeper.GetSwapTokenPair(ctx, tokenPairName2) 207 if err != nil { 208 return nil, err 209 } 210 if tokenPair2.BasePooledCoin.Amount.IsZero() || tokenPair2.QuotePooledCoin.IsZero() { 211 return nil, types.ErrIsZeroValue("base pooled coin or quote pooled coin") 212 } 213 nativeToken := CalculateTokenToBuy(tokenPair1, sellAmount, common.NativeToken, swapParams) 214 buyAmount = CalculateTokenToBuy(tokenPair2, nativeToken, queryParams.BuyToken, swapParams).Amount 215 216 // calculate market price 217 var sellTokenMarketPrice sdk.Dec 218 var routeTokenMarketPrice sdk.Dec 219 if tokenPair1.BasePooledCoin.Denom == sellAmount.Denom { 220 sellTokenMarketPrice = tokenPair1.QuotePooledCoin.Amount.Quo(tokenPair1.BasePooledCoin.Amount) 221 } else { 222 sellTokenMarketPrice = tokenPair1.BasePooledCoin.Amount.Quo(tokenPair1.QuotePooledCoin.Amount) 223 } 224 if tokenPair2.BasePooledCoin.Denom == common.NativeToken { 225 routeTokenMarketPrice = tokenPair2.QuotePooledCoin.Amount.Quo(tokenPair2.BasePooledCoin.Amount) 226 } else { 227 routeTokenMarketPrice = tokenPair2.BasePooledCoin.Amount.Quo(tokenPair2.QuotePooledCoin.Amount) 228 } 229 if routeTokenMarketPrice.IsPositive() && sellTokenMarketPrice.IsPositive() { 230 marketPrice = sellTokenMarketPrice.Mul(routeTokenMarketPrice) 231 } 232 233 // calculate fee 234 fee1 := sdk.NewDecCoinFromDec(sellAmount.Denom, sellAmount.Amount.Mul(swapParams.FeeRate)) 235 routeTokenFee := sdk.NewDecCoinFromDec(common.NativeToken, nativeToken.Amount.Mul(swapParams.FeeRate)) 236 fee2 := CalculateTokenToBuy(tokenPair1, routeTokenFee, sellAmount.Denom, swapParams) 237 fee = fee1.Add(fee2) 238 239 // swap by route 240 route = common.NativeToken 241 } 242 243 // calculate price 244 price := sdk.ZeroDec() 245 if sellAmount.Amount.IsPositive() { 246 price = buyAmount.Quo(sellAmount.Amount) 247 } 248 249 // calculate price impact 250 var priceImpact sdk.Dec 251 if marketPrice.IsPositive() { 252 if marketPrice.GT(price) { 253 priceImpact = marketPrice.Sub(price).Quo(marketPrice) 254 } else { 255 priceImpact = price.Sub(marketPrice).Quo(marketPrice) 256 } 257 } 258 259 swapBuyInfo := types.SwapBuyInfo{ 260 BuyAmount: buyAmount, 261 Price: price, 262 PriceImpact: priceImpact, 263 Fee: fee.String(), 264 Route: route, 265 } 266 267 response := common.GetBaseResponse(swapBuyInfo) 268 bz, err := json.Marshal(response) 269 if err != nil { 270 return nil, common.ErrMarshalJSONFailed(err.Error()) 271 } 272 return bz, nil 273 274 } 275 276 // querySwapAddLiquidityQuote returns swap information of adding liquidity 277 func querySwapAddLiquidityQuote(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { 278 var queryParams types.QuerySwapAddInfoParams 279 err := keeper.cdc.UnmarshalJSON(req.Data, &queryParams) 280 if err != nil { 281 return nil, common.ErrUnMarshalJSONFailed(err.Error()) 282 } 283 // check params 284 if queryParams.QuoteTokenAmount == "" { 285 return nil, types.ErrQueryParamsQuoteTokenAmountIsEmpty() 286 } 287 if queryParams.BaseToken == "" { 288 return nil, types.ErrQueryParamsBaseTokenIsEmpty() 289 } 290 queryTokenAmount, err := sdk.ParseDecCoin(queryParams.QuoteTokenAmount) 291 if err != nil { 292 return nil, types.ErrConvertQuoteTokenAmount(queryParams.QuoteTokenAmount, err) 293 } 294 295 tokenPairName := types.GetSwapTokenPairName(queryParams.BaseToken, queryTokenAmount.Denom) 296 swapTokenPair, err := keeper.GetSwapTokenPair(ctx, tokenPairName) 297 if err != nil { 298 return nil, err 299 } 300 if swapTokenPair.BasePooledCoin.Amount.IsZero() && swapTokenPair.QuotePooledCoin.Amount.IsZero() { 301 return nil, types.ErrIsZeroValue("base pooled coin or quote pooled coin") 302 } 303 304 totalSupply := keeper.GetPoolTokenAmount(ctx, swapTokenPair.PoolTokenName) 305 if totalSupply.IsZero() { 306 return nil, types.ErrIsZeroValue("total supply") 307 } 308 309 var addAmount sdk.Dec 310 var liquidity sdk.Dec 311 if swapTokenPair.BasePooledCoin.Denom == queryTokenAmount.Denom { 312 addAmount = common.MulAndQuo(queryTokenAmount.Amount, swapTokenPair.QuotePooledCoin.Amount, swapTokenPair.BasePooledCoin.Amount) 313 liquidity = common.MulAndQuo(addAmount, totalSupply, swapTokenPair.QuotePooledCoin.Amount) 314 } else { 315 addAmount = common.MulAndQuo(queryTokenAmount.Amount, swapTokenPair.BasePooledCoin.Amount, swapTokenPair.QuotePooledCoin.Amount) 316 liquidity = common.MulAndQuo(queryTokenAmount.Amount, totalSupply, swapTokenPair.QuotePooledCoin.Amount) 317 } 318 addInfo := types.SwapAddInfo{ 319 BaseTokenAmount: addAmount, 320 PoolShare: liquidity.Quo(totalSupply.Add(liquidity)), 321 Liquidity: liquidity, 322 } 323 response := common.GetBaseResponse(addInfo) 324 bz, err := json.Marshal(response) 325 if err != nil { 326 return nil, common.ErrMarshalJSONFailed(err.Error()) 327 } 328 return bz, nil 329 330 }