package keeper import ( "context" "errors" "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) lockedUsers, ok := k.Keeper.GetLockedUsers(ctx) if !ok { return nil, errors.New("unable to read locked providers (uninitialized)") } if lockedAmtStr, ok := lockedUsers.Users[msg.Creator]; !ok { return nil, fmt.Errorf("provider has not locked CLR tokens (min: %d uCLR)", k.GetParams(ctx).ProviderMinLockedUClr) } else { lockedAmt := new(big.Int) lockedAmt.SetString(lockedAmtStr, 10) required := big.NewInt(int64(k.GetParams(ctx).ProviderMinLockedUClr)) if lockedAmt.Cmp(required) == -1 { return nil, fmt.Errorf("provider has not locked enough CLR tokens (min: %d uCLR, locked: %s)", k.GetParams(ctx).ProviderMinLockedUClr, lockedAmt.String()) } } 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) } if verProvs, err := memdb.AuctionDB.GetVerifiedProviders(msg.AuctionIndex); err == nil { if len(verProvs) == 0 { goto bidderVerified } else { for _, provider := range verProvs { if msg.Creator == provider { goto bidderVerified } } return nil, fmt.Errorf("bid sender is not verified by the creator of auction %s", auction.Index) } } bidderVerified: 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.AuctionDB.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) } } // check that user has locked minimum required CLR bid := &types.Bid{ Amount: msg.Amount, Owner: msg.Creator, } if err := memdb.AuctionDB.AddBid(msg.AuctionIndex, bid); err != nil { return nil, fmt.Errorf("failed to add bid: %s", err) } // emit bid event ctx.EventManager().EmitEvent( sdk.NewEvent(types.BidCreatedEventType, sdk.NewAttribute(types.BidCreatedEventCreator, msg.Creator), sdk.NewAttribute(types.BidCreatedAuctionIndex, msg.AuctionIndex), sdk.NewAttribute(types.BidCreatedAmount, msg.Amount), ), ) return &types.MsgNewBidResponse{}, nil }