Using a standard library module with constraints (map function)

I want to ensure that my contract contains a minimum of 4 parties as “Hawalla’s”

So I imported DA Next Map with:

import DA.Next.Map as NM

and then added the ensure constraint, but I’m getting a parser error. I read the documentation for this module. I don’t think I’m using the key/value pairs correctly since in the documentation it says the function is injective, so I suspect I’m missing a step here but not sure what.

Here’s the code I wrote which is giving me a parser error on the ensure constraint

template User with
    agent: Party
    hawalla: [Party]
  where
    signatory agent
    observer hawalla
    
    ensure NM.size hawalla -> 4

Any help please

1 Like

You want to use length to get the size of the list and then compare against that. Also the operation for comparing if an Int is greater than or equal to another would be >=.

ensure length hawalla >= 4

I believe -> is only used for type definitions. Kind of an aside but one way to find out what function you want to use is to search our custom hoogle for a type definition that matches what you’re thinking of.

For example to find length what I did was go to hoogle and say I want a function that takes a list [a] and gives back an Int because that’s what length does so I found it with this query: [a] -> Int - Hoogle

Then for >= I said I want a function that takes two numbers Int and Int and gives me back a boolean Bool as that’s what all the integer comparison functions do: Int -> Int -> Bool - Hoogle

In both cases I actually get back multiple functions because each type definition I searched for had different things it could do (like compare for equality, or greater than) but it really helps narrow down the search once you get the hang of it.

@anthony’s answer is going in the right direction, but, as I understand it, you not only want 4 parties, you want to verify that they are 4 different parties. We’ll need two new functions for that. The first one is DA.List.unique, which is True if there are no duplicates in the given list. The second one is notElem, which is True if its first argument (type a) is not present in its second argument (type [a], or "list of a").

Using those, you could write something like:

module Main where

import Daml.Script
import qualified DA.List

template User
  with
    agent: Party
    hawalla: [Party]
  where
    ensure length hawalla >= 4 && DA.List.unique hawalla && agent `notElem` hawalla
    signatory agent

setup : Script ()
setup = script do
  alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")
  h1 <- allocatePartyWithHint "1" (PartyIdHint "1")
  h2 <- allocatePartyWithHint "2" (PartyIdHint "2")
  h3 <- allocatePartyWithHint "3" (PartyIdHint "3")
  h4 <- allocatePartyWithHint "4" (PartyIdHint "4")

  aliceTV <- submitMustFail alice do
    createCmd User with
      agent = alice
      hawalla = [h1, h2, h3]

  aliceTV <- submit alice do
    createCmd User with
      agent = alice
      hawalla = [h1, h2, h3, h4]

  aliceTV <- submitMustFail alice do
    createCmd User with
      agent = alice
      hawalla = [h1, h2, h3, h3]
  
  aliceTV <- submitMustFail alice do
    createCmd User with
      agent = alice
      hawalla = [h1, h2, h3, alice]
  
  return ()

Note that:

  • This ensure clause is complex enough that I’d recommend extracting it to a separate (top-level) function.
  • There are of course other ways to achieve the same; using DA.Next.Map is an option, but it will end up being more complicated and DA.Next.Map is slated for deprecation soon so I wouldn’t spend much time exploring that option.

Final note. With this simple approach, you have 4 parties in the hawalla field, but there is no guarantee that these parties exist, nor that the people behind those parties have signed any sort of agreement. For that, you’ll need to make the entire hawalla signatory (signatory agent, hawalla achieves that), but then, before you can create a contract of type User on the ledger, you’ll need to collect the authority of every single party in the desired hawalla list. That will require a propose/accept pattern; if you’re not clear on what that is, a good starting point would be the Iou template described in the documentation. If you have not read through it yet, I actually recommend the entire Introduction to Daml series.

1 Like

Yeah this is likely true and makes @Gary_Verhaegen’s answer the right one for this usecase.

Also worth noting is that `notElem` is using infix notation in the example above which is the same as typing out notElem agent hawalla and works with any function that takes two arguments. You’ll probably see the infix version most of the time for things like this as they read closer to how you’d say it in English so felt it was worth pointing out.

3 Likes

Thank you very much for this detailed write-up @Gary_Verhaegen . I’m going to study each line in detail to get a full understanding. I’ve gotten the gist of the underlying thought process here anyhow which has really helped.

You are right, I am going to need a proposal/accept pattern because the whole point of the agent here is that he gets each hawalla to agree to vouch for him. Long term, and ultimately I’m going to need to create groups of Hawalla’s in which each hawalla is an agent and each agent is a hawalla and the entire group forms a guild. That way, if one agent runs off with the money, the entire guild is responsible for paying back the investor(s) and also investors make their investment decisions based on the reputation of a guild as a whole.

1 Like

A post was split to a new topic: What is a qualified import?

Also, I didn’t quite understand this line

aliceTV <- submitMustFail alice do

My confusion comes from the use of AliceTV , where did “AliceTV” come from or what is the significance of it?

Ah, my bad, this is a confusing mistake on my end. There should not be any aliceTV in this code snippet.

As a more general answer, Daml code runs “next to” the ledger, but is not directly integrated with the ledger. This means that you have a notion of “asking things from the ledger”, and “getting an answer”. A value of type Action a in Daml represents a question that could be asked of the ledger* and for which the ledger will respond with a value of type a.

*: Action is actually a little bit more general than that, but this hand-wavy explanation serves our purposes for now.

The special syntax var <- expr, where var is a variable name and expr is an expression of type Action a, is what allows a Daml program to “realize” that question, i.e. effectively connect to the ledger and ask for the result of the given Action, saving the response (of type a in this case) as var for further use by the Daml code locally. The <- syntax can only be used within the context of a do block.

Let’s look at the default template (daml new t) for an example:

setup : Script AssetId
setup = script do
  alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")
  bob <- allocatePartyWithHint "Bob" (PartyIdHint "Bob")

  aliceTV <- submit alice do
    createCmd Asset with
      issuer = alice
      owner = alice
      name = "TV"

  bobTV <- submit alice do
    exerciseCmd aliceTV Give with newOwner = bob

  submit bob do
    exerciseCmd bobTV Give with newOwner = alice

First off, notice that setup is of type Script. This is not explicit here, but if you look at the documentation for Script you will see that Script is a type of Action (this is what the line instance Action Script means), so we can use the do and <- notations.

The first line of this script reads:

  alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")

If you look through the documentation, you will see that the return value of allocatePartyWithHint is Script Party, which means that this line means: “execute the allocatePartyWithHint operation on the ledger, get the answer, and then store that under the name alice`”.

Similarly, submit party cmds is of type Script, which is why we can save aliceTV and bobTV down the line.

So going back to the original question, in

aliceTV <- submitMustFail alice do

aliceTV is defined at this point; it did not exist before. (Each of the four blocks actually defines a “new” aliceTV that has no relationship with the previous one; this is a terrible practice and you should not do that.) All of those blocks really should just be:

submitMustFail alice do ...

Hope that helps.

2 Likes

Great. Thank you so much.

1 Like