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  }