mirror of
https://github.com/colinear-labs/chain.git
synced 2026-03-05 06:44:25 -08:00
remove hyphen in module name
This commit is contained in:
64
x/colinearcore/keeper/auction.go
Normal file
64
x/colinearcore/keeper/auction.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"colinear/x/colinearcore/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// SetAuction set a specific auction in the store from its index
|
||||
func (k Keeper) SetAuction(ctx sdk.Context, auction types.Auction) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.AuctionKeyPrefix))
|
||||
b := k.cdc.MustMarshal(&auction)
|
||||
store.Set(types.AuctionKey(
|
||||
auction.Index,
|
||||
), b)
|
||||
}
|
||||
|
||||
// GetAuction returns a auction from its index
|
||||
func (k Keeper) GetAuction(
|
||||
ctx sdk.Context,
|
||||
index string,
|
||||
|
||||
) (val types.Auction, found bool) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.AuctionKeyPrefix))
|
||||
|
||||
b := store.Get(types.AuctionKey(
|
||||
index,
|
||||
))
|
||||
if b == nil {
|
||||
return val, false
|
||||
}
|
||||
|
||||
k.cdc.MustUnmarshal(b, &val)
|
||||
return val, true
|
||||
}
|
||||
|
||||
// RemoveAuction removes a auction from the store
|
||||
func (k Keeper) RemoveAuction(
|
||||
ctx sdk.Context,
|
||||
index string,
|
||||
|
||||
) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.AuctionKeyPrefix))
|
||||
store.Delete(types.AuctionKey(
|
||||
index,
|
||||
))
|
||||
}
|
||||
|
||||
// GetAllAuction returns all auction
|
||||
func (k Keeper) GetAllAuction(ctx sdk.Context) (list []types.Auction) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.AuctionKeyPrefix))
|
||||
iterator := sdk.KVStorePrefixIterator(store, []byte{})
|
||||
|
||||
defer iterator.Close()
|
||||
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
var val types.Auction
|
||||
k.cdc.MustUnmarshal(iterator.Value(), &val)
|
||||
list = append(list, val)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
113
x/colinearcore/keeper/auction_state.go
Normal file
113
x/colinearcore/keeper/auction_state.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"colinear/x/colinearcore/math"
|
||||
"colinear/x/colinearcore/memdb"
|
||||
"colinear/x/colinearcore/types"
|
||||
"log"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (k Keeper) AuctionIsExpired(ctx sdk.Context, auctionId string) (bool, error) {
|
||||
auction, found := k.GetAuction(ctx, auctionId)
|
||||
|
||||
// make sure the auction exists on-chain
|
||||
if !found {
|
||||
return true, errors.Errorf("auction %s not found", auctionId)
|
||||
}
|
||||
|
||||
return uint64(ctx.BlockHeight()) >= auction.Deadline, nil
|
||||
}
|
||||
|
||||
func (k Keeper) FinalizeExpiredAuctions(ctx sdk.Context) {
|
||||
memdb.BidDB.ForEachAuction(func(auctionId string) error {
|
||||
auction, found := k.GetAuction(ctx, auctionId)
|
||||
// make sure the auction exists on-chain
|
||||
if !found {
|
||||
return errors.Errorf("auction %s not found", auctionId)
|
||||
}
|
||||
|
||||
if uint64(ctx.BlockHeight()) >= auction.Deadline {
|
||||
var err error
|
||||
auction.Best, err = memdb.BidDB.GetLowestBid(auctionId)
|
||||
if err != nil {
|
||||
return errors.Errorf("could not get highest bid for auction %s: %s", auctionId, err)
|
||||
}
|
||||
|
||||
// Remaining Unpaid: Full bid amount
|
||||
auction.Remaining = auction.Best.Amount
|
||||
|
||||
// clear auction
|
||||
if err := memdb.BidDB.ClearAuction(auctionId); err != nil {
|
||||
return errors.Errorf("failed to clear auction from memcache: %s", err)
|
||||
}
|
||||
|
||||
// pay out unpaid remainder to auction creator
|
||||
ceiling := new(big.Int)
|
||||
ceiling.SetString(auction.Ceiling, 10)
|
||||
lowestBidAmt := new(big.Int)
|
||||
lowestBidAmt.SetString(auction.Best.Amount, 10)
|
||||
// only pay out if there is a difference
|
||||
if ceiling.Cmp(lowestBidAmt) == 1 {
|
||||
amtRemaining := new(big.Int)
|
||||
amtRemaining.Sub(ceiling, lowestBidAmt)
|
||||
coins := sdk.NewCoins(sdk.NewCoin(
|
||||
auction.Denom,
|
||||
sdk.NewIntFromBigInt(amtRemaining),
|
||||
))
|
||||
recipAddr, err := sdk.AccAddressFromBech32(auction.Owner)
|
||||
if err != nil {
|
||||
return errors.Errorf("failed to parse address %s", auction.Owner)
|
||||
}
|
||||
if err := k.bank.SendCoinsFromModuleToAccount(ctx, types.ModuleName, recipAddr, coins); err != nil {
|
||||
log.Printf("Failed to send coins from module: %s\n", err)
|
||||
// log.Fatalf("Failed to send coins from module: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// lease period starts now
|
||||
auction.LeaseStart = uint64(ctx.BlockTime().Unix())
|
||||
|
||||
// end auction
|
||||
k.SetAuction(ctx, auction)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (k *Keeper) PayAuctionAmountDue(ctx sdk.Context, auctionId string) error {
|
||||
auction, found := k.GetAuction(ctx, auctionId)
|
||||
if !found {
|
||||
return errors.Errorf("auction %s not found", auctionId)
|
||||
}
|
||||
|
||||
blockTime := ctx.BlockTime()
|
||||
deadline := time.Unix(int64(auction.Deadline), 0)
|
||||
|
||||
if blockTime.After(deadline) {
|
||||
return nil
|
||||
} else {
|
||||
amtTotal := new(big.Int)
|
||||
amtTotal.SetString(auction.Best.Amount, 10)
|
||||
amtRemaining := new(big.Int)
|
||||
amtTotal.SetString(auction.Remaining, 10)
|
||||
amt, err := math.CalcAmountVestableLinear(
|
||||
amtTotal,
|
||||
amtRemaining,
|
||||
ctx.BlockTime(),
|
||||
time.Unix(int64(auction.LeaseStart), 0),
|
||||
time.Unix(int64(auction.LeaseEnd), 0),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
coins := sdk.NewCoins(sdk.NewCoin(auction.Denom, sdk.NewIntFromBigInt(amt)))
|
||||
err = k.bank.SendCoinsFromModuleToAccount(ctx, "colinear", sdk.AccAddress(auction.Best.Owner), coins)
|
||||
return err
|
||||
}
|
||||
}
|
||||
64
x/colinearcore/keeper/auction_test.go
Normal file
64
x/colinearcore/keeper/auction_test.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
keepertest "colinear/testutil/keeper"
|
||||
"colinear/testutil/nullify"
|
||||
"colinear/x/colinearcore/keeper"
|
||||
"colinear/x/colinearcore/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Prevent strconv unused error
|
||||
var _ = strconv.IntSize
|
||||
|
||||
func createNAuction(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.Auction {
|
||||
items := make([]types.Auction, n)
|
||||
for i := range items {
|
||||
items[i].Index = strconv.Itoa(i)
|
||||
|
||||
keeper.SetAuction(ctx, items[i])
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func TestAuctionGet(t *testing.T) {
|
||||
keeper, ctx := keepertest.ColinearcoreKeeper(t)
|
||||
items := createNAuction(keeper, ctx, 10)
|
||||
for _, item := range items {
|
||||
rst, found := keeper.GetAuction(ctx,
|
||||
item.Index,
|
||||
)
|
||||
require.True(t, found)
|
||||
require.Equal(t,
|
||||
nullify.Fill(&item),
|
||||
nullify.Fill(&rst),
|
||||
)
|
||||
}
|
||||
}
|
||||
func TestAuctionRemove(t *testing.T) {
|
||||
keeper, ctx := keepertest.ColinearcoreKeeper(t)
|
||||
items := createNAuction(keeper, ctx, 10)
|
||||
for _, item := range items {
|
||||
keeper.RemoveAuction(ctx,
|
||||
item.Index,
|
||||
)
|
||||
_, found := keeper.GetAuction(ctx,
|
||||
item.Index,
|
||||
)
|
||||
require.False(t, found)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuctionGetAll(t *testing.T) {
|
||||
keeper, ctx := keepertest.ColinearcoreKeeper(t)
|
||||
items := createNAuction(keeper, ctx, 10)
|
||||
require.ElementsMatch(t,
|
||||
nullify.Fill(items),
|
||||
nullify.Fill(keeper.GetAllAuction(ctx)),
|
||||
)
|
||||
}
|
||||
7
x/colinearcore/keeper/grpc_query.go
Normal file
7
x/colinearcore/keeper/grpc_query.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"colinear/x/colinearcore/types"
|
||||
)
|
||||
|
||||
var _ types.QueryServer = Keeper{}
|
||||
58
x/colinearcore/keeper/grpc_query_auction.go
Normal file
58
x/colinearcore/keeper/grpc_query_auction.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"colinear/x/colinearcore/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/query"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func (k Keeper) AuctionAll(c context.Context, req *types.QueryAllAuctionRequest) (*types.QueryAllAuctionResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "invalid request")
|
||||
}
|
||||
|
||||
var auctions []types.Auction
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
auctionStore := prefix.NewStore(store, types.KeyPrefix(types.AuctionKeyPrefix))
|
||||
|
||||
pageRes, err := query.Paginate(auctionStore, req.Pagination, func(key []byte, value []byte) error {
|
||||
var auction types.Auction
|
||||
if err := k.cdc.Unmarshal(value, &auction); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
auctions = append(auctions, auction)
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
return &types.QueryAllAuctionResponse{Auction: auctions, Pagination: pageRes}, nil
|
||||
}
|
||||
|
||||
func (k Keeper) Auction(c context.Context, req *types.QueryGetAuctionRequest) (*types.QueryGetAuctionResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "invalid request")
|
||||
}
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
|
||||
val, found := k.GetAuction(
|
||||
ctx,
|
||||
req.Index,
|
||||
)
|
||||
if !found {
|
||||
return nil, status.Error(codes.NotFound, "not found")
|
||||
}
|
||||
|
||||
return &types.QueryGetAuctionResponse{Auction: val}, nil
|
||||
}
|
||||
37
x/colinearcore/keeper/grpc_query_auction_bids.go
Normal file
37
x/colinearcore/keeper/grpc_query_auction_bids.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"colinear/x/colinearcore/memdb"
|
||||
"colinear/x/colinearcore/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func (k Keeper) AuctionBids(goCtx context.Context, req *types.QueryAuctionBidsRequest) (*types.QueryAuctionBidsResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "invalid request")
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(goCtx)
|
||||
|
||||
auction, found := k.GetAuction(ctx, req.Index)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("auction %s not found", req.Index)
|
||||
}
|
||||
|
||||
if uint64(ctx.BlockHeight()) >= auction.Deadline {
|
||||
return nil, fmt.Errorf("auction %s is already finalized", req.Index)
|
||||
}
|
||||
|
||||
bids, err := memdb.BidDB.GetBids(auction.Index)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get bids for auction %s: %s", auction.Index, err)
|
||||
}
|
||||
|
||||
return &types.QueryAuctionBidsResponse{Bids: bids}, nil
|
||||
}
|
||||
126
x/colinearcore/keeper/grpc_query_auction_test.go
Normal file
126
x/colinearcore/keeper/grpc_query_auction_test.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/query"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
keepertest "colinear/testutil/keeper"
|
||||
"colinear/testutil/nullify"
|
||||
"colinear/x/colinearcore/types"
|
||||
)
|
||||
|
||||
// Prevent strconv unused error
|
||||
var _ = strconv.IntSize
|
||||
|
||||
func TestAuctionQuerySingle(t *testing.T) {
|
||||
keeper, ctx := keepertest.ColinearcoreKeeper(t)
|
||||
wctx := sdk.WrapSDKContext(ctx)
|
||||
msgs := createNAuction(keeper, ctx, 2)
|
||||
for _, tc := range []struct {
|
||||
desc string
|
||||
request *types.QueryGetAuctionRequest
|
||||
response *types.QueryGetAuctionResponse
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "First",
|
||||
request: &types.QueryGetAuctionRequest{
|
||||
Index: msgs[0].Index,
|
||||
},
|
||||
response: &types.QueryGetAuctionResponse{Auction: msgs[0]},
|
||||
},
|
||||
{
|
||||
desc: "Second",
|
||||
request: &types.QueryGetAuctionRequest{
|
||||
Index: msgs[1].Index,
|
||||
},
|
||||
response: &types.QueryGetAuctionResponse{Auction: msgs[1]},
|
||||
},
|
||||
{
|
||||
desc: "KeyNotFound",
|
||||
request: &types.QueryGetAuctionRequest{
|
||||
Index: strconv.Itoa(100000),
|
||||
},
|
||||
err: status.Error(codes.NotFound, "not found"),
|
||||
},
|
||||
{
|
||||
desc: "InvalidRequest",
|
||||
err: status.Error(codes.InvalidArgument, "invalid request"),
|
||||
},
|
||||
} {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
response, err := keeper.Auction(wctx, tc.request)
|
||||
if tc.err != nil {
|
||||
require.ErrorIs(t, err, tc.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t,
|
||||
nullify.Fill(tc.response),
|
||||
nullify.Fill(response),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuctionQueryPaginated(t *testing.T) {
|
||||
keeper, ctx := keepertest.ColinearcoreKeeper(t)
|
||||
wctx := sdk.WrapSDKContext(ctx)
|
||||
msgs := createNAuction(keeper, ctx, 5)
|
||||
|
||||
request := func(next []byte, offset, limit uint64, total bool) *types.QueryAllAuctionRequest {
|
||||
return &types.QueryAllAuctionRequest{
|
||||
Pagination: &query.PageRequest{
|
||||
Key: next,
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
CountTotal: total,
|
||||
},
|
||||
}
|
||||
}
|
||||
t.Run("ByOffset", func(t *testing.T) {
|
||||
step := 2
|
||||
for i := 0; i < len(msgs); i += step {
|
||||
resp, err := keeper.AuctionAll(wctx, request(nil, uint64(i), uint64(step), false))
|
||||
require.NoError(t, err)
|
||||
require.LessOrEqual(t, len(resp.Auction), step)
|
||||
require.Subset(t,
|
||||
nullify.Fill(msgs),
|
||||
nullify.Fill(resp.Auction),
|
||||
)
|
||||
}
|
||||
})
|
||||
t.Run("ByKey", func(t *testing.T) {
|
||||
step := 2
|
||||
var next []byte
|
||||
for i := 0; i < len(msgs); i += step {
|
||||
resp, err := keeper.AuctionAll(wctx, request(next, 0, uint64(step), false))
|
||||
require.NoError(t, err)
|
||||
require.LessOrEqual(t, len(resp.Auction), step)
|
||||
require.Subset(t,
|
||||
nullify.Fill(msgs),
|
||||
nullify.Fill(resp.Auction),
|
||||
)
|
||||
next = resp.Pagination.NextKey
|
||||
}
|
||||
})
|
||||
t.Run("Total", func(t *testing.T) {
|
||||
resp, err := keeper.AuctionAll(wctx, request(nil, 0, 0, true))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(msgs), int(resp.Pagination.Total))
|
||||
require.ElementsMatch(t,
|
||||
nullify.Fill(msgs),
|
||||
nullify.Fill(resp.Auction),
|
||||
)
|
||||
})
|
||||
t.Run("InvalidRequest", func(t *testing.T) {
|
||||
_, err := keeper.AuctionAll(wctx, nil)
|
||||
require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request"))
|
||||
})
|
||||
}
|
||||
25
x/colinearcore/keeper/grpc_query_next_auction.go
Normal file
25
x/colinearcore/keeper/grpc_query_next_auction.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"colinear/x/colinearcore/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func (k Keeper) NextAuction(c context.Context, req *types.QueryGetNextAuctionRequest) (*types.QueryGetNextAuctionResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "invalid request")
|
||||
}
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
|
||||
val, found := k.GetNextAuction(ctx)
|
||||
if !found {
|
||||
return nil, status.Error(codes.NotFound, "not found")
|
||||
}
|
||||
|
||||
return &types.QueryGetNextAuctionResponse{NextAuction: val}, nil
|
||||
}
|
||||
49
x/colinearcore/keeper/grpc_query_next_auction_test.go
Normal file
49
x/colinearcore/keeper/grpc_query_next_auction_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
keepertest "colinear/testutil/keeper"
|
||||
"colinear/testutil/nullify"
|
||||
"colinear/x/colinearcore/types"
|
||||
)
|
||||
|
||||
func TestNextAuctionQuery(t *testing.T) {
|
||||
keeper, ctx := keepertest.ColinearcoreKeeper(t)
|
||||
wctx := sdk.WrapSDKContext(ctx)
|
||||
item := createTestNextAuction(keeper, ctx)
|
||||
for _, tc := range []struct {
|
||||
desc string
|
||||
request *types.QueryGetNextAuctionRequest
|
||||
response *types.QueryGetNextAuctionResponse
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "First",
|
||||
request: &types.QueryGetNextAuctionRequest{},
|
||||
response: &types.QueryGetNextAuctionResponse{NextAuction: item},
|
||||
},
|
||||
{
|
||||
desc: "InvalidRequest",
|
||||
err: status.Error(codes.InvalidArgument, "invalid request"),
|
||||
},
|
||||
} {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
response, err := keeper.NextAuction(wctx, tc.request)
|
||||
if tc.err != nil {
|
||||
require.ErrorIs(t, err, tc.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t,
|
||||
nullify.Fill(tc.response),
|
||||
nullify.Fill(response),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
20
x/colinearcore/keeper/grpc_query_params.go
Normal file
20
x/colinearcore/keeper/grpc_query_params.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"colinear/x/colinearcore/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func (k Keeper) Params(c context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "invalid request")
|
||||
}
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
|
||||
return &types.QueryParamsResponse{Params: k.GetParams(ctx)}, nil
|
||||
}
|
||||
22
x/colinearcore/keeper/grpc_query_params_test.go
Normal file
22
x/colinearcore/keeper/grpc_query_params_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
testkeeper "colinear/testutil/keeper"
|
||||
"colinear/x/colinearcore/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParamsQuery(t *testing.T) {
|
||||
keeper, ctx := testkeeper.ColinearcoreKeeper(t)
|
||||
wctx := sdk.WrapSDKContext(ctx)
|
||||
params := types.DefaultParams()
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
response, err := keeper.Params(wctx, &types.QueryParamsRequest{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, &types.QueryParamsResponse{Params: params}, response)
|
||||
}
|
||||
48
x/colinearcore/keeper/keeper.go
Normal file
48
x/colinearcore/keeper/keeper.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
"colinear/x/colinearcore/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
||||
)
|
||||
|
||||
type (
|
||||
Keeper struct {
|
||||
cdc codec.BinaryCodec
|
||||
storeKey sdk.StoreKey
|
||||
memKey sdk.StoreKey
|
||||
paramstore paramtypes.Subspace
|
||||
bank types.BankKeeper
|
||||
}
|
||||
)
|
||||
|
||||
func NewKeeper(
|
||||
cdc codec.BinaryCodec,
|
||||
storeKey,
|
||||
memKey sdk.StoreKey,
|
||||
ps paramtypes.Subspace,
|
||||
bank types.BankKeeper,
|
||||
) *Keeper {
|
||||
// set KeyTable if it has not already been set
|
||||
if !ps.HasKeyTable() {
|
||||
ps = ps.WithKeyTable(types.ParamKeyTable())
|
||||
}
|
||||
|
||||
return &Keeper{
|
||||
cdc: cdc,
|
||||
storeKey: storeKey,
|
||||
memKey: memKey,
|
||||
paramstore: ps,
|
||||
bank: bank,
|
||||
}
|
||||
}
|
||||
|
||||
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
|
||||
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
|
||||
}
|
||||
49
x/colinearcore/keeper/keeper_integration_test.go.bak
Normal file
49
x/colinearcore/keeper/keeper_integration_test.go.bak
Normal file
@@ -0,0 +1,49 @@
|
||||
// Reference file.
|
||||
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"colinear/x/colinearcore"
|
||||
"colinear/x/colinearcore/types"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
"github.com/stretchr/testify/suite"
|
||||
tendermintTypes "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
// Integration Tests Tutorial:
|
||||
// https://tutorials.cosmos.network/academy/3-my-own-chain/game-wager.html#integration-tests
|
||||
|
||||
// Example integration test suite:
|
||||
// https://github.com/cosmos/cosmos-sdk/blob/9e1ec7b/x/bank/keeper/keeper_test.go#L66-L110
|
||||
|
||||
type IntegrationTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
app colinear.AppModule
|
||||
msgServer types.MsgServer
|
||||
ctx sdk.Context
|
||||
queryClient types.QueryClient
|
||||
}
|
||||
|
||||
func (suite *IntegrationTestSuite) SetupTest() {
|
||||
suite.app = colinear.AppModule{
|
||||
Keeper: NewKeeper(cdc)
|
||||
}
|
||||
suite.ctx = suite.app.NewContext(false, tendermintTypes.Header{Time: time.Now()})
|
||||
suite.app.AccountKeeper.SetParams(suite.ctx, authtypes.DefaultParams())
|
||||
suite.app.BankKeeper.SetParams(suite.ctx, banktypes.DefaultParams())
|
||||
queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry())
|
||||
types.RegisterQueryServer(queryHelper)
|
||||
suite.queryClient = types.NewQueryClient(queryHelper)
|
||||
}
|
||||
|
||||
func TestKeeperTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(IntegrationTestSuite))
|
||||
}
|
||||
17
x/colinearcore/keeper/msg_server.go
Normal file
17
x/colinearcore/keeper/msg_server.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"colinear/x/colinearcore/types"
|
||||
)
|
||||
|
||||
type msgServer struct {
|
||||
Keeper
|
||||
}
|
||||
|
||||
// NewMsgServerImpl returns an implementation of the MsgServer interface
|
||||
// for the provided Keeper.
|
||||
func NewMsgServerImpl(keeper Keeper) types.MsgServer {
|
||||
return &msgServer{Keeper: keeper}
|
||||
}
|
||||
|
||||
var _ types.MsgServer = msgServer{}
|
||||
91
x/colinearcore/keeper/msg_server_new_auction.go
Normal file
91
x/colinearcore/keeper/msg_server_new_auction.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"colinear/x/colinearcore/auctionconfig"
|
||||
"colinear/x/colinearcore/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func (k msgServer) NewAuction(goCtx context.Context, msg *types.MsgNewAuction) (*types.MsgNewAuctionResponse, error) {
|
||||
ctx := sdk.UnwrapSDKContext(goCtx)
|
||||
|
||||
next, found := k.Keeper.GetNextAuction(ctx)
|
||||
if !found {
|
||||
return nil, errors.New("unable to get next auction index")
|
||||
}
|
||||
index := strconv.FormatUint(next.AuctionId, 10)
|
||||
|
||||
auctionLen := msg.LeaseEnd - uint64(ctx.BlockTime().Unix())
|
||||
|
||||
if auctionLen < auctionconfig.MinLeasePeriod {
|
||||
return nil, fmt.Errorf(
|
||||
"Auction length %d is below min lease period of %d",
|
||||
auctionLen,
|
||||
auctionconfig.MinLeasePeriod,
|
||||
)
|
||||
}
|
||||
|
||||
if auctionLen > auctionconfig.MaxLeasePeriod {
|
||||
return nil, fmt.Errorf(
|
||||
"Auction length %d is above max lease period of %d",
|
||||
auctionLen,
|
||||
auctionconfig.MaxLeasePeriod,
|
||||
)
|
||||
}
|
||||
|
||||
auction := types.Auction{
|
||||
Index: index,
|
||||
Name: msg.Name,
|
||||
Description: msg.Description,
|
||||
// best bid -> null
|
||||
// Best: new(types.Bid),
|
||||
Deadline: uint64(ctx.BlockHeight()) + auctionconfig.AuctionTime,
|
||||
Denom: msg.Denom,
|
||||
Owner: msg.Creator,
|
||||
Ceiling: msg.Ceiling,
|
||||
// lease start -> null
|
||||
// instead, initialize when auction is finalized
|
||||
// LeaseStart: uint64(ctx.BlockTime().Unix()),
|
||||
LeaseEnd: msg.LeaseEnd,
|
||||
// remaining payout -> null
|
||||
// Remaining: "0",
|
||||
}
|
||||
|
||||
senderAddr, err := sdk.AccAddressFromBech32(msg.Creator)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sender address `%s` format invalid (bech32 required)", msg.Creator)
|
||||
}
|
||||
|
||||
spendable := k.bank.SpendableCoins(ctx, senderAddr)
|
||||
// if balance does not exceed or equal proposed auction ceiling...
|
||||
ceiling := new(big.Int)
|
||||
ceiling.SetString(auction.Ceiling, 10)
|
||||
if spendable.AmountOf(auction.Denom).BigInt().Cmp(ceiling) == -1 {
|
||||
return nil, fmt.Errorf("not enough balance to set ceiling %s%s", msg.Ceiling, auction.Denom)
|
||||
}
|
||||
|
||||
coins := sdk.NewCoins(sdk.Coin{
|
||||
Amount: sdk.NewIntFromBigInt(ceiling),
|
||||
Denom: auction.Denom,
|
||||
})
|
||||
|
||||
if err := k.Keeper.bank.SendCoinsFromAccountToModule(ctx, senderAddr, types.ModuleName, coins); err != nil {
|
||||
return nil, fmt.Errorf("failed to transfer %s%s", auction.Ceiling, auction.Denom)
|
||||
}
|
||||
|
||||
k.Keeper.SetAuction(ctx, auction)
|
||||
next.AuctionId++
|
||||
|
||||
k.Keeper.SetNextAuction(ctx, types.NextAuction{AuctionId: next.AuctionId})
|
||||
|
||||
return &types.MsgNewAuctionResponse{
|
||||
AuctionId: strconv.FormatUint(next.AuctionId, 10),
|
||||
}, nil
|
||||
}
|
||||
78
x/colinearcore/keeper/msg_server_new_bid.go
Normal file
78
x/colinearcore/keeper/msg_server_new_bid.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
|
||||
"colinear/x/colinearcore/memdb"
|
||||
"colinear/x/colinearcore/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func (k msgServer) NewBid(goCtx context.Context, msg *types.MsgNewBid) (*types.MsgNewBidResponse, error) {
|
||||
ctx := sdk.UnwrapSDKContext(goCtx)
|
||||
|
||||
auction, found := k.Keeper.GetAuction(ctx, msg.AuctionIndex)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("didn't find auction of index %s", msg.AuctionIndex)
|
||||
}
|
||||
|
||||
auctionExpired, err := k.Keeper.AuctionIsExpired(ctx, msg.AuctionIndex)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while checking auction %s expiry status: %s", msg.AuctionIndex, err)
|
||||
}
|
||||
if auctionExpired {
|
||||
return nil, fmt.Errorf("auction %s is expired", msg.AuctionIndex)
|
||||
}
|
||||
|
||||
ok := false
|
||||
amt := new(big.Int)
|
||||
amt, ok = amt.SetString(msg.Amount, 10)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to convert `%s` to a large integer", msg.Amount)
|
||||
}
|
||||
if amt.Sign() != 1 {
|
||||
return nil, fmt.Errorf("bid amount must be greater than 0")
|
||||
}
|
||||
|
||||
ceiling := new(big.Int)
|
||||
ceiling.SetString(auction.Ceiling, 10)
|
||||
if amt.Cmp(ceiling) == 1 {
|
||||
return nil, fmt.Errorf("bid amount cannot be greater than auction price ceiling (%s)", auction.Ceiling)
|
||||
}
|
||||
|
||||
lowestBid, err := memdb.BidDB.GetLowestBid(msg.AuctionIndex)
|
||||
// we manually handle KeyNotFound in GetHighestBid, so should return (nil, nil) if not found
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get lowest bid: %s", reflect.TypeOf(err))
|
||||
}
|
||||
|
||||
if lowestBid != nil {
|
||||
amtPrev := new(big.Int)
|
||||
amtPrev, ok = amtPrev.SetString(lowestBid.Amount, 10)
|
||||
if !ok { // this should have been checked before, but whatever
|
||||
return nil, fmt.Errorf("failed to convert max bid (%s) to a large integer", msg.Amount)
|
||||
}
|
||||
|
||||
if amt.Cmp(amtPrev) != -1 {
|
||||
return nil, fmt.Errorf("bid amount must be less than current lowest bid (%s)", amtPrev)
|
||||
}
|
||||
}
|
||||
|
||||
bid := &types.Bid{
|
||||
Amount: msg.Amount,
|
||||
Owner: msg.Creator,
|
||||
}
|
||||
|
||||
if err := memdb.BidDB.AddBid(msg.AuctionIndex, bid); err != nil {
|
||||
return nil, fmt.Errorf("failed to add bid: %s", err)
|
||||
}
|
||||
|
||||
// auction.Bids = append(auction.Bids, bid)
|
||||
// k.Keeper.SetAuction(ctx, auction)
|
||||
|
||||
return &types.MsgNewBidResponse{}, nil
|
||||
}
|
||||
17
x/colinearcore/keeper/msg_server_test.go
Normal file
17
x/colinearcore/keeper/msg_server_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
keepertest "colinear/testutil/keeper"
|
||||
"colinear/x/colinearcore/keeper"
|
||||
"colinear/x/colinearcore/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func setupMsgServer(t testing.TB) (types.MsgServer, context.Context) {
|
||||
k, ctx := keepertest.ColinearcoreKeeper(t)
|
||||
return keeper.NewMsgServerImpl(*k), sdk.WrapSDKContext(ctx)
|
||||
}
|
||||
34
x/colinearcore/keeper/next_auction.go
Normal file
34
x/colinearcore/keeper/next_auction.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"colinear/x/colinearcore/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// SetNextAuction set nextAuction in the store
|
||||
func (k Keeper) SetNextAuction(ctx sdk.Context, nextAuction types.NextAuction) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.NextAuctionKey))
|
||||
b := k.cdc.MustMarshal(&nextAuction)
|
||||
store.Set([]byte{0}, b)
|
||||
}
|
||||
|
||||
// GetNextAuction returns nextAuction
|
||||
func (k Keeper) GetNextAuction(ctx sdk.Context) (val types.NextAuction, found bool) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.NextAuctionKey))
|
||||
|
||||
b := store.Get([]byte{0})
|
||||
if b == nil {
|
||||
return val, false
|
||||
}
|
||||
|
||||
k.cdc.MustUnmarshal(b, &val)
|
||||
return val, true
|
||||
}
|
||||
|
||||
// RemoveNextAuction removes nextAuction from the store
|
||||
func (k Keeper) RemoveNextAuction(ctx sdk.Context) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.NextAuctionKey))
|
||||
store.Delete([]byte{0})
|
||||
}
|
||||
38
x/colinearcore/keeper/next_auction_test.go
Normal file
38
x/colinearcore/keeper/next_auction_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
keepertest "colinear/testutil/keeper"
|
||||
"colinear/testutil/nullify"
|
||||
"colinear/x/colinearcore/keeper"
|
||||
"colinear/x/colinearcore/types"
|
||||
)
|
||||
|
||||
func createTestNextAuction(keeper *keeper.Keeper, ctx sdk.Context) types.NextAuction {
|
||||
item := types.NextAuction{}
|
||||
keeper.SetNextAuction(ctx, item)
|
||||
return item
|
||||
}
|
||||
|
||||
func TestNextAuctionGet(t *testing.T) {
|
||||
keeper, ctx := keepertest.ColinearcoreKeeper(t)
|
||||
item := createTestNextAuction(keeper, ctx)
|
||||
rst, found := keeper.GetNextAuction(ctx)
|
||||
require.True(t, found)
|
||||
require.Equal(t,
|
||||
nullify.Fill(&item),
|
||||
nullify.Fill(&rst),
|
||||
)
|
||||
}
|
||||
|
||||
func TestNextAuctionRemove(t *testing.T) {
|
||||
keeper, ctx := keepertest.ColinearcoreKeeper(t)
|
||||
createTestNextAuction(keeper, ctx)
|
||||
keeper.RemoveNextAuction(ctx)
|
||||
_, found := keeper.GetNextAuction(ctx)
|
||||
require.False(t, found)
|
||||
}
|
||||
17
x/colinearcore/keeper/params.go
Normal file
17
x/colinearcore/keeper/params.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"colinear/x/colinearcore/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// GetParams get all parameters as types.Params
|
||||
func (k Keeper) GetParams(ctx sdk.Context) types.Params {
|
||||
return types.NewParams()
|
||||
}
|
||||
|
||||
// SetParams set the params
|
||||
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
|
||||
k.paramstore.SetParamSet(ctx, ¶ms)
|
||||
}
|
||||
19
x/colinearcore/keeper/params_test.go
Normal file
19
x/colinearcore/keeper/params_test.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
testkeeper "colinear/testutil/keeper"
|
||||
"colinear/x/colinearcore/types"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetParams(t *testing.T) {
|
||||
k, ctx := testkeeper.ColinearcoreKeeper(t)
|
||||
params := types.DefaultParams()
|
||||
|
||||
k.SetParams(ctx, params)
|
||||
|
||||
require.EqualValues(t, params, k.GetParams(ctx))
|
||||
}
|
||||
Reference in New Issue
Block a user