mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-27 00:35:18 +00:00
88d4868316
* Simplify strategies to lend and savings * Add hard and savings keepers * Add ctx to strategy interface, fill in lend strategy * Rename lend strategy to hard * Fix hard deposit query, fix withdraw bank send * Fix misleading borrow instead of withdraw for hard * Remove liquidateall strategy method * Withdraw tests * Add hard gs to testutil suite * Update withdraw tests with working hard strategy, clean strategy interface methods * Check allowed denom for strategy * Update GetVaultTotalValue doc note * Update error wrap message for unsupported denom * Remove unnecessary viewvault keeper * Withdraw amount from account value, not supplied value * Test value > supplied withdraw * Use dec when dividing for withdrawAmountPercent * Use the correct store prefix for vault shares * Update swap references to earn * Simplify vault shares, use a single share for all coins per address
119 lines
3.5 KiB
Go
119 lines
3.5 KiB
Go
package keeper
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
|
|
|
"github.com/kava-labs/kava/x/earn/types"
|
|
)
|
|
|
|
// Withdraw removes the amount of supplied tokens from a vault and transfers it
|
|
// back to the account.
|
|
func (k *Keeper) Withdraw(ctx sdk.Context, from sdk.AccAddress, wantAmount sdk.Coin) error {
|
|
// Get AllowedVault, if not found (not a valid vault), return error
|
|
allowedVault, found := k.GetAllowedVault(ctx, wantAmount.Denom)
|
|
if !found {
|
|
return types.ErrInvalidVaultDenom
|
|
}
|
|
|
|
if wantAmount.IsZero() {
|
|
return types.ErrInsufficientAmount
|
|
}
|
|
|
|
// Check if VaultRecord exists
|
|
vaultRecord, found := k.GetVaultRecord(ctx, wantAmount.Denom)
|
|
if !found {
|
|
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())
|
|
|
|
// Check if account is not withdrawing more than they have
|
|
// account value < want withdraw amount
|
|
if vaultAccValue.Amount.LT(wantAmount.Amount) {
|
|
return sdkerrors.Wrapf(
|
|
types.ErrInsufficientValue,
|
|
"account vault value of %s is less than %s desired withdraw amount",
|
|
vaultAccValue,
|
|
wantAmount,
|
|
)
|
|
}
|
|
|
|
// Get the strategy for the vault
|
|
strategy, err := k.GetStrategy(allowedVault.VaultStrategy)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 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 {
|
|
return fmt.Errorf("failed to withdraw from strategy: %w", err)
|
|
}
|
|
|
|
// Send coins back to account, must withdraw from strategy first or the
|
|
// module account may not have any funds to send.
|
|
if err := k.bankKeeper.SendCoinsFromModuleToAccount(
|
|
ctx,
|
|
types.ModuleName,
|
|
from,
|
|
sdk.NewCoins(wantAmount),
|
|
); 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())
|
|
|
|
// Decrement VaultRecord and VaultShareRecord supplies
|
|
vaultRecord.TotalSupply = vaultRecord.TotalSupply.Sub(sharesWithdrawn)
|
|
vaultShareRecord.AmountSupplied = vaultShareRecord.AmountSupplied.Sub(sdk.NewCoins(sharesWithdrawn))
|
|
|
|
// Update VaultRecord and VaultShareRecord, deletes if zero supply
|
|
k.UpdateVaultRecord(ctx, vaultRecord)
|
|
k.UpdateVaultShareRecord(ctx, vaultShareRecord)
|
|
|
|
ctx.EventManager().EmitEvent(
|
|
sdk.NewEvent(
|
|
types.EventTypeVaultWithdraw,
|
|
sdk.NewAttribute(types.AttributeKeyVaultDenom, wantAmount.Denom),
|
|
sdk.NewAttribute(types.AttributeKeyOwner, from.String()),
|
|
sdk.NewAttribute(sdk.AttributeKeyAmount, wantAmount.Amount.String()),
|
|
),
|
|
)
|
|
|
|
return nil
|
|
}
|