Enjin Farmer: Implementation Breakdown
The Enjin Farmer sample project demonstrates a basic Enjin Platform integration within a Unity game. It's built with a client-server architecture to ensure security and scalability.
The project consists of two main components:
- 🎮 Unity Game (Client): The front-end game that players interact with. It handles gameplay, visuals, and user input, communicating with the game server to perform blockchain actions.
- 🖥️ Game Server (Backend): A Node.js application that manages all Enjin Platform logic. It securely handles wallet creation, token minting, and other on-chain operations on behalf of the players.
💡 Important Considerations
Before you begin, please keep the following in mind:
- Demonstration Purpose: This is a simplified example designed to showcase a basic integration. It is not suitable for a production environment as is.
- WebSockets: This implementation does not use WebSocket events. In a real-world application, WebSockets can simplify the process of listening for transaction finalization and receiving real-time updates, such as when a user receives an NFT from an external source like the marketplace.
- Wallet Funding: New managed wallets are created without any funds. To cover network fees for actions like melting or transferring tokens, you must either fund each wallet individually or use a Fuel Tank to subsidize transactions for all your users.
🖥️ Game Server
The game server is a RESTful API built with Node.js and Express. It serves as the secure bridge between the game client and the Enjin Platform. The main entry point is the src/index.js file.
Environment Variables
The server is configured using the following environment variables:
| Variable | Description | 
|---|---|
| PORT | The port the server listens on. Defaults to 3000. | 
| JWT_SECRET | A secure, random string used for signing player authentication tokens. | 
| ENJIN_API_URL | The Enjin Platform API URL. Use https://platform.canary.enjin.io/graphqlfor testing (Canary Network) orhttps://platform.enjin.io/graphqlfor production. | 
| ENJIN_API_KEY | Your API Key Token obtained from the Enjin Platform. | 
| DAEMON_WALLET_ADDRESS | The address of your Wallet Daemon. This wallet receives the initial supply of all created tokens. | 
| ENJIN_COLLECTION_ID | The ID of the Enjin Farmer collection. If left blank, the server will create a new collection on startup. | 
Server Initialization & Collection Setup
On startup, the server performs a one-time setup to ensure the necessary blockchain assets exist.
- Check for Collection: The server first checks if an ENJIN_COLLECTION_IDhas been provided.
- Create Collection: If the ID is missing, the server calls the createCollectionfunction, which executes theCreateCollectionmutation on the Enjin Platform. The Wallet Daemon automatically signs the request.mutation CreateCollection($name: String!, ...) {
 CreateCollection(...) {
 id
 method
 state
 }
 }
- Monitor Transaction: The mutation returns a request ID. The server then polls the GetTransactionquery until the transactionstateisFINALIZEDand theresultisEXTRINSIC_SUCCESS.query GetTransaction($requestId: Int!) {
 GetTransaction(id: $requestId) {
 state
 result
 events { ... }
 }
 }
- Extract Collection ID: Once finalized, the server extracts the new collection ID from the transaction's events and assigns it to the ENJIN_COLLECTION_IDvariable.
- Create NFTs: Using a similar process, the server then creates the three resource NFTs (Gold Coin, Gold Coin (Blue), and Green Gem) within the collection by calling the CreateTokenmutation for each and waiting for finalization.mutation CreateToken($collectionId: BigInt!, $name: String!, ...) {
 CreateToken(collectionId: $collectionId, params: { ... }) {
 id
 method
 state
 }
 }
After this setup is complete, the server starts listening for API requests.
API Endpoints
The server exposes several endpoints to handle game actions. The wallet and token endpoints are protected by a JWT authentication middleware, which verifies the player's identity before processing the request.
Authentication
- GET /api/auth/health-check: A simple endpoint to verify that the server is online.
- POST /api/auth/register: Creates a new player and an associated managed wallet. To create the wallet, it calls the- CreateWalletmutation, using the player's email address as the unique- externalId.- mutation CreateWallet($externalId: String!) {
 CreateWallet(externalId: $externalId)
 }
