Skip to content

Latest commit

 

History

History
111 lines (93 loc) · 4.24 KB

buy-name.md

File metadata and controls

111 lines (93 loc) · 4.24 KB

Buy Name

Msg

Now it is time to define the Msg for buying names and add it to the ./x/nameservice/msgs.go file. This code is very similar to SetName:

// MsgBuyName defines the BuyName message
type MsgBuyName struct {
	NameID string
	Bid    sdk.Coins
	Buyer  sdk.AccAddress
}

// NewMsgBuyName is the constructor function for MsgBuyName
func NewMsgBuyName(name string, bid sdk.Coins, buyer sdk.AccAddress) MsgBuyName {
	return MsgBuyName{
		NameID: name,
		Bid:    bid,
		Buyer:  buyer,
	}
}

// Type Implements Msg.
func (msg MsgBuyName) Type() string { return "nameservice" }

// Name Implements Msg.
func (msg MsgBuyName) Name() string { return "buy_name" }

// ValidateBasic Implements Msg.
func (msg MsgBuyName) ValidateBasic() sdk.Error {
	if msg.Buyer.Empty() {
		return sdk.ErrInvalidAddress(msg.Buyer.String())
	}
	if len(msg.NameID) == 0 {
		return sdk.ErrUnknownRequest("Name cannot be empty")
	}
	if !msg.Bid.IsPositive() {
		return sdk.ErrInsufficientCoins("Bids must be positive")
	}
	return nil
}

// GetSignBytes Implements Msg.
func (msg MsgBuyName) GetSignBytes() []byte {
	b, err := json.Marshal(msg)
	if err != nil {
		panic(err)
	}
	return sdk.MustSortJSON(b)
}

// GetSigners Implements Msg.
func (msg MsgBuyName) GetSigners() []sdk.AccAddress {
	return []sdk.AccAddress{msg.Buyer}
}

Next, in the ./x/nameservice/handler.go file, add the MsgBuyName handler to the module router:

// NewHandler returns a handler for "nameservice" type messages.
func NewHandler(keeper Keeper) sdk.Handler {
	return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
		switch msg := msg.(type) {
		case MsgSetName:
			return handleMsgSetName(ctx, keeper, msg)
		case MsgBuyName:
			return handleMsgBuyName(ctx, keeper, msg)
		default:
			errMsg := fmt.Sprintf("Unrecognized nameservice Msg type: %v", reflect.TypeOf(msg).Name())
			return sdk.ErrUnknownRequest(errMsg).Result()
		}
	}
}

Finally, define the actual handler function which performs the state transitions triggered by the message. Keep in mind that at this point that the message has had its ValidateBasic function run so there has been some input verification. However ValidateBasic should not have to query the state. Validation logic that is dependent on network state (i.e. address balances) should be performed in the handler function.

// Handle MsgBuyName
func handleMsgBuyName(ctx sdk.Context, keeper Keeper, msg MsgBuyName) sdk.Result {
	if keeper.GetPrice(ctx, msg.NameID).IsGTE(msg.Bid) { // Checks if the the bid price is greater than the price paid by the current owner
		return sdk.ErrInsufficientCoins("Bid not high enough").Result() // If not, throw an error
	}
	if keeper.HasOwner(ctx, msg.NameID) {
		_, err := keeper.coinKeeper.SendCoins(ctx, msg.Buyer, keeper.GetOwner(ctx, msg.NameID), msg.Bid)
		if err != nil {
			return sdk.ErrInsufficientCoins("Buyer does not have enough coins").Result()
		}
	} else {
		_, _, err := keeper.coinKeeper.SubtractCoins(ctx, msg.Buyer, msg.Bid) // If so, deduct the Bid amount from the sender
		if err != nil {
			return sdk.ErrInsufficientCoins("Buyer does not have enough coins").Result()
		}
	}
	keeper.SetOwner(ctx, msg.NameID, msg.Buyer)
	keeper.SetPrice(ctx, msg.NameID, msg.Bid)
	return sdk.Result{}
}

First check to make sure that the bid is higher than the current price. Then check to see whether the name already has an owner. If it does, the former owner will get transferred the money from the Buyer.

If there is no owner, our nameservice "burns" (sends to an unrecoverable address) the coins from the Buyer.

If either SubtractCoins or SendCoins returns a non-nil error, the handler throws an error, reverting the state transition. Otherwise, using the getters and setters defined on the Keeper earlier, set the buyer to the new owner and set the new price to be the current bid before exiting.

NOTE: This handler uses functions from the coinKeeper to perform currency operations. If your application is performing currency operations you man want to take a look at the godocs for this module.

Now that you have your Msgs and Handlers defined it's time to learn about making the data these transactions available for querying!