Auction rest fixes (#361)

* auction query fixes

* minor formating tweaks

* add phase info to debt & surplus auctions

* add base auction struct tags

* use module name in querier paths

* update rest tx endoint to use http body

* improve invalid coins error messages

* add rest examples

* align starting auction ID with gov and cdp

* fix cdp test broken by auction ID change

* fix all tests broken by ID change
This commit is contained in:
Ruaridh 2020-02-03 15:54:00 +00:00 committed by GitHub
parent 31e185c632
commit bbc5d3b7a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 202 additions and 106 deletions

View File

@ -0,0 +1,16 @@
{
"base_req": {
"from": "kava1xy7hrjy9r0algz9w3gzm8u6mrpq97kwta747gj",
"memo": "arbitrary text",
"chain_id": "testing",
"account_number": "0",
"sequence": "2",
"gas": "500000",
"gas_adjustment": "1.0",
"simulate": false
},
"amount": {
"denom": "usdx",
"amount": "100000000"
}
}

View File

@ -0,0 +1,32 @@
{
"type": "cosmos-sdk/StdTx",
"value": {
"msg": [
{
"type": "auction/MsgPlaceBid",
"value": {
"auction_id": "1",
"bidder": "kava1xy7hrjy9r0algz9w3gzm8u6mrpq97kwta747gj",
"amount": {
"denom": "usdx",
"amount": "100000000"
}
}
}
],
"fee": {
"amount": [],
"gas": "500000"
},
"signatures": [
{
"pub_key": {
"type": "tendermint/PubKeySecp256k1",
"value": "AsBYqm9S7cjIBdG6N/xzAGuEizTt2S2CJuM1FCsgta7u"
},
"signature": "O3zsB8P61SN7/5C+TNfO9hMaFYWV6hjr2aVigN0GrckR2PQGZhpyEZ03P71A9uK7DFLawzvAHDZRqQu833lAnA=="
}
],
"memo": "arbitrary text"
}
}

View File

@ -0,0 +1,24 @@
{
"type": "cosmos-sdk/StdTx",
"value": {
"msg": [
{
"type": "auction/MsgPlaceBid",
"value": {
"auction_id": "1",
"bidder": "kava1xy7hrjy9r0algz9w3gzm8u6mrpq97kwta747gj",
"amount": {
"denom": "usdx",
"amount": "100000000"
}
}
}
],
"fee": {
"amount": [],
"gas": "500000"
},
"signatures": null,
"memo": "arbitrary text"
}
}

View File

@ -0,0 +1,32 @@
{
"tx": {
"msg": [
{
"type": "auction/MsgPlaceBid",
"value": {
"auction_id": "1",
"bidder": "kava1xy7hrjy9r0algz9w3gzm8u6mrpq97kwta747gj",
"amount": {
"denom": "usdx",
"amount": "100000000"
}
}
}
],
"fee": {
"amount": [],
"gas": "500000"
},
"signatures": [
{
"pub_key": {
"type": "tendermint/PubKeySecp256k1",
"value": "AsBYqm9S7cjIBdG6N/xzAGuEizTt2S2CJuM1FCsgta7u"
},
"signature": "O3zsB8P61SN7/5C+TNfO9hMaFYWV6hjr2aVigN0GrckR2PQGZhpyEZ03P71A9uK7DFLawzvAHDZRqQu833lAnA=="
}
],
"memo": "arbitrary text"
},
"mode": "block"
}

View File

@ -31,6 +31,7 @@ const (
DefaultMaxAuctionDuration = types.DefaultMaxAuctionDuration
DefaultBidDuration = types.DefaultBidDuration
QueryGetAuction = types.QueryGetAuction
DefaultNextAuctionID = types.DefaultNextAuctionID
)
var (

View File

@ -85,7 +85,7 @@ func QueryGetAuctionsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
var auctions types.Auctions
cdc.MustUnmarshalJSON(res, &auctions)
var auctionsWithPhase []types.AuctionWithPhase
auctionsWithPhase := []types.AuctionWithPhase{} // using empty slice so json returns [] instead of null when there's no auctions
for _, a := range auctions {
auctionsWithPhase = append(auctionsWithPhase, types.NewAuctionWithPhase(a))
}

View File

@ -12,12 +12,12 @@ import (
"github.com/kava-labs/kava/x/auction/types"
)
const RestAuctionID = "auction-id"
const restAuctionID = "auction-id"
func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) {
r.HandleFunc(fmt.Sprintf("/auction/auctions/{%s}", RestAuctionID), queryAuctionHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc("/auction/auctions", queryAuctionsHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc("/auction/parameters", getParamsHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/{%s}/auctions", types.ModuleName), queryAuctionsHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/{%s}/auctions/{%s}", types.ModuleName, restAuctionID), queryAuctionHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/{%s}/parameters", types.ModuleName), getParamsHandlerFn(cliCtx)).Methods("GET")
}
func queryAuctionHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
@ -30,12 +30,12 @@ func queryAuctionHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
// Prepare params for querier
vars := mux.Vars(r)
if len(vars[RestAuctionID]) == 0 {
err := fmt.Errorf("%s required but not specified", RestAuctionID)
if len(vars[restAuctionID]) == 0 {
err := fmt.Errorf("%s required but not specified", restAuctionID)
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
auctionID, ok := rest.ParseUint64OrReturnBadRequest(w, vars[RestAuctionID])
auctionID, ok := rest.ParseUint64OrReturnBadRequest(w, vars[restAuctionID])
if !ok {
return
}
@ -51,17 +51,17 @@ func queryAuctionHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
// Decode and return results
cliCtx = cliCtx.WithHeight(height)
var auction types.Auction
err = cliCtx.Codec.UnmarshalJSON(res, &auction)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
// Decode and return results
cliCtx = cliCtx.WithHeight(height)
auctionWithPhase := types.NewAuctionWithPhase(auction)
rest.PostProcessResponse(w, cliCtx, cliCtx.Codec.MustMarshalJSON(auctionWithPhase))
}
}
@ -73,13 +73,15 @@ func queryAuctionsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
if !ok {
return
}
// Get all auctions
res, height, err := cliCtx.QueryWithData(fmt.Sprintf("/custom/%s/%s", types.ModuleName, types.QueryGetAuctions), nil)
if err != nil {
rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
return
}
// Return auctions
// Decode and return results
cliCtx = cliCtx.WithHeight(height)
var auctions types.Auctions
@ -89,11 +91,10 @@ func queryAuctionsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return
}
var auctionsWithPhase []types.AuctionWithPhase
auctionsWithPhase := []types.AuctionWithPhase{} // using empty slice so json returns [] instead of null when there's no auctions
for _, a := range auctions {
auctionsWithPhase = append(auctionsWithPhase, types.NewAuctionWithPhase(a))
}
rest.PostProcessResponse(w, cliCtx, cliCtx.Codec.MustMarshalJSON(auctionsWithPhase))
}
}
@ -111,7 +112,7 @@ func getParamsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
// Return the params
// Decode and return results
cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, res)
}

