Skip to content
Discussions/App Development/Variable not in scope: submitMultiForum ↗

Variable not in scope: submitMulti

App Development27 posts1,932 views31 likesLast activity Apr 2021
KR
krmmalikOP
Mar 2021

Following Indentation sensitivity, case sensitivity, and field names vs variable names - #5, I added member as signatory on the Guild template because ultimately I want all parties to agree before the guild can be created.

So, I understand that would then require all parties to authorise the creation of the contract. So for that I used SubmitMulti as outlined in the documentation.

Here is my code:

 guildCid <- submit martin do
    createCmd Individual
      with
        person = martin
        name = "Martin"
        guild
  submitMulti [martin, rita, john, michael, sam, james] [] do
    exerciseCmd guildCid CreateGuild
  pure()

But this gives me the following error

/Users/khurammalik/DevTree/Qirad Agent Network/app/daml/Qirad.daml:113:3: error:
    Variable not in scope:
      submitMulti
        : [Party] -> [a0] -> Commands (ContractId Guild) -> Script a1

I’m confused, because I have added all the parties that need to authorise this submission, I have also checked if they are all able to see this contract and I didn’t supply any arguments to ‘readAs’ as I didn’t feel they’re necessary. I’ve followed the syntax on how to submit this but it still doesn’t seem to be working?

CO
cocreature
Mar 2021

submitMulti is only available in SDK ≥ 1.9, you can see the addition in the corresponding release notes. I suspect you might be using an older SDK.

You also have to import Daml.Script but if you didn’t do that you would also get other errors about createCmd and exerciseCmd being out of scope so I don’t expect that this is the issue.

I think it’s also worth pointing out that while submitMulti is great for test cases, you often can’t rely on being able to use it in your actual application since it requires a single user to have authorization for multiple parties. For those cases, it is common to use a propose and accept workflow. You can find more information on that in our documentation.

KR
krmmalik
Mar 2021

I’m running 1.10

CO
cocreature
Mar 2021

Would it be possible to share your full project so we can try to reproduce this? On SDK 1.10 this should definitely work.

Maybe double check that you have import Daml.Script in your file. As mentioned, I would expect that you get other errors as well if you don’t but still worth making sure it’s there.

KR
krmmalik
Mar 2021

Sure. Actually, even though I have the latest SDK installed, my daml.yaml file actually showed it was running 1.8.1. ( I tried changing it to 1.10.0 to see if it made any difference)

I was indeed using the proposal-accept pattern which is what i was trying to test with the script.
Here is the template layout

module Qirad where

import Daml.Script
import qualified DA.List

-- THE GUILD
-- This is the most important aspect of Qirad

template Guild with
    guildname: Text
    creator: Party
    member: [Party]
  where
    signatory creator, member
    ensure length member >= 5 && DA.List.unique member


template Individual with
    person: Party
    name: Text
    guild: Guild
  where
    signatory person

    controller person can
      CreateGuild : ContractId Guild
        do
          create guild

and then the script


guildtest = do
  martin <- allocateParty "Martin"
  rita <- allocateParty "Rita"
  john <- allocateParty "John"
  michael <- allocateParty "Michael"
  sam <- allocateParty "Sam"
  james <- allocateParty "James"

  let
    guild = Guild with
      guildname = "Ahi"
      creator = martin
      member = [rita, john, michael, sam, james]

  guildCid <- submit martin do
    createCmd Individual
      with
        person = martin
        name = "Martin"
        guild
  submitMulti [martin, rita, john, michael, sam, james] [] do
    exerciseCmd guildCid CreateGuild
  pure()
GA
Gary_Verhaegen
Mar 2021

Hi @krmmalik,

With no changes to your templates, the following test works:

