Creating & Managing Shops

Overview

Giving players the ability to buy or sell items through in-game merchants, game item shops, skin purchase interfaces, and many other methods is a very common practice. MetaFab makes it easy to give players the ability to buy and/or sell in-game items using any in-game currency, or native chain token (Such as ETH, Matic, etc).

Using MetaFab, you can create fixed rate offers that your players can use through the Shop system. You can think of an Shop as a system for your game that allows you to create offers such as trade 10 in-game currency for 1 sword, or perhaps you want to give your players the ability to sell items they farm from mobs, quests or other mechanics such that they can sell 5 of some given item for 100 of your in-game currency. All of this and many other combinations of fixed rate trading behavior is possible through an Shop.

You can think of the Shop system similar to interacting with the types of offers an NPC (non-playable character) merchant may make available within a game, or as a way for players to spend game currency to buy skin items through a shop interface in a game, etc.

Please note, Item or currency given from offers can either be minted (with pre-authorization) by the shop, or transferred (default) out of the balance of a given item or currency held by the shop.

In this guide, we're going to cover how to setup your game's Shop system, adding usable offers to your Shop, allowing players to use those offers, and more.

Guide

Step 1: Create your First Shop.

We're going to start by creating a shop for our game. A shop can have a near-infinite number of usable offers available through it, so it nearly every case it should only ever be necessary for your game to have 1 shop system. However, if necessary, MetaFab allows you to launch as many individual shop systems as you need for you game.

To create an shop for our game, we're going to use the POST /v1/shops endpoint. Please keep in mind, your game's primary wallet must have a sufficient native token balance for the chain you want to deploy your collection to, as noted here.

We'll need to provide the following argument to create a shop using the creation endpoint.

  • chain: The blockchain where you'd like to deploy your collection. We recommend MATIC (Polygon) for production use cases, or MUMBAI (Polygon Testnet) for testing.

Additionally, we'll need to provide our game's secretKey for the X-Authorization header, as well as our game's walletDecryptKey for the X-Wallet-Decrypt-Key header.

After we successfully make the API request to create our player, we'll receive a response with a Shop object that looks something like this.

[
  {
    "id": "f9c6e827-00e7-4d93-b047-87aa508990f6",
    "gameId": "898d9a17-9a9b-45a2-9ab9-3cd4300ef5f9",
    "contractId": "3c3cb56b-d802-4f7b-86ec-aa4f003357a7",
    "updatedAt": "2022-10-28T23:32:32.329Z",
    "createdAt": "2022-10-28T23:32:32.329Z",
    "contract": {
      "id": "3c3cb56b-d802-4f7b-86ec-aa4f003357a7",
      "gameId": "898d9a17-9a9b-45a2-9ab9-3cd4300ef5f9",
      "chain": "MATIC",
      "abi": [ ...REDACTED FOR LENGTH ],
      "type": "Game_Shop",
      "address": "0xBD8Cbf87D11602108dcc3c02B6dF39089833868B",
      "updatedAt": "2022-10-28T23:32:32.329Z",
      "createdAt": "2022-10-28T23:32:32.329Z"
    }
  }
]

Step 2: Setup your First Offer

Awesome! We've created our game's shop. Now we can setup our first offer.

For our guide, we'll use the collection and the Fire Dagger item we created in our previous guide here.

This offer will allow spending 100 of our game currency that we created in this guide here, to receive 1 Fire Dagger item. You can use any item collection, items or game currency you'd like.

To setup an offer for our shop, we're going to use the POST /v1/shops/{shopId}/offers endpoint.

The endpoint to set up an offer accepts a wide variety of arguments to give you maximum flexibility, but for this example we'll be using the following.

  • id: A unique id that identifies this offer within this shop. Using an existing offer id for this shop will overwrite the offer. We'll use an offer id of 1.
  • inputCurrencyId: The MetaFab currency id for the currency required to input (spend) to use this offer.
  • inputCurrencyAmount: The amount of currency required to input (spend) to use this offer. Input currency spent is sent to the shop contract upon using an offer. We'll use a required currency amount of 100.
  • outputCollectionId: The MetaFab collection id for the item id(s) to output (give) when this offer is used.
  • outputCollectionItemIds: An array of item ids for the provided collection that are given. We'll use the item id of 1 for our fire dagger as, [ 1 ]
  • outputCollectionItemAmount: An array of amounts for each item id given. Amount at array index 0 is the amount given for the item id of outputCollectionItemIds at index 0, etc. We'll use [ 1 ]
  • maxUses: The maximum number of times this offer can ever be used in total. You can provide a positive integer to set a maximum number of uses, or exclude this argument to allow unlimited uses. We'll exclude this argument, which allows the offer to be used indefinitely with no limits.

