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
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
ensureclause 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.Mapis an option, but it will end up being more complicated andDA.Next.Mapis 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.
but, as I understand it, you not only want 4 parties, you want to verify that they are 4 different parties.
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.
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.
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.
*:
Actionis 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.
Great. Thank you so much.