guildtest = do
  martin <- allocateParty "Martin"
  rita <- allocateParty "Rita"
  john <- allocateParty "John"
  michael <- allocateParty "Michael"
  sam <- allocateParty "Sam"
  james <- allocateParty "James"

  let
    guild = Guild with
      guildname = "Ahi"
      creator = martin
      member = [rita, john, michael, sam, james]

  guildCid <- submitMulti [martin, rita, john, michael, sam, james] [] do
    createCmd guild
  pure()

There seems to be a few potential misconceptions here, so allow me to mention some clarifications:

  • The let guild = ... block is not creating a contract on the ledger. This is just creating a data record in the running code that has the same shape a contract would have, but is not itself a contract (no contract id, no signature, and thus also no authority). This type of data record can be useful, of course, and in my test above I use as the argument to the actual create command, but it’s important to realize it is not a contract.
  • What you call guildCid in your test is the contract ID of an Individual contract signed only by Martin. This is were the difference between a data record and a contract is crucial: the guild field in an Individual contract is just pure data in your definition. It is not a contract and is thus not signed, and the Individual contract does not carry the authority of the guild members.
  • Under a recent SDK, your code does find submitMulti but fails on an authorization error. This is because the exerciseCmd guildCid CreateGuild action is “dropping privileges”, in a way: despite the submitMulti having authority as all the parties, the CreateGuild choice on a Individual contract only has the authority of the signatory of the Individual contract and the controller of the CreateGuild choice (which in this case are both Martin). But as you can see in my code, if you do have the authority of everyone in the submit, you can create the guild directly.

Note, however, that, as @cocreature mentioned, this is not a propose/accept pattern, this is simulating a case where Martin somehow has the login credentials of everyone. It is unlikely to reflect a real use-case.

For reference, here is a guild creation with propose/accept. Note that no credential sharing is needed, i.e. all the submits are “non-multi”, i.e. each is done by one individual.

module Main where

import Daml.Script
import qualified DA.List

-- THE GUILD
-- This is the most important aspect of Qirad

template Guild with
    guildName: Text
    creator: Party
    members: [Party]
  where
    signatory creator, members
    ensure length members >= 4 && DA.List.unique members


template ProposeGuild with
    guildName: Text
    creator: Party
    requestedMembers: [Party]
    agreedMembers: [Party]
  where
    signatory creator, agreedMembers
    observer requestedMembers

    preconsuming choice Agree: ContractId ProposeGuild
        with member: Party
        controller member
      do
        assert $ member `elem` requestedMembers
        create this with agreedMembers = member::agreedMembers
                         requestedMembers = DA.List.delete member requestedMembers
    preconsuming choice Create: ContractId Guild
        controller creator
      do
        create Guild with guildName, creator, members = agreedMembers

guildtest = do
  martin <- allocateParty "Martin"
  rita <- allocateParty "Rita"
  john <- allocateParty "John"
  michael <- allocateParty "Michael"
  sam <- allocateParty "Sam"
  james <- allocateParty "James"

  propId1 <- submit martin do
    createCmd ProposeGuild with
      guildName = "Martin's guild"
      creator = martin
      requestedMembers = [rita, john, michael, sam, james]
      agreedMembers = []
  propId2 <- submit rita do
    exerciseCmd propId1 Agree with member = rita
  propId3 <- submit john do
    exerciseCmd propId2 Agree with member = john
  propId4 <- submit michael do
    exerciseCmd propId3 Agree with member = michael
  propId5 <- submit sam do
    exerciseCmd propId4 Agree with member = sam
  guildId <- submit martin do
    exerciseCmd propId5 Create
  pure()

A few more notes on this version:

  • I have decided to reduce the number from 5 to 4 to illustrate the option of having fewer accepting members than requested members.
  • Each prospective guild member is now adding their signature one at a time on the proposal. This is the core of the propose/accept pattern.
  • requestedMembers needs to be observer, as otherwise the invited members are not even aware of the proposal. The agreedMembers do not need to be observers because they are signatories and thus already know of the contract.
  • You may want to do more validation, for example that the proposal doesn’t have duplicates in its requestedMembers list, and that the requestedMembers list (+ agreedMembers) is at least as long as the minimal length for creating a guild. You could also add an explicit “refuse” option.