Additionally, we'll need to provide our game's secretKey for the X-Authorization header, as well as our game's walletDecryptKey for the X-Wallet-Decrypt-Key header.

After we successfully make the API request. to set our offer, we'll receive a response with a Transaction object, confirming the offer was successfully set. That transaction object response will look something like this.

{
  "id": "1f50d735-8221-42bc-a87f-1362b38c577c",
  "contractId": "3c3cb56b-d802-4f7b-86ec-aa4f003357a7",
  "walletId": "b32f7381-2e6b-403a-9b6a-f6da1d8e8d74",
  "function": "setOffer",
  "args": [
    1,
    [
      "0x0000000000000000000000000000000000000000",
      "0x6EC79386b40B03b4e9772ED156C005cB2ae2A3f0"
    ],
    [ [], [1] ],
    [ [], [1] ],
    [
      "0x047b6C10442612eE4b764115b9Bd2049DF36f7d9",
      "0x0000000000000000000000000000000000000000"
    ],
    [
      {
        "hex": "0x056bc75e2d63100000",
        "type": "BigNumber"
      },
      {
        "hex": "0x00",
        "type": "BigNumber"
      }
    ],
    0
  ],
  "hash": "0x86a523da5f8b4d5f8cde6366977096e8d29cc2d49ccc1370bfc53a1ea50a2ab2",
  "updatedAt": "2022-11-02T00:02:09.756Z",
  "createdAt": "2022-11-02T00:02:09.756Z"
}

Step 3: Allowing your Shop to Mint Items or Currency

By default, an shop will transfer the given items (or currency) for an offer from its own held balance. This means that the shop need to own the quantity of items (or currency) it gives for a used offer. If it doesn't hold enough of a balance of given items and/or currency, the offer is unusable.

For our use case, we want our players to be able to purchase our Fire Dagger any number of times through our offer, and we don't want to constantly worry about providing more items to the shop.

What we can do is give our shop permission to mint items directly from our collection. This means, any time an offer is used that involves a collection from which items are given, those items will automatically be created and given, ignoring the shop's held balance.

To enable this for our shop, we're going to use the POST /v1/collections/{collectionId}/roles endpoint.

This endpoint allows us to grant various permissions through roles. A role allows us to give different permissions on behalf of our collection. In this case, we want to give our shop the permission to mint items as needed from our collection. Setting this role once for the collection will allow the shop to mint any item id as needed to fulfill an offer involving any items from our collection.

We'll need to provide the following arguments to grant the required role.

  • collectionId: The id of the collection we're granting the role for. This is set as a path parameter in the request.
  • role: The role we want to grant, this will be a predefined role - in this case minter.
  • address: The address we want to grant the role to. We'll be using the address of our shop contract. This can be found as the shop -> contract -> address property of our created shop object.

Additionally, we'll need to provide our game's secretKey for the X-Authorization header, as well as our game's walletDecryptKey for the X-Wallet-Decrypt-Key header.

After we successfully make the API request to grant our role, we'll receive a response with a Transaction object that looks something like this.

{
  "id": "64a63e0a-1724-470b-b845-bbcd525d6a80",
  "contractId": "739f2147-9987-45c0-8010-46cafc583cf8",
  "walletId": "b32f7381-2e6b-403a-9b6a-f6da1d8e8d74",
  "function": "grantRole",
  "args": [
    "0xc9eb32e43bf5ecbceacf00b32281dfc5d6d700a0db676ea26ccf938a385ac3f7",
    "0xBD8Cbf87D11602108dcc3c02B6dF39089833868B"
  ],
  "hash": "0x758ca78d83ba39223373fec82fdf86b5f37b9c676c73d06d3cbe3c0e65c30407",
  "updatedAt": "2022-11-02T23:46:07.024Z",
  "createdAt": "2022-11-02T23:46:07.024Z"
}

