18 / 20Module 2: The Unity project
Link the client to the blockchain
This lesson connects your Unity application to the Chromia blockchain through the Postchain client. You will initialize the client and write methods that talk to the Tic Tac Toe game running on-chain.
Installation and setup
Your Unity project needs two dependencies before you can integrate with Chromia: the Postchain client and the FT4 client.
- Install the Postchain client by following the C# client guide.
- Install the FT4 client from its NuGet package page.
Initializing the client
First, create a client that connects to the Chromia blockchain. You supply the blockchain RID or IID, then configure the client to communicate with the network.
using System.Collections.Generic;using System.Threading.Tasks;using Postchain.Client;public class BlockchainManager : MonoBehaviour{ private ChromiaClient client; private Buffer blockchainRID = Buffer.From("7d565d92fd15bd1cdac2dc276cbcbc5581349d05a9e94ba919e1155ef4daf8f9"); // Replace with your blockchain RID async void Start() { await InitializeClient(); } private async Task InitializeClient() { // Initialize Client client = await ChromiaClient.Create("http://localhost:7740", blockchainRID); Debug.Log("Client initialized"); // Additional initialization logic can be added here }}Example: using BlockchainService for connection
The steps below show how BlockchainService links your Unity application to the Chromia blockchain.
-
Initialize the blockchain client: Call
BlockchainService.Initialize()and await it. Check that theHOSTandCHROMIA_PORTconstants point to your blockchain node. -
Register an account: Only the open registration strategy is supported at the moment.
OpenStrategyRegisterAccount()performs the registration and, if it succeeds, creates anFT4Connectionfor you to use afterwards. -
Interact with the blockchain: Once the
FT4Connectionexists, run every further blockchain operation through itsCurrentSession. To create a new game, for instance:c# var transactionReceipt = await _blockchainService.Ft4Connection.CurrentSession.Call(new Operation[] { new("create_game") });
BlockchainService code
public class BlockchainService{ public Ft4Connection Ft4Connection => _ft4Connection; public string UserAccountId => _ft4Connection != null ? _ft4Connection.CurrentSession.Account.Id : string.Empty; // The HOST and PORT values should be adjusted based on the blockchain's configuration private const string HOST = "localhost"; private const int CHROMIA_PORT = 7740; private ChromiaClient _client; private Ft4Connection _ft4Connection; public async Task Initialize() { var nodeUrl = $"http://{HOST}:{CHROMIA_PORT}/"; var brid = await ChromiaClient.GetBlockchainRID(nodeUrl, 0); _client = await ChromiaClient.Create(nodeUrl, brid); } public async UniTask<StoredAccountData> OpenStrategyRegisterAccount() { if (_client == null) { Debug.LogError("ChromiaClient has not been initialized."); return null; } // Generate a random private key var privateKeyBytes = new byte[32]; RandomNumberGenerator.Create().GetBytes(privateKeyBytes); var privateKey = Buffer.From(privateKeyBytes); var userSignature = SignatureProvider.Create(privateKey); // Create the authorization descriptor with account and transfer flags var userAuthDescriptor = new AuthDescriptor<SingleSignatureArgs>( new SingleSignatureArgs { Flags = new AuthFlag[] { AuthFlag.Account, AuthFlag.Transfer }, Signer = userSignature.PubKey } ); // Register the account using the open registration strategy var transaction = await Account.Register(_client, userSignature, userAuthDescriptor, RegistrationStrategies.Open); if (transaction.Status == TransactionReceipt.ResponseStatus.Confirmed) { await InitializeFt4Connection(userSignature); return new StoredAccountData { AccountId = _ft4Connection.CurrentSession.Account.Id, PrivateKey = privateKey }; } Debug.LogError(transaction.RejectReason); return null; } /// <summary> /// Authenticates and initializes an <see cref="Ft4Connection"/> with an existing account. /// </summary> /// <param name="privateKey">The private key used to authenticate.</param> /// <returns>True if the login was successful; otherwise, false.</returns> public async UniTask<bool> LoginWithExistingAccount(Buffer privateKey) { if (_client == null) { Debug.LogError("ChromiaClient has not been initialized."); return false; } var userSignature = SignatureProvider.Create(privateKey); await InitializeFt4Connection(userSignature); return _ft4Connection.CurrentSession.Account != null; } /// <summary> /// Initializes the <see cref="Ft4Connection"/> with the provided signature provider. /// </summary> private async UniTask InitializeFt4Connection(SignatureProvider userSignature) { _ft4Connection = await Ft4Connection.Create(_client, userSignature); }}Initialize(): Connects to the Chromia node at the configuredHOSTandCHROMIA_PORT.OpenStrategyRegisterAccount(): Creates a new account via the open strategy, then initializes theFT4Connectionwith that account.LoginWithExistingAccount(): Signs in with a private key you already have and creates anFT4Connection.InitializeFt4Connection(): Builds theFt4Connectionfrom theSignatureProvideryou pass in.
Connecting to the blockchain
To have node URLs resolved for you, connect through the Directory System Chain:
private async Task InitializeClientFromDirectory(){ var client = await ChromiaClient.CreateFromDirectory("http://localhost:7750", blockchainRID); Debug.Log("Client connected through directory");}Queries
Reading data from the blockchain means defining the query parameters and then invoking the query method. In Tic Tac Toe, you query the game state and the moves each player has made.
public async UniTask<GameData> GetGameById(int gameId){ return await client.Query<GameData>("get_game_by_id", ("id", gameId));}Transactions
A transaction consists of operations that you sign with a signature provider. You submit transactions through the CurrentSession of the Ft4Connection, which takes care of authorizing them with the right credentials.
public async UniTask<bool> MakeMove(int gameRowId, int slot){ var transactionReceipt = await _blockchainService.Ft4Connection.CurrentSession.Call(new Operation[] { new("make_move", gameRowId, slot) }); if (transactionReceipt.Status is TransactionReceipt.ResponseStatus.Confirmed) { return true; } Debug.LogError( $"Could not make move for slot {slot} in game {gameRowId}. Reason: {transactionReceipt.RejectReason}"); return false;}Error handling
Wrap client calls in try-catch blocks. When something goes wrong, the client throws exceptions such as ChromiaException or TransportException. Plan for failures in transaction signing as well as in communication with the client.
private async Task HandleTransactionAsync(){ try { await MakeMove("gameId", 1, 1, 'X'); } catch (ChromiaException ex) { Debug.LogError($"ChromiaException: {ex.Message}"); } catch (TransportException ex) { Debug.LogError($"TransportException: {ex.Message}"); } catch (Exception ex) { Debug.LogError($"Exception: {ex.Message}"); }}