Getting all choices for a given Party or Contract

Hi everyone,

is there any way that on React Js app we can fetch all the possible choices a given party or contract can exercise? Just like you guys have on navigator?

Thanks in advance :slight_smile:

1 Like

What the Navigator does is fetching packages, interpreting its contents (which includes the signatures for all templates and choices) and using those to allow to have a generic interface to the underlying ledger.

The HTTP JSON API Service and the React.js bindings (which work against the former) are not meant for “metaprogramming” and don’t expose facilities for “introspection”. However, you might be able to piggy back on the TypeScript codegen and leverage the dynamic aspects of JS to inspect the generated code and retrieve the available choices. Mostly thinking out loud, but it might work.

3 Likes

Thanks :slight_smile: I will take a look on both options.

You can also do what the Navigator does in the background: Decode the Daml package and interpret its contents.

You can download the Daml package (DALF) you need using the JSON API. As documented here, DALF packages are just binary encoded protobuf messages from the Daml-LF schema.

You can use protobuf.js to decode DALFs, thought to make it comfortable, you’ll want to load the proto schema files, which you can download on GitHub releases. You may need to hack around a bit to deal with relative vs absolute paths in the proto files and rename the constructor field in Enum since protobuf.js doesn’t deal with it, but once you put it all together, you can get your hands on the payload as a JS object, and then access template, choice, and type information dynamically.

A PoC grade way of getting at the package info here:

<head>
    <script src="https://cdn.rawgit.com/dcodeIO/protobuf.js/6.11.3/dist/protobuf.js"></script>
</head>
<body>
    <h1>Daml Archive Reader</h1>
    <p></p>

    <script>
        document.getElementsByTagName('p')[0].innerText = "foo";

        function withArchive(path, callback) {
            var req = new XMLHttpRequest();
            req.open("GET", path, true);
            req.responseType = 'arraybuffer';
            req.onload = function(e) {
                if (this.status == 200) {
                    callback(new Uint8Array(this.response)); 
                }
            }
            req.send();
        }
        
        protobuf.load("protos-2.2.0/com/daml/daml_lf_1_14/daml_lf.proto", function(err, root) {
            if (err)
                throw err;

            withArchive(".daml/dist/create-daml-app-0.1.0-1f477a11f7fd9914437b909e89ea15775e705e182d2296dfa60fb0414fb0b74c/create-daml-app-0.1.0-1f477a11f7fd9914437b909e89ea15775e705e182d2296dfa60fb0414fb0b74c.dalf", function(buffer) {
                // Get the archive
                var Archive = root.lookupType("daml_lf_1_13.Archive");
                var archive = Archive.decode(buffer);
                // Get the payload

                var Payload = root.lookupType("daml_lf_1_13.ArchivePayload");
                var payload = Payload.decode(archive.payload);

                document.getElementsByTagName('p')[0].innerText = JSON.stringify(payload);
            })
        });

    </script>
</body>
3 Likes

Thanks for the helpful insights bernhard :slight_smile: .
I will leave a feedback here after trying this approach! :slight_smile:

Thank you for your advice!

I was success to get all data as you recommended. But my problem is - I got all data, and it takes a lot of time to get it. So I’m looking for a solution, How can I pick up just needed pieces of data? I thought that I could get just one package and decode it. I use this doc to get it. But the response I receive with .bin resolution, and I can’t decode it properly. Can you advise how I can get just the needed piece of data from daml?
https://docs.daml.com/json-api/index.html?_ga=2.229756572.1649263458.1659520863-497201012.1642427044&_gl=1*hdodvm*_ga*NDk3MjAxMDEyLjE2NDI0MjcwNDQ.*_ga_GVK9ZHZSMR*MTY1OTUzMjE2NC4zLjEuMTY1OTUzNDA1Mi4w#download-a-dalf-package

You cannot fetch the data below the granularity of a package. If you need it to be more efficient, build a client-side cache to give you access to that information in a more efficient way. Depending on your requirements that could be an in-memory cache or at some point, you want to back that cache by a database.

That’s what I try to do,
I do a call to get some specific package and then try to unarchive with protobuf, the same as in Bernhard’s example.

        function withArchive(path, callback) {
            var req = new XMLHttpRequest();
            req.open("GET", path, true);
            req.responseType = 'arraybuffer';
            req.setRequestHeader("Authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczovL2RhbWwuY29tL2xlZGdlci1hcGkiOnsibGVkZ2VySWQiOiJ4YmZpcnN0LWxlZGdlci1zYW5kYm94IiwiYXBwbGljYXRpb25JZCI6InhiZmlyc3QtbGVkZ2VyLXNhbmRib3giLCJyZWFkQXMiOlsiUHVibGljIl19fQ.7hTOBSYv2POOJjXNLScE3p1ebH4ZMpsYM2a58YWNJM0")
            req.onload = function(e) {
                if (this.status == 200) {
                    callback(new Uint8Array(this.response)); 
                }
            }
            req.send();
        }
protobuf.load("protos-1.17.1/com/daml/daml_lf_1_14/daml_lf.proto", function(err, root) {
            console.log("test")
            if (err)
                throw err;

            // withArchive(".daml/dist/xbfirst-ledger-1.0.0/xbfirst-ledger-1.0.0-bfb772d261fbf18c9fbbed3450e626422b3dde16c840a70e10266df83262cd87/xbfirst-ledger-1.0.0-bfb772d261fbf18c9fbbed3450e626422b3dde16c840a70e10266df83262cd87.dalf", function(buffer) {
                withArchive("http://localhost:7576/v1/packages/cc348d369011362a5190fe96dd1f0dfbc697fdfd10e382b9e9666f0da05961b7", function(buffer) {
                // Get the archive
                var Archive = root.lookupType("daml_lf_1_13.Archive");
                var archive = Archive.decode(buffer);
                // Get the payload

                var Payload = root.lookupType("daml_lf_1_13.ArchivePayload");
                var payload = Payload.decode(archive.payload);

                document.getElementsByTagName('p')[0].innerText = JSON.stringify(payload);
            })
        });

The problem is in Bernhard’s example. We specify a direct path to local package with dalf resolution. But when I try to get some package with API request, I get a response with .bin resolution, and protobuf can’t decode it. How can I get the package with dalf resolution via API request?

What errors are you getting, it works just fine for me.

The package endpoint gives you directly the ArchivePayload without the Archive wrapper so try skipping the part where you decode just the Archive and directly decode what you get from the endpoint to the payload.

Maybe it’s not an important detail for you, but I want to draw your attention to the fact that Navigator actually doesn’t fulfill the “for a given party” part of your requirement. When you log in to Navigator as a certain party, you can see all the choices which are available on a certain contract, not just those ones where the logged-in party is the controller. If the logged-in party tries to exercise a random choice displayed by Navigator, the submission can fail.

The question “Is there an input value args such that party p can call choice c with argument args?” is in general undecidable, even when we pay attention only to the controller clause.

template Foo
  with
    p : Party
    someHash : Text
  where
    signatory p

    choice GoodLuck : ()
      with
        args : Text
      controller (if sha256 args == someHash then [p] else [])
      do
        return () 

You could use some heuristics to exclude choices that obviously won’t work like

    choice Uncallable : ()
      controller ([] : [Party])
      do
        return () 

And you can do that client-side. When you parse the Daml-LF, you get access to the controller expression. You could exclude all choices where that value is a single party or a list of parties, which isn’t a subset of the rights you hold in your claim.

3 Likes

Yes, that’s right.