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:
Derrick Lee 2022-09-12 09:23:26 -07:00 committed by GitHub
parent fe89ba938d
commit b5e162a930
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 2477 additions and 1552 deletions

View File

@ -604,8 +604,8 @@ func NewApp(
earnSubspace,
app.accountKeeper,
app.bankKeeper,
hardKeeper,
savingsKeeper,
&hardKeeper,
&savingsKeeper,
)
// create committee keeper with router

View File

@ -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) | | |

View File

@ -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];
}

View File

@ -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];
}

View File

@ -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
];
}

View File

@ -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)
},
}
}

View File

@ -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),
))
}

View File

@ -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)),
),
},
},
)

View File

@ -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()),
),
)

View File

@ -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() {

View File

@ -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

View File

@ -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",
)
}

View File

@ -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()),
),
)

View File

@ -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)
}

View File

@ -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))
}

View 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
}

View 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
}

View 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
}

View 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")
}

View 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)
}

View File

@ -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())
}

View File

@ -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()),
),
)

View File

@ -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() {

View File

@ -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,
)
}
}
}

View File

@ -26,10 +26,3 @@ func NewQueryDepositsRequest(
Pagination: pagination,
}
}
// NewQueryTotalDepositedRequest returns a new QueryTotalDepositedRequest
func NewQueryTotalDepositedRequest(denom string) *QueryTotalDepositedRequest {
return &QueryTotalDepositedRequest{
Denom: denom,
}
}

View File

@ -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

View File

@ -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
View 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
View 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)
}
}
}

View File

@ -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:])

View File

@ -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

View File

@ -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

View File

@ -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)
}