​ICP for .Net Developers

The ​Internet Computer Protocol (ICP)​ is a blockchain technology I find fascinating, but I wanted to work with it using my primary coding language C#/.NET. It didn't seem like anything existed out there, so I decided to write my own. So now that I have released v1.0 Nuget packages, I wanted to share a little about it.
​Github Repo: ​https://github.com/Gekctek/ICP.NET
Nuget Packages:
For an ​ICP intro, start here: https://f76k2-siaaa-aaaal-aaoba-cai.raw.ic0.app/d/icp-makes-me-want-to-develop-web-3

​Candid

​Candid is the JSON of the IC world. The main difference is that it includes types like services (API), functions and variants (ENUM where every option has its own type). This causes some issues with translating between C# and Candid types, since C# doesn't have built in support for all the types like variants and unbounded numbers. For more details see https://internetcomputer.org/docs/current/developer-docs/build/languages/candid/candid-concepts
​To make this less frustrating ​EdjCase.ICP.Candid​ can be used to either define a candid type/value manually:
CandidType nat64Type = new CandidPrimitiveType(PrimitiveType.Nat64); CandidValue nat64Value = CandidPrimitive.Nat64(1); CandidType vecType = new CandidVectorType(nat64Type); CandidValue vecValue = new CandidVector(new CandidValue[]{ nat64Value });
​​Or convert to/from a custom C# class/type:
ulong nat64 = 1;​ CandidValueWithType nat64ValueWithType = CandidValueWithType.FromObject(nat64); MyObj obj = new MyObj { Field1 = "Test", Field2 = 2 }; CandidValueWithType objValueWithType = CandidValueWithType.FromObject(obj);
​HTTP Agent
​Currently there is no 'direct' way to communicate with the internet computer from outside of it, rather 'boundary nodes' exist to facilitate it. In order to communicate through a boundary node, http requests can be sent to ​https://ic0.app​ with a CBOR payload and the boundary node can parse the request to call a canister. For more details see https://internetcomputer.org/docs/current/references/ic-interface-spec#http-interface.
​To make this process easier, the nuget library ​EdjCase.ICP.Agent​ can be utilized to handle the sending of Candid objects to canisters.

​Example:

​Getting proposal information by id
// Url to boundry node Uri url = new Uri($"https://ic0.app"); // Unauthenticated var identity = new AnonymousIdentity(); //Build Http Agent IAgent agent = new HttpAgent(identity, url); // Governance canister id Principal canisterId = Principal.FromText("rrkah-fqaaa-aaaaa-aaaaq-cai"); // Method to call in canister string method = "get_proposal_info"; // Supply method arguments in candid format CandidArg arg = CandidArg.FromCandid( CandidValueWithType.FromObject((ulong)1) ); // Make api call and await response QueryResponse response = await agent.QueryAsync(canisterId, method, arg); // Throw if error QueryReply reply = response.ThrowOrGetReply(); // Convert return value(s) to C# classes ProposalInfo? proposal = reply.Arg.Values[0].ToObjectOrDefault<ProposalInfo?>();
​​​Service Definition files (​.did​)
​Service definition files can be generated for a canister with the extension ​.did. Here is an example of what an address book API definition would look like in Candid.

​AddressBook.did

type address = record { street : text; city : text; zip_code : nat; country : text; }; service address_book : { set_address: (name : text, addr : address) -> (); get_address: (name : text) -> (opt address) query; }
The C# translation would look like
public class Address { public string Street { get; set; } public string City { get; set; } // Nat doesn't translate directly, using library type public EdjCase.ICP.Candid.UnboundedUInt ZipCode { get; set; } public string Country { get; set; } } public class AddressBookApiClient { public async Task SetAddressAsync(string name, Address addr) { // Code to call api route } public async Task<Address?> GetAddressAsync(string name) { // Code to call api route } }
​This process of translation can be a be a bit of a pain point to have to do for every canister. Can something like this be automated? Absolutely:
candid-client-generator -f "AddressBook.did" -o "./" -n "MyProject.AddressBook"
​In addition to the code libraries, there is an additional library that generates API clients based off of ​.did​ files. Nuget package ​EdjCase.ICP.ClientGenerator​ can either be referenced in code OR used as a dotnet tool. Dotnet Tool Info: https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools#install-a-global-tool.

​Installation

dotnet tool install -g EdjCase.ICP.ClientGenerator

​Usage

candid-client-generator -f {service file path} -o {directory to output files to} -n {namespace to give files} [-c {client name}]

​Authentication

​Authentication is a work in progress and has some issues. Since most authentication right now is browser based, its hard to integrate with a backend server, CLI or even Blazor frontend. I am evaluating building tools around this, but nothing exists right now. There are 2 options to use authentication in .NET right now:
byte[] publicKeyBytes = ...; var publicKey = new ED25519PublicKey(publicKeyBytes); byte[] privateKeyBytes = ...; var identity = new ED25519Identity(publicKey, privateKeyBytes);

​Need More?

​See the ​https://github.com/Gekctek/ICP.NET/blob/main/README.md for the most up to date info.
​For any questions or bugs create an issue at ​https://github.com/Gekctek/ICP.NET/issues​. I am actively working on it, but would love any feedback or contributions.
Made with Papyrs