This is not a complete solution by any means, but hopefully it’s still helpful.

KR
krmmalik
Mar 2021

@Gary_Verhaegen Thank you so much. You are a real gem and the single greatest contributor to my understand of DAML and Smart Contracts. Thank you so much for putting in so much effort and giving me such detailed replies every time. It really means a lot to me.

Thank you for clarifying the difference between the data declarative usage and contract usage and why this isn’t a true propose-accept pattern. I followed an article (not written by someone from Digital Asset) which said this was the way to do it, but to be honest, I had my reservations at the time as well. I am glad you have clarified, because I had my doubts anyway.

Anyhow, I do need to keep the minimum Guild size to 5 but I see what you’re saying (I think).

Ultimately, I want the Guild to be an entity in its own right such as the “use case 2” example in this article from the DAML Blog because in many cases when a Qirad agent creates a proposal, rather than having to list all the people vouching for him, it will make more sense to say he is part of “xyz” guild and then just to specify the guild which will vouch for him as a whole (but of course, all members of the guild then have to sign off on the proposal and are responsible if he runs away with the money).

I started with the simple (what was my then understanding of it) proposal-accept pattern to learn how to use it so then i could use it as a building block to build up from there.

KR
krmmalik
Mar 2021

To further clarify the points in my previous post.

  1. The creator is also a member of the Guild so 1 creator and 4 members make 5 guild members, i wasnt sure how to reflect that the creator is also a member

  2. You asked about constraints. I wanted to ensure that someone that joins a guild is not a member of another guild. I have NO clue about how to even begin that process. Ultimately, what I would like to setup is that one person can only join another guild if they’re either not a member of a guild currently, or they have to first leave the other guild then they can join the new guild.

GA
Gary_Verhaegen
Mar 2021

Hi @krmmalik,

These are interesting constraints. Daml cannot meaningfully have the kind of global, implicit state required to express “a person can only be a member of a single guild” without additional context, because Daml is designed for distributed systems in which nobody may know the entire state of the system.

This is the same in the real world: there is no way to ensure that someone is not a member of two groups without some kind of referee entity that knows about both groups.

The approach you would need to use here is the same kind of structure you would have to set up in the real world: the guild cannot exist by itself, it needs to exist as part of a context; some sort of registry that can see the membership of every guild and thus enforce the constraint. Note that this means “every guild that is part of the same registry”, not “every guild in the whole world”.

Another way to look at this problem is that Daml makes trust decisions explicit: users have to choose who they trust. And by “making explicit” here I mean that the nebulous concept of “the system” that you would have to trust in most other software design approaches does not exist in a Daml system. So ultimately you need to trust someone (person or group) to tell you whether or not a person is trying to be a member of more than one guild, and that someone can only know up to the guilds they can see.

Does that make sense? @bernhard is better than I am at explaining this part.

KR
krmmalik
Mar 2021

Yes @Gary_Verhaegen , that makes sense, thank you. This has given me enough input to know how to address the situation. I’ll keep this in mind going forward. Thank you.

AN
anthony
Mar 2021

A post was split to a new topic: Understanding preconsuming, assert, and this

KR
krmmalik
Mar 2021

I just wanted to go through each line that is new conceptually to me and see if i’ve understood it correctly just so that it can help me move forward. I did reference each line and piece of code against documentation but have some doubts about whether I fully understand what is going on with each line. I’d be grateful for a confirmation on each line to say whether I have understood it correctly or not?

It’s really the choices aspect only. I have my head around contract templates now but choices are completely new to me and this was a lot to take in one go.

so

preconsuming choice Agree: ContractId ProposeGuild

Here we are setting up a choice agree and setting it up for the ProposeGuild contract, yes?
I wasn’t sure why it was a preconsuming choice however. I mean, I understand that the contract is archived beforehand hence the word preconsuming, but I didn’t understand why that is necessary here?

