mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 06:37:26 +00:00 
			
		
		
		
	* 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
 | 
						|
}
 |