Scenarios in different file
Mod note: As of SDK 1.5.0 Scenarios have been superseded by the more powerful Daml Script. We now recommend using that for all purposes. For more information, and to learn how to use Script please check out @Andreas’ post on our blog.
Hi,
I create a scenario in one file and exercise scenario in another. Create is running successfully but while running exercise is giving error: Variable not in scope: iouEurBankCid : ContractId t0typecheck
I have also imported Createfile. I want two separate files and don’t want to merge them. Any workarounds?
File1: Exercise file
import CreateFile
iouTransferAliceCid <- submit eurBank do
exerciseCmd iouEurBankCid Iou_Transfer with newOwner = alice
File 2: Create File
iouEurBankCid <- submit eurBank do
createCmd Iou with
issuer = eurBank
owner = eurBank
currency = "EUR"
amount = 100.0
observers = [alice,bob,usBank]Preformatted textHi Nishant,
Are you sure these files compile? Someone should correct me if I’m wrong, but I don’t believe it’s valid DAML to use do variable binding syntax at the top level of a file line this
iouTransferAliceCid <- submit eurBank do
...
I believe you will have to do something like this:
myScenario : Scenario ()
myScenario = do
iouEurBankCid <- submit eurBank do
createCmd Iou with
issuer = eurBank
owner = eurBank
currency = "EUR"
amount = 100.0
observers = [alice,bob,usBank]
iouTransferAliceCid <- submit eurBank do
exerciseCmd iouEurBankCid Iou_Transfer with newOwner = Alice
You can’t generate values in one scenario and reference them in another, but you can execute a scenario from one file within another scenario.
Edit: If your aim is to factor out some common code, then you could do:
-- *** CreateFile.daml ***
createDefaultIou : Scenario (ContractId Iou)
createDefaultIou = submit eurBank do
createCmd Iou with
issuer = eurBank
owner = eurBank
currency = "EUR"
amount = 100.0
observers = [alice,bob,usBank]
-- *** ExerciseFile.daml ***
myScenario : Scenario ()
myScenario = do
iouEurBankCid <- createDefaultIou
iouTransferAliceCid <- submit eurBank do
exerciseCmd iouEurBankCid Iou_Transfer with newOwner = Alice
This is giving error. Not sure why
module CreateFile where
import Daml.Script
import Iou
import IouTrade()
trade_exercise : Script ()
trade_exercise = do
-- allocate parties
alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")
bob <- allocatePartyWithHint "Bob" (PartyIdHint "Bob")
usBank <- allocatePartyWithHint "USD_Bank" (PartyIdHint "USD_Bank")
eurBank <- allocatePartyWithHint "EUR_Bank`Preformatted text`" (PartyIdHint "EUR_Bank")
createDefaultIou : Script (ContractId Iou)
createDefaultIou = submit eurBank do
createCmd Iou with
issuer = eurBank
owner = eurBank
currency = "EUR"
amount = 100.0
observers = [alice,bob,usBank]
pure ()
The problem here is the definition of createDefaultIou. In a do block you have to options:
- Either you bind something using
<-as you are doing withallocatePartyWithHint. You can do that to fix your example:
trade_exercise : Script ()
trade_exercise = do
-- allocate parties
alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")
bob <- allocatePartyWithHint "Bob" (PartyIdHint "Bob")
usBank <- allocatePartyWithHint "USD_Bank" (PartyIdHint "USD_Bank")
eurBank <- allocatePartyWithHint "EUR_Bank`Preformatted text`" (PartyIdHint "EUR_Bank")
createDefaultIou <- submit eurBank do
createCmd Iou with
issuer = eurBank
owner = eurBank
currency = "EUR"
amount = 100.0
observers = [alice,bob,usBank]
pure ()
- Or you define a variable with
let variableName = expression. Note that this will only evaluateexpression but not execute any effects associated with it. So if you change your example, to use aletto definecreateDefaultIouthe command won’t actually be submitted. You can callcreateDefaultIou` afterwards to actually create it:
trade_exercise : Script ()
trade_exercise = do
-- allocate parties
alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")
bob <- allocatePartyWithHint "Bob" (PartyIdHint "Bob")
usBank <- allocatePartyWithHint "USD_Bank" (PartyIdHint "USD_Bank")
eurBank <- allocatePartyWithHint "EUR_Bank`Preformatted text`" (PartyIdHint "EUR_Bank")
let createDefaultIou : Script (ContractId Iou)
createDefaultIou = submit eurBank do
createCmd Iou with
issuer = eurBank
owner = eurBank
currency = "EUR"
amount = 100.0
observers = [alice,bob,usBank]
createDefaultIou
pure ()
In both mentioned above cases. I am not able to use createDefaultIou variable in ExerciseFile.daml.
Variable not in scope: createDefaultIou : Script (ContractId t0)typecheck
-- *** ExerciseFile.daml ***
myScenario : Scenario ()
myScenario = do
iouEurBankCid <- createDefaultIou
iouTransferAliceCid <- submit eurBank do
exerciseCmd iouEurBankCid Iou_Transfer with newOwner = AliceIf you want to call it from another definition, you need to define it as a top-level definition, so something like:
data Parties = Parties with
alice : Party
bob : Party
usBank : Party
eurBank : Party
trade_exercise : Script ()
trade_exercise = do
-- allocate parties
alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")
bob <- allocatePartyWithHint "Bob" (PartyIdHint "Bob")
usBank <- allocatePartyWithHint "USD_Bank" (PartyIdHint "USD_Bank")
eurBank <- allocatePartyWithHint "EUR_Bank`Preformatted text`" (PartyIdHint "EUR_Bank")
createDefaultIou Parties{..}
pure ()
createDefaultIou : Parties -> Script (ContractId Iou)
createDefaultIou Parties{..} = submit eurBank do
createCmd Iou with
issuer = eurBank
owner = eurBank
currency = "EUR"
amount = 100.0
observers = [alice,bob,usBank]
Note that createDefaultIou accepts the parties as an argument. I’ve chosen to define a datatype to make it easy to pass around all 4 of them instead of having to deal with 4 separate arguments.
Same issue as above. I cannot use createDefaultIou in ExerciseFile,daml.
Is it possible to share both files in the current state you are using?
File 1
module CreateFile where
import Daml.Script
import Iou
import IouTrade()
data Parties = Parties with
alice : Party
bob : Party
usBank : Party
eurBank : Party
trade_exercise : Script ()
trade_exercise = do
-- allocate parties
alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")
bob <- allocatePartyWithHint "Bob" (PartyIdHint "Bob")
usBank <- allocatePartyWithHint "USD_Bank" (PartyIdHint "USD_Bank")
eurBank <- allocatePartyWithHint "EUR_Bank`Preformatted text`" (PartyIdHint "EUR_Bank")
createDefaultIou Parties{..}
pure ()
createDefaultIou : Parties -> Script (ContractId Iou)
createDefaultIou Parties{..} = submit eurBank do
createCmd Iou with
issuer = eurBank
owner = eurBank
currency = "EUR"
amount = 100.0
observers = [alice,bob,usBank]
File 2
module ExerciseFile where
import CreateFile
import Daml.Script
import Iou
import IouTrade()
trade_exercise : Script ()
trade_exercise = do
-- allocate parties
alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")
bob <- allocatePartyWithHint "Bob" (PartyIdHint "Bob")
usBank <- allocatePartyWithHint "USD_Bank" (PartyIdHint "USD_Bank")
eurBank <- allocatePartyWithHint "EUR_Bank" (PartyIdHint "EUR_Bank")
iouEurBankCid <- createDefaultIou
iouTransferAliceCid <- submit eurBank do
exerciseCmd iouEurBankCid Iou_Transfer with newOwner = alice
pure ()You forgot to pass in the parties, if you change ExerciseFile to
module ExerciseFile where
import CreateFile
import Daml.Script
import Iou
import IouTrade()
trade_exercise : Script ()
trade_exercise = do
-- allocate parties
alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")
bob <- allocatePartyWithHint "Bob" (PartyIdHint "Bob")
usBank <- allocatePartyWithHint "USD_Bank" (PartyIdHint "USD_Bank")
eurBank <- allocatePartyWithHint "EUR_Bank" (PartyIdHint "EUR_Bank")
iouEurBankCid <- createDefaultIou Parties{..}
iouTransferAliceCid <- submit eurBank do
exerciseCmd iouEurBankCid Iou_Transfer with newOwner = alice
pure ()
it works.
Hi,
Thanks. Above solution is working fine on daml studio but not with sandbox.
daml script --dar .daml/dist/quickstart-0.0.1.dar --script-name CreateFile:trade_exercise --ledger-host localhost --ledger-port 6865
It is working but if after running this command when I run
daml script --dar .daml/dist/quickstart-0.0.1.dar --script-name ExerciseFile:trade_exercise --ledger-host localhost --ledger-port 6865
Exception in thread “main” io.grpc.StatusRuntimeException: INVALID_ARGUMENT: Invalid argument: Party already exists
In the second scenario, you don’t need to allocate the parties anymore, because you already did when running the first script. Instead, you can pass the parties as arguments to the second script and run the script with the --input-file option with a file containing the allocated parties. The process is described in more detail here: https://docs.daml.com/daml-script/index.html.
It is giving ambiguous party issue
module MoorgateCreate where
import Daml.Script
import Iou
import IouTrade()
data Parties = Parties with
alice : Party
bob : Party
usBank : Party
eurBank : Party
createDefaultIou : Parties -> Script (ContractId Iou)
createDefaultIou parties = do
submit parties.eurBank do
createCmd Iou with
issuer = parties.eurBank
owner = parties.eurBank
currency = "EUR"
amount = 100.0
observers = [parties.alice,parties.bob,parties.usBank]
module MoorgateExercise where
import MoorgateCreate
import Daml.Script
import Iou
import IouTrade()
data Parties = Parties with
alice : Party
bob : Party
usBank : Party
eurBank : Party
trade : MoorgateExercise.Parties -> Script ()
trade parties = do
iouEurBankCid <- createDefaultIou MoorgateExercise.Parties{..}
iouTransferAliceCid <- submit parties.eurBank do
exerciseCmd iouEurBankCid Iou_Transfer with newOwner = parties.alice
pure ()You only need to define the datatype Parties one in MoorgateCreate. Remove the second definition from MoorgateExercise.
If I remove it. It says party doesn’t have required strict fileds.
module MoorgateExercise where
import MoorgateCreate
import Daml.Script
import Iou
import IouTrade()
trade : Parties -> Script ()
trade parties = do
iouEurBankCid <- createDefaultIou Parties{..}
iouTransferAliceCid <- submit parties.eurBank do
exerciseCmd iouEurBankCid Iou_Transfer with newOwner = parties.alice
pure ()Hi @Nishant_Bansal,
I’m not entirely sure what you’re trying to do here, but I’ve taken what you have and tried to make the minimal amount of changes to get something that runs. Hopefully that helps. I have had to guess a bit around Iou and IouTrade as you hadn’t shared those; hope that’s not going to be confusing.
So here we go. First, IouTrade is not actually used so far, so I’ve just defined an empty module. Here is IouTrade.daml:
module IouTrade where
Then, Iou.daml, reconstructed from the way you use it:
module Iou where
template Iou with
issuer: Party
owner: Party
currency: Text
amount: Decimal
observers: [Party]
where
signatory issuer, owner
observer observers
preconsuming choice Iou_Transfer: ContractId Transfer
with newOwner: Party
controller owner
do
create Transfer with currentOwner = owner, proposedOwner = newOwner
-- TODO
template Transfer with
currentOwner: Party
proposedOwner: Party
where
signatory currentOwner
Now, on to the interesting ones. Looking first at MoorgateCreate.daml, it’s essentially unchanged, except for me pushing my pet peeve on qualified imports. I do believe that in tis case they make things a bit clearer.
module MoorgateCreate where
import Daml.Script
import qualified Iou
import qualified IouTrade()
data Parties = Parties with
alice : Party
bob : Party
usBank : Party
eurBank : Party
createDefaultIou : Parties -> Script (ContractId Iou.Iou)
createDefaultIou parties = do
submit parties.eurBank do
createCmd Iou.Iou with
issuer = parties.eurBank
owner = parties.eurBank
currency = "EUR"
amount = 100.0
observers = [parties.alice,parties.bob,parties.usBank]
Importantly, note that createDefaultIou is in this case a function, so it does not run yet.
And, finally, the one that brings them together, MoorgateExercise.daml:
module MoorgateExercise where
import qualified MoorgateCreate
import Daml.Script
import qualified Iou
import qualified IouTrade()
p: Text -> Script Party
p t = allocatePartyWithHint t (PartyIdHint t)
trade : Script ()
trade = do
alice <- p "alice"
bob <- p "bob"
eurBank <- p "eurBank"
usBank <- p "usBank"
let parties = MoorgateCreate.Parties { alice, bob, usBank, eurBank }
iouEurBankCid <- MoorgateCreate.createDefaultIou parties
iouTransferAliceCid <- submit parties.eurBank do
exerciseCmd iouEurBankCid Iou.Iou_Transfer with newOwner = parties.alice
return ()
Hi @Gary_Verhaegen,
I actually want to keep them separate. I have sandbox running. Now I want to
- Create a contract on ledger through daml script command.
daml script --dar .daml/dist/quickstart-0.0.1.dar --script-name CreateFile:trade_exercise --ledger-host localhost --ledger-port 6865 - I want contract id from this script and want to erercise choice on it. through
daml script --dar .daml/dist/quickstart-0.0.1.dar --script-name ExerciseFile:trade_exercise --ledger-host localhost --ledger-port 6865
These two are my separate script. One script is creating a contract other is exercising choice on it.
Here script1 means CreateFile.daml and Script2 means ExerciseFile.daml
So if you want to be able to run them as separate runs, you’ll still need some way of communicating between them. One mechanism we provide for that is by serializing DAML values as JSON through the --input-file and --output-file options to daml script. So in your case, you should make sure that the end result of the script in MoorgateCreate returns the ContractID of the contract it has created, save that as an --output-file, and then when you run the second script, you can use that same file as the --input-file.
You can find out how DAML-LF values translate to JSON here, though in this case you may not need to look at it at all as long as you ensure that the types align in your DAML code.
In particular, you’ll want for the second script to be a function (the first one probably shouldn’t be), and for that function to take as argument the same type as the output type of the first script.
Here’s a small, self-contained, concrete example. The Main.daml file would read:
module Main where
import Daml.Script
template Asset
with
p: Party
mark: Text
where
signatory p
nonconsuming choice Echo: Text
with
msg: Text
controller p
do
return $ mark <> msg
step1: Text -> Script (ContractId Asset)
step1 mark = script do
alice <- allocatePartyWithHint "alice" (PartyIdHint "alice")
submit alice $ createCmd Asset with p = alice, mark
step2: ContractId Asset -> Script ()
step2 cid = script do
-- created by step 1
let Some alice = partyFromText "alice"
msg <- submit alice $ exerciseCmd cid Echo with msg = "hello"
debug msg
The important point here is that the step1 function returns a ContractId Asset, and the step2 function takes a ContractId Asset as an argument. So then you can run (assuming a project named t because that’s my default name for projects, and after having started a sandbox somewhere with daml sandbox):
daml build
daml ledger upload-dar .daml/dist/t-0.0.1.dar --host localhost --port 6865
echo '"mark1"' > input_step_1.json
daml script --dar .daml/dist/t-0.0.1.dar \
--ledger-host localhost \
--ledger-port 6865 \
--script-name Main:step1 \
--input-file input_step_1.json \
--output-file output_step_1.json
and that should create a file output_step_1.json with a contract ID in it. That’s not a very readable format, but we can pass it back into the next script:
daml script --dar .daml/dist/t-0.0.1.dar \
--ledger-host localhost \
--ledger-port 6865 \
--script-name Main:step2 \
--input-file output_step_1.json
And you should see something like:
[DA.Internal.Prelude:540]: "mark1hello"
An important point compared to your existing code is that there is no link between step1 and step2 in DAML, except for the return/input types matching. In particular, step2 does not call step1.
Hope that helps.
Hi,
On the same grounds, I made
module TestScript where
import Daml.Script
import Iou
import IouTrade()
data Parties = Parties with
alice : Party
bob : Party
usBank : Party
eurBank : Party
trade_create : Parties ->Script (ContractId Iou)
trade_create parties = do
submit parties.eurBank do
createCmd Iou with
issuer = parties.eurBank
owner = parties.eurBank
currency = "EUR"
amount = 100.0
observers = []
trade_exercise : ContractId Iou -> Script ()
trade_exercise cid_iou = do
let Some alice = partyFromText "Alice"
let Some bob = partyFromText "Bob"
let Some usBank = partyFromText "USD_Bank"
let Some eurBank = partyFromText "EUR_Bank"
iouTransferAliceCid <- submit eurBank do
exerciseCmd cid_iou Iou_Transfer with newOwner = alice
pure ()
But on running:
daml script --dar .daml/dist/quickstart-0.0.1.dar --script-name TestScript:trade_create --ledger-host localhost --ledger-port 6865 --input-file parties.json --output-file result.json
It gives:
Error: User abort: Submit failed with code 3: Command interpretation error in LF-DAMLe: Couldn't find package cad2ce82e656bcb662fe8025ac74132f5e731efc850da95691be106f4a4797c0. Details: N/A.
Thar error means that one of the templates you tried to access is not known to the ledger. You have to upload the DAR to the ledger/pass it on startup. Note that any modification of your project will result in a different package id so even if you leave your templates untouched but modify the script, you have to upload the resulting new DAR. To the ledger the templates have still changed.