View File

@ -1,10 +1,8 @@
package rest
import (
"bytes"
"fmt"
"net/http"
"strconv"
"github.com/gorilla/mux"
@ -16,65 +14,45 @@ import (
"github.com/kava-labs/kava/x/auction/types"
)
type placeBidReq struct {
BaseReq rest.BaseReq `json:"base_req"`
AuctionID string `json:"auction_id"`
Bidder string `json:"bidder"`
Bid string `json:"bid"`
Lot string `json:"lot"`
func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) {
r.HandleFunc(fmt.Sprintf("/{%s}/auctions/{%s}/bids", types.ModuleName, restAuctionID), bidHandlerFn(cliCtx)).Methods("POST")
}
const (
restAuctionID = "auction_id"
restBidder = "bidder"
restBid = "bid"
restLot = "lot"
)
func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) {
r.HandleFunc(
fmt.Sprintf("/auction/bid/{%s}/{%s}/{%s}", restAuctionID, restBidder, restBid), bidHandlerFn(cliCtx)).Methods("PUT")
type placeBidReq struct {
BaseReq rest.BaseReq `json:"base_req"`
Amount sdk.Coin `json:"amount"`
}
func bidHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Get auction ID from url
auctionID, ok := rest.ParseUint64OrReturnBadRequest(w, mux.Vars(r)[restAuctionID])
if !ok {
return
}
// Get info from the http request body
var req placeBidReq
vars := mux.Vars(r)
strAuctionID := vars[restAuctionID]
bechBidder := vars[restBidder]
strBid := vars[restBid]
auctionID, err := strconv.ParseUint(strAuctionID, 10, 64)
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
return
}
req.BaseReq = req.BaseReq.Sanitize()
if !req.BaseReq.ValidateBasic(w) {
return
}
bidderAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
bidder, err := sdk.AccAddressFromBech32(bechBidder)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
bid, err := sdk.ParseCoin(strBid)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
msg := types.NewMsgPlaceBid(auctionID, bidder, bid)
// Create and return a StdTx
msg := types.NewMsgPlaceBid(auctionID, bidderAddr, req.Amount)
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From)
if !bytes.Equal(fromAddr, bidder) {
rest.WriteErrorResponse(w, http.StatusUnauthorized, "must bid from own address")
return
}
utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}

