mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-10-31 18:27:26 +00:00 
			
		
		
		
	Querier improvements: CDP and Auction priority 1 queries (#644)
* query auction by lot owner
* add SavingsRateDistributed to store
* v2cdps: filtered cdps query
* update v2cdps cli examples
* add savings rate dist counter to begin blocker
* implement savings rate dist cli query
* implement cdp REST queries
* minor auction CLI/REST updates
* fix auction querier bug
* update REST endpoint to 'cdps'
* update to savings-rate-dist
* update SavingsRateDistributed get/set
* update tests
* fix savings rate dist rounding errors
* 'collateralDenom' -> 'collateralType'
* refactor 'v2cdps' -> 'cdps', add ratio param
* fix augmented CDP type, msg string() method
* fix cdp querier test
* filter query results efficiently
* querier tests
* limit type iteration if owner defined
* improve savings rate dist genesis validation
* default sdk.Dec{} to sdk.ZeroDec in queries
* update condition logic for finding intersection
* fix cdp querier filtering
* Update kava-4 swagger (#653)
* add collateral_type, update cdp params
* savings rate, auctions, get cdps
* drop owner from AuctionResponse
* remove duplicate collateral denom
* update query paths with {collateral-type}
			
			
This commit is contained in:
		
							parent
							
								
									641d946ae7
								
							
						
					
					
						commit
						e2f515ba9e
					
				| @ -410,6 +410,8 @@ | ||||
|                   $ref: '#/definitions/CoinCollateral' | ||||
|                 principal: | ||||
|                   $ref: '#/definitions/CoinPrincipal' | ||||
|                 collateral_type: | ||||
|                   $ref: '#/definitions/CollateralType' | ||||
|         responses: | ||||
|           200: | ||||
|             description: Tx was successfully generated | ||||
| @ -419,7 +421,7 @@ | ||||
|             description: Invalid request | ||||
|           500: | ||||
|             description: Server internal error | ||||
|     /cdp/{owner}/{denom}/deposits: | ||||
|     /cdp/{owner}/{collateral-type}/deposits: | ||||
|       post: | ||||
|         summary: Create a deposit to cdp transaction | ||||
|         tags: | ||||
| @ -436,11 +438,11 @@ | ||||
|             type: string | ||||
|             x-example: kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c | ||||
|           - in: path | ||||
|             name: denom | ||||
|             description: Collateral denom | ||||
|             name: collateral_type | ||||
|             description: Collateral type | ||||
|             required: true | ||||
|             type: string | ||||
|             x-example: xrp | ||||
|             x-example: xrp-a | ||||
|           - description: deposit cdp post parameters | ||||
|             name: post_deposit_req | ||||
|             in: body | ||||
| @ -456,6 +458,8 @@ | ||||
|                   $ref: '#/definitions/Address' | ||||
|                 collateral: | ||||
|                   $ref: '#/definitions/CoinCollateral' | ||||
|                 collateral_type: | ||||
|                   $ref: '#/definitions/CollateralType' | ||||
|         responses: | ||||
|           200: | ||||
|             description: Tx was successfully generated | ||||
| @ -465,7 +469,7 @@ | ||||
|             description: Invalid request | ||||
|           500: | ||||
|             description: Server internal error | ||||
|     /cdp/{owner}/{denom}/withdraw: | ||||
|     /cdp/{owner}/{collateral-type}/withdraw: | ||||
|       post: | ||||
|         summary: create a withdraw collateral transaction | ||||
|         tags: | ||||
| @ -482,11 +486,11 @@ | ||||
|             type: string | ||||
|             x-example: kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c | ||||
|           - in: path | ||||
|             name: denom | ||||
|             description: Collateral denom | ||||
|             name: collateral_type | ||||
|             description: Collateral type | ||||
|             required: true | ||||
|             type: string | ||||
|             x-example: xrp | ||||
|             x-example: xrp-a | ||||
|           - description: withdraw cdp post parameters | ||||
|             name: post_withdraw_req | ||||
|             in: body | ||||
| @ -502,6 +506,8 @@ | ||||
|                   $ref: '#/definitions/Address' | ||||
|                 collateral: | ||||
|                   $ref: '#/definitions/CoinCollateral' | ||||
|                 collateral_type: | ||||
|                   $ref: '#/definitions/CollateralType' | ||||
|         responses: | ||||
|           200: | ||||
|             description: Tx was successfully generated | ||||
| @ -511,7 +517,7 @@ | ||||
|             description: Invalid request | ||||
|           500: | ||||
|             description: Server internal error | ||||
|     /cdp/{owner}/{denom}/draw: | ||||
|     /cdp/{owner}/{collateral-type}/draw: | ||||
|       post: | ||||
|         summary: Create a draw debt transaction | ||||
|         tags: | ||||
| @ -528,11 +534,11 @@ | ||||
|             type: string | ||||
|             x-example: kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c | ||||
|           - in: path | ||||
|             name: denom | ||||
|             description: Collateral denom | ||||
|             name: collateral_type | ||||
|             description: Collateral type | ||||
|             required: true | ||||
|             type: string | ||||
|             x-example: xrp | ||||
|             x-example: xrp-a | ||||
|           - description: draw cdp post parameters | ||||
|             name: post_draw_req | ||||
|             in: body | ||||
| @ -546,6 +552,8 @@ | ||||
|                   $ref: '#/definitions/Address' | ||||
|                 principal: | ||||
|                   $ref: '#/definitions/CoinPrincipal' | ||||
|                 collateral_type: | ||||
|                   $ref: '#/definitions/CollateralType' | ||||
|         responses: | ||||
|           200: | ||||
|             description: Tx was successfully generated | ||||
| @ -555,7 +563,7 @@ | ||||
|             description: Invalid request | ||||
|           500: | ||||
|             description: Server internal error | ||||
|     /cdp/{owner}/{denom}/repay: | ||||
|     /cdp/{owner}/{collateral-type}/repay: | ||||
|       post: | ||||
|         summary: Repay debt from a CDP | ||||
|         tags: | ||||
| @ -571,12 +579,12 @@ | ||||
|             required: true | ||||
|             type: string | ||||
|             x-example: kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c | ||||
|           - in: path | ||||
|             name: denom | ||||
|             description: Collateral denom | ||||
|         - in: path | ||||
|             name: collateral_type | ||||
|             description: Collateral type | ||||
|             required: true | ||||
|             type: string | ||||
|             x-example: xrp | ||||
|             x-example: xrp-a | ||||
|           - description: repay cdp post parameters | ||||
|             name: post_repay_req | ||||
|             in: body | ||||
| @ -590,6 +598,8 @@ | ||||
|                   $ref: '#/definitions/Address' | ||||
|                 payment: | ||||
|                   $ref: '#/definitions/CoinPrincipal' | ||||
|                 collateral_type: | ||||
|                   $ref: '#/definitions/CollateralType' | ||||
|         responses: | ||||
|           200: | ||||
|             description: Tx was successfully generated | ||||
| @ -640,7 +650,6 @@ | ||||
|                         type: number | ||||
|           500: | ||||
|             description: Server internal error | ||||
| 
 | ||||
|     /cdp/parameters: | ||||
|       get: | ||||
|         summary: Get the parameters of the cdp module | ||||
| @ -669,17 +678,26 @@ | ||||
|                 surplus_auction_threshold: | ||||
|                   type: string | ||||
|                   example: '1000000000' | ||||
|                 surplus_auction_lot: | ||||
|                   type: string | ||||
|                   example: '10000000' | ||||
|                 debt_auction_threshold: | ||||
|                   type: string | ||||
|                   example: '1000000000' | ||||
|                 surplus_auction_lot: | ||||
|                   type: string | ||||
|                   example: '10000000' | ||||
|                 savings_distribution_frequency: | ||||
|                   type: string | ||||
|                   example: '60000000' | ||||
|                 circuit_breaker: | ||||
|                   type: boolean | ||||
|                   example: false | ||||
|           500: | ||||
|             description: Server internal error | ||||
|     /cdp/cdps/cdp/{owner}/{denom}: | ||||
|     /cdp/cdps/cdp/{owner}/{collateral-type}: | ||||
|       get: | ||||
|         summary: Get the cdp associated with the input owner address and collateral denom | ||||
|         summary: Get the cdp associated with the input owner address and collateral type | ||||
|         tags: | ||||
|           - CDP | ||||
|         produces: | ||||
| @ -692,11 +710,11 @@ | ||||
|             type: string | ||||
|             x-example: kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c | ||||
|           - in: path | ||||
|             name: denom | ||||
|             description: Collateral denom | ||||
|             name: type | ||||
|             description: Collateral type | ||||
|             required: true | ||||
|             type: string | ||||
|             x-example: xrp | ||||
|             x-example: xrp-a | ||||
|         responses: | ||||
|           200: | ||||
|             description: CDP associated with owner | ||||
| @ -704,23 +722,23 @@ | ||||
|               $ref: '#/definitions/CdpResponse' | ||||
|           500: | ||||
|             description: Server internal error | ||||
|     /cdp/cdps/denom/{denom}: | ||||
|     /cdp/cdps/denom/{collateral-type}: | ||||
|       get: | ||||
|         summary: Get all cdps with collateral type equal to the input collateral denom | ||||
|         summary: Get all cdps with collateral type equal to the input collateral type | ||||
|         tags: | ||||
|           - CDP | ||||
|         produces: | ||||
|           - application/json | ||||
|         parameters: | ||||
|           - in: path | ||||
|             name: denom | ||||
|             description: Collateral denom | ||||
|             name: type | ||||
|             description: Collateral Type | ||||
|             required: true | ||||
|             type: string | ||||
|             x-example: xrp | ||||
|             x-example: xrp-a | ||||
|         responses: | ||||
|           200: | ||||
|             description: All CDPs with the input collateral denom | ||||
|             description: All CDPs with the input collateral type | ||||
|             schema: | ||||
|               type: object | ||||
|               properties: | ||||
| @ -734,20 +752,20 @@ | ||||
|                     $ref: '#/definitions/CdpResponse' | ||||
|           500: | ||||
|             description: Server internal error | ||||
|     /cdp/cdps/ratio/{denom}/{ratio}: | ||||
|     /cdp/cdps/ratio/{collateral-type}/{ratio}: | ||||
|       get: | ||||
|         summary: Get all cdps with collateral type equal to the input collateral denom and collateralization ratio strictly less than the input ratio | ||||
|         summary: Get all cdps with collateral type equal to the input collateral type and collateralization ratio strictly less than the input ratio | ||||
|         tags: | ||||
|           - CDP | ||||
|         produces: | ||||
|           - application/json | ||||
|         parameters: | ||||
|           - in: path | ||||
|             name: denom | ||||
|             description: Collateral denom | ||||
|             name: type | ||||
|             description: Collateral type | ||||
|             required: true | ||||
|             type: string | ||||
|             x-example: xrp | ||||
|             x-example: xrp-a | ||||
|           - in: path | ||||
|             name: ratio | ||||
|             description: Collateralization ratio | ||||
| @ -756,7 +774,7 @@ | ||||
|             x-example: "2.0" | ||||
|         responses: | ||||
|           200: | ||||
|             description: All CDPs with the input collateral denom and collateralization ratio less than the input ratio | ||||
|             description: All CDPs with the input collateral type and collateralization ratio less than the input ratio | ||||
|             schema: | ||||
|               type: object | ||||
|               properties: | ||||
| @ -770,9 +788,9 @@ | ||||
|                     $ref: '#/definitions/CdpResponse' | ||||
|           500: | ||||
|             description: Server internal error | ||||
|     /cdp/cdps/cdp/deposits/{owner}/{denom}: | ||||
|     /cdp/cdps/cdp/deposits/{owner}/{collateral-type}: | ||||
|       get: | ||||
|         summary: Get the deposits associated with the cdp owned by the input owner address and with collateral type equal to the input collateral denom | ||||
|         summary: Get the deposits associated with the cdp owned by an address for a collateral type | ||||
|         tags: | ||||
|           - CDP | ||||
|         produces: | ||||
| @ -785,11 +803,11 @@ | ||||
|             type: string | ||||
|             x-example: kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c | ||||
|           - in: path | ||||
|             name: denom | ||||
|             description: Collateral denom | ||||
|             name: type | ||||
|             description: Collateral type | ||||
|             required: true | ||||
|             type: string | ||||
|             x-example: xrp | ||||
|             x-example: xrp-a | ||||
|         responses: | ||||
|           200: | ||||
|             description: Deposits associated with the cdp | ||||
| @ -805,6 +823,73 @@ | ||||
|                     $ref: '#/definitions/CdpDepositResponse' | ||||
|           500: | ||||
|             description: Server internal error | ||||
|     /cdp/savings-rate-dist: | ||||
|       get: | ||||
|         summary: Get the total savings rate distributed by the cdp module | ||||
|         tags: | ||||
|           - CDP | ||||
|         produces: | ||||
|           - application/json | ||||
|         responses: | ||||
|           200: | ||||
|             description: The distributed savings rate | ||||
|               properties: | ||||
|                 height: | ||||
|                   type: string | ||||
|                   example: "100" | ||||
|                 result: | ||||
|                   savings_rate_distributed: | ||||
|                     type: string | ||||
|                     example: "5000000000000" | ||||
|           500: | ||||
|             description: Server internal error | ||||
|     /cdp/cdps: | ||||
|       get: | ||||
|         summary: Query all active cdps | ||||
|         tags: | ||||
|           - CDP | ||||
|         produces: | ||||
|           - application/json | ||||
|       parameters: | ||||
|           - in: query | ||||
|             name: owner | ||||
|             description: Owner address in bech32 format | ||||
|             required: false | ||||
|             type: string | ||||
|             x-example: kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c | ||||
|           - in: query | ||||
|             name: collateral-type | ||||
|             description: Collateral type | ||||
|             required: false | ||||
|             type: string | ||||
|             x-example: xrp-a | ||||
|           - in: query | ||||
|             name: id | ||||
|             description: CDP ID | ||||
|             required: false | ||||
|             type: string | ||||
|             x-example: "4" | ||||
|           - in: query | ||||
|             name: ratio | ||||
|             description: Collateralization ratio | ||||
|             required: false | ||||
|             type: string | ||||
|             x-example: "2.75" | ||||
|         responses: | ||||
|           200: | ||||
|             description: Query cdps | ||||
|             schema: | ||||
|               type: object | ||||
|               properties: | ||||
|                 height: | ||||
|                   type: string | ||||
|                   example: "100" | ||||
|                 result: | ||||
|                   type: array | ||||
|                   items: | ||||
|                     $ref: '#/definitions/CdpResponse' | ||||
|           500: | ||||
|             description: Internal Server Error | ||||
|     /bep3/swap/create: | ||||
|       post: | ||||
|         summary: Generate a create atomic swap transaction | ||||
| @ -1273,7 +1358,7 @@ | ||||
|                   $ref: '#/definitions/BaseReq' | ||||
|                 owner: | ||||
|                   $ref: '#/definitions/Address' | ||||
|                 denom: | ||||
|                 type: | ||||
|                   type: string | ||||
|                   example: bnb | ||||
|         responses: | ||||
| @ -1285,9 +1370,9 @@ | ||||
|             description: Invalid request | ||||
|           500: | ||||
|             description: Internal server error | ||||
|     /incentive/claims/{owner}/{denom}: | ||||
|     /incentive/claims/{owner}/{collateral-type}: | ||||
|       get: | ||||
|         summary: Get outstanding claims for the input owner and denom | ||||
|         summary: Get outstanding claims for the input owner and collateral type | ||||
|         tags: | ||||
|           - Incentive | ||||
|         produces: | ||||
| @ -1299,11 +1384,11 @@ | ||||
|             type: string | ||||
|             x-example: kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c | ||||
|           - in: path | ||||
|             name: denom | ||||
|             description: Collateral denom | ||||
|             name: type | ||||
|             description: Collateral type | ||||
|             required: true | ||||
|             type: string | ||||
|             x-example: bnb | ||||
|             x-example: bnb-a | ||||
|         responses: | ||||
|           200: | ||||
|             description: USDX Incentive Claims | ||||
| @ -3457,6 +3542,9 @@ | ||||
|         amount: | ||||
|           type: string | ||||
|           example: '555555' | ||||
|     CollateralType: | ||||
|       type: string | ||||
|       example: xrp-a | ||||
|     CoinCollateral: | ||||
|       type: object | ||||
|       properties: | ||||
| @ -3904,6 +3992,9 @@ | ||||
|           example: kava1q53rwutgpzx7szcrgzqguxyccjpzt9j4cyctn9 | ||||
|         collateral: | ||||
|           $ref: '#/definitions/CoinCollateral' | ||||
|         type: | ||||
|           type: string | ||||
|           example: "xrp-a" | ||||
|         principal: | ||||
|           $ref: '#/definitions/CoinPrincipal' | ||||
|         accumulated_fees: | ||||
| @ -3987,6 +4078,15 @@ | ||||
|         bid_duration: | ||||
|           type: string | ||||
|           example: 600000000000 | ||||
|         increment_surplus: | ||||
|           type: string | ||||
|           example: "0.05" | ||||
|         increment_debt: | ||||
|           type: string | ||||
|           example: "0.05" | ||||
|         increment_collateral: | ||||
|           type: string | ||||
|           example: "0.05" | ||||
|     AuctionResponse: | ||||
|       type: object | ||||
|       properties: | ||||
|  | ||||
| @ -22,6 +22,7 @@ const ( | ||||
| 	flagType  = "type" | ||||
| 	flagDenom = "denom" | ||||
| 	flagPhase = "phase" | ||||
| 	flagOwner = "owner" | ||||
| ) | ||||
| 
 | ||||
| // GetQueryCmd returns the cli query commands for this module
 | ||||
| @ -77,6 +78,7 @@ func QueryGetAuctionsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { | ||||
| 		Long: strings.TrimSpace(`Query for all paginated auctions that match optional filters: | ||||
| Example: | ||||
| $ kvcli q auction auctions --type=(collateral|surplus|debt) | ||||
| $ kvcli q auction auctions --owner=kava1hatdq32u5x4wnxrtv5wzjzmq49sxgjgsj0mffm | ||||
| $ kvcli q auction auctions --denom=bnb | ||||
| $ kvcli q auction auctions --phase=(forward|reverse) | ||||
| $ kvcli q auction auctions --page=2 --limit=100 | ||||
| @ -84,6 +86,7 @@ $ kvcli q auction auctions --page=2 --limit=100 | ||||
| 		), | ||||
| 		RunE: func(cmd *cobra.Command, args []string) error { | ||||
| 			strType := viper.GetString(flagType) | ||||
| 			strOwner := viper.GetString(flagOwner) | ||||
| 			strDenom := viper.GetString(flagDenom) | ||||
| 			strPhase := viper.GetString(flagPhase) | ||||
| 			page := viper.GetInt(flags.FlagPage) | ||||
| @ -91,11 +94,12 @@ $ kvcli q auction auctions --page=2 --limit=100 | ||||
| 
 | ||||
| 			var ( | ||||
| 				auctionType  string | ||||
| 				auctionOwner sdk.AccAddress | ||||
| 				auctionDenom string | ||||
| 				auctionPhase string | ||||
| 			) | ||||
| 
 | ||||
| 			params := types.NewQueryAllAuctionParams(page, limit, auctionType, auctionDenom, auctionPhase) | ||||
| 			params := types.NewQueryAllAuctionParams(page, limit, auctionType, auctionDenom, auctionPhase, auctionOwner) | ||||
| 
 | ||||
| 			if len(strType) != 0 { | ||||
| 				auctionType = strings.ToLower(strings.TrimSpace(strType)) | ||||
| @ -107,6 +111,18 @@ $ kvcli q auction auctions --page=2 --limit=100 | ||||
| 				params.Type = auctionType | ||||
| 			} | ||||
| 
 | ||||
| 			if len(auctionOwner) != 0 { | ||||
| 				if auctionType != types.CollateralAuctionType { | ||||
| 					return fmt.Errorf("cannot apply owner flag to non-collateral auction type") | ||||
| 				} | ||||
| 				auctionOwnerStr := strings.ToLower(strings.TrimSpace(strOwner)) | ||||
| 				auctionOwner, err := sdk.AccAddressFromBech32(auctionOwnerStr) | ||||
| 				if err != nil { | ||||
| 					return fmt.Errorf("cannot parse address from auction owner %s", auctionOwnerStr) | ||||
| 				} | ||||
| 				params.Owner = auctionOwner | ||||
| 			} | ||||
| 
 | ||||
| 			if len(strDenom) != 0 { | ||||
| 				auctionDenom := strings.TrimSpace(strDenom) | ||||
| 				err := sdk.ValidateDenom(auctionDenom) | ||||
| @ -160,6 +176,7 @@ $ kvcli q auction auctions --page=2 --limit=100 | ||||
| 	cmd.Flags().Int(flags.FlagPage, 1, "pagination page of auctions to to query for") | ||||
| 	cmd.Flags().Int(flags.FlagLimit, 100, "pagination limit of auctions to query for") | ||||
| 	cmd.Flags().String(flagType, "", "(optional) filter by auction type, type: collateral, debt, surplus") | ||||
| 	cmd.Flags().String(flagOwner, "", "(optional) filter by collateral auction owner") | ||||
| 	cmd.Flags().String(flagDenom, "", "(optional) filter by auction denom") | ||||
| 	cmd.Flags().String(flagPhase, "", "(optional) filter by collateral auction phase, phase: forward/reverse") | ||||
| 
 | ||||
|  | ||||
| @ -69,6 +69,7 @@ func queryAuctionsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||||
| 		} | ||||
| 
 | ||||
| 		var auctionType string | ||||
| 		var auctionOwner sdk.AccAddress | ||||
| 		var auctionDenom string | ||||
| 		var auctionPhase string | ||||
| 
 | ||||
| @ -82,6 +83,17 @@ func queryAuctionsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if x := r.URL.Query().Get(RestOwner); len(x) != 0 { | ||||
| 			if auctionType != types.CollateralAuctionType { | ||||
| 				rest.WriteErrorResponse(w, http.StatusBadRequest, "cannot apply owner flag to non-collateral auction type") | ||||
| 			} | ||||
| 			auctionOwnerStr := strings.ToLower(strings.TrimSpace(x)) | ||||
| 			auctionOwner, err = sdk.AccAddressFromBech32(auctionOwnerStr) | ||||
| 			if err != nil { | ||||
| 				rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("cannot parse address from auction owner %s", auctionOwnerStr)) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if x := r.URL.Query().Get(RestDenom); len(x) != 0 { | ||||
| 			auctionDenom = strings.TrimSpace(x) | ||||
| 			err := sdk.ValidateDenom(auctionDenom) | ||||
| @ -103,7 +115,7 @@ func queryAuctionsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		params := types.NewQueryAllAuctionParams(page, limit, auctionType, auctionDenom, auctionPhase) | ||||
| 		params := types.NewQueryAllAuctionParams(page, limit, auctionType, auctionDenom, auctionPhase, auctionOwner) | ||||
| 		bz, err := cliCtx.Codec.MarshalJSON(params) | ||||
| 		if err != nil { | ||||
| 			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) | ||||
|  | ||||
| @ -12,6 +12,7 @@ import ( | ||||
| // nolint
 | ||||
| const ( | ||||
| 	RestType  = "type" | ||||
| 	RestOwner = "owner" | ||||
| 	RestDenom = "denom" | ||||
| 	RestPhase = "phase" | ||||
| ) | ||||
|  | ||||
| @ -1,12 +1,11 @@ | ||||
| package keeper | ||||
| 
 | ||||
| import ( | ||||
| 	abci "github.com/tendermint/tendermint/abci/types" | ||||
| 
 | ||||
| 	"github.com/cosmos/cosmos-sdk/client" | ||||
| 	"github.com/cosmos/cosmos-sdk/codec" | ||||
| 	sdk "github.com/cosmos/cosmos-sdk/types" | ||||
| 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" | ||||
| 	abci "github.com/tendermint/tendermint/abci/types" | ||||
| 
 | ||||
| 	"github.com/kava-labs/kava/x/auction/types" | ||||
| ) | ||||
| @ -93,13 +92,29 @@ func filterAuctions(ctx sdk.Context, auctions types.Auctions, params types.Query | ||||
| 	filteredAuctions := make(types.Auctions, 0, len(auctions)) | ||||
| 
 | ||||
| 	for _, auc := range auctions { | ||||
| 		matchType, matchDenom, matchPhase := true, true, true | ||||
| 		matchType, matchOwner, matchDenom, matchPhase := true, true, true, true | ||||
| 
 | ||||
| 		// match auction type (if supplied)
 | ||||
| 		if len(params.Type) > 0 { | ||||
| 			matchType = auc.GetType() == params.Type | ||||
| 		} | ||||
| 
 | ||||
| 		// match auction owner (if supplied)
 | ||||
| 		if len(params.Owner) > 0 { | ||||
| 			if cAuc, ok := auc.(types.CollateralAuction); ok { | ||||
| 				foundOwnerAddr := false | ||||
| 				for _, addr := range cAuc.GetLotReturns().Addresses { | ||||
| 					if addr.Equals(params.Owner) { | ||||
| 						foundOwnerAddr = true | ||||
| 						break | ||||
| 					} | ||||
| 				} | ||||
| 				if !foundOwnerAddr { | ||||
| 					matchOwner = false | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// match auction denom (if supplied)
 | ||||
| 		if len(params.Denom) > 0 { | ||||
| 			matchDenom = auc.GetBid().Denom == params.Denom || auc.GetLot().Denom == params.Denom | ||||
| @ -110,7 +125,7 @@ func filterAuctions(ctx sdk.Context, auctions types.Auctions, params types.Query | ||||
| 			matchPhase = auc.GetPhase() == params.Phase | ||||
| 		} | ||||
| 
 | ||||
| 		if matchType && matchDenom && matchPhase { | ||||
| 		if matchType && matchOwner && matchDenom && matchPhase { | ||||
| 			filteredAuctions = append(filteredAuctions, auc) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @ -41,7 +41,7 @@ func (suite *QuerierTestSuite) SetupTest() { | ||||
| 	tApp := app.NewTestApp() | ||||
| 	ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()}) | ||||
| 
 | ||||
| 	_, addrs := app.GeneratePrivKeyAddressPairs(1) | ||||
| 	_, addrs := app.GeneratePrivKeyAddressPairs(10) | ||||
| 	buyer := addrs[0] | ||||
| 	modName := cdp.LiquidatorMacc | ||||
| 
 | ||||
| @ -64,8 +64,16 @@ func (suite *QuerierTestSuite) SetupTest() { | ||||
| 	// Populate with auctions
 | ||||
| 	randSrc := rand.New(rand.NewSource(int64(1234))) | ||||
| 	for j := 0; j < TestAuctionCount; j++ { | ||||
| 		lotAmount := simulation.RandIntBetween(randSrc, 10, 100) | ||||
| 		id, err := suite.keeper.StartSurplusAuction(suite.ctx, modName, c("token1", int64(lotAmount)), "token2") | ||||
| 		var id uint64 | ||||
| 		var err error | ||||
| 		lotAmount := int64(simulation.RandIntBetween(randSrc, 10, 100)) | ||||
| 		ownerAddrIndex := simulation.RandIntBetween(randSrc, 1, 9) | ||||
| 		if ownerAddrIndex%2 == 0 { | ||||
| 			id, err = suite.keeper.StartSurplusAuction(suite.ctx, modName, c("token1", lotAmount), "token2") | ||||
| 		} else { | ||||
| 			id, err = suite.keeper.StartCollateralAuction(suite.ctx, modName, c("token1", lotAmount), c("usdx", int64(20)), | ||||
| 				[]sdk.AccAddress{addrs[ownerAddrIndex]}, []sdk.Int{sdk.NewInt(lotAmount)}, c("debt", int64(10))) | ||||
| 		} | ||||
| 		suite.NoError(err) | ||||
| 
 | ||||
| 		auc, found := suite.keeper.GetAuction(suite.ctx, id) | ||||
| @ -108,7 +116,7 @@ func (suite *QuerierTestSuite) TestQueryAuctions() { | ||||
| 	query := abci.RequestQuery{ | ||||
| 		Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetAuctions}, "/"), | ||||
| 		Data: types.ModuleCdc.MustMarshalJSON( | ||||
| 			types.NewQueryAllAuctionParams(int(1), int(TestAuctionCount), "", "", ""), | ||||
| 			types.NewQueryAllAuctionParams(int(1), int(TestAuctionCount), "", "", "", nil), | ||||
| 		), | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -244,6 +244,11 @@ func (a CollateralAuction) GetPhase() string { | ||||
| 	return ForwardAuctionPhase | ||||
| } | ||||
| 
 | ||||
| // GetLotReturns returns a collateral auction's lot owners
 | ||||
| func (a CollateralAuction) GetLotReturns() WeightedAddresses { | ||||
| 	return a.LotReturns | ||||
| } | ||||
| 
 | ||||
| // Validate validates the CollateralAuction fields values.
 | ||||
| func (a CollateralAuction) Validate() error { | ||||
| 	if !a.CorrespondingDebt.IsValid() { | ||||
|  | ||||
| @ -1,5 +1,9 @@ | ||||
| package types | ||||
| 
 | ||||
| import ( | ||||
| 	sdk "github.com/cosmos/cosmos-sdk/types" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	// QueryGetAuction is the query path for querying one auction
 | ||||
| 	QueryGetAuction = "auction" | ||||
| @ -25,19 +29,21 @@ func NewQueryAuctionParams(id uint64) QueryAuctionParams { | ||||
| 
 | ||||
| // QueryAllAuctionParams is the params for an auctions query
 | ||||
| type QueryAllAuctionParams struct { | ||||
| 	Page  int    `json:"page" yaml:"page"` | ||||
| 	Limit int    `json:"limit" yaml:"limit"` | ||||
| 	Type  string `json:"type" yaml:"type"` | ||||
| 	Denom string `json:"denom" yaml:"denom"` | ||||
| 	Phase string `json:"phase" yaml:"phase"` | ||||
| 	Page  int            `json:"page" yaml:"page"` | ||||
| 	Limit int            `json:"limit" yaml:"limit"` | ||||
| 	Type  string         `json:"type" yaml:"type"` | ||||
| 	Owner sdk.AccAddress `json:"owner" yaml:"owner"` | ||||
| 	Denom string         `json:"denom" yaml:"denom"` | ||||
| 	Phase string         `json:"phase" yaml:"phase"` | ||||
| } | ||||
| 
 | ||||
| // NewQueryAllAuctionParams creates a new QueryAllAuctionParams
 | ||||
| func NewQueryAllAuctionParams(page, limit int, aucType, aucDenom, aucPhase string) QueryAllAuctionParams { | ||||
| func NewQueryAllAuctionParams(page, limit int, aucType, aucDenom, aucPhase string, aucOwner sdk.AccAddress) QueryAllAuctionParams { | ||||
| 	return QueryAllAuctionParams{ | ||||
| 		Page:  page, | ||||
| 		Limit: limit, | ||||
| 		Type:  aucType, | ||||
| 		Owner: aucOwner, | ||||
| 		Denom: aucDenom, | ||||
| 		Phase: aucPhase, | ||||
| 	} | ||||
|  | ||||
| @ -137,7 +137,9 @@ var ( | ||||
| 	DefaultDebtLot                      = types.DefaultDebtLot | ||||
| 	DefaultPreviousDistributionTime     = types.DefaultPreviousDistributionTime | ||||
| 	DefaultSavingsDistributionFrequency = types.DefaultSavingsDistributionFrequency | ||||
| 	DefaultSavingsRateDistributed       = types.DefaultSavingsRateDistributed | ||||
| 	MaxSortableDec                      = types.MaxSortableDec | ||||
| 	SavingsRateDistributedKey           = types.SavingsRateDistributedKey | ||||
| ) | ||||
| 
 | ||||
| type ( | ||||
|  | ||||
| @ -2,9 +2,11 @@ package cli | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/spf13/cobra" | ||||
| 	"github.com/spf13/viper" | ||||
| 
 | ||||
| 	"github.com/cosmos/cosmos-sdk/client/context" | ||||
| 	"github.com/cosmos/cosmos-sdk/client/flags" | ||||
| @ -16,6 +18,14 @@ import ( | ||||
| 	"github.com/kava-labs/kava/x/cdp/types" | ||||
| ) | ||||
| 
 | ||||
| // Query CDP flags
 | ||||
| const ( | ||||
| 	flagCollateralType = "collateral-type" | ||||
| 	flagOwner          = "owner" | ||||
| 	flagID             = "id" | ||||
| 	flagRatio          = "ratio" // returns CDPs under the given collateralization ratio threshold
 | ||||
| ) | ||||
| 
 | ||||
| // GetQueryCmd returns the cli query commands for this module
 | ||||
| func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { | ||||
| 	// Group nameservice queries under a subcommand
 | ||||
| @ -26,11 +36,11 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { | ||||
| 
 | ||||
| 	cdpQueryCmd.AddCommand(flags.GetCommands( | ||||
| 		QueryCdpCmd(queryRoute, cdc), | ||||
| 		QueryCdpsByCollateralTypeCmd(queryRoute, cdc), | ||||
| 		QueryCdpsByCollateralTypeAndRatioCmd(queryRoute, cdc), | ||||
| 		QueryGetCdpsCmd(queryRoute, cdc), | ||||
| 		QueryCdpDepositsCmd(queryRoute, cdc), | ||||
| 		QueryParamsCmd(queryRoute, cdc), | ||||
| 		QueryGetAccounts(queryRoute, cdc), | ||||
| 		QueryGetSavingsRateDistributed(queryRoute, cdc), | ||||
| 	)...) | ||||
| 
 | ||||
| 	return cdpQueryCmd | ||||
| @ -79,85 +89,103 @@ $ %s query %s cdp kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw atom-a | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // QueryCdpsByCollateralTypeCmd returns the command handler for querying cdps for a collateral type
 | ||||
| func QueryCdpsByCollateralTypeCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { | ||||
| 	return &cobra.Command{ | ||||
| 		Use:   "cdps [collateral-type]", | ||||
| 		Short: "query CDPs by collateral", | ||||
| 		Long: strings.TrimSpace( | ||||
| 			fmt.Sprintf(`List all CDPs collateralized with the specified asset. | ||||
| 
 | ||||
| // QueryGetCdpsCmd queries the cdps in the store
 | ||||
| func QueryGetCdpsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use:   "cdps", | ||||
| 		Short: "query cdps with optional filters", | ||||
| 		Long: strings.TrimSpace(`Query for all paginated cdps that match optional filters: | ||||
| Example: | ||||
| $ %s query %s cdps atom-a | ||||
| `, version.ClientName, types.ModuleName)), | ||||
| 		Args: cobra.ExactArgs(1), | ||||
| $ kvcli q cdp cdps --collateral-type=bnb | ||||
| $ kvcli q cdp cdps --owner=kava1hatdq32u5x4wnxrtv5wzjzmq49sxgjgsj0mffm | ||||
| $ kvcli q cdp cdps --id=21 | ||||
| $ kvcli q cdp cdps --ratio=2.75 | ||||
| $ kvcli q cdp cdps --page=2 --limit=100 | ||||
| `, | ||||
| 		), | ||||
| 		RunE: func(cmd *cobra.Command, args []string) error { | ||||
| 			cliCtx := context.NewCLIContext().WithCodec(cdc) | ||||
| 			strCollateralType := viper.GetString(flagCollateralType) | ||||
| 			strOwner := viper.GetString(flagOwner) | ||||
| 			strID := viper.GetString(flagID) | ||||
| 			strRatio := viper.GetString(flagRatio) | ||||
| 			page := viper.GetInt(flags.FlagPage) | ||||
| 			limit := viper.GetInt(flags.FlagLimit) | ||||
| 
 | ||||
| 			// Prepare params for querier
 | ||||
| 			bz, err := cdc.MarshalJSON(types.QueryCdpsParams{CollateralType: args[0]}) | ||||
| 			var ( | ||||
| 				cdpCollateralType string | ||||
| 				cdpOwner          sdk.AccAddress | ||||
| 				cdpID             uint64 | ||||
| 				cdpRatio          sdk.Dec | ||||
| 			) | ||||
| 
 | ||||
| 			params := types.NewQueryCdpsParams(page, limit, cdpCollateralType, cdpOwner, cdpID, cdpRatio) | ||||
| 
 | ||||
| 			if len(strCollateralType) != 0 { | ||||
| 				cdpCollateralType = strings.ToLower(strings.TrimSpace(strCollateralType)) | ||||
| 				params.CollateralType = cdpCollateralType | ||||
| 			} | ||||
| 
 | ||||
| 			if len(strOwner) != 0 { | ||||
| 				cdpOwner, err := sdk.AccAddressFromBech32(strings.ToLower(strings.TrimSpace(strOwner))) | ||||
| 				if err != nil { | ||||
| 					return fmt.Errorf("cannot parse address from cdp owner %s", strOwner) | ||||
| 				} | ||||
| 				params.Owner = cdpOwner | ||||
| 			} | ||||
| 
 | ||||
| 			if len(strID) != 0 { | ||||
| 				cdpID, err := strconv.ParseUint(strID, 10, 64) | ||||
| 				if err != nil { | ||||
| 					return fmt.Errorf("cannot parse cdp ID %s", strID) | ||||
| 				} | ||||
| 				params.ID = cdpID | ||||
| 			} | ||||
| 
 | ||||
| 			params.Ratio = sdk.ZeroDec() | ||||
| 			if len(strRatio) != 0 { | ||||
| 				cdpRatio, err := sdk.NewDecFromStr(strRatio) | ||||
| 				if err != nil { | ||||
| 					return fmt.Errorf("cannot parse cdp ratio %s", strRatio) | ||||
| 				} | ||||
| 				params.Ratio = cdpRatio | ||||
| 			} else { | ||||
| 				// Set to sdk.Dec(0) so that if not specified in params it doesn't panic when unmarshaled
 | ||||
| 				params.Ratio = sdk.ZeroDec() | ||||
| 			} | ||||
| 
 | ||||
| 			bz, err := cdc.MarshalJSON(params) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 
 | ||||
| 			cliCtx := context.NewCLIContext().WithCodec(cdc) | ||||
| 
 | ||||
| 			// Query
 | ||||
| 			route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetCdps) | ||||
| 			res, _, err := cliCtx.QueryWithData(route, bz) | ||||
| 			res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetCdps), bz) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 
 | ||||
| 			// Decode and print results
 | ||||
| 			var cdps types.AugmentedCDPs | ||||
| 			cdc.MustUnmarshalJSON(res, &cdps) | ||||
| 			return cliCtx.PrintOutput(cdps) | ||||
| 			var matchingCDPs types.AugmentedCDPs | ||||
| 			cdc.MustUnmarshalJSON(res, &matchingCDPs) | ||||
| 			if len(matchingCDPs) == 0 { | ||||
| 				return fmt.Errorf("No matching CDPs found") | ||||
| 			} | ||||
| 
 | ||||
| 			cliCtx = cliCtx.WithHeight(height) | ||||
| 			return cliCtx.PrintOutput(matchingCDPs) // nolint:errcheck
 | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // QueryCdpsByCollateralTypeAndRatioCmd returns the command handler for querying cdps
 | ||||
| // that are under the specified collateral ratio
 | ||||
| func QueryCdpsByCollateralTypeAndRatioCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { | ||||
| 	return &cobra.Command{ | ||||
| 		Use:   "cdps-by-ratio [collateral-type] [collateralization-ratio]", | ||||
| 		Short: "get cdps under a collateralization ratio", | ||||
| 		Long: strings.TrimSpace( | ||||
| 			fmt.Sprintf(`List all CDPs under a specified collateralization ratio. | ||||
| Collateralization ratio is: collateral * price / debt. | ||||
| 	cmd.Flags().Int(flags.FlagPage, 1, "pagination page of CDPs to to query for") | ||||
| 	cmd.Flags().Int(flags.FlagLimit, 100, "pagination limit of CDPs to query for") | ||||
| 	cmd.Flags().String(flagCollateralType, "", "(optional) filter by CDP collateral type") | ||||
| 	cmd.Flags().String(flagOwner, "", "(optional) filter by CDP owner") | ||||
| 	cmd.Flags().String(flagID, "", "(optional) filter by CDP ID") | ||||
| 	cmd.Flags().String(flagRatio, "", "(optional) filter by CDP collateralization ratio threshold") | ||||
| 
 | ||||
| Example: | ||||
| $ %s query %s cdps-by-ratio atom-a 1.6 | ||||
| `, version.ClientName, types.ModuleName)), | ||||
| 		Args: cobra.ExactArgs(2), | ||||
| 		RunE: func(cmd *cobra.Command, args []string) error { | ||||
| 			cliCtx := context.NewCLIContext().WithCodec(cdc) | ||||
| 
 | ||||
| 			// Prepare params for querier
 | ||||
| 			ratio, err := sdk.NewDecFromStr(args[1]) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			bz, err := cdc.MarshalJSON(types.QueryCdpsByRatioParams{ | ||||
| 				CollateralType: args[0], | ||||
| 				Ratio:          ratio, | ||||
| 			}) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 
 | ||||
| 			// Query
 | ||||
| 			route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetCdpsByCollateralization) | ||||
| 			res, _, err := cliCtx.QueryWithData(route, bz) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 
 | ||||
| 			// Decode and print results
 | ||||
| 			var cdps types.AugmentedCDPs | ||||
| 			cdc.MustUnmarshalJSON(res, &cdps) | ||||
| 			return cliCtx.PrintOutput(cdps) | ||||
| 		}, | ||||
| 	} | ||||
| 	return cmd | ||||
| } | ||||
| 
 | ||||
| // QueryCdpDepositsCmd returns the command handler for querying the deposits of a particular cdp
 | ||||
| @ -208,7 +236,7 @@ func QueryParamsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { | ||||
| 	return &cobra.Command{ | ||||
| 		Use:   "params", | ||||
| 		Short: "get the cdp module parameters", | ||||
| 		Long:  "Get the current global cdp module parameters.", | ||||
| 		Long:  "get the current global cdp module parameters.", | ||||
| 		Args:  cobra.NoArgs, | ||||
| 		RunE: func(cmd *cobra.Command, args []string) error { | ||||
| 			cliCtx := context.NewCLIContext().WithCodec(cdc) | ||||
| @ -228,11 +256,12 @@ func QueryParamsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // QueryGetAccounts queries CDP module accounts
 | ||||
| func QueryGetAccounts(queryRoute string, cdc *codec.Codec) *cobra.Command { | ||||
| 	return &cobra.Command{ | ||||
| 		Use:   "accounts", | ||||
| 		Short: "Get module accounts", | ||||
| 		Long:  "Get cdp module account addresses", | ||||
| 		Short: "get module accounts", | ||||
| 		Long:  "get cdp module account addresses", | ||||
| 		Args:  cobra.NoArgs, | ||||
| 		RunE: func(cmd *cobra.Command, args []string) error { | ||||
| 			cliCtx := context.NewCLIContext().WithCodec(cdc) | ||||
| @ -253,3 +282,30 @@ func QueryGetAccounts(queryRoute string, cdc *codec.Codec) *cobra.Command { | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // QueryGetSavingsRateDistributed queries the total amount of savings rate distributed in USDX
 | ||||
| func QueryGetSavingsRateDistributed(queryRoute string, cdc *codec.Codec) *cobra.Command { | ||||
| 	return &cobra.Command{ | ||||
| 		Use:   "savings-rate-dist", | ||||
| 		Short: "get total amount of savings rate distributed in USDX", | ||||
| 		Long:  "get total amount of savings rate distributed", | ||||
| 		Args:  cobra.NoArgs, | ||||
| 		RunE: func(cmd *cobra.Command, args []string) error { | ||||
| 			cliCtx := context.NewCLIContext().WithCodec(cdc) | ||||
| 
 | ||||
| 			// Query
 | ||||
| 			res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetSavingsRateDistributed), nil) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			cliCtx = cliCtx.WithHeight(height) | ||||
| 
 | ||||
| 			// Decode and print results
 | ||||
| 			var out sdk.Int | ||||
| 			if err := cdc.UnmarshalJSON(res, &out); err != nil { | ||||
| 				return fmt.Errorf("failed to unmarshal sdk.Int: %w", err) | ||||
| 			} | ||||
| 			return cliCtx.PrintOutput(out) | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -3,6 +3,8 @@ package rest | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/cosmos/cosmos-sdk/client/context" | ||||
| 	sdk "github.com/cosmos/cosmos-sdk/types" | ||||
| @ -16,9 +18,11 @@ import ( | ||||
| func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { | ||||
| 	r.HandleFunc("/cdp/accounts", getAccountsHandlerFn(cliCtx)).Methods("GET") | ||||
| 	r.HandleFunc("/cdp/parameters", getParamsHandlerFn(cliCtx)).Methods("GET") | ||||
| 	r.HandleFunc("/cdp/savingsRateDist", getSavingsRateDistributedHandler(cliCtx)).Methods("GET") | ||||
| 	r.HandleFunc(fmt.Sprintf("/cdp/cdps/cdp/{%s}/{%s}", types.RestOwner, types.RestCollateralType), queryCdpHandlerFn(cliCtx)).Methods("GET") | ||||
| 	r.HandleFunc(fmt.Sprintf("/cdp/cdps/collateralType/{%s}", types.RestCollateralType), queryCdpsHandlerFn(cliCtx)).Methods("GET") | ||||
| 	r.HandleFunc(fmt.Sprintf("/cdp/cdps/ratio/{%s}/{%s}", types.RestCollateralType, types.RestRatio), queryCdpsByRatioHandlerFn(cliCtx)).Methods("GET") | ||||
| 	r.HandleFunc(fmt.Sprintf("/cdp/cdps"), queryCdpsHandlerFn(cliCtx)).Methods("GET") | ||||
| 	r.HandleFunc(fmt.Sprintf("/cdp/cdps/collateralType/{%s}", types.RestCollateralType), queryCdpsByCollateralTypeHandlerFn(cliCtx)).Methods("GET")     // legacy
 | ||||
| 	r.HandleFunc(fmt.Sprintf("/cdp/cdps/ratio/{%s}/{%s}", types.RestCollateralType, types.RestRatio), queryCdpsByRatioHandlerFn(cliCtx)).Methods("GET") // legacy
 | ||||
| 	r.HandleFunc(fmt.Sprintf("/cdp/cdps/cdp/deposits/{%s}/{%s}", types.RestOwner, types.RestCollateralType), queryCdpDepositsHandlerFn(cliCtx)).Methods("GET") | ||||
| } | ||||
| 
 | ||||
| @ -58,7 +62,7 @@ func queryCdpHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func queryCdpsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||||
| func queryCdpsByCollateralTypeHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||||
| 	return func(w http.ResponseWriter, r *http.Request) { | ||||
| 		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) | ||||
| 		if !ok { | ||||
| @ -68,7 +72,7 @@ func queryCdpsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||||
| 		vars := mux.Vars(r) | ||||
| 		collateralType := vars[types.RestCollateralType] | ||||
| 
 | ||||
| 		params := types.NewQueryCdpsParams(collateralType) | ||||
| 		params := types.NewQueryCdpsByCollateralTypeParams(collateralType) | ||||
| 
 | ||||
| 		bz, err := cliCtx.Codec.MarshalJSON(params) | ||||
| 		if err != nil { | ||||
| @ -76,7 +80,7 @@ func queryCdpsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/cdp/%s", types.QueryGetCdps), bz) | ||||
| 		res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/cdp/%s", types.QueryGetCdpsByCollateralType), bz) | ||||
| 		if err != nil { | ||||
| 			rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) | ||||
| 			return | ||||
| @ -193,3 +197,91 @@ func getAccountsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||||
| 		rest.PostProcessResponse(w, cliCtx, res) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func getSavingsRateDistributedHandler(cliCtx context.CLIContext) http.HandlerFunc { | ||||
| 	return func(w http.ResponseWriter, r *http.Request) { | ||||
| 		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) | ||||
| 		if !ok { | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/cdp/%s", types.QueryGetSavingsRateDistributed), nil) | ||||
| 		cliCtx = cliCtx.WithHeight(height) | ||||
| 		if err != nil { | ||||
| 			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		rest.PostProcessResponse(w, cliCtx, res) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func queryCdpsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||||
| 	return func(w http.ResponseWriter, r *http.Request) { | ||||
| 		_, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0) | ||||
| 		if err != nil { | ||||
| 			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		// Parse the query height
 | ||||
| 		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) | ||||
| 		if !ok { | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		var cdpCollateralType string | ||||
| 		var cdpOwner sdk.AccAddress | ||||
| 		var cdpID uint64 | ||||
| 		var cdpRatio sdk.Dec | ||||
| 
 | ||||
| 		if x := r.URL.Query().Get(RestCollateralType); len(x) != 0 { | ||||
| 			cdpCollateralType = strings.TrimSpace(x) | ||||
| 		} | ||||
| 
 | ||||
| 		if x := r.URL.Query().Get(RestOwner); len(x) != 0 { | ||||
| 			cdpOwnerStr := strings.ToLower(strings.TrimSpace(x)) | ||||
| 			cdpOwner, err = sdk.AccAddressFromBech32(cdpOwnerStr) | ||||
| 			if err != nil { | ||||
| 				rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("cannot parse address from cdp owner %s", cdpOwnerStr)) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if x := r.URL.Query().Get(RestID); len(x) != 0 { | ||||
| 			cdpID, err = strconv.ParseUint(strings.TrimSpace(x), 10, 64) | ||||
| 			if err != nil { | ||||
| 				rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if x := r.URL.Query().Get(RestRatio); len(x) != 0 { | ||||
| 			cdpRatio, err = sdk.NewDecFromStr(strings.TrimSpace(x)) | ||||
| 			if err != nil { | ||||
| 				rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) | ||||
| 				return | ||||
| 			} | ||||
| 		} else { | ||||
| 			// Set to sdk.Dec(0) so that if not specified in params it doesn't panic when unmarshaled
 | ||||
| 			cdpRatio = sdk.ZeroDec() | ||||
| 		} | ||||
| 
 | ||||
| 		params := types.NewQueryCdpsParams(page, limit, cdpCollateralType, cdpOwner, cdpID, cdpRatio) | ||||
| 		bz, err := cliCtx.Codec.MarshalJSON(params) | ||||
| 		if err != nil { | ||||
| 			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		route := fmt.Sprintf("custom/%s/%s", types.ModuleName, types.QueryGetCdps) | ||||
| 		res, height, err := cliCtx.QueryWithData(route, bz) | ||||
| 		cliCtx = cliCtx.WithHeight(height) | ||||
| 		if err != nil { | ||||
| 			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		rest.PostProcessResponse(w, cliCtx, res) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -8,6 +8,15 @@ import ( | ||||
| 	"github.com/cosmos/cosmos-sdk/types/rest" | ||||
| ) | ||||
| 
 | ||||
| // REST Variable names
 | ||||
| // nolint
 | ||||
| const ( | ||||
| 	RestOwner          = "owner" | ||||
| 	RestCollateralType = "collateral-type" | ||||
| 	RestID             = "id" | ||||
| 	RestRatio          = "ratio" | ||||
| ) | ||||
| 
 | ||||
| // RegisterRoutes - Central function to define routes that get registered by the main application
 | ||||
| func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { | ||||
| 	registerQueryRoutes(cliCtx, r) | ||||
|  | ||||
| @ -20,7 +20,6 @@ func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) { | ||||
| 	r.HandleFunc("/cdp/{owner}/{collateralType}/withdraw", postWithdrawHandlerFn(cliCtx)).Methods("POST") | ||||
| 	r.HandleFunc("/cdp/{owner}/{collateralType}/draw", postDrawHandlerFn(cliCtx)).Methods("POST") | ||||
| 	r.HandleFunc("/cdp/{owner}/{collateralType}/repay", postRepayHandlerFn(cliCtx)).Methods("POST") | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func postCdpHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||||
|  | ||||
| @ -88,6 +88,8 @@ func InitGenesis(ctx sdk.Context, k Keeper, pk types.PricefeedKeeper, sk types.S | ||||
| 	for _, d := range gs.Deposits { | ||||
| 		k.SetDeposit(ctx, d) | ||||
| 	} | ||||
| 
 | ||||
| 	k.SetSavingsRateDistributed(ctx, gs.SavingsRateDistributed) | ||||
| } | ||||
| 
 | ||||
| // ExportGenesis export genesis state for cdp module
 | ||||
| @ -108,11 +110,12 @@ func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState { | ||||
| 	cdpID := k.GetNextCdpID(ctx) | ||||
| 	debtDenom := k.GetDebtDenom(ctx) | ||||
| 	govDenom := k.GetGovDenom(ctx) | ||||
| 	savingsRateDist := k.GetSavingsRateDistributed(ctx) | ||||
| 
 | ||||
| 	previousDistributionTime, found := k.GetPreviousSavingsDistribution(ctx) | ||||
| 	if !found { | ||||
| 		previousDistributionTime = DefaultPreviousDistributionTime | ||||
| 	} | ||||
| 
 | ||||
| 	return NewGenesisState(params, cdps, deposits, cdpID, debtDenom, govDenom, previousDistributionTime) | ||||
| 	return NewGenesisState(params, cdps, deposits, cdpID, debtDenom, govDenom, previousDistributionTime, savingsRateDist) | ||||
| } | ||||
|  | ||||
| @ -22,13 +22,14 @@ type GenesisTestSuite struct { | ||||
| 
 | ||||
| func (suite *GenesisTestSuite) TestInvalidGenState() { | ||||
| 	type args struct { | ||||
| 		params       cdp.Params | ||||
| 		cdps         cdp.CDPs | ||||
| 		deposits     cdp.Deposits | ||||
| 		startingID   uint64 | ||||
| 		debtDenom    string | ||||
| 		govDenom     string | ||||
| 		prevDistTime time.Time | ||||
| 		params          cdp.Params | ||||
| 		cdps            cdp.CDPs | ||||
| 		deposits        cdp.Deposits | ||||
| 		startingID      uint64 | ||||
| 		debtDenom       string | ||||
| 		govDenom        string | ||||
| 		prevDistTime    time.Time | ||||
| 		savingsRateDist sdk.Int | ||||
| 	} | ||||
| 	type errArgs struct { | ||||
| 		expectPass bool | ||||
| @ -47,12 +48,13 @@ func (suite *GenesisTestSuite) TestInvalidGenState() { | ||||
| 		{ | ||||
| 			name: "empty debt denom", | ||||
| 			args: args{ | ||||
| 				params:       cdp.DefaultParams(), | ||||
| 				cdps:         cdp.CDPs{}, | ||||
| 				deposits:     cdp.Deposits{}, | ||||
| 				debtDenom:    "", | ||||
| 				govDenom:     cdp.DefaultGovDenom, | ||||
| 				prevDistTime: cdp.DefaultPreviousDistributionTime, | ||||
| 				params:          cdp.DefaultParams(), | ||||
| 				cdps:            cdp.CDPs{}, | ||||
| 				deposits:        cdp.Deposits{}, | ||||
| 				debtDenom:       "", | ||||
| 				govDenom:        cdp.DefaultGovDenom, | ||||
| 				prevDistTime:    cdp.DefaultPreviousDistributionTime, | ||||
| 				savingsRateDist: cdp.DefaultSavingsRateDistributed, | ||||
| 			}, | ||||
| 			errArgs: errArgs{ | ||||
| 				expectPass: false, | ||||
| @ -62,12 +64,13 @@ func (suite *GenesisTestSuite) TestInvalidGenState() { | ||||
| 		{ | ||||
| 			name: "empty gov denom", | ||||
| 			args: args{ | ||||
| 				params:       cdp.DefaultParams(), | ||||
| 				cdps:         cdp.CDPs{}, | ||||
| 				deposits:     cdp.Deposits{}, | ||||
| 				debtDenom:    cdp.DefaultDebtDenom, | ||||
| 				govDenom:     "", | ||||
| 				prevDistTime: cdp.DefaultPreviousDistributionTime, | ||||
| 				params:          cdp.DefaultParams(), | ||||
| 				cdps:            cdp.CDPs{}, | ||||
| 				deposits:        cdp.Deposits{}, | ||||
| 				debtDenom:       cdp.DefaultDebtDenom, | ||||
| 				govDenom:        "", | ||||
| 				prevDistTime:    cdp.DefaultPreviousDistributionTime, | ||||
| 				savingsRateDist: cdp.DefaultSavingsRateDistributed, | ||||
| 			}, | ||||
| 			errArgs: errArgs{ | ||||
| 				expectPass: false, | ||||
| @ -77,27 +80,46 @@ func (suite *GenesisTestSuite) TestInvalidGenState() { | ||||
| 		{ | ||||
| 			name: "empty distribution time", | ||||
| 			args: args{ | ||||
| 				params:       cdp.DefaultParams(), | ||||
| 				cdps:         cdp.CDPs{}, | ||||
| 				deposits:     cdp.Deposits{}, | ||||
| 				debtDenom:    cdp.DefaultDebtDenom, | ||||
| 				govDenom:     cdp.DefaultGovDenom, | ||||
| 				prevDistTime: time.Time{}, | ||||
| 				params:          cdp.DefaultParams(), | ||||
| 				cdps:            cdp.CDPs{}, | ||||
| 				deposits:        cdp.Deposits{}, | ||||
| 				debtDenom:       cdp.DefaultDebtDenom, | ||||
| 				govDenom:        cdp.DefaultGovDenom, | ||||
| 				prevDistTime:    time.Time{}, | ||||
| 				savingsRateDist: cdp.DefaultSavingsRateDistributed, | ||||
| 			}, | ||||
| 			errArgs: errArgs{ | ||||
| 				expectPass: false, | ||||
| 				contains:   "previous distribution time not set", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "negative savings rate distributed", | ||||
| 			args: args{ | ||||
| 				params:          cdp.DefaultParams(), | ||||
| 				cdps:            cdp.CDPs{}, | ||||
| 				deposits:        cdp.Deposits{}, | ||||
| 				debtDenom:       cdp.DefaultDebtDenom, | ||||
| 				govDenom:        cdp.DefaultGovDenom, | ||||
| 				prevDistTime:    cdp.DefaultPreviousDistributionTime, | ||||
| 				savingsRateDist: sdk.NewInt(-100), | ||||
| 			}, | ||||
| 			errArgs: errArgs{ | ||||
| 				expectPass: false, | ||||
| 				contains:   "savings rate distributed should not be negative", | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	for _, tc := range testCases { | ||||
| 		suite.Run(tc.name, func() { | ||||
| 			gs := cdp.NewGenesisState(tc.args.params, tc.args.cdps, tc.args.deposits, tc.args.startingID, tc.args.debtDenom, tc.args.govDenom, tc.args.prevDistTime) | ||||
| 			gs := cdp.NewGenesisState(tc.args.params, tc.args.cdps, tc.args.deposits, tc.args.startingID, | ||||
| 				tc.args.debtDenom, tc.args.govDenom, tc.args.prevDistTime, tc.args.savingsRateDist) | ||||
| 			err := gs.Validate() | ||||
| 			if tc.errArgs.expectPass { | ||||
| 				suite.Require().NoError(err) | ||||
| 			} else { | ||||
| 				suite.Require().Error(err) | ||||
| 				suite.T().Log(err) | ||||
| 				suite.Require().True(strings.Contains(err.Error(), tc.errArgs.contains)) | ||||
| 			} | ||||
| 		}) | ||||
|  | ||||
| @ -110,3 +110,23 @@ func (k Keeper) IterateCdpsByCollateralRatio(ctx sdk.Context, collateralType str | ||||
| 
 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // SetSavingsRateDistributed sets the SavingsRateDistributed in the store
 | ||||
| func (k Keeper) SetSavingsRateDistributed(ctx sdk.Context, totalDistributed sdk.Int) { | ||||
| 	store := prefix.NewStore(ctx.KVStore(k.key), types.SavingsRateDistributedKey) | ||||
| 	bz := k.cdc.MustMarshalBinaryLengthPrefixed(totalDistributed) | ||||
| 	store.Set([]byte{}, bz) | ||||
| } | ||||
| 
 | ||||
| // GetSavingsRateDistributed gets the SavingsRateDistributed from the store
 | ||||
| func (k Keeper) GetSavingsRateDistributed(ctx sdk.Context) sdk.Int { | ||||
| 	savingsRateDistributed := sdk.ZeroInt() | ||||
| 	store := prefix.NewStore(ctx.KVStore(k.key), types.SavingsRateDistributedKey) | ||||
| 	bz := store.Get([]byte{}) | ||||
| 	if bz == nil { | ||||
| 		return savingsRateDistributed | ||||
| 	} | ||||
| 
 | ||||
| 	k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &savingsRateDistributed) | ||||
| 	return savingsRateDistributed | ||||
| } | ||||
|  | ||||
							
								
								
									
										48
									
								
								x/cdp/keeper/keeper_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								x/cdp/keeper/keeper_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | ||||
| package keeper_test | ||||
| 
 | ||||
| import ( | ||||
| 	sdk "github.com/cosmos/cosmos-sdk/types" | ||||
| 	"github.com/stretchr/testify/suite" | ||||
| 	abci "github.com/tendermint/tendermint/abci/types" | ||||
| 	tmtime "github.com/tendermint/tendermint/types/time" | ||||
| 
 | ||||
| 	"github.com/kava-labs/kava/app" | ||||
| 	"github.com/kava-labs/kava/x/cdp/keeper" | ||||
| ) | ||||
| 
 | ||||
| type KeeperTestSuite struct { | ||||
| 	suite.Suite | ||||
| 
 | ||||
| 	keeper keeper.Keeper | ||||
| 	app    app.TestApp | ||||
| 	ctx    sdk.Context | ||||
| } | ||||
| 
 | ||||
| func (suite *KeeperTestSuite) SetupTest() { | ||||
| 	config := sdk.GetConfig() | ||||
| 	app.SetBech32AddressPrefixes(config) | ||||
| 	suite.ResetChain() | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (suite *KeeperTestSuite) ResetChain() { | ||||
| 	tApp := app.NewTestApp() | ||||
| 	ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()}) | ||||
| 	keeper := tApp.GetCDPKeeper() | ||||
| 
 | ||||
| 	suite.app = tApp | ||||
| 	suite.ctx = ctx | ||||
| 	suite.keeper = keeper | ||||
| } | ||||
| 
 | ||||
| func (suite *KeeperTestSuite) TestGetSetSavingsRateDistributed() { | ||||
| 	suite.ResetChain() | ||||
| 
 | ||||
| 	// Set savings rate distributed value
 | ||||
| 	savingsRateDist := sdk.NewInt(555000555000) | ||||
| 	suite.keeper.SetSavingsRateDistributed(suite.ctx, savingsRateDist) | ||||
| 
 | ||||
| 	// Check store's savings rate distributed value
 | ||||
| 	s := suite.keeper.GetSavingsRateDistributed(suite.ctx) | ||||
| 	suite.Equal(savingsRateDist, s) | ||||
| } | ||||
| @ -31,6 +31,16 @@ func (k Keeper) GetCollateral(ctx sdk.Context, collateralType string) (types.Col | ||||
| 	return types.CollateralParam{}, false | ||||
| } | ||||
| 
 | ||||
| // GetCollateralTypes returns an array of collateral types
 | ||||
| func (k Keeper) GetCollateralTypes(ctx sdk.Context) []string { | ||||
| 	params := k.GetParams(ctx) | ||||
| 	var denoms []string | ||||
| 	for _, cp := range params.CollateralParams { | ||||
| 		denoms = append(denoms, cp.Type) | ||||
| 	} | ||||
| 	return denoms | ||||
| } | ||||
| 
 | ||||
| // GetDebtParam returns the debt param with matching denom
 | ||||
| func (k Keeper) GetDebtParam(ctx sdk.Context, denom string) (types.DebtParam, bool) { | ||||
| 	dp := k.GetParams(ctx).DebtParam | ||||
|  | ||||
| @ -3,6 +3,7 @@ package keeper | ||||
| import ( | ||||
| 	abci "github.com/tendermint/tendermint/abci/types" | ||||
| 
 | ||||
| 	"github.com/cosmos/cosmos-sdk/client" | ||||
| 	"github.com/cosmos/cosmos-sdk/codec" | ||||
| 	sdk "github.com/cosmos/cosmos-sdk/types" | ||||
| 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" | ||||
| @ -18,15 +19,19 @@ func NewQuerier(keeper Keeper) sdk.Querier { | ||||
| 		case types.QueryGetCdp: | ||||
| 			return queryGetCdp(ctx, req, keeper) | ||||
| 		case types.QueryGetCdps: | ||||
| 			return queryGetCdps(ctx, req, keeper) | ||||
| 		case types.QueryGetCdpDeposits: | ||||
| 			return queryGetDeposits(ctx, req, keeper) | ||||
| 		case types.QueryGetCdpsByCollateralType: // legacy, maintained for REST API
 | ||||
| 			return queryGetCdpsByCollateralType(ctx, req, keeper) | ||||
| 		case types.QueryGetCdpsByCollateralization: | ||||
| 		case types.QueryGetCdpsByCollateralization: // legacy, maintained for REST API
 | ||||
| 			return queryGetCdpsByRatio(ctx, req, keeper) | ||||
| 		case types.QueryGetParams: | ||||
| 			return queryGetParams(ctx, req, keeper) | ||||
| 		case types.QueryGetCdpDeposits: | ||||
| 			return queryGetDeposits(ctx, req, keeper) | ||||
| 		case types.QueryGetAccounts: | ||||
| 			return queryGetAccounts(ctx, req, keeper) | ||||
| 		case types.QueryGetSavingsRateDistributed: | ||||
| 			return queryGetSavingsRateDistributed(ctx, req, keeper) | ||||
| 		default: | ||||
| 			return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint %s", types.ModuleName, path[0]) | ||||
| 		} | ||||
| @ -43,7 +48,7 @@ func queryGetCdp(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, | ||||
| 
 | ||||
| 	_, valid := keeper.GetCollateralTypePrefix(ctx, requestParams.CollateralType) | ||||
| 	if !valid { | ||||
| 		return nil, sdkerrors.Wrap(types.ErrCollateralNotSupported, requestParams.CollateralType) | ||||
| 		return nil, sdkerrors.Wrap(types.ErrInvalidCollateral, requestParams.CollateralType) | ||||
| 	} | ||||
| 
 | ||||
| 	cdp, found := keeper.GetCdpByOwnerAndCollateralType(ctx, requestParams.Owner, requestParams.CollateralType) | ||||
| @ -71,7 +76,7 @@ func queryGetDeposits(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([] | ||||
| 
 | ||||
| 	_, valid := keeper.GetCollateralTypePrefix(ctx, requestParams.CollateralType) | ||||
| 	if !valid { | ||||
| 		return nil, sdkerrors.Wrap(types.ErrCollateralNotSupported, requestParams.CollateralType) | ||||
| 		return nil, sdkerrors.Wrap(types.ErrInvalidCollateral, requestParams.CollateralType) | ||||
| 	} | ||||
| 
 | ||||
| 	cdp, found := keeper.GetCdpByOwnerAndCollateralType(ctx, requestParams.Owner, requestParams.CollateralType) | ||||
| @ -98,7 +103,7 @@ func queryGetCdpsByRatio(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) | ||||
| 	} | ||||
| 	_, valid := keeper.GetCollateralTypePrefix(ctx, requestParams.CollateralType) | ||||
| 	if !valid { | ||||
| 		return nil, sdkerrors.Wrap(types.ErrCollateralNotSupported, requestParams.CollateralType) | ||||
| 		return nil, sdkerrors.Wrap(types.ErrInvalidCollateral, requestParams.CollateralType) | ||||
| 	} | ||||
| 
 | ||||
| 	ratio, err := keeper.CalculateCollateralizationRatioFromAbsoluteRatio(ctx, requestParams.CollateralType, requestParams.Ratio, "liquidation") | ||||
| @ -120,16 +125,16 @@ func queryGetCdpsByRatio(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) | ||||
| 	return bz, nil | ||||
| } | ||||
| 
 | ||||
| // query all cdps with matching collateral denom
 | ||||
| // query all cdps with matching collateral type
 | ||||
| func queryGetCdpsByCollateralType(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) { | ||||
| 	var requestParams types.QueryCdpsParams | ||||
| 	var requestParams types.QueryCdpsByCollateralTypeParams | ||||
| 	err := types.ModuleCdc.UnmarshalJSON(req.Data, &requestParams) | ||||
| 	if err != nil { | ||||
| 		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) | ||||
| 	} | ||||
| 	_, valid := keeper.GetCollateralTypePrefix(ctx, requestParams.CollateralType) | ||||
| 	if !valid { | ||||
| 		return nil, sdkerrors.Wrap(types.ErrCollateralNotSupported, requestParams.CollateralType) | ||||
| 		return nil, sdkerrors.Wrap(types.ErrInvalidCollateral, requestParams.CollateralType) | ||||
| 	} | ||||
| 
 | ||||
| 	cdps := keeper.GetAllCdpsByCollateralType(ctx, requestParams.CollateralType) | ||||
| @ -178,3 +183,175 @@ func queryGetAccounts(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([] | ||||
| 	} | ||||
| 	return bz, nil | ||||
| } | ||||
| 
 | ||||
| // query get savings rate distributed in the cdp store
 | ||||
| func queryGetSavingsRateDistributed(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) { | ||||
| 	// Get savings rate distributed
 | ||||
| 	savingsRateDist := keeper.GetSavingsRateDistributed(ctx) | ||||
| 
 | ||||
| 	// Encode results
 | ||||
| 	bz, err := codec.MarshalJSONIndent(types.ModuleCdc, savingsRateDist) | ||||
| 	if err != nil { | ||||
| 		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) | ||||
| 	} | ||||
| 	return bz, nil | ||||
| } | ||||
| 
 | ||||
| // query cdps in store and filter by request params
 | ||||
| func queryGetCdps(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) { | ||||
| 	var params types.QueryCdpsParams | ||||
| 	err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) | ||||
| 	if err != nil { | ||||
| 		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	// Filter CDPs
 | ||||
| 	filteredCDPs := FilterCDPs(ctx, keeper, params) | ||||
| 
 | ||||
| 	bz, err := codec.MarshalJSONIndent(keeper.cdc, filteredCDPs) | ||||
| 	if err != nil { | ||||
| 		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	return bz, nil | ||||
| } | ||||
| 
 | ||||
| // FilterCDPs queries the store for all CDPs that match query params
 | ||||
| func FilterCDPs(ctx sdk.Context, k Keeper, params types.QueryCdpsParams) types.AugmentedCDPs { | ||||
| 	var matchCollateralType, matchOwner, matchID, matchRatio types.CDPs | ||||
| 
 | ||||
| 	// match cdp owner (if supplied)
 | ||||
| 	if len(params.Owner) > 0 { | ||||
| 		denoms := k.GetCollateralTypes(ctx) | ||||
| 		for _, denom := range denoms { | ||||
| 			cdp, found := k.GetCdpByOwnerAndCollateralType(ctx, params.Owner, denom) | ||||
| 			if found { | ||||
| 				matchOwner = append(matchOwner, cdp) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// match cdp collateral denom (if supplied)
 | ||||
| 	if len(params.CollateralType) > 0 { | ||||
| 		// if owner is specified only iterate over already matched cdps for efficiency
 | ||||
| 		if len(params.Owner) > 0 { | ||||
| 			for _, cdp := range matchOwner { | ||||
| 				if cdp.Type == params.CollateralType { | ||||
| 					matchCollateralType = append(matchCollateralType, cdp) | ||||
| 				} | ||||
| 			} | ||||
| 		} else { | ||||
| 			matchCollateralType = k.GetAllCdpsByCollateralType(ctx, params.CollateralType) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// match cdp ID (if supplied)
 | ||||
| 	if params.ID != 0 { | ||||
| 		denoms := k.GetCollateralTypes(ctx) | ||||
| 		for _, denom := range denoms { | ||||
| 			cdp, found := k.GetCDP(ctx, denom, params.ID) | ||||
| 			if found { | ||||
| 				matchID = append(matchID, cdp) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// match cdp ratio (if supplied)
 | ||||
| 	if params.Ratio.GT(sdk.ZeroDec()) { | ||||
| 		denoms := k.GetCollateralTypes(ctx) | ||||
| 		for _, denom := range denoms { | ||||
| 			ratio, err := k.CalculateCollateralizationRatioFromAbsoluteRatio(ctx, denom, params.Ratio, "liquidation") | ||||
| 			if err != nil { | ||||
| 				continue | ||||
| 			} | ||||
| 			cdpsUnderRatio := k.GetAllCdpsByCollateralTypeAndRatio(ctx, denom, ratio) | ||||
| 			matchRatio = append(matchRatio, cdpsUnderRatio...) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	var commonCDPs types.CDPs | ||||
| 	// If no params specified, fetch all CDPs
 | ||||
| 	if len(params.CollateralType) == 0 && len(params.Owner) == 0 && params.ID == 0 && params.Ratio.Equal(sdk.ZeroDec()) { | ||||
| 		commonCDPs = k.GetAllCdps(ctx) | ||||
| 	} | ||||
| 
 | ||||
| 	// Find the intersection of any matched CDPs
 | ||||
| 	if len(params.CollateralType) > 0 { | ||||
| 		if len(matchCollateralType) > 0 { | ||||
| 			commonCDPs = matchCollateralType | ||||
| 		} else { | ||||
| 			return types.AugmentedCDPs{} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if len(params.Owner) > 0 { | ||||
| 		if len(matchCollateralType) > 0 { | ||||
| 			if len(commonCDPs) > 0 { | ||||
| 				commonCDPs = FindIntersection(commonCDPs, matchOwner) | ||||
| 			} else { | ||||
| 				commonCDPs = matchOwner | ||||
| 			} | ||||
| 		} else { | ||||
| 			commonCDPs = matchOwner | ||||
| 		} | ||||
| 	} | ||||
| 	if params.ID != 0 { | ||||
| 		if len(matchID) > 0 { | ||||
| 			if len(commonCDPs) > 0 { | ||||
| 				commonCDPs = FindIntersection(commonCDPs, matchID) | ||||
| 			} else { | ||||
| 				commonCDPs = matchID | ||||
| 			} | ||||
| 		} else { | ||||
| 			return types.AugmentedCDPs{} | ||||
| 		} | ||||
| 	} | ||||
| 	if params.Ratio.GT(sdk.ZeroDec()) { | ||||
| 		if len(matchRatio) > 0 { | ||||
| 			if len(commonCDPs) > 0 { | ||||
| 				commonCDPs = FindIntersection(commonCDPs, matchRatio) | ||||
| 			} else { | ||||
| 				commonCDPs = matchRatio | ||||
| 			} | ||||
| 		} else { | ||||
| 			return types.AugmentedCDPs{} | ||||
| 		} | ||||
| 	} | ||||
| 	// Load augmented CDPs
 | ||||
| 	var augmentedCDPs types.AugmentedCDPs | ||||
| 	for _, cdp := range commonCDPs { | ||||
| 		augmentedCDP := k.LoadAugmentedCDP(ctx, cdp) | ||||
| 		augmentedCDPs = append(augmentedCDPs, augmentedCDP) | ||||
| 	} | ||||
| 
 | ||||
| 	// Apply page and limit params
 | ||||
| 	var paginatedCDPs types.AugmentedCDPs | ||||
| 	start, end := client.Paginate(len(augmentedCDPs), params.Page, params.Limit, 100) | ||||
| 	if start < 0 || end < 0 { | ||||
| 		paginatedCDPs = types.AugmentedCDPs{} | ||||
| 	} else { | ||||
| 		paginatedCDPs = augmentedCDPs[start:end] | ||||
| 	} | ||||
| 
 | ||||
| 	return paginatedCDPs | ||||
| } | ||||
| 
 | ||||
| // FindIntersection finds the intersection of two CDP arrays in linear time complexity O(n + n)
 | ||||
| func FindIntersection(x types.CDPs, y types.CDPs) types.CDPs { | ||||
| 	cdpSet := make(types.CDPs, 0) | ||||
| 	cdpMap := make(map[uint64]bool) | ||||
| 
 | ||||
| 	for i := 0; i < len(x); i++ { | ||||
| 		cdp := x[i] | ||||
| 		cdpMap[cdp.ID] = true | ||||
| 	} | ||||
| 
 | ||||
| 	for i := 0; i < len(y); i++ { | ||||
| 		cdp := y[i] | ||||
| 		if _, found := cdpMap[cdp.ID]; found { | ||||
| 			cdpSet = append(cdpSet, cdp) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return cdpSet | ||||
| } | ||||
|  | ||||
| @ -116,7 +116,7 @@ func (suite *QuerierTestSuite) TestQueryCdp() { | ||||
| 	ctx := suite.ctx.WithIsCheckTx(false) | ||||
| 	query := abci.RequestQuery{ | ||||
| 		Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdp}, "/"), | ||||
| 		Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpParams(suite.cdps[0].Owner, suite.cdps[0].Collateral.Denom+"-a")), | ||||
| 		Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpParams(suite.cdps[0].Owner, suite.cdps[0].Type)), | ||||
| 	} | ||||
| 	bz, err := suite.querier(ctx, []string{types.QueryGetCdp}, query) | ||||
| 	suite.Nil(err) | ||||
| @ -156,10 +156,10 @@ func (suite *QuerierTestSuite) TestQueryCdp() { | ||||
| func (suite *QuerierTestSuite) TestQueryCdpsByCollateralType() { | ||||
| 	ctx := suite.ctx.WithIsCheckTx(false) | ||||
| 	query := abci.RequestQuery{ | ||||
| 		Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdps}, "/"), | ||||
| 		Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpsParams(suite.cdps[0].Collateral.Denom + "-a")), | ||||
| 		Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdpsByCollateralType}, "/"), | ||||
| 		Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpsByCollateralTypeParams(suite.cdps[0].Type)), | ||||
| 	} | ||||
| 	bz, err := suite.querier(ctx, []string{types.QueryGetCdps}, query) | ||||
| 	bz, err := suite.querier(ctx, []string{types.QueryGetCdpsByCollateralType}, query) | ||||
| 	suite.Nil(err) | ||||
| 	suite.NotNil(bz) | ||||
| 
 | ||||
| @ -168,10 +168,10 @@ func (suite *QuerierTestSuite) TestQueryCdpsByCollateralType() { | ||||
| 	suite.Equal(50, len(c)) | ||||
| 
 | ||||
| 	query = abci.RequestQuery{ | ||||
| 		Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdps}, "/"), | ||||
| 		Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpsParams("lol-a")), | ||||
| 		Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdpsByCollateralType}, "/"), | ||||
| 		Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpsByCollateralTypeParams("lol-a")), | ||||
| 	} | ||||
| 	_, err = suite.querier(ctx, []string{types.QueryGetCdps}, query) | ||||
| 	_, err = suite.querier(ctx, []string{types.QueryGetCdpsByCollateralType}, query) | ||||
| 	suite.Error(err) | ||||
| } | ||||
| 
 | ||||
| @ -265,7 +265,7 @@ func (suite *QuerierTestSuite) TestQueryDeposits() { | ||||
| 	ctx := suite.ctx.WithIsCheckTx(false) | ||||
| 	query := abci.RequestQuery{ | ||||
| 		Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdpDeposits}, "/"), | ||||
| 		Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpDeposits(suite.cdps[0].Owner, suite.cdps[0].Collateral.Denom+"-a")), | ||||
| 		Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryCdpDeposits(suite.cdps[0].Owner, suite.cdps[0].Type)), | ||||
| 	} | ||||
| 
 | ||||
| 	bz, err := suite.querier(ctx, []string{types.QueryGetCdpDeposits}, query) | ||||
| @ -303,6 +303,89 @@ func (suite *QuerierTestSuite) TestQueryAccounts() { | ||||
| 	suite.Require().True(findByName("savings")) | ||||
| } | ||||
| 
 | ||||
| func (suite *QuerierTestSuite) TestQuerySavingsRateDistributed() { | ||||
| 	ctx := suite.ctx.WithIsCheckTx(false) | ||||
| 	bz, err := suite.querier(ctx, []string{types.QueryGetSavingsRateDistributed}, abci.RequestQuery{}) | ||||
| 	suite.Nil(err) | ||||
| 	suite.NotNil(bz) | ||||
| 
 | ||||
| 	var distAmount sdk.Int | ||||
| 	suite.Nil(types.ModuleCdc.UnmarshalJSON(bz, &distAmount)) | ||||
| 	suite.True(sdk.ZeroInt().Equal(distAmount)) | ||||
| } | ||||
| 
 | ||||
| func (suite *QuerierTestSuite) TestFindIntersection() { | ||||
| 	a := types.CDPs{suite.cdps[0], suite.cdps[1], suite.cdps[2], suite.cdps[3], suite.cdps[4]} | ||||
| 	b := types.CDPs{suite.cdps[3], suite.cdps[4], suite.cdps[5], suite.cdps[6], suite.cdps[7]} | ||||
| 	expectedIntersection1 := types.CDPs{suite.cdps[3], suite.cdps[4]} | ||||
| 
 | ||||
| 	intersection1 := keeper.FindIntersection(a, b) | ||||
| 	suite.Require().Equal(intersection1, expectedIntersection1) | ||||
| 
 | ||||
| 	c := types.CDPs{suite.cdps[0], suite.cdps[1], suite.cdps[2], suite.cdps[3], suite.cdps[4]} | ||||
| 	d := types.CDPs{suite.cdps[5], suite.cdps[6], suite.cdps[7], suite.cdps[8], suite.cdps[9]} | ||||
| 	expectedIntersection2 := types.CDPs{} | ||||
| 
 | ||||
| 	intersection2 := keeper.FindIntersection(c, d) | ||||
| 	suite.Require().Equal(intersection2, expectedIntersection2) | ||||
| 
 | ||||
| 	e := types.CDPs{suite.cdps[0]} | ||||
| 	f := types.CDPs{} | ||||
| 	expectedIntersection3 := types.CDPs{} | ||||
| 
 | ||||
| 	intersection3 := keeper.FindIntersection(e, f) | ||||
| 	suite.Require().Equal(intersection3, expectedIntersection3) | ||||
| } | ||||
| 
 | ||||
| func (suite *QuerierTestSuite) TestFilterCDPs() { | ||||
| 	paramsType := types.NewQueryCdpsParams(1, 100, "btc-a", sdk.AccAddress{}, 0, sdk.ZeroDec()) | ||||
| 	filteredCDPs1 := keeper.FilterCDPs(suite.ctx, suite.keeper, paramsType) | ||||
| 	suite.Require().Equal(len(filteredCDPs1), 50) | ||||
| 
 | ||||
| 	paramsOwner := types.NewQueryCdpsParams(1, 100, "", suite.cdps[10].Owner, 0, sdk.ZeroDec()) | ||||
| 	filteredCDPs2 := keeper.FilterCDPs(suite.ctx, suite.keeper, paramsOwner) | ||||
| 	suite.Require().Equal(len(filteredCDPs2), 1) | ||||
| 	suite.Require().Equal(filteredCDPs2[0].Owner, suite.cdps[10].Owner) | ||||
| 
 | ||||
| 	paramsID := types.NewQueryCdpsParams(1, 100, "", sdk.AccAddress{}, 68, sdk.ZeroDec()) | ||||
| 	filteredCDPs3 := keeper.FilterCDPs(suite.ctx, suite.keeper, paramsID) | ||||
| 	suite.Require().Equal(len(filteredCDPs3), 1) | ||||
| 	suite.Require().Equal(filteredCDPs3[0].ID, suite.cdps[68-1].ID) | ||||
| 
 | ||||
| 	ratioCountBtc := 0 | ||||
| 	btcRatio := d("2500") | ||||
| 	for _, cdp := range suite.cdps { | ||||
| 		if cdp.Type == "btc-a" { | ||||
| 			absoluteRatio := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, cdp.Collateral, cdp.Type, cdp.Principal) | ||||
| 			collateralizationRatio, _ := suite.keeper.CalculateCollateralizationRatioFromAbsoluteRatio(suite.ctx, cdp.Type, absoluteRatio, "liquidation") | ||||
| 			if collateralizationRatio.LT(btcRatio) { | ||||
| 				ratioCountBtc += 1 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	paramsTypeAndRatio := types.NewQueryCdpsParams(1, 100, "btc-a", sdk.AccAddress{}, 0, sdk.NewDec(2500)) | ||||
| 	filteredCDPs4 := keeper.FilterCDPs(suite.ctx, suite.keeper, paramsTypeAndRatio) | ||||
| 	suite.Require().Equal(len(filteredCDPs4), ratioCountBtc) | ||||
| } | ||||
| 
 | ||||
| func (suite *QuerierTestSuite) TestQueryCdps() { | ||||
| 	ctx := suite.ctx.WithIsCheckTx(false) | ||||
| 	params := types.NewQueryCdpsParams(1, 100, "btc-a", sdk.AccAddress{}, 0, sdk.ZeroDec()) | ||||
| 
 | ||||
| 	query := abci.RequestQuery{ | ||||
| 		Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetCdps}, "/"), | ||||
| 		Data: types.ModuleCdc.MustMarshalJSON(params), | ||||
| 	} | ||||
| 
 | ||||
| 	bz, err := suite.querier(ctx, []string{types.QueryGetCdps}, query) | ||||
| 	suite.Nil(err) | ||||
| 	suite.NotNil(bz) | ||||
| 
 | ||||
| 	output := types.AugmentedCDPs{} | ||||
| 	suite.Nil(types.ModuleCdc.UnmarshalJSON(bz, &output)) | ||||
| 	suite.Equal(50, len(output)) | ||||
| } | ||||
| 
 | ||||
| func TestQuerierTestSuite(t *testing.T) { | ||||
| 	suite.Run(t, new(QuerierTestSuite)) | ||||
| } | ||||
|  | ||||
| @ -58,6 +58,11 @@ func (k Keeper) DistributeSavingsRate(ctx sdk.Context, debtDenom string) error { | ||||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 		// update total savings rate distributed by surplus to distribute
 | ||||
| 		previousSavingsDistributed := k.GetSavingsRateDistributed(ctx) | ||||
| 		newTotalDistributed := previousSavingsDistributed.Add(interest) | ||||
| 		k.SetSavingsRateDistributed(ctx, newTotalDistributed) | ||||
| 
 | ||||
| 		interestCoins := sdk.NewCoins(sdk.NewCoin(debtDenom, interest)) | ||||
| 		err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.SavingsRateMacc, acc.GetAddress(), interestCoins) | ||||
| 		if err != nil { | ||||
|  | ||||
| @ -18,15 +18,17 @@ import ( | ||||
| type SavingsTestSuite struct { | ||||
| 	suite.Suite | ||||
| 
 | ||||
| 	keeper keeper.Keeper | ||||
| 	app    app.TestApp | ||||
| 	ctx    sdk.Context | ||||
| 	addrs  []sdk.AccAddress | ||||
| 	keeper             keeper.Keeper | ||||
| 	app                app.TestApp | ||||
| 	ctx                sdk.Context | ||||
| 	addrs              []sdk.AccAddress | ||||
| 	amountToDistribute int64 | ||||
| } | ||||
| 
 | ||||
| func (suite *SavingsTestSuite) SetupTest() { | ||||
| 	tApp := app.NewTestApp() | ||||
| 	ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()}) | ||||
| 
 | ||||
| 	_, addrs := app.GeneratePrivKeyAddressPairs(3) | ||||
| 	authGS := app.NewAuthGenState( | ||||
| 		addrs, | ||||
| @ -34,25 +36,33 @@ func (suite *SavingsTestSuite) SetupTest() { | ||||
| 			cs(c("usdx", 100000)), cs(c("usdx", 50000)), cs(c("usdx", 50000)), | ||||
| 		}, | ||||
| 	) | ||||
| 
 | ||||
| 	tApp.InitializeFromGenesisStates( | ||||
| 		authGS, | ||||
| 		NewPricefeedGenStateMulti(), | ||||
| 		NewCDPGenStateMulti(), | ||||
| 	) | ||||
| 
 | ||||
| 	sk := tApp.GetSupplyKeeper() | ||||
| 	macc := sk.GetModuleAccount(ctx, types.SavingsRateMacc) | ||||
| 	err := sk.MintCoins(ctx, macc.GetName(), cs(c("usdx", 10000))) | ||||
| 	distAmount := int64(10000) | ||||
| 	err := sk.MintCoins(ctx, macc.GetName(), cs(c("usdx", distAmount))) | ||||
| 	suite.NoError(err) | ||||
| 
 | ||||
| 	keeper := tApp.GetCDPKeeper() | ||||
| 	suite.app = tApp | ||||
| 	suite.keeper = keeper | ||||
| 	suite.ctx = ctx | ||||
| 	suite.addrs = addrs | ||||
| 	suite.amountToDistribute = distAmount | ||||
| } | ||||
| 
 | ||||
| func (suite *SavingsTestSuite) TestApplySavingsRate() { | ||||
| 	preSavingsRateDistAmount := suite.keeper.GetSavingsRateDistributed(suite.ctx) | ||||
| 
 | ||||
| 	err := suite.keeper.DistributeSavingsRate(suite.ctx, "usdx") | ||||
| 	suite.NoError(err) | ||||
| 
 | ||||
| 	ak := suite.app.GetAccountKeeper() | ||||
| 	acc0 := ak.GetAccount(suite.ctx, suite.addrs[0]) | ||||
| 	suite.Equal(cs(c("usdx", 105000)), acc0.GetCoins()) | ||||
| @ -60,9 +70,14 @@ func (suite *SavingsTestSuite) TestApplySavingsRate() { | ||||
| 	suite.Equal(cs(c("usdx", 52500)), acc1.GetCoins()) | ||||
| 	acc2 := ak.GetAccount(suite.ctx, suite.addrs[2]) | ||||
| 	suite.Equal(cs(c("usdx", 52500)), acc2.GetCoins()) | ||||
| 
 | ||||
| 	sk := suite.app.GetSupplyKeeper() | ||||
| 	macc := sk.GetModuleAccount(suite.ctx, types.SavingsRateMacc) | ||||
| 	suite.True(macc.GetCoins().AmountOf("usdx").IsZero()) | ||||
| 
 | ||||
| 	expectedPostSavingsRateDistAmount := preSavingsRateDistAmount.Add(sdk.NewInt(suite.amountToDistribute)) | ||||
| 	postSavingsRateDistAmount := suite.keeper.GetSavingsRateDistributed(suite.ctx) | ||||
| 	suite.True(expectedPostSavingsRateDistAmount.Equal(postSavingsRateDistAmount)) | ||||
| } | ||||
| 
 | ||||
| func (suite *SavingsTestSuite) TestGetSetPreviousDistributionTime() { | ||||
| @ -76,7 +91,21 @@ func (suite *SavingsTestSuite) TestGetSetPreviousDistributionTime() { | ||||
| 	pdt, f := suite.keeper.GetPreviousSavingsDistribution(suite.ctx) | ||||
| 	suite.True(f) | ||||
| 	suite.Equal(now, pdt) | ||||
| } | ||||
| 
 | ||||
| func (suite *SavingsTestSuite) TestGetSetSavingsRateDistributed() { | ||||
| 	// Savings rate dist set to 0 when the default genesis is used
 | ||||
| 	preSavingsRateDistAmount := suite.keeper.GetSavingsRateDistributed(suite.ctx) | ||||
| 	suite.True(preSavingsRateDistAmount.Equal(types.DefaultSavingsRateDistributed)) | ||||
| 
 | ||||
| 	// Adding new dist amount to existing dist so default genesis value can be updated in the future
 | ||||
| 	amountToDistribute := sdk.NewInt(9876543210) | ||||
| 	newTotalDistributed := preSavingsRateDistAmount.Add(amountToDistribute) | ||||
| 
 | ||||
| 	suite.NotPanics(func() { suite.keeper.SetSavingsRateDistributed(suite.ctx, newTotalDistributed) }) | ||||
| 
 | ||||
| 	postSavingsRateDistAmount := suite.keeper.GetSavingsRateDistributed(suite.ctx) | ||||
| 	suite.Equal(newTotalDistributed, postSavingsRateDistAmount) | ||||
| } | ||||
| 
 | ||||
| func TestSavingsTestSuite(t *testing.T) { | ||||
|  | ||||
| @ -121,6 +121,7 @@ func NewAugmentedCDP(cdp CDP, collateralValue sdk.Coin, collateralizationRatio s | ||||
| 		CDP: CDP{ | ||||
| 			ID:              cdp.ID, | ||||
| 			Owner:           cdp.Owner, | ||||
| 			Type:            cdp.Type, | ||||
| 			Collateral:      cdp.Collateral, | ||||
| 			Principal:       cdp.Principal, | ||||
| 			AccumulatedFees: cdp.AccumulatedFees, | ||||
| @ -146,7 +147,7 @@ func (augCDP AugmentedCDP) String() string { | ||||
| 	Collateralization ratio: %s`, | ||||
| 		augCDP.Owner, | ||||
| 		augCDP.ID, | ||||
| 		augCDP.Collateral.Denom, | ||||
| 		augCDP.Type, | ||||
| 		augCDP.Collateral, | ||||
| 		augCDP.CollateralValue, | ||||
| 		augCDP.Principal, | ||||
|  | ||||
| @ -17,10 +17,12 @@ type GenesisState struct { | ||||
| 	DebtDenom                string    `json:"debt_denom" yaml:"debt_denom"` | ||||
| 	GovDenom                 string    `json:"gov_denom" yaml:"gov_denom"` | ||||
| 	PreviousDistributionTime time.Time `json:"previous_distribution_time" yaml:"previous_distribution_time"` | ||||
| 	SavingsRateDistributed   sdk.Int   `json:"savings_rate_distributed" yaml:"savings_rate_distributed"` | ||||
| } | ||||
| 
 | ||||
| // NewGenesisState returns a new genesis state
 | ||||
| func NewGenesisState(params Params, cdps CDPs, deposits Deposits, startingCdpID uint64, debtDenom, govDenom string, previousDistTime time.Time) GenesisState { | ||||
| func NewGenesisState(params Params, cdps CDPs, deposits Deposits, startingCdpID uint64, | ||||
| 	debtDenom, govDenom string, previousDistTime time.Time, savingsRateDist sdk.Int) GenesisState { | ||||
| 	return GenesisState{ | ||||
| 		Params:                   params, | ||||
| 		CDPs:                     cdps, | ||||
| @ -29,6 +31,7 @@ func NewGenesisState(params Params, cdps CDPs, deposits Deposits, startingCdpID | ||||
| 		DebtDenom:                debtDenom, | ||||
| 		GovDenom:                 govDenom, | ||||
| 		PreviousDistributionTime: previousDistTime, | ||||
| 		SavingsRateDistributed:   savingsRateDist, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -42,6 +45,7 @@ func DefaultGenesisState() GenesisState { | ||||
| 		DefaultDebtDenom, | ||||
| 		DefaultGovDenom, | ||||
| 		DefaultPreviousDistributionTime, | ||||
| 		DefaultSavingsRateDistributed, | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| @ -65,6 +69,10 @@ func (gs GenesisState) Validate() error { | ||||
| 		return fmt.Errorf("previous distribution time not set") | ||||
| 	} | ||||
| 
 | ||||
| 	if err := validateSavingsRateDistributed(gs.SavingsRateDistributed); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if err := sdk.ValidateDenom(gs.DebtDenom); err != nil { | ||||
| 		return fmt.Errorf(fmt.Sprintf("debt denom invalid: %v", err)) | ||||
| 	} | ||||
| @ -76,6 +84,19 @@ func (gs GenesisState) Validate() error { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func validateSavingsRateDistributed(i interface{}) error { | ||||
| 	savingsRateDist, ok := i.(sdk.Int) | ||||
| 	if !ok { | ||||
| 		return fmt.Errorf("invalid parameter type: %T", i) | ||||
| 	} | ||||
| 
 | ||||
| 	if savingsRateDist.IsNegative() { | ||||
| 		return fmt.Errorf("savings rate distributed should not be negative: %s", savingsRateDist) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Equal checks whether two gov GenesisState structs are equivalent
 | ||||
| func (gs GenesisState) Equal(gs2 GenesisState) bool { | ||||
| 	b1 := ModuleCdc.MustMarshalBinaryBare(gs) | ||||
|  | ||||
| @ -47,6 +47,7 @@ var sep = []byte(":") | ||||
| // - 0x07<denom>:feeRate
 | ||||
| // - 0x08:previousDistributionTime
 | ||||
| // - 0x09<marketID>:downTime
 | ||||
| // - 0x10:totalDistributed
 | ||||
| 
 | ||||
| // KVStore key prefixes
 | ||||
| var ( | ||||
| @ -60,6 +61,7 @@ var ( | ||||
| 	PrincipalKeyPrefix          = []byte{0x07} | ||||
| 	PreviousDistributionTimeKey = []byte{0x08} | ||||
| 	PricefeedStatusKeyPrefix    = []byte{0x09} | ||||
| 	SavingsRateDistributedKey   = []byte{0x10} | ||||
| ) | ||||
| 
 | ||||
| // GetCdpIDBytes returns the byte representation of the cdpID
 | ||||
|  | ||||
| @ -76,7 +76,8 @@ func (msg MsgCreateCDP) String() string { | ||||
|   Sender:         %s | ||||
| 	Collateral: %s | ||||
| 	Principal: %s | ||||
| `, msg.Sender, msg.Collateral, msg.Principal) | ||||
| 	Collateral Type: %s | ||||
| `, msg.Sender, msg.Collateral, msg.Principal, msg.CollateralType) | ||||
| } | ||||
| 
 | ||||
| // MsgDeposit deposit collateral to an existing cdp.
 | ||||
|  | ||||
| @ -14,19 +14,20 @@ import ( | ||||
| 
 | ||||
| // Parameter keys
 | ||||
| var ( | ||||
| 	KeyGlobalDebtLimit       = []byte("GlobalDebtLimit") | ||||
| 	KeyCollateralParams      = []byte("CollateralParams") | ||||
| 	KeyDebtParam             = []byte("DebtParam") | ||||
| 	KeyDistributionFrequency = []byte("DistributionFrequency") | ||||
| 	KeyCircuitBreaker        = []byte("CircuitBreaker") | ||||
| 	KeyDebtThreshold         = []byte("DebtThreshold") | ||||
| 	KeyDebtLot               = []byte("DebtLot") | ||||
| 	KeySurplusThreshold      = []byte("SurplusThreshold") | ||||
| 	KeySurplusLot            = []byte("SurplusLot") | ||||
| 	DefaultGlobalDebt        = sdk.NewCoin(DefaultStableDenom, sdk.ZeroInt()) | ||||
| 	DefaultCircuitBreaker    = false | ||||
| 	DefaultCollateralParams  = CollateralParams{} | ||||
| 	DefaultDebtParam         = DebtParam{ | ||||
| 	KeyGlobalDebtLimit        = []byte("GlobalDebtLimit") | ||||
| 	KeyCollateralParams       = []byte("CollateralParams") | ||||
| 	KeyDebtParam              = []byte("DebtParam") | ||||
| 	KeyDistributionFrequency  = []byte("DistributionFrequency") | ||||
| 	KeyCircuitBreaker         = []byte("CircuitBreaker") | ||||
| 	KeyDebtThreshold          = []byte("DebtThreshold") | ||||
| 	KeyDebtLot                = []byte("DebtLot") | ||||
| 	KeySurplusThreshold       = []byte("SurplusThreshold") | ||||
| 	KeySurplusLot             = []byte("SurplusLot") | ||||
| 	KeySavingsRateDistributed = []byte("SavingsRateDistributed") | ||||
| 	DefaultGlobalDebt         = sdk.NewCoin(DefaultStableDenom, sdk.ZeroInt()) | ||||
| 	DefaultCircuitBreaker     = false | ||||
| 	DefaultCollateralParams   = CollateralParams{} | ||||
| 	DefaultDebtParam          = DebtParam{ | ||||
| 		Denom:            "usdx", | ||||
| 		ReferenceAsset:   "usd", | ||||
| 		ConversionFactor: sdk.NewInt(6), | ||||
| @ -43,6 +44,7 @@ var ( | ||||
| 	DefaultDebtLot                      = sdk.NewInt(10000000000) | ||||
| 	DefaultPreviousDistributionTime     = tmtime.Canonical(time.Unix(0, 0)) | ||||
| 	DefaultSavingsDistributionFrequency = time.Hour * 12 | ||||
| 	DefaultSavingsRateDistributed       = sdk.NewInt(0) | ||||
| 	minCollateralPrefix                 = 0 | ||||
| 	maxCollateralPrefix                 = 255 | ||||
| 	stabilityFeeMax                     = sdk.MustNewDecFromStr("1.000000051034942716") // 500% APR
 | ||||
|  | ||||
| @ -7,28 +7,18 @@ import ( | ||||
| // Querier routes for the cdp module
 | ||||
| const ( | ||||
| 	QueryGetCdp                     = "cdp" | ||||
| 	QueryGetCdpDeposits             = "deposits" | ||||
| 	QueryGetCdps                    = "cdps" | ||||
| 	QueryGetCdpsByCollateralization = "ratio" | ||||
| 	QueryGetCdpDeposits             = "deposits" | ||||
| 	QueryGetCdpsByCollateralization = "ratio"          // legacy query, maintained for REST API
 | ||||
| 	QueryGetCdpsByCollateralType    = "collateralType" // legacy query, maintained for REST API
 | ||||
| 	QueryGetParams                  = "params" | ||||
| 	QueryGetAccounts                = "accounts" | ||||
| 	QueryGetSavingsRateDistributed  = "savings-rate-dist" | ||||
| 	RestOwner                       = "owner" | ||||
| 	RestCollateralType              = "collateral-type" | ||||
| 	RestRatio                       = "ratio" | ||||
| ) | ||||
| 
 | ||||
| // QueryCdpsParams params for query /cdp/cdps
 | ||||
| type QueryCdpsParams struct { | ||||
| 	CollateralType string // get CDPs with this collateral type
 | ||||
| } | ||||
| 
 | ||||
| // NewQueryCdpsParams returns QueryCdpsParams
 | ||||
| func NewQueryCdpsParams(collateralType string) QueryCdpsParams { | ||||
| 	return QueryCdpsParams{ | ||||
| 		CollateralType: collateralType, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // QueryCdpParams params for query /cdp/cdp
 | ||||
| type QueryCdpParams struct { | ||||
| 	CollateralType string         // get CDPs with this collateral type
 | ||||
| @ -43,6 +33,28 @@ func NewQueryCdpParams(owner sdk.AccAddress, collateralType string) QueryCdpPara | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // QueryCdpsParams is the params for a filtered CDP query
 | ||||
| type QueryCdpsParams struct { | ||||
| 	Page           int            `json:"page" yaml:"page"` | ||||
| 	Limit          int            `json:"limit" yaml:"limit"` | ||||
| 	CollateralType string         `json:"collateral_type" yaml:"collateral_type"` | ||||
| 	Owner          sdk.AccAddress `json:"owner" yaml:"owner"` | ||||
| 	ID             uint64         `json:"id" yaml:"id"` | ||||
| 	Ratio          sdk.Dec        `json:"ratio" yaml:"ratio"` | ||||
| } | ||||
| 
 | ||||
| // NewQueryCdpsParams creates a new QueryCdpsParams
 | ||||
| func NewQueryCdpsParams(page, limit int, collateralType string, owner sdk.AccAddress, id uint64, ratio sdk.Dec) QueryCdpsParams { | ||||
| 	return QueryCdpsParams{ | ||||
| 		Page:           page, | ||||
| 		Limit:          limit, | ||||
| 		CollateralType: collateralType, | ||||
| 		Owner:          owner, | ||||
| 		ID:             id, | ||||
| 		Ratio:          ratio, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // QueryCdpDeposits params for query /cdp/deposits
 | ||||
| type QueryCdpDeposits struct { | ||||
| 	CollateralType string         // get CDPs with this collateral type
 | ||||
| @ -57,6 +69,18 @@ func NewQueryCdpDeposits(owner sdk.AccAddress, collateralType string) QueryCdpDe | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // QueryCdpsByCollateralTypeParams params for query /cdp/cdps/{denom}
 | ||||
| type QueryCdpsByCollateralTypeParams struct { | ||||
| 	CollateralType string // get CDPs with this collateral type
 | ||||
| } | ||||
| 
 | ||||
| // NewQueryCdpsByCollateralTypeParams returns QueryCdpsByCollateralTypeParams
 | ||||
| func NewQueryCdpsByCollateralTypeParams(collateralType string) QueryCdpsByCollateralTypeParams { | ||||
| 	return QueryCdpsByCollateralTypeParams{ | ||||
| 		CollateralType: collateralType, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // QueryCdpsByRatioParams params for query /cdp/cdps/ratio
 | ||||
| type QueryCdpsByRatioParams struct { | ||||
| 	CollateralType string | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Denali Marsh
						Denali Marsh