with member: Party

member doesn’t appear in the ProposeGuild or Guild` template, so why do we set it up new here? Why not requestedMember or agreedmember? is it because he/she is neither agreed nor requested at this stage?

assert $ member `elem` requestedMembers

I hardly understood this line at all. Here we are asserting that the member will be mapped into requestedMember?

create this with agreedMembers = member::agreedMembers

Why are we using this ? I’ve not seen that in the creation of a contract before. What does it do? What are we creating here?

That’s my first round of questions, and then I have more on this choice you have setup if you don’t mind. I really appreciate you writing this out for me but I haven’t been able to get my head around what the code is doing exactly.

GA
Gary_Verhaegen
Mar 2021

Hi @krmmalik,

I’ll address one point at a time.

preconsuming choice Agree: ContractId ProposeGuild

Here we are setting up a choice agree and setting it up for the ProposeGuild contract, yes?

You can think of choices as functions acting on templates. Here we declare a new choice called Agree that acts on contracts of type ProposeGuild and has a return value of type ContractId ProposeGuild. In other words, when you later on write something like:

ret <- submit party do exerciseCmd proposeCid Agree with ..

the type of ret will be ContractId ProposeGuild.

I wasn’t sure why it was a preconsuming choice however.

A choice can have one of four qualifiers:

  • preconsuming archives the contract before the choice code is executed; this means that the choice code cannot fetch the current contract, because it has already been archived when the code runs. Note that this does not prevent you from accessing the payload (attributes) of the current contract, as those are still in scope.
  • postconsuming archives the contract after the choice has finished running. This means the choice code can fetch the contract if it needs to, but it also means you cannot create a new contract with the same key (because the current one has not been archived yet).
  • nonconsuming means the Daml engine will not automatically archive the contract for you. Note that this does not always mean that the choice does not archive the contract, as the choice code can still call archive self explicitly. This can be very useful if you want to do something before and after archiving, or if you only want to archive if some conditions are met.
  • The default behaviour, when no qualifier is present before the choice keyword, is what is known as a consuming choice in the documentation. Consuming choices have additional properties with respect to privacy that I do not fully understand, and therefore I tend to avoid them. This is why I default to preconsuming unless I have a good reason to use one of the others. In this specific case I think all three options (postconsuming, preconsuming and consuming) that end up archiving the contract would work equally well.
with member: Party

member doesn’t appear in the ProposeGuild or Guild` template, so why do we set it up new here? Why not requestedMember or agreedmember? is it because he/she is neither agreed nor requested at this stage?

This with ... syntax is defining the parameters of the choice, which you can think of as the function arguments. When you exercise the choice, you need to supply them; in this case, in order to call the choice with exerciseCmd, you will need to supply a party. We do not want to reuse a name that already exists here specifically because we want to be able to compare this member party provided by the caller of the API (who would be able to pass in whatever they want) with the parties saved in the contract (agreedMembers and requestedMembers).

assert $ member `elem` requestedMembers

I hardly understood this line at all. Here we are asserting that the member will be mapped into requestedMember?

Not quite. assert is a special operation that takes one Boolean argument (True or False) and does nothing if it is True. In this case, the expression:

member `elem` requestedMember

is True if the member supplied by the person calling the API to exercise the Agree choice is present in the requestedMember list supplied by the person who created the ProposeGuild contract, and False if member is not an element of the requestedMembers list.

If the argument to assert is False, the Daml system interrupts the execution and cancels the current transaction, without having made any change to the ledger. So this line can be read as “proceed only if the person who is currently accepting is someone who has been asked and has not responded yet”.

create this with agreedMembers = member::agreedMembers

Why are we using this ?

The create function takes a payload for a contract and creates a corresponding entry on the ledger. The syntax for creating records (contract payloads are always records) in Daml is fairly varied. Assuming a record defined by:

data MyRecord = MyRecord with a: Int, b: Text