View File

@ -87,7 +87,7 @@ func TestDebtAuctionBasic(t *testing.T) {
tApp.CheckBalance(t, ctx, buyerAddr, cs(c("debt", 80)))
// Place a bid
require.NoError(t, keeper.PlaceBid(ctx, 0, seller, c("token2", 10)))
require.NoError(t, keeper.PlaceBid(ctx, auctionID, seller, c("token2", 10)))
// Check seller's coins have decreased
tApp.CheckBalance(t, ctx, seller, cs(c("token1", 80), c("token2", 100)))
// Check buyer's coins have increased
@ -127,7 +127,7 @@ func TestDebtAuctionDebtRemaining(t *testing.T) {
tApp.CheckBalance(t, ctx, buyerAddr, cs(c("debt", 80)))
// Place a bid
require.NoError(t, keeper.PlaceBid(ctx, 0, seller, c("token2", 10)))
require.NoError(t, keeper.PlaceBid(ctx, auctionID, seller, c("token2", 10)))
// Check seller's coins have decreased
tApp.CheckBalance(t, ctx, seller, cs(c("token1", 90), c("token2", 100)))
// Check buyer's coins have increased
@ -173,7 +173,7 @@ func TestCollateralAuctionBasic(t *testing.T) {
tApp.CheckBalance(t, ctx, sellerAddr, cs(c("token1", 80), c("token2", 100), c("debt", 60)))
// Place a forward bid
require.NoError(t, keeper.PlaceBid(ctx, 0, buyer, c("token2", 10)))
require.NoError(t, keeper.PlaceBid(ctx, auctionID, buyer, c("token2", 10)))
// Check bidder's coins have decreased
tApp.CheckBalance(t, ctx, buyer, cs(c("token1", 100), c("token2", 90)))
// Check seller's coins have increased
@ -184,8 +184,8 @@ func TestCollateralAuctionBasic(t *testing.T) {
}
// Place a reverse bid
require.NoError(t, keeper.PlaceBid(ctx, 0, buyer, c("token2", 50))) // first bid up to max bid to switch phases
require.NoError(t, keeper.PlaceBid(ctx, 0, buyer, c("token1", 15)))
require.NoError(t, keeper.PlaceBid(ctx, auctionID, buyer, c("token2", 50))) // first bid up to max bid to switch phases
require.NoError(t, keeper.PlaceBid(ctx, auctionID, buyer, c("token1", 15)))
// Check bidder's coins have decreased
tApp.CheckBalance(t, ctx, buyer, cs(c("token1", 100), c("token2", 50)))
// Check seller's coins have increased
@ -233,7 +233,7 @@ func TestCollateralAuctionDebtRemaining(t *testing.T) {
tApp.CheckBalance(t, ctx, sellerAddr, cs(c("token1", 80), c("token2", 100), c("debt", 60)))
// Place a forward bid
require.NoError(t, keeper.PlaceBid(ctx, 0, buyer, c("token2", 10)))
require.NoError(t, keeper.PlaceBid(ctx, auctionID, buyer, c("token2", 10)))
// Check bidder's coins have decreased
tApp.CheckBalance(t, ctx, buyer, cs(c("token1", 100), c("token2", 90)))
// Check seller's coins have increased
@ -322,7 +322,7 @@ func TestStartSurplusAuction(t *testing.T) {
// check auction in store and is correct
require.True(t, found)
expectedAuction := types.Auction(types.SurplusAuction{BaseAuction: types.BaseAuction{
ID: 0,
ID: id,
Initiator: tc.args.seller,
Lot: tc.args.lot,
Bidder: nil,

View File

@ -78,7 +78,7 @@ func (suite *QuerierTestSuite) TestQueryAuction() {
// Set up request query
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetAuction}, "/"),
Data: types.ModuleCdc.MustMarshalJSON(types.QueryAuctionParams{AuctionID: 0}), // get the first auction
Data: types.ModuleCdc.MustMarshalJSON(types.QueryAuctionParams{AuctionID: types.DefaultNextAuctionID}), // get the first auction
}
// Execute query and check the []byte result

View File

@ -17,12 +17,15 @@ var DistantFuture = time.Date(9000, 1, 1, 0, 0, 0, 0, time.UTC)
type Auction interface {
GetID() uint64
WithID(uint64) Auction
GetInitiator() string
GetLot() sdk.Coin
GetBidder() sdk.AccAddress
GetBid() sdk.Coin
GetEndTime() time.Time
GetType() string
GetPhase() string
}
// Auctions is a slice of auctions.
@ -86,7 +89,7 @@ func (a BaseAuction) String() string {
// SurplusAuction is a forward auction that burns what it receives from bids.
// It is normally used to sell off excess pegged asset acquired by the CDP system.
type SurplusAuction struct {
BaseAuction
BaseAuction `json:"base_auction" yaml:"base_auction"`
}
// WithID returns an auction with the ID set.
@ -102,6 +105,9 @@ func (a SurplusAuction) GetModuleAccountCoins() sdk.Coins {
return sdk.NewCoins(a.Lot)
}
// GetPhase returns the direction of a surplus auction, which never changes.
func (a SurplusAuction) GetPhase() string { return "forward" }
// NewSurplusAuction returns a new surplus auction.
func NewSurplusAuction(seller string, lot sdk.Coin, bidDenom string, endTime time.Time) SurplusAuction {
auction := SurplusAuction{BaseAuction{
@ -120,7 +126,7 @@ func NewSurplusAuction(seller string, lot sdk.Coin, bidDenom string, endTime tim
// DebtAuction is a reverse auction that mints what it pays out.
// It is normally used to acquire pegged asset to cover the CDP system's debts that were not covered by selling collateral.
type DebtAuction struct {
BaseAuction
BaseAuction `json:"base_auction" yaml:"base_auction"`
CorrespondingDebt sdk.Coin `json:"corresponding_debt" yaml:"corresponding_debt"`
}
@ -139,6 +145,9 @@ func (a DebtAuction) GetModuleAccountCoins() sdk.Coins {
return sdk.NewCoins(a.CorrespondingDebt)
}
// GetPhase returns the direction of a debt auction, which never changes.
func (a DebtAuction) GetPhase() string { return "reverse" }
// NewDebtAuction returns a new debt auction.
func NewDebtAuction(buyerModAccName string, bid sdk.Coin, initialLot sdk.Coin, endTime time.Time, debt sdk.Coin) DebtAuction {
// Note: Bidder is set to the initiator's module account address instead of module name. (when the first bid is placed, it is paid out to the initiator)
@ -165,7 +174,7 @@ func NewDebtAuction(buyerModAccName string, bid sdk.Coin, initialLot sdk.Coin, e
// Unsold Lot is sent to LotReturns, being divided among the addresses by weight.
// Collateral auctions are normally used to sell off collateral seized from CDPs.
type CollateralAuction struct {
BaseAuction
BaseAuction `json:"base_auction" yaml:"base_auction"`
CorrespondingDebt sdk.Coin `json:"corresponding_debt" yaml:"corresponding_debt"`
MaxBid sdk.Coin `json:"max_bid" yaml:"max_bid"`
@ -186,12 +195,12 @@ func (a CollateralAuction) GetModuleAccountCoins() sdk.Coins {
}
// IsReversePhase returns whether the auction has switched over to reverse phase or not.
// Auction initially start in forward phase.
// CollateralAuctions initially start in forward phase.
func (a CollateralAuction) IsReversePhase() bool {
return a.Bid.IsEqual(a.MaxBid)
}
// GetPhase returns the phase of a collateral auction
// GetPhase returns the direction of a collateral auction.
func (a CollateralAuction) GetPhase() string {
if a.IsReversePhase() {
return "reverse"

View File

@ -7,6 +7,9 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// DefaultNextAuctionID is the starting poiint for auction IDs.
const DefaultNextAuctionID uint64 = 1
// GenesisAuction is an interface that extends the auction interface to add functionality needed for initializing auctions from genesis.
type GenesisAuction interface {
Auction
@ -35,7 +38,11 @@ func NewGenesisState(nextID uint64, ap Params, ga GenesisAuctions) GenesisState
// DefaultGenesisState returns the default genesis state for auction module.
func DefaultGenesisState() GenesisState {
return NewGenesisState(0, DefaultParams(), GenesisAuctions{})
return NewGenesisState(
DefaultNextAuctionID,
DefaultParams(),
GenesisAuctions{},
)
}
// Equal checks whether two GenesisState structs are equivalent.

View File

@ -1,6 +1,9 @@
package types
import sdk "github.com/cosmos/cosmos-sdk/types"
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// ensure Msg interface compliance at compile time
var _ sdk.Msg = &MsgPlaceBid{}
@ -30,10 +33,10 @@ func (msg MsgPlaceBid) Type() string { return "place_bid" }
// ValidateBasic does a simple validation check that doesn't require access to state.
func (msg MsgPlaceBid) ValidateBasic() sdk.Error {
if msg.Bidder.Empty() {
return sdk.ErrInternal("invalid (empty) bidder address")
return sdk.ErrInvalidAddress("invalid (empty) bidder address")
}
if !msg.Amount.IsValid() {
return sdk.ErrInvalidCoins(msg.Amount.String())
return sdk.ErrInvalidCoins(fmt.Sprintf("invalid bid amount: %s", msg.Amount))
}
return nil
}

View File

@ -30,7 +30,7 @@ func NewQueryAllAuctionParams(page int, limit int) QueryAllAuctionParams {
// AuctionWithPhase augmented type for collateral auctions which includes auction phase for querying
type AuctionWithPhase struct {
Auction Auction
Auction Auction `json:"auction" yaml:"auction"`
Type string `json:"type" yaml:"type"`
Phase string `json:"phase" yaml:"phase"`
@ -38,17 +38,9 @@ type AuctionWithPhase struct {
// NewAuctionWithPhase returns new AuctionWithPhase
func NewAuctionWithPhase(a Auction) AuctionWithPhase {
switch auc := a.(type) {
case CollateralAuction:
return AuctionWithPhase{
Auction: auc,
Type: auc.GetType(),
Phase: auc.GetPhase(),
}
default:
return AuctionWithPhase{
Auction: auc,
Type: auc.GetType(),
}
Auction: a,
Type: a.GetType(),
Phase: a.GetPhase(),
}
}

View File

@ -7,13 +7,14 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/simulation"
"github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types"
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/auction"
"github.com/kava-labs/kava/x/cdp/keeper"
"github.com/kava-labs/kava/x/cdp/types"
"github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types"
tmtime "github.com/tendermint/tendermint/types/time"
)
type SeizeTestSuite struct {
@ -136,7 +137,7 @@ func (suite *SeizeTestSuite) TestSeizeCollateral() {
tpa := suite.keeper.GetTotalPrincipal(suite.ctx, "xrp", "usdx")
suite.Equal(tpb.Sub(tpa), p)
auctionKeeper := suite.app.GetAuctionKeeper()
_, found := auctionKeeper.GetAuction(suite.ctx, 0)
_, found := auctionKeeper.GetAuction(suite.ctx, auction.DefaultNextAuctionID)
suite.True(found)
auctionMacc := sk.GetModuleAccount(suite.ctx, auction.ModuleName)
suite.Equal(cs(c("debt", p.Int64()), c("xrp", cl.Int64())), auctionMacc.GetCoins())

View File

@ -46,16 +46,16 @@ func (msg MsgCreateCDP) ValidateBasic() sdk.Error {
return sdk.ErrInvalidCoins(fmt.Sprintf("cdps do not support multiple collateral types: received %s", msg.Collateral))
}
if !msg.Collateral.IsValid() {
return sdk.ErrInvalidCoins(msg.Collateral.String())
return sdk.ErrInvalidCoins(fmt.Sprintf("invalid collateral amount: %s", msg.Collateral))
}
if !msg.Collateral.IsAllPositive() {
return sdk.ErrInvalidCoins(msg.Collateral.String())
return sdk.ErrInvalidCoins(fmt.Sprintf("negative collateral amount: %s", msg.Collateral))
}
if !msg.Principal.IsValid() {
return sdk.ErrInvalidCoins(msg.Principal.String())
return sdk.ErrInvalidCoins(fmt.Sprintf("invalid principal amount: %s", msg.Principal))
}
if !msg.Principal.IsAllPositive() {
return sdk.ErrInvalidCoins(msg.Collateral.String())
return sdk.ErrInvalidCoins(fmt.Sprintf("negative principal amount: %s", msg.Principal))
}
return nil
}
@ -114,10 +114,10 @@ func (msg MsgDeposit) ValidateBasic() sdk.Error {
return sdk.ErrInvalidCoins(fmt.Sprintf("cdps do not support multiple collateral types: received %s", msg.Collateral))
}
if !msg.Collateral.IsValid() {
return sdk.ErrInvalidCoins(msg.Collateral.String())
return sdk.ErrInvalidCoins(fmt.Sprintf("invalid collateral amount: %s", msg.Collateral))
}
if !msg.Collateral.IsAllPositive() {
return sdk.ErrInvalidCoins(msg.Collateral.String())
return sdk.ErrInvalidCoins(fmt.Sprintf("negative collateral amount: %s", msg.Collateral))
}
return nil
}
@ -176,10 +176,10 @@ func (msg MsgWithdraw) ValidateBasic() sdk.Error {
return sdk.ErrInvalidCoins(fmt.Sprintf("cdps do not support multiple collateral types: received %s", msg.Collateral))
}
if !msg.Collateral.IsValid() {
return sdk.ErrInvalidCoins(msg.Collateral.String())
return sdk.ErrInvalidCoins(fmt.Sprintf("invalid collateral amount: %s", msg.Collateral))
}
if !msg.Collateral.IsAllPositive() {
return sdk.ErrInvalidCoins(msg.Collateral.String())
return sdk.ErrInvalidCoins(fmt.Sprintf("negative collateral amount: %s", msg.Collateral))
}
return nil
}
@ -235,10 +235,10 @@ func (msg MsgDrawDebt) ValidateBasic() sdk.Error {
return sdk.ErrInternal("invalid (empty) cdp denom")
}
if !msg.Principal.IsValid() {
return sdk.ErrInvalidCoins(msg.Principal.String())
return sdk.ErrInvalidCoins(fmt.Sprintf("invalid principal amount: %s", msg.Principal))
}
if !msg.Principal.IsAllPositive() {
return sdk.ErrInvalidCoins(msg.Principal.String())
return sdk.ErrInvalidCoins(fmt.Sprintf("negative principal amount: %s", msg.Principal))
}
return nil
}
@ -294,10 +294,10 @@ func (msg MsgRepayDebt) ValidateBasic() sdk.Error {
return sdk.ErrInternal("invalid (empty) cdp denom")
}
if !msg.Payment.IsValid() {
return sdk.ErrInvalidCoins(msg.Payment.String())
return sdk.ErrInvalidCoins(fmt.Sprintf("invalid payment amount: %s", msg.Payment))
}
if !msg.Payment.IsAllPositive() {
return sdk.ErrInvalidCoins(msg.Payment.String())
return sdk.ErrInvalidCoins(fmt.Sprintf("negative payment amount: %s", msg.Payment))
}
return nil
}