Skip to content

Local blockchain development

VladyslavLapin edited this page May 7, 2024 · 1 revision

Setup a local blockchain environment

If you want to start working with Unity on a blockchain the minimum requirement is that you can access a blockchain. The following possibilities exist.

  • Target a hosted Blockchain, through a (secure) web socket
  • Target a local Blockchain, through a HTTP-connection

In the next step, you will create the individual SDK to access your local blockchain.

Generate the Extension

To create your Extension library, which builds upon the Substrate .NET API, you will need to have your local blockchain up and running, you can follow either the Toolchain readme

dotnet new install Substrate.DotNet.Template

Create a directory called Substrate.MyNode.NET (mkdir Substrate.MyNode.NET) then change into this directory (cd Substrate.MyNode.NET) and execute the commands. (Make sure to change to the version of the template you previously installed) For not local blockchain make sure to replace ws://127.0.0.1:9944 with the secure web socket node URL.

dotnet new sln
dotnet new substrate \
   --sdk_version 0.4.8 \
   --rest_service Substrate.MyNode.NET.RestService \
   --net_api Substrate.MyNode.NET.NetApiExt \
   --rest_client Substrate.MyNode.NET.RestClient \
   --metadata_websocket ws://127.0.0.1:9944 \
   --generate_openapi_documentation false \
   --force \
   --allow-scripts yes

Integrate the Extension

As a result of the generation you will have now a c# solution including your extension, and additional services like a REST-Services and a REST-Client, the DLLs are builder as part of the toolchain execution and can be found under the following directory ..\Substrate.MyNode.NET\Substrate.MyNode.NET.NetApiExt\bin\Release you can choose between NET6.0, NETStandard2.0, and NETStandard2.1, we suggest you always choose the highest NETStandard that you Unity version is using. Please verify this on the Unity page .NET profile support.

Copy the Substrate.MyNode.NET.NetApiExt.dll into the Unity Asset/Plugins folder and you are ready to go!

Connect to the local blockchain

using Substrate.MyNode.NET.NetApiExt.Generated;
using Substrate.NetApi.Model.Extrinsics;
using System;
using System.Threading.Tasks;

namespace MyNode
{
    internal class Program
    {
        static async Task Main(string[] args)
        {

            var client = new SubstrateClientExt(new Uri("ws://127.0.0.1:9944"), ChargeTransactionPayment.Default());

            await client.ConnectAsync();

            Console.WriteLine($"Client is connected? {client.IsConnected}");

            await client.CloseAsync();
        }

    }
}

Access RPC Modules, Extension Storage & Execute Transactions

using Schnorrkel.Keys;
using Serilog;
using StreamJsonRpc;
using Substrate.MyNode.NET.NetApiExt.Generated;
using Substrate.MyNode.NET.NetApiExt.Generated.Model.sp_core.crypto;
using Substrate.MyNode.NET.NetApiExt.Generated.Model.sp_runtime.multiaddress;
using Substrate.MyNode.NET.NetApiExt.Generated.Storage;
using Substrate.NetApi;
using Substrate.NetApi.Model.Extrinsics;
using Substrate.NetApi.Model.Rpc;
using Substrate.NetApi.Model.Types;
using Substrate.NetApi.Model.Types.Base;
using Substrate.NetApi.Model.Types.Primitive;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace MyNode
{
    internal class Program
    {
        public static MiniSecret MiniSecretAlice => new MiniSecret(Utils.HexToByteArray("0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a"), ExpandMode.Ed25519);
        public static Account Alice => Account.Build(KeyType.Sr25519, MiniSecretAlice.ExpandToSecret().ToBytes(), MiniSecretAlice.GetPair().Public.Key);

        static async Task Main(string[] args)
        {

            var yourAccount = Alice; // local blockchain use alice account

            var client = new SubstrateClientExt(new Uri("ws://127.0.0.1:9944"), ChargeTransactionPayment.Default());

            // connect to substrate node
            await client.ConnectAsync();

            // leave if we can't connect
            if (!client.IsConnected)
            {
                Console.WriteLine($"Couldn't connect to local host.");
                return;
            }

            // ... do something with the client

            // direct rpc call module chain
            var blockHash = await client.Chain.GetBlockHashAsync();

            // direct rpc call module system
            var name = await client.System.NameAsync();

            // extension storage rpc call
            var blocknumber = await client.SystemStorage.Number(CancellationToken.None);

            // extension extrinsic rpc call
            EnumMultiAddress address = new EnumMultiAddress();
            var account = new AccountId32();
            account.Create(Utils.GetPublicKeyFrom("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"));
            address.Create(MultiAddress.Id, account);
            var amount = new BaseCom<U128>(1000000000000);
            var extrinsicMethod = BalancesCalls.TransferKeepAlive(address, amount);

            string subscription = null;
            try
            {
                subscription = await client.Author.SubmitAndWatchExtrinsicAsync(ActionExtrinsicUpdate, extrinsicMethod, yourAccount, ChargeTransactionPayment.Default(), 64, CancellationToken.None);
                Console.WriteLine($"BalancesCalls.TransferKeepAlive: {subscription}");
            }
            catch (RemoteInvocationException e)
            {
                Log.Error("RemoteInvocationException: {0}", e.Message);
            }


            // disconnect from substrate node
            await client.CloseAsync();
        }

        private static void ActionExtrinsicUpdate(string subscription, ExtrinsicStatus status)
        {
            // call back for extrinsic update
        }
    }
}
Clone this wiki locally