which, as you may recall, can be used directly in Daml, but is also what would be generated for a template:

template MyRecord
  with
    a: Int
    b: Text
  where
  ...

the most direct way to create a record of that type would be:

let mr = MyRecord with a = 1, b = "hello"

But if you already have one, there is a convenient notation to copy it then change only a few keys:

let mr2 = mr with a = 3 -- same as MyRecord with a = 3, b = "hello"

Getting back to our use-case, in the code of a choice you always have access to the special this variable that represents the payload of the contract the choice is being exercised on. So in this case the syntax:

this with agreedMembers = member::agreedMembers
                requestedMembers = DA.List.Delete member requestedMembers

is just a shorthand for:

ProposeGuild with guildName = guildName
                  creator = creator
                  agreedMembers = member::agreedMembers
                  requestedMembers = DA.List.Delete member 

Did I mention that the syntax to create records is very varied? Both of these would also have been equivalent in this context:

ProposeGuild with guildName -- if there is no "=", use the value with the
                  creator -- same name in the current scope
                  agreedMembers = member::agreedMembers
                  requestedMembers = DA.List.Delete member 
ProposeGuild with agreedMembers = member::agreedMembers
                  requestedMembers = DA.List.Delete member
                  .. -- .. means "use values from scope" for
                     -- all values that have the same name as a field

You may also have been confused by the expressions member::agreedMembers and DA.List.delete member requestedMembers. The first one,

member :: agreedMembers

is using the “cons” operator :: to construct a list. You can see this in operation at the repl:

$ daml repl
daml> let a = [1, 2]
daml> 3 :: a
[3,1,2]
daml> 

The function DA.List.delete elem list returns a new list that has the same elements as list except for the first occurrence of elem. Back to the repl:

daml> import DA.List
daml> DA.List.delete 1 [2, 3, 4]
[2,3,4]
daml> DA.List.delete 4 [2, 3, 4]
[2,3]
daml> DA.List.delete 4 [2, 3, 4, 5, 4]
[2,3,5,4]
daml> 

One important line you did not ask about is:

controller member

This tells Daml that only the party member is allowed to exercise this choice. In this case, the combination of these two lines:

        with member: Party
        controller member

means that the person (party) exercising the choice has to pass themselves as a parameter, as in:

  propId2 <- submit rita do
    exerciseCmd propId1 Agree with member = rita

In many cases the controller of a choice will be a single, fixed, known party identified directly on the contract (say, in this case, creator). But in some cases (like here) it is useful to make the controller of a choice an explicit parameter of that choice. Daml will ensure that only the party itself can submit that choice (i.e. Rita could not do exerciseCmd propId2 Agree with member = john), but we can then add more constraints on it if we want. In this case, it is useful to restrict further to only people who are invited.

Hope that helps clarify some of your questions; do not hesitate to ask more if needed.

KR
krmmalik
Mar 2021

Holy wowsers. Thank you so much for breaking this down for me. I’m going to take some time to study everything that you’ve said line by line and get back to you.

I was actually rather embarrassed to ask about some of the other stuff you mentioned that I didn’t so I’m really glad you covered it because it probably would have been my next set of questions.

Please allow me some time and I will get back to you.

AN
anthony
Mar 2021
krmmalik:

rather embarrassed

No need to be, none of us were born programmers.

KR
krmmalik
Mar 2021

I appreciate you saying that. This means much more to me than you know. A lot of this journey – even though it’s just begun – has just been dealing with the sheer imposter syndrome of it all.

AN
anthony
Mar 2021
krmmalik:

been dealing with the sheer imposter syndrome of it all

Let me know when you get over it, I sure haven’t lol

KR
krmmalik
Mar 2021

Aha! That’s ironically quite reassuring!

KR
krmmalik
Mar 2021

Just circling back here @Gary_Verhaegen to let you know I finally managed to get back to working on this. Apologies for getting back to you after quite some time.

