mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-04-04 15:55:23 +00:00 
			
		
		
		
	Update Earn vaults to use sdk.Dec shares (#1283)
* Change vault supply to shares * Update deposit shares * Use shares instead of supplied * Update tests, fix share calculation * Pass hard and savings keeper as pointer to earn keeper * Update remaining failing test * Add different share price test, fix comment for share price * Add shares amount to events * Additional share tests, use share to asset conversion for withdraw amount * Update VaultTotalValue test * Use sdk.Dec for vault shares instead of sdk.Int * Add test for expensive 20:1 shares * Update ConvertToShares comment for division, remove redundant test * Add vault share tests
This commit is contained in:
		
							parent
							
								
									fe89ba938d
								
							
						
					
					
						commit
						b5e162a930
					
				| @ -604,8 +604,8 @@ func NewApp( | ||||
| 		earnSubspace, | ||||
| 		app.accountKeeper, | ||||
| 		app.bankKeeper, | ||||
| 		hardKeeper, | ||||
| 		savingsKeeper, | ||||
| 		&hardKeeper, | ||||
| 		&savingsKeeper, | ||||
| 	) | ||||
| 
 | ||||
| 	// create committee keeper with router
 | ||||
|  | ||||
| @ -184,6 +184,7 @@ | ||||
| - [kava/earn/v1beta1/vault.proto](#kava/earn/v1beta1/vault.proto) | ||||
|     - [AllowedVault](#kava.earn.v1beta1.AllowedVault) | ||||
|     - [VaultRecord](#kava.earn.v1beta1.VaultRecord) | ||||
|     - [VaultShare](#kava.earn.v1beta1.VaultShare) | ||||
|     - [VaultShareRecord](#kava.earn.v1beta1.VaultShareRecord) | ||||
|    | ||||
| - [kava/earn/v1beta1/params.proto](#kava/earn/v1beta1/params.proto) | ||||
| @ -198,8 +199,6 @@ | ||||
|     - [QueryDepositsResponse](#kava.earn.v1beta1.QueryDepositsResponse) | ||||
|     - [QueryParamsRequest](#kava.earn.v1beta1.QueryParamsRequest) | ||||
|     - [QueryParamsResponse](#kava.earn.v1beta1.QueryParamsResponse) | ||||
|     - [QueryTotalDepositedRequest](#kava.earn.v1beta1.QueryTotalDepositedRequest) | ||||
|     - [QueryTotalDepositedResponse](#kava.earn.v1beta1.QueryTotalDepositedResponse) | ||||
|     - [QueryVaultsRequest](#kava.earn.v1beta1.QueryVaultsRequest) | ||||
|     - [QueryVaultsResponse](#kava.earn.v1beta1.QueryVaultsResponse) | ||||
|     - [VaultResponse](#kava.earn.v1beta1.VaultResponse) | ||||
| @ -2817,14 +2816,28 @@ modified via parameter governance. | ||||
| <a name="kava.earn.v1beta1.VaultRecord"></a> | ||||
| 
 | ||||
| ### VaultRecord | ||||
| VaultRecord is the state of a vault and is used to store the state of a | ||||
| vault. | ||||
| VaultRecord is the state of a vault. | ||||
| 
 | ||||
| 
 | ||||
| | Field | Type | Label | Description | | ||||
| | ----- | ---- | ----- | ----------- | | ||||
| | `denom` | [string](#string) |  | Denom is the only supported denomination of the vault for deposits and withdrawals. | | ||||
| | `total_supply` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) |  | TotalSupply is the total supply of the vault, denominated **only** in the user deposit/withdrawal denom, must be the same as the Denom field. | | ||||
| | `total_shares` | [VaultShare](#kava.earn.v1beta1.VaultShare) |  | TotalShares is the total distributed number of shares in the vault. | | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| <a name="kava.earn.v1beta1.VaultShare"></a> | ||||
| 
 | ||||
| ### VaultShare | ||||
| VaultShare defines shares of a vault owned by a depositor. | ||||
| 
 | ||||
| 
 | ||||
| | Field | Type | Label | Description | | ||||
| | ----- | ---- | ----- | ----------- | | ||||
| | `denom` | [string](#string) |  |  | | ||||
| | `amount` | [string](#string) |  |  | | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| @ -2840,7 +2853,7 @@ VaultShareRecord defines the vault shares owned by a depositor. | ||||
| | Field | Type | Label | Description | | ||||
| | ----- | ---- | ----- | ----------- | | ||||
| | `depositor` | [bytes](#bytes) |  | Depositor represents the owner of the shares | | ||||
| | `amount_supplied` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | AmountSupplied represents the total amount a depositor has supplied to the vault. The vault is determined by the coin denom. | | ||||
| | `shares` | [VaultShare](#kava.earn.v1beta1.VaultShare) | repeated | Shares represent the vault shares owned by the depositor. | | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| @ -2936,7 +2949,7 @@ DepositResponse defines a deposit query response type. | ||||
| | Field | Type | Label | Description | | ||||
| | ----- | ---- | ----- | ----------- | | ||||
| | `depositor` | [string](#string) |  | depositor represents the owner of the deposit. | | ||||
| | `amount_supplied` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | Amount represents the amount supplied to vaults. | | ||||
| | `shares` | [VaultShare](#kava.earn.v1beta1.VaultShare) | repeated | Shares represent the issued shares from their corresponding vaults. | | ||||
| | `value` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | Value represents the total accumulated value of denom coins supplied to vaults. This may be greater than or equal to amount_supplied depending on the strategy. | | ||||
| 
 | ||||
| 
 | ||||
| @ -3002,36 +3015,6 @@ QueryParamsResponse defines the response type for querying x/earn parameters. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| <a name="kava.earn.v1beta1.QueryTotalDepositedRequest"></a> | ||||
| 
 | ||||
| ### QueryTotalDepositedRequest | ||||
| QueryTotalDepositedRequest is the request type for the Query/TotalDeposited RPC method. | ||||
| 
 | ||||
| 
 | ||||
| | Field | Type | Label | Description | | ||||
| | ----- | ---- | ----- | ----------- | | ||||
| | `denom` | [string](#string) |  | denom represents the vault denom to query total deposited amount for. | | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| <a name="kava.earn.v1beta1.QueryTotalDepositedResponse"></a> | ||||
| 
 | ||||
| ### QueryTotalDepositedResponse | ||||
| QueryTotalDepositedResponse is the response type for the Query/TotalDeposited RPC method. | ||||
| 
 | ||||
| 
 | ||||
| | Field | Type | Label | Description | | ||||
| | ----- | ---- | ----- | ----------- | | ||||
| | `supplied_coins` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated |  | | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| <a name="kava.earn.v1beta1.QueryVaultsRequest"></a> | ||||
| 
 | ||||
| ### QueryVaultsRequest | ||||
| @ -3072,7 +3055,7 @@ VaultResponse is the response type for a vault. | ||||
| | ----- | ---- | ----- | ----------- | | ||||
| | `denom` | [string](#string) |  | denom represents the denom of the vault | | ||||
| | `vault_strategy` | [StrategyType](#kava.earn.v1beta1.StrategyType) |  | VaultStrategy is the strategy used for this vault. | | ||||
| | `total_supplied` | [string](#string) |  | TotalSupplied is the total amount of denom coins supplied to the vault. | | ||||
| | `total_shares` | [string](#string) |  | TotalShares is the total amount of shares issued to depositors. | | ||||
| | `total_value` | [string](#string) |  | TotalValue is the total value of denom coins supplied to the vault if the vault were to be liquidated. | | ||||
| 
 | ||||
| 
 | ||||
| @ -3096,7 +3079,6 @@ Query defines the gRPC querier service for earn module | ||||
| | `Params` | [QueryParamsRequest](#kava.earn.v1beta1.QueryParamsRequest) | [QueryParamsResponse](#kava.earn.v1beta1.QueryParamsResponse) | Params queries all parameters of the earn module. | GET|/kava/earn/v1beta1/params| | ||||
| | `Vaults` | [QueryVaultsRequest](#kava.earn.v1beta1.QueryVaultsRequest) | [QueryVaultsResponse](#kava.earn.v1beta1.QueryVaultsResponse) | Vaults queries vaults based on vault denom | GET|/kava/earn/v1beta1/vaults/{denom}| | ||||
| | `Deposits` | [QueryDepositsRequest](#kava.earn.v1beta1.QueryDepositsRequest) | [QueryDepositsResponse](#kava.earn.v1beta1.QueryDepositsResponse) | Deposits queries deposit details based on owner address and vault | GET|/kava/earn/v1beta1/deposits| | ||||
| | `TotalDeposited` | [QueryTotalDepositedRequest](#kava.earn.v1beta1.QueryTotalDepositedRequest) | [QueryTotalDepositedResponse](#kava.earn.v1beta1.QueryTotalDepositedResponse) | TotalDeposited queries total deposited amount for each vault. | GET|/kava/earn/v1beta1/total-deposited/{denom}| | ||||
| 
 | ||||
|  <!-- end services --> | ||||
| 
 | ||||
| @ -3131,6 +3113,11 @@ MsgDeposit represents a message for depositing assedts into a vault | ||||
| MsgDepositResponse defines the Msg/Deposit response type. | ||||
| 
 | ||||
| 
 | ||||
| | Field | Type | Label | Description | | ||||
| | ----- | ---- | ----- | ----------- | | ||||
| | `shares` | [VaultShare](#kava.earn.v1beta1.VaultShare) |  |  | | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| @ -3157,6 +3144,11 @@ MsgWithdraw represents a message for withdrawing liquidity from a vault | ||||
| MsgWithdrawResponse defines the Msg/Withdraw response type. | ||||
| 
 | ||||
| 
 | ||||
| | Field | Type | Label | Description | | ||||
| | ----- | ---- | ----- | ----------- | | ||||
| | `shares` | [VaultShare](#kava.earn.v1beta1.VaultShare) |  |  | | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -11,6 +11,7 @@ import "gogoproto/gogo.proto"; | ||||
| import "google/api/annotations.proto"; | ||||
| import "kava/earn/v1beta1/params.proto"; | ||||
| import "kava/earn/v1beta1/strategy.proto"; | ||||
| import "kava/earn/v1beta1/vault.proto"; | ||||
| 
 | ||||
| // Query defines the gRPC querier service for earn module | ||||
| service Query { | ||||
| @ -28,11 +29,6 @@ service Query { | ||||
|   rpc Deposits(QueryDepositsRequest) returns (QueryDepositsResponse) { | ||||
|     option (google.api.http).get = "/kava/earn/v1beta1/deposits"; | ||||
|   } | ||||
| 
 | ||||
|   // TotalDeposited queries total deposited amount for each vault. | ||||
|   rpc TotalDeposited(QueryTotalDepositedRequest) returns (QueryTotalDepositedResponse) { | ||||
|     option (google.api.http).get = "/kava/earn/v1beta1/total-deposited/{denom}"; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // QueryParamsRequest defines the request type for querying x/earn parameters. | ||||
| @ -40,7 +36,6 @@ message QueryParamsRequest {} | ||||
| 
 | ||||
| // QueryParamsResponse defines the response type for querying x/earn parameters. | ||||
| message QueryParamsResponse { | ||||
| 
 | ||||
|   // params represents the earn module parameters | ||||
|   Params params = 1 [(gogoproto.nullable) = false]; | ||||
| } | ||||
| @ -65,12 +60,8 @@ message VaultResponse { | ||||
|   // VaultStrategy is the strategy used for this vault. | ||||
|   StrategyType vault_strategy = 2; | ||||
| 
 | ||||
|   // TotalSupplied is the total amount of denom coins supplied to the vault. | ||||
|   string total_supplied = 3 [ | ||||
|     (cosmos_proto.scalar)  = "cosmos.Int", | ||||
|     (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", | ||||
|     (gogoproto.nullable)   = false | ||||
|   ]; | ||||
|   // TotalShares is the total amount of shares issued to depositors. | ||||
|   string total_shares = 3; | ||||
| 
 | ||||
|   // TotalValue is the total value of denom coins supplied to the vault if the | ||||
|   // vault were to be liquidated. | ||||
| @ -107,9 +98,8 @@ message DepositResponse { | ||||
|   // depositor represents the owner of the deposit. | ||||
|   string depositor = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; | ||||
| 
 | ||||
|   // Amount represents the amount supplied to vaults. | ||||
|   repeated cosmos.base.v1beta1.Coin amount_supplied = 2 | ||||
|       [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", (gogoproto.nullable) = false]; | ||||
|   // Shares represent the issued shares from their corresponding vaults. | ||||
|   repeated VaultShare shares = 2 [(gogoproto.castrepeated) = "VaultShares", (gogoproto.nullable) = false]; | ||||
| 
 | ||||
|   // Value represents the total accumulated value of denom coins supplied to | ||||
|   // vaults. This may be greater than or equal to amount_supplied depending on | ||||
| @ -117,15 +107,3 @@ message DepositResponse { | ||||
|   repeated cosmos.base.v1beta1.Coin value = 3 | ||||
|       [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", (gogoproto.nullable) = false]; | ||||
| } | ||||
| 
 | ||||
| // QueryTotalDepositedRequest is the request type for the Query/TotalDeposited RPC method. | ||||
| message QueryTotalDepositedRequest { | ||||
|   // denom represents the vault denom to query total deposited amount for. | ||||
|   string denom = 1; | ||||
| } | ||||
| 
 | ||||
| // QueryTotalDepositedResponse is the response type for the Query/TotalDeposited RPC method. | ||||
| message QueryTotalDepositedResponse { | ||||
|   repeated cosmos.base.v1beta1.Coin supplied_coins = 1 | ||||
|       [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", (gogoproto.nullable) = false]; | ||||
| } | ||||
|  | ||||
| @ -4,6 +4,7 @@ package kava.earn.v1beta1; | ||||
| import "cosmos_proto/cosmos.proto"; | ||||
| import "cosmos/base/v1beta1/coin.proto"; | ||||
| import "gogoproto/gogo.proto"; | ||||
| import "kava/earn/v1beta1/vault.proto"; | ||||
| 
 | ||||
| option go_package = "github.com/kava-labs/kava/x/earn/types"; | ||||
| 
 | ||||
| @ -27,7 +28,9 @@ message MsgDeposit { | ||||
| } | ||||
| 
 | ||||
| // MsgDepositResponse defines the Msg/Deposit response type. | ||||
| message MsgDepositResponse {} | ||||
| message MsgDepositResponse { | ||||
|   VaultShare shares = 1 [(gogoproto.nullable) = false]; | ||||
| } | ||||
| 
 | ||||
| // MsgWithdraw represents a message for withdrawing liquidity from a vault | ||||
| message MsgWithdraw { | ||||
| @ -42,4 +45,6 @@ message MsgWithdraw { | ||||
| } | ||||
| 
 | ||||
| // MsgWithdrawResponse defines the Msg/Withdraw response type. | ||||
| message MsgWithdrawResponse {} | ||||
| message MsgWithdrawResponse { | ||||
|   VaultShare shares = 1 [(gogoproto.nullable) = false]; | ||||
| } | ||||
|  | ||||
| @ -18,15 +18,10 @@ message AllowedVault { | ||||
|   StrategyType vault_strategy = 2; | ||||
| } | ||||
| 
 | ||||
| // VaultRecord is the state of a vault and is used to store the state of a | ||||
| // vault. | ||||
| // VaultRecord is the state of a vault. | ||||
| message VaultRecord { | ||||
|   // Denom is the only supported denomination of the vault for deposits and | ||||
|   // withdrawals. | ||||
|   string denom = 1; | ||||
|   // TotalSupply is the total supply of the vault, denominated **only** in the | ||||
|   // user deposit/withdrawal denom, must be the same as the Denom field. | ||||
|   cosmos.base.v1beta1.Coin total_supply = 2 [(gogoproto.nullable) = false]; | ||||
|   // TotalShares is the total distributed number of shares in the vault. | ||||
|   VaultShare total_shares = 1 [(gogoproto.nullable) = false]; | ||||
| } | ||||
| 
 | ||||
| // VaultShareRecord defines the vault shares owned by a depositor. | ||||
| @ -36,8 +31,18 @@ message VaultShareRecord { | ||||
|     (cosmos_proto.scalar) = "cosmos.AddressBytes", | ||||
|     (gogoproto.casttype)  = "github.com/cosmos/cosmos-sdk/types.AccAddress" | ||||
|   ]; | ||||
|   // AmountSupplied represents the total amount a depositor has supplied to the | ||||
|   // vault. The vault is determined by the coin denom. | ||||
|   repeated cosmos.base.v1beta1.Coin amount_supplied = 2 | ||||
|       [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", (gogoproto.nullable) = false]; | ||||
| } | ||||
|   // Shares represent the vault shares owned by the depositor. | ||||
|   repeated VaultShare shares = 2 [(gogoproto.castrepeated) = "VaultShares", (gogoproto.nullable) = false]; | ||||
| } | ||||
| 
 | ||||
| // VaultShare defines shares of a vault owned by a depositor. | ||||
| message VaultShare { | ||||
|   option (gogoproto.goproto_stringer) = false; | ||||
| 
 | ||||
|   string denom  = 1; | ||||
|   string amount = 2 [ | ||||
|     (cosmos_proto.scalar)  = "cosmos.Dec", | ||||
|     (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", | ||||
|     (gogoproto.nullable)   = false | ||||
|   ]; | ||||
| } | ||||
|  | ||||
| @ -33,7 +33,6 @@ func GetQueryCmd() *cobra.Command { | ||||
| 		queryParamsCmd(), | ||||
| 		queryVaultsCmd(), | ||||
| 		queryDepositsCmd(), | ||||
| 		queryTotalDepositedCmd(), | ||||
| 	} | ||||
| 
 | ||||
| 	for _, cmd := range cmds { | ||||
| @ -155,35 +154,3 @@ func queryDepositsCmd() *cobra.Command { | ||||
| 
 | ||||
| 	return cmd | ||||
| } | ||||
| 
 | ||||
| func queryTotalDepositedCmd() *cobra.Command { | ||||
| 	return &cobra.Command{ | ||||
| 		Use:   "total-deposited", | ||||
| 		Short: "get the current total deposited amount", | ||||
| 		Long:  "Get the current total deposited amount in the earn module vaults.", | ||||
| 		Args:  cobra.MaximumNArgs(1), | ||||
| 		Example: fmt.Sprintf(`%[1]s q %[2]s total-deposited | ||||
| %[1]s q %[2]s total-deposited usdx`, version.AppName, types.ModuleName), | ||||
| 		RunE: func(cmd *cobra.Command, args []string) error { | ||||
| 			clientCtx, err := client.GetClientQueryContext(cmd) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 
 | ||||
| 			queryClient := types.NewQueryClient(clientCtx) | ||||
| 
 | ||||
| 			vaultDenom := "" | ||||
| 			if len(args) > 1 { | ||||
| 				vaultDenom = args[0] | ||||
| 			} | ||||
| 
 | ||||
| 			req := types.NewQueryTotalDepositedRequest(vaultDenom) | ||||
| 			res, err := queryClient.TotalDeposited(context.Background(), req) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 
 | ||||
| 			return clientCtx.PrintProto(res) | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -21,14 +21,14 @@ func InitGenesis( | ||||
| 	} | ||||
| 
 | ||||
| 	// Total of all vault share records, vault record total supply should equal this
 | ||||
| 	vaultTotalSupplies := sdk.NewCoins() | ||||
| 	vaultTotalShares := types.NewVaultShares() | ||||
| 
 | ||||
| 	for _, vaultShareRecord := range gs.VaultShareRecords { | ||||
| 		if err := vaultShareRecord.Validate(); err != nil { | ||||
| 			panic(fmt.Sprintf("invalid vault share: %s", err)) | ||||
| 		} | ||||
| 
 | ||||
| 		vaultTotalSupplies = vaultTotalSupplies.Add(vaultShareRecord.AmountSupplied...) | ||||
| 		vaultTotalShares = vaultTotalShares.Add(vaultShareRecord.Shares...) | ||||
| 
 | ||||
| 		k.SetVaultShareRecord(ctx, vaultShareRecord) | ||||
| 	} | ||||
| @ -38,12 +38,12 @@ func InitGenesis( | ||||
| 			panic(fmt.Sprintf("invalid vault record: %s", err)) | ||||
| 		} | ||||
| 
 | ||||
| 		if !vaultRecord.TotalSupply.Amount.Equal(vaultTotalSupplies.AmountOf(vaultRecord.Denom)) { | ||||
| 		if !vaultRecord.TotalShares.Amount.Equal(vaultTotalShares.AmountOf(vaultRecord.TotalShares.Denom)) { | ||||
| 			panic(fmt.Sprintf( | ||||
| 				"invalid vault record total supply for %s, got %s but sum of vault shares is %s", | ||||
| 				vaultRecord.Denom, | ||||
| 				vaultRecord.TotalSupply.Amount, | ||||
| 				vaultTotalSupplies.AmountOf(vaultRecord.Denom), | ||||
| 				vaultRecord.TotalShares.Denom, | ||||
| 				vaultRecord.TotalShares.Amount, | ||||
| 				vaultTotalShares.AmountOf(vaultRecord.TotalShares.Denom), | ||||
| 			)) | ||||
| 		} | ||||
| 
 | ||||
|  | ||||
| @ -25,8 +25,9 @@ func (suite *genesisTestSuite) Test_InitGenesis_ValidationPanic() { | ||||
| 		}, | ||||
| 		types.VaultRecords{ | ||||
| 			{ | ||||
| 				Denom:       "", | ||||
| 				TotalSupply: sdk.NewInt64Coin("usdx", 0), | ||||
| 				TotalShares: types.VaultShare{ | ||||
| 					Denom: "", Amount: sdk.NewDec(1), | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		types.VaultShareRecords{}, | ||||
| @ -53,22 +54,26 @@ func (suite *genesisTestSuite) Test_InitAndExportGenesis() { | ||||
| 		}, | ||||
| 		types.VaultRecords{ | ||||
| 			types.VaultRecord{ | ||||
| 				Denom:       "ukava", | ||||
| 				TotalSupply: sdk.NewInt64Coin("ukava", 2000000), | ||||
| 				TotalShares: types.NewVaultShare("ukava", sdk.NewDec(3800000)), | ||||
| 			}, | ||||
| 			types.VaultRecord{ | ||||
| 				Denom:       "usdx", | ||||
| 				TotalSupply: sdk.NewInt64Coin("usdx", 1000000), | ||||
| 				TotalShares: types.NewVaultShare("usdx", sdk.NewDec(1000000)), | ||||
| 			}, | ||||
| 		}, | ||||
| 		types.VaultShareRecords{ | ||||
| 			types.VaultShareRecord{ | ||||
| 				Depositor:      depositor_1, | ||||
| 				AmountSupplied: sdk.NewCoins(sdk.NewInt64Coin("usdx", 500000), sdk.NewInt64Coin("ukava", 1900000)), | ||||
| 				Depositor: depositor_1, | ||||
| 				Shares: types.NewVaultShares( | ||||
| 					types.NewVaultShare("usdx", sdk.NewDec(500000)), | ||||
| 					types.NewVaultShare("ukava", sdk.NewDec(1900000)), | ||||
| 				), | ||||
| 			}, | ||||
| 			types.VaultShareRecord{ | ||||
| 				Depositor:      depositor_2, | ||||
| 				AmountSupplied: sdk.NewCoins(sdk.NewInt64Coin("usdx", 500000), sdk.NewInt64Coin("ukava", 100000)), | ||||
| 				Depositor: depositor_2, | ||||
| 				Shares: types.NewVaultShares( | ||||
| 					types.NewVaultShare("usdx", sdk.NewDec(500000)), | ||||
| 					types.NewVaultShare("ukava", sdk.NewDec(1900000)), | ||||
| 				), | ||||
| 			}, | ||||
| 		}, | ||||
| 	) | ||||
| @ -107,22 +112,26 @@ func (suite *genesisTestSuite) Test_Marshall() { | ||||
| 		}, | ||||
| 		types.VaultRecords{ | ||||
| 			types.VaultRecord{ | ||||
| 				Denom:       "ukava", | ||||
| 				TotalSupply: sdk.NewInt64Coin("ukava", 2000000), | ||||
| 				TotalShares: types.NewVaultShare("ukava", sdk.NewDec(3800000)), | ||||
| 			}, | ||||
| 			types.VaultRecord{ | ||||
| 				Denom:       "usdx", | ||||
| 				TotalSupply: sdk.NewInt64Coin("usdx", 1000000), | ||||
| 				TotalShares: types.NewVaultShare("usdx", sdk.NewDec(1000000)), | ||||
| 			}, | ||||
| 		}, | ||||
| 		types.VaultShareRecords{ | ||||
| 			types.VaultShareRecord{ | ||||
| 				Depositor:      depositor_1, | ||||
| 				AmountSupplied: sdk.NewCoins(sdk.NewInt64Coin("usdx", 500000), sdk.NewInt64Coin("ukava", 1900000)), | ||||
| 				Depositor: depositor_1, | ||||
| 				Shares: types.NewVaultShares( | ||||
| 					types.NewVaultShare("usdx", sdk.NewDec(500000)), | ||||
| 					types.NewVaultShare("ukava", sdk.NewDec(1900000)), | ||||
| 				), | ||||
| 			}, | ||||
| 			types.VaultShareRecord{ | ||||
| 				Depositor:      depositor_2, | ||||
| 				AmountSupplied: sdk.NewCoins(sdk.NewInt64Coin("usdx", 500000), sdk.NewInt64Coin("ukava", 100000)), | ||||
| 				Depositor: depositor_2, | ||||
| 				Shares: types.NewVaultShares( | ||||
| 					types.NewVaultShare("usdx", sdk.NewDec(500000)), | ||||
| 					types.NewVaultShare("ukava", sdk.NewDec(1900000)), | ||||
| 				), | ||||
| 			}, | ||||
| 		}, | ||||
| 	) | ||||
|  | ||||
| @ -1,6 +1,8 @@ | ||||
| package keeper | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	sdk "github.com/cosmos/cosmos-sdk/types" | ||||
| 	"github.com/kava-labs/kava/x/earn/types" | ||||
| ) | ||||
| @ -22,7 +24,7 @@ func (k *Keeper) Deposit(ctx sdk.Context, depositor sdk.AccAddress, amount sdk.C | ||||
| 	vaultRecord, found := k.GetVaultRecord(ctx, amount.Denom) | ||||
| 	if !found { | ||||
| 		// Create a new VaultRecord with 0 supply
 | ||||
| 		vaultRecord = types.NewVaultRecord(amount.Denom) | ||||
| 		vaultRecord = types.NewVaultRecord(amount.Denom, sdk.ZeroDec()) | ||||
| 	} | ||||
| 
 | ||||
| 	// Get the strategy for the vault
 | ||||
| @ -45,14 +47,17 @@ func (k *Keeper) Deposit(ctx sdk.Context, depositor sdk.AccAddress, amount sdk.C | ||||
| 	vaultShareRecord, found := k.GetVaultShareRecord(ctx, depositor) | ||||
| 	if !found { | ||||
| 		// Create a new empty VaultShareRecord with 0 supply
 | ||||
| 		vaultShareRecord = types.NewVaultShareRecord(depositor) | ||||
| 		vaultShareRecord = types.NewVaultShareRecord(depositor, types.NewVaultShares()) | ||||
| 	} | ||||
| 
 | ||||
| 	// Increment VaultRecord supply
 | ||||
| 	vaultRecord.TotalSupply = vaultRecord.TotalSupply.Add(amount) | ||||
| 	shares, err := k.ConvertToShares(ctx, amount) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to convert assets to shares: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Increment VaultShareRecord supply
 | ||||
| 	vaultShareRecord.AmountSupplied = vaultShareRecord.AmountSupplied.Add(amount) | ||||
| 	// Increment VaultRecord total shares and account shares
 | ||||
| 	vaultRecord.TotalShares = vaultRecord.TotalShares.Add(shares) | ||||
| 	vaultShareRecord.Shares = vaultShareRecord.Shares.Add(shares) | ||||
| 
 | ||||
| 	// Update VaultRecord and VaultShareRecord
 | ||||
| 	k.SetVaultRecord(ctx, vaultRecord) | ||||
| @ -68,6 +73,7 @@ func (k *Keeper) Deposit(ctx sdk.Context, depositor sdk.AccAddress, amount sdk.C | ||||
| 			types.EventTypeVaultDeposit, | ||||
| 			sdk.NewAttribute(types.AttributeKeyVaultDenom, amount.Denom), | ||||
| 			sdk.NewAttribute(types.AttributeKeyDepositor, depositor.String()), | ||||
| 			sdk.NewAttribute(types.AttributeKeyShares, shares.Amount.String()), | ||||
| 			sdk.NewAttribute(sdk.AttributeKeyAmount, amount.Amount.String()), | ||||
| 		), | ||||
| 	) | ||||
|  | ||||
| @ -51,7 +51,9 @@ func (suite *depositTestSuite) TestDeposit_Balances() { | ||||
| 	) | ||||
| 
 | ||||
| 	suite.VaultTotalValuesEqual(sdk.NewCoins(depositAmount)) | ||||
| 	suite.VaultTotalSuppliedEqual(sdk.NewCoins(depositAmount)) | ||||
| 	suite.VaultTotalSharesEqual(types.NewVaultShares( | ||||
| 		types.NewVaultShare(depositAmount.Denom, depositAmount.Amount.ToDec()), | ||||
| 	)) | ||||
| } | ||||
| 
 | ||||
| func (suite *depositTestSuite) TestDeposit_Exceed() { | ||||
|  | ||||
| @ -69,10 +69,10 @@ func (s queryServer) Vaults( | ||||
| 	vaults := []types.VaultResponse{} | ||||
| 
 | ||||
| 	for _, allowedVault := range queriedAllowedVaults { | ||||
| 		totalSupplied, err := s.keeper.GetVaultTotalSupplied(sdkCtx, allowedVault.Denom) | ||||
| 		if err != nil { | ||||
| 		vaultTotalShares, found := s.keeper.GetVaultTotalShares(sdkCtx, allowedVault.Denom) | ||||
| 		if !found { | ||||
| 			// No supply yet, no error just zero
 | ||||
| 			totalSupplied = sdk.NewCoin(allowedVault.Denom, sdk.ZeroInt()) | ||||
| 			vaultTotalShares = types.NewVaultShare(allowedVault.Denom, sdk.ZeroDec()) | ||||
| 		} | ||||
| 
 | ||||
| 		totalValue, err := s.keeper.GetVaultTotalValue(sdkCtx, allowedVault.Denom) | ||||
| @ -83,7 +83,7 @@ func (s queryServer) Vaults( | ||||
| 		vaults = append(vaults, types.VaultResponse{ | ||||
| 			Denom:         allowedVault.Denom, | ||||
| 			VaultStrategy: allowedVault.VaultStrategy, | ||||
| 			TotalSupplied: totalSupplied.Amount, | ||||
| 			TotalShares:   vaultTotalShares.Amount.String(), | ||||
| 			TotalValue:    totalValue.Amount, | ||||
| 		}) | ||||
| 	} | ||||
| @ -106,126 +106,159 @@ func (s queryServer) Deposits( | ||||
| 
 | ||||
| 	// 1. Specific account and specific vault
 | ||||
| 	if req.Owner != "" && req.Denom != "" { | ||||
| 		owner, err := sdk.AccAddressFromBech32(req.Owner) | ||||
| 		if err != nil { | ||||
| 			return nil, status.Error(codes.InvalidArgument, "Invalid address") | ||||
| 		} | ||||
| 
 | ||||
| 		shareRecord, found := s.keeper.GetVaultShareRecord(sdkCtx, owner) | ||||
| 		if !found { | ||||
| 			return nil, status.Error(codes.NotFound, "No deposit found for owner") | ||||
| 		} | ||||
| 
 | ||||
| 		if shareRecord.AmountSupplied.AmountOf(req.Denom).IsZero() { | ||||
| 			return nil, status.Error(codes.NotFound, fmt.Sprintf("No deposit for denom %s found for owner", req.Denom)) | ||||
| 		} | ||||
| 
 | ||||
| 		value, err := getAccountValue(sdkCtx, s.keeper, owner, shareRecord.AmountSupplied) | ||||
| 		if err != nil { | ||||
| 			return nil, status.Error(codes.InvalidArgument, err.Error()) | ||||
| 		} | ||||
| 
 | ||||
| 		return &types.QueryDepositsResponse{ | ||||
| 			Deposits: []types.DepositResponse{ | ||||
| 				{ | ||||
| 					Depositor:      owner.String(), | ||||
| 					AmountSupplied: shareRecord.AmountSupplied, | ||||
| 					Value:          value, | ||||
| 				}, | ||||
| 			}, | ||||
| 			Pagination: nil, | ||||
| 		}, nil | ||||
| 		return s.getAccountVaultDeposit(sdkCtx, req) | ||||
| 	} | ||||
| 
 | ||||
| 	// 2. All accounts, specific vault
 | ||||
| 	if req.Owner == "" && req.Denom != "" { | ||||
| 		_, found := s.keeper.GetVaultRecord(sdkCtx, req.Denom) | ||||
| 		if !found { | ||||
| 			return nil, status.Error(codes.NotFound, "Vault record for denom not found") | ||||
| 		} | ||||
| 
 | ||||
| 		deposits := []types.DepositResponse{} | ||||
| 		store := prefix.NewStore(sdkCtx.KVStore(s.keeper.key), types.VaultShareRecordKeyPrefix) | ||||
| 
 | ||||
| 		pageRes, err := query.FilteredPaginate( | ||||
| 			store, | ||||
| 			req.Pagination, | ||||
| 			func(key []byte, value []byte, accumulate bool) (bool, error) { | ||||
| 				var record types.VaultShareRecord | ||||
| 				err := s.keeper.cdc.Unmarshal(value, &record) | ||||
| 				if err != nil { | ||||
| 					return false, err | ||||
| 				} | ||||
| 
 | ||||
| 				// Only those that have amount of requested denom
 | ||||
| 				if record.AmountSupplied.AmountOf(req.Denom).IsZero() { | ||||
| 					// inform paginate that there was no match on this key
 | ||||
| 					return false, nil | ||||
| 				} | ||||
| 
 | ||||
| 				if accumulate { | ||||
| 					accValue, err := getAccountValue(sdkCtx, s.keeper, record.Depositor, record.AmountSupplied) | ||||
| 					if err != nil { | ||||
| 						return false, err | ||||
| 					} | ||||
| 
 | ||||
| 					// only add to results if paginate tells us to
 | ||||
| 					deposits = append(deposits, types.DepositResponse{ | ||||
| 						Depositor:      record.Depositor.String(), | ||||
| 						AmountSupplied: record.AmountSupplied, | ||||
| 						Value:          accValue, | ||||
| 					}) | ||||
| 				} | ||||
| 
 | ||||
| 				// inform paginate that were was a match on this key
 | ||||
| 				return true, nil | ||||
| 			}, | ||||
| 		) | ||||
| 
 | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 
 | ||||
| 		return &types.QueryDepositsResponse{ | ||||
| 			Deposits:   deposits, | ||||
| 			Pagination: pageRes, | ||||
| 		}, nil | ||||
| 		return s.getVaultAllDeposits(sdkCtx, req) | ||||
| 	} | ||||
| 
 | ||||
| 	// 3. Specific account, all vaults
 | ||||
| 	if req.Owner != "" && req.Denom == "" { | ||||
| 		owner, err := sdk.AccAddressFromBech32(req.Owner) | ||||
| 		if err != nil { | ||||
| 			return nil, status.Error(codes.InvalidArgument, "Invalid address") | ||||
| 		} | ||||
| 
 | ||||
| 		deposits := []types.DepositResponse{} | ||||
| 
 | ||||
| 		accountShare, found := s.keeper.GetVaultShareRecord(sdkCtx, owner) | ||||
| 		if !found { | ||||
| 			return nil, status.Error(codes.NotFound, "No deposit found for owner") | ||||
| 		} | ||||
| 
 | ||||
| 		value, err := getAccountValue(sdkCtx, s.keeper, owner, accountShare.AmountSupplied) | ||||
| 		if err != nil { | ||||
| 			return nil, status.Error(codes.InvalidArgument, err.Error()) | ||||
| 		} | ||||
| 
 | ||||
| 		deposits = append(deposits, types.DepositResponse{ | ||||
| 			Depositor:      owner.String(), | ||||
| 			AmountSupplied: accountShare.AmountSupplied, | ||||
| 			Value:          value, | ||||
| 		}) | ||||
| 
 | ||||
| 		return &types.QueryDepositsResponse{ | ||||
| 			Deposits:   deposits, | ||||
| 			Pagination: nil, | ||||
| 		}, nil | ||||
| 		return s.getAccountAllDeposits(sdkCtx, req) | ||||
| 	} | ||||
| 
 | ||||
| 	// 4. All accounts, all vaults
 | ||||
| 	return s.getAllDeposits(sdkCtx, req) | ||||
| } | ||||
| 
 | ||||
| // getAccountVaultDeposit returns deposits for a specific vault and a specific
 | ||||
| // account
 | ||||
| func (s queryServer) getAccountVaultDeposit( | ||||
| 	ctx sdk.Context, | ||||
| 	req *types.QueryDepositsRequest, | ||||
| ) (*types.QueryDepositsResponse, error) { | ||||
| 	owner, err := sdk.AccAddressFromBech32(req.Owner) | ||||
| 	if err != nil { | ||||
| 		return nil, status.Error(codes.InvalidArgument, "Invalid address") | ||||
| 	} | ||||
| 
 | ||||
| 	shareRecord, found := s.keeper.GetVaultShareRecord(ctx, owner) | ||||
| 	if !found { | ||||
| 		return nil, status.Error(codes.NotFound, "No deposit found for owner") | ||||
| 	} | ||||
| 
 | ||||
| 	if shareRecord.Shares.AmountOf(req.Denom).IsZero() { | ||||
| 		return nil, status.Error(codes.NotFound, fmt.Sprintf("No deposit for denom %s found for owner", req.Denom)) | ||||
| 	} | ||||
| 
 | ||||
| 	value, err := getAccountValue(ctx, s.keeper, owner, shareRecord.Shares) | ||||
| 	if err != nil { | ||||
| 		return nil, status.Error(codes.InvalidArgument, err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	return &types.QueryDepositsResponse{ | ||||
| 		Deposits: []types.DepositResponse{ | ||||
| 			{ | ||||
| 				Depositor: owner.String(), | ||||
| 				Shares:    shareRecord.Shares, | ||||
| 				Value:     value, | ||||
| 			}, | ||||
| 		}, | ||||
| 		Pagination: nil, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // getVaultAllDeposits returns all deposits for a specific vault
 | ||||
| func (s queryServer) getVaultAllDeposits( | ||||
| 	ctx sdk.Context, | ||||
| 	req *types.QueryDepositsRequest, | ||||
| ) (*types.QueryDepositsResponse, error) { | ||||
| 	_, found := s.keeper.GetVaultRecord(ctx, req.Denom) | ||||
| 	if !found { | ||||
| 		return nil, status.Error(codes.NotFound, "Vault record for denom not found") | ||||
| 	} | ||||
| 
 | ||||
| 	deposits := []types.DepositResponse{} | ||||
| 	store := prefix.NewStore(sdkCtx.KVStore(s.keeper.key), types.VaultShareRecordKeyPrefix) | ||||
| 	store := prefix.NewStore(ctx.KVStore(s.keeper.key), types.VaultShareRecordKeyPrefix) | ||||
| 
 | ||||
| 	pageRes, err := query.FilteredPaginate( | ||||
| 		store, | ||||
| 		req.Pagination, | ||||
| 		func(key []byte, value []byte, accumulate bool) (bool, error) { | ||||
| 			var record types.VaultShareRecord | ||||
| 			err := s.keeper.cdc.Unmarshal(value, &record) | ||||
| 			if err != nil { | ||||
| 				return false, err | ||||
| 			} | ||||
| 
 | ||||
| 			// Only those that have amount of requested denom
 | ||||
| 			if record.Shares.AmountOf(req.Denom).IsZero() { | ||||
| 				// inform paginate that there was no match on this key
 | ||||
| 				return false, nil | ||||
| 			} | ||||
| 
 | ||||
| 			if accumulate { | ||||
| 				accValue, err := getAccountValue(ctx, s.keeper, record.Depositor, record.Shares) | ||||
| 				if err != nil { | ||||
| 					return false, err | ||||
| 				} | ||||
| 
 | ||||
| 				// only add to results if paginate tells us to
 | ||||
| 				deposits = append(deposits, types.DepositResponse{ | ||||
| 					Depositor: record.Depositor.String(), | ||||
| 					Shares:    record.Shares, | ||||
| 					Value:     accValue, | ||||
| 				}) | ||||
| 			} | ||||
| 
 | ||||
| 			// inform paginate that were was a match on this key
 | ||||
| 			return true, nil | ||||
| 		}, | ||||
| 	) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return &types.QueryDepositsResponse{ | ||||
| 		Deposits:   deposits, | ||||
| 		Pagination: pageRes, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // getAccountAllDeposits returns deposits for all vaults for a specific account
 | ||||
| func (s queryServer) getAccountAllDeposits( | ||||
| 	ctx sdk.Context, | ||||
| 	req *types.QueryDepositsRequest, | ||||
| ) (*types.QueryDepositsResponse, error) { | ||||
| 	owner, err := sdk.AccAddressFromBech32(req.Owner) | ||||
| 	if err != nil { | ||||
| 		return nil, status.Error(codes.InvalidArgument, "Invalid address") | ||||
| 	} | ||||
| 
 | ||||
| 	deposits := []types.DepositResponse{} | ||||
| 
 | ||||
| 	accountShare, found := s.keeper.GetVaultShareRecord(ctx, owner) | ||||
| 	if !found { | ||||
| 		return nil, status.Error(codes.NotFound, "No deposit found for owner") | ||||
| 	} | ||||
| 
 | ||||
| 	value, err := getAccountValue(ctx, s.keeper, owner, accountShare.Shares) | ||||
| 	if err != nil { | ||||
| 		return nil, status.Error(codes.InvalidArgument, err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	deposits = append(deposits, types.DepositResponse{ | ||||
| 		Depositor: owner.String(), | ||||
| 		Shares:    accountShare.Shares, | ||||
| 		Value:     value, | ||||
| 	}) | ||||
| 
 | ||||
| 	return &types.QueryDepositsResponse{ | ||||
| 		Deposits:   deposits, | ||||
| 		Pagination: nil, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // getAllDeposits returns all deposits for all vaults
 | ||||
| func (s queryServer) getAllDeposits( | ||||
| 	ctx sdk.Context, | ||||
| 	req *types.QueryDepositsRequest, | ||||
| ) (*types.QueryDepositsResponse, error) { | ||||
| 	deposits := []types.DepositResponse{} | ||||
| 	store := prefix.NewStore(ctx.KVStore(s.keeper.key), types.VaultShareRecordKeyPrefix) | ||||
| 
 | ||||
| 	pageRes, err := query.Paginate( | ||||
| 		store, | ||||
| @ -237,16 +270,16 @@ func (s queryServer) Deposits( | ||||
| 				return err | ||||
| 			} | ||||
| 
 | ||||
| 			accValue, err := getAccountValue(sdkCtx, s.keeper, record.Depositor, record.AmountSupplied) | ||||
| 			accValue, err := getAccountValue(ctx, s.keeper, record.Depositor, record.Shares) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 
 | ||||
| 			// only add to results if paginate tells us to
 | ||||
| 			deposits = append(deposits, types.DepositResponse{ | ||||
| 				Depositor:      record.Depositor.String(), | ||||
| 				AmountSupplied: record.AmountSupplied, | ||||
| 				Value:          accValue, | ||||
| 				Depositor: record.Depositor.String(), | ||||
| 				Shares:    record.Shares, | ||||
| 				Value:     accValue, | ||||
| 			}) | ||||
| 
 | ||||
| 			return nil | ||||
| @ -263,56 +296,21 @@ func (s queryServer) Deposits( | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // Deposits implements the gRPC service handler for querying x/earn deposits.
 | ||||
| func (s queryServer) TotalDeposited( | ||||
| 	ctx context.Context, | ||||
| 	req *types.QueryTotalDepositedRequest, | ||||
| ) (*types.QueryTotalDepositedResponse, error) { | ||||
| 	if req == nil { | ||||
| 		return nil, status.Errorf(codes.InvalidArgument, "empty request") | ||||
| 	} | ||||
| 
 | ||||
| 	sdkCtx := sdk.UnwrapSDKContext(ctx) | ||||
| 
 | ||||
| 	// Single vault
 | ||||
| 	if req.Denom != "" { | ||||
| 		totalSupplied, err := s.keeper.GetVaultTotalSupplied(sdkCtx, req.Denom) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 
 | ||||
| 		return &types.QueryTotalDepositedResponse{ | ||||
| 			SuppliedCoins: sdk.NewCoins(totalSupplied), | ||||
| 		}, nil | ||||
| 	} | ||||
| 
 | ||||
| 	coins := sdk.NewCoins() | ||||
| 	vaults := s.keeper.GetAllVaultRecords(sdkCtx) | ||||
| 
 | ||||
| 	for _, vault := range vaults { | ||||
| 		coins = coins.Add(vault.TotalSupply) | ||||
| 	} | ||||
| 
 | ||||
| 	return &types.QueryTotalDepositedResponse{ | ||||
| 		SuppliedCoins: coins, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func getAccountValue( | ||||
| 	ctx sdk.Context, | ||||
| 	keeper Keeper, | ||||
| 	account sdk.AccAddress, | ||||
| 	supplied sdk.Coins, | ||||
| 	shares types.VaultShares, | ||||
| ) (sdk.Coins, error) { | ||||
| 	value := sdk.NewCoins() | ||||
| 
 | ||||
| 	for _, coin := range supplied { | ||||
| 		accValue, err := keeper.GetVaultAccountValue(ctx, coin.Denom, account) | ||||
| 	for _, share := range shares { | ||||
| 		accValue, err := keeper.GetVaultAccountValue(ctx, share.Denom, account) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 
 | ||||
| 		value = value.Add(sdk.NewCoin(coin.Denom, accValue.Amount)) | ||||
| 		value = value.Add(sdk.NewCoin(share.Denom, accValue.Amount)) | ||||
| 	} | ||||
| 
 | ||||
| 	return value, nil | ||||
|  | ||||
| @ -70,7 +70,7 @@ func (suite *grpcQueryTestSuite) TestVaults_ZeroSupply() { | ||||
| 			types.VaultResponse{ | ||||
| 				Denom:         "usdx", | ||||
| 				VaultStrategy: types.STRATEGY_TYPE_HARD, | ||||
| 				TotalSupplied: sdk.NewInt(0), | ||||
| 				TotalShares:   sdk.NewDec(0).String(), | ||||
| 				TotalValue:    sdk.NewInt(0), | ||||
| 			}, | ||||
| 			res.Vaults[0], | ||||
| @ -86,13 +86,13 @@ func (suite *grpcQueryTestSuite) TestVaults_ZeroSupply() { | ||||
| 				{ | ||||
| 					Denom:         "usdx", | ||||
| 					VaultStrategy: types.STRATEGY_TYPE_HARD, | ||||
| 					TotalSupplied: sdk.NewInt(0), | ||||
| 					TotalShares:   sdk.NewDec(0).String(), | ||||
| 					TotalValue:    sdk.NewInt(0), | ||||
| 				}, | ||||
| 				{ | ||||
| 					Denom:         "busd", | ||||
| 					VaultStrategy: types.STRATEGY_TYPE_HARD, | ||||
| 					TotalSupplied: sdk.NewInt(0), | ||||
| 					TotalShares:   sdk.NewDec(0).String(), | ||||
| 					TotalValue:    sdk.NewInt(0), | ||||
| 				}, | ||||
| 			}, | ||||
| @ -121,7 +121,7 @@ func (suite *grpcQueryTestSuite) TestVaults_WithSupply() { | ||||
| 		types.VaultResponse{ | ||||
| 			Denom:         "usdx", | ||||
| 			VaultStrategy: types.STRATEGY_TYPE_HARD, | ||||
| 			TotalSupplied: depositAmount.Amount, | ||||
| 			TotalShares:   depositAmount.Amount.ToDec().String(), | ||||
| 			TotalValue:    depositAmount.Amount, | ||||
| 		}, | ||||
| 		res.Vaults[0], | ||||
| @ -183,8 +183,11 @@ func (suite *grpcQueryTestSuite) TestDeposits() { | ||||
| 				{ | ||||
| 					Depositor: acc1.String(), | ||||
| 					// Still includes all deposits
 | ||||
| 					AmountSupplied: sdk.NewCoins(deposit1Amount, deposit2Amount), | ||||
| 					Value:          sdk.NewCoins(deposit1Amount, deposit2Amount), | ||||
| 					Shares: types.NewVaultShares( | ||||
| 						types.NewVaultShare(deposit1Amount.Denom, deposit1Amount.Amount.ToDec()), | ||||
| 						types.NewVaultShare(deposit2Amount.Denom, deposit2Amount.Amount.ToDec()), | ||||
| 					), | ||||
| 					Value: sdk.NewCoins(deposit1Amount, deposit2Amount), | ||||
| 				}, | ||||
| 			}, | ||||
| 			res.Deposits, | ||||
| @ -213,9 +216,12 @@ func (suite *grpcQueryTestSuite) TestDeposits() { | ||||
| 		suite.Require().ElementsMatch( | ||||
| 			[]types.DepositResponse{ | ||||
| 				{ | ||||
| 					Depositor:      acc1.String(), | ||||
| 					AmountSupplied: sdk.NewCoins(deposit1Amount, deposit2Amount), | ||||
| 					Value:          sdk.NewCoins(deposit1Amount, deposit2Amount), | ||||
| 					Depositor: acc1.String(), | ||||
| 					Shares: types.NewVaultShares( | ||||
| 						types.NewVaultShare(deposit1Amount.Denom, deposit1Amount.Amount.ToDec()), | ||||
| 						types.NewVaultShare(deposit2Amount.Denom, deposit2Amount.Amount.ToDec()), | ||||
| 					), | ||||
| 					Value: sdk.NewCoins(deposit1Amount, deposit2Amount), | ||||
| 				}, | ||||
| 			}, | ||||
| 			res.Deposits, | ||||
| @ -233,9 +239,12 @@ func (suite *grpcQueryTestSuite) TestDeposits() { | ||||
| 		suite.Require().ElementsMatch( | ||||
| 			[]types.DepositResponse{ | ||||
| 				{ | ||||
| 					Depositor:      acc2.String(), | ||||
| 					AmountSupplied: sdk.NewCoins(deposit1Amount, deposit3Amount), | ||||
| 					Value:          sdk.NewCoins(deposit1Amount, deposit3Amount), | ||||
| 					Depositor: acc2.String(), | ||||
| 					Shares: types.NewVaultShares( | ||||
| 						types.NewVaultShare(deposit1Amount.Denom, deposit1Amount.Amount.ToDec()), | ||||
| 						types.NewVaultShare(deposit3Amount.Denom, deposit3Amount.Amount.ToDec()), | ||||
| 					), | ||||
| 					Value: sdk.NewCoins(deposit1Amount, deposit3Amount), | ||||
| 				}, | ||||
| 			}, | ||||
| 			res.Deposits, | ||||
| @ -253,14 +262,20 @@ func (suite *grpcQueryTestSuite) TestDeposits() { | ||||
| 		suite.Require().ElementsMatchf( | ||||
| 			[]types.DepositResponse{ | ||||
| 				{ | ||||
| 					Depositor:      acc1.String(), | ||||
| 					AmountSupplied: sdk.NewCoins(deposit1Amount, deposit2Amount), | ||||
| 					Value:          sdk.NewCoins(deposit1Amount, deposit2Amount), | ||||
| 					Depositor: acc1.String(), | ||||
| 					Shares: types.NewVaultShares( | ||||
| 						types.NewVaultShare(deposit1Amount.Denom, deposit1Amount.Amount.ToDec()), | ||||
| 						types.NewVaultShare(deposit2Amount.Denom, deposit2Amount.Amount.ToDec()), | ||||
| 					), | ||||
| 					Value: sdk.NewCoins(deposit1Amount, deposit2Amount), | ||||
| 				}, | ||||
| 				{ | ||||
| 					Depositor:      acc2.String(), | ||||
| 					AmountSupplied: sdk.NewCoins(deposit1Amount, deposit3Amount), | ||||
| 					Value:          sdk.NewCoins(deposit1Amount, deposit3Amount), | ||||
| 					Depositor: acc2.String(), | ||||
| 					Shares: types.NewVaultShares( | ||||
| 						types.NewVaultShare(deposit1Amount.Denom, deposit1Amount.Amount.ToDec()), | ||||
| 						types.NewVaultShare(deposit3Amount.Denom, deposit3Amount.Amount.ToDec()), | ||||
| 					), | ||||
| 					Value: sdk.NewCoins(deposit1Amount, deposit3Amount), | ||||
| 				}, | ||||
| 			}, | ||||
| 			res.Deposits, | ||||
| @ -294,103 +309,3 @@ func (suite *grpcQueryTestSuite) TestDeposits_InvalidAddress() { | ||||
| 	suite.Require().Error(err) | ||||
| 	suite.Require().ErrorIs(err, status.Error(codes.InvalidArgument, "Invalid address")) | ||||
| } | ||||
| 
 | ||||
| func (suite *grpcQueryTestSuite) TestTotalDeposited_NoSupply() { | ||||
| 	// Add vaults
 | ||||
| 	suite.CreateVault("usdx", types.STRATEGY_TYPE_HARD) | ||||
| 	suite.CreateVault("cats", types.STRATEGY_TYPE_HARD) | ||||
| 
 | ||||
| 	res, err := suite.queryClient.TotalDeposited(context.Background(), types.NewQueryTotalDepositedRequest("")) | ||||
| 	suite.Require().NoError(err) | ||||
| 	suite.Require().True(res.SuppliedCoins.Empty(), "supplied coins should be empty") | ||||
| } | ||||
| 
 | ||||
| func (suite *grpcQueryTestSuite) TestTotalDeposited_All() { | ||||
| 	vault1Denom := "usdx" | ||||
| 	vault2Denom := "busd" | ||||
| 
 | ||||
| 	// Add vaults
 | ||||
| 	suite.CreateVault(vault1Denom, types.STRATEGY_TYPE_HARD) | ||||
| 	suite.CreateVault(vault2Denom, types.STRATEGY_TYPE_HARD) | ||||
| 
 | ||||
| 	startBalance := sdk.NewCoins( | ||||
| 		sdk.NewInt64Coin(vault1Denom, 1000), | ||||
| 		sdk.NewInt64Coin(vault2Denom, 1000), | ||||
| 	) | ||||
| 	deposit1Amount := sdk.NewInt64Coin(vault1Denom, 100) | ||||
| 	deposit2Amount := sdk.NewInt64Coin(vault2Denom, 100) | ||||
| 
 | ||||
| 	acc := suite.CreateAccount(startBalance, 0).GetAddress() | ||||
| 	err := suite.Keeper.Deposit(suite.Ctx, acc, deposit1Amount) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	res, err := suite.queryClient.TotalDeposited( | ||||
| 		context.Background(), | ||||
| 		types.NewQueryTotalDepositedRequest(""), // query all
 | ||||
| 	) | ||||
| 	suite.Require().NoError(err) | ||||
| 	suite.Require().Equal( | ||||
| 		sdk.NewCoins(deposit1Amount), | ||||
| 		res.SuppliedCoins, | ||||
| 		"supplied coins should be sum of all supplied coins", | ||||
| 	) | ||||
| 
 | ||||
| 	err = suite.Keeper.Deposit(suite.Ctx, acc, deposit2Amount) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	res, err = suite.queryClient.TotalDeposited( | ||||
| 		context.Background(), | ||||
| 		types.NewQueryTotalDepositedRequest(""), // query all
 | ||||
| 	) | ||||
| 	suite.Require().NoError(err) | ||||
| 	suite.Require().Equal( | ||||
| 		sdk.NewCoins(deposit1Amount, deposit2Amount), | ||||
| 		res.SuppliedCoins, | ||||
| 		"supplied coins should be sum of all supplied coins for multiple coins", | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| func (suite *grpcQueryTestSuite) TestTotalDeposited_Single() { | ||||
| 	vault1Denom := "usdx" | ||||
| 	vault2Denom := "busd" | ||||
| 
 | ||||
| 	// Add vaults
 | ||||
| 	suite.CreateVault(vault1Denom, types.STRATEGY_TYPE_HARD) | ||||
| 	suite.CreateVault(vault2Denom, types.STRATEGY_TYPE_HARD) | ||||
| 
 | ||||
| 	startBalance := sdk.NewCoins( | ||||
| 		sdk.NewInt64Coin(vault1Denom, 1000), | ||||
| 		sdk.NewInt64Coin(vault2Denom, 1000), | ||||
| 	) | ||||
| 	deposit1Amount := sdk.NewInt64Coin(vault1Denom, 100) | ||||
| 	deposit2Amount := sdk.NewInt64Coin(vault2Denom, 100) | ||||
| 
 | ||||
| 	acc := suite.CreateAccount(startBalance, 0).GetAddress() | ||||
| 	err := suite.Keeper.Deposit(suite.Ctx, acc, deposit1Amount) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	err = suite.Keeper.Deposit(suite.Ctx, acc, deposit2Amount) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	res, err := suite.queryClient.TotalDeposited( | ||||
| 		context.Background(), | ||||
| 		types.NewQueryTotalDepositedRequest(vault1Denom), | ||||
| 	) | ||||
| 	suite.Require().NoError(err) | ||||
| 	suite.Require().Equal( | ||||
| 		sdk.NewCoins(deposit1Amount), | ||||
| 		res.SuppliedCoins, | ||||
| 		"should only contain queried denom", | ||||
| 	) | ||||
| 
 | ||||
| 	res, err = suite.queryClient.TotalDeposited( | ||||
| 		context.Background(), | ||||
| 		types.NewQueryTotalDepositedRequest(vault2Denom), | ||||
| 	) | ||||
| 	suite.Require().NoError(err) | ||||
| 	suite.Require().Equal( | ||||
| 		sdk.NewCoins(deposit2Amount), | ||||
| 		res.SuppliedCoins, | ||||
| 		"should only contain queried denom", | ||||
| 	) | ||||
| } | ||||
|  | ||||
| @ -68,6 +68,8 @@ func (suite *msgServerTestSuite) TestDeposit() { | ||||
| 			types.EventTypeVaultDeposit, | ||||
| 			sdk.NewAttribute(types.AttributeKeyVaultDenom, depositAmount.Denom), | ||||
| 			sdk.NewAttribute(types.AttributeKeyDepositor, acc.GetAddress().String()), | ||||
| 			// Shares 1:1 to amount
 | ||||
| 			sdk.NewAttribute(types.AttributeKeyShares, depositAmount.Amount.ToDec().String()), | ||||
| 			sdk.NewAttribute(sdk.AttributeKeyAmount, depositAmount.Amount.String()), | ||||
| 		), | ||||
| 	) | ||||
| @ -120,6 +122,7 @@ func (suite *msgServerTestSuite) TestWithdraw() { | ||||
| 			types.EventTypeVaultWithdraw, | ||||
| 			sdk.NewAttribute(types.AttributeKeyVaultDenom, depositAmount.Denom), | ||||
| 			sdk.NewAttribute(types.AttributeKeyOwner, acc.GetAddress().String()), | ||||
| 			sdk.NewAttribute(types.AttributeKeyShares, depositAmount.Amount.ToDec().String()), | ||||
| 			sdk.NewAttribute(sdk.AttributeKeyAmount, depositAmount.Amount.String()), | ||||
| 		), | ||||
| 	) | ||||
|  | ||||
| @ -45,7 +45,9 @@ func (suite *strategyHardTestSuite) TestDeposit_SingleAcc() { | ||||
| 
 | ||||
| 	suite.HardDepositAmountEqual(sdk.NewCoins(depositAmount)) | ||||
| 	suite.VaultTotalValuesEqual(sdk.NewCoins(depositAmount)) | ||||
| 	suite.VaultTotalSuppliedEqual(sdk.NewCoins(depositAmount)) | ||||
| 	suite.VaultTotalSharesEqual(types.NewVaultShares( | ||||
| 		types.NewVaultShare(depositAmount.Denom, depositAmount.Amount.ToDec()), | ||||
| 	)) | ||||
| 
 | ||||
| 	// Query vault total
 | ||||
| 	totalValue, err := suite.Keeper.GetVaultTotalValue(suite.Ctx, vaultDenom) | ||||
| @ -70,10 +72,12 @@ func (suite *strategyHardTestSuite) TestDeposit_SingleAcc_MultipleDeposits() { | ||||
| 	err = suite.Keeper.Deposit(suite.Ctx, acc.GetAddress(), depositAmount) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	expectedVaultBalance := sdk.NewCoins(depositAmount.Add(depositAmount)) | ||||
| 	suite.HardDepositAmountEqual(expectedVaultBalance) | ||||
| 	suite.VaultTotalValuesEqual(expectedVaultBalance) | ||||
| 	suite.VaultTotalSuppliedEqual(expectedVaultBalance) | ||||
| 	expectedVaultBalance := depositAmount.Add(depositAmount) | ||||
| 	suite.HardDepositAmountEqual(sdk.NewCoins(expectedVaultBalance)) | ||||
| 	suite.VaultTotalValuesEqual(sdk.NewCoins(expectedVaultBalance)) | ||||
| 	suite.VaultTotalSharesEqual(types.NewVaultShares( | ||||
| 		types.NewVaultShare(expectedVaultBalance.Denom, expectedVaultBalance.Amount.ToDec()), | ||||
| 	)) | ||||
| 
 | ||||
| 	// Query vault total
 | ||||
| 	totalValue, err := suite.Keeper.GetVaultTotalValue(suite.Ctx, vaultDenom) | ||||
| @ -107,7 +111,9 @@ func (suite *strategyHardTestSuite) TestDeposit_MultipleAcc_MultipleDeposits() { | ||||
| 
 | ||||
| 	suite.HardDepositAmountEqual(sdk.NewCoins(expectedTotalValue)) | ||||
| 	suite.VaultTotalValuesEqual(sdk.NewCoins(expectedTotalValue)) | ||||
| 	suite.VaultTotalSuppliedEqual(sdk.NewCoins(expectedTotalValue)) | ||||
| 	suite.VaultTotalSharesEqual(types.NewVaultShares( | ||||
| 		types.NewVaultShare(expectedTotalValue.Denom, expectedTotalValue.Amount.ToDec()), | ||||
| 	)) | ||||
| 
 | ||||
| 	// Query vault total
 | ||||
| 	totalValue, err := suite.Keeper.GetVaultTotalValue(suite.Ctx, vaultDenom) | ||||
| @ -183,7 +189,7 @@ func (suite *strategyHardTestSuite) TestWithdraw() { | ||||
| 
 | ||||
| 	suite.HardDepositAmountEqual(sdk.NewCoins()) | ||||
| 	suite.VaultTotalValuesEqual(sdk.NewCoins()) | ||||
| 	suite.VaultTotalSuppliedEqual(sdk.NewCoins()) | ||||
| 	suite.VaultTotalSharesEqual(types.NewVaultShares()) | ||||
| 
 | ||||
| 	totalValue, err = suite.Keeper.GetVaultTotalValue(suite.Ctx, vaultDenom) | ||||
| 	suite.Require().NoError(err) | ||||
| @ -232,26 +238,32 @@ func (suite *strategyHardTestSuite) TestWithdraw_WithAccumulatedHard() { | ||||
| 
 | ||||
| 	suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD) | ||||
| 
 | ||||
| 	// Deposits from 2 accounts
 | ||||
| 	// Deposits accounts
 | ||||
| 	acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0).GetAddress() | ||||
| 	acc2 := suite.CreateAccount(sdk.NewCoins(startBalance), 1).GetAddress() | ||||
| 
 | ||||
| 	err := suite.Keeper.Deposit(suite.Ctx, acc, depositAmount) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	// Deposit from acc2 so the vault doesn't get deleted when withdrawing
 | ||||
| 	err = suite.Keeper.Deposit(suite.Ctx, acc2, depositAmount) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	// Direct hard deposit from module account to increase vault value
 | ||||
| 	suite.App.FundModuleAccount(suite.Ctx, types.ModuleName, sdk.NewCoins(sdk.NewInt64Coin(vaultDenom, 10))) | ||||
| 	suite.App.FundModuleAccount(suite.Ctx, types.ModuleName, sdk.NewCoins(sdk.NewInt64Coin(vaultDenom, 20))) | ||||
| 	macc := suite.AccountKeeper.GetModuleAccount(suite.Ctx, types.ModuleName) | ||||
| 	suite.HardKeeper.Deposit(suite.Ctx, macc.GetAddress(), sdk.NewCoins(sdk.NewInt64Coin(vaultDenom, 10))) | ||||
| 	suite.HardKeeper.Deposit(suite.Ctx, macc.GetAddress(), sdk.NewCoins(sdk.NewInt64Coin(vaultDenom, 20))) | ||||
| 
 | ||||
| 	// Query account value
 | ||||
| 	accValue, err := suite.Keeper.GetVaultAccountValue(suite.Ctx, vaultDenom, acc) | ||||
| 	suite.Require().NoError(err) | ||||
| 	suite.Equal(depositAmount.AddAmount(sdk.NewInt(10)), accValue) | ||||
| 
 | ||||
| 	// Withdraw 10, 10 remaining
 | ||||
| 	// Withdraw 100, 10 remaining
 | ||||
| 	err = suite.Keeper.Withdraw(suite.Ctx, acc, depositAmount) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	// Withdraw again -- too much
 | ||||
| 	// Withdraw 100 again -- too much
 | ||||
| 	err = suite.Keeper.Withdraw(suite.Ctx, acc, depositAmount) | ||||
| 	suite.Require().Error(err) | ||||
| 	suite.Require().ErrorIs( | ||||
| @ -268,7 +280,209 @@ func (suite *strategyHardTestSuite) TestWithdraw_WithAccumulatedHard() { | ||||
| 	err = suite.Keeper.Withdraw(suite.Ctx, acc, sdk.NewCoin(vaultDenom, sdk.NewInt(5))) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	_, err = suite.Keeper.GetVaultAccountValue(suite.Ctx, vaultDenom, acc) | ||||
| 	suite.Require().Error(err) | ||||
| 	suite.Require().ErrorIs(err, types.ErrVaultRecordNotFound) | ||||
| 	accValue, err = suite.Keeper.GetVaultAccountValue(suite.Ctx, vaultDenom, acc) | ||||
| 	suite.Require().Errorf( | ||||
| 		err, | ||||
| 		"account should be deleted when all shares withdrawn but has %s value still", | ||||
| 		accValue, | ||||
| 	) | ||||
| 	suite.Require().Equal("account vault share record for usdx not found", err.Error()) | ||||
| } | ||||
| 
 | ||||
| func (suite *strategyHardTestSuite) TestAccountShares() { | ||||
| 	vaultDenom := "usdx" | ||||
| 	startBalance := sdk.NewInt64Coin(vaultDenom, 1000) | ||||
| 	depositAmount := sdk.NewInt64Coin(vaultDenom, 100) | ||||
| 	suite.App.FundModuleAccount(suite.Ctx, types.ModuleName, sdk.NewCoins(sdk.NewInt64Coin(vaultDenom, 1000))) | ||||
| 
 | ||||
| 	suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD) | ||||
| 
 | ||||
| 	// Deposit from account1
 | ||||
| 	acc1 := suite.CreateAccount(sdk.NewCoins(startBalance), 0).GetAddress() | ||||
| 	acc2 := suite.CreateAccount(sdk.NewCoins(startBalance), 1).GetAddress() | ||||
| 
 | ||||
| 	// 1. acc1 deposit 100
 | ||||
| 	err := suite.Keeper.Deposit(suite.Ctx, acc1, depositAmount) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	acc1Shares, found := suite.Keeper.GetVaultAccountShares(suite.Ctx, acc1) | ||||
| 	suite.Require().True(found) | ||||
| 	suite.Equal(sdk.NewDec(100), acc1Shares.AmountOf(vaultDenom), "initial deposit 1:1 shares") | ||||
| 
 | ||||
| 	// 2. Direct hard deposit from module account to increase vault value
 | ||||
| 	// Total value: 100 -> 110
 | ||||
| 	macc := suite.AccountKeeper.GetModuleAccount(suite.Ctx, types.ModuleName) | ||||
| 	err = suite.HardKeeper.Deposit(suite.Ctx, macc.GetAddress(), sdk.NewCoins(sdk.NewInt64Coin(vaultDenom, 10))) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	// 2. acc2 deposit 100
 | ||||
| 	// share price is 10% more expensive now
 | ||||
| 	// hard 110 -> 210
 | ||||
| 	err = suite.Keeper.Deposit(suite.Ctx, acc2, depositAmount) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	// 100 * 100 / 210 = 47.619047619 shares
 | ||||
| 	// 2.1 price * 47.619047619 = 99.9999999999
 | ||||
| 	acc2Value, err := suite.Keeper.GetVaultAccountValue(suite.Ctx, vaultDenom, acc2) | ||||
| 	suite.Require().NoError(err) | ||||
| 	suite.Equal( | ||||
| 		sdk.NewInt(99), | ||||
| 		acc2Value.Amount, | ||||
| 		"value 1 less than deposit amount with different share price, decimals truncated", | ||||
| 	) | ||||
| 
 | ||||
| 	acc2Shares, found := suite.Keeper.GetVaultAccountShares(suite.Ctx, acc2) | ||||
| 	suite.Require().True(found) | ||||
| 	// 100 * 100 / 110 = 190.909090909090909091
 | ||||
| 	// QuoInt64() truncates
 | ||||
| 	expectedAcc2Shares := sdk.NewDec(100).MulInt64(100).QuoInt64(110) | ||||
| 	suite.Equal(expectedAcc2Shares, acc2Shares.AmountOf(vaultDenom)) | ||||
| 
 | ||||
| 	vaultTotalShares, found := suite.Keeper.GetVaultTotalShares(suite.Ctx, vaultDenom) | ||||
| 	suite.Require().True(found) | ||||
| 	suite.Equal(sdk.NewDec(100).Add(expectedAcc2Shares), vaultTotalShares.Amount) | ||||
| 
 | ||||
| 	// Hard deposit again from module account to triple original value
 | ||||
| 	// 210 -> 300
 | ||||
| 	suite.HardKeeper.Deposit(suite.Ctx, macc.GetAddress(), sdk.NewCoins(sdk.NewInt64Coin(vaultDenom, 90))) | ||||
| 
 | ||||
| 	// Deposit again from acc1
 | ||||
| 	err = suite.Keeper.Deposit(suite.Ctx, acc1, depositAmount) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	acc1Shares, found = suite.Keeper.GetVaultAccountShares(suite.Ctx, acc1) | ||||
| 	suite.Require().True(found) | ||||
| 	// totalShares = 100 + 90            = 190
 | ||||
| 	// totalValue  = 100 + 10 + 100 + 90 = 300
 | ||||
| 	// sharesIssued = assetAmount * (shareCount / totalTokens)
 | ||||
| 	// sharedIssued = 100 * 190 / 300 = 63.3 = 63
 | ||||
| 	// total shares = 100 + 63 = 163
 | ||||
| 	suite.Equal( | ||||
| 		sdk.NewDec(100).Add(sdk.NewDec(100).Mul(vaultTotalShares.Amount).Quo(sdk.NewDec(300))), | ||||
| 		acc1Shares.AmountOf(vaultDenom), | ||||
| 		"shares should consist of 100 of 1x share price and 63 of 3x share price", | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| func (suite *strategyHardTestSuite) TestWithdraw_AccumulatedAmount() { | ||||
| 	vaultDenom := "usdx" | ||||
| 	startBalance := sdk.NewInt64Coin(vaultDenom, 1000) | ||||
| 	depositAmount := sdk.NewInt64Coin(vaultDenom, 100) | ||||
| 	suite.App.FundModuleAccount(suite.Ctx, types.ModuleName, sdk.NewCoins(sdk.NewInt64Coin(vaultDenom, 1000))) | ||||
| 
 | ||||
| 	suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD) | ||||
| 
 | ||||
| 	// Deposit from account1
 | ||||
| 	acc1 := suite.CreateAccount(sdk.NewCoins(startBalance), 0).GetAddress() | ||||
| 	acc2 := suite.CreateAccount(sdk.NewCoins(startBalance), 1).GetAddress() | ||||
| 
 | ||||
| 	// 1. acc1 deposit 100
 | ||||
| 	err := suite.Keeper.Deposit(suite.Ctx, acc1, depositAmount) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	// acc2 deposit 100, just to make sure other deposits do not affect acc1
 | ||||
| 	err = suite.Keeper.Deposit(suite.Ctx, acc2, depositAmount) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	acc1Shares, found := suite.Keeper.GetVaultAccountShares(suite.Ctx, acc1) | ||||
| 	suite.Require().True(found) | ||||
| 	suite.Equal(sdk.NewDec(100), acc1Shares.AmountOf(vaultDenom), "initial deposit 1:1 shares") | ||||
| 
 | ||||
| 	// 2. Direct hard deposit from module account to increase vault value
 | ||||
| 	// Total value: 200 -> 220, 110 each account
 | ||||
| 	macc := suite.AccountKeeper.GetModuleAccount(suite.Ctx, types.ModuleName) | ||||
| 	err = suite.HardKeeper.Deposit(suite.Ctx, macc.GetAddress(), sdk.NewCoins(sdk.NewInt64Coin(vaultDenom, 20))) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	// 3. Withdraw all from acc1 - including accumulated amount
 | ||||
| 	err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount.AddAmount(sdk.NewInt(10))) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	_, found = suite.Keeper.GetVaultAccountShares(suite.Ctx, acc1) | ||||
| 	suite.Require().False(found, "should have withdrawn entire shares") | ||||
| } | ||||
| 
 | ||||
| func (suite *strategyHardTestSuite) TestWithdraw_AccumulatedTruncated() { | ||||
| 	vaultDenom := "usdx" | ||||
| 	startBalance := sdk.NewInt64Coin(vaultDenom, 1000) | ||||
| 	depositAmount := sdk.NewInt64Coin(vaultDenom, 100) | ||||
| 	suite.App.FundModuleAccount(suite.Ctx, types.ModuleName, sdk.NewCoins(sdk.NewInt64Coin(vaultDenom, 1000))) | ||||
| 
 | ||||
| 	suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD) | ||||
| 
 | ||||
| 	// Deposit from account1
 | ||||
| 	acc1 := suite.CreateAccount(sdk.NewCoins(startBalance), 0).GetAddress() | ||||
| 	acc2 := suite.CreateAccount(sdk.NewCoins(startBalance), 1).GetAddress() | ||||
| 
 | ||||
| 	// 1. acc1 deposit 100
 | ||||
| 	err := suite.Keeper.Deposit(suite.Ctx, acc1, depositAmount) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	// acc2 deposit 100, just to make sure other deposits do not affect acc1
 | ||||
| 	err = suite.Keeper.Deposit(suite.Ctx, acc2, depositAmount) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	acc1Shares, found := suite.Keeper.GetVaultAccountShares(suite.Ctx, acc1) | ||||
| 	suite.Require().True(found) | ||||
| 	suite.Equal(sdk.NewDec(100), acc1Shares.AmountOf(vaultDenom), "initial deposit 1:1 shares") | ||||
| 
 | ||||
| 	// 2. Direct hard deposit from module account to increase vault value
 | ||||
| 	// Total value: 200 -> 211, 105.5 each account
 | ||||
| 	macc := suite.AccountKeeper.GetModuleAccount(suite.Ctx, types.ModuleName) | ||||
| 	err = suite.HardKeeper.Deposit(suite.Ctx, macc.GetAddress(), sdk.NewCoins(sdk.NewInt64Coin(vaultDenom, 11))) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	accBal, err := suite.Keeper.GetVaultAccountValue(suite.Ctx, vaultDenom, acc1) | ||||
| 	suite.Require().NoError(err) | ||||
| 	suite.Equal(depositAmount.AddAmount(sdk.NewInt(5)), accBal, "acc1 should have 105 usdx") | ||||
| 
 | ||||
| 	// 3. Withdraw all from acc1 - including accumulated amount
 | ||||
| 	err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount.AddAmount(sdk.NewInt(5))) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	acc1Shares, found = suite.Keeper.GetVaultAccountShares(suite.Ctx, acc1) | ||||
| 	suite.Require().Falsef(found, "should have withdrawn entire shares but has %s", acc1Shares) | ||||
| 
 | ||||
| 	_, err = suite.Keeper.GetVaultAccountValue(suite.Ctx, vaultDenom, acc1) | ||||
| 	suite.Require().Error(err) | ||||
| } | ||||
| 
 | ||||
| func (suite *strategyHardTestSuite) TestWithdraw_ExpensiveShares() { | ||||
| 	vaultDenom := "usdx" | ||||
| 	startBalance := sdk.NewInt64Coin(vaultDenom, 1000) | ||||
| 	depositAmount := sdk.NewInt64Coin(vaultDenom, 100) | ||||
| 	suite.App.FundModuleAccount(suite.Ctx, types.ModuleName, sdk.NewCoins(sdk.NewInt64Coin(vaultDenom, 2000))) | ||||
| 
 | ||||
| 	suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD) | ||||
| 
 | ||||
| 	// Deposit from account1
 | ||||
| 	acc1 := suite.CreateAccount(sdk.NewCoins(startBalance), 0).GetAddress() | ||||
| 
 | ||||
| 	// 1. acc1 deposit 100
 | ||||
| 	err := suite.Keeper.Deposit(suite.Ctx, acc1, depositAmount) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	acc1Shares, found := suite.Keeper.GetVaultAccountShares(suite.Ctx, acc1) | ||||
| 	suite.Require().True(found) | ||||
| 	suite.Equal(sdk.NewDec(100), acc1Shares.AmountOf(vaultDenom), "initial deposit 1:1 shares") | ||||
| 
 | ||||
| 	// 2. Direct hard deposit from module account to increase vault value
 | ||||
| 	// Total value: 100 -> 2000, shares now 10usdx each
 | ||||
| 	macc := suite.AccountKeeper.GetModuleAccount(suite.Ctx, types.ModuleName) | ||||
| 	err = suite.HardKeeper.Deposit(suite.Ctx, macc.GetAddress(), sdk.NewCoins(sdk.NewInt64Coin(vaultDenom, 1900))) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	accBal, err := suite.Keeper.GetVaultAccountValue(suite.Ctx, vaultDenom, acc1) | ||||
| 	suite.Require().NoError(err) | ||||
| 	suite.Equal(sdk.NewInt(2000), accBal.Amount, "acc1 should have 2000 usdx") | ||||
| 
 | ||||
| 	// 3. Withdraw all from acc1 - including accumulated amount
 | ||||
| 	err = suite.Keeper.Withdraw(suite.Ctx, acc1, sdk.NewInt64Coin(vaultDenom, 2000)) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	acc1Shares, found = suite.Keeper.GetVaultAccountShares(suite.Ctx, acc1) | ||||
| 	suite.Require().Falsef(found, "should have withdrawn entire shares but has %s", acc1Shares) | ||||
| 
 | ||||
| 	_, err = suite.Keeper.GetVaultAccountValue(suite.Ctx, vaultDenom, acc1) | ||||
| 	suite.Require().Error(err) | ||||
| } | ||||
|  | ||||
| @ -1,24 +1,25 @@ | ||||
| package keeper | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/cosmos/cosmos-sdk/store/prefix" | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	sdk "github.com/cosmos/cosmos-sdk/types" | ||||
| 	"github.com/kava-labs/kava/x/earn/types" | ||||
| ) | ||||
| 
 | ||||
| // GetVaultTotalSupplied returns the total balance supplied to the vault. This
 | ||||
| // GetVaultTotalShares returns the total balance supplied to the vault. This
 | ||||
| // may not necessarily be the current value of the vault, as it is the sum
 | ||||
| // of the supplied denom and the value may be higher due to accumulated APYs.
 | ||||
| func (k *Keeper) GetVaultTotalSupplied( | ||||
| func (k *Keeper) GetVaultTotalShares( | ||||
| 	ctx sdk.Context, | ||||
| 	denom string, | ||||
| ) (sdk.Coin, error) { | ||||
| ) (types.VaultShare, bool) { | ||||
| 	vault, found := k.GetVaultRecord(ctx, denom) | ||||
| 	if !found { | ||||
| 		return sdk.Coin{}, types.ErrVaultRecordNotFound | ||||
| 		return types.VaultShare{}, false | ||||
| 	} | ||||
| 
 | ||||
| 	return vault.TotalSupply, nil | ||||
| 	return vault.TotalShares, true | ||||
| } | ||||
| 
 | ||||
| // GetTotalValue returns the total **value** of all coins in this vault,
 | ||||
| @ -47,16 +48,16 @@ func (k *Keeper) GetVaultTotalValue( | ||||
| 
 | ||||
| // GetVaultAccountSupplied returns the supplied amount for a single address
 | ||||
| // within a vault.
 | ||||
| func (k *Keeper) GetVaultAccountSupplied( | ||||
| func (k *Keeper) GetVaultAccountShares( | ||||
| 	ctx sdk.Context, | ||||
| 	acc sdk.AccAddress, | ||||
| ) (sdk.Coins, error) { | ||||
| ) (types.VaultShares, bool) { | ||||
| 	vaultShareRecord, found := k.GetVaultShareRecord(ctx, acc) | ||||
| 	if !found { | ||||
| 		return sdk.Coins{}, types.ErrVaultShareRecordNotFound | ||||
| 		return nil, false | ||||
| 	} | ||||
| 
 | ||||
| 	return vaultShareRecord.AmountSupplied, nil | ||||
| 	return vaultShareRecord.Shares, true | ||||
| } | ||||
| 
 | ||||
| // GetVaultAccountValue returns the value of a single address within a vault
 | ||||
| @ -66,190 +67,10 @@ func (k *Keeper) GetVaultAccountValue( | ||||
| 	denom string, | ||||
| 	acc sdk.AccAddress, | ||||
| ) (sdk.Coin, error) { | ||||
| 	totalSupplied, err := k.GetVaultTotalSupplied(ctx, denom) | ||||
| 	if err != nil { | ||||
| 		return sdk.Coin{}, err | ||||
| 	accShares, found := k.GetVaultAccountShares(ctx, acc) | ||||
| 	if !found { | ||||
| 		return sdk.Coin{}, fmt.Errorf("account vault share record for %s not found", denom) | ||||
| 	} | ||||
| 
 | ||||
| 	accSupplied, err := k.GetVaultAccountSupplied(ctx, acc) | ||||
| 	if err != nil { | ||||
| 		return sdk.Coin{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	vaultTotalValue, err := k.GetVaultTotalValue(ctx, denom) | ||||
| 	if err != nil { | ||||
| 		return sdk.Coin{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Percent of vault account ownership = accountSupply / totalSupply
 | ||||
| 	// Value of vault account ownership = percentOwned * totalValue
 | ||||
| 	vaultShare := accSupplied.AmountOf(denom).ToDec().Quo(totalSupplied.Amount.ToDec()) | ||||
| 	shareValueDec := vaultTotalValue.Amount.ToDec().Mul(vaultShare) | ||||
| 
 | ||||
| 	return sdk.NewCoin(denom, shareValueDec.TruncateInt()), nil | ||||
| } | ||||
| 
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
| // VaultRecord -- vault total supplies
 | ||||
| 
 | ||||
| // GetVaultRecord returns the vault record for a given denom.
 | ||||
| func (k *Keeper) GetVaultRecord( | ||||
| 	ctx sdk.Context, | ||||
| 	vaultDenom string, | ||||
| ) (types.VaultRecord, bool) { | ||||
| 	store := prefix.NewStore(ctx.KVStore(k.key), types.VaultRecordKeyPrefix) | ||||
| 
 | ||||
| 	bz := store.Get(types.VaultKey(vaultDenom)) | ||||
| 	if bz == nil { | ||||
| 		return types.VaultRecord{}, false | ||||
| 	} | ||||
| 
 | ||||
| 	var record types.VaultRecord | ||||
| 	k.cdc.MustUnmarshal(bz, &record) | ||||
| 
 | ||||
| 	return record, true | ||||
| } | ||||
| 
 | ||||
| // UpdateVaultRecord updates the vault record in state for a given denom. This
 | ||||
| // deletes it if the supply is zero and updates the state if supply is non-zero.
 | ||||
| func (k *Keeper) UpdateVaultRecord( | ||||
| 	ctx sdk.Context, | ||||
| 	vaultRecord types.VaultRecord, | ||||
| ) { | ||||
| 	if vaultRecord.TotalSupply.IsZero() { | ||||
| 		k.DeleteVaultRecord(ctx, vaultRecord.Denom) | ||||
| 	} else { | ||||
| 		k.SetVaultRecord(ctx, vaultRecord) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // DeleteVaultRecord deletes the vault record for a given denom.
 | ||||
| func (k *Keeper) DeleteVaultRecord(ctx sdk.Context, vaultDenom string) { | ||||
| 	store := prefix.NewStore(ctx.KVStore(k.key), types.VaultRecordKeyPrefix) | ||||
| 	store.Delete(types.VaultKey(vaultDenom)) | ||||
| } | ||||
| 
 | ||||
| // SetVaultRecord sets the vault record for a given denom.
 | ||||
| func (k *Keeper) SetVaultRecord(ctx sdk.Context, record types.VaultRecord) { | ||||
| 	store := prefix.NewStore(ctx.KVStore(k.key), types.VaultRecordKeyPrefix) | ||||
| 	bz := k.cdc.MustMarshal(&record) | ||||
| 	store.Set(types.VaultKey(record.Denom), bz) | ||||
| } | ||||
| 
 | ||||
| // IterateVaultRecords iterates over all vault objects in the store and performs
 | ||||
| // a callback function.
 | ||||
| func (k Keeper) IterateVaultRecords( | ||||
| 	ctx sdk.Context, | ||||
| 	cb func(record types.VaultRecord) (stop bool), | ||||
| ) { | ||||
| 	store := prefix.NewStore(ctx.KVStore(k.key), types.VaultRecordKeyPrefix) | ||||
| 	iterator := sdk.KVStorePrefixIterator(store, []byte{}) | ||||
| 	defer iterator.Close() | ||||
| 
 | ||||
| 	for ; iterator.Valid(); iterator.Next() { | ||||
| 		var record types.VaultRecord | ||||
| 		k.cdc.MustUnmarshal(iterator.Value(), &record) | ||||
| 		if cb(record) { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // GetAllVaultRecords returns all vault records from the store.
 | ||||
| func (k Keeper) GetAllVaultRecords(ctx sdk.Context) types.VaultRecords { | ||||
| 	var records types.VaultRecords | ||||
| 
 | ||||
| 	k.IterateVaultRecords(ctx, func(record types.VaultRecord) bool { | ||||
| 		records = append(records, record) | ||||
| 		return false | ||||
| 	}) | ||||
| 
 | ||||
| 	return records | ||||
| } | ||||
| 
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
| // VaultShare -- user shares per vault
 | ||||
| 
 | ||||
| // GetVaultShareRecord returns the vault share record for a given denom and
 | ||||
| // account.
 | ||||
| func (k *Keeper) GetVaultShareRecord( | ||||
| 	ctx sdk.Context, | ||||
| 	acc sdk.AccAddress, | ||||
| ) (types.VaultShareRecord, bool) { | ||||
| 	store := prefix.NewStore(ctx.KVStore(k.key), types.VaultShareRecordKeyPrefix) | ||||
| 
 | ||||
| 	bz := store.Get(types.DepositorVaultSharesKey(acc)) | ||||
| 	if bz == nil { | ||||
| 		return types.VaultShareRecord{}, false | ||||
| 	} | ||||
| 
 | ||||
| 	var record types.VaultShareRecord | ||||
| 	k.cdc.MustUnmarshal(bz, &record) | ||||
| 
 | ||||
| 	return record, true | ||||
| } | ||||
| 
 | ||||
| // UpdateVaultShareRecord updates the vault share record in state for a given
 | ||||
| // denom and account. This deletes it if the supply is zero and updates the
 | ||||
| // state if supply is non-zero.
 | ||||
| func (k *Keeper) UpdateVaultShareRecord( | ||||
| 	ctx sdk.Context, | ||||
| 	record types.VaultShareRecord, | ||||
| ) { | ||||
| 	if record.AmountSupplied.IsZero() { | ||||
| 		k.DeleteVaultShareRecord(ctx, record.Depositor) | ||||
| 	} else { | ||||
| 		k.SetVaultShareRecord(ctx, record) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // DeleteVaultShareRecord deletes the vault share record for a given denom and
 | ||||
| // account.
 | ||||
| func (k *Keeper) DeleteVaultShareRecord( | ||||
| 	ctx sdk.Context, | ||||
| 	acc sdk.AccAddress, | ||||
| ) { | ||||
| 	store := prefix.NewStore(ctx.KVStore(k.key), types.VaultShareRecordKeyPrefix) | ||||
| 	store.Delete(types.DepositorVaultSharesKey(acc)) | ||||
| } | ||||
| 
 | ||||
| // SetVaultShareRecord sets the vault share record for a given denom and account.
 | ||||
| func (k *Keeper) SetVaultShareRecord( | ||||
| 	ctx sdk.Context, | ||||
| 	record types.VaultShareRecord, | ||||
| ) { | ||||
| 	store := prefix.NewStore(ctx.KVStore(k.key), types.VaultShareRecordKeyPrefix) | ||||
| 	bz := k.cdc.MustMarshal(&record) | ||||
| 	store.Set(types.DepositorVaultSharesKey(record.Depositor), bz) | ||||
| } | ||||
| 
 | ||||
| // IterateVaultShareRecords iterates over all vault share objects in the store
 | ||||
| // and performs a callback function.
 | ||||
| func (k Keeper) IterateVaultShareRecords( | ||||
| 	ctx sdk.Context, | ||||
| 	cb func(record types.VaultShareRecord) (stop bool), | ||||
| ) { | ||||
| 	store := prefix.NewStore(ctx.KVStore(k.key), types.VaultShareRecordKeyPrefix) | ||||
| 	iterator := sdk.KVStorePrefixIterator(store, []byte{}) | ||||
| 	defer iterator.Close() | ||||
| 
 | ||||
| 	for ; iterator.Valid(); iterator.Next() { | ||||
| 		var record types.VaultShareRecord | ||||
| 		k.cdc.MustUnmarshal(iterator.Value(), &record) | ||||
| 		if cb(record) { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // GetAllVaultShareRecords returns all vault share records from the store.
 | ||||
| func (k Keeper) GetAllVaultShareRecords(ctx sdk.Context) types.VaultShareRecords { | ||||
| 	var records types.VaultShareRecords | ||||
| 
 | ||||
| 	k.IterateVaultShareRecords(ctx, func(record types.VaultShareRecord) bool { | ||||
| 		records = append(records, record) | ||||
| 		return false | ||||
| 	}) | ||||
| 
 | ||||
| 	return records | ||||
| 	return k.ConvertToAssets(ctx, accShares.GetShare(denom)) | ||||
| } | ||||
|  | ||||
							
								
								
									
										85
									
								
								x/earn/keeper/vault_record.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								x/earn/keeper/vault_record.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,85 @@ | ||||
| package keeper | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/cosmos/cosmos-sdk/store/prefix" | ||||
| 	sdk "github.com/cosmos/cosmos-sdk/types" | ||||
| 	"github.com/kava-labs/kava/x/earn/types" | ||||
| ) | ||||
| 
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
| // VaultRecord -- vault total shares
 | ||||
| 
 | ||||
| // GetVaultRecord returns the vault record for a given denom.
 | ||||
| func (k *Keeper) GetVaultRecord( | ||||
| 	ctx sdk.Context, | ||||
| 	vaultDenom string, | ||||
| ) (types.VaultRecord, bool) { | ||||
| 	store := prefix.NewStore(ctx.KVStore(k.key), types.VaultRecordKeyPrefix) | ||||
| 
 | ||||
| 	bz := store.Get(types.VaultKey(vaultDenom)) | ||||
| 	if bz == nil { | ||||
| 		return types.VaultRecord{}, false | ||||
| 	} | ||||
| 
 | ||||
| 	var record types.VaultRecord | ||||
| 	k.cdc.MustUnmarshal(bz, &record) | ||||
| 
 | ||||
| 	return record, true | ||||
| } | ||||
| 
 | ||||
| // UpdateVaultRecord updates the vault record in state for a given denom. This
 | ||||
| // deletes it if the supply is zero and updates the state if supply is non-zero.
 | ||||
| func (k *Keeper) UpdateVaultRecord( | ||||
| 	ctx sdk.Context, | ||||
| 	vaultRecord types.VaultRecord, | ||||
| ) { | ||||
| 	if vaultRecord.TotalShares.Amount.IsZero() { | ||||
| 		k.DeleteVaultRecord(ctx, vaultRecord.TotalShares.Denom) | ||||
| 	} else { | ||||
| 		k.SetVaultRecord(ctx, vaultRecord) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // DeleteVaultRecord deletes the vault record for a given denom.
 | ||||
| func (k *Keeper) DeleteVaultRecord(ctx sdk.Context, vaultDenom string) { | ||||
| 	store := prefix.NewStore(ctx.KVStore(k.key), types.VaultRecordKeyPrefix) | ||||
| 	store.Delete(types.VaultKey(vaultDenom)) | ||||
| } | ||||
| 
 | ||||
| // SetVaultRecord sets the vault record for a given denom.
 | ||||
| func (k *Keeper) SetVaultRecord(ctx sdk.Context, record types.VaultRecord) { | ||||
| 	store := prefix.NewStore(ctx.KVStore(k.key), types.VaultRecordKeyPrefix) | ||||
| 	bz := k.cdc.MustMarshal(&record) | ||||
| 	store.Set(types.VaultKey(record.TotalShares.Denom), bz) | ||||
| } | ||||
| 
 | ||||
| // IterateVaultRecords iterates over all vault objects in the store and performs
 | ||||
| // a callback function.
 | ||||
| func (k Keeper) IterateVaultRecords( | ||||
| 	ctx sdk.Context, | ||||
| 	cb func(record types.VaultRecord) (stop bool), | ||||
| ) { | ||||
| 	store := prefix.NewStore(ctx.KVStore(k.key), types.VaultRecordKeyPrefix) | ||||
| 	iterator := sdk.KVStorePrefixIterator(store, []byte{}) | ||||
| 	defer iterator.Close() | ||||
| 
 | ||||
| 	for ; iterator.Valid(); iterator.Next() { | ||||
| 		var record types.VaultRecord | ||||
| 		k.cdc.MustUnmarshal(iterator.Value(), &record) | ||||
| 		if cb(record) { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // GetAllVaultRecords returns all vault records from the store.
 | ||||
| func (k Keeper) GetAllVaultRecords(ctx sdk.Context) types.VaultRecords { | ||||
| 	var records types.VaultRecords | ||||
| 
 | ||||
| 	k.IterateVaultRecords(ctx, func(record types.VaultRecord) bool { | ||||
| 		records = append(records, record) | ||||
| 		return false | ||||
| 	}) | ||||
| 
 | ||||
| 	return records | ||||
| } | ||||
							
								
								
									
										82
									
								
								x/earn/keeper/vault_share.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								x/earn/keeper/vault_share.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,82 @@ | ||||
| package keeper | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	sdk "github.com/cosmos/cosmos-sdk/types" | ||||
| 	"github.com/kava-labs/kava/x/earn/types" | ||||
| ) | ||||
| 
 | ||||
| // ConvertToShares converts a given amount of tokens to shares.
 | ||||
| func (k *Keeper) ConvertToShares(ctx sdk.Context, assets sdk.Coin) (types.VaultShare, error) { | ||||
| 	totalShares, found := k.GetVaultTotalShares(ctx, assets.Denom) | ||||
| 	if !found { | ||||
| 		// No shares issued yet, so shares are issued 1:1
 | ||||
| 		return types.NewVaultShare(assets.Denom, assets.Amount.ToDec()), nil | ||||
| 	} | ||||
| 
 | ||||
| 	totalValue, err := k.GetVaultTotalValue(ctx, assets.Denom) | ||||
| 	if err != nil { | ||||
| 		return types.VaultShare{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	if totalValue.Amount.IsZero() { | ||||
| 		return types.VaultShare{}, fmt.Errorf("total value of vault is zero") | ||||
| 	} | ||||
| 
 | ||||
| 	// sharePrice   = totalValue / totalShares
 | ||||
| 	// issuedShares = assets / sharePrice
 | ||||
| 	// issuedShares = assets / (totalValue / totalShares)
 | ||||
| 	//              = assets * (totalShares / totalValue)
 | ||||
| 	//              = (assets * totalShares) / totalValue
 | ||||
| 	//
 | ||||
| 	// Multiply by reciprocal of sharePrice to avoid two divisions and limit
 | ||||
| 	// rounding to one time. Per-share price is also not used as there is a loss
 | ||||
| 	// of precision.
 | ||||
| 
 | ||||
| 	// Division is done at the last step as there is a slight amount that is
 | ||||
| 	// rounded down.
 | ||||
| 	// For example:
 | ||||
| 	// 100 * 100 / 105   == 10000 / 105                == 95.238095238095238095
 | ||||
| 	// 100 * (100 / 105) == 100 * 0.952380952380952380 == 95.238095238095238000
 | ||||
| 	//                    rounded down and truncated ^    loss of precision ^
 | ||||
| 	issuedShares := assets.Amount.ToDec().Mul(totalShares.Amount).QuoTruncate(totalValue.Amount.ToDec()) | ||||
| 
 | ||||
| 	if issuedShares.IsZero() { | ||||
| 		return types.VaultShare{}, fmt.Errorf("share count is zero") | ||||
| 	} | ||||
| 
 | ||||
| 	return types.NewVaultShare(assets.Denom, issuedShares), nil | ||||
| } | ||||
| 
 | ||||
| // ConvertToAssets converts a given amount of shares to tokens.
 | ||||
| func (k *Keeper) ConvertToAssets(ctx sdk.Context, share types.VaultShare) (sdk.Coin, error) { | ||||
| 	totalVaultShares, found := k.GetVaultTotalShares(ctx, share.Denom) | ||||
| 	if !found { | ||||
| 		return sdk.Coin{}, fmt.Errorf("vault for %s not found", share.Denom) | ||||
| 	} | ||||
| 
 | ||||
| 	totalValue, err := k.GetVaultTotalValue(ctx, share.Denom) | ||||
| 	if err != nil { | ||||
| 		return sdk.Coin{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	// percentOwnership := accShares / totalVaultShares
 | ||||
| 	// accValue := totalValue * percentOwnership
 | ||||
| 	// accValue := totalValue * accShares / totalVaultShares
 | ||||
| 	// Division must be last to avoid rounding errors and properly truncate.
 | ||||
| 	value := totalValue.Amount.ToDec().Mul(share.Amount).QuoTruncate(totalVaultShares.Amount) | ||||
| 
 | ||||
| 	return sdk.NewCoin(share.Denom, value.TruncateInt()), nil | ||||
| } | ||||
| 
 | ||||
| // ShareIsDust returns true if the share value is less than 1 coin
 | ||||
| func (k *Keeper) ShareIsDust(ctx sdk.Context, share types.VaultShare) (bool, error) { | ||||
| 	coin, err := k.ConvertToAssets(ctx, share) | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Truncated int, becomes zero if < 1
 | ||||
| 	return coin.IsZero(), nil | ||||
| } | ||||
							
								
								
									
										94
									
								
								x/earn/keeper/vault_share_record.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								x/earn/keeper/vault_share_record.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,94 @@ | ||||
| package keeper | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/cosmos/cosmos-sdk/store/prefix" | ||||
| 	sdk "github.com/cosmos/cosmos-sdk/types" | ||||
| 	"github.com/kava-labs/kava/x/earn/types" | ||||
| ) | ||||
| 
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
| // VaultShareRecords -- user shares per vault
 | ||||
| 
 | ||||
| // GetVaultShareRecord returns the vault share record for a given denom and
 | ||||
| // account.
 | ||||
| func (k *Keeper) GetVaultShareRecord( | ||||
| 	ctx sdk.Context, | ||||
| 	acc sdk.AccAddress, | ||||
| ) (types.VaultShareRecord, bool) { | ||||
| 	store := prefix.NewStore(ctx.KVStore(k.key), types.VaultShareRecordKeyPrefix) | ||||
| 
 | ||||
| 	bz := store.Get(types.DepositorVaultSharesKey(acc)) | ||||
| 	if bz == nil { | ||||
| 		return types.VaultShareRecord{}, false | ||||
| 	} | ||||
| 
 | ||||
| 	var record types.VaultShareRecord | ||||
| 	k.cdc.MustUnmarshal(bz, &record) | ||||
| 
 | ||||
| 	return record, true | ||||
| } | ||||
| 
 | ||||
| // UpdateVaultShareRecord updates the vault share record in state for a given
 | ||||
| // denom and account. This deletes it if the supply is zero and updates the
 | ||||
| // state if supply is non-zero.
 | ||||
| func (k *Keeper) UpdateVaultShareRecord( | ||||
| 	ctx sdk.Context, | ||||
| 	record types.VaultShareRecord, | ||||
| ) { | ||||
| 	if record.Shares.IsZero() { | ||||
| 		k.DeleteVaultShareRecord(ctx, record.Depositor) | ||||
| 	} else { | ||||
| 		k.SetVaultShareRecord(ctx, record) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // DeleteVaultShareRecord deletes the vault share record for a given denom and
 | ||||
| // account.
 | ||||
| func (k *Keeper) DeleteVaultShareRecord( | ||||
| 	ctx sdk.Context, | ||||
| 	acc sdk.AccAddress, | ||||
| ) { | ||||
| 	store := prefix.NewStore(ctx.KVStore(k.key), types.VaultShareRecordKeyPrefix) | ||||
| 	store.Delete(types.DepositorVaultSharesKey(acc)) | ||||
| } | ||||
| 
 | ||||
| // SetVaultShareRecord sets the vault share record for a given denom and account.
 | ||||
| func (k *Keeper) SetVaultShareRecord( | ||||
| 	ctx sdk.Context, | ||||
| 	record types.VaultShareRecord, | ||||
| ) { | ||||
| 	store := prefix.NewStore(ctx.KVStore(k.key), types.VaultShareRecordKeyPrefix) | ||||
| 	bz := k.cdc.MustMarshal(&record) | ||||
| 	store.Set(types.DepositorVaultSharesKey(record.Depositor), bz) | ||||
| } | ||||
| 
 | ||||
| // IterateVaultShareRecords iterates over all vault share objects in the store
 | ||||
| // and performs a callback function.
 | ||||
| func (k Keeper) IterateVaultShareRecords( | ||||
| 	ctx sdk.Context, | ||||
| 	cb func(record types.VaultShareRecord) (stop bool), | ||||
| ) { | ||||
| 	store := prefix.NewStore(ctx.KVStore(k.key), types.VaultShareRecordKeyPrefix) | ||||
| 	iterator := sdk.KVStorePrefixIterator(store, []byte{}) | ||||
| 	defer iterator.Close() | ||||
| 
 | ||||
| 	for ; iterator.Valid(); iterator.Next() { | ||||
| 		var record types.VaultShareRecord | ||||
| 		k.cdc.MustUnmarshal(iterator.Value(), &record) | ||||
| 		if cb(record) { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // GetAllVaultShareRecords returns all vault share records from the store.
 | ||||
| func (k Keeper) GetAllVaultShareRecords(ctx sdk.Context) types.VaultShareRecords { | ||||
| 	var records types.VaultShareRecords | ||||
| 
 | ||||
| 	k.IterateVaultShareRecords(ctx, func(record types.VaultShareRecord) bool { | ||||
| 		records = append(records, record) | ||||
| 		return false | ||||
| 	}) | ||||
| 
 | ||||
| 	return records | ||||
| } | ||||
							
								
								
									
										90
									
								
								x/earn/keeper/vault_share_record_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								x/earn/keeper/vault_share_record_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,90 @@ | ||||
| package keeper_test | ||||
| 
 | ||||
| import ( | ||||
| 	sdk "github.com/cosmos/cosmos-sdk/types" | ||||
| 	"github.com/kava-labs/kava/x/earn/types" | ||||
| ) | ||||
| 
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
| // State methods
 | ||||
| 
 | ||||
| func (suite *vaultTestSuite) TestGetVaultRecord() { | ||||
| 	record := types.NewVaultRecord("usdx", sdk.ZeroDec()) | ||||
| 
 | ||||
| 	_, found := suite.Keeper.GetVaultRecord(suite.Ctx, record.TotalShares.Denom) | ||||
| 	suite.Require().False(found) | ||||
| 
 | ||||
| 	suite.Keeper.SetVaultRecord(suite.Ctx, record) | ||||
| 
 | ||||
| 	stateRecord, found := suite.Keeper.GetVaultRecord(suite.Ctx, record.TotalShares.Denom) | ||||
| 	suite.Require().True(found) | ||||
| 	suite.Require().Equal(record, stateRecord) | ||||
| } | ||||
| 
 | ||||
| func (suite *vaultTestSuite) TestUpdateVaultRecord() { | ||||
| 	record := types.NewVaultRecord("usdx", sdk.ZeroDec()) | ||||
| 
 | ||||
| 	record.TotalShares = types.NewVaultShare("usdx", sdk.NewDec(100)) | ||||
| 
 | ||||
| 	// Update vault
 | ||||
| 	suite.Keeper.UpdateVaultRecord(suite.Ctx, record) | ||||
| 
 | ||||
| 	stateRecord, found := suite.Keeper.GetVaultRecord(suite.Ctx, record.TotalShares.Denom) | ||||
| 	suite.Require().True(found, "vault record with supply should exist") | ||||
| 	suite.Require().Equal(record, stateRecord) | ||||
| 
 | ||||
| 	// Remove supply
 | ||||
| 	record.TotalShares = types.NewVaultShare("usdx", sdk.NewDec(0)) | ||||
| 	suite.Keeper.UpdateVaultRecord(suite.Ctx, record) | ||||
| 
 | ||||
| 	_, found = suite.Keeper.GetVaultRecord(suite.Ctx, record.TotalShares.Denom) | ||||
| 	suite.Require().False(found, "vault record with 0 supply should be deleted") | ||||
| } | ||||
| 
 | ||||
| func (suite *vaultTestSuite) TestGetVaultShareRecord() { | ||||
| 	vaultDenom := "usdx" | ||||
| 	startBalance := sdk.NewInt64Coin(vaultDenom, 1000) | ||||
| 	acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) | ||||
| 
 | ||||
| 	record := types.NewVaultShareRecord(acc.GetAddress(), types.NewVaultShares()) | ||||
| 
 | ||||
| 	// Check share doesn't exist before deposit
 | ||||
| 
 | ||||
| 	_, found := suite.Keeper.GetVaultShareRecord(suite.Ctx, acc.GetAddress()) | ||||
| 	suite.Require().False(found, "vault share record should not exist before deposit") | ||||
| 
 | ||||
| 	// Update share record
 | ||||
| 	record.Shares = types.NewVaultShares( | ||||
| 		types.NewVaultShare(vaultDenom, sdk.NewDec(100)), | ||||
| 	) | ||||
| 	suite.Keeper.SetVaultShareRecord(suite.Ctx, record) | ||||
| 
 | ||||
| 	// Check share exists and matches set value
 | ||||
| 	stateRecord, found := suite.Keeper.GetVaultShareRecord(suite.Ctx, acc.GetAddress()) | ||||
| 	suite.Require().True(found) | ||||
| 	suite.Require().Equal(record, stateRecord) | ||||
| } | ||||
| 
 | ||||
| func (suite *vaultTestSuite) TestUpdateVaultShareRecord() { | ||||
| 	vaultDenom := "usdx" | ||||
| 	startBalance := sdk.NewInt64Coin(vaultDenom, 1000) | ||||
| 	acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) | ||||
| 
 | ||||
| 	record := types.NewVaultShareRecord(acc.GetAddress(), types.NewVaultShares( | ||||
| 		types.NewVaultShare(vaultDenom, sdk.NewDec(100)), | ||||
| 	)) | ||||
| 
 | ||||
| 	// Update vault
 | ||||
| 	suite.Keeper.UpdateVaultShareRecord(suite.Ctx, record) | ||||
| 
 | ||||
| 	stateRecord, found := suite.Keeper.GetVaultShareRecord(suite.Ctx, acc.GetAddress()) | ||||
| 	suite.Require().True(found, "vault share record with supply should exist") | ||||
| 	suite.Require().Equal(record, stateRecord) | ||||
| 
 | ||||
| 	// Remove supply
 | ||||
| 	record.Shares = types.NewVaultShares() | ||||
| 	suite.Keeper.UpdateVaultShareRecord(suite.Ctx, record) | ||||
| 
 | ||||
| 	_, found = suite.Keeper.GetVaultShareRecord(suite.Ctx, acc.GetAddress()) | ||||
| 	suite.Require().False(found, "vault share record with 0 supply should be deleted") | ||||
| } | ||||
							
								
								
									
										132
									
								
								x/earn/keeper/vault_share_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								x/earn/keeper/vault_share_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,132 @@ | ||||
| package keeper_test | ||||
| 
 | ||||
| import ( | ||||
| 	"testing" | ||||
| 
 | ||||
| 	sdk "github.com/cosmos/cosmos-sdk/types" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/suite" | ||||
| 
 | ||||
| 	"github.com/kava-labs/kava/x/earn/testutil" | ||||
| 	"github.com/kava-labs/kava/x/earn/types" | ||||
| ) | ||||
| 
 | ||||
| type vaultShareTestSuite struct { | ||||
| 	testutil.Suite | ||||
| } | ||||
| 
 | ||||
| func (suite *vaultShareTestSuite) SetupTest() { | ||||
| 	suite.Suite.SetupTest() | ||||
| 	suite.Keeper.SetParams(suite.Ctx, types.DefaultParams()) | ||||
| } | ||||
| 
 | ||||
| func TestVaultShareTestSuite(t *testing.T) { | ||||
| 	suite.Run(t, new(vaultShareTestSuite)) | ||||
| } | ||||
| 
 | ||||
| func (suite *vaultShareTestSuite) TestConvertToShares() { | ||||
| 	vaultDenom := "usdx" | ||||
| 
 | ||||
| 	tests := []struct { | ||||
| 		name          string | ||||
| 		beforeConvert func() | ||||
| 		giveAmount    sdk.Coin | ||||
| 		wantShares    types.VaultShare | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:          "initial 1:1", | ||||
| 			beforeConvert: func() {}, | ||||
| 			giveAmount:    sdk.NewCoin(vaultDenom, sdk.NewInt(100)), | ||||
| 			wantShares:    types.NewVaultShare(vaultDenom, sdk.NewDec(100)), | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "value doubled", | ||||
| 
 | ||||
| 			beforeConvert: func() { | ||||
| 				// set total shares set total value for hard
 | ||||
| 				// value is double than shares
 | ||||
| 				// shares is 2x price now
 | ||||
| 				suite.addTotalShareAndValue(vaultDenom, sdk.NewDec(100), sdk.NewInt(200)) | ||||
| 			}, | ||||
| 			giveAmount: sdk.NewCoin(vaultDenom, sdk.NewInt(100)), | ||||
| 			wantShares: types.NewVaultShare(vaultDenom, sdk.NewDec(50)), | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "truncate", | ||||
| 
 | ||||
| 			beforeConvert: func() { | ||||
| 				suite.addTotalShareAndValue(vaultDenom, sdk.NewDec(1000), sdk.NewInt(1001)) | ||||
| 			}, | ||||
| 			giveAmount: sdk.NewCoin(vaultDenom, sdk.NewInt(100)), | ||||
| 			// 100 * 100 / 101 = 99.0099something
 | ||||
| 			wantShares: types.NewVaultShare(vaultDenom, sdk.NewDec(100).MulInt64(1000).QuoInt64(1001)), | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, tt := range tests { | ||||
| 		suite.Run(tt.name, func() { | ||||
| 			// Reset state
 | ||||
| 			suite.Suite.SetupTest() | ||||
| 			suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD) | ||||
| 			err := suite.App.FundModuleAccount( | ||||
| 				suite.Ctx, | ||||
| 				types.ModuleName, | ||||
| 				sdk.NewCoins(sdk.NewInt64Coin(vaultDenom, 10000)), | ||||
| 			) | ||||
| 			suite.Require().NoError(err) | ||||
| 
 | ||||
| 			// Run any deposits or any other setup
 | ||||
| 			tt.beforeConvert() | ||||
| 
 | ||||
| 			issuedShares, err := suite.Keeper.ConvertToShares(suite.Ctx, tt.giveAmount) | ||||
| 			suite.Require().NoError(err) | ||||
| 
 | ||||
| 			suite.Equal(tt.wantShares, issuedShares) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (suite *vaultShareTestSuite) addTotalShareAndValue( | ||||
| 	vaultDenom string, | ||||
| 	vaultShares sdk.Dec, | ||||
| 	hardDeposit sdk.Int, | ||||
| ) { | ||||
| 	macc := suite.AccountKeeper.GetModuleAccount(suite.Ctx, types.ModuleName) | ||||
| 
 | ||||
| 	vaultRecord, found := suite.Keeper.GetVaultRecord(suite.Ctx, vaultDenom) | ||||
| 	if !found { | ||||
| 		vaultRecord = types.NewVaultRecord(vaultDenom, sdk.ZeroDec()) | ||||
| 	} | ||||
| 
 | ||||
| 	// Add to vault record
 | ||||
| 	vaultRecord.TotalShares.Amount = vaultRecord.TotalShares.Amount.Add(vaultShares) | ||||
| 
 | ||||
| 	// set total shares
 | ||||
| 	suite.Keeper.UpdateVaultRecord( | ||||
| 		suite.Ctx, | ||||
| 		vaultRecord, | ||||
| 	) | ||||
| 	// add value for hard -- this does not set
 | ||||
| 	err := suite.HardKeeper.Deposit( | ||||
| 		suite.Ctx, | ||||
| 		macc.GetAddress(), | ||||
| 		sdk.NewCoins(sdk.NewCoin(vaultDenom, hardDeposit)), | ||||
| 	) | ||||
| 	suite.Require().NoError(err) | ||||
| } | ||||
| 
 | ||||
| func TestPrecisionMulQuoOrder(t *testing.T) { | ||||
| 	assetAmount := sdk.NewDec(100) | ||||
| 	totalShares := sdk.NewDec(100) | ||||
| 	totalValue := sdk.NewDec(105) | ||||
| 
 | ||||
| 	// issuedShares =  assetAmount * (totalValue / totalShares)
 | ||||
| 	//              = (assetAmount * totalShares) / totalValue
 | ||||
| 	mulFirst := assetAmount.Mul(totalShares).QuoTruncate(totalValue) | ||||
| 	quoFirst := assetAmount.Mul(totalShares.QuoTruncate(totalValue)) | ||||
| 
 | ||||
| 	assert.Equal(t, sdk.MustNewDecFromStr("95.238095238095238095"), mulFirst) | ||||
| 	assert.Equal(t, sdk.MustNewDecFromStr("95.238095238095238000"), quoFirst) | ||||
| 
 | ||||
| 	assert.NotEqual(t, mulFirst, quoFirst) | ||||
| } | ||||
| @ -23,7 +23,7 @@ func TestVaultTestSuite(t *testing.T) { | ||||
| 	suite.Run(t, new(vaultTestSuite)) | ||||
| } | ||||
| 
 | ||||
| func (suite *vaultTestSuite) TestGetVaultTotalSupplied() { | ||||
| func (suite *vaultTestSuite) TestGetVaultTotalShares() { | ||||
| 	vaultDenom := "usdx" | ||||
| 	startBalance := sdk.NewInt64Coin(vaultDenom, 1000) | ||||
| 	depositAmount := sdk.NewInt64Coin(vaultDenom, 100) | ||||
| @ -35,22 +35,27 @@ func (suite *vaultTestSuite) TestGetVaultTotalSupplied() { | ||||
| 	err := suite.Keeper.Deposit(suite.Ctx, acc.GetAddress(), depositAmount) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	vaultTotalSupplied, err := suite.Keeper.GetVaultTotalSupplied(suite.Ctx, vaultDenom) | ||||
| 	suite.Require().NoError(err) | ||||
| 	vaultTotalShares, found := suite.Keeper.GetVaultTotalShares(suite.Ctx, vaultDenom) | ||||
| 	suite.Require().True(found) | ||||
| 
 | ||||
| 	suite.Equal(depositAmount, vaultTotalSupplied) | ||||
| 	suite.Equal(depositAmount.Amount.ToDec(), vaultTotalShares.Amount) | ||||
| } | ||||
| 
 | ||||
| func (suite *vaultTestSuite) TestGetVaultTotalSupplied_NotFound() { | ||||
| func (suite *vaultTestSuite) TestGetVaultTotalShares_NotFound() { | ||||
| 	vaultDenom := "usdx" | ||||
| 
 | ||||
| 	_, err := suite.Keeper.GetVaultTotalSupplied(suite.Ctx, vaultDenom) | ||||
| 	suite.Require().Error(err) | ||||
| 	suite.Require().ErrorIs(err, types.ErrVaultRecordNotFound) | ||||
| 	_, found := suite.Keeper.GetVaultTotalShares(suite.Ctx, vaultDenom) | ||||
| 	suite.Require().False(found) | ||||
| } | ||||
| 
 | ||||
| func (suite *vaultTestSuite) TestGetVaultTotalValue() { | ||||
| 	// TODO: After strategy implemented GetEstimatedTotalAssets
 | ||||
| 	vaultDenom := "usdx" | ||||
| 
 | ||||
| 	suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD) | ||||
| 
 | ||||
| 	totalValue, err := suite.Keeper.GetVaultTotalValue(suite.Ctx, vaultDenom) | ||||
| 	suite.Require().NoError(err) | ||||
| 	suite.Equal(sdk.NewInt(0), totalValue.Amount) | ||||
| } | ||||
| 
 | ||||
| func (suite *vaultTestSuite) TestGetVaultTotalValue_NotFound() { | ||||
| @ -83,17 +88,14 @@ func (suite *vaultTestSuite) TestGetVaultAccountSupplied() { | ||||
| 
 | ||||
| 	// Before deposit, account supplied is 0
 | ||||
| 
 | ||||
| 	_, err := suite.Keeper.GetVaultAccountSupplied(suite.Ctx, acc1.GetAddress()) | ||||
| 	suite.Require().Error(err) | ||||
| 	suite.Require().ErrorIs(err, types.ErrVaultShareRecordNotFound) | ||||
| 	_, found := suite.Keeper.GetVaultShareRecord(suite.Ctx, acc1.GetAddress()) | ||||
| 	suite.Require().False(found) | ||||
| 
 | ||||
| 	_, err = suite.Keeper.GetVaultAccountSupplied(suite.Ctx, acc2.GetAddress()) | ||||
| 	suite.Require().Error(err) | ||||
| 	suite.Require().ErrorIs(err, types.ErrVaultShareRecordNotFound) | ||||
| 	_, found = suite.Keeper.GetVaultShareRecord(suite.Ctx, acc2.GetAddress()) | ||||
| 	suite.Require().False(found) | ||||
| 
 | ||||
| 	// Deposits from both accounts
 | ||||
| 
 | ||||
| 	err = suite.Keeper.Deposit(suite.Ctx, acc1.GetAddress(), deposit1Amount) | ||||
| 	err := suite.Keeper.Deposit(suite.Ctx, acc1.GetAddress(), deposit1Amount) | ||||
| 	suite.Require().NoError(err) | ||||
| 
 | ||||
| 	err = suite.Keeper.Deposit(suite.Ctx, acc2.GetAddress(), deposit2Amount) | ||||
| @ -101,15 +103,15 @@ func (suite *vaultTestSuite) TestGetVaultAccountSupplied() { | ||||
| 
 | ||||
| 	// Check balances
 | ||||
| 
 | ||||
| 	vaultAcc1Supplied, err := suite.Keeper.GetVaultAccountSupplied(suite.Ctx, acc1.GetAddress()) | ||||
| 	suite.Require().NoError(err) | ||||
| 	vaultAcc1Supplied, found := suite.Keeper.GetVaultShareRecord(suite.Ctx, acc1.GetAddress()) | ||||
| 	suite.Require().True(found) | ||||
| 
 | ||||
| 	vaultAcc2Supplied, err := suite.Keeper.GetVaultAccountSupplied(suite.Ctx, acc2.GetAddress()) | ||||
| 	suite.Require().NoError(err) | ||||
| 	vaultAcc2Supplied, found := suite.Keeper.GetVaultShareRecord(suite.Ctx, acc2.GetAddress()) | ||||
| 	suite.Require().True(found) | ||||
| 
 | ||||
| 	// Account supply only includes the deposit from respective accounts
 | ||||
| 	suite.Equal(sdk.NewCoins(deposit1Amount), vaultAcc1Supplied) | ||||
| 	suite.Equal(sdk.NewCoins(deposit1Amount), vaultAcc2Supplied) | ||||
| 	suite.Equal(deposit1Amount.Amount.ToDec(), vaultAcc1Supplied.Shares.AmountOf(vaultDenom)) | ||||
| 	suite.Equal(deposit1Amount.Amount.ToDec(), vaultAcc2Supplied.Shares.AmountOf(vaultDenom)) | ||||
| } | ||||
| 
 | ||||
| func (suite *vaultTestSuite) TestGetVaultAccountValue() { | ||||
| @ -135,7 +137,7 @@ func (suite *vaultTestSuite) TestGetVaultAccountValue_VaultNotFound() { | ||||
| 
 | ||||
| 	_, err := suite.Keeper.GetVaultAccountValue(suite.Ctx, vaultDenom, acc.GetAddress()) | ||||
| 	suite.Require().Error(err) | ||||
| 	suite.Require().ErrorIs(err, types.ErrVaultRecordNotFound) | ||||
| 	suite.Require().Equal("account vault share record for usdx not found", err.Error()) | ||||
| } | ||||
| 
 | ||||
| func (suite *vaultTestSuite) TestGetVaultAccountValue_ShareNotFound() { | ||||
| @ -155,87 +157,5 @@ func (suite *vaultTestSuite) TestGetVaultAccountValue_ShareNotFound() { | ||||
| 	// Query from acc2 with no share record
 | ||||
| 	_, err = suite.Keeper.GetVaultAccountValue(suite.Ctx, vaultDenom, acc2.GetAddress()) | ||||
| 	suite.Require().Error(err) | ||||
| 	suite.Require().ErrorIs(err, types.ErrVaultShareRecordNotFound) | ||||
| } | ||||
| 
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
| // State methods
 | ||||
| 
 | ||||
| func (suite *vaultTestSuite) TestGetVaultRecord() { | ||||
| 	record := types.NewVaultRecord("usdx") | ||||
| 
 | ||||
| 	_, found := suite.Keeper.GetVaultRecord(suite.Ctx, record.Denom) | ||||
| 	suite.Require().False(found) | ||||
| 
 | ||||
| 	suite.Keeper.SetVaultRecord(suite.Ctx, record) | ||||
| 
 | ||||
| 	stateRecord, found := suite.Keeper.GetVaultRecord(suite.Ctx, record.Denom) | ||||
| 	suite.Require().True(found) | ||||
| 	suite.Require().Equal(record, stateRecord) | ||||
| } | ||||
| 
 | ||||
| func (suite *vaultTestSuite) TestUpdateVaultRecord() { | ||||
| 	record := types.NewVaultRecord("usdx") | ||||
| 
 | ||||
| 	record.TotalSupply = sdk.NewInt64Coin("usdx", 100) | ||||
| 
 | ||||
| 	// Update vault
 | ||||
| 	suite.Keeper.UpdateVaultRecord(suite.Ctx, record) | ||||
| 
 | ||||
| 	stateRecord, found := suite.Keeper.GetVaultRecord(suite.Ctx, record.Denom) | ||||
| 	suite.Require().True(found, "vault record with supply should exist") | ||||
| 	suite.Require().Equal(record, stateRecord) | ||||
| 
 | ||||
| 	// Remove supply
 | ||||
| 	record.TotalSupply = sdk.NewInt64Coin("usdx", 0) | ||||
| 	suite.Keeper.UpdateVaultRecord(suite.Ctx, record) | ||||
| 
 | ||||
| 	_, found = suite.Keeper.GetVaultRecord(suite.Ctx, record.Denom) | ||||
| 	suite.Require().False(found, "vault record with 0 supply should be deleted") | ||||
| } | ||||
| 
 | ||||
| func (suite *vaultTestSuite) TestGetVaultShareRecord() { | ||||
| 	vaultDenom := "usdx" | ||||
| 	startBalance := sdk.NewInt64Coin(vaultDenom, 1000) | ||||
| 	depositAmount := sdk.NewInt64Coin(vaultDenom, 100) | ||||
| 	acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) | ||||
| 
 | ||||
| 	record := types.NewVaultShareRecord(acc.GetAddress()) | ||||
| 
 | ||||
| 	// Check share doesn't exist before deposit
 | ||||
| 
 | ||||
| 	_, found := suite.Keeper.GetVaultShareRecord(suite.Ctx, acc.GetAddress()) | ||||
| 	suite.Require().False(found, "vault share record should not exist before deposit") | ||||
| 
 | ||||
| 	// Update share record
 | ||||
| 	record.AmountSupplied = sdk.NewCoins(depositAmount) | ||||
| 	suite.Keeper.SetVaultShareRecord(suite.Ctx, record) | ||||
| 
 | ||||
| 	// Check share exists and matches set value
 | ||||
| 	stateRecord, found := suite.Keeper.GetVaultShareRecord(suite.Ctx, acc.GetAddress()) | ||||
| 	suite.Require().True(found) | ||||
| 	suite.Require().Equal(record, stateRecord) | ||||
| } | ||||
| 
 | ||||
| func (suite *vaultTestSuite) TestUpdateVaultShareRecord() { | ||||
| 	vaultDenom := "usdx" | ||||
| 	startBalance := sdk.NewInt64Coin(vaultDenom, 1000) | ||||
| 	depositAmount := sdk.NewInt64Coin(vaultDenom, 100) | ||||
| 	acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) | ||||
| 
 | ||||
| 	record := types.NewVaultShareRecord(acc.GetAddress(), depositAmount) | ||||
| 
 | ||||
| 	// Update vault
 | ||||
| 	suite.Keeper.UpdateVaultShareRecord(suite.Ctx, record) | ||||
| 
 | ||||
| 	stateRecord, found := suite.Keeper.GetVaultShareRecord(suite.Ctx, acc.GetAddress()) | ||||
| 	suite.Require().True(found, "vault share record with supply should exist") | ||||
| 	suite.Require().Equal(record, stateRecord) | ||||
| 
 | ||||
| 	// Remove supply
 | ||||
| 	record.AmountSupplied = sdk.NewCoins() | ||||
| 	suite.Keeper.UpdateVaultShareRecord(suite.Ctx, record) | ||||
| 
 | ||||
| 	_, found = suite.Keeper.GetVaultShareRecord(suite.Ctx, acc.GetAddress()) | ||||
| 	suite.Require().False(found, "vault share record with 0 supply should be deleted") | ||||
| 	suite.Require().Equal("account vault share record for usdx not found", err.Error()) | ||||
| } | ||||
|  | ||||
| @ -28,34 +28,48 @@ func (k *Keeper) Withdraw(ctx sdk.Context, from sdk.AccAddress, wantAmount sdk.C | ||||
| 		return types.ErrVaultRecordNotFound | ||||
| 	} | ||||
| 
 | ||||
| 	// Get account value for vault
 | ||||
| 	vaultAccValue, err := k.GetVaultAccountValue(ctx, wantAmount.Denom, from) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if vaultAccValue.IsZero() { | ||||
| 		panic("vault account value is zero") | ||||
| 	} | ||||
| 
 | ||||
| 	// Get account share record for the vault
 | ||||
| 	vaultShareRecord, found := k.GetVaultShareRecord(ctx, from) | ||||
| 	if !found { | ||||
| 		return types.ErrVaultShareRecordNotFound | ||||
| 	} | ||||
| 
 | ||||
| 	// Percent of vault account value the account is withdrawing
 | ||||
| 	// This is the total account value, not just the supplied amount.
 | ||||
| 	withdrawAmountPercent := wantAmount.Amount.ToDec().Quo(vaultAccValue.Amount.ToDec()) | ||||
| 	withdrawShares, err := k.ConvertToShares(ctx, wantAmount) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to convert assets to shares: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Check if account is not withdrawing more than they have
 | ||||
| 	// account value < want withdraw amount
 | ||||
| 	if vaultAccValue.Amount.LT(wantAmount.Amount) { | ||||
| 	accCurrentShares := vaultShareRecord.Shares.AmountOf(wantAmount.Denom) | ||||
| 	// Check if account is not withdrawing more shares than they have
 | ||||
| 	if accCurrentShares.LT(withdrawShares.Amount) { | ||||
| 		return sdkerrors.Wrapf( | ||||
| 			types.ErrInsufficientValue, | ||||
| 			"account vault value of %s is less than %s desired withdraw amount", | ||||
| 			vaultAccValue, | ||||
| 			wantAmount, | ||||
| 			"account has less %s vault shares than withdraw shares, %s < %s", | ||||
| 			wantAmount.Denom, | ||||
| 			accCurrentShares, | ||||
| 			withdrawShares.Amount, | ||||
| 		) | ||||
| 	} | ||||
| 
 | ||||
| 	// Convert shares to amount to get truncated true share value
 | ||||
| 	withdrawAmount, err := k.ConvertToAssets(ctx, withdrawShares) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to convert shares to assets: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	accountValue, err := k.GetVaultAccountValue(ctx, wantAmount.Denom, from) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to get account value: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Check if withdrawAmount > account value
 | ||||
| 	if withdrawAmount.Amount.GT(accountValue.Amount) { | ||||
| 		return sdkerrors.Wrapf( | ||||
| 			types.ErrInsufficientValue, | ||||
| 			"account has less %s vault value than withdraw amount, %s < %s", | ||||
| 			withdrawAmount.Denom, | ||||
| 			accountValue.Amount, | ||||
| 			withdrawAmount.Amount, | ||||
| 		) | ||||
| 	} | ||||
| 
 | ||||
| @ -68,8 +82,8 @@ func (k *Keeper) Withdraw(ctx sdk.Context, from sdk.AccAddress, wantAmount sdk.C | ||||
| 	// Not necessary to check if amount denom is allowed for the strategy, as
 | ||||
| 	// there would be no vault record if it weren't allowed.
 | ||||
| 
 | ||||
| 	// Withdraw the wantAmount from the strategy
 | ||||
| 	if err := strategy.Withdraw(ctx, wantAmount); err != nil { | ||||
| 	// Withdraw the withdrawAmount from the strategy
 | ||||
| 	if err := strategy.Withdraw(ctx, withdrawAmount); err != nil { | ||||
| 		return fmt.Errorf("failed to withdraw from strategy: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| @ -79,27 +93,33 @@ func (k *Keeper) Withdraw(ctx sdk.Context, from sdk.AccAddress, wantAmount sdk.C | ||||
| 		ctx, | ||||
| 		types.ModuleName, | ||||
| 		from, | ||||
| 		sdk.NewCoins(wantAmount), | ||||
| 		sdk.NewCoins(withdrawAmount), | ||||
| 	); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Shares withdrawn from vault
 | ||||
| 	// For example:
 | ||||
| 	// account supplied = 10hard
 | ||||
| 	// account value    = 20hard
 | ||||
| 	// wantAmount       = 10hard
 | ||||
| 	// withdrawAmountPercent = 10hard / 20hard = 0.5
 | ||||
| 	// sharesWithdrawn = 0.5 * 10hard = 5hard
 | ||||
| 	vaultShareAmount := vaultShareRecord.AmountSupplied.AmountOf(wantAmount.Denom) | ||||
| 	sharesWithdrawn := sdk.NewCoin(wantAmount.Denom, vaultShareAmount. | ||||
| 		ToDec(). | ||||
| 		Mul(withdrawAmountPercent). | ||||
| 		TruncateInt()) | ||||
| 	// Check if new account balance of shares results in account share value
 | ||||
| 	// of < 1 of a sdk.Coin. This share value is not able to be withdrawn and
 | ||||
| 	// should just be removed.
 | ||||
| 	isDust, err := k.ShareIsDust( | ||||
| 		ctx, | ||||
| 		vaultShareRecord.Shares.GetShare(withdrawAmount.Denom).Sub(withdrawShares), | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Decrement VaultRecord and VaultShareRecord supplies
 | ||||
| 	vaultRecord.TotalSupply = vaultRecord.TotalSupply.Sub(sharesWithdrawn) | ||||
| 	vaultShareRecord.AmountSupplied = vaultShareRecord.AmountSupplied.Sub(sdk.NewCoins(sharesWithdrawn)) | ||||
| 	if isDust { | ||||
| 		// Modify withdrawShares to subtract entire share balance for denom
 | ||||
| 		// This does not modify the actual withdraw coin amount as the
 | ||||
| 		// difference is < 1coin.
 | ||||
| 		withdrawShares = vaultShareRecord.Shares.GetShare(withdrawAmount.Denom) | ||||
| 	} | ||||
| 
 | ||||
| 	// Decrement VaultRecord and VaultShareRecord supplies - must delete same
 | ||||
| 	// amounts
 | ||||
| 	vaultShareRecord.Shares = vaultShareRecord.Shares.Sub(withdrawShares) | ||||
| 	vaultRecord.TotalShares = vaultRecord.TotalShares.Sub(withdrawShares) | ||||
| 
 | ||||
| 	// Update VaultRecord and VaultShareRecord, deletes if zero supply
 | ||||
| 	k.UpdateVaultRecord(ctx, vaultRecord) | ||||
| @ -108,9 +128,10 @@ func (k *Keeper) Withdraw(ctx sdk.Context, from sdk.AccAddress, wantAmount sdk.C | ||||
| 	ctx.EventManager().EmitEvent( | ||||
| 		sdk.NewEvent( | ||||
| 			types.EventTypeVaultWithdraw, | ||||
| 			sdk.NewAttribute(types.AttributeKeyVaultDenom, wantAmount.Denom), | ||||
| 			sdk.NewAttribute(types.AttributeKeyVaultDenom, withdrawAmount.Denom), | ||||
| 			sdk.NewAttribute(types.AttributeKeyOwner, from.String()), | ||||
| 			sdk.NewAttribute(sdk.AttributeKeyAmount, wantAmount.Amount.String()), | ||||
| 			sdk.NewAttribute(types.AttributeKeyShares, withdrawShares.Amount.String()), | ||||
| 			sdk.NewAttribute(sdk.AttributeKeyAmount, withdrawAmount.Amount.String()), | ||||
| 		), | ||||
| 	) | ||||
| 
 | ||||
|  | ||||
| @ -76,7 +76,9 @@ func (suite *withdrawTestSuite) TestWithdraw_NoVaultShareRecord() { | ||||
| 	) | ||||
| 
 | ||||
| 	suite.VaultTotalValuesEqual(sdk.NewCoins(acc1DepositAmount)) | ||||
| 	suite.VaultTotalSuppliedEqual(sdk.NewCoins(acc1DepositAmount)) | ||||
| 	suite.VaultTotalSharesEqual(types.NewVaultShares( | ||||
| 		types.NewVaultShare(acc1DepositAmount.Denom, acc1DepositAmount.Amount.ToDec()), | ||||
| 	)) | ||||
| } | ||||
| 
 | ||||
| func (suite *withdrawTestSuite) TestWithdraw_ExceedBalance() { | ||||
| @ -103,7 +105,9 @@ func (suite *withdrawTestSuite) TestWithdraw_ExceedBalance() { | ||||
| 	) | ||||
| 
 | ||||
| 	suite.VaultTotalValuesEqual(sdk.NewCoins(depositAmount)) | ||||
| 	suite.VaultTotalSuppliedEqual(sdk.NewCoins(depositAmount)) | ||||
| 	suite.VaultTotalSharesEqual(types.NewVaultShares( | ||||
| 		types.NewVaultShare(depositAmount.Denom, depositAmount.Amount.ToDec()), | ||||
| 	)) | ||||
| } | ||||
| 
 | ||||
| func (suite *withdrawTestSuite) TestWithdraw_Zero() { | ||||
|  | ||||
| @ -256,21 +256,29 @@ func (suite *Suite) VaultTotalValuesEqual(expected sdk.Coins) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (suite *Suite) VaultTotalSuppliedEqual(expected sdk.Coins) { | ||||
| 	for _, coin := range expected { | ||||
| 		vaultBal, err := suite.Keeper.GetVaultTotalSupplied(suite.Ctx, coin.Denom) | ||||
| 		suite.Require().NoError(err, "failed to get vault balance") | ||||
| 		suite.Require().Equal(coin, vaultBal) | ||||
| func (suite *Suite) VaultTotalSharesEqual(expected types.VaultShares) { | ||||
| 	for _, share := range expected { | ||||
| 		vaultBal, found := suite.Keeper.GetVaultTotalShares(suite.Ctx, share.Denom) | ||||
| 		suite.Require().Truef(found, "%s vault does not exist", share.Denom) | ||||
| 		suite.Require().Equal(share.Amount, vaultBal.Amount) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (suite *Suite) AccountTotalSuppliedEqual(accs []sdk.AccAddress, supplies []sdk.Coins) { | ||||
| func (suite *Suite) VaultAccountSharesEqual(accs []sdk.AccAddress, supplies []sdk.Coins) { | ||||
| 	for i, acc := range accs { | ||||
| 		coins := supplies[i] | ||||
| 
 | ||||
| 		accVaultBal, err := suite.Keeper.GetVaultAccountSupplied(suite.Ctx, acc) | ||||
| 		suite.Require().NoError(err) | ||||
| 		suite.Require().True(coins.IsEqual(accVaultBal), "expected account vault balance to equal coins %s, but got %s", coins, accVaultBal) | ||||
| 		accVaultBal, found := suite.Keeper.GetVaultAccountShares(suite.Ctx, acc) | ||||
| 		suite.Require().True(found) | ||||
| 
 | ||||
| 		for _, coin := range coins { | ||||
| 			suite.Require().Equal( | ||||
| 				coin.Amount, | ||||
| 				accVaultBal.AmountOf(coin.Denom), | ||||
| 				"expected account vault balance to equal coins %s, but got %s", | ||||
| 				coins, accVaultBal, | ||||
| 			) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -26,10 +26,3 @@ func NewQueryDepositsRequest( | ||||
| 		Pagination: pagination, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // NewQueryTotalDepositedRequest returns a new QueryTotalDepositedRequest
 | ||||
| func NewQueryTotalDepositedRequest(denom string) *QueryTotalDepositedRequest { | ||||
| 	return &QueryTotalDepositedRequest{ | ||||
| 		Denom: denom, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -193,8 +193,8 @@ type VaultResponse struct { | ||||
| 	Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` | ||||
| 	// VaultStrategy is the strategy used for this vault.
 | ||||
| 	VaultStrategy StrategyType `protobuf:"varint,2,opt,name=vault_strategy,json=vaultStrategy,proto3,enum=kava.earn.v1beta1.StrategyType" json:"vault_strategy,omitempty"` | ||||
| 	// TotalSupplied is the total amount of denom coins supplied to the vault.
 | ||||
| 	TotalSupplied github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,3,opt,name=total_supplied,json=totalSupplied,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"total_supplied"` | ||||
| 	// TotalShares is the total amount of shares issued to depositors.
 | ||||
| 	TotalShares string `protobuf:"bytes,3,opt,name=total_shares,json=totalShares,proto3" json:"total_shares,omitempty"` | ||||
| 	// TotalValue is the total value of denom coins supplied to the vault if the
 | ||||
| 	// vault were to be liquidated.
 | ||||
| 	TotalValue github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,4,opt,name=total_value,json=totalValue,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"total_value"` | ||||
| @ -321,8 +321,8 @@ var xxx_messageInfo_QueryDepositsResponse proto.InternalMessageInfo | ||||
| type DepositResponse struct { | ||||
| 	// depositor represents the owner of the deposit.
 | ||||
| 	Depositor string `protobuf:"bytes,1,opt,name=depositor,proto3" json:"depositor,omitempty"` | ||||
| 	// Amount represents the amount supplied to vaults.
 | ||||
| 	AmountSupplied github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=amount_supplied,json=amountSupplied,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount_supplied"` | ||||
| 	// Shares represent the issued shares from their corresponding vaults.
 | ||||
| 	Shares VaultShares `protobuf:"bytes,2,rep,name=shares,proto3,castrepeated=VaultShares" json:"shares"` | ||||
| 	// Value represents the total accumulated value of denom coins supplied to
 | ||||
| 	// vaults. This may be greater than or equal to amount_supplied depending on
 | ||||
| 	// the strategy.
 | ||||
| @ -362,83 +362,6 @@ func (m *DepositResponse) XXX_DiscardUnknown() { | ||||
| 
 | ||||
| var xxx_messageInfo_DepositResponse proto.InternalMessageInfo | ||||
| 
 | ||||
| // QueryTotalDepositedRequest is the request type for the Query/TotalDeposited RPC method.
 | ||||
| type QueryTotalDepositedRequest struct { | ||||
| 	// denom represents the vault denom to query total deposited amount for.
 | ||||
| 	Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (m *QueryTotalDepositedRequest) Reset()         { *m = QueryTotalDepositedRequest{} } | ||||
| func (m *QueryTotalDepositedRequest) String() string { return proto.CompactTextString(m) } | ||||
| func (*QueryTotalDepositedRequest) ProtoMessage()    {} | ||||
| func (*QueryTotalDepositedRequest) Descriptor() ([]byte, []int) { | ||||
| 	return fileDescriptor_63f8dee2f3192a6b, []int{8} | ||||
| } | ||||
| func (m *QueryTotalDepositedRequest) XXX_Unmarshal(b []byte) error { | ||||
| 	return m.Unmarshal(b) | ||||
| } | ||||
| func (m *QueryTotalDepositedRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||
| 	if deterministic { | ||||
| 		return xxx_messageInfo_QueryTotalDepositedRequest.Marshal(b, m, deterministic) | ||||
| 	} else { | ||||
| 		b = b[:cap(b)] | ||||
| 		n, err := m.MarshalToSizedBuffer(b) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return b[:n], nil | ||||
| 	} | ||||
| } | ||||
| func (m *QueryTotalDepositedRequest) XXX_Merge(src proto.Message) { | ||||
| 	xxx_messageInfo_QueryTotalDepositedRequest.Merge(m, src) | ||||
| } | ||||
| func (m *QueryTotalDepositedRequest) XXX_Size() int { | ||||
| 	return m.Size() | ||||
| } | ||||
| func (m *QueryTotalDepositedRequest) XXX_DiscardUnknown() { | ||||
| 	xxx_messageInfo_QueryTotalDepositedRequest.DiscardUnknown(m) | ||||
| } | ||||
| 
 | ||||
| var xxx_messageInfo_QueryTotalDepositedRequest proto.InternalMessageInfo | ||||
| 
 | ||||
| // QueryTotalDepositedResponse is the response type for the Query/TotalDeposited RPC method.
 | ||||
| type QueryTotalDepositedResponse struct { | ||||
| 	SuppliedCoins github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=supplied_coins,json=suppliedCoins,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"supplied_coins"` | ||||
| } | ||||
| 
 | ||||
| func (m *QueryTotalDepositedResponse) Reset()         { *m = QueryTotalDepositedResponse{} } | ||||
| func (m *QueryTotalDepositedResponse) String() string { return proto.CompactTextString(m) } | ||||
| func (*QueryTotalDepositedResponse) ProtoMessage()    {} | ||||
| func (*QueryTotalDepositedResponse) Descriptor() ([]byte, []int) { | ||||
| 	return fileDescriptor_63f8dee2f3192a6b, []int{9} | ||||
| } | ||||
| func (m *QueryTotalDepositedResponse) XXX_Unmarshal(b []byte) error { | ||||
| 	return m.Unmarshal(b) | ||||
| } | ||||
| func (m *QueryTotalDepositedResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||
| 	if deterministic { | ||||
| 		return xxx_messageInfo_QueryTotalDepositedResponse.Marshal(b, m, deterministic) | ||||
| 	} else { | ||||
| 		b = b[:cap(b)] | ||||
| 		n, err := m.MarshalToSizedBuffer(b) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return b[:n], nil | ||||
| 	} | ||||
| } | ||||
| func (m *QueryTotalDepositedResponse) XXX_Merge(src proto.Message) { | ||||
| 	xxx_messageInfo_QueryTotalDepositedResponse.Merge(m, src) | ||||
| } | ||||
| func (m *QueryTotalDepositedResponse) XXX_Size() int { | ||||
| 	return m.Size() | ||||
| } | ||||
| func (m *QueryTotalDepositedResponse) XXX_DiscardUnknown() { | ||||
| 	xxx_messageInfo_QueryTotalDepositedResponse.DiscardUnknown(m) | ||||
| } | ||||
| 
 | ||||
| var xxx_messageInfo_QueryTotalDepositedResponse proto.InternalMessageInfo | ||||
| 
 | ||||
| func init() { | ||||
| 	proto.RegisterType((*QueryParamsRequest)(nil), "kava.earn.v1beta1.QueryParamsRequest") | ||||
| 	proto.RegisterType((*QueryParamsResponse)(nil), "kava.earn.v1beta1.QueryParamsResponse") | ||||
| @ -448,67 +371,60 @@ func init() { | ||||
| 	proto.RegisterType((*QueryDepositsRequest)(nil), "kava.earn.v1beta1.QueryDepositsRequest") | ||||
| 	proto.RegisterType((*QueryDepositsResponse)(nil), "kava.earn.v1beta1.QueryDepositsResponse") | ||||
| 	proto.RegisterType((*DepositResponse)(nil), "kava.earn.v1beta1.DepositResponse") | ||||
| 	proto.RegisterType((*QueryTotalDepositedRequest)(nil), "kava.earn.v1beta1.QueryTotalDepositedRequest") | ||||
| 	proto.RegisterType((*QueryTotalDepositedResponse)(nil), "kava.earn.v1beta1.QueryTotalDepositedResponse") | ||||
| } | ||||
| 
 | ||||
| func init() { proto.RegisterFile("kava/earn/v1beta1/query.proto", fileDescriptor_63f8dee2f3192a6b) } | ||||
| 
 | ||||
| var fileDescriptor_63f8dee2f3192a6b = []byte{ | ||||
| 	// 833 bytes of a gzipped FileDescriptorProto
 | ||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0x4f, 0x6f, 0xd3, 0x48, | ||||
| 	0x14, 0x8f, 0xd3, 0x26, 0x6a, 0x27, 0x4a, 0xaa, 0x9d, 0xcd, 0x4a, 0x49, 0xba, 0x75, 0x52, 0x57, | ||||
| 	0xdb, 0x66, 0xab, 0x8d, 0xbd, 0xcd, 0x4a, 0xbb, 0x97, 0xd5, 0x4a, 0x9b, 0xad, 0x16, 0xf5, 0x82, | ||||
| 	0xc0, 0x2d, 0x3d, 0x20, 0xa1, 0x68, 0x12, 0x8f, 0x8c, 0xd5, 0xc4, 0xe3, 0xda, 0x93, 0x40, 0x41, | ||||
| 	0x5c, 0xfa, 0x09, 0x40, 0x7c, 0x03, 0x40, 0x1c, 0xb8, 0x21, 0xfa, 0x21, 0x7a, 0xac, 0xca, 0x05, | ||||
| 	0x71, 0x28, 0xd0, 0xf2, 0x41, 0x90, 0xe7, 0x4f, 0x52, 0x27, 0x0e, 0xe9, 0xa1, 0xa7, 0x64, 0xe6, | ||||
| 	0xbd, 0xf7, 0xfb, 0xfd, 0xe6, 0xcd, 0x6f, 0x9e, 0xc1, 0xd2, 0x1e, 0xea, 0x23, 0x03, 0x23, 0xdf, | ||||
| 	0x35, 0xfa, 0x1b, 0x2d, 0x4c, 0xd1, 0x86, 0xb1, 0xdf, 0xc3, 0xfe, 0x81, 0xee, 0xf9, 0x84, 0x12, | ||||
| 	0xf8, 0x43, 0x18, 0xd6, 0xc3, 0xb0, 0x2e, 0xc2, 0xa5, 0x62, 0x9b, 0x04, 0x5d, 0x12, 0x34, 0x59, | ||||
| 	0x82, 0xc1, 0x17, 0x3c, 0xbb, 0xb4, 0xce, 0x57, 0x46, 0x0b, 0x05, 0x98, 0xc3, 0x0c, 0x40, 0x3d, | ||||
| 	0x64, 0x3b, 0x2e, 0xa2, 0x0e, 0x71, 0x45, 0xae, 0x7a, 0x39, 0x57, 0x66, 0xb5, 0x89, 0x23, 0xe3, | ||||
| 	0x79, 0x9b, 0xd8, 0x84, 0x73, 0x84, 0xff, 0xc4, 0xee, 0xcf, 0x36, 0x21, 0x76, 0x07, 0x1b, 0xc8, | ||||
| 	0x73, 0x0c, 0xe4, 0xba, 0x84, 0x32, 0x48, 0xc9, 0xaf, 0x8e, 0x1f, 0xc6, 0x43, 0x3e, 0xea, 0xca, | ||||
| 	0x78, 0x65, 0x3c, 0x1e, 0x50, 0x1f, 0x51, 0x6c, 0x8b, 0xf3, 0x6a, 0x79, 0x00, 0x6f, 0x87, 0xba, | ||||
| 	0x6f, 0xb1, 0x32, 0x13, 0xef, 0xf7, 0x70, 0x40, 0xb5, 0x9b, 0xe0, 0xc7, 0xc8, 0x6e, 0xe0, 0x11, | ||||
| 	0x37, 0xc0, 0xf0, 0x2f, 0x90, 0xe6, 0xf0, 0x05, 0xa5, 0xa2, 0x54, 0x33, 0xf5, 0xa2, 0x3e, 0xd6, | ||||
| 	0x2d, 0x9d, 0x97, 0x34, 0x66, 0x8f, 0xcf, 0xca, 0x09, 0x53, 0xa4, 0x6b, 0xeb, 0x82, 0x65, 0x17, | ||||
| 	0xf5, 0x3a, 0x54, 0xb2, 0xc0, 0x3c, 0x48, 0x59, 0xd8, 0x25, 0x5d, 0x86, 0x36, 0x6f, 0xf2, 0x85, | ||||
| 	0x76, 0x47, 0x70, 0xcb, 0x5c, 0xc1, 0xfd, 0x0f, 0x48, 0xf7, 0xd9, 0x4e, 0x41, 0xa9, 0xcc, 0x54, | ||||
| 	0x33, 0xf5, 0x4a, 0x0c, 0x37, 0x2b, 0x91, 0x15, 0x52, 0x02, 0xaf, 0xd2, 0xde, 0x26, 0x41, 0x36, | ||||
| 	0x12, 0x8f, 0xa7, 0x87, 0xff, 0x83, 0x1c, 0xab, 0x68, 0xca, 0x46, 0x15, 0x92, 0x15, 0xa5, 0x9a, | ||||
| 	0xab, 0x97, 0x63, 0xf8, 0xb6, 0x45, 0xca, 0xce, 0x81, 0x87, 0xcd, 0x2c, 0x2b, 0x93, 0x5b, 0xb0, | ||||
| 	0x0d, 0x72, 0x94, 0x50, 0xd4, 0x69, 0x06, 0x3d, 0xcf, 0xeb, 0x38, 0xd8, 0x2a, 0xcc, 0x84, 0x34, | ||||
| 	0x8d, 0xbf, 0x43, 0x55, 0x1f, 0xcf, 0xca, 0xab, 0xb6, 0x43, 0xef, 0xf7, 0x5a, 0x7a, 0x9b, 0x74, | ||||
| 	0x85, 0xa7, 0xc4, 0x4f, 0x2d, 0xb0, 0xf6, 0x0c, 0x7a, 0xe0, 0xe1, 0x40, 0xdf, 0x72, 0xe9, 0xe9, | ||||
| 	0x51, 0x0d, 0x08, 0xcb, 0x6d, 0xb9, 0xd4, 0xcc, 0x32, 0xcc, 0x6d, 0x01, 0x09, 0xef, 0x81, 0x0c, | ||||
| 	0x27, 0xe9, 0xa3, 0x4e, 0x0f, 0x17, 0x66, 0xaf, 0x81, 0x01, 0x30, 0xc0, 0xdd, 0x10, 0x4f, 0x7b, | ||||
| 	0xa5, 0x80, 0x3c, 0xbb, 0x8b, 0x4d, 0xec, 0x91, 0xc0, 0x19, 0xde, 0x9c, 0x0e, 0x52, 0xe4, 0x81, | ||||
| 	0x8b, 0x7d, 0xde, 0xba, 0x46, 0xe1, 0xf4, 0xa8, 0x96, 0x17, 0x18, 0xff, 0x5a, 0x96, 0x8f, 0x83, | ||||
| 	0x60, 0x9b, 0xfa, 0x8e, 0x6b, 0x9b, 0x3c, 0x6d, 0xd8, 0xea, 0x64, 0xb4, 0xd5, 0x60, 0xf8, 0x4a, | ||||
| 	0x58, 0x7b, 0x32, 0xf5, 0x55, 0x5d, 0xe0, 0x84, 0xcf, 0x44, 0xe7, 0x2f, 0x73, 0x68, 0x2d, 0x1b, | ||||
| 	0x0b, 0x05, 0xe6, 0xa5, 0x4a, 0xed, 0xb5, 0x02, 0x7e, 0x1a, 0x91, 0x29, 0xae, 0x78, 0x13, 0xcc, | ||||
| 	0x59, 0x62, 0x4f, 0xd8, 0x46, 0x8b, 0xb9, 0x46, 0x51, 0x36, 0x62, 0x9c, 0x41, 0x25, 0xbc, 0x11, | ||||
| 	0xd1, 0x99, 0x64, 0x3a, 0xd7, 0xa6, 0xea, 0xe4, 0x60, 0x11, 0xa1, 0x2f, 0x92, 0x60, 0x61, 0x84, | ||||
| 	0x0c, 0xfe, 0x09, 0xe6, 0x05, 0x11, 0x99, 0xde, 0xce, 0x61, 0x2a, 0xa4, 0x60, 0x01, 0x75, 0x49, | ||||
| 	0xcf, 0xa5, 0x43, 0x83, 0x25, 0xd9, 0x09, 0x8b, 0x11, 0x65, 0x52, 0xd3, 0x7f, 0xc4, 0x71, 0x1b, | ||||
| 	0xbf, 0x87, 0x07, 0x7b, 0xf3, 0xa9, 0x5c, 0xbd, 0x82, 0x33, 0xc2, 0x82, 0xc0, 0xcc, 0x71, 0x8e, | ||||
| 	0x81, 0xe1, 0x10, 0x48, 0x71, 0xab, 0xcd, 0x5c, 0x3f, 0x17, 0x47, 0xd6, 0xea, 0xa0, 0xc4, 0x2e, | ||||
| 	0x73, 0x27, 0xf4, 0xa1, 0xe8, 0x16, 0xb6, 0xbe, 0x3f, 0x33, 0x9e, 0x29, 0x60, 0x31, 0xb6, 0x48, | ||||
| 	0x34, 0xd9, 0x07, 0x39, 0xd9, 0xa5, 0x66, 0x38, 0x72, 0xa5, 0x1b, 0xae, 0x55, 0x7f, 0x56, 0x52, | ||||
| 	0xb0, 0x65, 0xfd, 0xdd, 0x2c, 0x48, 0x31, 0x4d, 0xf0, 0x11, 0x48, 0xf3, 0xa9, 0x08, 0x7f, 0x89, | ||||
| 	0x71, 0xdf, 0xf8, 0xf8, 0x2d, 0xad, 0x4e, 0x4b, 0xe3, 0xc7, 0xd2, 0x96, 0x0f, 0xdf, 0x7f, 0x7d, | ||||
| 	0x9e, 0x5c, 0x84, 0x45, 0x63, 0xd2, 0x77, 0x00, 0x1e, 0x2a, 0x20, 0xcd, 0x27, 0xe9, 0x64, 0xf2, | ||||
| 	0xc8, 0x54, 0x9e, 0x4c, 0x1e, 0x1d, 0xc8, 0xda, 0xaf, 0x8c, 0x7c, 0x05, 0x2e, 0xc7, 0x90, 0xf3, | ||||
| 	0x99, 0x6b, 0x3c, 0x66, 0xb7, 0xf3, 0x24, 0x14, 0x31, 0x27, 0xdf, 0x26, 0x5c, 0x9b, 0x84, 0x3f, | ||||
| 	0x32, 0x64, 0x4a, 0xd5, 0xe9, 0x89, 0x42, 0xca, 0x0a, 0x93, 0xb2, 0x04, 0x17, 0x63, 0xa4, 0x0c, | ||||
| 	0x5e, 0xf1, 0x4b, 0x05, 0xe4, 0xa2, 0xf6, 0x80, 0xb5, 0x49, 0x0c, 0xb1, 0xde, 0x2b, 0xe9, 0x57, | ||||
| 	0x4d, 0x17, 0xb2, 0xea, 0x4c, 0xd6, 0x6f, 0x70, 0x3d, 0x46, 0x16, 0x9b, 0xb2, 0x35, 0x4b, 0xd6, | ||||
| 	0xc8, 0x56, 0x35, 0x36, 0x8f, 0xbf, 0xa8, 0x89, 0xe3, 0x73, 0x55, 0x39, 0x39, 0x57, 0x95, 0xcf, | ||||
| 	0xe7, 0xaa, 0xf2, 0xf4, 0x42, 0x4d, 0x9c, 0x5c, 0xa8, 0x89, 0x0f, 0x17, 0x6a, 0xe2, 0xee, 0xe5, | ||||
| 	0x91, 0x1e, 0x62, 0xd6, 0x3a, 0xa8, 0x15, 0x70, 0xf4, 0x87, 0x1c, 0x9f, 0x19, 0xb2, 0x95, 0x66, | ||||
| 	0x1f, 0xf7, 0x3f, 0xbe, 0x05, 0x00, 0x00, 0xff, 0xff, 0x3b, 0xb1, 0x1a, 0x34, 0xed, 0x08, 0x00, | ||||
| 	0x00, | ||||
| 	// 762 bytes of a gzipped FileDescriptorProto
 | ||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0xcf, 0x4f, 0x13, 0x4f, | ||||
| 	0x14, 0xef, 0xb6, 0xb4, 0x81, 0xe9, 0x17, 0xbe, 0x71, 0xa8, 0x49, 0x5b, 0x64, 0x5b, 0x96, 0x08, | ||||
| 	0x95, 0xa4, 0xbb, 0x52, 0x13, 0xbd, 0x18, 0x13, 0x2b, 0xd1, 0xe0, 0xc1, 0xe8, 0xa2, 0x1c, 0x4c, | ||||
| 	0x4c, 0x33, 0xa5, 0x93, 0x65, 0x43, 0xbb, 0xb3, 0xec, 0x4c, 0xab, 0x68, 0xbc, 0x70, 0x37, 0x31, | ||||
| 	0xf1, 0x5f, 0x30, 0x1e, 0x3c, 0xf3, 0x47, 0x70, 0x24, 0x78, 0x31, 0x1e, 0x50, 0xc1, 0xbb, 0x57, | ||||
| 	0x8f, 0x66, 0x7e, 0x2c, 0x65, 0xdb, 0x6d, 0xea, 0xa9, 0x9d, 0xf7, 0xe3, 0xf3, 0xf9, 0xbc, 0x37, | ||||
| 	0x6f, 0xde, 0x82, 0xf9, 0x1d, 0xd4, 0x43, 0x16, 0x46, 0x81, 0x67, 0xf5, 0x56, 0x9b, 0x98, 0xa1, | ||||
| 	0x55, 0x6b, 0xb7, 0x8b, 0x83, 0x3d, 0xd3, 0x0f, 0x08, 0x23, 0xf0, 0x12, 0x77, 0x9b, 0xdc, 0x6d, | ||||
| 	0x2a, 0x77, 0xb1, 0xb0, 0x45, 0x68, 0x87, 0xd0, 0x86, 0x08, 0xb0, 0xe4, 0x41, 0x46, 0x17, 0x57, | ||||
| 	0xe4, 0xc9, 0x6a, 0x22, 0x8a, 0x25, 0xcc, 0x39, 0xa8, 0x8f, 0x1c, 0xd7, 0x43, 0xcc, 0x25, 0x9e, | ||||
| 	0x8a, 0xd5, 0x2f, 0xc6, 0x86, 0x51, 0x5b, 0xc4, 0x0d, 0xfd, 0x39, 0x87, 0x38, 0x44, 0x72, 0xf0, | ||||
| 	0x7f, 0xca, 0x7a, 0xc5, 0x21, 0xc4, 0x69, 0x63, 0x0b, 0xf9, 0xae, 0x85, 0x3c, 0x8f, 0x30, 0x01, | ||||
| 	0x19, 0xf2, 0xeb, 0xc3, 0xc5, 0xf8, 0x28, 0x40, 0x9d, 0xd0, 0x5f, 0x1e, 0xf6, 0x53, 0x16, 0x20, | ||||
| 	0x86, 0x1d, 0x55, 0x6f, 0x31, 0xa6, 0x1d, 0x3d, 0xd4, 0x6d, 0x33, 0xe9, 0x36, 0x72, 0x00, 0x3e, | ||||
| 	0xe1, 0x65, 0x3d, 0x16, 0xa8, 0x36, 0xde, 0xed, 0x62, 0xca, 0x8c, 0x47, 0x60, 0x36, 0x62, 0xa5, | ||||
| 	0x3e, 0xf1, 0x28, 0x86, 0xb7, 0x40, 0x46, 0xb2, 0xe7, 0xb5, 0xb2, 0x56, 0xc9, 0xd6, 0x0a, 0xe6, | ||||
| 	0x50, 0x33, 0x4d, 0x99, 0x52, 0x9f, 0x38, 0x3c, 0x29, 0x25, 0x6c, 0x15, 0x6e, 0xac, 0x28, 0x96, | ||||
| 	0x4d, 0xce, 0x1c, 0xb2, 0xc0, 0x1c, 0x48, 0xb7, 0xb0, 0x47, 0x3a, 0x02, 0x6d, 0xca, 0x96, 0x07, | ||||
| 	0xe3, 0x99, 0xe2, 0x0e, 0x63, 0x15, 0xf7, 0x1d, 0x90, 0x11, 0xba, 0x39, 0x77, 0xaa, 0x92, 0xad, | ||||
| 	0x95, 0x63, 0xb8, 0x45, 0x4a, 0x98, 0x11, 0x4a, 0x90, 0x59, 0xc6, 0x6f, 0x0d, 0x4c, 0x47, 0xfc, | ||||
| 	0xf1, 0xf4, 0xf0, 0x3e, 0x98, 0x11, 0x19, 0x8d, 0xb0, 0x8f, 0xf9, 0x64, 0x59, 0xab, 0xcc, 0xd4, | ||||
| 	0x4a, 0x31, 0x7c, 0x1b, 0x2a, 0xe4, 0xe9, 0x9e, 0x8f, 0xed, 0x69, 0x91, 0x16, 0x9a, 0xe0, 0x02, | ||||
| 	0xf8, 0x8f, 0x11, 0x86, 0xda, 0x0d, 0xba, 0x8d, 0x02, 0x4c, 0xf3, 0x29, 0x41, 0x92, 0x15, 0xb6, | ||||
| 	0x0d, 0x61, 0x82, 0x2f, 0x80, 0x3c, 0x36, 0x7a, 0xa8, 0xdd, 0xc5, 0xf9, 0x09, 0x1e, 0x51, 0xbf, | ||||
| 	0xcd, 0x55, 0x7f, 0x3b, 0x29, 0x2d, 0x39, 0x2e, 0xdb, 0xee, 0x36, 0xcd, 0x2d, 0xd2, 0x51, 0x23, | ||||
| 	0xa9, 0x7e, 0xaa, 0xb4, 0xb5, 0x63, 0xb1, 0x3d, 0x1f, 0x53, 0x73, 0xdd, 0x63, 0xc7, 0x07, 0x55, | ||||
| 	0xa0, 0x26, 0x76, 0xdd, 0x63, 0x36, 0x10, 0x80, 0x9b, 0x1c, 0xcf, 0xf8, 0xa8, 0x81, 0x9c, 0xe8, | ||||
| 	0xe4, 0x1a, 0xf6, 0x09, 0x75, 0xfb, 0x7d, 0x37, 0x41, 0x9a, 0xbc, 0xf4, 0x70, 0x20, 0x0b, 0xaf, | ||||
| 	0xe7, 0x8f, 0x0f, 0xaa, 0x39, 0x85, 0x71, 0xb7, 0xd5, 0x0a, 0x30, 0xa5, 0x1b, 0x2c, 0x70, 0x3d, | ||||
| 	0xc7, 0x96, 0x61, 0xfd, 0x46, 0x25, 0xa3, 0x8d, 0x02, 0xfd, 0x27, 0x20, 0xca, 0xcb, 0xd6, 0x96, | ||||
| 	0x4c, 0x85, 0xc3, 0xdf, 0x80, 0x29, 0x9f, 0x5d, 0x7f, 0x30, 0x1c, 0xac, 0x14, 0xd8, 0x17, 0x32, | ||||
| 	0x8d, 0x4f, 0x1a, 0xb8, 0x3c, 0x20, 0x53, 0x5d, 0xd0, 0x1a, 0x98, 0x6c, 0x29, 0x9b, 0xba, 0x74, | ||||
| 	0x23, 0xe6, 0x12, 0x54, 0xda, 0xc0, 0xb5, 0x9f, 0x67, 0xc2, 0x07, 0x11, 0x9d, 0x49, 0xa1, 0x73, | ||||
| 	0x79, 0xac, 0x4e, 0x09, 0x16, 0x11, 0xfa, 0x47, 0x03, 0xff, 0x0f, 0x90, 0xc1, 0x9b, 0x60, 0x4a, | ||||
| 	0x11, 0x91, 0xf1, 0xed, 0xec, 0x87, 0xc2, 0x87, 0x20, 0xa3, 0xe6, 0x22, 0x29, 0x0a, 0x9b, 0x1f, | ||||
| 	0x35, 0xcd, 0x62, 0x54, 0xea, 0xb3, 0xbc, 0xa6, 0xcf, 0xdf, 0x4b, 0xd9, 0xbe, 0x8d, 0xda, 0x0a, | ||||
| 	0x01, 0x22, 0x90, 0x96, 0x03, 0x94, 0x12, 0x50, 0x85, 0x48, 0x6d, 0x21, 0xd8, 0x3d, 0xe2, 0x7a, | ||||
| 	0xf5, 0xeb, 0x0a, 0xa6, 0xf2, 0x0f, 0xb3, 0xc5, 0x13, 0xa8, 0x2d, 0x91, 0x6b, 0xef, 0x52, 0x20, | ||||
| 	0x2d, 0xee, 0x08, 0xbe, 0x06, 0x19, 0xf9, 0xc2, 0xe1, 0xd5, 0x18, 0xc9, 0xc3, 0xab, 0xa4, 0xb8, | ||||
| 	0x34, 0x2e, 0x4c, 0x76, 0xd2, 0x58, 0xd8, 0xff, 0xf2, 0xeb, 0x43, 0x72, 0x0e, 0x16, 0xac, 0x51, | ||||
| 	0x2b, 0x0f, 0xee, 0x6b, 0x20, 0x23, 0xb7, 0xc2, 0x68, 0xf2, 0xc8, 0x86, 0x19, 0x4d, 0x1e, 0x5d, | ||||
| 	0x2e, 0xc6, 0x35, 0x41, 0xbe, 0x08, 0x17, 0xac, 0x11, 0xdb, 0x92, 0x5a, 0x6f, 0xc4, 0xd4, 0xbf, | ||||
| 	0xe5, 0x22, 0x26, 0xc3, 0x49, 0x85, 0xcb, 0xa3, 0xf0, 0x07, 0x9e, 0x5c, 0xb1, 0x32, 0x3e, 0x50, | ||||
| 	0x49, 0x59, 0x14, 0x52, 0xe6, 0xe1, 0x5c, 0x8c, 0x94, 0x70, 0xa6, 0xeb, 0x6b, 0x87, 0x3f, 0xf5, | ||||
| 	0xc4, 0xe1, 0xa9, 0xae, 0x1d, 0x9d, 0xea, 0xda, 0x8f, 0x53, 0x5d, 0x7b, 0x7f, 0xa6, 0x27, 0x8e, | ||||
| 	0xce, 0xf4, 0xc4, 0xd7, 0x33, 0x3d, 0xf1, 0xfc, 0xe2, 0xea, 0xe0, 0x20, 0xd5, 0x36, 0x6a, 0x52, | ||||
| 	0x09, 0xf7, 0x4a, 0x02, 0x8a, 0x2b, 0x6e, 0x66, 0xc4, 0x27, 0xe0, 0xc6, 0xdf, 0x00, 0x00, 0x00, | ||||
| 	0xff, 0xff, 0x27, 0x75, 0x39, 0xf4, 0x32, 0x07, 0x00, 0x00, | ||||
| } | ||||
| 
 | ||||
| // Reference imports to suppress errors if they are not otherwise used.
 | ||||
| @ -529,8 +445,6 @@ type QueryClient interface { | ||||
| 	Vaults(ctx context.Context, in *QueryVaultsRequest, opts ...grpc.CallOption) (*QueryVaultsResponse, error) | ||||
| 	// Deposits queries deposit details based on owner address and vault
 | ||||
| 	Deposits(ctx context.Context, in *QueryDepositsRequest, opts ...grpc.CallOption) (*QueryDepositsResponse, error) | ||||
| 	// TotalDeposited queries total deposited amount for each vault.
 | ||||
| 	TotalDeposited(ctx context.Context, in *QueryTotalDepositedRequest, opts ...grpc.CallOption) (*QueryTotalDepositedResponse, error) | ||||
| } | ||||
| 
 | ||||
| type queryClient struct { | ||||
| @ -568,15 +482,6 @@ func (c *queryClient) Deposits(ctx context.Context, in *QueryDepositsRequest, op | ||||
| 	return out, nil | ||||
| } | ||||
| 
 | ||||
| func (c *queryClient) TotalDeposited(ctx context.Context, in *QueryTotalDepositedRequest, opts ...grpc.CallOption) (*QueryTotalDepositedResponse, error) { | ||||
| 	out := new(QueryTotalDepositedResponse) | ||||
| 	err := c.cc.Invoke(ctx, "/kava.earn.v1beta1.Query/TotalDeposited", in, out, opts...) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return out, nil | ||||
| } | ||||
| 
 | ||||
| // QueryServer is the server API for Query service.
 | ||||
| type QueryServer interface { | ||||
| 	// Params queries all parameters of the earn module.
 | ||||
| @ -585,8 +490,6 @@ type QueryServer interface { | ||||
| 	Vaults(context.Context, *QueryVaultsRequest) (*QueryVaultsResponse, error) | ||||
| 	// Deposits queries deposit details based on owner address and vault
 | ||||
| 	Deposits(context.Context, *QueryDepositsRequest) (*QueryDepositsResponse, error) | ||||
| 	// TotalDeposited queries total deposited amount for each vault.
 | ||||
| 	TotalDeposited(context.Context, *QueryTotalDepositedRequest) (*QueryTotalDepositedResponse, error) | ||||
| } | ||||
| 
 | ||||
| // UnimplementedQueryServer can be embedded to have forward compatible implementations.
 | ||||
| @ -602,9 +505,6 @@ func (*UnimplementedQueryServer) Vaults(ctx context.Context, req *QueryVaultsReq | ||||
| func (*UnimplementedQueryServer) Deposits(ctx context.Context, req *QueryDepositsRequest) (*QueryDepositsResponse, error) { | ||||
| 	return nil, status.Errorf(codes.Unimplemented, "method Deposits not implemented") | ||||
| } | ||||
| func (*UnimplementedQueryServer) TotalDeposited(ctx context.Context, req *QueryTotalDepositedRequest) (*QueryTotalDepositedResponse, error) { | ||||
| 	return nil, status.Errorf(codes.Unimplemented, "method TotalDeposited not implemented") | ||||
| } | ||||
| 
 | ||||
| func RegisterQueryServer(s grpc1.Server, srv QueryServer) { | ||||
| 	s.RegisterService(&_Query_serviceDesc, srv) | ||||
| @ -664,24 +564,6 @@ func _Query_Deposits_Handler(srv interface{}, ctx context.Context, dec func(inte | ||||
| 	return interceptor(ctx, in, info, handler) | ||||
| } | ||||
| 
 | ||||
| func _Query_TotalDeposited_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { | ||||
| 	in := new(QueryTotalDepositedRequest) | ||||
| 	if err := dec(in); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if interceptor == nil { | ||||
| 		return srv.(QueryServer).TotalDeposited(ctx, in) | ||||
| 	} | ||||
| 	info := &grpc.UnaryServerInfo{ | ||||
| 		Server:     srv, | ||||
| 		FullMethod: "/kava.earn.v1beta1.Query/TotalDeposited", | ||||
| 	} | ||||
| 	handler := func(ctx context.Context, req interface{}) (interface{}, error) { | ||||
| 		return srv.(QueryServer).TotalDeposited(ctx, req.(*QueryTotalDepositedRequest)) | ||||
| 	} | ||||
| 	return interceptor(ctx, in, info, handler) | ||||
| } | ||||
| 
 | ||||
| var _Query_serviceDesc = grpc.ServiceDesc{ | ||||
| 	ServiceName: "kava.earn.v1beta1.Query", | ||||
| 	HandlerType: (*QueryServer)(nil), | ||||
| @ -698,10 +580,6 @@ var _Query_serviceDesc = grpc.ServiceDesc{ | ||||
| 			MethodName: "Deposits", | ||||
| 			Handler:    _Query_Deposits_Handler, | ||||
| 		}, | ||||
| 		{ | ||||
| 			MethodName: "TotalDeposited", | ||||
| 			Handler:    _Query_TotalDeposited_Handler, | ||||
| 		}, | ||||
| 	}, | ||||
| 	Streams:  []grpc.StreamDesc{}, | ||||
| 	Metadata: "kava/earn/v1beta1/query.proto", | ||||
| @ -860,16 +738,13 @@ func (m *VaultResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { | ||||
| 	} | ||||
| 	i-- | ||||
| 	dAtA[i] = 0x22 | ||||
| 	{ | ||||
| 		size := m.TotalSupplied.Size() | ||||
| 		i -= size | ||||
| 		if _, err := m.TotalSupplied.MarshalTo(dAtA[i:]); err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
| 		i = encodeVarintQuery(dAtA, i, uint64(size)) | ||||
| 	if len(m.TotalShares) > 0 { | ||||
| 		i -= len(m.TotalShares) | ||||
| 		copy(dAtA[i:], m.TotalShares) | ||||
| 		i = encodeVarintQuery(dAtA, i, uint64(len(m.TotalShares))) | ||||
| 		i-- | ||||
| 		dAtA[i] = 0x1a | ||||
| 	} | ||||
| 	i-- | ||||
| 	dAtA[i] = 0x1a | ||||
| 	if m.VaultStrategy != 0 { | ||||
| 		i = encodeVarintQuery(dAtA, i, uint64(m.VaultStrategy)) | ||||
| 		i-- | ||||
| @ -1017,10 +892,10 @@ func (m *DepositResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { | ||||
| 			dAtA[i] = 0x1a | ||||
| 		} | ||||
| 	} | ||||
| 	if len(m.AmountSupplied) > 0 { | ||||
| 		for iNdEx := len(m.AmountSupplied) - 1; iNdEx >= 0; iNdEx-- { | ||||
| 	if len(m.Shares) > 0 { | ||||
| 		for iNdEx := len(m.Shares) - 1; iNdEx >= 0; iNdEx-- { | ||||
| 			{ | ||||
| 				size, err := m.AmountSupplied[iNdEx].MarshalToSizedBuffer(dAtA[:i]) | ||||
| 				size, err := m.Shares[iNdEx].MarshalToSizedBuffer(dAtA[:i]) | ||||
| 				if err != nil { | ||||
| 					return 0, err | ||||
| 				} | ||||
| @ -1041,73 +916,6 @@ func (m *DepositResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { | ||||
| 	return len(dAtA) - i, nil | ||||
| } | ||||
| 
 | ||||
| func (m *QueryTotalDepositedRequest) Marshal() (dAtA []byte, err error) { | ||||
| 	size := m.Size() | ||||
| 	dAtA = make([]byte, size) | ||||
| 	n, err := m.MarshalToSizedBuffer(dAtA[:size]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return dAtA[:n], nil | ||||
| } | ||||
| 
 | ||||
| func (m *QueryTotalDepositedRequest) MarshalTo(dAtA []byte) (int, error) { | ||||
| 	size := m.Size() | ||||
| 	return m.MarshalToSizedBuffer(dAtA[:size]) | ||||
| } | ||||
| 
 | ||||
| func (m *QueryTotalDepositedRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { | ||||
| 	i := len(dAtA) | ||||
| 	_ = i | ||||
| 	var l int | ||||
| 	_ = l | ||||
| 	if len(m.Denom) > 0 { | ||||
| 		i -= len(m.Denom) | ||||
| 		copy(dAtA[i:], m.Denom) | ||||
| 		i = encodeVarintQuery(dAtA, i, uint64(len(m.Denom))) | ||||
| 		i-- | ||||
| 		dAtA[i] = 0xa | ||||
| 	} | ||||
| 	return len(dAtA) - i, nil | ||||
| } | ||||
| 
 | ||||
| func (m *QueryTotalDepositedResponse) Marshal() (dAtA []byte, err error) { | ||||
| 	size := m.Size() | ||||
| 	dAtA = make([]byte, size) | ||||
| 	n, err := m.MarshalToSizedBuffer(dAtA[:size]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return dAtA[:n], nil | ||||
| } | ||||
| 
 | ||||
| func (m *QueryTotalDepositedResponse) MarshalTo(dAtA []byte) (int, error) { | ||||
| 	size := m.Size() | ||||
| 	return m.MarshalToSizedBuffer(dAtA[:size]) | ||||
| } | ||||
| 
 | ||||
| func (m *QueryTotalDepositedResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { | ||||
| 	i := len(dAtA) | ||||
| 	_ = i | ||||
| 	var l int | ||||
| 	_ = l | ||||
| 	if len(m.SuppliedCoins) > 0 { | ||||
| 		for iNdEx := len(m.SuppliedCoins) - 1; iNdEx >= 0; iNdEx-- { | ||||
| 			{ | ||||
| 				size, err := m.SuppliedCoins[iNdEx].MarshalToSizedBuffer(dAtA[:i]) | ||||
| 				if err != nil { | ||||
| 					return 0, err | ||||
| 				} | ||||
| 				i -= size | ||||
| 				i = encodeVarintQuery(dAtA, i, uint64(size)) | ||||
| 			} | ||||
| 			i-- | ||||
| 			dAtA[i] = 0xa | ||||
| 		} | ||||
| 	} | ||||
| 	return len(dAtA) - i, nil | ||||
| } | ||||
| 
 | ||||
| func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { | ||||
| 	offset -= sovQuery(v) | ||||
| 	base := offset | ||||
| @ -1180,8 +988,10 @@ func (m *VaultResponse) Size() (n int) { | ||||
| 	if m.VaultStrategy != 0 { | ||||
| 		n += 1 + sovQuery(uint64(m.VaultStrategy)) | ||||
| 	} | ||||
| 	l = m.TotalSupplied.Size() | ||||
| 	n += 1 + l + sovQuery(uint64(l)) | ||||
| 	l = len(m.TotalShares) | ||||
| 	if l > 0 { | ||||
| 		n += 1 + l + sovQuery(uint64(l)) | ||||
| 	} | ||||
| 	l = m.TotalValue.Size() | ||||
| 	n += 1 + l + sovQuery(uint64(l)) | ||||
| 	return n | ||||
| @ -1237,8 +1047,8 @@ func (m *DepositResponse) Size() (n int) { | ||||
| 	if l > 0 { | ||||
| 		n += 1 + l + sovQuery(uint64(l)) | ||||
| 	} | ||||
| 	if len(m.AmountSupplied) > 0 { | ||||
| 		for _, e := range m.AmountSupplied { | ||||
| 	if len(m.Shares) > 0 { | ||||
| 		for _, e := range m.Shares { | ||||
| 			l = e.Size() | ||||
| 			n += 1 + l + sovQuery(uint64(l)) | ||||
| 		} | ||||
| @ -1252,34 +1062,6 @@ func (m *DepositResponse) Size() (n int) { | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| func (m *QueryTotalDepositedRequest) Size() (n int) { | ||||
| 	if m == nil { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	var l int | ||||
| 	_ = l | ||||
| 	l = len(m.Denom) | ||||
| 	if l > 0 { | ||||
| 		n += 1 + l + sovQuery(uint64(l)) | ||||
| 	} | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| func (m *QueryTotalDepositedResponse) Size() (n int) { | ||||
| 	if m == nil { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	var l int | ||||
| 	_ = l | ||||
| 	if len(m.SuppliedCoins) > 0 { | ||||
| 		for _, e := range m.SuppliedCoins { | ||||
| 			l = e.Size() | ||||
| 			n += 1 + l + sovQuery(uint64(l)) | ||||
| 		} | ||||
| 	} | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| func sovQuery(x uint64) (n int) { | ||||
| 	return (math_bits.Len64(x|1) + 6) / 7 | ||||
| } | ||||
| @ -1667,7 +1449,7 @@ func (m *VaultResponse) Unmarshal(dAtA []byte) error { | ||||
| 			} | ||||
| 		case 3: | ||||
| 			if wireType != 2 { | ||||
| 				return fmt.Errorf("proto: wrong wireType = %d for field TotalSupplied", wireType) | ||||
| 				return fmt.Errorf("proto: wrong wireType = %d for field TotalShares", wireType) | ||||
| 			} | ||||
| 			var stringLen uint64 | ||||
| 			for shift := uint(0); ; shift += 7 { | ||||
| @ -1695,9 +1477,7 @@ func (m *VaultResponse) Unmarshal(dAtA []byte) error { | ||||
| 			if postIndex > l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			if err := m.TotalSupplied.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			m.TotalShares = string(dAtA[iNdEx:postIndex]) | ||||
| 			iNdEx = postIndex | ||||
| 		case 4: | ||||
| 			if wireType != 2 { | ||||
| @ -2087,7 +1867,7 @@ func (m *DepositResponse) Unmarshal(dAtA []byte) error { | ||||
| 			iNdEx = postIndex | ||||
| 		case 2: | ||||
| 			if wireType != 2 { | ||||
| 				return fmt.Errorf("proto: wrong wireType = %d for field AmountSupplied", wireType) | ||||
| 				return fmt.Errorf("proto: wrong wireType = %d for field Shares", wireType) | ||||
| 			} | ||||
| 			var msglen int | ||||
| 			for shift := uint(0); ; shift += 7 { | ||||
| @ -2114,8 +1894,8 @@ func (m *DepositResponse) Unmarshal(dAtA []byte) error { | ||||
| 			if postIndex > l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			m.AmountSupplied = append(m.AmountSupplied, types.Coin{}) | ||||
| 			if err := m.AmountSupplied[len(m.AmountSupplied)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { | ||||
| 			m.Shares = append(m.Shares, VaultShare{}) | ||||
| 			if err := m.Shares[len(m.Shares)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			iNdEx = postIndex | ||||
| @ -2174,172 +1954,6 @@ func (m *DepositResponse) Unmarshal(dAtA []byte) error { | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| func (m *QueryTotalDepositedRequest) Unmarshal(dAtA []byte) error { | ||||
| 	l := len(dAtA) | ||||
| 	iNdEx := 0 | ||||
| 	for iNdEx < l { | ||||
| 		preIndex := iNdEx | ||||
| 		var wire uint64 | ||||
| 		for shift := uint(0); ; shift += 7 { | ||||
| 			if shift >= 64 { | ||||
| 				return ErrIntOverflowQuery | ||||
| 			} | ||||
| 			if iNdEx >= l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			b := dAtA[iNdEx] | ||||
| 			iNdEx++ | ||||
| 			wire |= uint64(b&0x7F) << shift | ||||
| 			if b < 0x80 { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		fieldNum := int32(wire >> 3) | ||||
| 		wireType := int(wire & 0x7) | ||||
| 		if wireType == 4 { | ||||
| 			return fmt.Errorf("proto: QueryTotalDepositedRequest: wiretype end group for non-group") | ||||
| 		} | ||||
| 		if fieldNum <= 0 { | ||||
| 			return fmt.Errorf("proto: QueryTotalDepositedRequest: illegal tag %d (wire type %d)", fieldNum, wire) | ||||
| 		} | ||||
| 		switch fieldNum { | ||||
| 		case 1: | ||||
| 			if wireType != 2 { | ||||
| 				return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType) | ||||
| 			} | ||||
| 			var stringLen uint64 | ||||
| 			for shift := uint(0); ; shift += 7 { | ||||
| 				if shift >= 64 { | ||||
| 					return ErrIntOverflowQuery | ||||
| 				} | ||||
| 				if iNdEx >= l { | ||||
| 					return io.ErrUnexpectedEOF | ||||
| 				} | ||||
| 				b := dAtA[iNdEx] | ||||
| 				iNdEx++ | ||||
| 				stringLen |= uint64(b&0x7F) << shift | ||||
| 				if b < 0x80 { | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			intStringLen := int(stringLen) | ||||
| 			if intStringLen < 0 { | ||||
| 				return ErrInvalidLengthQuery | ||||
| 			} | ||||
| 			postIndex := iNdEx + intStringLen | ||||
| 			if postIndex < 0 { | ||||
| 				return ErrInvalidLengthQuery | ||||
| 			} | ||||
| 			if postIndex > l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			m.Denom = string(dAtA[iNdEx:postIndex]) | ||||
| 			iNdEx = postIndex | ||||
| 		default: | ||||
| 			iNdEx = preIndex | ||||
| 			skippy, err := skipQuery(dAtA[iNdEx:]) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			if (skippy < 0) || (iNdEx+skippy) < 0 { | ||||
| 				return ErrInvalidLengthQuery | ||||
| 			} | ||||
| 			if (iNdEx + skippy) > l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			iNdEx += skippy | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if iNdEx > l { | ||||
| 		return io.ErrUnexpectedEOF | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| func (m *QueryTotalDepositedResponse) Unmarshal(dAtA []byte) error { | ||||
| 	l := len(dAtA) | ||||
| 	iNdEx := 0 | ||||
| 	for iNdEx < l { | ||||
| 		preIndex := iNdEx | ||||
| 		var wire uint64 | ||||
| 		for shift := uint(0); ; shift += 7 { | ||||
| 			if shift >= 64 { | ||||
| 				return ErrIntOverflowQuery | ||||
| 			} | ||||
| 			if iNdEx >= l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			b := dAtA[iNdEx] | ||||
| 			iNdEx++ | ||||
| 			wire |= uint64(b&0x7F) << shift | ||||
| 			if b < 0x80 { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		fieldNum := int32(wire >> 3) | ||||
| 		wireType := int(wire & 0x7) | ||||
| 		if wireType == 4 { | ||||
| 			return fmt.Errorf("proto: QueryTotalDepositedResponse: wiretype end group for non-group") | ||||
| 		} | ||||
| 		if fieldNum <= 0 { | ||||
| 			return fmt.Errorf("proto: QueryTotalDepositedResponse: illegal tag %d (wire type %d)", fieldNum, wire) | ||||
| 		} | ||||
| 		switch fieldNum { | ||||
| 		case 1: | ||||
| 			if wireType != 2 { | ||||
| 				return fmt.Errorf("proto: wrong wireType = %d for field SuppliedCoins", wireType) | ||||
| 			} | ||||
| 			var msglen int | ||||
| 			for shift := uint(0); ; shift += 7 { | ||||
| 				if shift >= 64 { | ||||
| 					return ErrIntOverflowQuery | ||||
| 				} | ||||
| 				if iNdEx >= l { | ||||
| 					return io.ErrUnexpectedEOF | ||||
| 				} | ||||
| 				b := dAtA[iNdEx] | ||||
| 				iNdEx++ | ||||
| 				msglen |= int(b&0x7F) << shift | ||||
| 				if b < 0x80 { | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			if msglen < 0 { | ||||
| 				return ErrInvalidLengthQuery | ||||
| 			} | ||||
| 			postIndex := iNdEx + msglen | ||||
| 			if postIndex < 0 { | ||||
| 				return ErrInvalidLengthQuery | ||||
| 			} | ||||
| 			if postIndex > l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			m.SuppliedCoins = append(m.SuppliedCoins, types.Coin{}) | ||||
| 			if err := m.SuppliedCoins[len(m.SuppliedCoins)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			iNdEx = postIndex | ||||
| 		default: | ||||
| 			iNdEx = preIndex | ||||
| 			skippy, err := skipQuery(dAtA[iNdEx:]) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			if (skippy < 0) || (iNdEx+skippy) < 0 { | ||||
| 				return ErrInvalidLengthQuery | ||||
| 			} | ||||
| 			if (iNdEx + skippy) > l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			iNdEx += skippy | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if iNdEx > l { | ||||
| 		return io.ErrUnexpectedEOF | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| func skipQuery(dAtA []byte) (n int, err error) { | ||||
| 	l := len(dAtA) | ||||
| 	iNdEx := 0 | ||||
|  | ||||
| @ -139,60 +139,6 @@ func local_request_Query_Deposits_0(ctx context.Context, marshaler runtime.Marsh | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func request_Query_TotalDeposited_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { | ||||
| 	var protoReq QueryTotalDepositedRequest | ||||
| 	var metadata runtime.ServerMetadata | ||||
| 
 | ||||
| 	var ( | ||||
| 		val string | ||||
| 		ok  bool | ||||
| 		err error | ||||
| 		_   = err | ||||
| 	) | ||||
| 
 | ||||
| 	val, ok = pathParams["denom"] | ||||
| 	if !ok { | ||||
| 		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "denom") | ||||
| 	} | ||||
| 
 | ||||
| 	protoReq.Denom, err = runtime.String(val) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "denom", err) | ||||
| 	} | ||||
| 
 | ||||
| 	msg, err := client.TotalDeposited(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) | ||||
| 	return msg, metadata, err | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func local_request_Query_TotalDeposited_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { | ||||
| 	var protoReq QueryTotalDepositedRequest | ||||
| 	var metadata runtime.ServerMetadata | ||||
| 
 | ||||
| 	var ( | ||||
| 		val string | ||||
| 		ok  bool | ||||
| 		err error | ||||
| 		_   = err | ||||
| 	) | ||||
| 
 | ||||
| 	val, ok = pathParams["denom"] | ||||
| 	if !ok { | ||||
| 		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "denom") | ||||
| 	} | ||||
| 
 | ||||
| 	protoReq.Denom, err = runtime.String(val) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "denom", err) | ||||
| 	} | ||||
| 
 | ||||
| 	msg, err := server.TotalDeposited(ctx, &protoReq) | ||||
| 	return msg, metadata, err | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| // RegisterQueryHandlerServer registers the http handlers for service Query to "mux".
 | ||||
| // UnaryRPC     :call QueryServer directly.
 | ||||
| // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
 | ||||
| @ -259,26 +205,6 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv | ||||
| 
 | ||||
| 	}) | ||||
| 
 | ||||
| 	mux.Handle("GET", pattern_Query_TotalDeposited_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { | ||||
| 		ctx, cancel := context.WithCancel(req.Context()) | ||||
| 		defer cancel() | ||||
| 		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) | ||||
| 		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) | ||||
| 		if err != nil { | ||||
| 			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) | ||||
| 			return | ||||
| 		} | ||||
| 		resp, md, err := local_request_Query_TotalDeposited_0(rctx, inboundMarshaler, server, req, pathParams) | ||||
| 		ctx = runtime.NewServerMetadataContext(ctx, md) | ||||
| 		if err != nil { | ||||
| 			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		forward_Query_TotalDeposited_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) | ||||
| 
 | ||||
| 	}) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| @ -380,26 +306,6 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie | ||||
| 
 | ||||
| 	}) | ||||
| 
 | ||||
| 	mux.Handle("GET", pattern_Query_TotalDeposited_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { | ||||
| 		ctx, cancel := context.WithCancel(req.Context()) | ||||
| 		defer cancel() | ||||
| 		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) | ||||
| 		rctx, err := runtime.AnnotateContext(ctx, mux, req) | ||||
| 		if err != nil { | ||||
| 			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) | ||||
| 			return | ||||
| 		} | ||||
| 		resp, md, err := request_Query_TotalDeposited_0(rctx, inboundMarshaler, client, req, pathParams) | ||||
| 		ctx = runtime.NewServerMetadataContext(ctx, md) | ||||
| 		if err != nil { | ||||
| 			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		forward_Query_TotalDeposited_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) | ||||
| 
 | ||||
| 	}) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| @ -409,8 +315,6 @@ var ( | ||||
| 	pattern_Query_Vaults_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"kava", "earn", "v1beta1", "vaults", "denom"}, "", runtime.AssumeColonVerbOpt(false))) | ||||
| 
 | ||||
| 	pattern_Query_Deposits_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"kava", "earn", "v1beta1", "deposits"}, "", runtime.AssumeColonVerbOpt(false))) | ||||
| 
 | ||||
| 	pattern_Query_TotalDeposited_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"kava", "earn", "v1beta1", "total-deposited", "denom"}, "", runtime.AssumeColonVerbOpt(false))) | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| @ -419,6 +323,4 @@ var ( | ||||
| 	forward_Query_Vaults_0 = runtime.ForwardResponseMessage | ||||
| 
 | ||||
| 	forward_Query_Deposits_0 = runtime.ForwardResponseMessage | ||||
| 
 | ||||
| 	forward_Query_TotalDeposited_0 = runtime.ForwardResponseMessage | ||||
| ) | ||||
|  | ||||
							
								
								
									
										383
									
								
								x/earn/types/share.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										383
									
								
								x/earn/types/share.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,383 @@ | ||||
| package types | ||||
| 
 | ||||
| import ( | ||||
| 	fmt "fmt" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	sdk "github.com/cosmos/cosmos-sdk/types" | ||||
| 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" | ||||
| ) | ||||
| 
 | ||||
| // NewVaultShare returns a new VaultShare
 | ||||
| func NewVaultShare(denom string, amount sdk.Dec) VaultShare { | ||||
| 	share := VaultShare{ | ||||
| 		Denom:  denom, | ||||
| 		Amount: amount, | ||||
| 	} | ||||
| 
 | ||||
| 	if err := share.Validate(); err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 
 | ||||
| 	return share | ||||
| } | ||||
| 
 | ||||
| // Validate returns an error if a VaultShare is invalid.
 | ||||
| func (share VaultShare) Validate() error { | ||||
| 	if err := sdk.ValidateDenom(share.Denom); err != nil { | ||||
| 		return sdkerrors.Wrap(ErrInvalidVaultDenom, err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	if share.Amount.IsNil() { | ||||
| 		return fmt.Errorf("nil share amount: %s", share.Amount) | ||||
| 	} | ||||
| 
 | ||||
| 	if share.Amount.IsNegative() { | ||||
| 		return fmt.Errorf("vault share amount %v is negative", share.Amount) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // IsValid returns true if the VaultShare is valid
 | ||||
| func (share VaultShare) IsValid() bool { | ||||
| 	return share.Validate() == nil | ||||
| } | ||||
| 
 | ||||
| func (share VaultShare) IsPositive() bool { | ||||
| 	return share.Amount.IsPositive() | ||||
| } | ||||
| 
 | ||||
| // Add adds amounts of two vault shares with same denom. If the shares differ in
 | ||||
| // denom then it panics.
 | ||||
| func (share VaultShare) Add(vsB VaultShare) VaultShare { | ||||
| 	if share.Denom != vsB.Denom { | ||||
| 		panic(fmt.Sprintf("invalid share denominations; %s, %s", share.Denom, vsB.Denom)) | ||||
| 	} | ||||
| 
 | ||||
| 	return NewVaultShare(share.Denom, share.Amount.Add(vsB.Amount)) | ||||
| } | ||||
| 
 | ||||
| // IsZero returns if this represents no shares
 | ||||
| func (share VaultShare) IsZero() bool { | ||||
| 	return share.Amount.IsZero() | ||||
| } | ||||
| 
 | ||||
| // IsNegative returns true if the share amount is negative and false otherwise.
 | ||||
| func (share VaultShare) IsNegative() bool { | ||||
| 	return share.Amount.IsNegative() | ||||
| } | ||||
| 
 | ||||
| // Sub subtracts amounts of two vault shares with same denom. If the shares
 | ||||
| // differ in denom then it panics.
 | ||||
| func (share VaultShare) Sub(vsB VaultShare) VaultShare { | ||||
| 	if share.Denom != vsB.Denom { | ||||
| 		panic(fmt.Sprintf("invalid share denominations; %s, %s", share.Denom, vsB.Denom)) | ||||
| 	} | ||||
| 
 | ||||
| 	res := NewVaultShare(share.Denom, share.Amount.Sub(vsB.Amount)) | ||||
| 	if res.Amount.IsNegative() { | ||||
| 		panic("negative share amount") | ||||
| 	} | ||||
| 
 | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| func (share VaultShare) String() string { | ||||
| 	return fmt.Sprintf("%v%v", share.Amount, share.Denom) | ||||
| } | ||||
| 
 | ||||
| // VaultShares is a slice of VaultShare.
 | ||||
| type VaultShares []VaultShare | ||||
| 
 | ||||
| // NewVaultShares returns new VaultShares
 | ||||
| func NewVaultShares(shares ...VaultShare) VaultShares { | ||||
| 	newVaultShares := sanitizeVaultShares(shares) | ||||
| 	if err := newVaultShares.Validate(); err != nil { | ||||
| 		panic(fmt.Errorf("invalid share set %s: %w", newVaultShares, err)) | ||||
| 	} | ||||
| 
 | ||||
| 	return newVaultShares | ||||
| } | ||||
| 
 | ||||
| func sanitizeVaultShares(shares VaultShares) VaultShares { | ||||
| 	newVaultShares := removeZeroShares(shares) | ||||
| 	if len(newVaultShares) == 0 { | ||||
| 		return VaultShares{} | ||||
| 	} | ||||
| 
 | ||||
| 	return newVaultShares.Sort() | ||||
| } | ||||
| 
 | ||||
| // Validate returns an error if a slice of VaultShares is invalid.
 | ||||
| func (shares VaultShares) Validate() error { | ||||
| 	switch len(shares) { | ||||
| 	case 0: | ||||
| 		return nil | ||||
| 
 | ||||
| 	case 1: | ||||
| 		if err := sdk.ValidateDenom(shares[0].Denom); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if !shares[0].IsPositive() { | ||||
| 			return fmt.Errorf("share %s amount is not positive", shares[0]) | ||||
| 		} | ||||
| 		return nil | ||||
| 	default: | ||||
| 		// check single share case
 | ||||
| 		if err := (VaultShares{shares[0]}).Validate(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		lowDenom := shares[0].Denom | ||||
| 		seenDenoms := make(map[string]bool) | ||||
| 		seenDenoms[lowDenom] = true | ||||
| 
 | ||||
| 		for _, share := range shares[1:] { | ||||
| 			if seenDenoms[share.Denom] { | ||||
| 				return fmt.Errorf("duplicate denomination %s", share.Denom) | ||||
| 			} | ||||
| 			if err := sdk.ValidateDenom(share.Denom); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			if share.Denom <= lowDenom { | ||||
| 				return fmt.Errorf("denomination %s is not sorted", share.Denom) | ||||
| 			} | ||||
| 			if !share.IsPositive() { | ||||
| 				return fmt.Errorf("share %s amount is not positive", share.Denom) | ||||
| 			} | ||||
| 
 | ||||
| 			// we compare each share against the last denom
 | ||||
| 			lowDenom = share.Denom | ||||
| 			seenDenoms[share.Denom] = true | ||||
| 		} | ||||
| 
 | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // IsValid returns true if the VaultShares are valid
 | ||||
| func (shares VaultShares) IsValid() bool { | ||||
| 	return shares.Validate() == nil | ||||
| } | ||||
| 
 | ||||
| // AmountOf returns the amount of shares of the given denom.
 | ||||
| func (shares VaultShares) Add(sharesB ...VaultShare) VaultShares { | ||||
| 	return shares.safeAdd(sharesB) | ||||
| } | ||||
| 
 | ||||
| // safeAdd will perform addition of two shares sets. If both share sets are
 | ||||
| // empty, then an empty set is returned. If only a single set is empty, the
 | ||||
| // other set is returned. Otherwise, the shares are compared in order of their
 | ||||
| // denomination and addition only occurs when the denominations match, otherwise
 | ||||
| // the share is simply added to the sum assuming it's not zero.
 | ||||
| // The function panics if `shares` or  `sharesB` are not sorted (ascending).
 | ||||
| func (shares VaultShares) safeAdd(sharesB VaultShares) VaultShares { | ||||
| 	// probably the best way will be to make Shares and interface and hide the structure
 | ||||
| 	// definition (type alias)
 | ||||
| 	if !shares.isSorted() { | ||||
| 		panic("Shares (self) must be sorted") | ||||
| 	} | ||||
| 	if !sharesB.isSorted() { | ||||
| 		panic("Wrong argument: shares must be sorted") | ||||
| 	} | ||||
| 
 | ||||
| 	sum := (VaultShares)(nil) | ||||
| 	indexA, indexB := 0, 0 | ||||
| 	lenA, lenB := len(shares), len(sharesB) | ||||
| 
 | ||||
| 	for { | ||||
| 		if indexA == lenA { | ||||
| 			if indexB == lenB { | ||||
| 				// return nil shares if both sets are empty
 | ||||
| 				return sum | ||||
| 			} | ||||
| 
 | ||||
| 			// return set B (excluding zero shares) if set A is empty
 | ||||
| 			return append(sum, removeZeroShares(sharesB[indexB:])...) | ||||
| 		} else if indexB == lenB { | ||||
| 			// return set A (excluding zero shares) if set B is empty
 | ||||
| 			return append(sum, removeZeroShares(shares[indexA:])...) | ||||
| 		} | ||||
| 
 | ||||
| 		shareA, shareB := shares[indexA], sharesB[indexB] | ||||
| 
 | ||||
| 		switch strings.Compare(shareA.Denom, shareB.Denom) { | ||||
| 		case -1: // share A denom < share B denom
 | ||||
| 			if !shareA.IsZero() { | ||||
| 				sum = append(sum, shareA) | ||||
| 			} | ||||
| 
 | ||||
| 			indexA++ | ||||
| 
 | ||||
| 		case 0: // share A denom == share B denom
 | ||||
| 			res := shareA.Add(shareB) | ||||
| 			if !res.IsZero() { | ||||
| 				sum = append(sum, res) | ||||
| 			} | ||||
| 
 | ||||
| 			indexA++ | ||||
| 			indexB++ | ||||
| 
 | ||||
| 		case 1: // share A denom > share B denom
 | ||||
| 			if !shareB.IsZero() { | ||||
| 				sum = append(sum, shareB) | ||||
| 			} | ||||
| 
 | ||||
| 			indexB++ | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Sub subtracts a set of shares from another.
 | ||||
| //
 | ||||
| // e.g.
 | ||||
| // {2A, 3B} - {A} = {A, 3B}
 | ||||
| // {2A} - {0B} = {2A}
 | ||||
| // {A, B} - {A} = {B}
 | ||||
| //
 | ||||
| // CONTRACT: Sub will never return Shares where one Share has a non-positive
 | ||||
| // amount. In otherwords, IsValid will always return true.
 | ||||
| func (shares VaultShares) Sub(sharesB ...VaultShare) VaultShares { | ||||
| 	diff, hasNeg := shares.SafeSub(sharesB) | ||||
| 	if hasNeg { | ||||
| 		panic("negative share amount") | ||||
| 	} | ||||
| 
 | ||||
| 	return diff | ||||
| } | ||||
| 
 | ||||
| // SafeSub performs the same arithmetic as Sub but returns a boolean if any
 | ||||
| // negative share amount was returned.
 | ||||
| // The function panics if `shares` or  `sharesB` are not sorted (ascending).
 | ||||
| func (shares VaultShares) SafeSub(sharesB VaultShares) (VaultShares, bool) { | ||||
| 	diff := shares.safeAdd(sharesB.negative()) | ||||
| 	return diff, diff.IsAnyNegative() | ||||
| } | ||||
| 
 | ||||
| // IsAnyNegative returns true if there is at least one share whose amount
 | ||||
| // is negative; returns false otherwise. It returns false if the share set
 | ||||
| // is empty too.
 | ||||
| func (shares VaultShares) IsAnyNegative() bool { | ||||
| 	for _, share := range shares { | ||||
| 		if share.IsNegative() { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // negative returns a set of shares with all amount negative.
 | ||||
| func (shares VaultShares) negative() VaultShares { | ||||
| 	res := make(VaultShares, 0, len(shares)) | ||||
| 
 | ||||
| 	for _, share := range shares { | ||||
| 		res = append(res, VaultShare{ | ||||
| 			Denom:  share.Denom, | ||||
| 			Amount: share.Amount.Neg(), | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| // AmountOf returns the amount of shares of the given denom.
 | ||||
| func (v VaultShares) AmountOf(denom string) sdk.Dec { | ||||
| 	for _, s := range v { | ||||
| 		if s.Denom == denom { | ||||
| 			return s.Amount | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return sdk.ZeroDec() | ||||
| } | ||||
| 
 | ||||
| // GetShare the single share of the given denom.
 | ||||
| func (v VaultShares) GetShare(denom string) VaultShare { | ||||
| 	for _, s := range v { | ||||
| 		if s.Denom == denom { | ||||
| 			return s | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return NewVaultShare(denom, sdk.ZeroDec()) | ||||
| } | ||||
| 
 | ||||
| // IsZero returns true if the VaultShares is empty.
 | ||||
| func (v VaultShares) IsZero() bool { | ||||
| 	for _, s := range v { | ||||
| 		// If any amount is non-zero, false
 | ||||
| 		if !s.Amount.IsZero() { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (shares VaultShares) isSorted() bool { | ||||
| 	for i := 1; i < len(shares); i++ { | ||||
| 		if shares[i-1].Denom > shares[i].Denom { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (shares VaultShares) String() string { | ||||
| 	if len(shares) == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
| 
 | ||||
| 	out := "" | ||||
| 	for _, share := range shares { | ||||
| 		out += fmt.Sprintf("%v,", share.String()) | ||||
| 	} | ||||
| 
 | ||||
| 	return out[:len(out)-1] | ||||
| } | ||||
| 
 | ||||
| // removeZeroShares removes all zero shares from the given share set in-place.
 | ||||
| func removeZeroShares(shares VaultShares) VaultShares { | ||||
| 	for i := 0; i < len(shares); i++ { | ||||
| 		if shares[i].IsZero() { | ||||
| 			break | ||||
| 		} else if i == len(shares)-1 { | ||||
| 			return shares | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	var result VaultShares | ||||
| 	if len(shares) > 0 { | ||||
| 		result = make(VaultShares, 0, len(shares)-1) | ||||
| 	} | ||||
| 
 | ||||
| 	for _, share := range shares { | ||||
| 		if !share.IsZero() { | ||||
| 			result = append(result, share) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return result | ||||
| } | ||||
| 
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
| // VaultShares sort interface
 | ||||
| 
 | ||||
| func (a VaultShares) Len() int { return len(a) } | ||||
| 
 | ||||
| // Less implements sort.Interface for VaultShares
 | ||||
| func (shares VaultShares) Less(i, j int) bool { return shares[i].Denom < shares[j].Denom } | ||||
| 
 | ||||
| // Swap implements sort.Interface for VaultShares
 | ||||
| func (shares VaultShares) Swap(i, j int) { shares[i], shares[j] = shares[j], shares[i] } | ||||
| 
 | ||||
| var _ sort.Interface = VaultShares{} | ||||
| 
 | ||||
| // Sort is a helper function to sort the set of vault shares in-place
 | ||||
| func (shares VaultShares) Sort() VaultShares { | ||||
| 	sort.Sort(shares) | ||||
| 	return shares | ||||
| } | ||||
							
								
								
									
										446
									
								
								x/earn/types/share_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										446
									
								
								x/earn/types/share_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,446 @@ | ||||
| package types_test | ||||
| 
 | ||||
| import ( | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/kava-labs/kava/x/earn/types" | ||||
| 	"github.com/stretchr/testify/suite" | ||||
| 
 | ||||
| 	sdk "github.com/cosmos/cosmos-sdk/types" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	testDenom1 = "ukava" | ||||
| 	testDenom2 = "usdx" | ||||
| ) | ||||
| 
 | ||||
| func d(i int64) sdk.Dec { | ||||
| 	return sdk.NewDec(i) | ||||
| } | ||||
| 
 | ||||
| type vaultShareTestSuite struct { | ||||
| 	suite.Suite | ||||
| } | ||||
| 
 | ||||
| func TestVaultShareTestSuite(t *testing.T) { | ||||
| 	suite.Run(t, new(vaultShareTestSuite)) | ||||
| } | ||||
| 
 | ||||
| func (s *vaultShareTestSuite) TestNewVaultShareFromDec() { | ||||
| 	s.Require().NotPanics(func() { | ||||
| 		types.NewVaultShare(testDenom1, sdk.NewDec(5)) | ||||
| 	}) | ||||
| 	s.Require().NotPanics(func() { | ||||
| 		types.NewVaultShare(testDenom1, sdk.ZeroDec()) | ||||
| 	}) | ||||
| 	s.Require().NotPanics(func() { | ||||
| 		types.NewVaultShare(strings.ToUpper(testDenom1), sdk.NewDec(5)) | ||||
| 	}) | ||||
| 	s.Require().Panics(func() { | ||||
| 		types.NewVaultShare(testDenom1, sdk.NewDec(-5)) | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func (s *vaultShareTestSuite) TestAddVaultShare() { | ||||
| 	vaultShareA1 := types.NewVaultShare(testDenom1, sdk.NewDecWithPrec(11, 1)) | ||||
| 	vaultShareA2 := types.NewVaultShare(testDenom1, sdk.NewDecWithPrec(22, 1)) | ||||
| 	vaultShareB1 := types.NewVaultShare(testDenom2, sdk.NewDecWithPrec(11, 1)) | ||||
| 
 | ||||
| 	// regular add
 | ||||
| 	res := vaultShareA1.Add(vaultShareA1) | ||||
| 	s.Require().Equal(vaultShareA2, res, "sum of shares is incorrect") | ||||
| 
 | ||||
| 	// bad denom add
 | ||||
| 	s.Require().Panics(func() { | ||||
| 		vaultShareA1.Add(vaultShareB1) | ||||
| 	}, "expected panic on sum of different denoms") | ||||
| } | ||||
| 
 | ||||
| func (s *vaultShareTestSuite) TestAddVaultShares() { | ||||
| 	one := sdk.NewDec(1) | ||||
| 	zero := sdk.NewDec(0) | ||||
| 	two := sdk.NewDec(2) | ||||
| 
 | ||||
| 	cases := []struct { | ||||
| 		inputOne types.VaultShares | ||||
| 		inputTwo types.VaultShares | ||||
| 		expected types.VaultShares | ||||
| 	}{ | ||||
| 		{ | ||||
| 			types.VaultShares{ | ||||
| 				{testDenom1, one}, | ||||
| 				{testDenom2, one}, | ||||
| 			}, | ||||
| 			types.VaultShares{ | ||||
| 				{testDenom1, one}, | ||||
| 				{testDenom2, one}, | ||||
| 			}, | ||||
| 			types.VaultShares{ | ||||
| 				{testDenom1, two}, | ||||
| 				{testDenom2, two}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			types.VaultShares{ | ||||
| 				{testDenom1, zero}, | ||||
| 				{testDenom2, one}, | ||||
| 			}, | ||||
| 			types.VaultShares{ | ||||
| 				{testDenom1, zero}, | ||||
| 				{testDenom2, zero}, | ||||
| 			}, | ||||
| 			types.VaultShares{ | ||||
| 				{testDenom2, one}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			types.VaultShares{ | ||||
| 				{testDenom1, zero}, | ||||
| 				{testDenom2, zero}, | ||||
| 			}, | ||||
| 			types.VaultShares{ | ||||
| 				{testDenom1, zero}, | ||||
| 				{testDenom2, zero}, | ||||
| 			}, | ||||
| 			types.VaultShares(nil), | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	for tcIndex, tc := range cases { | ||||
| 		res := tc.inputOne.Add(tc.inputTwo...) | ||||
| 		s.Require().Equal(tc.expected, res, "sum of shares is incorrect, tc #%d", tcIndex) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *vaultShareTestSuite) TestFilteredZeroVaultShares() { | ||||
| 	cases := []struct { | ||||
| 		name     string | ||||
| 		input    types.VaultShares | ||||
| 		original string | ||||
| 		expected string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name: "all greater than zero", | ||||
| 			input: types.VaultShares{ | ||||
| 				{"testa", sdk.NewDec(1)}, | ||||
| 				{"testb", sdk.NewDec(2)}, | ||||
| 				{"testc", sdk.NewDec(3)}, | ||||
| 				{"testd", sdk.NewDec(4)}, | ||||
| 				{"teste", sdk.NewDec(5)}, | ||||
| 			}, | ||||
| 			original: "1.000000000000000000testa,2.000000000000000000testb,3.000000000000000000testc,4.000000000000000000testd,5.000000000000000000teste", | ||||
| 			expected: "1.000000000000000000testa,2.000000000000000000testb,3.000000000000000000testc,4.000000000000000000testd,5.000000000000000000teste", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "zero share in middle", | ||||
| 			input: types.VaultShares{ | ||||
| 				{"testa", sdk.NewDec(1)}, | ||||
| 				{"testb", sdk.NewDec(2)}, | ||||
| 				{"testc", sdk.NewDec(0)}, | ||||
| 				{"testd", sdk.NewDec(4)}, | ||||
| 				{"teste", sdk.NewDec(5)}, | ||||
| 			}, | ||||
| 			original: "1.000000000000000000testa,2.000000000000000000testb,0.000000000000000000testc,4.000000000000000000testd,5.000000000000000000teste", | ||||
| 			expected: "1.000000000000000000testa,2.000000000000000000testb,4.000000000000000000testd,5.000000000000000000teste", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "zero share end (unordered)", | ||||
| 			input: types.VaultShares{ | ||||
| 				{"teste", sdk.NewDec(5)}, | ||||
| 				{"testc", sdk.NewDec(3)}, | ||||
| 				{"testa", sdk.NewDec(1)}, | ||||
| 				{"testd", sdk.NewDec(4)}, | ||||
| 				{"testb", sdk.NewDec(0)}, | ||||
| 			}, | ||||
| 			original: "5.000000000000000000teste,3.000000000000000000testc,1.000000000000000000testa,4.000000000000000000testd,0.000000000000000000testb", | ||||
| 			expected: "1.000000000000000000testa,3.000000000000000000testc,4.000000000000000000testd,5.000000000000000000teste", | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, tt := range cases { | ||||
| 		undertest := types.NewVaultShares(tt.input...) | ||||
| 		s.Require().Equal(tt.expected, undertest.String(), "NewVaultShares must return expected results") | ||||
| 		s.Require().Equal(tt.original, tt.input.String(), "input must be unmodified and match original") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *vaultShareTestSuite) TestIsValid() { | ||||
| 	tests := []struct { | ||||
| 		share      types.VaultShare | ||||
| 		expectPass bool | ||||
| 		msg        string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			types.NewVaultShare("mytoken", sdk.NewDec(10)), | ||||
| 			true, | ||||
| 			"valid shares should have passed", | ||||
| 		}, | ||||
| 		{ | ||||
| 			types.VaultShare{Denom: "BTC", Amount: sdk.NewDec(10)}, | ||||
| 			true, | ||||
| 			"valid uppercase denom", | ||||
| 		}, | ||||
| 		{ | ||||
| 			types.VaultShare{Denom: "Bitshare", Amount: sdk.NewDec(10)}, | ||||
| 			true, | ||||
| 			"valid mixed case denom", | ||||
| 		}, | ||||
| 		{ | ||||
| 			types.VaultShare{Denom: "btc", Amount: sdk.NewDec(-10)}, | ||||
| 			false, | ||||
| 			"negative amount", | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, tc := range tests { | ||||
| 		tc := tc | ||||
| 		if tc.expectPass { | ||||
| 			s.Require().True(tc.share.IsValid(), tc.msg) | ||||
| 		} else { | ||||
| 			s.Require().False(tc.share.IsValid(), tc.msg) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *vaultShareTestSuite) TestSubVaultShare() { | ||||
| 	tests := []struct { | ||||
| 		share      types.VaultShare | ||||
| 		expectPass bool | ||||
| 		msg        string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			types.NewVaultShare("mytoken", sdk.NewDec(20)), | ||||
| 			true, | ||||
| 			"valid shares should have passed", | ||||
| 		}, | ||||
| 		{ | ||||
| 			types.NewVaultShare("othertoken", sdk.NewDec(20)), | ||||
| 			false, | ||||
| 			"denom mismatch", | ||||
| 		}, | ||||
| 		{ | ||||
| 			types.NewVaultShare("mytoken", sdk.NewDec(9)), | ||||
| 			false, | ||||
| 			"negative amount", | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	vaultShare := types.NewVaultShare("mytoken", sdk.NewDec(10)) | ||||
| 
 | ||||
| 	for _, tc := range tests { | ||||
| 		tc := tc | ||||
| 		if tc.expectPass { | ||||
| 			equal := tc.share.Sub(vaultShare) | ||||
| 			s.Require().Equal(equal, vaultShare, tc.msg) | ||||
| 		} else { | ||||
| 			s.Require().Panics(func() { tc.share.Sub(vaultShare) }, tc.msg) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *vaultShareTestSuite) TestSubVaultShares() { | ||||
| 	tests := []struct { | ||||
| 		shares     types.VaultShares | ||||
| 		expectPass bool | ||||
| 		msg        string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			types.NewVaultShares(types.NewVaultShare("mytoken", d(10)), types.NewVaultShare("btc", d(20)), types.NewVaultShare("eth", d(30))), | ||||
| 			true, | ||||
| 			"sorted shares should have passed", | ||||
| 		}, | ||||
| 		{ | ||||
| 			types.VaultShares{types.NewVaultShare("mytoken", d(10)), types.NewVaultShare("btc", d(20)), types.NewVaultShare("eth", d(30))}, | ||||
| 			false, | ||||
| 			"unorted shares should panic", | ||||
| 		}, | ||||
| 		{ | ||||
| 			types.VaultShares{types.VaultShare{Denom: "BTC", Amount: sdk.NewDec(10)}, types.NewVaultShare("eth", d(15)), types.NewVaultShare("mytoken", d(5))}, | ||||
| 			false, | ||||
| 			"invalid denoms", | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	vaultShares := types.NewVaultShares(types.NewVaultShare("btc", d(10)), types.NewVaultShare("eth", d(15)), types.NewVaultShare("mytoken", d(5))) | ||||
| 
 | ||||
| 	for _, tc := range tests { | ||||
| 		tc := tc | ||||
| 		if tc.expectPass { | ||||
| 			equal := tc.shares.Sub(vaultShares...) | ||||
| 			s.Require().Equal(equal, vaultShares, tc.msg) | ||||
| 		} else { | ||||
| 			s.Require().Panics(func() { tc.shares.Sub(vaultShares...) }, tc.msg) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *vaultShareTestSuite) TestSortVaultShares() { | ||||
| 	good := types.VaultShares{ | ||||
| 		types.NewVaultShare("gas", d(1)), | ||||
| 		types.NewVaultShare("mineral", d(1)), | ||||
| 		types.NewVaultShare("tree", d(1)), | ||||
| 	} | ||||
| 	empty := types.VaultShares{ | ||||
| 		types.NewVaultShare("gold", d(0)), | ||||
| 	} | ||||
| 	badSort1 := types.VaultShares{ | ||||
| 		types.NewVaultShare("tree", d(1)), | ||||
| 		types.NewVaultShare("gas", d(1)), | ||||
| 		types.NewVaultShare("mineral", d(1)), | ||||
| 	} | ||||
| 	badSort2 := types.VaultShares{ // both are after the first one, but the second and third are in the wrong order
 | ||||
| 		types.NewVaultShare("gas", d(1)), | ||||
| 		types.NewVaultShare("tree", d(1)), | ||||
| 		types.NewVaultShare("mineral", d(1)), | ||||
| 	} | ||||
| 	badAmt := types.VaultShares{ | ||||
| 		types.NewVaultShare("gas", d(1)), | ||||
| 		types.NewVaultShare("tree", d(0)), | ||||
| 		types.NewVaultShare("mineral", d(1)), | ||||
| 	} | ||||
| 	dup := types.VaultShares{ | ||||
| 		types.NewVaultShare("gas", d(1)), | ||||
| 		types.NewVaultShare("gas", d(1)), | ||||
| 		types.NewVaultShare("mineral", d(1)), | ||||
| 	} | ||||
| 	cases := []struct { | ||||
| 		name          string | ||||
| 		shares        types.VaultShares | ||||
| 		before, after bool // valid before/after sort
 | ||||
| 	}{ | ||||
| 		{"valid shares", good, true, true}, | ||||
| 		{"empty shares", empty, false, false}, | ||||
| 		{"unsorted shares (1)", badSort1, false, true}, | ||||
| 		{"unsorted shares (2)", badSort2, false, true}, | ||||
| 		{"zero amount shares", badAmt, false, false}, | ||||
| 		{"duplicate shares", dup, false, false}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, tc := range cases { | ||||
| 		s.Require().Equal(tc.before, tc.shares.IsValid(), "share validity is incorrect before sorting; %s", tc.name) | ||||
| 		tc.shares.Sort() | ||||
| 		s.Require().Equal(tc.after, tc.shares.IsValid(), "share validity is incorrect after sorting;  %s", tc.name) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *vaultShareTestSuite) TestVaultSharesValidate() { | ||||
| 	testCases := []struct { | ||||
| 		input        types.VaultShares | ||||
| 		expectedPass bool | ||||
| 	}{ | ||||
| 		{types.VaultShares{}, true}, | ||||
| 		{types.VaultShares{types.VaultShare{testDenom1, sdk.NewDec(5)}}, true}, | ||||
| 		{types.VaultShares{types.VaultShare{testDenom1, sdk.NewDec(5)}, types.VaultShare{testDenom2, sdk.NewDec(100000)}}, true}, | ||||
| 		{types.VaultShares{types.VaultShare{testDenom1, sdk.NewDec(-5)}}, false}, | ||||
| 		{types.VaultShares{types.VaultShare{"BTC", sdk.NewDec(5)}}, true}, | ||||
| 		{types.VaultShares{types.VaultShare{"0BTC", sdk.NewDec(5)}}, false}, | ||||
| 		{types.VaultShares{types.VaultShare{testDenom1, sdk.NewDec(5)}, types.VaultShare{"B", sdk.NewDec(100000)}}, false}, | ||||
| 		{types.VaultShares{types.VaultShare{testDenom1, sdk.NewDec(5)}, types.VaultShare{testDenom2, sdk.NewDec(-100000)}}, false}, | ||||
| 		{types.VaultShares{types.VaultShare{testDenom1, sdk.NewDec(-5)}, types.VaultShare{testDenom2, sdk.NewDec(100000)}}, false}, | ||||
| 		{types.VaultShares{types.VaultShare{"BTC", sdk.NewDec(5)}, types.VaultShare{testDenom2, sdk.NewDec(100000)}}, true}, | ||||
| 		{types.VaultShares{types.VaultShare{"0BTC", sdk.NewDec(5)}, types.VaultShare{testDenom2, sdk.NewDec(100000)}}, false}, | ||||
| 	} | ||||
| 
 | ||||
| 	for i, tc := range testCases { | ||||
| 		err := tc.input.Validate() | ||||
| 		if tc.expectedPass { | ||||
| 			s.Require().NoError(err, "unexpected result for test case #%d, input: %v", i, tc.input) | ||||
| 		} else { | ||||
| 			s.Require().Error(err, "unexpected result for test case #%d, input: %v", i, tc.input) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *vaultShareTestSuite) TestVaultSharesString() { | ||||
| 	testCases := []struct { | ||||
| 		input    types.VaultShares | ||||
| 		expected string | ||||
| 	}{ | ||||
| 		{types.VaultShares{}, ""}, | ||||
| 		{ | ||||
| 			types.VaultShares{ | ||||
| 				types.NewVaultShare("atom", sdk.NewDecWithPrec(5040000000000000000, sdk.Precision)), | ||||
| 				types.NewVaultShare("stake", sdk.NewDecWithPrec(4000000000000000, sdk.Precision)), | ||||
| 			}, | ||||
| 			"5.040000000000000000atom,0.004000000000000000stake", | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	for i, tc := range testCases { | ||||
| 		out := tc.input.String() | ||||
| 		s.Require().Equal(tc.expected, out, "unexpected result for test case #%d, input: %v", i, tc.input) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *vaultShareTestSuite) TestNewVaultSharesWithIsValid() { | ||||
| 	fake1 := append(types.NewVaultShares(types.NewVaultShare("mytoken", d(10))), types.VaultShare{Denom: "10BTC", Amount: sdk.NewDec(10)}) | ||||
| 	fake2 := append(types.NewVaultShares(types.NewVaultShare("mytoken", d(10))), types.VaultShare{Denom: "BTC", Amount: sdk.NewDec(-10)}) | ||||
| 
 | ||||
| 	tests := []struct { | ||||
| 		share      types.VaultShares | ||||
| 		expectPass bool | ||||
| 		msg        string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			types.NewVaultShares(types.NewVaultShare("mytoken", d(10))), | ||||
| 			true, | ||||
| 			"valid shares should have passed", | ||||
| 		}, | ||||
| 		{ | ||||
| 			fake1, | ||||
| 			false, | ||||
| 			"invalid denoms", | ||||
| 		}, | ||||
| 		{ | ||||
| 			fake2, | ||||
| 			false, | ||||
| 			"negative amount", | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, tc := range tests { | ||||
| 		tc := tc | ||||
| 		if tc.expectPass { | ||||
| 			s.Require().True(tc.share.IsValid(), tc.msg) | ||||
| 		} else { | ||||
| 			s.Require().False(tc.share.IsValid(), tc.msg) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *vaultShareTestSuite) TestVaultShares_AddVaultShareWithIsValid() { | ||||
| 	lengthTestVaultShares := types.NewVaultShares().Add(types.NewVaultShare("mytoken", d(10))).Add(types.VaultShare{Denom: "BTC", Amount: sdk.NewDec(10)}) | ||||
| 	s.Require().Equal(2, len(lengthTestVaultShares), "should be 2") | ||||
| 
 | ||||
| 	tests := []struct { | ||||
| 		share      types.VaultShares | ||||
| 		expectPass bool | ||||
| 		msg        string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			types.NewVaultShares().Add(types.NewVaultShare("mytoken", d(10))), | ||||
| 			true, | ||||
| 			"valid shares should have passed", | ||||
| 		}, | ||||
| 		{ | ||||
| 			types.NewVaultShares().Add(types.NewVaultShare("mytoken", d(10))).Add(types.VaultShare{Denom: "0BTC", Amount: sdk.NewDec(10)}), | ||||
| 			false, | ||||
| 			"invalid denoms", | ||||
| 		}, | ||||
| 		{ | ||||
| 			types.NewVaultShares().Add(types.NewVaultShare("mytoken", d(10))).Add(types.VaultShare{Denom: "BTC", Amount: sdk.NewDec(-10)}), | ||||
| 			false, | ||||
| 			"negative amount", | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, tc := range tests { | ||||
| 		tc := tc | ||||
| 		if tc.expectPass { | ||||
| 			s.Require().True(tc.share.IsValid(), tc.msg) | ||||
| 		} else { | ||||
| 			s.Require().False(tc.share.IsValid(), tc.msg) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @ -74,6 +74,7 @@ var xxx_messageInfo_MsgDeposit proto.InternalMessageInfo | ||||
| 
 | ||||
| // MsgDepositResponse defines the Msg/Deposit response type.
 | ||||
| type MsgDepositResponse struct { | ||||
| 	Shares VaultShare `protobuf:"bytes,1,opt,name=shares,proto3" json:"shares"` | ||||
| } | ||||
| 
 | ||||
| func (m *MsgDepositResponse) Reset()         { *m = MsgDepositResponse{} } | ||||
| @ -109,6 +110,13 @@ func (m *MsgDepositResponse) XXX_DiscardUnknown() { | ||||
| 
 | ||||
| var xxx_messageInfo_MsgDepositResponse proto.InternalMessageInfo | ||||
| 
 | ||||
| func (m *MsgDepositResponse) GetShares() VaultShare { | ||||
| 	if m != nil { | ||||
| 		return m.Shares | ||||
| 	} | ||||
| 	return VaultShare{} | ||||
| } | ||||
| 
 | ||||
| // MsgWithdraw represents a message for withdrawing liquidity from a vault
 | ||||
| type MsgWithdraw struct { | ||||
| 	// from represents the address we are withdrawing for
 | ||||
| @ -153,6 +161,7 @@ var xxx_messageInfo_MsgWithdraw proto.InternalMessageInfo | ||||
| 
 | ||||
| // MsgWithdrawResponse defines the Msg/Withdraw response type.
 | ||||
| type MsgWithdrawResponse struct { | ||||
| 	Shares VaultShare `protobuf:"bytes,1,opt,name=shares,proto3" json:"shares"` | ||||
| } | ||||
| 
 | ||||
| func (m *MsgWithdrawResponse) Reset()         { *m = MsgWithdrawResponse{} } | ||||
| @ -188,6 +197,13 @@ func (m *MsgWithdrawResponse) XXX_DiscardUnknown() { | ||||
| 
 | ||||
| var xxx_messageInfo_MsgWithdrawResponse proto.InternalMessageInfo | ||||
| 
 | ||||
| func (m *MsgWithdrawResponse) GetShares() VaultShare { | ||||
| 	if m != nil { | ||||
| 		return m.Shares | ||||
| 	} | ||||
| 	return VaultShare{} | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	proto.RegisterType((*MsgDeposit)(nil), "kava.earn.v1beta1.MsgDeposit") | ||||
| 	proto.RegisterType((*MsgDepositResponse)(nil), "kava.earn.v1beta1.MsgDepositResponse") | ||||
| @ -198,30 +214,33 @@ func init() { | ||||
| func init() { proto.RegisterFile("kava/earn/v1beta1/tx.proto", fileDescriptor_2e9dcf48a3fa0009) } | ||||
| 
 | ||||
| var fileDescriptor_2e9dcf48a3fa0009 = []byte{ | ||||
| 	// 367 bytes of a gzipped FileDescriptorProto
 | ||||
| 	// 404 bytes of a gzipped FileDescriptorProto
 | ||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xca, 0x4e, 0x2c, 0x4b, | ||||
| 	0xd4, 0x4f, 0x4d, 0x2c, 0xca, 0xd3, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, 0x2f, 0xa9, | ||||
| 	0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x04, 0xc9, 0xe9, 0x81, 0xe4, 0xf4, 0xa0, 0x72, | ||||
| 	0x52, 0x92, 0xc9, 0xf9, 0xc5, 0xb9, 0xf9, 0xc5, 0xf1, 0x60, 0x05, 0xfa, 0x10, 0x0e, 0x44, 0xb5, | ||||
| 	0x94, 0x1c, 0x84, 0xa7, 0x9f, 0x94, 0x58, 0x9c, 0x0a, 0x37, 0x2b, 0x39, 0x3f, 0x33, 0x0f, 0x2a, | ||||
| 	0x2f, 0x92, 0x9e, 0x9f, 0x9e, 0x0f, 0xd1, 0x07, 0x62, 0x41, 0x44, 0x95, 0x9a, 0x19, 0xb9, 0xb8, | ||||
| 	0x7c, 0x8b, 0xd3, 0x5d, 0x52, 0x0b, 0xf2, 0x8b, 0x33, 0x4b, 0x84, 0xcc, 0xb8, 0x38, 0x53, 0x20, | ||||
| 	0xcc, 0xfc, 0x22, 0x09, 0x46, 0x05, 0x46, 0x0d, 0x4e, 0x27, 0x89, 0x4b, 0x5b, 0x74, 0x45, 0xa0, | ||||
| 	0x36, 0x39, 0xa6, 0xa4, 0x14, 0xa5, 0x16, 0x17, 0x07, 0x97, 0x14, 0x65, 0xe6, 0xa5, 0x07, 0x21, | ||||
| 	0x94, 0x0a, 0x99, 0x73, 0xb1, 0x25, 0xe6, 0xe6, 0x97, 0xe6, 0x95, 0x48, 0x30, 0x29, 0x30, 0x6a, | ||||
| 	0x70, 0x1b, 0x49, 0xea, 0x41, 0x75, 0x80, 0x5c, 0x03, 0x73, 0xbd, 0x9e, 0x73, 0x7e, 0x66, 0x9e, | ||||
| 	0x13, 0xcb, 0x89, 0x7b, 0xf2, 0x0c, 0x41, 0x50, 0xe5, 0x56, 0x2c, 0x1d, 0x0b, 0xe4, 0x19, 0x94, | ||||
| 	0x44, 0xb8, 0x84, 0x10, 0x8e, 0x08, 0x4a, 0x2d, 0x2e, 0xc8, 0xcf, 0x2b, 0x4e, 0x55, 0xaa, 0xe2, | ||||
| 	0xe2, 0xf6, 0x2d, 0x4e, 0x0f, 0xcf, 0x2c, 0xc9, 0x48, 0x29, 0x4a, 0x2c, 0x17, 0xd2, 0xe1, 0x62, | ||||
| 	0x49, 0x2b, 0xca, 0xcf, 0x25, 0xe8, 0x2c, 0xb0, 0x2a, 0x4a, 0x5d, 0x24, 0xca, 0x25, 0x8c, 0x64, | ||||
| 	0x37, 0xcc, 0x49, 0x46, 0xab, 0x18, 0xb9, 0x98, 0x7d, 0x8b, 0xd3, 0x85, 0xfc, 0xb9, 0xd8, 0x61, | ||||
| 	0x41, 0x26, 0xab, 0x87, 0x11, 0x4d, 0x7a, 0x08, 0xcf, 0x48, 0xa9, 0xe2, 0x95, 0x86, 0x19, 0x2c, | ||||
| 	0x14, 0xc4, 0xc5, 0x01, 0xf7, 0xa8, 0x1c, 0x76, 0x2d, 0x30, 0x79, 0x29, 0x35, 0xfc, 0xf2, 0x30, | ||||
| 	0x33, 0x9d, 0x1c, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, | ||||
| 	0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0x4a, 0x2d, 0x3d, | ||||
| 	0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x1f, 0x64, 0x96, 0x6e, 0x4e, 0x62, 0x52, | ||||
| 	0x31, 0x98, 0xa5, 0x5f, 0x01, 0x49, 0x8c, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0x49, 0x6c, 0xe0, 0x44, | ||||
| 	0x62, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xc0, 0x48, 0xd0, 0x39, 0xa6, 0x02, 0x00, 0x00, | ||||
| 	0x2f, 0x92, 0x9e, 0x9f, 0x9e, 0x0f, 0xd1, 0x07, 0x62, 0x41, 0x45, 0x65, 0x31, 0xed, 0x2f, 0x4b, | ||||
| 	0x2c, 0xcd, 0x29, 0x81, 0x48, 0x2b, 0x35, 0x33, 0x72, 0x71, 0xf9, 0x16, 0xa7, 0xbb, 0xa4, 0x16, | ||||
| 	0xe4, 0x17, 0x67, 0x96, 0x08, 0x99, 0x71, 0x71, 0xa6, 0x40, 0x98, 0xf9, 0x45, 0x12, 0x8c, 0x0a, | ||||
| 	0x8c, 0x1a, 0x9c, 0x4e, 0x12, 0x97, 0xb6, 0xe8, 0x8a, 0x40, 0x1d, 0xe2, 0x98, 0x92, 0x52, 0x94, | ||||
| 	0x5a, 0x5c, 0x1c, 0x5c, 0x52, 0x94, 0x99, 0x97, 0x1e, 0x84, 0x50, 0x2a, 0x64, 0xce, 0xc5, 0x96, | ||||
| 	0x98, 0x9b, 0x5f, 0x9a, 0x57, 0x22, 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, 0x6d, 0x24, 0xa9, 0x07, 0xd5, | ||||
| 	0x01, 0x72, 0x2c, 0xcc, 0x73, 0x7a, 0xce, 0xf9, 0x99, 0x79, 0x4e, 0x2c, 0x27, 0xee, 0xc9, 0x33, | ||||
| 	0x04, 0x41, 0x95, 0x5b, 0xb1, 0x74, 0x2c, 0x90, 0x67, 0x50, 0x0a, 0xe4, 0x12, 0x42, 0x38, 0x22, | ||||
| 	0x28, 0xb5, 0xb8, 0x20, 0x3f, 0xaf, 0x38, 0x55, 0xc8, 0x9a, 0x8b, 0xad, 0x38, 0x23, 0xb1, 0x28, | ||||
| 	0xb5, 0x18, 0xec, 0x12, 0x6e, 0x23, 0x59, 0x3d, 0x8c, 0xf0, 0xd2, 0x0b, 0x03, 0xf9, 0x25, 0x18, | ||||
| 	0xa4, 0x0a, 0x66, 0x30, 0x44, 0x8b, 0x52, 0x15, 0x17, 0xb7, 0x6f, 0x71, 0x7a, 0x78, 0x66, 0x49, | ||||
| 	0x46, 0x4a, 0x51, 0x62, 0xb9, 0x90, 0x0e, 0x17, 0x4b, 0x5a, 0x51, 0x7e, 0x2e, 0x41, 0x3f, 0x81, | ||||
| 	0x55, 0x51, 0xea, 0x9d, 0x20, 0x2e, 0x61, 0x24, 0xbb, 0xa9, 0xe2, 0x1f, 0xa3, 0x55, 0x8c, 0x5c, | ||||
| 	0xcc, 0xbe, 0xc5, 0xe9, 0x42, 0xfe, 0x5c, 0xec, 0xb0, 0xc8, 0xc2, 0xa6, 0x1f, 0x11, 0x8c, 0x52, | ||||
| 	0xaa, 0x78, 0xa5, 0xe1, 0xae, 0x0a, 0xe2, 0xe2, 0x80, 0x87, 0x92, 0x1c, 0x76, 0x2d, 0x30, 0x79, | ||||
| 	0x29, 0x35, 0xfc, 0xf2, 0x30, 0x33, 0x9d, 0x1c, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, | ||||
| 	0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, | ||||
| 	0x8e, 0x21, 0x4a, 0x2d, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x1f, 0x64, | ||||
| 	0x96, 0x6e, 0x4e, 0x62, 0x52, 0x31, 0x98, 0xa5, 0x5f, 0x01, 0x49, 0xa5, 0x25, 0x95, 0x05, 0xa9, | ||||
| 	0xc5, 0x49, 0x6c, 0xe0, 0xe4, 0x69, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xb4, 0xb0, 0x19, 0x18, | ||||
| 	0x3f, 0x03, 0x00, 0x00, | ||||
| } | ||||
| 
 | ||||
| // Reference imports to suppress errors if they are not otherwise used.
 | ||||
| @ -404,6 +423,16 @@ func (m *MsgDepositResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { | ||||
| 	_ = i | ||||
| 	var l int | ||||
| 	_ = l | ||||
| 	{ | ||||
| 		size, err := m.Shares.MarshalToSizedBuffer(dAtA[:i]) | ||||
| 		if err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
| 		i -= size | ||||
| 		i = encodeVarintTx(dAtA, i, uint64(size)) | ||||
| 	} | ||||
| 	i-- | ||||
| 	dAtA[i] = 0xa | ||||
| 	return len(dAtA) - i, nil | ||||
| } | ||||
| 
 | ||||
| @ -467,6 +496,16 @@ func (m *MsgWithdrawResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { | ||||
| 	_ = i | ||||
| 	var l int | ||||
| 	_ = l | ||||
| 	{ | ||||
| 		size, err := m.Shares.MarshalToSizedBuffer(dAtA[:i]) | ||||
| 		if err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
| 		i -= size | ||||
| 		i = encodeVarintTx(dAtA, i, uint64(size)) | ||||
| 	} | ||||
| 	i-- | ||||
| 	dAtA[i] = 0xa | ||||
| 	return len(dAtA) - i, nil | ||||
| } | ||||
| 
 | ||||
| @ -502,6 +541,8 @@ func (m *MsgDepositResponse) Size() (n int) { | ||||
| 	} | ||||
| 	var l int | ||||
| 	_ = l | ||||
| 	l = m.Shares.Size() | ||||
| 	n += 1 + l + sovTx(uint64(l)) | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| @ -526,6 +567,8 @@ func (m *MsgWithdrawResponse) Size() (n int) { | ||||
| 	} | ||||
| 	var l int | ||||
| 	_ = l | ||||
| 	l = m.Shares.Size() | ||||
| 	n += 1 + l + sovTx(uint64(l)) | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| @ -679,6 +722,39 @@ func (m *MsgDepositResponse) Unmarshal(dAtA []byte) error { | ||||
| 			return fmt.Errorf("proto: MsgDepositResponse: illegal tag %d (wire type %d)", fieldNum, wire) | ||||
| 		} | ||||
| 		switch fieldNum { | ||||
| 		case 1: | ||||
| 			if wireType != 2 { | ||||
| 				return fmt.Errorf("proto: wrong wireType = %d for field Shares", wireType) | ||||
| 			} | ||||
| 			var msglen int | ||||
| 			for shift := uint(0); ; shift += 7 { | ||||
| 				if shift >= 64 { | ||||
| 					return ErrIntOverflowTx | ||||
| 				} | ||||
| 				if iNdEx >= l { | ||||
| 					return io.ErrUnexpectedEOF | ||||
| 				} | ||||
| 				b := dAtA[iNdEx] | ||||
| 				iNdEx++ | ||||
| 				msglen |= int(b&0x7F) << shift | ||||
| 				if b < 0x80 { | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			if msglen < 0 { | ||||
| 				return ErrInvalidLengthTx | ||||
| 			} | ||||
| 			postIndex := iNdEx + msglen | ||||
| 			if postIndex < 0 { | ||||
| 				return ErrInvalidLengthTx | ||||
| 			} | ||||
| 			if postIndex > l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			if err := m.Shares.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			iNdEx = postIndex | ||||
| 		default: | ||||
| 			iNdEx = preIndex | ||||
| 			skippy, err := skipTx(dAtA[iNdEx:]) | ||||
| @ -844,6 +920,39 @@ func (m *MsgWithdrawResponse) Unmarshal(dAtA []byte) error { | ||||
| 			return fmt.Errorf("proto: MsgWithdrawResponse: illegal tag %d (wire type %d)", fieldNum, wire) | ||||
| 		} | ||||
| 		switch fieldNum { | ||||
| 		case 1: | ||||
| 			if wireType != 2 { | ||||
| 				return fmt.Errorf("proto: wrong wireType = %d for field Shares", wireType) | ||||
| 			} | ||||
| 			var msglen int | ||||
| 			for shift := uint(0); ; shift += 7 { | ||||
| 				if shift >= 64 { | ||||
| 					return ErrIntOverflowTx | ||||
| 				} | ||||
| 				if iNdEx >= l { | ||||
| 					return io.ErrUnexpectedEOF | ||||
| 				} | ||||
| 				b := dAtA[iNdEx] | ||||
| 				iNdEx++ | ||||
| 				msglen |= int(b&0x7F) << shift | ||||
| 				if b < 0x80 { | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			if msglen < 0 { | ||||
| 				return ErrInvalidLengthTx | ||||
| 			} | ||||
| 			postIndex := iNdEx + msglen | ||||
| 			if postIndex < 0 { | ||||
| 				return ErrInvalidLengthTx | ||||
| 			} | ||||
| 			if postIndex > l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			if err := m.Shares.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			iNdEx = postIndex | ||||
| 		default: | ||||
| 			iNdEx = preIndex | ||||
| 			skippy, err := skipTx(dAtA[iNdEx:]) | ||||
|  | ||||
| @ -8,32 +8,15 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| // NewVaultRecord returns a new VaultRecord with 0 supply.
 | ||||
| func NewVaultRecord(vaultDenom string) VaultRecord { | ||||
| func NewVaultRecord(vaultDenom string, amount sdk.Dec) VaultRecord { | ||||
| 	return VaultRecord{ | ||||
| 		Denom:       vaultDenom, | ||||
| 		TotalSupply: sdk.NewCoin(vaultDenom, sdk.ZeroInt()), | ||||
| 		TotalShares: NewVaultShare(vaultDenom, amount), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Validate returns an error if a VaultRecord is invalid.
 | ||||
| func (vr *VaultRecord) Validate() error { | ||||
| 	if err := sdk.ValidateDenom(vr.Denom); err != nil { | ||||
| 		return sdkerrors.Wrap(ErrInvalidVaultDenom, err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	if vr.TotalSupply.Denom != vr.Denom { | ||||
| 		return fmt.Errorf( | ||||
| 			"total supply denom %v does not match vault record denom %v", | ||||
| 			vr.TotalSupply.Denom, | ||||
| 			vr.Denom, | ||||
| 		) | ||||
| 	} | ||||
| 
 | ||||
| 	if err := vr.TotalSupply.Validate(); err != nil { | ||||
| 		return fmt.Errorf("vault total supply is invalid: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| 	return vr.TotalShares.Validate() | ||||
| } | ||||
| 
 | ||||
| // VaultRecords is a slice of VaultRecord.
 | ||||
| @ -48,11 +31,11 @@ func (vrs VaultRecords) Validate() error { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		if denoms[vr.Denom] { | ||||
| 			return fmt.Errorf("duplicate vault denom %s", vr.Denom) | ||||
| 		if denoms[vr.TotalShares.Denom] { | ||||
| 			return fmt.Errorf("duplicate vault denom %s", vr.TotalShares.Denom) | ||||
| 		} | ||||
| 
 | ||||
| 		denoms[vr.Denom] = true | ||||
| 		denoms[vr.TotalShares.Denom] = true | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| @ -60,10 +43,10 @@ func (vrs VaultRecords) Validate() error { | ||||
| 
 | ||||
| // NewVaultShareRecord returns a new VaultShareRecord with the provided supplied
 | ||||
| // coins.
 | ||||
| func NewVaultShareRecord(depositor sdk.AccAddress, supplied ...sdk.Coin) VaultShareRecord { | ||||
| func NewVaultShareRecord(depositor sdk.AccAddress, shares VaultShares) VaultShareRecord { | ||||
| 	return VaultShareRecord{ | ||||
| 		Depositor:      depositor, | ||||
| 		AmountSupplied: sdk.NewCoins(supplied...), | ||||
| 		Depositor: depositor, | ||||
| 		Shares:    shares, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -73,8 +56,8 @@ func (vsr VaultShareRecord) Validate() error { | ||||
| 		return fmt.Errorf("depositor is empty") | ||||
| 	} | ||||
| 
 | ||||
| 	if err := vsr.AmountSupplied.Validate(); err != nil { | ||||
| 		return fmt.Errorf("invalid vault share record amount supplied: %w", err) | ||||
| 	if err := vsr.Shares.Validate(); err != nil { | ||||
| 		return fmt.Errorf("invalid vault share record shares: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
|  | ||||
| @ -6,8 +6,8 @@ package types | ||||
| import ( | ||||
| 	fmt "fmt" | ||||
| 	_ "github.com/cosmos/cosmos-proto" | ||||
| 	_ "github.com/cosmos/cosmos-sdk/types" | ||||
| 	github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" | ||||
| 	types "github.com/cosmos/cosmos-sdk/types" | ||||
| 	_ "github.com/gogo/protobuf/gogoproto" | ||||
| 	proto "github.com/gogo/protobuf/proto" | ||||
| 	io "io" | ||||
| @ -82,15 +82,10 @@ func (m *AllowedVault) GetVaultStrategy() StrategyType { | ||||
| 	return STRATEGY_TYPE_UNSPECIFIED | ||||
| } | ||||
| 
 | ||||
| // VaultRecord is the state of a vault and is used to store the state of a
 | ||||
| // vault.
 | ||||
| // VaultRecord is the state of a vault.
 | ||||
| type VaultRecord struct { | ||||
| 	// Denom is the only supported denomination of the vault for deposits and
 | ||||
| 	// withdrawals.
 | ||||
| 	Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` | ||||
| 	// TotalSupply is the total supply of the vault, denominated **only** in the
 | ||||
| 	// user deposit/withdrawal denom, must be the same as the Denom field.
 | ||||
| 	TotalSupply types.Coin `protobuf:"bytes,2,opt,name=total_supply,json=totalSupply,proto3" json:"total_supply"` | ||||
| 	// TotalShares is the total distributed number of shares in the vault.
 | ||||
| 	TotalShares VaultShare `protobuf:"bytes,1,opt,name=total_shares,json=totalShares,proto3" json:"total_shares"` | ||||
| } | ||||
| 
 | ||||
| func (m *VaultRecord) Reset()         { *m = VaultRecord{} } | ||||
| @ -126,27 +121,19 @@ func (m *VaultRecord) XXX_DiscardUnknown() { | ||||
| 
 | ||||
| var xxx_messageInfo_VaultRecord proto.InternalMessageInfo | ||||
| 
 | ||||
| func (m *VaultRecord) GetDenom() string { | ||||
| func (m *VaultRecord) GetTotalShares() VaultShare { | ||||
| 	if m != nil { | ||||
| 		return m.Denom | ||||
| 		return m.TotalShares | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| 
 | ||||
| func (m *VaultRecord) GetTotalSupply() types.Coin { | ||||
| 	if m != nil { | ||||
| 		return m.TotalSupply | ||||
| 	} | ||||
| 	return types.Coin{} | ||||
| 	return VaultShare{} | ||||
| } | ||||
| 
 | ||||
| // VaultShareRecord defines the vault shares owned by a depositor.
 | ||||
| type VaultShareRecord struct { | ||||
| 	// Depositor represents the owner of the shares
 | ||||
| 	Depositor github_com_cosmos_cosmos_sdk_types.AccAddress `protobuf:"bytes,1,opt,name=depositor,proto3,casttype=github.com/cosmos/cosmos-sdk/types.AccAddress" json:"depositor,omitempty"` | ||||
| 	// AmountSupplied represents the total amount a depositor has supplied to the
 | ||||
| 	// vault. The vault is determined by the coin denom.
 | ||||
| 	AmountSupplied github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=amount_supplied,json=amountSupplied,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount_supplied"` | ||||
| 	// Shares represent the vault shares owned by the depositor.
 | ||||
| 	Shares VaultShares `protobuf:"bytes,2,rep,name=shares,proto3,castrepeated=VaultShares" json:"shares"` | ||||
| } | ||||
| 
 | ||||
| func (m *VaultShareRecord) Reset()         { *m = VaultShareRecord{} } | ||||
| @ -189,50 +176,97 @@ func (m *VaultShareRecord) GetDepositor() github_com_cosmos_cosmos_sdk_types.Acc | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (m *VaultShareRecord) GetAmountSupplied() github_com_cosmos_cosmos_sdk_types.Coins { | ||||
| func (m *VaultShareRecord) GetShares() VaultShares { | ||||
| 	if m != nil { | ||||
| 		return m.AmountSupplied | ||||
| 		return m.Shares | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // VaultShare defines shares of a vault owned by a depositor.
 | ||||
| type VaultShare struct { | ||||
| 	Denom  string                                 `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` | ||||
| 	Amount github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=amount,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"amount"` | ||||
| } | ||||
| 
 | ||||
| func (m *VaultShare) Reset()      { *m = VaultShare{} } | ||||
| func (*VaultShare) ProtoMessage() {} | ||||
| func (*VaultShare) Descriptor() ([]byte, []int) { | ||||
| 	return fileDescriptor_884eb89509fbdc04, []int{3} | ||||
| } | ||||
| func (m *VaultShare) XXX_Unmarshal(b []byte) error { | ||||
| 	return m.Unmarshal(b) | ||||
| } | ||||
| func (m *VaultShare) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { | ||||
| 	if deterministic { | ||||
| 		return xxx_messageInfo_VaultShare.Marshal(b, m, deterministic) | ||||
| 	} else { | ||||
| 		b = b[:cap(b)] | ||||
| 		n, err := m.MarshalToSizedBuffer(b) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return b[:n], nil | ||||
| 	} | ||||
| } | ||||
| func (m *VaultShare) XXX_Merge(src proto.Message) { | ||||
| 	xxx_messageInfo_VaultShare.Merge(m, src) | ||||
| } | ||||
| func (m *VaultShare) XXX_Size() int { | ||||
| 	return m.Size() | ||||
| } | ||||
| func (m *VaultShare) XXX_DiscardUnknown() { | ||||
| 	xxx_messageInfo_VaultShare.DiscardUnknown(m) | ||||
| } | ||||
| 
 | ||||
| var xxx_messageInfo_VaultShare proto.InternalMessageInfo | ||||
| 
 | ||||
| func (m *VaultShare) GetDenom() string { | ||||
| 	if m != nil { | ||||
| 		return m.Denom | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	proto.RegisterType((*AllowedVault)(nil), "kava.earn.v1beta1.AllowedVault") | ||||
| 	proto.RegisterType((*VaultRecord)(nil), "kava.earn.v1beta1.VaultRecord") | ||||
| 	proto.RegisterType((*VaultShareRecord)(nil), "kava.earn.v1beta1.VaultShareRecord") | ||||
| 	proto.RegisterType((*VaultShare)(nil), "kava.earn.v1beta1.VaultShare") | ||||
| } | ||||
| 
 | ||||
| func init() { proto.RegisterFile("kava/earn/v1beta1/vault.proto", fileDescriptor_884eb89509fbdc04) } | ||||
| 
 | ||||
| var fileDescriptor_884eb89509fbdc04 = []byte{ | ||||
| 	// 417 bytes of a gzipped FileDescriptorProto
 | ||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xbd, 0x8e, 0xd3, 0x40, | ||||
| 	0x10, 0xc7, 0xed, 0xe3, 0x43, 0xba, 0x4d, 0x08, 0x60, 0xae, 0xc8, 0x9d, 0x84, 0x6d, 0xa5, 0x40, | ||||
| 	0x69, 0xbc, 0xe6, 0x8e, 0x17, 0x20, 0x46, 0x42, 0xd4, 0x36, 0xa2, 0xa0, 0x89, 0xd6, 0xde, 0xc5, | ||||
| 	0xb1, 0x62, 0x7b, 0x2c, 0xef, 0x3a, 0xe0, 0xb7, 0xe0, 0x39, 0xa8, 0x79, 0x88, 0x94, 0x11, 0x15, | ||||
| 	0x55, 0x40, 0xc9, 0x0b, 0x50, 0x53, 0xa1, 0xfd, 0x48, 0x40, 0x8a, 0x40, 0x54, 0xde, 0x9d, 0x99, | ||||
| 	0xff, 0xfc, 0xfe, 0x33, 0x5e, 0xf4, 0x78, 0x49, 0x56, 0x24, 0x64, 0xa4, 0xad, 0xc3, 0xd5, 0x75, | ||||
| 	0xca, 0x04, 0xb9, 0x0e, 0x57, 0xa4, 0x2b, 0x05, 0x6e, 0x5a, 0x10, 0xe0, 0x3c, 0x94, 0x69, 0x2c, | ||||
| 	0xd3, 0xd8, 0xa4, 0xaf, 0x2e, 0x33, 0xe0, 0x15, 0xf0, 0xb9, 0x2a, 0x08, 0xf5, 0x45, 0x57, 0x5f, | ||||
| 	0xb9, 0xfa, 0x16, 0xa6, 0x84, 0xb3, 0x63, 0xbb, 0x0c, 0x8a, 0xda, 0xe4, 0x2f, 0x72, 0xc8, 0x41, | ||||
| 	0xeb, 0xe4, 0xc9, 0x44, 0xfd, 0x53, 0x0b, 0x5c, 0xb4, 0x44, 0xb0, 0xbc, 0xd7, 0x15, 0x93, 0x12, | ||||
| 	0x0d, 0x67, 0x65, 0x09, 0xef, 0x19, 0x7d, 0x23, 0xbd, 0x39, 0x17, 0xe8, 0x0e, 0x65, 0x35, 0x54, | ||||
| 	0x63, 0xdb, 0xb7, 0xa7, 0xe7, 0xb1, 0xbe, 0x38, 0x2f, 0xd1, 0x48, 0x59, 0x9f, 0x1f, 0xd4, 0xe3, | ||||
| 	0x33, 0xdf, 0x9e, 0x8e, 0x6e, 0x3c, 0x7c, 0x32, 0x04, 0x4e, 0x4c, 0xc9, 0xeb, 0xbe, 0x61, 0xf1, | ||||
| 	0x3d, 0x25, 0x3b, 0x84, 0x26, 0x39, 0x1a, 0x28, 0x4c, 0xcc, 0x32, 0x68, 0xe9, 0x5f, 0x60, 0x11, | ||||
| 	0x1a, 0x0a, 0x10, 0xa4, 0x9c, 0xf3, 0xae, 0x69, 0x4a, 0x8d, 0x1a, 0xdc, 0x5c, 0x62, 0xb3, 0x0f, | ||||
| 	0xb9, 0x81, 0x23, 0xec, 0x05, 0x14, 0x75, 0x74, 0x7b, 0xbd, 0xf5, 0xac, 0x78, 0xa0, 0x44, 0x89, | ||||
| 	0xd2, 0x4c, 0x7e, 0xd8, 0xe8, 0x81, 0x22, 0x25, 0x0b, 0xd2, 0x32, 0x83, 0x7b, 0x87, 0xce, 0x29, | ||||
| 	0x6b, 0x80, 0x17, 0x02, 0x5a, 0x85, 0x1c, 0x46, 0xaf, 0x7e, 0x6e, 0xbd, 0x20, 0x2f, 0xc4, 0xa2, | ||||
| 	0x4b, 0x71, 0x06, 0x95, 0xd9, 0xb9, 0xf9, 0x04, 0x9c, 0x2e, 0x43, 0xd1, 0x37, 0x8c, 0xe3, 0x59, | ||||
| 	0x96, 0xcd, 0x28, 0x6d, 0x19, 0xe7, 0x5f, 0x3e, 0x07, 0x8f, 0x8c, 0x13, 0x13, 0x89, 0x7a, 0xc1, | ||||
| 	0x78, 0xfc, 0xbb, 0xb5, 0x23, 0xd0, 0x7d, 0x52, 0x41, 0x57, 0x0b, 0x3d, 0x41, 0xc1, 0xe8, 0xf8, | ||||
| 	0xcc, 0xbf, 0xf5, 0xef, 0x19, 0x9e, 0xca, 0x19, 0x3e, 0x7d, 0xf3, 0xa6, 0xff, 0x61, 0x46, 0x0a, | ||||
| 	0x78, 0x3c, 0xd2, 0x8c, 0xc4, 0x20, 0xa2, 0xe7, 0xeb, 0x9d, 0x6b, 0x6f, 0x76, 0xae, 0xfd, 0x7d, | ||||
| 	0xe7, 0xda, 0x1f, 0xf7, 0xae, 0xb5, 0xd9, 0xbb, 0xd6, 0xd7, 0xbd, 0x6b, 0xbd, 0x7d, 0xf2, 0x47, | ||||
| 	0x4f, 0xf9, 0xbf, 0x82, 0x92, 0xa4, 0x5c, 0x9d, 0xc2, 0x0f, 0xfa, 0x71, 0xa8, 0xbe, 0xe9, 0x5d, | ||||
| 	0xf5, 0x24, 0x9e, 0xfd, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x6e, 0x80, 0xa4, 0xff, 0xb9, 0x02, 0x00, | ||||
| 	0x00, | ||||
| 	// 445 bytes of a gzipped FileDescriptorProto
 | ||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0x31, 0x8f, 0xd3, 0x30, | ||||
| 	0x18, 0x4d, 0x8e, 0xa3, 0x52, 0x9d, 0x72, 0x82, 0xdc, 0x0d, 0xc7, 0x49, 0x4d, 0xa2, 0x0c, 0xa8, | ||||
| 	0x4b, 0x12, 0xb5, 0x6c, 0x88, 0x81, 0x46, 0x55, 0x85, 0x18, 0xd3, 0xc2, 0xc0, 0x52, 0x39, 0x8e, | ||||
| 	0x49, 0xab, 0x26, 0x71, 0x14, 0xbb, 0x85, 0x2c, 0xfc, 0x06, 0x46, 0x46, 0x66, 0xe6, 0xfe, 0x06, | ||||
| 	0xd4, 0xb1, 0xea, 0x84, 0x18, 0x0a, 0x6a, 0xff, 0x05, 0x13, 0xb2, 0xe3, 0x12, 0xa4, 0x82, 0xb8, | ||||
| 	0x29, 0xf6, 0xfb, 0xde, 0x7b, 0xdf, 0xfb, 0xf2, 0x19, 0xb4, 0xe7, 0x70, 0x09, 0x3d, 0x0c, 0x8b, | ||||
| 	0xcc, 0x5b, 0x76, 0x43, 0xcc, 0x60, 0xd7, 0x5b, 0xc2, 0x45, 0xc2, 0xdc, 0xbc, 0x20, 0x8c, 0xe8, | ||||
| 	0x0f, 0x78, 0xd9, 0xe5, 0x65, 0x57, 0x96, 0x6f, 0x1e, 0x22, 0x42, 0x53, 0x42, 0x27, 0x82, 0xe0, | ||||
| 	0x55, 0x97, 0x8a, 0x7d, 0x63, 0x54, 0x37, 0x2f, 0x84, 0x14, 0xff, 0xb6, 0x43, 0x64, 0x96, 0xc9, | ||||
| 	0xfa, 0x55, 0x4c, 0x62, 0x52, 0xe9, 0xf8, 0x49, 0xa2, 0xd6, 0x69, 0x04, 0xca, 0x0a, 0xc8, 0x70, | ||||
| 	0x5c, 0x56, 0x0c, 0x3b, 0x01, 0xad, 0x7e, 0x92, 0x90, 0xb7, 0x38, 0x7a, 0xc5, 0xb3, 0xe9, 0x57, | ||||
| 	0xe0, 0x6e, 0x84, 0x33, 0x92, 0x5e, 0xab, 0x96, 0xda, 0x69, 0x06, 0xd5, 0x45, 0x1f, 0x82, 0x0b, | ||||
| 	0x11, 0x7d, 0x72, 0x54, 0x5f, 0x9f, 0x59, 0x6a, 0xe7, 0xa2, 0x67, 0xba, 0x27, 0x43, 0xb8, 0x23, | ||||
| 	0x49, 0x19, 0x97, 0x39, 0x0e, 0xee, 0x09, 0xd9, 0x11, 0xb2, 0x5f, 0x02, 0x4d, 0xb4, 0x09, 0x30, | ||||
| 	0x22, 0x45, 0xa4, 0x0f, 0x41, 0x8b, 0x11, 0x06, 0x93, 0x09, 0x9d, 0xc2, 0x02, 0x53, 0xd1, 0x53, | ||||
| 	0xeb, 0xb5, 0xff, 0x62, 0x2a, 0x54, 0x23, 0xce, 0xf2, 0xcf, 0xd7, 0x3b, 0x53, 0x09, 0x34, 0x21, | ||||
| 	0x14, 0x08, 0xb5, 0xbf, 0xa8, 0xe0, 0x7e, 0xcd, 0x90, 0xe6, 0x6f, 0x40, 0x33, 0xc2, 0x39, 0xa1, | ||||
| 	0x33, 0x46, 0x0a, 0xe1, 0xdc, 0xf2, 0x9f, 0xff, 0xdc, 0x99, 0x4e, 0x3c, 0x63, 0xd3, 0x45, 0xe8, | ||||
| 	0x22, 0x92, 0xca, 0x3f, 0x2c, 0x3f, 0x0e, 0x8d, 0xe6, 0x1e, 0x2b, 0x73, 0x4c, 0xdd, 0x3e, 0x42, | ||||
| 	0xfd, 0x28, 0x2a, 0x30, 0xa5, 0xdb, 0x95, 0x73, 0x29, 0xf7, 0x20, 0x11, 0xbf, 0x64, 0x98, 0x06, | ||||
| 	0xb5, 0xb5, 0xfe, 0x02, 0x34, 0x64, 0xfc, 0x33, 0xeb, 0xce, 0xff, 0xe3, 0x5f, 0xf2, 0xf8, 0x9f, | ||||
| 	0xbf, 0x9b, 0x5a, 0x8d, 0xd1, 0x40, 0x3a, 0xd8, 0xef, 0x01, 0xa8, 0xe1, 0x7f, 0xec, 0x62, 0x0c, | ||||
| 	0x1a, 0x30, 0x25, 0x8b, 0x8c, 0x89, 0x1d, 0x34, 0xfd, 0xa7, 0xdc, 0xf0, 0xdb, 0xce, 0x7c, 0x74, | ||||
| 	0x8b, 0xc1, 0x06, 0x18, 0x6d, 0x57, 0x0e, 0x90, 0x13, 0x0d, 0x30, 0x0a, 0xa4, 0xd7, 0x93, 0xf3, | ||||
| 	0x8f, 0x9f, 0x4c, 0xc5, 0x7f, 0xb6, 0xde, 0x1b, 0xea, 0x66, 0x6f, 0xa8, 0x3f, 0xf6, 0x86, 0xfa, | ||||
| 	0xe1, 0x60, 0x28, 0x9b, 0x83, 0xa1, 0x7c, 0x3d, 0x18, 0xca, 0xeb, 0x3f, 0xdd, 0xf9, 0x7c, 0x4e, | ||||
| 	0x02, 0x43, 0x2a, 0x4e, 0xde, 0xbb, 0xea, 0x81, 0x89, 0x0e, 0x61, 0x43, 0x3c, 0xab, 0xc7, 0xbf, | ||||
| 	0x02, 0x00, 0x00, 0xff, 0xff, 0x19, 0x75, 0x6f, 0x0e, 0xfd, 0x02, 0x00, 0x00, | ||||
| } | ||||
| 
 | ||||
| func (m *AllowedVault) Marshal() (dAtA []byte, err error) { | ||||
| @ -291,7 +325,7 @@ func (m *VaultRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) { | ||||
| 	var l int | ||||
| 	_ = l | ||||
| 	{ | ||||
| 		size, err := m.TotalSupply.MarshalToSizedBuffer(dAtA[:i]) | ||||
| 		size, err := m.TotalShares.MarshalToSizedBuffer(dAtA[:i]) | ||||
| 		if err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
| @ -299,14 +333,7 @@ func (m *VaultRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) { | ||||
| 		i = encodeVarintVault(dAtA, i, uint64(size)) | ||||
| 	} | ||||
| 	i-- | ||||
| 	dAtA[i] = 0x12 | ||||
| 	if len(m.Denom) > 0 { | ||||
| 		i -= len(m.Denom) | ||||
| 		copy(dAtA[i:], m.Denom) | ||||
| 		i = encodeVarintVault(dAtA, i, uint64(len(m.Denom))) | ||||
| 		i-- | ||||
| 		dAtA[i] = 0xa | ||||
| 	} | ||||
| 	dAtA[i] = 0xa | ||||
| 	return len(dAtA) - i, nil | ||||
| } | ||||
| 
 | ||||
| @ -330,10 +357,10 @@ func (m *VaultShareRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) { | ||||
| 	_ = i | ||||
| 	var l int | ||||
| 	_ = l | ||||
| 	if len(m.AmountSupplied) > 0 { | ||||
| 		for iNdEx := len(m.AmountSupplied) - 1; iNdEx >= 0; iNdEx-- { | ||||
| 	if len(m.Shares) > 0 { | ||||
| 		for iNdEx := len(m.Shares) - 1; iNdEx >= 0; iNdEx-- { | ||||
| 			{ | ||||
| 				size, err := m.AmountSupplied[iNdEx].MarshalToSizedBuffer(dAtA[:i]) | ||||
| 				size, err := m.Shares[iNdEx].MarshalToSizedBuffer(dAtA[:i]) | ||||
| 				if err != nil { | ||||
| 					return 0, err | ||||
| 				} | ||||
| @ -354,6 +381,46 @@ func (m *VaultShareRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) { | ||||
| 	return len(dAtA) - i, nil | ||||
| } | ||||
| 
 | ||||
| func (m *VaultShare) Marshal() (dAtA []byte, err error) { | ||||
| 	size := m.Size() | ||||
| 	dAtA = make([]byte, size) | ||||
| 	n, err := m.MarshalToSizedBuffer(dAtA[:size]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return dAtA[:n], nil | ||||
| } | ||||
| 
 | ||||
| func (m *VaultShare) MarshalTo(dAtA []byte) (int, error) { | ||||
| 	size := m.Size() | ||||
| 	return m.MarshalToSizedBuffer(dAtA[:size]) | ||||
| } | ||||
| 
 | ||||
| func (m *VaultShare) MarshalToSizedBuffer(dAtA []byte) (int, error) { | ||||
| 	i := len(dAtA) | ||||
| 	_ = i | ||||
| 	var l int | ||||
| 	_ = l | ||||
| 	{ | ||||
| 		size := m.Amount.Size() | ||||
| 		i -= size | ||||
| 		if _, err := m.Amount.MarshalTo(dAtA[i:]); err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
| 		i = encodeVarintVault(dAtA, i, uint64(size)) | ||||
| 	} | ||||
| 	i-- | ||||
| 	dAtA[i] = 0x12 | ||||
| 	if len(m.Denom) > 0 { | ||||
| 		i -= len(m.Denom) | ||||
| 		copy(dAtA[i:], m.Denom) | ||||
| 		i = encodeVarintVault(dAtA, i, uint64(len(m.Denom))) | ||||
| 		i-- | ||||
| 		dAtA[i] = 0xa | ||||
| 	} | ||||
| 	return len(dAtA) - i, nil | ||||
| } | ||||
| 
 | ||||
| func encodeVarintVault(dAtA []byte, offset int, v uint64) int { | ||||
| 	offset -= sovVault(v) | ||||
| 	base := offset | ||||
| @ -387,11 +454,7 @@ func (m *VaultRecord) Size() (n int) { | ||||
| 	} | ||||
| 	var l int | ||||
| 	_ = l | ||||
| 	l = len(m.Denom) | ||||
| 	if l > 0 { | ||||
| 		n += 1 + l + sovVault(uint64(l)) | ||||
| 	} | ||||
| 	l = m.TotalSupply.Size() | ||||
| 	l = m.TotalShares.Size() | ||||
| 	n += 1 + l + sovVault(uint64(l)) | ||||
| 	return n | ||||
| } | ||||
| @ -406,8 +469,8 @@ func (m *VaultShareRecord) Size() (n int) { | ||||
| 	if l > 0 { | ||||
| 		n += 1 + l + sovVault(uint64(l)) | ||||
| 	} | ||||
| 	if len(m.AmountSupplied) > 0 { | ||||
| 		for _, e := range m.AmountSupplied { | ||||
| 	if len(m.Shares) > 0 { | ||||
| 		for _, e := range m.Shares { | ||||
| 			l = e.Size() | ||||
| 			n += 1 + l + sovVault(uint64(l)) | ||||
| 		} | ||||
| @ -415,6 +478,21 @@ func (m *VaultShareRecord) Size() (n int) { | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| func (m *VaultShare) Size() (n int) { | ||||
| 	if m == nil { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	var l int | ||||
| 	_ = l | ||||
| 	l = len(m.Denom) | ||||
| 	if l > 0 { | ||||
| 		n += 1 + l + sovVault(uint64(l)) | ||||
| 	} | ||||
| 	l = m.Amount.Size() | ||||
| 	n += 1 + l + sovVault(uint64(l)) | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| func sovVault(x uint64) (n int) { | ||||
| 	return (math_bits.Len64(x|1) + 6) / 7 | ||||
| } | ||||
| @ -553,39 +631,7 @@ func (m *VaultRecord) Unmarshal(dAtA []byte) error { | ||||
| 		switch fieldNum { | ||||
| 		case 1: | ||||
| 			if wireType != 2 { | ||||
| 				return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType) | ||||
| 			} | ||||
| 			var stringLen uint64 | ||||
| 			for shift := uint(0); ; shift += 7 { | ||||
| 				if shift >= 64 { | ||||
| 					return ErrIntOverflowVault | ||||
| 				} | ||||
| 				if iNdEx >= l { | ||||
| 					return io.ErrUnexpectedEOF | ||||
| 				} | ||||
| 				b := dAtA[iNdEx] | ||||
| 				iNdEx++ | ||||
| 				stringLen |= uint64(b&0x7F) << shift | ||||
| 				if b < 0x80 { | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			intStringLen := int(stringLen) | ||||
| 			if intStringLen < 0 { | ||||
| 				return ErrInvalidLengthVault | ||||
| 			} | ||||
| 			postIndex := iNdEx + intStringLen | ||||
| 			if postIndex < 0 { | ||||
| 				return ErrInvalidLengthVault | ||||
| 			} | ||||
| 			if postIndex > l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			m.Denom = string(dAtA[iNdEx:postIndex]) | ||||
| 			iNdEx = postIndex | ||||
| 		case 2: | ||||
| 			if wireType != 2 { | ||||
| 				return fmt.Errorf("proto: wrong wireType = %d for field TotalSupply", wireType) | ||||
| 				return fmt.Errorf("proto: wrong wireType = %d for field TotalShares", wireType) | ||||
| 			} | ||||
| 			var msglen int | ||||
| 			for shift := uint(0); ; shift += 7 { | ||||
| @ -612,7 +658,7 @@ func (m *VaultRecord) Unmarshal(dAtA []byte) error { | ||||
| 			if postIndex > l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			if err := m.TotalSupply.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { | ||||
| 			if err := m.TotalShares.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			iNdEx = postIndex | ||||
| @ -702,7 +748,7 @@ func (m *VaultShareRecord) Unmarshal(dAtA []byte) error { | ||||
| 			iNdEx = postIndex | ||||
| 		case 2: | ||||
| 			if wireType != 2 { | ||||
| 				return fmt.Errorf("proto: wrong wireType = %d for field AmountSupplied", wireType) | ||||
| 				return fmt.Errorf("proto: wrong wireType = %d for field Shares", wireType) | ||||
| 			} | ||||
| 			var msglen int | ||||
| 			for shift := uint(0); ; shift += 7 { | ||||
| @ -729,8 +775,124 @@ func (m *VaultShareRecord) Unmarshal(dAtA []byte) error { | ||||
| 			if postIndex > l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			m.AmountSupplied = append(m.AmountSupplied, types.Coin{}) | ||||
| 			if err := m.AmountSupplied[len(m.AmountSupplied)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { | ||||
| 			m.Shares = append(m.Shares, VaultShare{}) | ||||
| 			if err := m.Shares[len(m.Shares)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			iNdEx = postIndex | ||||
| 		default: | ||||
| 			iNdEx = preIndex | ||||
| 			skippy, err := skipVault(dAtA[iNdEx:]) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			if (skippy < 0) || (iNdEx+skippy) < 0 { | ||||
| 				return ErrInvalidLengthVault | ||||
| 			} | ||||
| 			if (iNdEx + skippy) > l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			iNdEx += skippy | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if iNdEx > l { | ||||
| 		return io.ErrUnexpectedEOF | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| func (m *VaultShare) Unmarshal(dAtA []byte) error { | ||||
| 	l := len(dAtA) | ||||
| 	iNdEx := 0 | ||||
| 	for iNdEx < l { | ||||
| 		preIndex := iNdEx | ||||
| 		var wire uint64 | ||||
| 		for shift := uint(0); ; shift += 7 { | ||||
| 			if shift >= 64 { | ||||
| 				return ErrIntOverflowVault | ||||
| 			} | ||||
| 			if iNdEx >= l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			b := dAtA[iNdEx] | ||||
| 			iNdEx++ | ||||
| 			wire |= uint64(b&0x7F) << shift | ||||
| 			if b < 0x80 { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		fieldNum := int32(wire >> 3) | ||||
| 		wireType := int(wire & 0x7) | ||||
| 		if wireType == 4 { | ||||
| 			return fmt.Errorf("proto: VaultShare: wiretype end group for non-group") | ||||
| 		} | ||||
| 		if fieldNum <= 0 { | ||||
| 			return fmt.Errorf("proto: VaultShare: illegal tag %d (wire type %d)", fieldNum, wire) | ||||
| 		} | ||||
| 		switch fieldNum { | ||||
| 		case 1: | ||||
| 			if wireType != 2 { | ||||
| 				return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType) | ||||
| 			} | ||||
| 			var stringLen uint64 | ||||
| 			for shift := uint(0); ; shift += 7 { | ||||
| 				if shift >= 64 { | ||||
| 					return ErrIntOverflowVault | ||||
| 				} | ||||
| 				if iNdEx >= l { | ||||
| 					return io.ErrUnexpectedEOF | ||||
| 				} | ||||
| 				b := dAtA[iNdEx] | ||||
| 				iNdEx++ | ||||
| 				stringLen |= uint64(b&0x7F) << shift | ||||
| 				if b < 0x80 { | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			intStringLen := int(stringLen) | ||||
| 			if intStringLen < 0 { | ||||
| 				return ErrInvalidLengthVault | ||||
| 			} | ||||
| 			postIndex := iNdEx + intStringLen | ||||
| 			if postIndex < 0 { | ||||
| 				return ErrInvalidLengthVault | ||||
| 			} | ||||
| 			if postIndex > l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			m.Denom = string(dAtA[iNdEx:postIndex]) | ||||
| 			iNdEx = postIndex | ||||
| 		case 2: | ||||
| 			if wireType != 2 { | ||||
| 				return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) | ||||
| 			} | ||||
| 			var stringLen uint64 | ||||
| 			for shift := uint(0); ; shift += 7 { | ||||
| 				if shift >= 64 { | ||||
| 					return ErrIntOverflowVault | ||||
| 				} | ||||
| 				if iNdEx >= l { | ||||
| 					return io.ErrUnexpectedEOF | ||||
| 				} | ||||
| 				b := dAtA[iNdEx] | ||||
| 				iNdEx++ | ||||
| 				stringLen |= uint64(b&0x7F) << shift | ||||
| 				if b < 0x80 { | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			intStringLen := int(stringLen) | ||||
| 			if intStringLen < 0 { | ||||
| 				return ErrInvalidLengthVault | ||||
| 			} | ||||
| 			postIndex := iNdEx + intStringLen | ||||
| 			if postIndex < 0 { | ||||
| 				return ErrInvalidLengthVault | ||||
| 			} | ||||
| 			if postIndex > l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			iNdEx = postIndex | ||||
|  | ||||
| @ -25,12 +25,10 @@ func TestVaultRecordValidate(t *testing.T) { | ||||
| 			name: "valid vault records", | ||||
| 			vaultRecords: types.VaultRecords{ | ||||
| 				{ | ||||
| 					Denom:       "usdx", | ||||
| 					TotalSupply: sdk.NewInt64Coin("usdx", 0), | ||||
| 					TotalShares: types.NewVaultShare("usdx", sdk.NewDec(0)), | ||||
| 				}, | ||||
| 				{ | ||||
| 					Denom:       "ukava", | ||||
| 					TotalSupply: sdk.NewInt64Coin("ukava", 5), | ||||
| 					TotalShares: types.NewVaultShare("ukava", sdk.NewDec(5)), | ||||
| 				}, | ||||
| 			}, | ||||
| 			errArgs: errArgs{ | ||||
| @ -41,12 +39,10 @@ func TestVaultRecordValidate(t *testing.T) { | ||||
| 			name: "invalid - duplicate denom", | ||||
| 			vaultRecords: types.VaultRecords{ | ||||
| 				{ | ||||
| 					Denom:       "usdx", | ||||
| 					TotalSupply: sdk.NewInt64Coin("usdx", 0), | ||||
| 					TotalShares: types.NewVaultShare("usdx", sdk.NewDec(0)), | ||||
| 				}, | ||||
| 				{ | ||||
| 					Denom:       "usdx", | ||||
| 					TotalSupply: sdk.NewInt64Coin("usdx", 5), | ||||
| 					TotalShares: types.NewVaultShare("usdx", sdk.NewDec(5)), | ||||
| 				}, | ||||
| 			}, | ||||
| 			errArgs: errArgs{ | ||||
| @ -58,8 +54,7 @@ func TestVaultRecordValidate(t *testing.T) { | ||||
| 			name: "invalid - invalid denom", | ||||
| 			vaultRecords: types.VaultRecords{ | ||||
| 				{ | ||||
| 					Denom:       "", | ||||
| 					TotalSupply: sdk.NewInt64Coin("ukava", 0), | ||||
| 					TotalShares: types.VaultShare{Denom: "", Amount: sdk.NewDec(0)}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			errArgs: errArgs{ | ||||
| @ -67,30 +62,16 @@ func TestVaultRecordValidate(t *testing.T) { | ||||
| 				contains:   "invalid denom", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "invalid - mismatch denom", | ||||
| 			vaultRecords: types.VaultRecords{ | ||||
| 				{ | ||||
| 					Denom:       "usdx", | ||||
| 					TotalSupply: sdk.NewInt64Coin("ukava", 0), | ||||
| 				}, | ||||
| 			}, | ||||
| 			errArgs: errArgs{ | ||||
| 				expectPass: false, | ||||
| 				contains:   "total supply denom ukava does not match vault record denom usdx", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "invalid - negative", | ||||
| 			vaultRecords: types.VaultRecords{ | ||||
| 				{ | ||||
| 					Denom:       "usdx", | ||||
| 					TotalSupply: sdk.Coin{Denom: "usdx", Amount: sdk.NewInt(-1)}, | ||||
| 					TotalShares: types.VaultShare{"usdx", sdk.NewDec(-5)}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			errArgs: errArgs{ | ||||
| 				expectPass: false, | ||||
| 				contains:   "negative coin amount", | ||||
| 				contains:   "vault share amount -5.000000000000000000 is negative", | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| @ -127,15 +108,15 @@ func TestVaultShareRecordsValidate(t *testing.T) { | ||||
| 			vaultRecords: types.VaultShareRecords{ | ||||
| 				{ | ||||
| 					Depositor: addrs[0], | ||||
| 					AmountSupplied: sdk.NewCoins( | ||||
| 						sdk.NewInt64Coin("usdx", 0), | ||||
| 					Shares: types.NewVaultShares( | ||||
| 						types.NewVaultShare("usdx", sdk.NewDec(0)), | ||||
| 					), | ||||
| 				}, | ||||
| 				{ | ||||
| 					Depositor: addrs[1], | ||||
| 					AmountSupplied: sdk.NewCoins( | ||||
| 						sdk.NewInt64Coin("usdx", 0), | ||||
| 						sdk.NewInt64Coin("ukava", 5), | ||||
| 					Shares: types.NewVaultShares( | ||||
| 						types.NewVaultShare("usdx", sdk.NewDec(0)), | ||||
| 						types.NewVaultShare("ukava", sdk.NewDec(5)), | ||||
| 					), | ||||
| 				}, | ||||
| 			}, | ||||
| @ -148,15 +129,15 @@ func TestVaultShareRecordsValidate(t *testing.T) { | ||||
| 			vaultRecords: types.VaultShareRecords{ | ||||
| 				{ | ||||
| 					Depositor: addrs[0], | ||||
| 					AmountSupplied: sdk.NewCoins( | ||||
| 						sdk.NewInt64Coin("usdx", 0), | ||||
| 					Shares: types.NewVaultShares( | ||||
| 						types.NewVaultShare("usdx", sdk.NewDec(0)), | ||||
| 					), | ||||
| 				}, | ||||
| 				{ | ||||
| 					Depositor: addrs[0], | ||||
| 					AmountSupplied: sdk.NewCoins( | ||||
| 						sdk.NewInt64Coin("usdx", 0), | ||||
| 						sdk.NewInt64Coin("ukava", 5), | ||||
| 					Shares: types.NewVaultShares( | ||||
| 						types.NewVaultShare("usdx", sdk.NewDec(0)), | ||||
| 						types.NewVaultShare("ukava", sdk.NewDec(5)), | ||||
| 					), | ||||
| 				}, | ||||
| 			}, | ||||
| @ -170,8 +151,8 @@ func TestVaultShareRecordsValidate(t *testing.T) { | ||||
| 			vaultRecords: types.VaultShareRecords{ | ||||
| 				{ | ||||
| 					Depositor: sdk.AccAddress{}, | ||||
| 					AmountSupplied: sdk.NewCoins( | ||||
| 						sdk.NewInt64Coin("usdx", 0), | ||||
| 					Shares: types.NewVaultShares( | ||||
| 						types.NewVaultShare("usdx", sdk.NewDec(0)), | ||||
| 					), | ||||
| 				}, | ||||
| 			}, | ||||
| @ -185,14 +166,15 @@ func TestVaultShareRecordsValidate(t *testing.T) { | ||||
| 			vaultRecords: types.VaultShareRecords{ | ||||
| 				{ | ||||
| 					Depositor: addrs[0], | ||||
| 					AmountSupplied: sdk.Coins{ | ||||
| 						sdk.Coin{Denom: "ukava", Amount: sdk.NewInt(-1)}, | ||||
| 					// Direct slice, not NewVaultShares() which panics
 | ||||
| 					Shares: types.VaultShares{ | ||||
| 						types.VaultShare{"usdx", sdk.NewDec(-5)}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			errArgs: errArgs{ | ||||
| 				expectPass: false, | ||||
| 				contains:   "amount is not positive", | ||||
| 				contains:   "invalid vault share record shares: share -5.000000000000000000usdx amount is not positive", | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| @ -300,11 +282,11 @@ func TestAllowedVaultsValidate(t *testing.T) { | ||||
| func TestNewVaultShareRecord(t *testing.T) { | ||||
| 	_, addrs := app.GeneratePrivKeyAddressPairs(1) | ||||
| 
 | ||||
| 	coins := sdk.NewCoins( | ||||
| 		sdk.NewInt64Coin("usdx", 10), | ||||
| 		sdk.NewInt64Coin("ukava", 5), | ||||
| 	shares := types.NewVaultShares( | ||||
| 		types.NewVaultShare("usdx", sdk.NewDec(0)), | ||||
| 		types.NewVaultShare("ukava", sdk.NewDec(5)), | ||||
| 	) | ||||
| 
 | ||||
| 	shareRecord := types.NewVaultShareRecord(addrs[0], coins...) | ||||
| 	require.Equal(t, coins, shareRecord.AmountSupplied) | ||||
| 	shareRecord := types.NewVaultShareRecord(addrs[0], shares) | ||||
| 	require.Equal(t, shares, shareRecord.Shares) | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Derrick Lee
						Derrick Lee