Filters

# Let Players Set a Wager

Make sure you have everything you need before proceeding:

In this section, you will:

  • Add wager information (only).
  • Update unit tests.

With the introduction of game expiry in the previous section and other features, you have now addressed the cases when two players start a game and finish it, or let it expire.

In this section, you will go one step closer to adding an extra layer to a game, with wagers or stakes. Your application already includes all the necessary modules.

Players choose to wager money or not, and the winner gets both wagers. The forfeiter loses their wager. To reduce complexity, start by letting players wager in the staking token of your application.

Now that no games can be left stranded, it is possible for players to safely wager on their games. How could this be implemented?

# Some initial thoughts

When thinking about implementing a wager on games, ask:

  • What form will a wager take?
  • Who decides on the amount of wagers?
  • Where is a wager recorded?
  • At what junctures do you need to handle payments, refunds, and wins?

This is a lot to go through. Therefore, the work is divided into three sections. In this first section, you only add new information, while the second section is where the tokens are actually handled, and in the third section you add integration tests.

Some answers:

  • Even if only as a start, it makes sense to let the game creator decide on the wager.
  • It seems reasonable to save this information in the game itself so that wagers can be handled at any point in the lifecycle of the game.

# Code needs

When it comes to your code:

  • What Ignite CLI commands, if any, will assist you?
  • How do you adjust what Ignite CLI created for you?
  • Where do you make your changes?
  • What event should you emit?
  • How would you unit-test these new elements?
  • How would you use Ignite CLI to locally run a one-node blockchain and interact with it via the CLI to see what you get?

# New information

Add this wager value to the StoredGame's Protobuf definition:

Copy message StoredGame { ... + uint64 wager = 11; } proto checkers stored_game.proto View source

You can let players choose the wager they want by adding a dedicated field in the message to create a game, in proto/checkers/tx.proto:

Copy message MsgCreateGame { ... + uint64 wager = 4; } proto checkers tx.proto View source

Have Ignite CLI and Protobuf recompile these two files:

Now add a helper function to StoredGame using the Cosmos SDK Coin in full_game.go:

Copy func (storedGame *StoredGame) GetWagerCoin() (wager sdk.Coin) { return sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(int64(storedGame.Wager))) } x checkers types full_game.go View source

This encapsulates information about the wager (where sdk.DefaultBondDenom is most likely "stake").

# Saving the wager

Time to ensure that the new field is saved in the storage and it is part of the creation event.

  1. Define a new event key as a constant:

    Copy const ( ... + GameCreatedEventWager = "wager" ) x checkers types keys.go View source
  2. Set the actual value in the new StoredGame as it is instantiated in the create game handler:

    Copy storedGame := types.StoredGame{ ... + Wager: msg.Wager, } x checkers keeper msg_server_create_game.go View source
  3. And in the event:

    Copy ctx.EventManager().EmitEvent( sdk.NewEvent(sdk.EventTypeMessage, ... + sdk.NewAttribute(types.GameCreatedEventWager, strconv.FormatUint(msg.Wager, 10)), ) ) x checkers keeper msg_server_create_game.go View source
  4. Modify the constructor among the interface definition of MsgCreateGame in x/checkers/types/message_create_game.go to avoid surprises:

    Copy - func NewMsgCreateGame(creator string, black string, red string) *MsgCreateGame { + func NewMsgCreateGame(creator string, black string, red string, wager uint64) *MsgCreateGame { return &MsgCreateGame{ ... + Wager: wager, } } x checkers types message_create_game.go View source
  5. Adjust the CLI client accordingly:

    Copy func CmdCreateGame() *cobra.Command { cmd := &cobra.Command{ - Use: "create-game [black] [red]", + Use: "create-game [black] [red] [wager]", Short: "Broadcast message createGame", - Args: cobra.ExactArgs(2), + Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) (err error) { argBlack := args[0] argRed := args[1] + argWager, err := strconv.ParseUint(args[2], 10, 64) + if err != nil { + return err + } clientCtx, err := client.GetClientTxContext(cmd) if err != nil { return err } msg := types.NewMsgCreateGame( clientCtx.GetFromAddress().String(), argBlack, argRed, + argWager, ) if err := msg.ValidateBasic(); err != nil { return err } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } flags.AddTxFlagsToCmd(cmd) return cmd } x checkers ... cli tx_create_game.go View source

That is it. Adding just a field is quick.

# Unit tests

Some of your unit tests no longer pass because of this new field. Ajust accordingly.

  1. When creating a game:

    Copy createResponse, err := msgServer.CreateGame(context, &types.MsgCreateGame{ ... + Wager: 45, }) x checkers keeper msg_server_create_game_test.go View source
  2. When checking that it was saved correctly:

    Copy require.EqualValues(t, types.StoredGame{ ... + Wager: 45, }, game1) x checkers keeper msg_server_create_game_test.go View source
  3. When checking that the event was emitted correctly:

    Copy require.EqualValues(t, sdk.StringEvent{ Type: "new-game-created", Attributes: []sdk.Attribute{ ... + {Key: "wager", Value: "45"}, }, }, event) x checkers keeper msg_server_create_game_test.go View source

Go ahead and make the rest of the changes as necessary.

# Interact via the CLI

With the tests done, see what happens at the command line. All there is to check at this stage is that the wager field appears where expected.

After restarting the Ignite CLI, how much do Alice and Bob have to start with?

This prints:

Copy balances: - amount: "100000000" denom: stake - amount: "20000" denom: token pagination: next_key: null total: "0" balances: - amount: "100000000" denom: stake - amount: "10000" denom: token pagination: next_key: null total: "0"

Create a game with a wager:

Which mentions the wager:

Copy ... raw_log: '[{"events":[{"type":"message","attributes":[{"key":"action","value":"create_game"}]},{"type":"new-game-created","attributes":[{"key":"creator","value":"cosmos1yysy889jzf4kgd84mf6649gt6024x6upzs6pde"},{"key":"game-index","value":"1"},{"key":"black","value":"cosmos1yysy889jzf4kgd84mf6649gt6024x6upzs6pde"},{"key":"red","value":"cosmos1ktgz57udyk4sprkpm5m6znuhsm904l0een8k6y"},{"key":"wager","value":"1000000"}]}]}]'

Confirm that the balances of both Alice and Bob are unchanged, as expected.

Was the game stored correctly?

This returns:

Copy storedGame: ... wager: "1000000"

This confirms what you expected with regards to the command-line interactions.

synopsis

To summarize, this section has explored:

  • How to add the new "wager" value, modify the "create a game" message to allow players to choose the wager they want to make, and add a helper function.
  • How to save the wager and adjust an event, modifying the create game handler.
  • How to minimally adjust unit tests.
  • How to interact via the CLI to check that wager values are being recorded.