feat(x/accounts): Add new lockup account type (#19048)
Co-authored-by: testinginprod <frojdi@faulttolerance.net> Co-authored-by: Facundo Medica <14063057+facundomedica@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
testinginprod
Facundo Medica
coderabbitai[bot]
parent
5424b55c57
commit
3ce9224f00
@@ -0,0 +1,5 @@
|
||||
# x/accounts/lockup
|
||||
|
||||
<!--- TODO: need to expand more on this --->
|
||||
|
||||
The x/accounts/lockup module provides the implementation for lockup accounts within the x/accounts module.
|
||||
@@ -0,0 +1,225 @@
|
||||
package lockup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"cosmossdk.io/collections"
|
||||
collcodec "cosmossdk.io/collections/codec"
|
||||
"cosmossdk.io/math"
|
||||
"cosmossdk.io/x/accounts/accountstd"
|
||||
lockuptypes "cosmossdk.io/x/accounts/lockup/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
// Compile-time type assertions
|
||||
var (
|
||||
_ accountstd.Interface = (*ContinuousLockingAccount)(nil)
|
||||
)
|
||||
|
||||
// NewContinuousLockingAccount creates a new ContinuousLockingAccount object.
|
||||
func NewContinuousLockingAccount(d accountstd.Dependencies) (*ContinuousLockingAccount, error) {
|
||||
baseLockup := newBaseLockup(d)
|
||||
|
||||
ContinuousLockingAccount := ContinuousLockingAccount{
|
||||
BaseLockup: baseLockup,
|
||||
StartTime: collections.NewItem(d.SchemaBuilder, StartTimePrefix, "start_time", collcodec.KeyToValueCodec[time.Time](sdk.TimeKey)),
|
||||
}
|
||||
|
||||
return &ContinuousLockingAccount, nil
|
||||
}
|
||||
|
||||
type ContinuousLockingAccount struct {
|
||||
*BaseLockup
|
||||
StartTime collections.Item[time.Time]
|
||||
}
|
||||
|
||||
func (cva ContinuousLockingAccount) Init(ctx context.Context, msg *lockuptypes.MsgInitLockupAccount) (*lockuptypes.MsgInitLockupAccountResponse, error) {
|
||||
if msg.EndTime.IsZero() {
|
||||
return nil, sdkerrors.ErrInvalidRequest.Wrapf("invalid end time %s", msg.EndTime.String())
|
||||
}
|
||||
|
||||
if msg.EndTime.Before(msg.StartTime) {
|
||||
return nil, sdkerrors.ErrInvalidRequest.Wrap("invalid start and end time (must be start before end)")
|
||||
}
|
||||
|
||||
hs := cva.headerService.GetHeaderInfo(ctx)
|
||||
|
||||
start := msg.StartTime
|
||||
if msg.StartTime.IsZero() {
|
||||
start = hs.Time
|
||||
}
|
||||
|
||||
err := cva.StartTime.Set(ctx, start)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cva.BaseLockup.Init(ctx, msg)
|
||||
}
|
||||
|
||||
func (cva *ContinuousLockingAccount) Delegate(ctx context.Context, msg *lockuptypes.MsgDelegate) (
|
||||
*lockuptypes.MsgExecuteMessagesResponse, error,
|
||||
) {
|
||||
return cva.BaseLockup.Delegate(ctx, msg, cva.GetLockedCoinsWithDenoms)
|
||||
}
|
||||
|
||||
func (cva *ContinuousLockingAccount) Undelegate(ctx context.Context, msg *lockuptypes.MsgUndelegate) (
|
||||
*lockuptypes.MsgExecuteMessagesResponse, error,
|
||||
) {
|
||||
return cva.BaseLockup.Undelegate(ctx, msg)
|
||||
}
|
||||
|
||||
func (cva *ContinuousLockingAccount) SendCoins(ctx context.Context, msg *lockuptypes.MsgSend) (
|
||||
*lockuptypes.MsgExecuteMessagesResponse, error,
|
||||
) {
|
||||
return cva.BaseLockup.SendCoins(ctx, msg, cva.GetLockedCoinsWithDenoms)
|
||||
}
|
||||
|
||||
func (cva *ContinuousLockingAccount) WithdrawUnlockedCoins(ctx context.Context, msg *lockuptypes.MsgWithdraw) (
|
||||
*lockuptypes.MsgWithdrawResponse, error,
|
||||
) {
|
||||
return cva.BaseLockup.WithdrawUnlockedCoins(ctx, msg, cva.GetLockedCoinsWithDenoms)
|
||||
}
|
||||
|
||||
// GetLockCoinsInfo returns the total number of unlocked and locked coins.
|
||||
func (cva ContinuousLockingAccount) GetLockCoinsInfo(ctx context.Context, blockTime time.Time) (unlockedCoins, lockedCoins sdk.Coins, err error) {
|
||||
unlockedCoins = sdk.Coins{}
|
||||
lockedCoins = sdk.Coins{}
|
||||
|
||||
// We must handle the case where the start time for a lockup account has
|
||||
// been set into the future or when the start of the chain is not exactly
|
||||
// known.
|
||||
startTime, err := cva.StartTime.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
endTime, err := cva.EndTime.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var originalVesting sdk.Coins
|
||||
err = cva.IterateCoinEntries(ctx, cva.OriginalLocking, func(key string, value math.Int) (stop bool, err error) {
|
||||
originalVesting = append(originalVesting, sdk.NewCoin(key, value))
|
||||
vestedCoin, vestingCoin, err := cva.GetLockCoinInfoWithDenom(ctx, blockTime, key)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
unlockedCoins = append(unlockedCoins, *vestedCoin)
|
||||
lockedCoins = append(lockedCoins, *vestingCoin)
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if startTime.After(blockTime) {
|
||||
return unlockedCoins, originalVesting, nil
|
||||
} else if endTime.Before(blockTime) {
|
||||
return originalVesting, lockedCoins, nil
|
||||
}
|
||||
|
||||
return unlockedCoins, lockedCoins, nil
|
||||
}
|
||||
|
||||
// GetLockCoinInfoWithDenom returns the number of locked coin for a specific denom. If no coins are locked,
|
||||
// nil is returned.
|
||||
func (cva ContinuousLockingAccount) GetLockCoinInfoWithDenom(ctx context.Context, blockTime time.Time, denom string) (unlockedCoin, lockedCoin *sdk.Coin, err error) {
|
||||
// We must handle the case where the start time for a lockup account has
|
||||
// been set into the future or when the start of the chain is not exactly
|
||||
// known.
|
||||
startTime, err := cva.StartTime.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
endTime, err := cva.EndTime.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
originalLockingAmt, err := cva.OriginalLocking.Get(ctx, denom)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
originalLocking := sdk.NewCoin(denom, originalLockingAmt)
|
||||
if startTime.After(blockTime) {
|
||||
return &sdk.Coin{}, &originalLocking, nil
|
||||
} else if endTime.Before(blockTime) {
|
||||
return &originalLocking, &sdk.Coin{}, nil
|
||||
}
|
||||
|
||||
// calculate the locking scalar
|
||||
x := blockTime.Unix() - startTime.Unix()
|
||||
y := endTime.Unix() - startTime.Unix()
|
||||
s := math.LegacyNewDec(x).Quo(math.LegacyNewDec(y))
|
||||
|
||||
unlockedAmt := math.LegacyNewDecFromInt(originalLocking.Amount).Mul(s).RoundInt()
|
||||
unlocked := sdk.NewCoin(originalLocking.Denom, unlockedAmt)
|
||||
|
||||
locked := originalLocking.Sub(unlocked)
|
||||
|
||||
return &unlocked, &locked, nil
|
||||
}
|
||||
|
||||
// GetLockedCoins returns the total number of locked coins.
|
||||
func (cva ContinuousLockingAccount) GetLockedCoins(ctx context.Context, blockTime time.Time) (sdk.Coins, error) {
|
||||
_, lockedCoins, err := cva.GetLockCoinsInfo(ctx, blockTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return lockedCoins, nil
|
||||
}
|
||||
|
||||
// GetLockedCoinsWithDenoms returns the number of locked coin for a specific denom.
|
||||
func (cva ContinuousLockingAccount) GetLockedCoinsWithDenoms(ctx context.Context, blockTime time.Time, denoms ...string) (sdk.Coins, error) {
|
||||
lockedCoins := sdk.Coins{}
|
||||
for _, denom := range denoms {
|
||||
_, lockedCoin, err := cva.GetLockCoinInfoWithDenom(ctx, blockTime, denom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lockedCoins = append(lockedCoins, *lockedCoin)
|
||||
}
|
||||
|
||||
return lockedCoins, nil
|
||||
}
|
||||
|
||||
func (cva ContinuousLockingAccount) QueryLockupAccountInfo(ctx context.Context, req *lockuptypes.QueryLockupAccountInfoRequest) (
|
||||
*lockuptypes.QueryLockupAccountInfoResponse, error,
|
||||
) {
|
||||
resp, err := cva.BaseLockup.QueryLockupAccountBaseInfo(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
startTime, err := cva.StartTime.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hs := cva.headerService.GetHeaderInfo(ctx)
|
||||
unlockedCoins, lockedCoins, err := cva.GetLockCoinsInfo(ctx, hs.Time)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.StartTime = &startTime
|
||||
resp.LockedCoins = lockedCoins
|
||||
resp.UnlockedCoins = unlockedCoins
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Implement smart account interface
|
||||
func (cva ContinuousLockingAccount) RegisterInitHandler(builder *accountstd.InitBuilder) {
|
||||
accountstd.RegisterInitHandler(builder, cva.Init)
|
||||
}
|
||||
|
||||
func (cva ContinuousLockingAccount) RegisterExecuteHandlers(builder *accountstd.ExecuteBuilder) {
|
||||
accountstd.RegisterExecuteHandler(builder, cva.Delegate)
|
||||
accountstd.RegisterExecuteHandler(builder, cva.Undelegate)
|
||||
accountstd.RegisterExecuteHandler(builder, cva.SendCoins)
|
||||
accountstd.RegisterExecuteHandler(builder, cva.WithdrawUnlockedCoins)
|
||||
}
|
||||
|
||||
func (cva ContinuousLockingAccount) RegisterQueryHandlers(builder *accountstd.QueryBuilder) {
|
||||
accountstd.RegisterQueryHandler(builder, cva.QueryLockupAccountInfo)
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package lockup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
"cosmossdk.io/x/accounts/accountstd"
|
||||
lockuptypes "cosmossdk.io/x/accounts/lockup/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
// Compile-time type assertions
|
||||
var (
|
||||
_ accountstd.Interface = (*DelayedLockingAccount)(nil)
|
||||
)
|
||||
|
||||
// NewDelayedLockingAccount creates a new DelayedLockingAccount object.
|
||||
func NewDelayedLockingAccount(d accountstd.Dependencies) (*DelayedLockingAccount, error) {
|
||||
baseLockup := newBaseLockup(d)
|
||||
return &DelayedLockingAccount{
|
||||
baseLockup,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type DelayedLockingAccount struct {
|
||||
*BaseLockup
|
||||
}
|
||||
|
||||
func (dva DelayedLockingAccount) Init(ctx context.Context, msg *lockuptypes.MsgInitLockupAccount) (*lockuptypes.MsgInitLockupAccountResponse, error) {
|
||||
if msg.EndTime.IsZero() {
|
||||
return nil, sdkerrors.ErrInvalidRequest.Wrapf("invalid end time %s", msg.EndTime.String())
|
||||
}
|
||||
|
||||
return dva.BaseLockup.Init(ctx, msg)
|
||||
}
|
||||
|
||||
func (dva *DelayedLockingAccount) Delegate(ctx context.Context, msg *lockuptypes.MsgDelegate) (
|
||||
*lockuptypes.MsgExecuteMessagesResponse, error,
|
||||
) {
|
||||
return dva.BaseLockup.Delegate(ctx, msg, dva.GetLockedCoinsWithDenoms)
|
||||
}
|
||||
|
||||
func (dva *DelayedLockingAccount) Undelegate(ctx context.Context, msg *lockuptypes.MsgUndelegate) (
|
||||
*lockuptypes.MsgExecuteMessagesResponse, error,
|
||||
) {
|
||||
return dva.BaseLockup.Undelegate(ctx, msg)
|
||||
}
|
||||
|
||||
func (dva *DelayedLockingAccount) SendCoins(ctx context.Context, msg *lockuptypes.MsgSend) (
|
||||
*lockuptypes.MsgExecuteMessagesResponse, error,
|
||||
) {
|
||||
return dva.BaseLockup.SendCoins(ctx, msg, dva.GetLockedCoinsWithDenoms)
|
||||
}
|
||||
|
||||
func (dva *DelayedLockingAccount) WithdrawUnlockedCoins(ctx context.Context, msg *lockuptypes.MsgWithdraw) (
|
||||
*lockuptypes.MsgWithdrawResponse, error,
|
||||
) {
|
||||
return dva.BaseLockup.WithdrawUnlockedCoins(ctx, msg, dva.GetLockedCoinsWithDenoms)
|
||||
}
|
||||
|
||||
// GetLockCoinsInfo returns the total number of unlocked and locked coins.
|
||||
func (dva DelayedLockingAccount) GetLockCoinsInfo(ctx context.Context, blockTime time.Time) (sdk.Coins, sdk.Coins, error) {
|
||||
endTime, err := dva.EndTime.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
originalLocking := sdk.Coins{}
|
||||
err = dva.IterateCoinEntries(ctx, dva.OriginalLocking, func(key string, value math.Int) (stop bool, err error) {
|
||||
originalLocking = append(originalLocking, sdk.NewCoin(key, value))
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if blockTime.After(endTime) {
|
||||
return originalLocking, sdk.Coins{}, nil
|
||||
}
|
||||
|
||||
return sdk.Coins{}, originalLocking, nil
|
||||
}
|
||||
|
||||
// GetLockedCoins returns the total number of locked coins. If no coins are
|
||||
// locked, nil is returned.
|
||||
func (dva DelayedLockingAccount) GetLockedCoins(ctx context.Context, blockTime time.Time) (sdk.Coins, error) {
|
||||
_, lockedCoins, err := dva.GetLockCoinsInfo(ctx, blockTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return lockedCoins, nil
|
||||
}
|
||||
|
||||
// GetLockCoinInfoWithDenom returns the number of unlocked and locked coin for a specific denom.
|
||||
func (dva DelayedLockingAccount) GetLockCoinInfoWithDenom(ctx context.Context, blockTime time.Time, denom string) (*sdk.Coin, *sdk.Coin, error) {
|
||||
endTime, err := dva.EndTime.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
originalLockingAmt, err := dva.OriginalLocking.Get(ctx, denom)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
originalLockingCoin := sdk.NewCoin(denom, originalLockingAmt)
|
||||
|
||||
if blockTime.After(endTime) {
|
||||
return &originalLockingCoin, &sdk.Coin{}, nil
|
||||
}
|
||||
|
||||
return &sdk.Coin{}, &originalLockingCoin, nil
|
||||
}
|
||||
|
||||
// GetLockedCoinsWithDenoms returns the number of locked coin for a specific denom.
|
||||
func (dva DelayedLockingAccount) GetLockedCoinsWithDenoms(ctx context.Context, blockTime time.Time, denoms ...string) (sdk.Coins, error) {
|
||||
vestingCoins := sdk.Coins{}
|
||||
for _, denom := range denoms {
|
||||
_, vestingCoin, err := dva.GetLockCoinInfoWithDenom(ctx, blockTime, denom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vestingCoins = append(vestingCoins, *vestingCoin)
|
||||
}
|
||||
return vestingCoins, nil
|
||||
}
|
||||
|
||||
func (dva DelayedLockingAccount) QueryVestingAccountInfo(ctx context.Context, req *lockuptypes.QueryLockupAccountInfoRequest) (
|
||||
*lockuptypes.QueryLockupAccountInfoResponse, error,
|
||||
) {
|
||||
resp, err := dva.BaseLockup.QueryLockupAccountBaseInfo(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hs := dva.headerService.GetHeaderInfo(ctx)
|
||||
unlockedCoins, lockedCoins, err := dva.GetLockCoinsInfo(ctx, hs.Time)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.LockedCoins = lockedCoins
|
||||
resp.UnlockedCoins = unlockedCoins
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Implement smart account interface
|
||||
func (dva DelayedLockingAccount) RegisterInitHandler(builder *accountstd.InitBuilder) {
|
||||
accountstd.RegisterInitHandler(builder, dva.Init)
|
||||
}
|
||||
|
||||
func (dva DelayedLockingAccount) RegisterExecuteHandlers(builder *accountstd.ExecuteBuilder) {
|
||||
accountstd.RegisterExecuteHandler(builder, dva.Delegate)
|
||||
accountstd.RegisterExecuteHandler(builder, dva.Undelegate)
|
||||
accountstd.RegisterExecuteHandler(builder, dva.SendCoins)
|
||||
accountstd.RegisterExecuteHandler(builder, dva.WithdrawUnlockedCoins)
|
||||
}
|
||||
|
||||
func (dva DelayedLockingAccount) RegisterQueryHandlers(builder *accountstd.QueryBuilder) {
|
||||
accountstd.RegisterQueryHandler(builder, dva.QueryVestingAccountInfo)
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
module cosmossdk.io/x/accounts/lockup
|
||||
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
cosmossdk.io/collections v0.4.0
|
||||
cosmossdk.io/core v0.12.1-0.20231114100755-569e3ff6a0d7
|
||||
cosmossdk.io/x/accounts v0.0.0-20240226161501-23359a0b6d91
|
||||
cosmossdk.io/x/bank v0.0.0-20240226161501-23359a0b6d91
|
||||
github.com/cosmos/cosmos-sdk v0.51.0
|
||||
github.com/cosmos/gogoproto v1.4.11
|
||||
)
|
||||
|
||||
require (
|
||||
cosmossdk.io/api v0.7.3
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/spf13/cobra v1.8.0 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
|
||||
google.golang.org/grpc v1.62.1 // indirect
|
||||
google.golang.org/protobuf v1.33.0
|
||||
)
|
||||
|
||||
require (
|
||||
cosmossdk.io/errors v1.0.1
|
||||
cosmossdk.io/log v1.3.1 // indirect
|
||||
cosmossdk.io/math v1.3.0
|
||||
cosmossdk.io/store v1.0.2 // indirect
|
||||
cosmossdk.io/x/tx v0.13.1 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
|
||||
github.com/99designs/keyring v1.2.2 // indirect
|
||||
github.com/DataDog/datadog-go v4.8.3+incompatible // indirect
|
||||
github.com/DataDog/zstd v1.5.5 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cockroachdb/errors v1.11.1 // indirect
|
||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
|
||||
github.com/cockroachdb/pebble v1.1.0 // indirect
|
||||
github.com/cockroachdb/redact v1.1.5 // indirect
|
||||
github.com/cometbft/cometbft v0.38.5 // indirect
|
||||
github.com/cometbft/cometbft-db v0.11.0 // indirect
|
||||
github.com/cosmos/btcutil v1.0.5 // indirect
|
||||
github.com/cosmos/cosmos-db v1.0.2 // indirect
|
||||
github.com/cosmos/cosmos-proto v1.0.0-beta.4
|
||||
github.com/cosmos/go-bip39 v1.0.0 // indirect
|
||||
github.com/cosmos/gogogateway v1.2.0 // indirect
|
||||
github.com/cosmos/iavl v1.0.1 // indirect
|
||||
github.com/cosmos/ics23/go v0.10.0 // indirect
|
||||
github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect
|
||||
github.com/danieljoos/wincred v1.2.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/dvsekhvalnov/jose2go v1.6.0 // indirect
|
||||
github.com/emicklei/dot v1.6.1 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/getsentry/sentry-go v0.27.0 // indirect
|
||||
github.com/go-kit/kit v0.13.0 // indirect
|
||||
github.com/go-kit/log v0.2.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
|
||||
github.com/gogo/googleapis v1.4.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/glog v1.2.0 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/gorilla/handlers v1.5.2 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.1 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
|
||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.2 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
github.com/hashicorp/go-metrics v0.5.3 // indirect
|
||||
github.com/hashicorp/go-plugin v1.6.0 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
|
||||
github.com/huandu/skiplist v1.2.0 // indirect
|
||||
github.com/iancoleman/strcase v0.3.0 // indirect
|
||||
github.com/improbable-eng/grpc-web v0.15.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jmhodges/levigo v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.17.7 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
||||
github.com/linxGnu/grocksdb v1.8.12 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mtibben/percent v0.2.1 // indirect
|
||||
github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.19.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.0 // indirect
|
||||
github.com/prometheus/common v0.50.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
github.com/rs/cors v1.10.1 // indirect
|
||||
github.com/rs/zerolog v1.32.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sasha-s/go-deadlock v0.3.1 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.18.2 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
||||
github.com/tendermint/go-amino v0.16.0 // indirect
|
||||
github.com/tidwall/btree v1.7.0 // indirect
|
||||
github.com/zondax/hid v0.9.2 // indirect
|
||||
github.com/zondax/ledger-go v0.14.3 // indirect
|
||||
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect
|
||||
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect
|
||||
go.etcd.io/bbolt v1.3.9 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.21.0 // indirect
|
||||
golang.org/x/mod v0.15.0 // indirect
|
||||
golang.org/x/net v0.22.0 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/term v0.18.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.18.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240221002015-b0ce06bbee7c // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gotest.tools/v3 v3.5.1 // indirect
|
||||
nhooyr.io/websocket v1.8.10 // indirect
|
||||
pgregory.net/rapid v1.1.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.32.0-20240130113600-88ef6483f90f.1 // indirect
|
||||
buf.build/gen/go/tendermint/tendermint/protocolbuffers/go v1.32.0-20231117195010-33ed361a9051.1 // indirect
|
||||
cosmossdk.io/x/auth v0.0.0-00010101000000-000000000000 // indirect
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
|
||||
)
|
||||
|
||||
replace github.com/cosmos/cosmos-sdk => ../../../../.
|
||||
|
||||
replace (
|
||||
cosmossdk.io/api => ../../../../api
|
||||
cosmossdk.io/core => ../../../../core
|
||||
cosmossdk.io/depinject => ../../../../depinject
|
||||
cosmossdk.io/x/accounts => ../../.
|
||||
cosmossdk.io/x/auth => ../../../auth
|
||||
cosmossdk.io/x/bank => ../../../bank
|
||||
cosmossdk.io/x/distribution => ../../../distribution
|
||||
cosmossdk.io/x/gov => ../../../gov
|
||||
cosmossdk.io/x/mint => ../../../mint
|
||||
cosmossdk.io/x/protocolpool => ../../../protocolpool
|
||||
cosmossdk.io/x/slashing => ../../../slashing
|
||||
cosmossdk.io/x/staking => ../../../staking
|
||||
github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.9.1
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,587 @@
|
||||
package lockup
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"cosmossdk.io/collections"
|
||||
collcodec "cosmossdk.io/collections/codec"
|
||||
"cosmossdk.io/core/address"
|
||||
"cosmossdk.io/core/header"
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
"cosmossdk.io/math"
|
||||
"cosmossdk.io/x/accounts/accountstd"
|
||||
lockuptypes "cosmossdk.io/x/accounts/lockup/types"
|
||||
banktypes "cosmossdk.io/x/bank/types"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
"github.com/cosmos/gogoproto/proto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
OriginalLockingPrefix = collections.NewPrefix(0)
|
||||
DelegatedFreePrefix = collections.NewPrefix(1)
|
||||
DelegatedLockingPrefix = collections.NewPrefix(2)
|
||||
EndTimePrefix = collections.NewPrefix(3)
|
||||
StartTimePrefix = collections.NewPrefix(4)
|
||||
LockingPeriodsPrefix = collections.NewPrefix(5)
|
||||
OwnerPrefix = collections.NewPrefix(6)
|
||||
WithdrawedCoinsPrefix = collections.NewPrefix(7)
|
||||
)
|
||||
|
||||
var (
|
||||
CONTINUOUS_LOCKING_ACCOUNT = "continuous-locking-account"
|
||||
DELAYED_LOCKING_ACCOUNT = "delayed-locking-account"
|
||||
PERIODIC_LOCKING_ACCOUNT = "periodic-locking-account"
|
||||
PERMANENT_LOCKING_ACCOUNT = "permanent-locking-account"
|
||||
)
|
||||
|
||||
type getLockedCoinsFunc = func(ctx context.Context, time time.Time, denoms ...string) (sdk.Coins, error)
|
||||
|
||||
// newBaseLockup creates a new BaseLockup object.
|
||||
func newBaseLockup(d accountstd.Dependencies) *BaseLockup {
|
||||
BaseLockup := &BaseLockup{
|
||||
Owner: collections.NewItem(d.SchemaBuilder, OwnerPrefix, "owner", collections.BytesValue),
|
||||
OriginalLocking: collections.NewMap(d.SchemaBuilder, OriginalLockingPrefix, "original_locking", collections.StringKey, sdk.IntValue),
|
||||
DelegatedFree: collections.NewMap(d.SchemaBuilder, DelegatedFreePrefix, "delegated_free", collections.StringKey, sdk.IntValue),
|
||||
DelegatedLocking: collections.NewMap(d.SchemaBuilder, DelegatedLockingPrefix, "delegated_locking", collections.StringKey, sdk.IntValue),
|
||||
WithdrawedCoins: collections.NewMap(d.SchemaBuilder, WithdrawedCoinsPrefix, "withdrawed_coins", collections.StringKey, sdk.IntValue),
|
||||
addressCodec: d.AddressCodec,
|
||||
headerService: d.Environment.HeaderService,
|
||||
EndTime: collections.NewItem(d.SchemaBuilder, EndTimePrefix, "end_time", collcodec.KeyToValueCodec[time.Time](sdk.TimeKey)),
|
||||
}
|
||||
|
||||
return BaseLockup
|
||||
}
|
||||
|
||||
type BaseLockup struct {
|
||||
// Owner is the address of the account owner.
|
||||
Owner collections.Item[[]byte]
|
||||
OriginalLocking collections.Map[string, math.Int]
|
||||
DelegatedFree collections.Map[string, math.Int]
|
||||
DelegatedLocking collections.Map[string, math.Int]
|
||||
WithdrawedCoins collections.Map[string, math.Int]
|
||||
addressCodec address.Codec
|
||||
headerService header.Service
|
||||
// lockup end time.
|
||||
EndTime collections.Item[time.Time]
|
||||
}
|
||||
|
||||
func (bva *BaseLockup) Init(ctx context.Context, msg *lockuptypes.MsgInitLockupAccount) (
|
||||
*lockuptypes.MsgInitLockupAccountResponse, error,
|
||||
) {
|
||||
owner, err := bva.addressCodec.StringToBytes(msg.Owner)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'owner' address: %s", err)
|
||||
}
|
||||
err = bva.Owner.Set(ctx, owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
funds := accountstd.Funds(ctx)
|
||||
|
||||
sortedAmt := funds.Sort()
|
||||
for _, coin := range sortedAmt {
|
||||
err = bva.OriginalLocking.Set(ctx, coin.Denom, coin.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set initial value for all locked token
|
||||
err = bva.WithdrawedCoins.Set(ctx, coin.Denom, math.ZeroInt())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set initial value for all locked token
|
||||
err = bva.DelegatedFree.Set(ctx, coin.Denom, math.ZeroInt())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set initial value for all locked token
|
||||
err = bva.DelegatedLocking.Set(ctx, coin.Denom, math.ZeroInt())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
err = bva.EndTime.Set(ctx, msg.EndTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &lockuptypes.MsgInitLockupAccountResponse{}, nil
|
||||
}
|
||||
|
||||
func (bva *BaseLockup) Delegate(
|
||||
ctx context.Context, msg *lockuptypes.MsgDelegate, getLockedCoinsFunc getLockedCoinsFunc,
|
||||
) (
|
||||
*lockuptypes.MsgExecuteMessagesResponse, error,
|
||||
) {
|
||||
err := bva.checkSender(ctx, msg.Sender)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
whoami := accountstd.Whoami(ctx)
|
||||
delegatorAddress, err := bva.addressCodec.BytesToString(whoami)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hs := bva.headerService.GetHeaderInfo(ctx)
|
||||
|
||||
balance, err := bva.getBalance(ctx, delegatorAddress, msg.Amount.Denom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lockedCoins, err := getLockedCoinsFunc(ctx, hs.Time, msg.Amount.Denom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = bva.TrackDelegation(
|
||||
ctx,
|
||||
sdk.Coins{*balance},
|
||||
lockedCoins,
|
||||
sdk.Coins{msg.Amount},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msgDelegate := makeMsgDelegate(delegatorAddress, msg.ValidatorAddress, msg.Amount)
|
||||
responses, err := sendMessage(ctx, msgDelegate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &lockuptypes.MsgExecuteMessagesResponse{Responses: responses}, nil
|
||||
}
|
||||
|
||||
func (bva *BaseLockup) Undelegate(
|
||||
ctx context.Context, msg *lockuptypes.MsgUndelegate,
|
||||
) (
|
||||
*lockuptypes.MsgExecuteMessagesResponse, error,
|
||||
) {
|
||||
err := bva.checkSender(ctx, msg.Sender)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
whoami := accountstd.Whoami(ctx)
|
||||
delegatorAddress, err := bva.addressCodec.BytesToString(whoami)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = bva.TrackUndelegation(ctx, sdk.Coins{msg.Amount})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msgUndelegate := makeMsgUndelegate(delegatorAddress, msg.ValidatorAddress, msg.Amount)
|
||||
responses, err := sendMessage(ctx, msgUndelegate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &lockuptypes.MsgExecuteMessagesResponse{Responses: responses}, nil
|
||||
}
|
||||
|
||||
func (bva *BaseLockup) SendCoins(
|
||||
ctx context.Context, msg *lockuptypes.MsgSend, getLockedCoinsFunc getLockedCoinsFunc,
|
||||
) (
|
||||
*lockuptypes.MsgExecuteMessagesResponse, error,
|
||||
) {
|
||||
err := bva.checkSender(ctx, msg.Sender)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
whoami := accountstd.Whoami(ctx)
|
||||
fromAddress, err := bva.addressCodec.BytesToString(whoami)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hs := bva.headerService.GetHeaderInfo(ctx)
|
||||
|
||||
lockedCoins, err := getLockedCoinsFunc(ctx, hs.Time, msg.Amount.Denoms()...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = bva.checkTokensSendable(ctx, fromAddress, msg.Amount, lockedCoins)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msgSend := makeMsgSend(fromAddress, msg.ToAddress, msg.Amount)
|
||||
responses, err := sendMessage(ctx, msgSend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &lockuptypes.MsgExecuteMessagesResponse{Responses: responses}, nil
|
||||
}
|
||||
|
||||
// WithdrawUnlockedCoins allow owner to withdraw the unlocked token for a specific denoms to an
|
||||
// account of choice. Update the withdrawed token tracking for lockup account
|
||||
func (bva *BaseLockup) WithdrawUnlockedCoins(
|
||||
ctx context.Context, msg *lockuptypes.MsgWithdraw, getLockedCoinsFunc getLockedCoinsFunc,
|
||||
) (
|
||||
*lockuptypes.MsgWithdrawResponse, error,
|
||||
) {
|
||||
err := bva.checkSender(ctx, msg.Withdrawer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
whoami := accountstd.Whoami(ctx)
|
||||
fromAddress, err := bva.addressCodec.BytesToString(whoami)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hs := bva.headerService.GetHeaderInfo(ctx)
|
||||
lockedCoins, err := getLockedCoinsFunc(ctx, hs.Time, msg.Denoms...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
amount := sdk.Coins{}
|
||||
for _, denom := range msg.Denoms {
|
||||
balance, err := bva.getBalance(ctx, fromAddress, denom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lockedAmt := lockedCoins.AmountOf(denom)
|
||||
|
||||
// get lockedCoin from that are not bonded for the sent denom
|
||||
notBondedLockedCoin, err := bva.GetNotBondedLockedCoin(ctx, sdk.NewCoin(denom, lockedAmt), denom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
spendable, err := balance.SafeSub(notBondedLockedCoin)
|
||||
if err != nil {
|
||||
return nil, errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds,
|
||||
"locked amount exceeds account balance funds: %s > %s", notBondedLockedCoin, balance)
|
||||
}
|
||||
|
||||
withdrawedAmt, err := bva.WithdrawedCoins.Get(ctx, denom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
originalLockingAmt, err := bva.OriginalLocking.Get(ctx, denom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// withdrawable amount is equal to original locking amount subtract already withdrawed amount
|
||||
withdrawableAmt, err := originalLockingAmt.SafeSub(withdrawedAmt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
withdrawAmt := math.MinInt(withdrawableAmt, spendable.Amount)
|
||||
// if zero amount go to the next iteration
|
||||
if withdrawAmt.IsZero() {
|
||||
continue
|
||||
}
|
||||
amount = append(amount, sdk.NewCoin(denom, withdrawAmt))
|
||||
|
||||
// update the withdrawed amount
|
||||
err = bva.WithdrawedCoins.Set(ctx, denom, withdrawedAmt.Add(withdrawAmt))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(amount) == 0 {
|
||||
return nil, fmt.Errorf("no tokens available for withdrawing")
|
||||
}
|
||||
|
||||
msgSend := makeMsgSend(fromAddress, msg.ToAddress, amount)
|
||||
_, err = sendMessage(ctx, msgSend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &lockuptypes.MsgWithdrawResponse{
|
||||
Reciever: msg.ToAddress,
|
||||
AmountReceived: amount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (bva *BaseLockup) checkSender(ctx context.Context, sender string) error {
|
||||
owner, err := bva.Owner.Get(ctx)
|
||||
if err != nil {
|
||||
return sdkerrors.ErrInvalidAddress.Wrapf("invalid owner address: %s", err.Error())
|
||||
}
|
||||
senderBytes, err := bva.addressCodec.StringToBytes(sender)
|
||||
if err != nil {
|
||||
return sdkerrors.ErrInvalidAddress.Wrapf("invalid sender address: %s", err.Error())
|
||||
}
|
||||
if !bytes.Equal(owner, senderBytes) {
|
||||
return fmt.Errorf("sender is not the owner of this vesting account")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendMessage(ctx context.Context, msg proto.Message) ([]*codectypes.Any, error) {
|
||||
response, err := accountstd.ExecModuleUntyped(ctx, msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
respAny, err := accountstd.PackAny(response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []*codectypes.Any{respAny}, nil
|
||||
}
|
||||
|
||||
// TrackDelegation tracks a delegation amount for any given lockup account type
|
||||
// given the amount of coins currently being locked and the current account balance
|
||||
// of the delegation denominations.
|
||||
//
|
||||
// CONTRACT: The account's coins, delegation coins, locked coins, and delegated
|
||||
// locking coins must be sorted.
|
||||
func (bva *BaseLockup) TrackDelegation(
|
||||
ctx context.Context, balance, lockedCoins, amount sdk.Coins,
|
||||
) error {
|
||||
for _, coin := range amount {
|
||||
baseAmt := balance.AmountOf(coin.Denom)
|
||||
lockedAmt := lockedCoins.AmountOf(coin.Denom)
|
||||
delLockingAmt, err := bva.DelegatedLocking.Get(ctx, coin.Denom)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delFreeAmt, err := bva.DelegatedFree.Get(ctx, coin.Denom)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// return error if the delegation amount is zero or if the base coins does not
|
||||
// exceed the desired delegation amount.
|
||||
if coin.Amount.IsZero() || baseAmt.LT(coin.Amount) {
|
||||
return sdkerrors.ErrInvalidCoins.Wrap("delegation attempt with zero coins or insufficient funds")
|
||||
}
|
||||
|
||||
// compute x and y per the specification, where:
|
||||
// X := min(max(V - DV, 0), D)
|
||||
// Y := D - X
|
||||
x := math.MinInt(math.MaxInt(lockedAmt.Sub(delLockingAmt), math.ZeroInt()), coin.Amount)
|
||||
y := coin.Amount.Sub(x)
|
||||
|
||||
delLockingCoin := sdk.NewCoin(coin.Denom, delLockingAmt)
|
||||
delFreeCoin := sdk.NewCoin(coin.Denom, delFreeAmt)
|
||||
if !x.IsZero() {
|
||||
xCoin := sdk.NewCoin(coin.Denom, x)
|
||||
newDelLocking := delLockingCoin.Add(xCoin)
|
||||
err = bva.DelegatedLocking.Set(ctx, newDelLocking.Denom, newDelLocking.Amount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !y.IsZero() {
|
||||
yCoin := sdk.NewCoin(coin.Denom, y)
|
||||
newDelFree := delFreeCoin.Add(yCoin)
|
||||
err = bva.DelegatedFree.Set(ctx, newDelFree.Denom, newDelFree.Amount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TrackUndelegation tracks an undelegation amount by setting the necessary
|
||||
// values by which delegated locking and delegated free need to decrease and
|
||||
// by which amount the base coins need to increase.
|
||||
//
|
||||
// NOTE: The undelegation (bond refund) amount may exceed the delegated
|
||||
// locking (bond) amount due to the way undelegation truncates the bond refund,
|
||||
// which can increase the validator's exchange rate (tokens/shares) slightly if
|
||||
// the undelegated tokens are non-integral.
|
||||
//
|
||||
// CONTRACT: The account's coins and undelegation coins must be sorted.
|
||||
func (bva *BaseLockup) TrackUndelegation(ctx context.Context, amount sdk.Coins) error {
|
||||
for _, coin := range amount {
|
||||
// return error if the undelegation amount is zero
|
||||
if coin.Amount.IsZero() {
|
||||
return sdkerrors.ErrInvalidCoins.Wrap("undelegation attempt with zero coins")
|
||||
}
|
||||
delFreeAmt, err := bva.DelegatedFree.Get(ctx, coin.Denom)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delLockingAmt, err := bva.DelegatedLocking.Get(ctx, coin.Denom)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// compute x and y per the specification, where:
|
||||
// X := min(DF, D)
|
||||
// Y := min(DV, D - X)
|
||||
x := math.MinInt(delFreeAmt, coin.Amount)
|
||||
y := math.MinInt(delLockingAmt, coin.Amount.Sub(x))
|
||||
|
||||
delLockingCoin := sdk.NewCoin(coin.Denom, delLockingAmt)
|
||||
delFreeCoin := sdk.NewCoin(coin.Denom, delFreeAmt)
|
||||
if !x.IsZero() {
|
||||
xCoin := sdk.NewCoin(coin.Denom, x)
|
||||
newDelFree := delFreeCoin.Sub(xCoin)
|
||||
err = bva.DelegatedFree.Set(ctx, newDelFree.Denom, newDelFree.Amount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !y.IsZero() {
|
||||
yCoin := sdk.NewCoin(coin.Denom, y)
|
||||
newDelLocking := delLockingCoin.Sub(yCoin)
|
||||
err = bva.DelegatedLocking.Set(ctx, newDelLocking.Denom, newDelLocking.Amount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bva BaseLockup) getBalance(ctx context.Context, sender, denom string) (*sdk.Coin, error) {
|
||||
// Query account balance for the sent denom
|
||||
balanceQueryReq := banktypes.NewQueryBalanceRequest(sdk.AccAddress(sender), denom)
|
||||
resp, err := accountstd.QueryModule[banktypes.QueryBalanceResponse](ctx, balanceQueryReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.Balance, nil
|
||||
}
|
||||
|
||||
func (bva BaseLockup) checkTokensSendable(ctx context.Context, sender string, amount, lockedCoins sdk.Coins) error {
|
||||
// Check if any sent tokens is exceeds lockup account balances
|
||||
for _, coin := range amount {
|
||||
balance, err := bva.getBalance(ctx, sender, coin.Denom)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lockedAmt := lockedCoins.AmountOf(coin.Denom)
|
||||
|
||||
// get lockedCoin from that are not bonded for the sent denom
|
||||
notBondedLockedCoin, err := bva.GetNotBondedLockedCoin(ctx, sdk.NewCoin(coin.Denom, lockedAmt), coin.Denom)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
spendable, hasNeg := sdk.Coins{*balance}.SafeSub(notBondedLockedCoin)
|
||||
if hasNeg {
|
||||
return errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds,
|
||||
"locked amount exceeds account balance funds: %s > %s", notBondedLockedCoin, balance)
|
||||
}
|
||||
|
||||
if _, hasNeg := spendable.SafeSub(coin); hasNeg {
|
||||
if len(spendable) == 0 {
|
||||
spendable = sdk.Coins{sdk.NewCoin(coin.Denom, math.ZeroInt())}
|
||||
}
|
||||
return errorsmod.Wrapf(
|
||||
sdkerrors.ErrInsufficientFunds,
|
||||
"spendable balance %s is smaller than %s",
|
||||
spendable, coin,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IterateSendEnabledEntries iterates over all the SendEnabled entries.
|
||||
func (bva BaseLockup) IterateCoinEntries(
|
||||
ctx context.Context,
|
||||
entries collections.Map[string, math.Int],
|
||||
cb func(denom string, value math.Int) (bool, error),
|
||||
) error {
|
||||
err := entries.Walk(ctx, nil, func(key string, value math.Int) (stop bool, err error) {
|
||||
return cb(key, value)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// GetNotBondedLockedCoin returns the coin that are not spendable that are not bonded by denom
|
||||
// for a lockup account. If the coin by the provided denom are not locked, an coin with zero amount is returned.
|
||||
func (bva BaseLockup) GetNotBondedLockedCoin(ctx context.Context, lockedCoin sdk.Coin, denom string) (sdk.Coin, error) {
|
||||
delegatedLockingAmt, err := bva.DelegatedLocking.Get(ctx, denom)
|
||||
if err != nil {
|
||||
return sdk.Coin{}, err
|
||||
}
|
||||
|
||||
x := math.MinInt(lockedCoin.Amount, delegatedLockingAmt)
|
||||
lockedAmt := lockedCoin.Amount.Sub(x)
|
||||
|
||||
return sdk.NewCoin(denom, lockedAmt), nil
|
||||
}
|
||||
|
||||
// QueryLockupAccountBaseInfo returns a lockup account's info
|
||||
func (bva BaseLockup) QueryLockupAccountBaseInfo(ctx context.Context, _ *lockuptypes.QueryLockupAccountInfoRequest) (
|
||||
*lockuptypes.QueryLockupAccountInfoResponse, error,
|
||||
) {
|
||||
owner, err := bva.Owner.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ownerAddress, err := bva.addressCodec.BytesToString(owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
endTime, err := bva.EndTime.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
originalLocking := sdk.Coins{}
|
||||
err = bva.IterateCoinEntries(ctx, bva.OriginalLocking, func(key string, value math.Int) (stop bool, err error) {
|
||||
originalLocking = append(originalLocking, sdk.NewCoin(key, value))
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delegatedLocking := sdk.Coins{}
|
||||
err = bva.IterateCoinEntries(ctx, bva.DelegatedLocking, func(key string, value math.Int) (stop bool, err error) {
|
||||
delegatedLocking = append(delegatedLocking, sdk.NewCoin(key, value))
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delegatedFree := sdk.Coins{}
|
||||
err = bva.IterateCoinEntries(ctx, bva.DelegatedFree, func(key string, value math.Int) (stop bool, err error) {
|
||||
delegatedFree = append(delegatedFree, sdk.NewCoin(key, value))
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &lockuptypes.QueryLockupAccountInfoResponse{
|
||||
Owner: ownerAddress,
|
||||
OriginalLocking: originalLocking,
|
||||
DelegatedLocking: delegatedLocking,
|
||||
DelegatedFree: delegatedFree,
|
||||
EndTime: &endTime,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
package lockup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"cosmossdk.io/collections"
|
||||
collcodec "cosmossdk.io/collections/codec"
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
"cosmossdk.io/math"
|
||||
"cosmossdk.io/x/accounts/accountstd"
|
||||
lockuptypes "cosmossdk.io/x/accounts/lockup/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
// Compile-time type assertions
|
||||
var (
|
||||
_ accountstd.Interface = (*PeriodicLockingAccount)(nil)
|
||||
)
|
||||
|
||||
// NewPeriodicLockingAccount creates a new PeriodicLockingAccount object.
|
||||
func NewPeriodicLockingAccount(d accountstd.Dependencies) (*PeriodicLockingAccount, error) {
|
||||
baseLockup := newBaseLockup(d)
|
||||
|
||||
periodicsVestingAccount := PeriodicLockingAccount{
|
||||
BaseLockup: baseLockup,
|
||||
StartTime: collections.NewItem(d.SchemaBuilder, StartTimePrefix, "start_time", collcodec.KeyToValueCodec[time.Time](sdk.TimeKey)),
|
||||
LockingPeriods: collections.NewVec(d.SchemaBuilder, LockingPeriodsPrefix, "locking_periods", codec.CollValue[lockuptypes.Period](d.LegacyStateCodec)),
|
||||
}
|
||||
|
||||
return &periodicsVestingAccount, nil
|
||||
}
|
||||
|
||||
type PeriodicLockingAccount struct {
|
||||
*BaseLockup
|
||||
StartTime collections.Item[time.Time]
|
||||
LockingPeriods collections.Vec[lockuptypes.Period]
|
||||
}
|
||||
|
||||
func (pva PeriodicLockingAccount) Init(ctx context.Context, msg *lockuptypes.MsgInitPeriodicLockingAccount) (*lockuptypes.MsgInitPeriodicLockingAccountResponse, error) {
|
||||
owner, err := pva.addressCodec.StringToBytes(msg.Owner)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'owner' address: %s", err)
|
||||
}
|
||||
|
||||
hs := pva.headerService.GetHeaderInfo(ctx)
|
||||
|
||||
if msg.StartTime.Before(hs.Time) {
|
||||
return nil, sdkerrors.ErrInvalidRequest.Wrap("start time %s should be after block time")
|
||||
}
|
||||
|
||||
totalCoins := sdk.Coins{}
|
||||
endTime := msg.StartTime
|
||||
for _, period := range msg.LockingPeriods {
|
||||
if period.Length.Seconds() <= 0 {
|
||||
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "invalid period duration length %d", period.Length)
|
||||
}
|
||||
|
||||
if err := validateAmount(period.Amount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
totalCoins = totalCoins.Add(period.Amount...)
|
||||
// Calculate end time
|
||||
endTime = endTime.Add(period.Length)
|
||||
err = pva.LockingPeriods.Push(ctx, period)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
funds := accountstd.Funds(ctx)
|
||||
if !funds.Equal(totalCoins) {
|
||||
return nil, sdkerrors.ErrInvalidRequest.Wrap("invalid funding amount, should be equal to total coins lockup")
|
||||
}
|
||||
|
||||
sortedAmt := totalCoins.Sort()
|
||||
for _, coin := range sortedAmt {
|
||||
err := pva.OriginalLocking.Set(ctx, coin.Denom, coin.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
err = pva.StartTime.Set(ctx, msg.StartTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = pva.EndTime.Set(ctx, endTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = pva.Owner.Set(ctx, owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &lockuptypes.MsgInitPeriodicLockingAccountResponse{}, nil
|
||||
}
|
||||
|
||||
func (pva *PeriodicLockingAccount) Delegate(ctx context.Context, msg *lockuptypes.MsgDelegate) (
|
||||
*lockuptypes.MsgExecuteMessagesResponse, error,
|
||||
) {
|
||||
return pva.BaseLockup.Delegate(ctx, msg, pva.GetLockedCoinsWithDenoms)
|
||||
}
|
||||
|
||||
func (pva *PeriodicLockingAccount) Undelegate(ctx context.Context, msg *lockuptypes.MsgUndelegate) (
|
||||
*lockuptypes.MsgExecuteMessagesResponse, error,
|
||||
) {
|
||||
return pva.BaseLockup.Undelegate(ctx, msg)
|
||||
}
|
||||
|
||||
func (pva *PeriodicLockingAccount) SendCoins(ctx context.Context, msg *lockuptypes.MsgSend) (
|
||||
*lockuptypes.MsgExecuteMessagesResponse, error,
|
||||
) {
|
||||
return pva.BaseLockup.SendCoins(ctx, msg, pva.GetLockedCoinsWithDenoms)
|
||||
}
|
||||
|
||||
func (pva *PeriodicLockingAccount) WithdrawUnlockedCoins(ctx context.Context, msg *lockuptypes.MsgWithdraw) (
|
||||
*lockuptypes.MsgWithdrawResponse, error,
|
||||
) {
|
||||
return pva.BaseLockup.WithdrawUnlockedCoins(ctx, msg, pva.GetLockedCoinsWithDenoms)
|
||||
}
|
||||
|
||||
// IterateSendEnabledEntries iterates over all the SendEnabled entries.
|
||||
func (pva PeriodicLockingAccount) IteratePeriods(
|
||||
ctx context.Context,
|
||||
cb func(value lockuptypes.Period) (bool, error),
|
||||
) error {
|
||||
err := pva.LockingPeriods.Walk(ctx, nil, func(_ uint64, value lockuptypes.Period) (stop bool, err error) {
|
||||
return cb(value)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLockCoinsInfo returns the total number of locked and unlocked coins.
|
||||
func (pva PeriodicLockingAccount) GetLockCoinsInfo(ctx context.Context, blockTime time.Time) (unlockedCoins, lockedCoins sdk.Coins, err error) {
|
||||
unlockedCoins = sdk.Coins{}
|
||||
lockedCoins = sdk.Coins{}
|
||||
|
||||
// We must handle the case where the start time for a lockup account has
|
||||
// been set into the future or when the start of the chain is not exactly
|
||||
// known.
|
||||
startTime, err := pva.StartTime.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
endTime, err := pva.EndTime.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
originalLocking := sdk.Coins{}
|
||||
err = pva.IterateCoinEntries(ctx, pva.OriginalLocking, func(key string, value math.Int) (stop bool, err error) {
|
||||
originalLocking = append(originalLocking, sdk.NewCoin(key, value))
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if blockTime.Before(startTime) {
|
||||
return unlockedCoins, originalLocking, nil
|
||||
} else if blockTime.After(endTime) {
|
||||
return originalLocking, lockedCoins, nil
|
||||
}
|
||||
|
||||
// track the start time of the next period
|
||||
currentPeriodStartTime, err := pva.StartTime.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
err = pva.IteratePeriods(ctx, func(period lockuptypes.Period) (stop bool, err error) {
|
||||
x := blockTime.Sub(currentPeriodStartTime)
|
||||
if x.Seconds() < period.Length.Seconds() {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
unlockedCoins = unlockedCoins.Add(period.Amount...)
|
||||
|
||||
// update the start time of the next period
|
||||
err = pva.StartTime.Set(ctx, currentPeriodStartTime.Add(period.Length))
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
lockedCoins = originalLocking.Sub(unlockedCoins...)
|
||||
|
||||
return unlockedCoins, lockedCoins, err
|
||||
}
|
||||
|
||||
// GetLockedCoins returns the total number of locked coins. If no coins are
|
||||
// locked, nil is returned.
|
||||
func (pva PeriodicLockingAccount) GetLockedCoins(ctx context.Context, blockTime time.Time) (sdk.Coins, error) {
|
||||
_, vestingCoins, err := pva.GetLockCoinsInfo(ctx, blockTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vestingCoins, nil
|
||||
}
|
||||
|
||||
// GetLockCoinInfoWithDenom returns the total number of locked and unlocked coin for a specific denom.
|
||||
func (pva PeriodicLockingAccount) GetLockCoinInfoWithDenom(ctx context.Context, blockTime time.Time, denom string) (unlockedCoin, lockedCoin *sdk.Coin, err error) {
|
||||
// We must handle the case where the start time for a lockup account has
|
||||
// been set into the future or when the start of the chain is not exactly
|
||||
// known.
|
||||
startTime, err := pva.StartTime.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
endTime, err := pva.EndTime.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
originalLockingAmt, err := pva.OriginalLocking.Get(ctx, denom)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
originalLockingCoin := sdk.NewCoin(denom, originalLockingAmt)
|
||||
|
||||
if blockTime.Before(startTime) {
|
||||
return &sdk.Coin{}, &originalLockingCoin, nil
|
||||
} else if blockTime.After(endTime) {
|
||||
return &originalLockingCoin, &sdk.Coin{}, nil
|
||||
}
|
||||
|
||||
// track the start time of the next period
|
||||
currentPeriodStartTime, err := pva.StartTime.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
unlocked := sdk.NewCoin(denom, math.ZeroInt())
|
||||
err = pva.IteratePeriods(ctx, func(period lockuptypes.Period) (stop bool, err error) {
|
||||
x := blockTime.Sub(currentPeriodStartTime)
|
||||
if x.Seconds() < period.Length.Seconds() {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
unlocked = unlocked.Add(sdk.NewCoin(denom, period.Amount.AmountOf(denom)))
|
||||
|
||||
// update the start time of the next period
|
||||
err = pva.StartTime.Set(ctx, currentPeriodStartTime.Add(period.Length))
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
locked := originalLockingCoin.Sub(unlocked)
|
||||
|
||||
return &unlocked, &locked, err
|
||||
}
|
||||
|
||||
// GetLockedCoinsWithDenoms returns the total number of locked coins. If no coins are
|
||||
// locked, nil is returned.
|
||||
func (pva PeriodicLockingAccount) GetLockedCoinsWithDenoms(ctx context.Context, blockTime time.Time, denoms ...string) (sdk.Coins, error) {
|
||||
lockedCoins := sdk.Coins{}
|
||||
for _, denom := range denoms {
|
||||
_, lockedCoin, err := pva.GetLockCoinInfoWithDenom(ctx, blockTime, denom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lockedCoins = append(lockedCoins, *lockedCoin)
|
||||
}
|
||||
return lockedCoins, nil
|
||||
}
|
||||
|
||||
func (pva PeriodicLockingAccount) QueryLockupAccountInfo(ctx context.Context, req *lockuptypes.QueryLockupAccountInfoRequest) (
|
||||
*lockuptypes.QueryLockupAccountInfoResponse, error,
|
||||
) {
|
||||
resp, err := pva.BaseLockup.QueryLockupAccountBaseInfo(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
startTime, err := pva.StartTime.Get(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hs := pva.headerService.GetHeaderInfo(ctx)
|
||||
unlockedCoins, lockedCoins, err := pva.GetLockCoinsInfo(ctx, hs.Time)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.StartTime = &startTime
|
||||
resp.LockedCoins = lockedCoins
|
||||
resp.UnlockedCoins = unlockedCoins
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (pva PeriodicLockingAccount) QueryLockingPeriods(ctx context.Context, msg *lockuptypes.QueryLockingPeriodsRequest) (
|
||||
*lockuptypes.QueryLockingPeriodsResponse, error,
|
||||
) {
|
||||
lockingPeriods := []*lockuptypes.Period{}
|
||||
err := pva.IteratePeriods(ctx, func(period lockuptypes.Period) (stop bool, err error) {
|
||||
lockingPeriods = append(lockingPeriods, &period)
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lockuptypes.QueryLockingPeriodsResponse{
|
||||
LockingPeriods: lockingPeriods,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Implement smart account interface
|
||||
func (pva PeriodicLockingAccount) RegisterInitHandler(builder *accountstd.InitBuilder) {
|
||||
accountstd.RegisterInitHandler(builder, pva.Init)
|
||||
}
|
||||
|
||||
func (pva PeriodicLockingAccount) RegisterExecuteHandlers(builder *accountstd.ExecuteBuilder) {
|
||||
accountstd.RegisterExecuteHandler(builder, pva.Delegate)
|
||||
accountstd.RegisterExecuteHandler(builder, pva.Undelegate)
|
||||
accountstd.RegisterExecuteHandler(builder, pva.SendCoins)
|
||||
accountstd.RegisterExecuteHandler(builder, pva.WithdrawUnlockedCoins)
|
||||
}
|
||||
|
||||
func (pva PeriodicLockingAccount) RegisterQueryHandlers(builder *accountstd.QueryBuilder) {
|
||||
accountstd.RegisterQueryHandler(builder, pva.QueryLockupAccountInfo)
|
||||
accountstd.RegisterQueryHandler(builder, pva.QueryLockingPeriods)
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package lockup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
"cosmossdk.io/x/accounts/accountstd"
|
||||
lockuptypes "cosmossdk.io/x/accounts/lockup/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Compile-time type assertions
|
||||
var (
|
||||
_ accountstd.Interface = (*PermanentLockingAccount)(nil)
|
||||
)
|
||||
|
||||
// NewPermanentLockingAccount creates a new PermanentLockingAccount object.
|
||||
func NewPermanentLockingAccount(d accountstd.Dependencies) (*PermanentLockingAccount, error) {
|
||||
baseLockup := newBaseLockup(d)
|
||||
|
||||
return &PermanentLockingAccount{baseLockup}, nil
|
||||
}
|
||||
|
||||
type PermanentLockingAccount struct {
|
||||
*BaseLockup
|
||||
}
|
||||
|
||||
func (plva PermanentLockingAccount) Init(ctx context.Context, msg *lockuptypes.MsgInitLockupAccount) (*lockuptypes.MsgInitLockupAccountResponse, error) {
|
||||
resp, err := plva.BaseLockup.Init(ctx, msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = plva.EndTime.Set(ctx, time.Time{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// GetlockedCoinsWithDenoms returns the total number of locked coins. If no coins are
|
||||
// locked, nil is returned.
|
||||
func (plva PermanentLockingAccount) GetlockedCoinsWithDenoms(ctx context.Context, blockTime time.Time, denoms ...string) (sdk.Coins, error) {
|
||||
vestingCoins := sdk.Coins{}
|
||||
for _, denom := range denoms {
|
||||
originalVestingAmt, err := plva.OriginalLocking.Get(ctx, denom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vestingCoins = append(vestingCoins, sdk.NewCoin(denom, originalVestingAmt))
|
||||
}
|
||||
return vestingCoins, nil
|
||||
}
|
||||
|
||||
func (plva *PermanentLockingAccount) Delegate(ctx context.Context, msg *lockuptypes.MsgDelegate) (
|
||||
*lockuptypes.MsgExecuteMessagesResponse, error,
|
||||
) {
|
||||
return plva.BaseLockup.Delegate(ctx, msg, plva.GetlockedCoinsWithDenoms)
|
||||
}
|
||||
|
||||
func (plva *PermanentLockingAccount) Undelegate(ctx context.Context, msg *lockuptypes.MsgUndelegate) (
|
||||
*lockuptypes.MsgExecuteMessagesResponse, error,
|
||||
) {
|
||||
return plva.BaseLockup.Undelegate(ctx, msg)
|
||||
}
|
||||
|
||||
func (plva *PermanentLockingAccount) SendCoins(ctx context.Context, msg *lockuptypes.MsgSend) (
|
||||
*lockuptypes.MsgExecuteMessagesResponse, error,
|
||||
) {
|
||||
return plva.BaseLockup.SendCoins(ctx, msg, plva.GetlockedCoinsWithDenoms)
|
||||
}
|
||||
|
||||
func (plva PermanentLockingAccount) QueryLockupAccountInfo(ctx context.Context, req *lockuptypes.QueryLockupAccountInfoRequest) (
|
||||
*lockuptypes.QueryLockupAccountInfoResponse, error,
|
||||
) {
|
||||
resp, err := plva.BaseLockup.QueryLockupAccountBaseInfo(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
originalLocking := sdk.Coins{}
|
||||
err = plva.IterateCoinEntries(ctx, plva.OriginalLocking, func(key string, value math.Int) (stop bool, err error) {
|
||||
originalLocking = append(originalLocking, sdk.NewCoin(key, value))
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp.LockedCoins = originalLocking
|
||||
resp.UnlockedCoins = sdk.Coins{}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Implement smart account interface
|
||||
func (plva PermanentLockingAccount) RegisterInitHandler(builder *accountstd.InitBuilder) {
|
||||
accountstd.RegisterInitHandler(builder, plva.Init)
|
||||
}
|
||||
|
||||
func (plva PermanentLockingAccount) RegisterExecuteHandlers(builder *accountstd.ExecuteBuilder) {
|
||||
accountstd.RegisterExecuteHandler(builder, plva.Delegate)
|
||||
accountstd.RegisterExecuteHandler(builder, plva.Undelegate)
|
||||
accountstd.RegisterExecuteHandler(builder, plva.SendCoins)
|
||||
}
|
||||
|
||||
func (plva PermanentLockingAccount) RegisterQueryHandlers(builder *accountstd.QueryBuilder) {
|
||||
accountstd.RegisterQueryHandler(builder, plva.QueryLockupAccountInfo)
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package lockup
|
||||
|
||||
import (
|
||||
bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
|
||||
v1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
|
||||
stakingv1beta1 "cosmossdk.io/api/cosmos/staking/v1beta1"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/runtime/protoiface"
|
||||
)
|
||||
|
||||
type ProtoMsg = protoiface.MessageV1
|
||||
|
||||
type gogoProtoPlusV2 interface {
|
||||
proto.Message
|
||||
ProtoMsg
|
||||
}
|
||||
|
||||
// protoV2GogoWrapper is a wrapper of a protov2 message into a gogo message.
|
||||
// this is exceptionally allowed to enable accounts to be decoupled from
|
||||
// the SDK, since x/accounts can support only protov1 in its APIs.
|
||||
// But in order to keep it decoupled from the SDK we need to use the API module.
|
||||
// This is a temporary solution that is being used here:
|
||||
// https://github.com/cosmos/cosmos-sdk/blob/main/x/accounts/coin_transfer.go
|
||||
type protoV2GogoWrapper struct {
|
||||
gogoProtoPlusV2
|
||||
}
|
||||
|
||||
func (h protoV2GogoWrapper) XXX_MessageName() string {
|
||||
return string(proto.MessageName(h.gogoProtoPlusV2))
|
||||
}
|
||||
|
||||
func makeMsgSend(fromAddr, toAddr string, coins sdk.Coins) ProtoMsg {
|
||||
v2Coins := make([]*v1beta1.Coin, len(coins))
|
||||
for i, coin := range coins {
|
||||
v2Coins[i] = &v1beta1.Coin{
|
||||
Denom: coin.Denom,
|
||||
Amount: coin.Amount.String(),
|
||||
}
|
||||
}
|
||||
return protoV2GogoWrapper{&bankv1beta1.MsgSend{
|
||||
FromAddress: fromAddr,
|
||||
ToAddress: toAddr,
|
||||
Amount: v2Coins,
|
||||
}}
|
||||
}
|
||||
|
||||
func makeMsgDelegate(delegatorAddr, validatorAddr string, amount sdk.Coin) ProtoMsg {
|
||||
v2Coin := &v1beta1.Coin{
|
||||
Denom: amount.Denom,
|
||||
Amount: amount.Amount.String(),
|
||||
}
|
||||
return protoV2GogoWrapper{&stakingv1beta1.MsgDelegate{
|
||||
DelegatorAddress: delegatorAddr,
|
||||
ValidatorAddress: validatorAddr,
|
||||
Amount: v2Coin,
|
||||
}}
|
||||
}
|
||||
|
||||
func makeMsgUndelegate(delegatorAddr, validatorAddr string, amount sdk.Coin) ProtoMsg {
|
||||
v2Coin := &v1beta1.Coin{
|
||||
Denom: amount.Denom,
|
||||
Amount: amount.Amount.String(),
|
||||
}
|
||||
return protoV2GogoWrapper{&stakingv1beta1.MsgUndelegate{
|
||||
DelegatorAddress: delegatorAddr,
|
||||
ValidatorAddress: validatorAddr,
|
||||
Amount: v2Coin,
|
||||
}}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
proto "github.com/cosmos/gogoproto/proto"
|
||||
)
|
||||
|
||||
func UnpackAnyRaw(m *codectypes.Any) (proto.Message, error) {
|
||||
split := strings.Split(m.TypeUrl, "/")
|
||||
name := split[len(split)-1]
|
||||
typ := proto.MessageType(name)
|
||||
if typ == nil {
|
||||
return nil, fmt.Errorf("no message type found for %s", name)
|
||||
}
|
||||
concreteMsg := reflect.New(typ.Elem()).Interface().(proto.Message)
|
||||
err := proto.Unmarshal(m.Value, concreteMsg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return concreteMsg, nil
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: cosmos/accounts/defaults/lockup/lockup.proto
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types"
|
||||
types "github.com/cosmos/cosmos-sdk/types"
|
||||
_ "github.com/cosmos/cosmos-sdk/types/tx/amino"
|
||||
_ "github.com/cosmos/gogoproto/gogoproto"
|
||||
proto "github.com/cosmos/gogoproto/proto"
|
||||
github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types"
|
||||
_ "google.golang.org/protobuf/types/known/durationpb"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
time "time"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
var _ = time.Kitchen
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// Period defines a length of time and amount of coins that will be lock.
|
||||
type Period struct {
|
||||
// Period duration
|
||||
Length time.Duration `protobuf:"bytes,1,opt,name=length,proto3,stdduration" json:"length"`
|
||||
Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"`
|
||||
}
|
||||
|
||||
func (m *Period) Reset() { *m = Period{} }
|
||||
func (m *Period) String() string { return proto.CompactTextString(m) }
|
||||
func (*Period) ProtoMessage() {}
|
||||
func (*Period) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_79b466256e1a079c, []int{0}
|
||||
}
|
||||
func (m *Period) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *Period) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_Period.Marshal(b, m, deterministic)
|
||||
} else {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
}
|
||||
func (m *Period) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Period.Merge(m, src)
|
||||
}
|
||||
func (m *Period) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *Period) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Period.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Period proto.InternalMessageInfo
|
||||
|
||||
func (m *Period) GetLength() time.Duration {
|
||||
if m != nil {
|
||||
return m.Length
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Period) GetAmount() github_com_cosmos_cosmos_sdk_types.Coins {
|
||||
if m != nil {
|
||||
return m.Amount
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Period)(nil), "cosmos.accounts.defaults.lockup.Period")
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("cosmos/accounts/defaults/lockup/lockup.proto", fileDescriptor_79b466256e1a079c)
|
||||
}
|
||||
|
||||
var fileDescriptor_79b466256e1a079c = []byte{
|
||||
// 326 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x91, 0x31, 0x4e, 0xc3, 0x30,
|
||||
0x14, 0x86, 0x63, 0x90, 0x32, 0x04, 0x18, 0xa8, 0x18, 0x4a, 0x07, 0xa7, 0x62, 0xaa, 0x2a, 0x6a,
|
||||
0xab, 0x70, 0x01, 0x54, 0x10, 0xac, 0x88, 0x91, 0x05, 0x39, 0x8e, 0xeb, 0x5a, 0x4d, 0xf2, 0xaa,
|
||||
0xda, 0x41, 0xf4, 0x16, 0x8c, 0x88, 0x13, 0x20, 0xa6, 0x5e, 0x02, 0xa9, 0x63, 0x47, 0x26, 0x8a,
|
||||
0x9a, 0xa1, 0xd7, 0x40, 0xb1, 0x9d, 0x91, 0xc5, 0xef, 0x59, 0xfe, 0xbf, 0xf7, 0xbf, 0x5f, 0x8e,
|
||||
0xce, 0x39, 0xe8, 0x1c, 0x34, 0x65, 0x9c, 0x43, 0x59, 0x18, 0x4d, 0x53, 0x31, 0x66, 0x65, 0x66,
|
||||
0x34, 0xcd, 0x80, 0x4f, 0xcb, 0x99, 0x2f, 0x64, 0x36, 0x07, 0x03, 0xad, 0xd8, 0xa9, 0x49, 0xa3,
|
||||
0x26, 0x8d, 0x9a, 0x38, 0x59, 0xe7, 0x98, 0xe5, 0xaa, 0x00, 0x6a, 0x4f, 0xc7, 0x74, 0xb0, 0x77,
|
||||
0x48, 0x98, 0x16, 0xf4, 0x79, 0x98, 0x08, 0xc3, 0x86, 0x94, 0x83, 0x2a, 0xfc, 0xfb, 0x89, 0x04,
|
||||
0x09, 0xb6, 0xa5, 0x75, 0xd7, 0x50, 0x12, 0x40, 0x66, 0x82, 0xda, 0x5b, 0x52, 0x8e, 0x69, 0x5a,
|
||||
0xce, 0x99, 0x51, 0xe0, 0xa9, 0xb3, 0x2f, 0x14, 0x85, 0xf7, 0x62, 0xae, 0x20, 0x6d, 0x5d, 0x45,
|
||||
0x61, 0x26, 0x0a, 0x69, 0x26, 0x6d, 0xd4, 0x45, 0xbd, 0x83, 0x8b, 0x53, 0xe2, 0x58, 0xd2, 0xb0,
|
||||
0xe4, 0xc6, 0xb3, 0xa3, 0xa3, 0xd5, 0x4f, 0x1c, 0xbc, 0x6d, 0x62, 0xf4, 0xb1, 0x5b, 0xf6, 0xd1,
|
||||
0x83, 0xe7, 0x5a, 0x8b, 0x28, 0x64, 0x79, 0x1d, 0xa8, 0xbd, 0xd7, 0xdd, 0xb7, 0x13, 0x7c, 0xce,
|
||||
0x7a, 0x67, 0xe2, 0x77, 0x26, 0xd7, 0xa0, 0x8a, 0xd1, 0x6d, 0x3d, 0xe1, 0x73, 0x13, 0xf7, 0xa4,
|
||||
0x32, 0x93, 0x32, 0x21, 0x1c, 0x72, 0xea, 0x03, 0xba, 0x32, 0xd0, 0xe9, 0x94, 0x9a, 0xc5, 0x4c,
|
||||
0x68, 0x0b, 0xe8, 0xf7, 0xdd, 0xb2, 0x7f, 0x98, 0x09, 0xc9, 0xf8, 0xe2, 0xa9, 0x4e, 0xad, 0xbd,
|
||||
0xb5, 0x33, 0x1c, 0xdd, 0xad, 0xb6, 0x18, 0xad, 0xb7, 0x18, 0xfd, 0x6e, 0x31, 0x7a, 0xad, 0x70,
|
||||
0xb0, 0xae, 0x70, 0xf0, 0x5d, 0xe1, 0xe0, 0x71, 0xe0, 0xe6, 0xe9, 0x74, 0x4a, 0x14, 0xd0, 0x97,
|
||||
0xff, 0x7f, 0xc8, 0x9a, 0x25, 0xa1, 0x4d, 0x7b, 0xf9, 0x17, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xa5,
|
||||
0x33, 0xd0, 0xd1, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *Period) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *Period) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *Period) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Amount) > 0 {
|
||||
for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- {
|
||||
{
|
||||
size, err := m.Amount[iNdEx].MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintLockup(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
}
|
||||
}
|
||||
n1, err1 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.Length, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.Length):])
|
||||
if err1 != nil {
|
||||
return 0, err1
|
||||
}
|
||||
i -= n1
|
||||
i = encodeVarintLockup(dAtA, i, uint64(n1))
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func encodeVarintLockup(dAtA []byte, offset int, v uint64) int {
|
||||
offset -= sovLockup(v)
|
||||
base := offset
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return base
|
||||
}
|
||||
func (m *Period) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.Length)
|
||||
n += 1 + l + sovLockup(uint64(l))
|
||||
if len(m.Amount) > 0 {
|
||||
for _, e := range m.Amount {
|
||||
l = e.Size()
|
||||
n += 1 + l + sovLockup(uint64(l))
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func sovLockup(x uint64) (n int) {
|
||||
return (math_bits.Len64(x|1) + 6) / 7
|
||||
}
|
||||
func sozLockup(x uint64) (n int) {
|
||||
return sovLockup(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
}
|
||||
func (m *Period) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowLockup
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: Period: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: Period: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Length", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowLockup
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthLockup
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthLockup
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(&m.Length, dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowLockup
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthLockup
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthLockup
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Amount = append(m.Amount, types.Coin{})
|
||||
if err := m.Amount[len(m.Amount)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipLockup(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthLockup
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func skipLockup(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
depth := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowLockup
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
wireType := int(wire & 0x7)
|
||||
switch wireType {
|
||||
case 0:
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowLockup
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx++
|
||||
if dAtA[iNdEx-1] < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowLockup
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
length |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthLockup
|
||||
}
|
||||
iNdEx += length
|
||||
case 3:
|
||||
depth++
|
||||
case 4:
|
||||
if depth == 0 {
|
||||
return 0, ErrUnexpectedEndOfGroupLockup
|
||||
}
|
||||
depth--
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
default:
|
||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||
}
|
||||
if iNdEx < 0 {
|
||||
return 0, ErrInvalidLengthLockup
|
||||
}
|
||||
if depth == 0 {
|
||||
return iNdEx, nil
|
||||
}
|
||||
}
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidLengthLockup = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowLockup = fmt.Errorf("proto: integer overflow")
|
||||
ErrUnexpectedEndOfGroupLockup = fmt.Errorf("proto: unexpected end of group")
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
package lockup
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
func validateAmount(amount sdk.Coins) error {
|
||||
if !amount.IsValid() {
|
||||
return sdkerrors.ErrInvalidCoins.Wrap(amount.String())
|
||||
}
|
||||
|
||||
if amount.IsZero() {
|
||||
return sdkerrors.ErrInvalidCoins.Wrap(amount.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
syntax = "proto3";
|
||||
package cosmos.accounts.defaults.lockup;
|
||||
|
||||
import "amino/amino.proto";
|
||||
import "cosmos/base/v1beta1/coin.proto";
|
||||
import "gogoproto/gogo.proto";
|
||||
import "google/protobuf/duration.proto";
|
||||
|
||||
option go_package = "cosmossdk.io/x/accounts/defaults/lockup/types";
|
||||
|
||||
// Period defines a length of time and amount of coins that will be lock.
|
||||
message Period {
|
||||
// Period duration
|
||||
google.protobuf.Duration length = 1
|
||||
[(gogoproto.nullable) = false, (amino.dont_omitempty) = true, (gogoproto.stdduration) = true];
|
||||
repeated cosmos.base.v1beta1.Coin amount = 2 [
|
||||
(gogoproto.nullable) = false,
|
||||
(amino.dont_omitempty) = true,
|
||||
(amino.encoding) = "legacy_coins",
|
||||
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
syntax = "proto3";
|
||||
package cosmos.accounts.defaults.lockup;
|
||||
|
||||
import "cosmos/accounts/defaults/lockup/lockup.proto";
|
||||
import "cosmos/base/v1beta1/coin.proto";
|
||||
import "gogoproto/gogo.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "cosmossdk.io/x/accounts/defaults/lockup/types";
|
||||
|
||||
// QueryLockupAccountInfoRequest get lockup account info
|
||||
message QueryLockupAccountInfoRequest {}
|
||||
|
||||
// QueryLockupAccountInfoResponse return lockup account info
|
||||
message QueryLockupAccountInfoResponse {
|
||||
// original_locking defines the value of the account original locking coins.
|
||||
repeated cosmos.base.v1beta1.Coin original_locking = 1
|
||||
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
|
||||
|
||||
// delegated_free defines the value of the account free delegated amount.
|
||||
repeated cosmos.base.v1beta1.Coin delegated_free = 2
|
||||
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
|
||||
|
||||
// delegated_locking defines the value of the account locking delegated amount.
|
||||
repeated cosmos.base.v1beta1.Coin delegated_locking = 3
|
||||
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
|
||||
|
||||
// end_time defines the value of the account lockup start time.
|
||||
google.protobuf.Timestamp start_time = 4 [(gogoproto.stdtime) = true];
|
||||
|
||||
// end_time defines the value of the account lockup end time.
|
||||
google.protobuf.Timestamp end_time = 5 [(gogoproto.stdtime) = true];
|
||||
|
||||
// locked_coins defines the value of the account locking coins.
|
||||
repeated cosmos.base.v1beta1.Coin locked_coins = 6
|
||||
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
|
||||
|
||||
// unlocked_coins defines the value of the account released coins from lockup.
|
||||
repeated cosmos.base.v1beta1.Coin unlocked_coins = 7
|
||||
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
|
||||
|
||||
// owner defines the value of the owner of the lockup account.
|
||||
string owner = 8;
|
||||
}
|
||||
|
||||
// QueryLockingPeriodsRequest is used to query the periodic lockup account locking periods.
|
||||
message QueryLockingPeriodsRequest {}
|
||||
|
||||
// QueryLockingPeriodsResponse returns the periodic lockup account locking periods.
|
||||
message QueryLockingPeriodsResponse {
|
||||
// lockup_periods defines the value of the periodic lockup account locking periods.
|
||||
repeated Period locking_periods = 1;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
syntax = "proto3";
|
||||
package cosmos.accounts.defaults.lockup;
|
||||
|
||||
import "amino/amino.proto";
|
||||
import "cosmos/base/v1beta1/coin.proto";
|
||||
import "cosmos/accounts/defaults/lockup/lockup.proto";
|
||||
import "cosmos/msg/v1/msg.proto";
|
||||
import "cosmos_proto/cosmos.proto";
|
||||
import "gogoproto/gogo.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "cosmossdk.io/x/accounts/defaults/lockup/types";
|
||||
|
||||
//-------------------------------------- INIT --------------------------------------
|
||||
|
||||
// MsgInitLockupAccount defines a message that enables creating a lockup
|
||||
// account.
|
||||
message MsgInitLockupAccount {
|
||||
option (amino.name) = "cosmos-sdk/MsgInitLockupAccount";
|
||||
|
||||
option (gogoproto.equal) = true;
|
||||
|
||||
// owner of the vesting account
|
||||
string owner = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
|
||||
|
||||
// end of lockup
|
||||
google.protobuf.Timestamp end_time = 2
|
||||
[(gogoproto.nullable) = false, (amino.dont_omitempty) = true, (gogoproto.stdtime) = true];
|
||||
// start of lockup
|
||||
google.protobuf.Timestamp start_time = 3
|
||||
[(gogoproto.nullable) = false, (amino.dont_omitempty) = true, (gogoproto.stdtime) = true];
|
||||
}
|
||||
|
||||
// MsgInitLockupAccountResponse defines the Msg/InitLockupAccount response type.
|
||||
message MsgInitLockupAccountResponse {}
|
||||
|
||||
// MsgInitPeriodicLockingAccount defines a message that enables creating a periodic locking
|
||||
// account.
|
||||
message MsgInitPeriodicLockingAccount {
|
||||
option (amino.name) = "cosmos-sdk/MsgInitPeriodLockupAccount";
|
||||
|
||||
option (gogoproto.equal) = false;
|
||||
|
||||
// owner of the lockup account
|
||||
string owner = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
|
||||
// start of lockup
|
||||
google.protobuf.Timestamp start_time = 2
|
||||
[(gogoproto.nullable) = false, (amino.dont_omitempty) = true, (gogoproto.stdtime) = true];
|
||||
repeated Period locking_periods = 3 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true];
|
||||
}
|
||||
|
||||
// MsgInitPeriodicLockingAccountResponse defines the Msg/InitPeriodicLockingAccount
|
||||
// response type.
|
||||
message MsgInitPeriodicLockingAccountResponse {}
|
||||
|
||||
// MsgDelegate defines a message that enable lockup account to execute delegate message
|
||||
message MsgDelegate {
|
||||
option (cosmos.msg.v1.signer) = "sender";
|
||||
|
||||
option (gogoproto.equal) = false;
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
|
||||
// sender is the owner of the lockup account
|
||||
string sender = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
|
||||
string validator_address = 2 [(cosmos_proto.scalar) = "cosmos.ValidatorAddressString"];
|
||||
cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true];
|
||||
}
|
||||
|
||||
// MsgUndelegate defines a message that enable lockup account to execute undelegate message
|
||||
message MsgUndelegate {
|
||||
option (cosmos.msg.v1.signer) = "sender";
|
||||
|
||||
option (gogoproto.equal) = false;
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
|
||||
string sender = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
|
||||
string validator_address = 2 [(cosmos_proto.scalar) = "cosmos.ValidatorAddressString"];
|
||||
cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true];
|
||||
}
|
||||
|
||||
// MsgSend defines a message that enable lockup account to execute send message
|
||||
message MsgSend {
|
||||
option (cosmos.msg.v1.signer) = "sender";
|
||||
|
||||
option (gogoproto.equal) = false;
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
|
||||
string sender = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
|
||||
string to_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
|
||||
repeated cosmos.base.v1beta1.Coin amount = 3 [
|
||||
(gogoproto.nullable) = false,
|
||||
(amino.dont_omitempty) = true,
|
||||
(amino.encoding) = "legacy_coins",
|
||||
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
|
||||
];
|
||||
}
|
||||
|
||||
// MsgExecuteMessagesResponse defines the response for lockup execute operations
|
||||
message MsgExecuteMessagesResponse {
|
||||
repeated google.protobuf.Any responses = 1;
|
||||
}
|
||||
|
||||
// MsgWithdraw defines a message that the owner of the lockup can perform to withdraw unlocked token to an account of
|
||||
// choice
|
||||
message MsgWithdraw {
|
||||
option (cosmos.msg.v1.signer) = "withdrawer";
|
||||
|
||||
option (gogoproto.equal) = false;
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
|
||||
string withdrawer = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
|
||||
string to_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
|
||||
repeated string denoms = 3;
|
||||
}
|
||||
|
||||
// MsgWithdrawResponse defines the response for MsgWithdraw
|
||||
message MsgWithdrawResponse {
|
||||
string reciever = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
|
||||
repeated cosmos.base.v1beta1.Coin amount_received = 2 [
|
||||
(gogoproto.nullable) = false,
|
||||
(amino.dont_omitempty) = true,
|
||||
(amino.encoding) = "legacy_coins",
|
||||
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user