func (pm PostManager) IDADonate(ctx sdk.Context, from linotypes.AccountKey, n linotypes.MiniIDA, author linotypes.AccountKey, postID string, app, signer linotypes.AccountKey) sdk.Error {
	if err := pm.validateIDADonate(ctx, from, n, author, postID, app); err != nil {
		return err
	}
	signerApp, err := pm.dev.GetAffiliatingApp(ctx, signer)
	if err != nil || signerApp != app {
		return types.ErrInvalidSigner()
	}
	idaPrice, err := pm.dev.GetMiniIDAPrice(ctx, app)
	if err != nil {
		return err
	}
	rate := sdk.MustNewDecFromStr(linotypes.ConsumptionFrictionRate)
	dollarAmount := linotypes.MiniIDAToMiniDollar(n, idaPrice)  
	tax := linotypes.NewMiniDollarFromInt(dollarAmount.ToDec().Mul(rate).TruncateInt())
	taxcoins, err := pm.dev.BurnIDA(ctx, app, from, tax)
	if err != nil {
		return err
	}
	if !taxcoins.IsPositive() {
		return types.ErrDonateAmountTooLittle()
	}
	err = pm.am.MoveToPool(ctx,
		linotypes.VoteFrictionPool, linotypes.NewAccOrAddrFromAcc(from), taxcoins)
	if err != nil {
		return err
	}
	if err := pm.dev.MoveIDA(ctx, app, from, author, dollarAmount.Minus(tax)); err != nil {
		return err
	}
	return pm.afterDonation(ctx, author, postID, from, dollarAmount, taxcoins, app)
}
IDADonate - handle IDA donation.
package manager
import (
	"fmt"
	codec "github.com/cosmos/cosmos-sdk/codec"
	sdk "github.com/cosmos/cosmos-sdk/types"
	linotypes "github.com/lino-network/lino/types"
	"github.com/lino-network/lino/utils"
	acc "github.com/lino-network/lino/x/account"
	dev "github.com/lino-network/lino/x/developer"
	global "github.com/lino-network/lino/x/global"
	"github.com/lino-network/lino/x/post/model"
	"github.com/lino-network/lino/x/post/types"
	price "github.com/lino-network/lino/x/price"
	rep "github.com/lino-network/lino/x/reputation"
	vote "github.com/lino-network/lino/x/vote"
)
const (
	exportVersion = 2
	importVersion = 2
)
type PostManager struct {
	postStorage model.PostStorage
	am    acc.AccountKeeper
	gm    global.GlobalKeeper
	dev   dev.DeveloperKeeper
	rep   rep.ReputationKeeper
	vote  vote.VoteKeeper
	price price.PriceKeeper
}
func NewPostManager(key sdk.StoreKey, am acc.AccountKeeper, gm global.GlobalKeeper, dev dev.DeveloperKeeper, rep rep.ReputationKeeper, price price.PriceKeeper, vote vote.VoteKeeper) PostManager {
	return PostManager{
		postStorage: model.NewPostStorage(key),
		am:          am,
		gm:          gm,
		dev:         dev,
		rep:         rep,
		vote:        vote,
		price:       price,
	}
}
func (pm PostManager) DoesPostExist(ctx sdk.Context, permlink linotypes.Permlink) bool {
	if !pm.postStorage.HasPost(ctx, permlink) {
		return false
	}
	post, _ := pm.postStorage.GetPost(ctx, permlink)
	return !post.IsDeleted
}
func (pm PostManager) GetPost(ctx sdk.Context, permlink linotypes.Permlink) (model.Post, sdk.Error) {
	post, err := pm.postStorage.GetPost(ctx, permlink)
	if err != nil {
		return model.Post{}, err
	}
	if post.IsDeleted {
		return model.Post{}, types.ErrPostDeleted(permlink)
	}
	return *post, nil
}
func (pm PostManager) CreatePost(ctx sdk.Context, author linotypes.AccountKey, postID string, createdBy linotypes.AccountKey, content string, title string) sdk.Error {
	if !pm.am.DoesAccountExist(ctx, author) {
		return types.ErrAccountNotFound(author)
	}
	if !pm.am.DoesAccountExist(ctx, createdBy) {
		return types.ErrAccountNotFound(createdBy)
	}
	permlink := linotypes.GetPermlink(author, postID)
	if pm.postStorage.HasPost(ctx, permlink) {
		return types.ErrPostAlreadyExist(permlink)
	}
	if author != createdBy {
		dev := createdBy
		var err error
		createdBy, err = pm.dev.GetAffiliatingApp(ctx, createdBy)
		if err != nil {
			return types.ErrDeveloperNotFound(dev)
		}
	}
	createdAt := ctx.BlockHeader().Time.Unix()
	postInfo := &model.Post{
		PostID:    postID,
		Title:     title,
		Content:   content,
		Author:    author,
		CreatedBy: createdBy,
		CreatedAt: createdAt,
		UpdatedAt: createdAt,
	}
	pm.postStorage.SetPost(ctx, postInfo)
	return nil
}
func (pm PostManager) UpdatePost(ctx sdk.Context, author linotypes.AccountKey, postID, title, content string) sdk.Error {
	permlink := linotypes.GetPermlink(author, postID)
	postInfo, err := pm.postStorage.GetPost(ctx, permlink)
	if err != nil {
		return err
	}
	postInfo.Title = title
	postInfo.Content = content
	postInfo.UpdatedAt = ctx.BlockHeader().Time.Unix()
	pm.postStorage.SetPost(ctx, postInfo)
	return nil
}
func (pm PostManager) DeletePost(ctx sdk.Context, permlink linotypes.Permlink) sdk.Error {
	post, err := pm.postStorage.GetPost(ctx, permlink)
	if err != nil {
		return err
	}
	if post.IsDeleted {
		return types.ErrPostDeleted(permlink)
	}
	post.IsDeleted = true
	post.Title = ""
	post.Content = ""
	pm.postStorage.SetPost(ctx, post)
	return nil
}
func (pm PostManager) LinoDonate(ctx sdk.Context, from linotypes.AccountKey, amount linotypes.Coin, author linotypes.AccountKey, postID string, app linotypes.AccountKey) sdk.Error {
	if err := pm.validateLinoDonation(ctx, from, amount, author, postID, app); err != nil {
		return err
	}
	rate := sdk.MustNewDecFromStr(linotypes.ConsumptionFrictionRate)
	frictionCoin := linotypes.DecToCoin(amount.ToDec().Mul(rate))
	if frictionCoin.IsZero() {
		return types.ErrDonateAmountTooLittle()
	}
	err := pm.am.MoveToPool(ctx,
		linotypes.VoteFrictionPool, linotypes.NewAccOrAddrFromAcc(from), frictionCoin)
	if err != nil {
		return err
	}
	err = pm.am.MoveCoin(ctx, linotypes.NewAccOrAddrFromAcc(from),
		linotypes.NewAccOrAddrFromAcc(author), amount.Minus(frictionCoin))
	if err != nil {
		return err
	}
	mdamount, err := pm.price.CoinToMiniDollar(ctx, amount)
	if err != nil {
		return err
	}
	return pm.afterDonation(ctx, author, postID, from, mdamount, frictionCoin, app)
}