How to create an on-chain game on Starknet [Episode 2] 🏎

*In the first part of our series on building an on-chain game on StarkNet, we set up and built a simple on-chain game called Stark Racing using the open-source HTML5 game development framework Phaser. The game allows users to connect their StarkNet wallet directly on Phaser, mint a car with a random speed property, and race against another car. The fastest car will win the race and be rewarded with a token called $SPEED. *

But that’s not all.

We’ve learned how to create a React application, install dependencies including Starknet.js, Phaser, and argent/getstarknet, and create a Phaser component with a LoginScene. Then demonstrated how to create a plugin to define a wallet connection function and handle errors by forcing users to connect and play only via the testing Goerli network.

Setting up our smart-contract environment and creating / deploying our first NFT (ERC721) collection

In part two, we will see how to create, compile and deploy our first smart-contract in Cairo.

Let’s get started.

First, we need to generate our NFT collection (PNG + JSON) - I created mine with Hashlips - you can clone the repo and try this tool to generate your own NFT collection.

For this second part, you can find the NFT collections I generated in the project repository.

You can click on the GitHub link and import them (public/assets/nft).

Let’s now explain how it works and upload our first NFT collection on IPFS!

What is IPFS?

The Interplanetary File System (IPFS) is a distributed file storage protocol (a decentralised storage system) that allows computers all over the globe to store and serve files as part of a giant peer-to-peer network.

We will use it to ensure that the images cannot change or go offline (which can happen if we store the NFT on our own server for example).

But if needed you can store your NFT on your own API in order to have control over it (if you need to update some statistics for example in case of nerf/up).

Let’s create and upload our StarkNet NFT collection on IPFS

Each PNG file is associated with a JSON file that contains metadata about the image (for example, the file 1.png refers to the metadata of the 1.json file).

Our JSON must contain the metadata we want, in our case we will have two properties:

In this example, we would like these two properties:

  • Type - Basic Car, Super Car or Truck

  • Speed - A note /100 that defines the speed of our car

We now need to upload our StarkNet NFTs to IPFS.

We'll be using, but there are other options available. 

To do this, upload the PNG to generate a CID* which we can use to replace in our JSON (on Hashlips the NewUriToReplace part).

*CID: A Content Identifier or CID is a label that is formed using a unique string of letters and numbers (known as a cryptographic hash) to represent content like pictures, videos or other files on the IPFS network

Then upload our JSON file, It should look similar to this:

We've successfully uploaded our StarkNet NFTs on IPFS with our baseURI (What there is before the 1.json). This will be very important later as we will need to supply the baseURI as an argument to our contract's constructor.

Create our first NFT (ERC721) Smart-Contract

**     Setting up our development environment**

To get started we'll need to set up our Python development environment to compile our contract using the cairo-lang python library.

You will need python 3.9 (I used python 3.9.10) installed on your computer. Once you have all that, you're ready to create your virtual environment. 

Do the following:

python -m venv cairo-venv
source cairo-venv/bin/activate

// Linux
sudo apt install -y libgmp3-dev

// Mac (Intel chip)
brew install gmp

// Mac M1
CFLAGS=-Ibrew --prefix gmp/include LDFLAGS=-Lbrew --prefix gmp/lib pip install ecdsa fastecdsa sympy

pip install --upgrade pip
pip install --upgrade pip

Now we can install cairo-lang and openzeppelin-cairo-contracts with:

pip install cairo-lang
pip install openzeppelin-cairo-contracts

We can now write our smart-contract and create a file named “StarkRacing.cairo” in a contracts folder.

You can clone my contracts folder directly here and paste it in your project.

Here’s a preview of our StarkRacing.cairo smart contract, and you’ll see all the comments in the project's GitHub repo.

The structure of our Cairo smart-contract is :
(i) We declare our contract -> (ii) import the necessary libraries -> (iii) create the constructor with some parameters -> (iv) getters -> (v) external functions

%lang starknet // Only one line of code needed to compile our starknet contract
// Import of the different libraries (openzeppelin / contracts folder locally / starkware)
from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin
from starkware.cairo.common.uint256 import Uint256, uint256_add
from starkware.starknet.common.syscalls import get_caller_address

from openzeppelin.token.erc721.library import ERC721
from openzeppelin.introspection.erc165.library import ERC165
from openzeppelin.access.ownable.library import Ownable

from contracts.token.ERC721.ERC721_Metadata_base import (

func next_token() -> (token_id: Uint256) {
// Constructor

func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
   name: felt,
   symbol: felt,
   owner: felt,
  base_token_uri_len: felt,
   base_token_uri: felt*,
   token_uri_suffix: felt,
) {
   ERC721.initializer(name, symbol);
   ERC721_Metadata_setBaseTokenURI(base_token_uri_len, base_token_uri, token_uri_suffix);
   next_token.write(Uint256(1, 0));
   return ();

// Getters

Let’s deploy our first Cairo smart-contract in 3 steps

1. Compile our contract

For this one we can use starknet-compile by filling out the following command :

starknet-compile contracts/StarkRacing.cairo --output StarkRacing.json --abi StarkRacing_abi.json

2. Declare the contract

Now that we have our abi, we can declare our contract on the Goerli test network with :

starknet declare --network alpha-goerli --contract StarkRacing.json --no_wallet --sender 0x04a71727f73352b28C2D2f29fD8038d01b0E14b66660e8fa9b08535535bd7F3e --max_fee 1000000000000000

The declared transaction was successfully sent.

A contract class_hash has been returned and we need it to deploy our contract on testnet.

3. Deploy the contract on the Starknet Testnet

To properly deploy our smart contract, we have parameters in our constructor that we need to fill in.

In Cairo, we need to convert our strings to felt. For this, I have made a python script where you can enter your arguments and it will return their values.

Our strings are converted into felt which gives us :

StarkRacing = 100890435118976371236892263
RACING = 90440255229511

We need to deploy our smart contract to the Starknet Goerli Testnet. To do this, we will use the Argent smart-contract deployer. You can only access this in Argent X, Argent’s StarkNet wallet. Here’s their guide on creating an Argent X wallet if you haven’t already installed one. 

We then need to provide our classHash, select the network and the account that will deploy the smart contract, and pay the gas fees.

Let's fill in our arguments to finally approve the transaction and deploy our smart-contract:

Our smart contract has been successfully deployed on testnet, let's now go to StarkScan to mint our first NFT:

On the "Write Contract" tab, let's connect our wallet in order to call our mint function. Once the transaction is complete, our NFT is displayed in our collectibles on our Argent wallet.

On Mintsquare, we can see the properties of our car, it is a Basic Car that has a speed of 30.

And if we repeat the process, you’ll get a new car. In this case, we were lucky and got a Super Car with a speed of 90. 

In the 3rd part of this series, we will explain how to use starknet.js to call our smart-contract functions on our game and make our first Web3 scene.

Sources :

Thank you for reading this article. If you have any questions feel free to contact me on Twitter:

Don’t miss upcoming articles:

Subscribe to Matchbox DAO
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
This entry has been permanently stored onchain and signed by its creator.