func (am AccountManager) minusCoinFromAddress(ctx sdk.Context, address sdk.AccAddress, coin linotypes.Coin) sdk.Error {
	if coin.IsZero() {
		return nil
	}
	bank, err := am.storage.GetBank(ctx, address)
	if err != nil {
		return err
	}
	bank.Saving = bank.Saving.Minus(coin)
	if !bank.Saving.IsGTE(am.paramHolder.GetAccountParam(ctx).MinimumBalance) {
		return types.ErrAccountSavingCoinNotEnough()
	}
	am.storage.SetBank(ctx, address, bank)
	return nil
}
minusCoinFromAddress - minus coin from address
package manager
import (
	"bytes"
	"fmt"
	"reflect"
	codec "github.com/cosmos/cosmos-sdk/codec"
	sdk "github.com/cosmos/cosmos-sdk/types"
	"github.com/tendermint/tendermint/crypto"
	"github.com/lino-network/lino/param"
	linotypes "github.com/lino-network/lino/types"
	"github.com/lino-network/lino/utils"
	"github.com/lino-network/lino/x/account/model"
	"github.com/lino-network/lino/x/account/types"
)
const (
	nSecOfOneHour = 3600
	nHourOfOneYear = 8766
	exportVersion = 3
	importVersion = 3
)
type AccountManager struct {
	storage     model.AccountStorage
	paramHolder param.ParamKeeper
}
func NewAccountManager(key sdk.StoreKey, holder param.ParamKeeper) AccountManager {
	return AccountManager{
		storage:     model.NewAccountStorage(key),
		paramHolder: holder,
	}
}
func (am AccountManager) InitGenesis(ctx sdk.Context, total linotypes.Coin, pools []model.Pool) {
	neverInited := false
	func() {
		defer func() {
			if r := recover(); r != nil {
				neverInited = true
			}
		}()
		am.storage.GetSupply(ctx)
	}()
	if !neverInited {
		panic("Account Module Already Inited")
	}
	am.storage.SetSupply(ctx, &model.Supply{
		LastYearTotal:     total,
		Total:             total,
		ChainStartTime:    ctx.BlockTime().Unix(),
		LastInflationTime: ctx.BlockTime().Unix(),
	})
	for _, pool := range pools {
		am.storage.SetPool(ctx, &pool)
	}
}
func (am AccountManager) GetPool(
	ctx sdk.Context, poolName linotypes.PoolName) (linotypes.Coin, sdk.Error) {
	pool, err := am.storage.GetPool(ctx, poolName)
	if err != nil {
		return linotypes.NewCoinFromInt64(0), err
	}
	return pool.Balance, nil
}
func (am AccountManager) MoveCoin(ctx sdk.Context, sender, receiver linotypes.AccOrAddr, coin linotypes.Coin) sdk.Error {
	if coin.IsNegative() {
		return types.ErrNegativeMoveAmount(coin)
	}
	err := am.minusCoin(ctx, sender, coin)
	if err != nil {
		return err
	}
	return am.addCoin(ctx, receiver, coin)
}
func (am AccountManager) MoveFromPool(ctx sdk.Context, poolName linotypes.PoolName, dest linotypes.AccOrAddr, amount linotypes.Coin) sdk.Error {
	if amount.IsNegative() {
		return types.ErrNegativeMoveAmount(amount)
	}
	pool, err := am.storage.GetPool(ctx, poolName)
	if err != nil {
		return err
	}
	if !pool.Balance.IsGTE(amount) {
		return types.ErrPoolNotEnough(poolName)
	}
	pool.Balance = pool.Balance.Minus(amount)
	am.storage.SetPool(ctx, pool)
	return am.addCoin(ctx, dest, amount)
}
func (am AccountManager) MoveToPool(ctx sdk.Context, poolName linotypes.PoolName, from linotypes.AccOrAddr, amount linotypes.Coin) sdk.Error {
	if amount.IsNegative() {
		return types.ErrNegativeMoveAmount(amount)
	}
	err := am.minusCoin(ctx, from, amount)
	if err != nil {
		return err
	}
	pool, err := am.storage.GetPool(ctx, poolName)
	if err != nil {
		return err
	}
	pool.Balance = pool.Balance.Plus(amount)
	am.storage.SetPool(ctx, pool)
	return nil
}
func (am AccountManager) MoveBetweenPools(ctx sdk.Context, from, to linotypes.PoolName, amount linotypes.Coin) sdk.Error {
	if amount.IsNegative() {
		return types.ErrNegativeMoveAmount(amount)
	}
	fromPool, err := am.storage.GetPool(ctx, from)
	if err != nil {
		return err
	}
	toPool, err := am.storage.GetPool(ctx, to)
	if err != nil {
		return err
	}
	if !fromPool.Balance.IsGTE(amount) {
		return types.ErrPoolNotEnough(from)
	}
	fromPool.Balance = fromPool.Balance.Minus(amount)
	toPool.Balance = toPool.Balance.Plus(amount)
	am.storage.SetPool(ctx, fromPool)
	am.storage.SetPool(ctx, toPool)
	return nil
}
func (am AccountManager) Mint(ctx sdk.Context) sdk.Error {
	supply := am.storage.GetSupply(ctx)
	chainStartTime := supply.ChainStartTime
	lastInflation := supply.LastInflationTime
	blockTime := ctx.BlockTime().Unix()
	nLastInflation := (lastInflation - chainStartTime) / nSecOfOneHour
	nCurrent := (blockTime - chainStartTime) / nSecOfOneHour
	if nCurrent <= nLastInflation {
		return nil
	}
	for nth := nLastInflation + 1; nth <= nCurrent; nth++ {
		err := am.hourlyMintOn(ctx, supply)
		if err != nil {
			return err
		}
		if nth%nHourOfOneYear == 0 {
			supply.LastYearTotal = supply.Total
		}
	}
	supply.LastInflationTime = blockTime
	am.storage.SetSupply(ctx, supply)
	return nil
}
func (am AccountManager) hourlyMintOn(ctx sdk.Context, supply *model.Supply) sdk.Error {
	allocation := am.paramHolder.GetGlobalAllocationParam(ctx)
	growthRate := allocation.GlobalGrowthRate
	minted := linotypes.DecToCoin(
		supply.LastYearTotal.ToDec().Mul(growthRate).
			Mul(linotypes.NewDecFromRat(1, nHourOfOneYear)))
	contentCreator := linotypes.DecToCoin(minted.ToDec().Mul(allocation.ContentCreatorAllocation))
	validator := linotypes.DecToCoin(minted.ToDec().Mul(allocation.ValidatorAllocation))
	developer := minted.Minus(contentCreator).Minus(validator)
	if err := am.mintToPool(ctx, linotypes.InflationConsumptionPool, contentCreator); err != nil {
		return err
	}
	if err := am.mintToPool(ctx, linotypes.InflationValidatorPool, validator); err != nil {
		return err
	}
	if err := am.mintToPool(ctx, linotypes.InflationDeveloperPool, developer); err != nil {
		return err
	}
	supply.Total = supply.Total.Plus(minted)
	return nil
}
func (am AccountManager) mintToPool(ctx sdk.Context, poolName linotypes.PoolName, amount linotypes.Coin) sdk.Error {
	pool, err := am.storage.GetPool(ctx, poolName)
	if err != nil {
		return err
	}
	pool.Balance = pool.Balance.Plus(amount)
	am.storage.SetPool(ctx, pool)
	return nil
}
func (am AccountManager) addCoin(ctx sdk.Context, dest linotypes.AccOrAddr, amount linotypes.Coin) sdk.Error {
	if !dest.IsAddr {
		return am.addCoinToUsername(ctx, dest.AccountKey, amount)
	} else {
		am.addCoinToAddress(ctx, dest.Addr, amount)
	}
	return nil
}
func (am AccountManager) addCoinToUsername(ctx sdk.Context, username linotypes.AccountKey, coin linotypes.Coin) sdk.Error {
	accInfo, err := am.storage.GetInfo(ctx, username)
	if err != nil {
		return types.ErrAccountNotFound(username)
	}
	am.addCoinToAddress(ctx, accInfo.Address, coin)
	return nil
}
func (am AccountManager) addCoinToAddress(ctx sdk.Context, addr sdk.AccAddress, coin linotypes.Coin) {
	if coin.IsZero() {
		return
	}
	bank, err := am.storage.GetBank(ctx, addr)
	if err != nil {
		bank = &model.AccountBank{
			Saving:  linotypes.NewCoinFromInt64(0),
			Pending: linotypes.NewCoinFromInt64(0),
		}
	}
	bank.Saving = bank.Saving.Plus(coin)
	am.storage.SetBank(ctx, addr, bank)
}
func (am AccountManager) minusCoin(ctx sdk.Context, from linotypes.AccOrAddr, amount linotypes.Coin) sdk.Error {
	if !from.IsAddr {
		return am.minusCoinFromUsername(ctx, from.AccountKey, amount)
	} else {
		return am.minusCoinFromAddress(ctx, from.Addr, amount)
	}
}
func (am AccountManager) minusCoinFromUsername(ctx sdk.Context, username linotypes.AccountKey, coin linotypes.Coin) sdk.Error {
	accInfo, err := am.storage.GetInfo(ctx, username)
	if err != nil {
		return types.ErrAccountNotFound(username)
	}
	return am.minusCoinFromAddress(ctx, accInfo.Address, coin)
}