I went through each line, line by line and all of your explanations and I managed to understand exactly what is going on now, thanks to your explanations. So a big thank you!

GA
Gary_Verhaegen
Mar 2021

No worry at all and glad I could be of assistance!

KR
krmmalik
Mar 2021

So I have a next set of questions which is not around requiring further explanations on the previous as such but next on my journey forward. Admin might want to break this out into a new thread however, but it does require some repetition so i’ll repost some code to carry along the background context.

I have setup a proposal contract so a party can issue proposals and for that I need all members of the Guild to be a signatory on the issuing of the proposal.

Now rather than doing that in a drawn out explicit way, I was reading Use Case 2 in this blog post and wondering if I can use that same learning?

I know that I have set up Guild as a contract template here, but is there a way I could group the creator and members together so that I can have one entity as a signatory on my proposal contract?

I tried to follow the blog post example but while i understand it somewhat conceptually I got stuck trying to implement it in practice.

Here’s my code for the Proposal contract:

template Proposal with
    issuer: Party
    investor: Party
    projectdescription: Text
    unitsrequired: Int
    marketingcost: Int
    distributioncost: Int
    additionalcost: Int
    proposalId: Text -- Key
    item: Item

  where
    signatory issuer
    observer investor

This is where I wanted to add something like say

signatory issuer, guild

Where all the members of the Guild sign off on the proposal. Note that the issuer would have to be said member of the guild as well and there would be overlap between the issuer and members of the guild as the issuer is also a member of the guild as well anyway. Only a guild member can issue a proposal.

I know that right now I could do something like

guild: Guild

In the template

and then I could add something like

signatory issuer, guild.members

(not sure if my syntax is entirely correct here, but hopefully this demonstrates the point)

and then all the members of the guild would have to sign-off on this proposal. I don’t know if I would have to run an extra check before hand to see if that person is a member of said guild and if the guild needs to be specified beforehand but this is the general pattern I was going to follow logically speaking.

Would this be the right way to go about it? Or perhaps there is a more concise/elegant way of doing it?

AN
anthony
Mar 2021
krmmalik:

and then all the members of the guild would have to sign-off on this proposal. I don’t know if I would have to run an extra check before hand to see if that person is a member of said guild and if the guild needs to be specified beforehand but this is the general pattern I was going to follow logically speaking.

This way sounds right to me, you’d just need to use the Propose and Accept pattern to make sure everyone signs the contract. If you want to make sure the issuer is a member of guild.members then you can add an ensure clause like:

ensure issuer `elem` guild.members -- can also write this as the following but it's less common: ensure elem issuer guild.members

Here the function elem is specified inline between the arguments issuer and guild.members and it returns a Bool, evaluating to True if issuer is an element of the guild.members list. Then ensure only succeeds if the boolean it receives is True if this check failed then the contract wouldn’t be allowed to be created.

Now because you’re using ensure to make sure the issuer is an element of the guild.members then you technically don’t need to specify the issuer twice so your signatory line can simply be:

signatory guild.members

One gotcha here is that any guild.member can attempt to make any member an issuer unless the issuer is careful to check that new obligations aren’t being made for them that they didn’t consent to so this will need to be handled in client-side code to check.

Here’s my full code for reference:

template Guild
  with
    members: [Party]
    owner: Party
  where
    signatory owner

template Proposal with
    issuer: Party
    investor: Party
    guild: Guild
    projectdescription: Text
    unitsrequired: Int
    marketingcost: Int
    distributioncost: Int
    additionalcost: Int
    proposalId: Text -- Key
    item: Item

  where
    signatory guild.members
    ensure issuer `elem` guild.members
    observer investor
KR
krmmalik
Mar 2021

Thanks! I’ll try implementing this next week and report back.

GA
Gary_Verhaegen
Mar 2021

Hi @krmmalik,

This is veering a bit away from my expertise. While I know how the Daml language works “in the small”, I have not personally written any large Daml application and am thus not best placed to recommend “big” design decisions. I’ll comment as best I can, but I won’t be able to provide definite answers.