Step 4: Using our Offer

Alright! It's time for a player account to use our offer to purchase a fire dagger!

In this example we'll be using our player account arkdev that we created and already gave some of our game currency to in this previous guide.

To use an offer from our shop, we're going to use the POST /v1/shops/{shopId}/offers/{shopOfferId}/uses endpoint.

This endpoint accepts the following arguments.

  • shopId: The id of the shop we want to use an offer from. This is set as a path parameter in the request.
  • shopOfferId: The id of an offer set for the shop. This is set as a path parameter in the request.

Additionally, we'll need to provide our player's accessToken for the X-Authorization header, as well as our player's walletDecryptKey for the X-Wallet-Decrypt-Key header.

After we successfully make the API request for our player to use our offer, we'll receive a response with a Transaction object that looks something like this.

{
  "id": "e07adc8c-cc72-4e22-aa7d-1fe5270ca5d0",
  "contractId": "3c3cb56b-d802-4f7b-86ec-aa4f003357a7",
  "walletId": "79984b2f-502c-4046-96c4-e5062df59c4a",
  "function": "useOffer",
  "args": [
    "1"
  ],
  "hash": "0xa6514b837500341185fd28bbf96a0cf4495e74834bc8315f6bc83602164aae26",
  "updatedAt": "2022-11-03T00:18:22.491Z",
  "createdAt": "2022-11-03T00:18:22.491Z"
}

Step 5: Verifying the Offer was Used Successfully (Optional)

We've successfully created an offer through our shop that's been used by one of our player's accounts. Our player has spent 100 of our game currency and in exchange received 1 Fire Dagger.

We can verify this by using the GET /v1/collections/{collectionId}/balances to get the balances of items owned by our player's wallet for our collection.

Upon making this GET request, we get the following response.

{
  "1": "1",
  "2": "0",
  "4": "0"
}

Looks good! Our player own 1 quantity of our item id 1 which is our collection's fire dagger!

Example: Creating Shop Offers for Item Upgrading