- POST /api/auth/login: Logs in an existing player, returning their wallet address and a JWT.
Wallet Management
- POST /api/wallet/create: Creates a new managed wallet
- POST /api/wallet/get: Retrieves details for the authenticated player's managed wallet.
- GET /api/wallet/get-tokens: Retrieves the player's managed wallet and all tokens it holds. It calls the- GetWalletquery (using GraphQL Pagination to loop through all pages of results, ensuring the complete inventory is fetched).- query GetWalletTokens($externalId: String!) {
 GetWallet(externalId: $externalId) {
 account { ... }
 tokenAccounts(...) { ... }
 }
 }
Token Actions
- POST /api/token/mint: Mints a token to the player's wallet. The server calls the- MintTokenmutation. The recipient address is extracted from the player's authenticated session.- mutation mintToken($recipient: String!, $collectionId: BigInt!, ...) {
 MintToken(recipient: $recipient, collectionId: $collectionId, ...) {
 id
 }
 }
- POST /api/token/melt: Melts a token from the player's wallet. This uses the- Burnmutation. Since the token needs to be melted from the managed wallet's account, it needs to be signed by the managed wallet. For that, the player's managed wallet address is specified as the- signingAccount. (Learn more about managed wallets here)- mutation burnToken($signingAccount: String!, $collectionId: BigInt!, ...) {
 Burn(signingAccount: $signingAccount, collectionId: $collectionId, ...) {
 id
 }
 }
- POST /api/token/transfer: Transfers a token from the player's managed wallet to another address. This uses the- SimpleTransferTokenmutation, again specifying the player's managed wallet as the- signingAccount.- mutation transferToken($signingAccount: String!, $recipient: String!, ...) {
 SimpleTransferToken(signingAccount: $signingAccount, recipient: $recipient, ...) {
 id
 }
 }
🎮 Unity Game
The Unity game is the client-facing part of the project. It focuses on gameplay and user experience while offloading all sensitive blockchain operations to the game server.
Core Components
The Enjin integration is managed by a few key scripts and a central prefab:
- EnjinManager.prefab: The heart of the integration. This prefab is added to the- Farm_Outdoorscene and configures the Host URL (e.g.,- http://localhost:3000) in the Inspector to connect to your game server.
- EnjinManager.cs: A singleton controller that manages the player's session (auth token, wallet data) and exposes high-level methods like- MintToken()for other game scripts to use.
- EnjinApiService.cs: Handles all REST API communication with the game server using Unity's- UnityWebRequest.
- EnjinItem.cs: A- ScriptableObjectthat represents the data of a blockchain item, such as its display name and its corresponding on-chain token ID.
- UI Scripts (BackpackUI.cs,BackpackItemController.cs): Scripts that manage the UI for viewing and interacting with the player's NFT inventory.
Initial Setup & Player Authentication
- Health Check: On launch, the client calls the /api/auth/health-checkendpoint to ensure the server is available.
- Login/Register: From the login screen, the player clicks "Login", which calls the EnjinManager.Instance.RegisterAndLogin()method.
- API Request: This triggers EnjinApiServiceto send a POST request to the/api/auth/registerendpoint.
- Store Auth Token: The server responds with a JWT authentication token. The client saves this token locally using PlayerPrefsand loads it on subsequent launches for a seamless experience.
In-Game NFT Interactions
All blockchain actions are initiated by the client but securely executed by the server.
Harvesting and Minting Tokens
When a player harvests a crop with the Hoe tool, they have a chance to find a resource token.
- An EnjinTokenGameObject appears on the harvested tile.
- When the player collects this GameObject, its InteractedWith()method is triggered.
- This calls EnjinItem.Collect(), which in turn callsEnjinManager.Instance.MintToken().
- EnjinManagerthen uses- EnjinApiServiceto send a request to the- /api/token/mintendpoint.
Viewing the Wallet (Backpack UI)
- Clicking the backpack icon opens the inventory screen, managed by BackpackUI.cs.
- The UI calls EnjinManager.Instance.GetManagedWalletTokens(), which sends a request to the/api/wallet/get-tokensendpoint.
- The server saves the list of tokens, and the BackpackUIpopulates the view with the data.
- The BackpackUIalso subscribes to theEnjinManager.Instance.OnWalletUpdatedevent to automatically refresh the inventory after a token is minted, melted, or transferred.
Melting and Transferring Tokens
- Melting: The player clicks "Melt" in the backpack. This flows through EnjinManagerand sends a request to the/api/token/meltendpoint.
- Transferring: The player clicks "Send", sending a request to the /api/token/transferendpoint.