mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-24 23:35:19 +00:00
b5e162a930
* 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
83 lines
2.8 KiB
Go
83 lines
2.8 KiB
Go
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
|
|
}
|