How to get the value of an enum from transaction service events?

Hi, I was trying to read all events from transactions by using the transactions service and was trying to get the value of an enum from a ‘created’ event but I can’t find it anywhere in the event payload.

In the value of a field I see an ‘enum_id’ but inside of it there is no value of the enum but only a structure having ‘package_id’, ‘module_name’ and ‘entity_name’.

How can I get the value of the enum from the event?

If you are using the Java bindings, I definitely recommend using the Java codegen, which does the translation of contract payloads into friendly Java classes a lot easier.

Hi Stefano, thanks for the answer.

Actually, I’m not using Java bindings but I’m using Nodejs.

Is that any chance to have the enum value without using the java bindings?

It’s not strictly necessary. The Java bindings are simply convenient, but are built on top of the Ledger API, which uses Protobuf for the encoding. The encoding for Daml values over the Ledger API is defined here. Consider that depending on what you have in your Daml code you might have an enumeration, which would correspond to a definition in Daml like the following:

data Boolean = True | False -- all possible values are singletons

or a variant, which arises when your data type could have a constructor, as in the following example:

data Option a = Some a | None

I took a look at the protobufs definition and it seems that for the ‘Enum’ datatype we have the ‘Identifier’ definition which have only the ‘package_id’, ‘module_name’ and ‘entity_name’.

// Unique identifier of an entity.
message Identifier {

  // The identifier of the Daml package that contains the entity.
  // Must be a valid PackageIdString.
  // Required
  string package_id = 1;

  reserved 2; // was `name` old compact representation of identifier.
              // removed in favor of  ``module_name`` and ``entity_name``.

  // The dot-separated module name of the identifier.
  // Required
  string module_name = 3;

  // The dot-separated name of the entity (e.g. record, template, ...) within the module.
  // Required
  string entity_name = 4;
}

Indeed, in the node listener I get these information (the package_id, the module_name, the entity_name) but I’m not able to get the value of that enum.

However I’m thinking to change the contract template from Enum to Text, so I will be able to have the value on the nodejs listener. Any thoughts on that?

If the enumeration is a good fit, I would keep that rather then changing it for a string. Using strings as enum-like values might be a better fit to make the model easier to adapt in the future, to the cost of shifting the cost of dealing with the evolution to the client. Consider this.

If you want to get the value of the enum, you need to look into the contract payload variant, which should be an enum, which you can see defined here: as you can see, you get both the identifier that you shared before and the constructor, which is the specific value of the enum for that payload.

I see. Indeed the ease to adapt a model is a good reason to use strings as enum-likes.

However, regarding the value of the enum,

It’s not returned from the transaction service in the payload of the created event (I’m reading rom the transactionTree service).

Here is a screenshot of the console.log of what I’m receiving:

the first object in the screenshot is the console.log of the field, while the second object in the screenshot is the console.log of the enum_id object.

As you can see, inside the enum object (the first one in the screenshot) there is no key ‘constructor’ with the string.

Sorry, I’ll need to ask a few question to better understand why the value is not showing up.

  1. Are you using the plain Node.js code generated by gRPC or some other convenience? We had a set of Node.js bindings that turned the gRPC in-memory data structures into a JSON-like form but they have been marked as deprecated a long time ago and might not have updated to take enums into account.
  2. If this is a representation of the plain Protobuf output, what tool are you using to translate the Protobuf payload into this JSON representation?

1 - I’m using plain node.js. I’m connecting using the @grpc/grpc-js.
2. Yes, it’s the plain rappresentation. It’s a console log from what is received by the method GetTransactionTrees. However since we had problem with the imports of the protobufs (because of folders structure) we created a single .proto file containing all the protos definitions (therefore also the proto definitions inside Value.proto) .

This is the code to load the proto definition:

const protoLoader = require('@grpc/proto-loader');
...
const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
    keepCase: true,
    longs: String,
    enums: String,
    defaults: true,
    oneofs: true
});


module.exports = function () {
    return grpc.loadPackageDefinition(packageDefinition);
};

Here is a piece of code to connect and iterate of those transaction items:

 //===========================================
    // create the stream to receive the transactions
    //============================================
    const transactionsStream = client.GetTransactionTrees(request, function () {
    });

    transactionsStream.on("status", function (status) {
        console.log("status", status);
    });
    transactionsStream.on("error", function (error) {
        console.error(error);
    });
    transactionsStream.on("end", function () {
        console.log("end");
    });


    //===========================================
    // stream async iterable
    //============================================
    for await (var item of transactionsStream) {
        await ProcessTransactionsTree(item.transactions);
    }

Does it change anything if you flip the value for verbose (Ledger API Reference — Daml SDK 2.5.3 documentation)? Its effect should be that of hiding the enum_id when it’s false, which it is by default.

Can you also check this is not something simply missing from the string representation? A few tools automatically generate a constructor method and this hides the actual constructor in the Protobuf. I’m not sure of how the proto-loader library works, when I was working with Node.js and Protobuf I statically generated the code and used the getConstructor method on that.

I’m already using the verbose flag.

However, I think I found the problem:

We have commented out the field called ‘constructor’ in the Variant message definition .

This because, if we leave that field as it is, we have a problem loading the protobuf with Nodejs using the library @grpc/proto-loader because
of this issue:

“Error: duplicate name ‘constructor’ in Type Variant”

and it seems to be related to a similar issue found in other library Error: duplicate name 'constructor' in Type · Issue #1113 · protobufjs/protobuf.js · GitHub

basically it seems that it’s not possible to use a field called ‘constructor’ because it cause issues when loading the proto definition.

So, we have chnaged to use strings instead of enums.

Protobuf does not care about the field names only the field ids for decoding. So given that you seem to have your own custom patched protobuf anyway, you could also just rename the constructor field.

2 Likes

Brilliant!