This tutorial introduces a simple financial contract in pseudocode, before explaining how it is modified to work in Marlowe, giving the first example of a Marlowe contract.
Suppose that alice
wants to buy a cat from bob
, but neither of
them trusts the other. Fortunately, they have a mutual friend carol
whom they both trust to be neutral (but not enough to give her the money
and act as an intermediary). They therefore agree on the following
contract, written using simple functional pseudocode. This kind of
contract is a simple example of escrow.
When aliceChoice
(When bobChoice
(If (aliceChosen `ValueEQ` bobChosen)
agreement
arbitrate))
The contract is described using the constructors of a Haskell data
type. The outermost constructor When
has two arguments: the first is
an observation and the second is another contract. The intended
meaning of this is that when the observation becomes true, the second
contract is activated.
The second contract is itself another When
– asking for a decision from bob
– but inside that, there is a choice: If
alice
and bob
agree on what to do, it is done; if not, carol
is asked to arbitrate and make a decision.
In fact, we can allow for the option of bob
making the first choice, rather than alice
, and so in general When
offers= a list of cases,[1] each with an action and a corresponding contract that is triggered when that action happens:
When [ Case aliceChoice
(When [ Case bobChoice
(If (aliceChosen `ValueEQ` bobChosen)
agreement
arbitrate) ],
Case bobChoice
(When [ Case aliceChoice
(If (aliceChosen `ValueEQ` bobChosen)
agreement
arbitrate) ]
]
In this contract, either Alice or Bob can make the first choice; the other then makes a choice. If they agree, then that is done; if not, Carol arbitrates.
Exercise
Think about executing this contract in practice. Suppose that Alice has already committed some money to the contract. What will happen if Bob chooses not to participate any further?
We have assumed that Alice has already committed her payment, but suppose that we want to design a contract to ensure that: what would we need to do to?
Marlowe contracts incorporate extra constructs to ensure that they progress properly. Each time we see a When
, we need to provide two additional things:
-
a timeout after which the contract will progress, and
-
the continuation contract to which it progresses.
First, let us examine how to modify what we have written to take care of
the case that the condition of the When
never becomes true. So, we add timeout and continuation values to each When
occurring in the contract.
When [ Case aliceChoice
(When [ Case bobChoice
(If (aliceChosen `ValueEQ` bobChosen)
agreement
arbitrate) ]
60 -- ADDED
arbitrate), -- ADDED
Case bobChoice
(When [ Case aliceChoice
(If (aliceChosen `ValueEQ` bobChosen)
agreement
arbitrate) ]
60 -- ADDED
arbitrate) -- ADDED
]
40 -- ADDED
Refund -- ADDED
The outermost When
calls for the first choice to be made by Alice or Bob: if neither of them has made a choice by time 40
, all the funds in the contract are refunded.
Refund
is typically the last step in every “path” through a Marlowe contract, and its effect is to refund the money in the contract to the participants; we will describe this in more detail in a later tutorial. In this particular case, refund will happen after 40
slots.
Looking at the inner constructs, if a choice has been made, then we wait for a second one. If that is not forthcoming by slot 60
, then Carol is called upon to arbitrate.[2]
Next, we should look at how cash is committed as the first step of the contract.
When [Case (Deposit "alice" "alice" price) -- ADDED
(When [ Case aliceChoice
(When [ Case bobChoice
(If (aliceChosen `ValueEQ` bobChosen)
agreement
arbitrate) ]
60
arbitrate),
Case bobChoice
(When [ Case aliceChoice
(If (aliceChosen `ValueEQ` bobChosen)
agreement
arbitrate) ]
60
arbitrate)
]
40
Refund)
]
10 -- ADDED
Refund -- ADDED
A deposit of price
is requested from "alice"
: if it is given, then it is held in an account, also called "alice"
. Accounts like this exist for the life of the contract only; each account belongs to a single contract.
There is a timeout of 10
slots on making the deposit; if that is reached without a deposit being made, then all the money already in the contract is refunded. In this case, that is simply the end of the contract.
We will see later that parts of this
contract description, such as arbitrate
, agreement
, and price
, use the Haskell
embedding of Marlowe DSL to give some shorthand definitions. We also use overloaded strings to make some descriptions – e.g. of accounts – more concise.
These are discussed in more detail when we look at embedded Marlowe.
Exercise
Comment on the choice of timeout values, and look at alternatives.
For example, what would happen if the timeout of
40
on theWhen
were to be replaced by60
, and vice versa? Would it be sensible to have the same timeout, of100
say, on eachWhen
? If not, why not?
This example has shown many of the ingredients of the Marlowe contract language; in the next tutorial we will present the complete language.
-
While accounts names need to be provided manually in the example here, these could be generated by users’ wallets in a version of Marlowe deployed on a blockchain.