cosmos-sdk/x/gov/keeper/deposit.go

203 lines
6.6 KiB
Go

package keeper
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/gov/types"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
)
// GetDeposit gets the deposit of a specific depositor on a specific proposal
func (keeper Keeper) GetDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress) (deposit v1.Deposit, found bool) {
store := ctx.KVStore(keeper.storeKey)
bz := store.Get(types.DepositKey(proposalID, depositorAddr))
if bz == nil {
return deposit, false
}
keeper.cdc.MustUnmarshal(bz, &deposit)
return deposit, true
}
// SetDeposit sets a Deposit to the gov store
func (keeper Keeper) SetDeposit(ctx sdk.Context, deposit v1.Deposit) {
store := ctx.KVStore(keeper.storeKey)
bz := keeper.cdc.MustMarshal(&deposit)
depositor := sdk.MustAccAddressFromBech32(deposit.Depositor)
store.Set(types.DepositKey(deposit.ProposalId, depositor), bz)
}
// GetAllDeposits returns all the deposits from the store
func (keeper Keeper) GetAllDeposits(ctx sdk.Context) (deposits v1.Deposits) {
keeper.IterateAllDeposits(ctx, func(deposit v1.Deposit) bool {
deposits = append(deposits, &deposit)
return false
})
return
}
// GetDeposits returns all the deposits from a proposal
func (keeper Keeper) GetDeposits(ctx sdk.Context, proposalID uint64) (deposits v1.Deposits) {
keeper.IterateDeposits(ctx, proposalID, func(deposit v1.Deposit) bool {
deposits = append(deposits, &deposit)
return false
})
return
}
// DeleteAndBurnDeposits deletes and burn all the deposits on a specific proposal.
func (keeper Keeper) DeleteAndBurnDeposits(ctx sdk.Context, proposalID uint64) {
store := ctx.KVStore(keeper.storeKey)
keeper.IterateDeposits(ctx, proposalID, func(deposit v1.Deposit) bool {
err := keeper.bankKeeper.BurnCoins(ctx, types.ModuleName, deposit.Amount)
if err != nil {
panic(err)
}
depositor := sdk.MustAccAddressFromBech32(deposit.Depositor)
store.Delete(types.DepositKey(proposalID, depositor))
return false
})
}
// IterateAllDeposits iterates over all the stored deposits and performs a callback function
func (keeper Keeper) IterateAllDeposits(ctx sdk.Context, cb func(deposit v1.Deposit) (stop bool)) {
store := ctx.KVStore(keeper.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.DepositsKeyPrefix)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var deposit v1.Deposit
keeper.cdc.MustUnmarshal(iterator.Value(), &deposit)
if cb(deposit) {
break
}
}
}
// IterateDeposits iterates over all the proposals deposits and performs a callback function
func (keeper Keeper) IterateDeposits(ctx sdk.Context, proposalID uint64, cb func(deposit v1.Deposit) (stop bool)) {
store := ctx.KVStore(keeper.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.DepositsKey(proposalID))
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var deposit v1.Deposit
keeper.cdc.MustUnmarshal(iterator.Value(), &deposit)
if cb(deposit) {
break
}
}
}
// AddDeposit adds or updates a deposit of a specific depositor on a specific proposal.
// Activates voting period when appropriate and returns true in that case, else returns false.
func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress, depositAmount sdk.Coins) (bool, error) {
// Checks to see if proposal exists
proposal, ok := keeper.GetProposal(ctx, proposalID)
if !ok {
return false, sdkerrors.Wrapf(types.ErrUnknownProposal, "%d", proposalID)
}
// Check if proposal is still depositable
if (proposal.Status != v1.StatusDepositPeriod) && (proposal.Status != v1.StatusVotingPeriod) {
return false, sdkerrors.Wrapf(types.ErrInactiveProposal, "%d", proposalID)
}
// update the governance module's account coins pool
err := keeper.bankKeeper.SendCoinsFromAccountToModule(ctx, depositorAddr, types.ModuleName, depositAmount)
if err != nil {
return false, err
}
// Update proposal
proposal.TotalDeposit = sdk.NewCoins(proposal.TotalDeposit...).Add(depositAmount...)
keeper.SetProposal(ctx, proposal)
// Check if deposit has provided sufficient total funds to transition the proposal into the voting period
activatedVotingPeriod := false
if proposal.Status == v1.StatusDepositPeriod && sdk.NewCoins(proposal.TotalDeposit...).IsAllGTE(keeper.GetParams(ctx).MinDeposit) {
keeper.ActivateVotingPeriod(ctx, proposal)
activatedVotingPeriod = true
}
// Add or update deposit object
deposit, found := keeper.GetDeposit(ctx, proposalID, depositorAddr)
if found {
deposit.Amount = sdk.NewCoins(deposit.Amount...).Add(depositAmount...)
} else {
deposit = v1.NewDeposit(proposalID, depositorAddr, depositAmount)
}
// called when deposit has been added to a proposal, however the proposal may not be active
keeper.Hooks().AfterProposalDeposit(ctx, proposalID, depositorAddr)
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeProposalDeposit,
sdk.NewAttribute(sdk.AttributeKeyAmount, depositAmount.String()),
sdk.NewAttribute(types.AttributeKeyProposalID, fmt.Sprintf("%d", proposalID)),
),
)
keeper.SetDeposit(ctx, deposit)
return activatedVotingPeriod, nil
}
// RefundAndDeleteDeposits refunds and deletes all the deposits on a specific proposal.
func (keeper Keeper) RefundAndDeleteDeposits(ctx sdk.Context, proposalID uint64) {
store := ctx.KVStore(keeper.storeKey)
keeper.IterateDeposits(ctx, proposalID, func(deposit v1.Deposit) bool {
depositor := sdk.MustAccAddressFromBech32(deposit.Depositor)
err := keeper.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, depositor, deposit.Amount)
if err != nil {
panic(err)
}
store.Delete(types.DepositKey(proposalID, depositor))
return false
})
}
// validateInitialDeposit validates if initial deposit is greater than or equal to the minimum
// required at the time of proposal submission. This threshold amount is determined by
// the deposit parameters. Returns nil on success, error otherwise.
func (keeper Keeper) validateInitialDeposit(ctx sdk.Context, initialDeposit sdk.Coins) error {
params := keeper.GetParams(ctx)
minInitialDepositRatio, err := sdk.NewDecFromStr(params.MinInitialDepositRatio)
if err != nil {
return err
}
if minInitialDepositRatio.IsZero() {
return nil
}
minDepositCoins := params.MinDeposit
for i := range minDepositCoins {
minDepositCoins[i].Amount = sdk.NewDecFromInt(minDepositCoins[i].Amount).Mul(minInitialDepositRatio).RoundInt()
}
if !initialDeposit.IsAllGTE(minDepositCoins) {
return sdkerrors.Wrapf(types.ErrMinDepositTooSmall, "was (%s), need (%s)", initialDeposit, minDepositCoins)
}
return nil
}