Git Product home page Git Product logo

icp.net's Introduction

ICP.NET

Collection of Internet Computer Protocol (ICP) libraries for .NET/Blazor/Unity

  • Agent - Library to communicate to and from the Internet Computer

  • Candid - Library of Candid Encoding, Models and Helpers

  • Client Generator - Client source code generator for ICP canisters

See each individual project README for more in depth guides

๐ŸŽฎ Unity Integration

  • Download latest agent.unitypackage from: https://github.com/edjCase/ICP.NET/releases
  • Import agent.unitypackage into your unity project
  • If using WebGL, follow the additional WebGL instructions in the Agent Docs
  • If generating a client (see below), place the generated files into the scripts folder: Assets/scripts/MyClient
  • Start coding ๐Ÿ’ป

๐Ÿ“ก Generating a client for a canister

You can specify all the models and api calls yourself, but this is a tool to automatically generate a client and models based on the cansiter or .did file

  • Prerequisite: Have .Net 6 installed (https://dotnet.microsoft.com/en-us/download/dotnet)

  • Navigate to directory of .Net project

    cd {path/to/project}
    
  • Add Agent nuget package to project

    dotnet add package EdjCase.ICP.Agent
    
  • Install ClientGenerator

    dotnet tool install -g EdjCase.ICP.ClientGenerator
    

    This will allow a client to be automatically be generated for a canister. See ClientGenerator README for more details and advanced config

  • Initialize ClientGenerator config (first run only)

    candid-client-generator init
    

    This will create a TOML config file in the directory that can be changed for more advanced options

  • Update created config file candid-client.toml

    If using a canister id:

    namespace = "ProjectGovernance" # Base namespace to use
    output-directory = "./Clients" # Output directory
    
    [[clients]]
    name = "Governance" # Label of client to use
    type = "canister" # Indicates to make client from a canister id
    canister-id = "rrkah-fqaaa-aaaaa-aaaaq-cai" # Canister id to make client for

    If using a service definition file (.did)

    namespace = "ProjectGovernance" # Base namespace to use
    output-directory = "./Clients" # Output directory
    
    [[clients]]
    name = "Governance" # Label of client to use
    type = "file" # Indicates to make client from a service definition file
    file-path = "Governance.did" # File to use

    For all configuration options see ClientGenerator README for more details

  • Generate Client

    candid-client-generator gen
    

    Will output C# file to the output directory specified in the config

  • Use client in code

    var agent = new HttpAgent();
    Principal canisterId = Principal.FromText("rrkah-fqaaa-aaaaa-aaaaq-cai");
    var client = new GovernanceApiClient(agent, canisterId);
    OptionalValue<Sample.Shared.Governance.Models.ProposalInfo> proposalInfo = await client.GetProposalInfo(110174);
    ...
  • SHIP IT! ๐Ÿš€

Breaking change migrations

3.x.x => 4.x.x

The big change here was around variant classes and their attributes. Before the option types were defined by the attribute on each enum member, but in 4.x.x it changed to using method return types and having not type information in attributes. Also the VariantAttribute now gets the enum type from the Tag property vs the attribute

Version 3

[Variant(typeof(MyVariantTag))] // Required to flag as variant and define options with enum
public class MyVariant
{
    [VariantTagProperty] // Flag for tag/enum property, not required if name is `Tag`
    public MyVariantTag Tag { get; set; }
    [VariantValueProperty] // Flag for value property, not required if name is `Value`
    public object? Value { get; set; }
}

public enum MyVariantTag
{
    [CandidName("o1")] // Used to override name for candid
    Option1,
    [CandidName("o2")]
    [VariantType(typeof(string))] // Used to specify if the option has a value associated
    Option2
}

Version 4

[Variant] // Required to flag as variant
public class MyVariant
{
	[VariantTagProperty] // Flag for tag/enum property, not required if name is `Tag`
	public MyVariantTag Tag { get; set; }
	[VariantValueProperty] // Flag for value property, not required if name is `Value`
	public object? Value { get; set; }


	// This method is used to specify if the option has a type/value associated
	[VariantOption("o2")] // Specify the candid tag if different than 'As{CandidTag}' like 'Option2' here
	public string AsOption2()
	{
		return (string)this.Value!;
	}
}

public enum MyVariantTag
{
    [CandidName("o1")] // Used to override name for candid
    Option1,
    [CandidName("o2")]
    Option2
}

Candid Related Links

icp.net's People

Contributors

gekctek avatar godcare avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

icp.net's Issues

IL2CPP bug

Bug with readonly stucts with IL2CPP translation.

Currently there is a bug report waiting for a fix

Aliases + using statements error

From filharvey.icp on discord

The name spaces being generated for our candid need a lot of editing.
image
So basically you can't have using like this. These usings are declared further down the file
If possible can you find the top most of what the using should be
like here
using Types_Ascii_Text = Types_Orm_Text;
which again is not good
But this is correct
using Types_Orm_Text = System.String;
so if possible go through the parenting of the alias names.
as we have 1000's of these errors with our candid

Update FIDO with code

private static string defaultDevice = "windows://hello";

        private static string GetFidoDeviceNameForSign()
        {
            // TODO: on Windows platforms, we must use windows hello (even if we're using an external, i.e. non-platform authenticator).
            // on other platforms, we actually have to pick a device (so give the user some choice, or assume theres only one device?)
            return defaultDevice;
        }

        private static void setFidoDevice(string device)
        {
            defaultDevice = device;
        }

        private static string [] GetFido2DeviceList()
        {
            List<string> deviceList = new List<string>();

            using (var devlist = new Fido2Net.FidoDeviceInfoList(64)) {
                foreach (var device in devlist) {

                    // decide if you want to use `device'
                    deviceList.Add(device.ProductString);
                }
            }

            return deviceList.ToArray();
        }
        ```

C# Variant Changes

From @LorenzGit on discord

I would like to suggest a change in the way C# classes of Variants are parsed. The reason is because this would allow us to manually create classes with generics.
In line 428 of IResolvableTypeInfo.cs a list of properties is already retrieved, that should contain all the information necessary to achieve this, would require a few code changes in that area. This pattern should work fine also for C# generated files that do not use generics.
image
image

No support for PEM key import

So I have a Pxy Staking canister which got several admin methods which only works with my default identity installed in my ubuntu machine in the location '\wsl.localhost\Ubuntu-20.04\home\ving.config\dfx\identity\default'.

As you may know, the file 'identity.pem' has the private key.

Everyday, I run pxy's stats method (ex: runStats) in Ubuntu CLI and it runs in the default identity context of the above identity since that is the primary controller of the canister. It works just fine.

Ex: dfx canister --network ic call staking runStats

I am trying to automate this by using your ICP.Net library. I am using my default identity's Principal as "public key" and copied the base64 encoded string as the "private key". Then I am instantiating HttpAgent by passing the identity object. But for some reason, its throwing exception.

Please follow the code snippet below.

static async Task AdminUnstake()
{
    try
    {
        // public key
        // obtained from your identity's principal id by running following command
        // dfx identity get-principal
        // ex: ax3nc-yx45z-w6iew-d2hds-2gmuc-z4hkb-okg7h-4z6y5-3ach6-eclvr-yqe
        var principal = Principal.FromText("my-default-identity-principal");
        var hex = principal.ToHex();
        byte[] publicKeyBytes = ByteUtil.FromHexString(hex);
        var publicKey = DerEncodedPublicKey.FromEd25519(publicKeyBytes);

        // private key
        // copied from identity.pem file
        byte[] privateKeyBytes = Convert.FromBase64String("base-64-encoded-string-found-inside-identity.pem-file");
        
        var identity = new Ed25519Identity(publicKey, privateKeyBytes);

        var agent = new HttpAgent(identity);
        Principal canisterId = Principal.FromText("5xaxa-xaaaa-aaaal-abcpa-cai");
        var pxyStakingClient = new PxyStakingApiClient(agent, canisterId);
        var result = await pxyStakingClient.AdminUnstake(2829);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

The exception is not super informative. So, it didnt help much.

However, I am posting it below for your reference.

image

Very impressive work on how the ClientGenerator and it just saves a LOT of time. Thank you!! :-)

Replace Sysem.Object? Value in Variants to an Interface.

Could it be possible to replace the "System.Object? Value" with a "IMyVariant Value" instead? And every variant type to implement that type?

The IMyVariant would be empty, it would literally be used simply as an interface identifier. So that way the validation of the proper Object is built in at the compiler level.
Currently you can put anything you want in Value, because it's an Object.
Having it be IMyVariant would allow for compiler validation.

This also require that the classes MyVariantOne and MyVariantTwo will need to be generated as MyVariantOne : IMyVariant and MyVariantTwo : IMyVariant

Public method for signing a transaction

We are developing a software in .NET for interacting with IC using the EdjCase.ICP.Agent nuget package (https://www.nuget.org/packages/EdjCase.ICP.Agent). Our software basically consists of two components: the server side app (webapi) and the client side app (a console app), that securely holds the private key.

In our flow, the transaction submission process is divided into 4 steps:

  1. on the server side - prepare the transaction to be signed,
  2. on the client side - retrieve the (nonsigned) transaction from the server side, sign it and send the signed transaction back to the server-side,
  3. on the server side - submit the signed transaction to the IC using HTTP.API,
  4. on the server side - check the status of the transaction.

In the EdjCase.ICP.Agent library, steps 1-3 are performed within a private SendAsync method:

private async Task<(HttpResponse Response, RequestId RequestId)> SendAsync<TRequest>(
.

What we would need to accomplish our flow, is to split this private method into 3 separate public methods:

icnuget

Can this functionality be added to the EdjCase.ICP.Agent nuget package?

It Seems you need Create PathSegmentCborConverter for PathSegment

When Send ReadStatus Returned Failed: Could not parse body as read request: invalid type: map, expected a borrowed byte array
So I Create a class below:

public class PathSegmentCborConverter : CborConverterBase<PathSegment?>
{
public override PathSegment? Read(ref CborReader reader)
{
throw new System.NotImplementedException();
}
public override void Write(ref CborWriter writer, PathSegment? value)
{
if (value == null)
{
writer.WriteNull();
return;
}
writer.WriteByteString(value.Value);
}
}

OptionalValue as an attribute instead of a new type

For the sake of keeping the model as pure as possible and standardize serialization, is it possible to change the OptionalValue so that it will be an attribute instead of a new type?

// Current translation:
[CandidName("quality")]
public OptionalValue<UInt16> Quality { get; set; }

// Can this be written as:
[CandidName("quality"), OptionalValue]
public ushort? Quality { get; set; }

[BUG] Failed to handle HashMap from canister

version 2.0.8

candid

service : () -> {
    get_wasm_info : () -> (vec record {
        text;
        text
    }) query;
}

gen code

public class Arg0ItemRecord
{
    [CandidName("0")] public string F0 { get; set; }

    [CandidName("1")] public string F1 { get; set; }

    public Arg0ItemRecord(string f0, string f1)
    {
        this.F0 = f0;
        this.F1 = f1;
    }

    public Arg0ItemRecord()
    {
    }
}

but
image

image

then

System.Collections.Generic.KeyNotFoundException: The given key '0(48)' was not present in the dictionary.
   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   at EdjCase.ICP.Candid.Mapping.Mappers.RecordMapper.Map(CandidValue value, CandidConverter converter)
   at EdjCase.ICP.Candid.CandidConverter.ToObject(Type objType, CandidValue value)
   at EdjCase.ICP.Candid.Mapping.Mappers.VectorMapper.<>c__DisplayClass16_0.<Map>b__0(CandidValue v)
   at System.Linq.Enumerable.SelectArrayIterator`2.MoveNext()
   at EdjCase.ICP.Candid.Mapping.DefaultMapperFactory.<>c__DisplayClass2_0.<BuildTypeInfo>b__35(IEnumerable`1 v, CandidConverter options)
   at EdjCase.ICP.Candid.Mapping.Mappers.VectorMapper.Map(CandidValue value, CandidConverter converter)
   at EdjCase.ICP.Candid.CandidConverter.ToObject(Type objType, CandidValue value)
   at EdjCase.ICP.Candid.CandidConverter.ToObject[T](CandidValue value)
   at EdjCase.ICP.Candid.Models.CandidTypedValue.ToObject[T](CandidConverter converter)
   at EdjCase.ICP.Candid.Models.CandidArg.ToObjects[T1](CandidConverter candidConverter)
   at xxxxxApiClient.cs:line 69

AssetCanisterApiClient.MAX_CHUNK_SIZE is too high

Currently the MAX_CHUNK_SIZE is defined as follows

public const int MAX_INGRESS_MESSAGE_SIZE = 2 * 1024 * 1024; // 2MB
...
public const int MAX_CHUNK_SIZE = MAX_INGRESS_MESSAGE_SIZE - 200; // Just under 2MB

With this value I could not upload successfully a chunked file to the AssetCanister. The execution is stuck at

CreateChunkResult result = await this.CreateChunkAsync(createBatchResult.BatchId, chunkBytes);

If I put in a smaller value like

public const int MAX_CHUNK_SIZE = MAX_INGRESS_MESSAGE_SIZE - 500;

it works.

AssetCanisterApiClient.GetAsync - (Download Asset) is only returning first chunk

When using the Download Asset as shown in the /samples/Sample.CLI project (which is using the GetAsync(string key, List<string> acceptEncodings) of the AssetCanister) I only get the bytes of the first chunk.

How can I download the whole file?

I noticed there is a GetChunkAsync method, but I don't know how many chunks are there in total for each file. Also I don't think that the user of this package should need to do this by himself.

[BUG] multiple tuple conflict

version 2.10

candid

service : (opt InitArgs) -> {
    get_named_principal : () -> (
    vec record { PrincipalNames; vec principal },
    ) query;
    get_wasm_info : () -> (vec record { text; text }) query;
}

but

public class Arg0ItemRecord
{
	[CandidTag(0U)]
	public Models.PrincipalNames F0 { get; set; }

	[CandidTag(1U)]
	public List<Principal> F1 { get; set; }

	public Arg0ItemRecord(Models.PrincipalNames f0, List<Principal> f1)
	{
		this.F0 = f0;
		this.F1 = f1;
	}

	public Arg0ItemRecord()
	{
	}
}

public class Arg0ItemRecord
{
	[CandidTag(0U)]
	public string F0 { get; set; }

	[CandidTag(1U)]
	public string F1 { get; set; }

	public Arg0ItemRecord(string f0, string f1)
	{
		this.F0 = f0;
		this.F1 = f1;
	}

	public Arg0ItemRecord()
	{
	}
}

then

  xxxApiClient.cs(136, 16): [CS0102] The type 'xxxApiClient' already contains a definition for 'Arg0ItemRecord'

Issue Generating c# files for icrc1_ledger ckbtc

I updated the c# generator to the latest (3.1.2) I tried to generate the c# files for icrc1_ledger ckbtc using its canister id . There is a bug when generating its files, The "Candid.Icrc1Ledger.Models.Value" is the only one being affected. The Value field's name is identical to the class name and t
image 1: https://github.com/edjCase/ICP.NET/assets/44788655/da79f864-c95c-4bff-a9fa-06bfe070900f
image 2: https://github.com/edjCase/ICP.NET/assets/44788655/c3630538-7730-417c-bcac-5d2f8285bc2f

Custom type configuration for generation

So was wondering the candid here could be exported like this easily,

Instead of being exported as
public class ItemService
{
public List<Items_Dictionary> dictionary {get; set; }
}

We have a lot of hash maps on the backend, and would like to keep them that way
And we lose the middle object in the data.

Gekctek โ€” Today at 11:07 AM
hmmm. Im sure its possible, but i think I need to come up with a good system for auto mapping to certain types
Like in this scenario it looks like you want to take a vec record and make it a dictionary with a key of the name field. That would require configuration that is specific to that one class/property.
A thing i was thinking about was maybe if we could modify the .did file itself to add config, but there is an issue with regenerating the .did file
So maybe we can do a configuration 'by name' where we could have a seperate file that is specific for code generation that would specify a type like Items_Dictionary and say we want it to be a dictionary with a key of name

Cannot convert to a class with numbers as member names

I have a call which returns this candid.

"([{0:\'content\'; 1:qaa6y-5yaaa-aaaaa-aaafa-cai}, {0:\'global\'; 1:qhbym-qaaaa-aaaaa-aaafq-cai}])"

But you cannot have a C# class which is like this

public class services_canister { public string 0; public string 1; }

Serialization Performance

Improve performance of Candid serialization and show performance metrics around them compared to other libraries

AssetCanisterApiClient.UploadAssetChunkedAsync - file is not chunked correctly

When using the AssetCanisterApiClient Class to upload files there is a problem when chunking the file.

int bytesRead = await contentStream.ReadAsync(buffer.AsMemory());
...
byte[] chunkBytes = bytesRead < buffer.Length
				? buffer[0..(bytesRead - 1)]
				: buffer;

The upper bound of the array selector is exclusive, meaning reading 5 bytes should be buffer[0..5] which reads position 0-4 therefore the correct code should be buffer[0..(bytesRead - 1)] (without the -1).

Use built-in type names

Please add a flag to always use built-in types. For example string instead of String, ushort instead of UInt16 etc.

Generate simple enums when a full object variant is not necessary

Currently any enum is generated as a variant object in C#, which makes sense for enums that have structs with extra values inside. Although, could it be possible for the generator to understand when an enum is simple in certain cases? essentially not generating it as a variant, but just as a simple enum?

So that:
variant { #one; #two; #three }

would generate
enum A { one, two, three }

Instead of a class + enum

The main reason is performance. In most cases, when you want an enum to act simply as their pure form of constant integral identifiers, if you have a list of many objects, it is faster to use them as enums instead of having to create a new object for each one of them.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.