In this tutorial we will consider how to add TON blockchain to a game. For our example we will use a Flappy Bird clone written in Phaser and will add GameFi features step by step. In the tutorial we will use short code pieces and pseudocode to make it more readable. Also, we will provide links to real code blocks to help you understand better. The whole implementation can be found in the demo repo.
We are going to implement the following:
- Achievements. Let’s reward our users with SBTs. Achievement system is a great tool to increase user engagement.
- Game currency. In TON blockchain it’s easy to launch your own token (jetton). The token can be used to create an in-game economy. Our users will be able to earn the game coins to spend them later.
- Game shop. We will provide users with a possibility to purchase in-game items using either in-game currency or TON coin itself.
First, we will set up the game environment. To do that we need to install gamefi-sdk
. The package is designed to prepare everything developers need to integrate the blockchain into games. The lib can be used either from CLI or from Node.js scripts. In this tutorial we stick with the CLI approach.
// install globally
npm install -g @ton-community/gamefi-sdk
Next, we need to create a master wallet. Master wallet is a wallet we will use to mint the jetton, collections, NFTs, SBTs and receive payments.
gamefi-sdk setup-env
You will be asked a few questions:
Field | Hint |
---|---|
Network | Select testnet as far it’s a test game. |
Type | Select highload-v2 type of wallet as far it’s the best, performant option to use as a master wallet. |
Storage | The storage will be used to store NFT /SB T files. Amazon S3 (centralized) or Pinata (decentralized). For the tutorial let’s use Pinata as far as decentralized storage will be more illustrative for the Web3 game. |
The script outputs the link you can open to see the created wallet state.
As you can see the wallet is not actually created yet. To the wallet be really created we need to deposit some funds into it. In the real world scenario, you can deposit the wallet any way you prefer using the wallet address. In our case we will use Testgiver TON Bot. Please open it to claim 5 test TON coins.
A bit later you could see 5 TON on the wallet and its status became Uninit
. The wallet is ready . After the first usage it changes status to Active
.
We are going to create in-game currency to reward users with it:
gamefi-sdk deploy-jetton
You will be asked a few questions:
Field | Hint |
---|---|
Name | Token name, for example Flappy Jetton . |
Description | Token description, for instance: A vibrant digital token from the Flappy Bird universe. |
Image | Download prepared jetton logo and specify file path. Of course, you can use any image. |
Symbol | FLAP or enter any abbreviation you want to use. |
Decimals | How many zeros after the dot your currency will have. Let’ it be 0 in our case. |
The script outputs the link you can open to see the created jetton state. It will have Active
status. The wallet status will change the status from Uninit
to Active
.
Just for example, in the demo game we will reward users for the first and the fifth games. So, we will mint two collections to put SBTs into them when users achieve related conditions – play first and fifth time:
gamefi-sdk deploy-nft-collection
Field | First game | Fifth game |
---|---|---|
Name | Flappy First Flight | Flappy High Fiver |
Description | Commemorating your inaugural journey in the Flappy Bird game! | Celebrate your persistent play with the Flappy High Fiver NFT! |
Image | You can download the image here | You can download the image here |
Type | sbt |
sbt |
We are fully prepared. So, let’s go to the logic implementation.
Everything starts from a user connects its wallet. So, let’s add wallet connect integration. To work with the blockchain from the client side we need to install GameFi SDK for Phaser:
npm install –save @ton/phaser-sdk
Now let’s setup GameFi SDK and create an instance of it:
import { GameFi } from '@ton/phaser-sdk'
const gameFi = await GameFi.create({
network: 'testnet'
connector: {
// if tonconnect-manifest.json is placed in the root you can skip this option
manifestUrl: '/assets/tonconnect-manifest.json',
actionsConfiguration: {
// address of your Telegram Mini App to return to after the wallet is connected
// url you provided to BothFather during the app creation process
// to read more please read https://github.com/ton-community/flappy-bird#telegram-bot--telegram-web-app
twaReturnUrl: URL_YOU_ASSIGNED_TO_YOUR_APP
},
contentResolver: {
// some NFT marketplaces don't support CORS, so we need to use a proxy
// you are able to use any format of the URL, %URL% will be replaced with the actual URL
urlProxy: `${YOUR_BACKEND_URL}/${PROXY_URL}?url=%URL%`
},
// where in-game purchases come to
merchant: {
// in-game jetton purchases (FLAP)
// use address you got running `gamefi-sdk deploy-jetton`
jettonAddress: FLAP_ADDRESS,
// in-game TON purchases
// use master wallet address you got running `gamefi-sdk setup-env`
tonAddress: config.TOKEN_RECIPIENT
}
},
})
To learn more about initialization options please read the library documentation.
To learn what
tonconnect-manifest.json
is please check ton-connect manifest description.
Now we are ready to create a wallet connect button. Let’s create an UI scene in Phaser which will contain the connect button:
class UiScene extends Phaser.Scene {
// receive gameFi instance via constructor
private gameFi: GameFi;
create() {
this.button = this.gameFi.createConnectButton({
scene: this,
// you can calculate the position for the button in your UI scene
x: 0,
y: 0,
button: {
onError: (error) => {
console.error(error)
}
// other options, read the docs
}
})
}
}
Read how to create connect button and the UI scene.
To watch when a user connects or disconnects its wallet let’s use the following piece of code:
function onWalletChange(wallet: Wallet | null) {
if (wallet) {
// wallet is ready to use
} else {
// wallet is disconnected
}
}
const unsubscribe = gameFi.onWalletChange(onWalletChange)
To learn about more complex scenarios please check out the full implementation of wallet connect flow.
Read how game UI managing might be implemented.
Now we have user wallet connected and we can move forward.
To implement achievements and reward system we need to prepare an endpoint which will be requested per user try.
We need to create an endpoint /played
which must do the following:
- receive a body with user wallet address and Telegram initial data passed to Mini app during app launch. The initial data needs to be parsed to extract authentication data and ensure a user sends the request only on its behalf.
- the endpoint must calculate and store the number of games a user played.
- the endpoint must check if it’s the first or fifth game for a user and if so, reward a user with related SBT.
- the endpoint must reward a user with 1 FLAP for each game.
Read /played endpoint code.
Every time the bird hits a pipe or falling down the client code must call /played
endpoint passing the correct body:
async function submitPlayed(endpoint: string, walletAddress: string) {
return await (await fetch(endpoint + '/played', {
body: JSON.stringify({
tg_data: (window as any).Telegram.WebApp.initData,
wallet: walletAddress
}),
headers: {
'content-type': 'application/json'
},
method: 'POST'
})).json()
}
const playedInfo = await submitPlayed('http://localhost:3001', wallet.account.address);
Read submitPlayer function code.
Let’s play the first time and ensure we will be rewarded with a FLAP token and SBT. Click the Play button, fly through a pipe or two and then hit into a tube. Alright, everything works!
Play 4 more times to get the second SBT, then open your Wallet, TON Space. Here your collectibles are:
To have an in-game shop we need to have two components. The first is an endpoint which provides info about users purchases. The second is a global loop to watch user transactions and assign game properties to its owners.
The endpoint does the following:
- receive
auth
get param with Telegram Mini Apps initial data. - the endpoint gets items user purchased and respond with the items list.
Read /purchases endpoint code.
To know when users make payments, we need to watch the master wallet transactions. Every transaction must contain message userId
:itemId
. We will remember the last processed transaction, get only new ones, assign users properties they bought using userId
and itemId
, rewrite the last transaction hash. This will work in infinite loop.
Read purchase loop code.
On the client side we have the Shop button.
Opening the Shop will trigger purchased items loading and updating it every 10 seconds:
// inside of fetchPurchases function
await fetch('http://localhost:3000/purchases?auth=' + encodeURIComponent((window as any).Telegram.WebApp.initData))
// watch for purchases
setTimeout(() => { fetchPurchases() }, 10000)
Read showShop function code.
Now we need to implement the purchase itself. To do that, we will create GameFi SDK instance first and then use pay
method:
gameFi.buy({
// pay with jetton set up in the gameFi instance as `merchant.jettonAddress`
jetton: true,
amount: BigInt(price),
// message which will be used in the Purchase Loop to assign the property to the user
forwardMessage: (window as any).Telegram.WebApp.initDataUnsafe.user.id + ':' + itemId,
// fee prepay to deliver the `forwardMessage` - extra amount will be refunded
forwardFee: BigInt(1),
})
It's also possible to pay with TON coin:
import { Convertor } from '@ton/phaser-sdk'
gameFi.buy({
amount: Convertor.toNano(0.5),
comment: (window as any).Telegram.WebApp.initDataUnsafe.user.id + ':' + itemId
})
That’s it for this tutorial! We considered the basic GameFi features, but the SDK delivers more functionality like transfers between players, utils to work NFTs and collections, etc. We will deliver even more features in the future.
To learn about all the GameFi features you can use read the docs of ton-org/game-engines-sdk and @ton-community/gamefi-sdk.
So, let us know what you think in the Discussions!
The complete implementation is available in flappy-bird repo.
finnished the first step , but step second : the Error : gamefi-sdk is not command