573 lines
18 KiB
Go
573 lines
18 KiB
Go
package keeper
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
|
"github.com/cosmos/cosmos-sdk/store/prefix"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
|
vestexported "github.com/cosmos/cosmos-sdk/x/auth/vesting/exported"
|
|
"github.com/cosmos/cosmos-sdk/x/bank/internal/types"
|
|
"github.com/cosmos/cosmos-sdk/x/params"
|
|
)
|
|
|
|
var _ Keeper = (*BaseKeeper)(nil)
|
|
|
|
// Keeper defines a module interface that facilitates the transfer of coins
|
|
// between accounts.
|
|
type Keeper interface {
|
|
SendKeeper
|
|
|
|
DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error
|
|
UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins) error
|
|
}
|
|
|
|
// BaseKeeper manages transfers between accounts. It implements the Keeper interface.
|
|
type BaseKeeper struct {
|
|
BaseSendKeeper
|
|
|
|
ak types.AccountKeeper
|
|
paramSpace params.Subspace
|
|
}
|
|
|
|
func NewBaseKeeper(
|
|
cdc *codec.Codec, storeKey sdk.StoreKey, ak types.AccountKeeper, paramSpace params.Subspace, blacklistedAddrs map[string]bool,
|
|
) BaseKeeper {
|
|
|
|
ps := paramSpace.WithKeyTable(types.ParamKeyTable())
|
|
return BaseKeeper{
|
|
BaseSendKeeper: NewBaseSendKeeper(cdc, storeKey, ak, ps, blacklistedAddrs),
|
|
ak: ak,
|
|
paramSpace: ps,
|
|
}
|
|
}
|
|
|
|
// DelegateCoins performs delegation by deducting amt coins from an account with
|
|
// address addr. For vesting accounts, delegations amounts are tracked for both
|
|
// vesting and vested coins. The coins are then transferred from the delegator
|
|
// address to a ModuleAccount address. If any of the delegation amounts are negative,
|
|
// an error is returned.
|
|
func (k BaseKeeper) DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error {
|
|
moduleAcc := k.ak.GetAccount(ctx, moduleAccAddr)
|
|
if moduleAcc == nil {
|
|
return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", moduleAccAddr)
|
|
}
|
|
|
|
if !amt.IsValid() {
|
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String())
|
|
}
|
|
|
|
balances := sdk.NewCoins()
|
|
|
|
for _, coin := range amt {
|
|
balance := k.GetBalance(ctx, delegatorAddr, coin.Denom)
|
|
if balance.IsLT(coin) {
|
|
return sdkerrors.Wrapf(
|
|
sdkerrors.ErrInsufficientFunds, "failed to delegate; %s < %s", balance, amt,
|
|
)
|
|
}
|
|
|
|
balances = balances.Add(balance)
|
|
k.SetBalance(ctx, delegatorAddr, balance.Sub(coin))
|
|
}
|
|
|
|
if err := k.trackDelegation(ctx, delegatorAddr, ctx.BlockHeader().Time, balances, amt); err != nil {
|
|
return sdkerrors.Wrap(err, "failed to track delegation")
|
|
}
|
|
|
|
_, err := k.AddCoins(ctx, moduleAccAddr, amt)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// UndelegateCoins performs undelegation by crediting amt coins to an account with
|
|
// address addr. For vesting accounts, undelegation amounts are tracked for both
|
|
// vesting and vested coins. The coins are then transferred from a ModuleAccount
|
|
// address to the delegator address. If any of the undelegation amounts are
|
|
// negative, an error is returned.
|
|
func (k BaseKeeper) UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins) error {
|
|
moduleAcc := k.ak.GetAccount(ctx, moduleAccAddr)
|
|
if moduleAcc == nil {
|
|
return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", moduleAccAddr)
|
|
}
|
|
|
|
if !amt.IsValid() {
|
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String())
|
|
}
|
|
|
|
_, err := k.SubtractCoins(ctx, moduleAccAddr, amt)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := k.trackUndelegation(ctx, delegatorAddr, amt); err != nil {
|
|
return sdkerrors.Wrap(err, "failed to track undelegation")
|
|
}
|
|
|
|
_, err = k.AddCoins(ctx, delegatorAddr, amt)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SendKeeper defines a module interface that facilitates the transfer of coins
|
|
// between accounts without the possibility of creating coins.
|
|
type SendKeeper interface {
|
|
ViewKeeper
|
|
|
|
InputOutputCoins(ctx sdk.Context, inputs []types.Input, outputs []types.Output) error
|
|
SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error
|
|
|
|
SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, error)
|
|
AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, error)
|
|
|
|
SetBalance(ctx sdk.Context, addr sdk.AccAddress, balance sdk.Coin) error
|
|
SetBalances(ctx sdk.Context, addr sdk.AccAddress, balances sdk.Coins) error
|
|
|
|
GetSendEnabled(ctx sdk.Context) bool
|
|
SetSendEnabled(ctx sdk.Context, enabled bool)
|
|
|
|
BlacklistedAddr(addr sdk.AccAddress) bool
|
|
}
|
|
|
|
var _ SendKeeper = (*BaseSendKeeper)(nil)
|
|
|
|
// BaseSendKeeper only allows transfers between accounts without the possibility of
|
|
// creating coins. It implements the SendKeeper interface.
|
|
type BaseSendKeeper struct {
|
|
BaseViewKeeper
|
|
|
|
cdc *codec.Codec
|
|
ak types.AccountKeeper
|
|
storeKey sdk.StoreKey
|
|
paramSpace params.Subspace
|
|
|
|
// list of addresses that are restricted from receiving transactions
|
|
blacklistedAddrs map[string]bool
|
|
}
|
|
|
|
func NewBaseSendKeeper(
|
|
cdc *codec.Codec, storeKey sdk.StoreKey, ak types.AccountKeeper, paramSpace params.Subspace, blacklistedAddrs map[string]bool,
|
|
) BaseSendKeeper {
|
|
|
|
return BaseSendKeeper{
|
|
BaseViewKeeper: NewBaseViewKeeper(cdc, storeKey, ak),
|
|
cdc: cdc,
|
|
ak: ak,
|
|
storeKey: storeKey,
|
|
paramSpace: paramSpace,
|
|
blacklistedAddrs: blacklistedAddrs,
|
|
}
|
|
}
|
|
|
|
// InputOutputCoins performs multi-send functionality. It accepts a series of
|
|
// inputs that correspond to a series of outputs. It returns an error if the
|
|
// inputs and outputs don't lineup or if any single transfer of tokens fails.
|
|
func (k BaseSendKeeper) InputOutputCoins(ctx sdk.Context, inputs []types.Input, outputs []types.Output) error {
|
|
// Safety check ensuring that when sending coins the keeper must maintain the
|
|
// Check supply invariant and validity of Coins.
|
|
if err := types.ValidateInputsOutputs(inputs, outputs); err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, in := range inputs {
|
|
_, err := k.SubtractCoins(ctx, in.Address, in.Coins)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx.EventManager().EmitEvent(
|
|
sdk.NewEvent(
|
|
sdk.EventTypeMessage,
|
|
sdk.NewAttribute(types.AttributeKeySender, in.Address.String()),
|
|
),
|
|
)
|
|
}
|
|
|
|
for _, out := range outputs {
|
|
_, err := k.AddCoins(ctx, out.Address, out.Coins)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx.EventManager().EmitEvent(
|
|
sdk.NewEvent(
|
|
types.EventTypeTransfer,
|
|
sdk.NewAttribute(types.AttributeKeyRecipient, out.Address.String()),
|
|
sdk.NewAttribute(sdk.AttributeKeyAmount, out.Coins.String()),
|
|
),
|
|
)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SendCoins transfers amt coins from a sending account to a receiving account.
|
|
// An error is returned upon failure.
|
|
func (k BaseSendKeeper) SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error {
|
|
ctx.EventManager().EmitEvents(sdk.Events{
|
|
sdk.NewEvent(
|
|
types.EventTypeTransfer,
|
|
sdk.NewAttribute(types.AttributeKeyRecipient, toAddr.String()),
|
|
sdk.NewAttribute(sdk.AttributeKeyAmount, amt.String()),
|
|
),
|
|
sdk.NewEvent(
|
|
sdk.EventTypeMessage,
|
|
sdk.NewAttribute(types.AttributeKeySender, fromAddr.String()),
|
|
),
|
|
})
|
|
|
|
_, err := k.SubtractCoins(ctx, fromAddr, amt)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = k.AddCoins(ctx, toAddr, amt)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create account if recipient does not exist.
|
|
//
|
|
// NOTE: This should ultimately be removed in favor a more flexible approach
|
|
// such as delegated fee messages.
|
|
acc := k.ak.GetAccount(ctx, toAddr)
|
|
if acc == nil {
|
|
k.ak.SetAccount(ctx, k.ak.NewAccountWithAddress(ctx, toAddr))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SubtractCoins removes amt coins the account by the given address. An error is
|
|
// returned if the resulting balance is negative or the initial amount is invalid.
|
|
func (k BaseSendKeeper) SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, error) {
|
|
if !amt.IsValid() {
|
|
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String())
|
|
}
|
|
|
|
resultCoins := sdk.NewCoins()
|
|
lockedCoins := k.LockedCoins(ctx, addr)
|
|
|
|
for _, coin := range amt {
|
|
balance := k.GetBalance(ctx, addr, coin.Denom)
|
|
locked := sdk.NewCoin(coin.Denom, lockedCoins.AmountOf(coin.Denom))
|
|
spendable := balance.Sub(locked)
|
|
|
|
_, hasNeg := sdk.Coins{spendable}.SafeSub(sdk.Coins{coin})
|
|
if hasNeg {
|
|
return nil, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, "%s < %s", spendable, coin)
|
|
}
|
|
|
|
newBalance := balance.Sub(coin)
|
|
resultCoins = resultCoins.Add(newBalance)
|
|
|
|
k.SetBalance(ctx, addr, newBalance)
|
|
}
|
|
|
|
return resultCoins, nil
|
|
}
|
|
|
|
// AddCoins adds amt to the account balance given by the provided address. An
|
|
// error is returned if the initial amount is invalid or if any resulting new
|
|
// balance is negative.
|
|
func (k BaseSendKeeper) AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, error) {
|
|
if !amt.IsValid() {
|
|
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String())
|
|
}
|
|
|
|
var resultCoins sdk.Coins
|
|
|
|
for _, coin := range amt {
|
|
balance := k.GetBalance(ctx, addr, coin.Denom)
|
|
newBalance := balance.Add(coin)
|
|
resultCoins = resultCoins.Add(newBalance)
|
|
|
|
k.SetBalance(ctx, addr, newBalance)
|
|
}
|
|
|
|
return resultCoins, nil
|
|
}
|
|
|
|
// ClearBalances removes all balances for a given account by address.
|
|
func (k BaseSendKeeper) ClearBalances(ctx sdk.Context, addr sdk.AccAddress) {
|
|
keys := [][]byte{}
|
|
k.IterateAccountBalances(ctx, addr, func(balance sdk.Coin) bool {
|
|
keys = append(keys, []byte(balance.Denom))
|
|
return false
|
|
})
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
|
balancesStore := prefix.NewStore(store, types.BalancesPrefix)
|
|
accountStore := prefix.NewStore(balancesStore, addr.Bytes())
|
|
|
|
for _, key := range keys {
|
|
accountStore.Delete(key)
|
|
}
|
|
}
|
|
|
|
// SetBalances sets the balance (multiple coins) for an account by address. It will
|
|
// clear out all balances prior to setting the new coins as to set existing balances
|
|
// to zero if they don't exist in amt. An error is returned upon failure.
|
|
func (k BaseSendKeeper) SetBalances(ctx sdk.Context, addr sdk.AccAddress, balances sdk.Coins) error {
|
|
k.ClearBalances(ctx, addr)
|
|
|
|
for _, balance := range balances {
|
|
err := k.SetBalance(ctx, addr, balance)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SetBalance sets the coin balance for an account by address.
|
|
func (k BaseSendKeeper) SetBalance(ctx sdk.Context, addr sdk.AccAddress, balance sdk.Coin) error {
|
|
if !balance.IsValid() {
|
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, balance.String())
|
|
}
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
|
balancesStore := prefix.NewStore(store, types.BalancesPrefix)
|
|
accountStore := prefix.NewStore(balancesStore, addr.Bytes())
|
|
|
|
bz := k.cdc.MustMarshalBinaryBare(balance)
|
|
accountStore.Set([]byte(balance.Denom), bz)
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetSendEnabled returns the current SendEnabled
|
|
func (k BaseSendKeeper) GetSendEnabled(ctx sdk.Context) bool {
|
|
var enabled bool
|
|
k.paramSpace.Get(ctx, types.ParamStoreKeySendEnabled, &enabled)
|
|
return enabled
|
|
}
|
|
|
|
// SetSendEnabled sets the send enabled
|
|
func (k BaseSendKeeper) SetSendEnabled(ctx sdk.Context, enabled bool) {
|
|
k.paramSpace.Set(ctx, types.ParamStoreKeySendEnabled, &enabled)
|
|
}
|
|
|
|
// BlacklistedAddr checks if a given address is blacklisted (i.e restricted from
|
|
// receiving funds)
|
|
func (k BaseSendKeeper) BlacklistedAddr(addr sdk.AccAddress) bool {
|
|
return k.blacklistedAddrs[addr.String()]
|
|
}
|
|
|
|
var _ ViewKeeper = (*BaseViewKeeper)(nil)
|
|
|
|
// ViewKeeper defines a module interface that facilitates read only access to
|
|
// account balances.
|
|
type ViewKeeper interface {
|
|
ValidateBalance(ctx sdk.Context, addr sdk.AccAddress) error
|
|
HasBalance(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coin) bool
|
|
|
|
GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
|
|
GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
|
|
|
|
LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
|
|
SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
|
|
|
|
IterateAccountBalances(ctx sdk.Context, addr sdk.AccAddress, cb func(coin sdk.Coin) (stop bool))
|
|
IterateAllBalances(ctx sdk.Context, cb func(address sdk.AccAddress, coin sdk.Coin) (stop bool))
|
|
}
|
|
|
|
// BaseViewKeeper implements a read only keeper implementation of ViewKeeper.
|
|
type BaseViewKeeper struct {
|
|
cdc *codec.Codec
|
|
storeKey sdk.StoreKey
|
|
ak types.AccountKeeper
|
|
}
|
|
|
|
// NewBaseViewKeeper returns a new BaseViewKeeper.
|
|
func NewBaseViewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, ak types.AccountKeeper) BaseViewKeeper {
|
|
return BaseViewKeeper{
|
|
cdc: cdc,
|
|
storeKey: storeKey,
|
|
ak: ak,
|
|
}
|
|
}
|
|
|
|
// Logger returns a module-specific logger.
|
|
func (k BaseViewKeeper) Logger(ctx sdk.Context) log.Logger {
|
|
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
|
|
}
|
|
|
|
// HasBalance returns whether or not an account has at least amt balance.
|
|
func (k BaseViewKeeper) HasBalance(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coin) bool {
|
|
return k.GetBalance(ctx, addr, amt.Denom).IsGTE(amt)
|
|
}
|
|
|
|
// GetAllBalances returns all the account balances for the given account address.
|
|
func (k BaseViewKeeper) GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins {
|
|
balances := sdk.NewCoins()
|
|
k.IterateAccountBalances(ctx, addr, func(balance sdk.Coin) bool {
|
|
balances = balances.Add(balance)
|
|
return false
|
|
})
|
|
|
|
return balances.Sort()
|
|
}
|
|
|
|
// GetBalance returns the balance of a specific denomination for a given account
|
|
// by address.
|
|
func (k BaseViewKeeper) GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin {
|
|
store := ctx.KVStore(k.storeKey)
|
|
balancesStore := prefix.NewStore(store, types.BalancesPrefix)
|
|
accountStore := prefix.NewStore(balancesStore, addr.Bytes())
|
|
|
|
bz := accountStore.Get([]byte(denom))
|
|
if bz == nil {
|
|
return sdk.NewCoin(denom, sdk.ZeroInt())
|
|
}
|
|
|
|
var balance sdk.Coin
|
|
k.cdc.MustUnmarshalBinaryBare(bz, &balance)
|
|
|
|
return balance
|
|
}
|
|
|
|
// IterateAccountBalances iterates over the balances of a single account and
|
|
// provides the token balance to a callback. If true is returned from the
|
|
// callback, iteration is halted.
|
|
func (k BaseViewKeeper) IterateAccountBalances(ctx sdk.Context, addr sdk.AccAddress, cb func(sdk.Coin) bool) {
|
|
store := ctx.KVStore(k.storeKey)
|
|
balancesStore := prefix.NewStore(store, types.BalancesPrefix)
|
|
accountStore := prefix.NewStore(balancesStore, addr.Bytes())
|
|
|
|
iterator := accountStore.Iterator(nil, nil)
|
|
defer iterator.Close()
|
|
|
|
for ; iterator.Valid(); iterator.Next() {
|
|
var balance sdk.Coin
|
|
k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &balance)
|
|
|
|
if cb(balance) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// IterateAllBalances iterates over all the balances of all accounts and
|
|
// denominations that are provided to a callback. If true is returned from the
|
|
// callback, iteration is halted.
|
|
func (k BaseViewKeeper) IterateAllBalances(ctx sdk.Context, cb func(sdk.AccAddress, sdk.Coin) bool) {
|
|
store := ctx.KVStore(k.storeKey)
|
|
balancesStore := prefix.NewStore(store, types.BalancesPrefix)
|
|
|
|
iterator := balancesStore.Iterator(nil, nil)
|
|
defer iterator.Close()
|
|
|
|
for ; iterator.Valid(); iterator.Next() {
|
|
address := types.AddressFromBalancesStore(iterator.Key())
|
|
|
|
var balance sdk.Coin
|
|
k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &balance)
|
|
|
|
if cb(address, balance) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// LockedCoins returns all the coins that are not spendable (i.e. locked) for an
|
|
// account by address. For standard accounts, the result will always be no coins.
|
|
// For vesting accounts, LockedCoins is delegated to the concrete vesting account
|
|
// type.
|
|
func (k BaseViewKeeper) LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins {
|
|
acc := k.ak.GetAccount(ctx, addr)
|
|
if acc != nil {
|
|
vacc, ok := acc.(vestexported.VestingAccount)
|
|
if ok {
|
|
return vacc.LockedCoins(ctx.BlockTime())
|
|
}
|
|
}
|
|
|
|
return sdk.NewCoins()
|
|
}
|
|
|
|
// SpendableCoins returns the total balances of spendable coins for an account
|
|
// by address. If the account has no spendable coins, an empty Coins slice is
|
|
// returned.
|
|
func (k BaseViewKeeper) SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins {
|
|
balances := k.GetAllBalances(ctx, addr)
|
|
locked := k.LockedCoins(ctx, addr)
|
|
|
|
spendable, hasNeg := balances.SafeSub(locked)
|
|
if hasNeg {
|
|
return sdk.NewCoins()
|
|
}
|
|
|
|
return spendable
|
|
}
|
|
|
|
// ValidateBalance validates all balances for a given account address returning
|
|
// an error if any balance is invalid. It will check for vesting account types
|
|
// and validate the balances against the original vesting balances.
|
|
//
|
|
// CONTRACT: ValidateBalance should only be called upon genesis state. In the
|
|
// case of vesting accounts, balances may change in a valid manner that would
|
|
// otherwise yield an error from this call.
|
|
func (k BaseViewKeeper) ValidateBalance(ctx sdk.Context, addr sdk.AccAddress) error {
|
|
acc := k.ak.GetAccount(ctx, addr)
|
|
if acc == nil {
|
|
return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", addr)
|
|
}
|
|
|
|
balances := k.GetAllBalances(ctx, addr)
|
|
if !balances.IsValid() {
|
|
return fmt.Errorf("account balance of %s is invalid", balances)
|
|
}
|
|
|
|
vacc, ok := acc.(vestexported.VestingAccount)
|
|
if ok {
|
|
ogv := vacc.GetOriginalVesting()
|
|
if ogv.IsAnyGT(balances) {
|
|
return fmt.Errorf("vesting amount %s cannot be greater than total amount %s", ogv, balances)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (k BaseKeeper) trackDelegation(ctx sdk.Context, addr sdk.AccAddress, blockTime time.Time, balance, amt sdk.Coins) error {
|
|
acc := k.ak.GetAccount(ctx, addr)
|
|
if acc == nil {
|
|
return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", addr)
|
|
}
|
|
|
|
vacc, ok := acc.(vestexported.VestingAccount)
|
|
if ok {
|
|
// TODO: return error on account.TrackDelegation
|
|
vacc.TrackDelegation(blockTime, balance, amt)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (k BaseKeeper) trackUndelegation(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error {
|
|
acc := k.ak.GetAccount(ctx, addr)
|
|
if acc == nil {
|
|
return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", addr)
|
|
}
|
|
|
|
vacc, ok := acc.(vestexported.VestingAccount)
|
|
if ok {
|
|
// TODO: return error on account.TrackUndelegation
|
|
vacc.TrackUndelegation(amt)
|
|
}
|
|
|
|
return nil
|
|
}
|