krmmalik:

Now rather than doing that in a drawn out explicit way, I was reading Use Case 2 in this blog post and wondering if I can use that same learning?

I think it’s important to clarify exactly what you want to express here. The delegation pattern in this blog post illustrates a way to give special authority to each member of a group. This is is not the same as saying every single member of the group has agreed to the decision.

In the specific example of the blog post, Alice is given authority to add people to the legal team, and then she can just go and do it. Note that:

  • She does not need to be a member of the legal team herself.
  • Other admins do not need to vet what she’s doing; there is no record of them ever agreeing to anything she does.
  • Members of the legal team do not know about each other.
  • Other team admins may not know about Alice being an admin until they observe her first admin action.
  • Other admins are not involved in nominating a new admin; this is a unilateral decision on the part of org.

Some of these can be easily changed (e.g. adding group as an observer on the GroupMember template would mean group members know about each other), but the fundamental process here is one of delegation: an authority figure (here the org party) gives a limited set of administrative powers individually to each person in a group. The group is there to facilitate the management of those administrative rights; individual members do not “act as a group”, so to speak.

The actions of Alice can only reasonably engage the responsibility of Alice herself and of org; other admins have no say in the matter.

Another point to note is that, because the blog post defines group membership as the existence of a (manager, group, member) contract on the ledger, with only manager and member as stakeholders, groups do not exist on the ledger as a separate thing and group members do not (necessarily) know about each other. This is fine for permission groups, but from what I understand of your use-case I’m not sure it’s a good fit. You probably want to be able to talk about properties of the guild as a separate thing itself.

krmmalik:

I have setup a proposal contract so a party can issue proposals and for that I need all members of the Guild to be a signatory on the issuing of the proposal .

If you wanted to say that every member of a guild can make proposals to investors on behalf of the entire guild without needing the explicit approval of other guild members, then you could use the pattern from the blog post. You would likely still need a propose/accept pattern between “the guild” and the investors, though.

GA
Gary_Verhaegen
Mar 2021

I think it’s worth noting that there are really two “stages” of propose/accept in this process:

  1. A guild member wants to make a proposal to an investor and needs to collect the signature of other guild members.
  2. “The guild”, or a subset thereof, makes a proposal (signed by all, or “enough”, guild members) to an investor.

@anthony’s Proposal template is already the second stage of that process. The names are getting in the way a little bit here, but you’d likely want to have a ProposalProposal template made by a single member to the rest of the guild first (and that would get rid of the issuer problem mentioned by @anthony). You could work around the awkward names by using more creative (but longer) names for your templates, such as GuildMemberWantsToMakeProposal and ProposalFromGuildToInvestors. Or, well, you can probably come up with something better. The point is, because you have two levels of propose/accept, you’ll likely need at least 3 templates:

  1. The initial template created by a single guild member to collect all the signatures (we’ve discussed that one in the past).
  2. The template that represents a “signed by the guild” proposal ready to be shown to an investor, with the investor(s) as observers, used to collect investor signatures.
  3. A final template that represents the agreed upon contract, where all the guild members and all the participating investors are signatories.
KR
krmmalik
Mar 2021

Thanks for the great write-ups as always @Gary_Verhaegen . Experience “in the small” is very valuable to me here, although on a sidenote, it would be nice to pick the brain of someone that has worked on a large application for maybe a podcast interview or something. That would be super interesting.

Anyway, I appreciate you breaking down the differences between what I am intending and what the blog post is describing. I didn’t realise DAML allows you to be so granular about the way things are agreed and what authority is required in light of that.

I’m going to attempt the proposal-accept pattern the way you suggested over the next few days. To be honest, I did have a feeling it would have to be done in some way similar to this and it will now give me a chance to practise the proposal-accept pattern a little further.

Thanks again.

CO
cocreature
Apr 2021

A post was split to a new topic: Indentation error in record creation

← Back to Discussions