Getting started with the .NET SDK: Create an account and transfer crypto
May 13, 2020
by Jason Fabritz
President & Owner, BugBytes, Inc.

The Hedera ecosystem consists of a wonderful diversity of software platforms and emerging wallet options. The genius of implementing the access to Hedera’s Core Network via Google’s Remote Protocol Buffer (gRPC) has both enabled and encouraged the growth of many tools and platforms. Developers can access the network functionality while programming in Java, JavaScript, Go, Swift, C, Python, Rust, and last but not least, .NET. In this first of the .NET SDK blog post series, we will explore how to get started with the .NET SDK, check account balances, transfer crypto and even create accounts for our friends.

Meet Alice

Alice is a .NET programmer who has recently been introduced to Hedera Hashgraph and has made it through Know Your Customer (KYC) in the Hedera Portal (https://portal.hedera.com/) to create an account on the mainnet. She possesses the mainnet account 0.0.38456 and has purchased hbars on an exchange. Alice has just recently transferred her hbars from the exchange to her mainnet account and wishes to confirm her balance. Since she has big plans to build services on the network, this is a perfect time to get started by querying her balance using the .NET SDK.

Account balance query

Alice has already ventured to https://dotnet.microsoft.com/ and downloaded .NET Core 3.1 Runtime and SDK. Double checking her currently installed version reveals:

Code Snippet Background

PS D:\Alice> dotnet --version

3.1.101

To keep things simple, Alice intends on foregoing a user interface and creates an empty console application to get started. The dot net Command Line Interface (CLI) provides a command to create a new console project:

Code Snippet Background

PS D:\Alice> dotnet new console

The template "Console Application" was created successfully.

Processing post-creation actions...

Running 'dotnet restore' on D:\Alice\Alice.csproj...

Restore completed in 146.2 ms for D:\Alice\Alice.csproj.

Restore succeeded.

Before diving in and editing code, Alice knows that she will need to reference the .NET Client Library for Hedera Hashgraph (https://www.nuget.org/packages/Hashgraph/) in her project. Fortunately, the .NET CLI makes it easy to add the library to her project:

Code Snippet Background

PS D:\Alice> dotnet add package Hashgraph

Writing C:\Users\alice\AppData\Local\Temp\tmp6B12.tmp

info : Adding PackageReference for package 'Hashgraph' into project 'D:\Alice\Alice.csproj'.

info : Restoring packages for D:\Alice\Alice.csproj...

info : GET https://api.nuget.org/v3-flatcontainer/hashgraph/index.json

info : OK https://api.nuget.org/v3-flatcontainer/hashgraph/index.json 97ms

info : Package 'Hashgraph' is compatible with all the specified frameworks in project 'D:\Alice\Alice.csproj'.

info : PackageReference for package 'Hashgraph' version '5.3.3' added to file 'D:\Alice\Alice.csproj'.

info : Committing restore...

info : Writing assets file to disk. Path: D:\Alice\obj\project.assets.json

log : Restore completed in 690.91 ms for D:\Alice\Alice.csproj.

The next step is writing the C# code to perform the balance query. Borrowing inspiration from the documentation found at https://bugbytesinc.github.io/Hashgraph/, she edits the Program.cs file generated from the dotnet new command:

Code Snippet Background

using System;

using System.Threading.Tasks;

using Hashgraph;

namespace Alice

{

class Program

{

static async Task Main()

{

try

{

var address = new Address(0, 0, 38456);

var gateway = new Gateway("0.testnet.hedera.com:50211", 0, 0, 3);

await using var client = new Client(ctx => { ctx.Gateway = gateway; });

var balance = await client.GetAccountBalanceAsync(address);

Console.WriteLine($"Account Balance for {address.AccountNum} is {balance / 100_000_000:#,##0.0} hBars.");

}

catch (Exception ex)

{

Console.Error.WriteLine(ex.Message);

Console.Error.WriteLine(ex.StackTrace);

}

}

}

}

It takes only a mere five lines of C# code to perform the task of a balance query. Let’s look at each line to introduce some basic concepts:

Address

The first concept is the Address. An address is an identifier for an Account, Contract, File or Consensus Topic. The identifier consists of three parts: Shard, Realm and Number. Currently, the Hedera network only has one instance of a shard and realm; for the time being, these values will be zero. The third identifier is the address number. The network generates this number upon item creation. Alice’s account number is 0.0.38456: shard zero, realm zero with number 38456.

Gateway

The Gateway is an object provided by the library for identifying a Hedera node. Each Hedera node has a public addressable internet endpoint and linked network Account address. The internet endpoint represents the publicly available gRPC service hosted by the network node, this is the service the .NET library will connect to when making the balance request. The account address represents the Hedera crypto account paired with the node that receives funds from transaction requests requiring payment. Hedera lists the mainnet address book of gateways at https://docs.hedera.com/hedera/networks/mainnet/mainnet-nodes.

Client

The Client object orchestrates communication with the Hedera network. It takes on the role of encoding messages, sending them to the gateway, waiting for a response and then finally decoding the results, returning normal .NET class objects. It provides one or more methods relating to each possible network function; each following the standard .net async/await pattern. This design decision is part of what sets the .NET SDK apart from the other SDKs. Many Hedera SDKs follow a fluent design pattern, we chose an older-school flat API design so that we can fully leverage the .NET async/await patterns. Additionally, the client is resilient to busy network responses and generally waits for network consensus before returning results. We feel this provides a good balance with interface simplicity and performance. It just works.

Context

Some initial configuration is required when creating a Client object. At the very least for balance queries, the Client must know which Gateway to contact to ask for the information. The Context, exposed through the IContext interface, provides the means to configure a client. We will revisit configuration later, but for a balance query, setting the Gateway is all that is necessary.

Getting the balance

Once the client has been properly setup, getting the balance is a simple method call to GetAccountBalanceAsync. This async method returns the balance of the account in tinybars, the smallest unit of cryptocurrency in Hedera. One hbar equals 100,000,000 tinybars.

Alice invokes her nascent program via the dotnet run command:

Code Snippet Background

PS D:\Alice> dotnet run

Account Balance for 38456 is 100.0 hBars.

Yes, indeed 100 hbars have arrived in Alice’s account.

Account info query

Alice is aware that there is other metadata associated with her account, but she has never seen it. For example, what is the auto-renewal duration for her account, and when does it expire? The .net Client provides a method, GetAccountInfoAsync, to retrieve this information.

Payer & Signatory

Whereas querying the Hedera network for an account balance is presently free, obtaining the metadata associated with an account requires the payment of a small fee. The .NET library refers to the account paying these transaction fees as the Payer. The payer consists of two pieces of information, the Account identifying the payer, and a Signatory authorizing the spending of funds from the account. The Signatory is typically backed by the account holder’s private key, which is the case for Alice. Alice’s account is protected by a single Ed25519 key with the value of 302e020100300506032b6570042204209fc8d8558759051b5fbbe7a6434f664ee86a4e5f83cb8a60e9be5b0753c66259. (Note: for Ed25519 keys, the library uses DER encoding, the keys are encoded 48 bytes in length, prefixed with the value of 302e020100300506032b6570.)

Getting the Info

To obtain the account information, Alice modifies her program to add the payment information and replaces the method call with GetAccountInfoAsync. The program now looks like the following:

Code Snippet Background

using System;

using System.Threading.Tasks;

using Hashgraph;

namespace Alice

{

class Program

{

static async Task Main()

{

try

{

var address = new Address(0, 0, 38456);

var gateway = new Gateway("0.testnet.hedera.com:50211", 0, 0, 3);

var signatory = new Signatory(Hex.ToBytes("302e020100300506032b6570042204209fc8d8558759051b5fbbe7a6434f664ee86a4e5f83cb8a60e9be5b0753c66259"));

await using var client = new Client(ctx =>

{

ctx.Gateway = gateway;

ctx.Payer = address;

ctx.Signatory = signatory;

});

var info = await client.GetAccountInfoAsync(address);

Console.WriteLine($"Account: 0.0.{info.Address.AccountNum}");

Console.WriteLine($"Smart Contract ID: {info.SmartContractId}");

Console.WriteLine($"Proxy Address: 0.0.{info.Proxy.AccountNum}");

Console.WriteLine($"Balance: {info.Balance:#,#} tb");

Console.WriteLine($"Send Record Thrshld: {info.SendThresholdCreateRecord:#,#} tb");

Console.WriteLine($"Rec. Record Thrshld: {info.ReceiveThresholdCreateRecord:#,#} tb");

Console.WriteLine($"Receive Sig. Required: {info.ReceiveSignatureRequired}");

Console.WriteLine($"Auto-Renewal Period: {info.AutoRenewPeriod}");

}

catch (Exception ex)

{

Console.Error.WriteLine(ex.Message);

Console.Error.WriteLine(ex.StackTrace);

}

}

}

}

One should note, since Alice is just experimenting with the SDK, she would never hard-code her private key into an application she released. That could cost her the loss of her funds if the source code were decompiled by an enterprising thief.

Executing the updated program yields:

Code Snippet Background

PS D:\Alice> dotnet run

Account: 0.0.38456

Smart Contract ID: 0000000000000000000000000000000000009638

Proxy Address: 0.0.0

Balance: 10,000,000,000 tb

Send Record Thrshld: 9,223,372,036,854,775,807 tb

Rec. Record Thrshld: 9,223,372,036,854,775,807 tb

Receive Sig. Required: False

Auto-Renewal Period: 91.07:40:00

To review, Alice used her account 0.0.38456, to pay for querying the network for the details of her account. She used her private key, 302e020100300506032b6570042204209fc8d8558759051b5fbbe7a6434f664ee86a4e5f83cb8a60e9be5b0753c66259, to authorize the payment. One might ask, why is the balance still 100 hbars? The reason is because the query returned the information before settling the transaction paying the Gateway node to process the request. Subsequent queries on Alice’s balance will show a small reduction in balance.

Create Account

After discovering how easy it is to interact with the network, Alice wishes to introduce her friend Bob to Hedera Hashgraph; but first he needs an account. Bob could go through the Hedera portal and wait for KYC to obtain an account, but after an additional search in the documentation, https://bugbytesinc.github.io/Hashgraph/recipie/crypto/create, Alice realizes she can create an account for her friend without waiting

Endorsement

Most accounts are secured by a single private key, such as Alice’s Ed25519 key described above. The network never sees Alice’s key, but has been given the public key corresponding to her private key during the creation of her account. The same needs to happen when Alice creates Bob’s new account. Bob will create a public/private key pair, sending the public key to Alice and keeping his private key safe. Alice will in turn forward that public key value with the account creation request. The .NET SDK provides the Endorsement object to hold this value.

An Endorsement object can represent a complex multi-key structure, fully representing what the Hedera network is capable of processing, but for Alice’s purposes, a single Ed25519 public key will suffice. Bob has generated his key pair and sent the public key to secure his account: 302a300506032b6570032100de5f513a5e67e14d6cc2b818b51c009eab953775bfec14c2fd0c35c8389bd965. (Note: for Ed25519 public keys, the library uses DER encoding, the keys are encoded 44 bytes starting with the prefix 0x302a300506032b6570032100.) Alice will never see Bob’s private key.

Creating the account

Alice modifies her application one more time to create Bob’s account:

Code Snippet Background

using System;

using System.Threading.Tasks;

using Hashgraph;

namespace Alice

{

class Program

{

static async Task Main()

{

try

{

var gateway = new Gateway("0.testnet.hedera.com:50211", 0, 0, 3);

var payer = new Address(0, 0, 38456);

var signatory = new Signatory(Hex.ToBytes("302e020100300506032b6570042204209fc8d8558759051b5fbbe7a6434f664ee86a4e5f83cb8a60e9be5b0753c66259"));

var endorsement = new Endorsement(Hex.ToBytes("302a300506032b6570032100de5f513a5e67e14d6cc2b818b51c009eab953775bfec14c2fd0c35c8389bd965"));

await using var client = new Client(ctx =>

{

ctx.Gateway = gateway;

ctx.Payer = payer;

ctx.Signatory = signatory;

});

var createParams = new CreateAccountParams

{

Endorsement = endorsement,

InitialBalance = 1_000_000_000

};

var account = await client.CreateAccountAsync(createParams);

var address = account.Address;

Console.WriteLine($"New Account ID: {address.ShardNum}.{address.RealmNum}.{address.AccountNum}");

}

catch (Exception ex)

{

Console.Error.WriteLine(ex.Message);

Console.Error.WriteLine(ex.StackTrace);

}

}

}

}

Executing the program yields:

Code Snippet Background
PS D:\Alice> dotnet run 
New Account ID: 0.0.38460

Bob’s account was successfully created and is fully functional. Alice sends the account number to Bob. Bob can now import the account number into the wallet of his choice or even choose to talk to the network directly through an SDK.

Sending Crypto

After Alice created Bob’s account, she realized that she only sent him 10 hbars, she intended on giving him 20. She needs to send him an additional 10 hbars. Fortunately, the library offers just the method for the task TransferAsync. Alice once again modifies her test program to make the transfer:

Code Snippet Background

using System;

using System.Threading.Tasks;

using Hashgraph;

namespace Alice

{

class Program

{

static async Task Main()

{

try

{

var gateway = new Gateway("0.testnet.hedera.com:50211", 0, 0, 3);

var payer = new Address(0, 0, 38456);

var signatory = new Signatory(Hex.ToBytes("302e020100300506032b6570042204209fc8d8558759051b5fbbe7a6434f664ee86a4e5f83cb8a60e9be5b0753c66259"));

var receiver = new Address(0, 0, 38460);

await using var client = new Client(ctx =>

{

ctx.Gateway = gateway;

ctx.Payer = payer;

ctx.Signatory = signatory;

});

await client.TransferAsync(payer, receiver, 1_000_000_000);

Console.WriteLine($"Done.");

}

catch (Exception ex)

{

Console.Error.WriteLine(ex.Message);

Console.Error.WriteLine(ex.StackTrace);

}

}

}

}

Running the application does not result in much output:

Code Snippet Background

PS D:\Alice> dotnet run

Done.

To review, Alice used her account 0.0.38456, to pay for sending 10 hbars to Bob’s account, 0.0.38460. She used her private key, 302e020100300506032b6570042204209fc8d8558759051b5fbbe7a6434f664ee86a4e5f83cb8a60e9be5b0753c66259, to authorize the payment. She asked the Hedera Node with the address 0.0.3 and endpoint 0.testnet.hedera.com:50211 to process and gossip the transaction to the remainder of the network.

Conclusion

It’s now time to take a break from Alice’s journey of discovery of the Hedera Network using the .NET SDK to quickly review the basic concepts introduced here. The central component of the SDK orchestrating the native communication with the network is the Client object. It hides much of the complexity of communication with the network, exposing the functionality through easy to invoke async functions. A Client in-turn is configured by updating properties of its Context when created. At the very minimum, this Context must be assigned a Gateway holding the information identifying a network node that can accept requests and return results. The Gateway consists of two parts, the Address of the crypto account associated and an internet address and port for the nodes public gRPC interface. An Address consists of three identifiers, Shard, Realm and Number. Each Hedera account holder owns an Address identifying their account and crypto balance on the network. When an account holder wishes to perform an action on the network requiring a fee, such as a crypto transfer, they also must assign a Payer to the Context. The Payer is the address of the crypto account that pays the transaction fee. In addition to setting the Payer property of the context, the account holder must assign the Signatory. In most cases the Signatory holds the private key that can sign transactions submitted to the network on behalf of the account holder. For most private accounts, the Signatory is a single Ed25519 key, but can be a more complex set of keys if so desired. Also, for each Signatory representing the private signing key(s) for an account, a corresponding Endorsement can be created, representing the public key structure requirements matching the private key(s).

We hope you enjoyed this quick narrative on how to quickly get started with the .NET SDK for Hedera Hashgraph. Please stay tuned for future stories, including uploading and sharing files, and of course the Hedera Consensus Service. In the meanwhile, please check out https://bugbytesinc.github.io/Hashgraph/ for more examples on how to incorporate Hedera Hashgraph into your .NET projects.