Shop offers can be composed to support a variety of scenarios. For example, let's say we want to give our player the ability to upgrade their fire dagger to a more powerful blazing fire dagger. To upgrade the dagger, we've decided we want the player to have to provide 1 fire dagger, a fire gem item (we'll create this for this example) and 50 of our in-game currency as a cost to perform the upgrade.

For the sake of this example, we've already created the "Fire Gem" item and "Blazing Fire Dagger" in our items collection using the same create item endpoint we've used previously, so we'll exclude those steps from this guide and focus on constructing the offer.

To setup this offer for our shop, we're going to use the POST /v1/shops/{shopId}/offers endpoint like we have for our previous offer.

For this example we'll be using the following endpoint arguments.

  • id: A unique id that identifies this offer within this shop. Using an existing offer id for this shop will overwrite the offer. We'll use offer id 2.
  • inputCurrencyId: The MetaFab currency id for the currency required to input (spend) to use this offer.
  • inputCurrencyAmount: The amount of currency required to input (spend) to use this offer. Input currency spent is sent to the shop contract upon using an offer. 50 for this example.
  • inputCollectionId: The MetaFab collection id for the item ids required to input (spend) to use this offer.
  • inputCollectionItemIds: An array of item ids for the provided collection that are required to input (spend). We'll be using 1 for our fire dagger and 3 for our fire gem material item.
  • inputCollectionItemAmounts: An array of amounts for each item id given. We'll be using [1, 1] since 1 of each item is required.
  • outputCollectionId: The MetaFab collection id for the item id(s) to output (give) when this offer is used.
  • outputCollectionItemIds: An array of item ids for the provided collection that are given. Our blazing fire dagger is now id 4 in our collection, so we'll use id 4.
  • outputCollectionItemAmount: An array of amounts for each item id given. Amount at array index 0 is the amount given for the item id of outputCollectionItemIds at index 0, etc.

Additionally, we'll need to provide our game's secretKey for the X-Authorization header, as well as our game's walletDecryptKey for the X-Wallet-Decrypt-Key header.

After we successfully make the API request. to set our offer, we'll receive a response with a Transaction object, confirming the offer was successfully set. That transaction object response will look something like this.

{
  "id": "685a26d5-2671-4a23-94c6-ffb5afd94ec9",
  "contractId": "3c3cb56b-d802-4f7b-86ec-aa4f003357a7",
  "walletId": "b32f7381-2e6b-403a-9b6a-f6da1d8e8d74",
  "function": "setOffer",
  "args": [
    2,
    [
      "0x6EC79386b40B03b4e9772ED156C005cB2ae2A3f0",
      "0x6EC79386b40B03b4e9772ED156C005cB2ae2A3f0"
    ],
    [ [ 1, 3 ], [ 4 ] ],
    [ [ 1, 1 ], [ 1 ] ],
    [
      "0x047b6C10442612eE4b764115b9Bd2049DF36f7d9",
      "0x0000000000000000000000000000000000000000"
    ],
    [
      {
        "hex": "0x02b5e3af16b1880000",
        "type": "BigNumber"
      },
      {
        "hex": "0x00",
        "type": "BigNumber"
      }
    ],
    0
  ],
  "hash": "0x8444beb368d7c3458a412376adb8bcfed6d7cc4cd9c10ba233d3f965ede317cc",
  "updatedAt": "2022-11-03T17:55:33.367Z",
  "createdAt": "2022-11-03T17:55:33.367Z"
}

Example: Creating Shop Offers for Item Selling

In some cases, you may want to give your players the ability to sell items they earn for various amounts of your in-game currency. In some games, this pattern is common when players defeat enemies and earn varying rarity of item drops that they may later not need and want to sell at some fixed rate available through in-game item NPC merchants.

For this example, we'll create an offer where players can input (spend/sell) 1 fire dagger and receive 80 of our game's currency.

To setup this offer for our shop, we're going to use the POST /v1/shops/{shopId}/offers endpoint like we have for our previous offers.

For this example we'll be using the following endpoint arguments.

  • id: A unique id that identifies this offer within this shop. Using an existing offer id for this shop will overwrite the offer. We'll use offer id 3.
  • inputCollectionId: The MetaFab collection id for the item ids required to input (spend) to use this offer.
  • inputCollectionItemIds: An array of item ids for the provided collection that are required to input (spend). We'll be using 1 for our fire dagger.
  • inputCollectionItemAmounts: An array of amounts for each item id given. We'll be using [1] since 1 fire dagger is required.
  • outputCurrencyId: The MetaFab currency id for the currency given when this offer is used.
  • outputCurrencyAmount: The amount of currency given when this offer is used. We'll use a value of 80 for this example, giving the user of the offer 80 of our game currency in exchange for 1 fire dagger.

Additionally, we'll need to provide our game's secretKey for the X-Authorization header, as well as our game's walletDecryptKey for the X-Wallet-Decrypt-Key header.

After we successfully make the API request. to set our offer, we'll receive a response with a Transaction object, confirming the offer was successfully set. That transaction object response will look something like this.

{
  "id": "f03f30ac-a536-49d1-bf3c-a13ec2157e8c",
  "contractId": "3c3cb56b-d802-4f7b-86ec-aa4f003357a7",
  "walletId": "b32f7381-2e6b-403a-9b6a-f6da1d8e8d74",
  "function": "setOffer",
  "args": [
    3,
    [
      "0x6EC79386b40B03b4e9772ED156C005cB2ae2A3f0",
      "0x0000000000000000000000000000000000000000"
    ],
    [ [ 1 ], [] ],
    [ [ 1 ], [] ],
    [
      "0x0000000000000000000000000000000000000000",
      "0x047b6C10442612eE4b764115b9Bd2049DF36f7d9"
    ],
    [
      {
        "hex": "0x00",
        "type": "BigNumber"
      },
      {
        "hex": "0x04563918244f400000",
        "type": "BigNumber"
      }
    ],
    0
  ],
  "hash": "0x40b9087eb58efec28468c4c883161dbd2173941cd3c9c45568bf01ac0b501102",
  "updatedAt": "2022-11-03T18:54:42.644Z",
  "createdAt": "2022-11-03T18:54:42.644Z"
}