Marlowe step by step¶
Marlowe has six ways of building contracts. Five of these – Pay
,
Let
, If
, When
and Assert
– build a complex contract from
simpler contracts, and the sixth, Close
, is a simple contract. At
each step of execution, as well as returning a new state and
continuation contract, it is possible that effects – payments – and
warnings can be generated too.
In explaining these contracts we will also explain Marlowe values, observations and actions, which are used to supply external information and inputs to a running contract to control how it will evolve.
Pay¶
A payment contract Pay a p t v cont
will make a payment of value
v
of token t
from the account a
to a payee p
, which will
be one of the contract participants or another account in the contract.
Warnings will be generated if the value v
is negative, or if there
is not enough in the account to make the payment in full (even if there
are positive balances of other tokens in the account). In the latter
case, a partial payment (of all the money available) is made. The
continuation contract is the one given in the contract: cont
.
Close¶
A contract Close
provides for the contract to be closed (or
terminated). The only action that it performs is to provide refunds to
the owners of accounts that contain a positive balance. This is
performed one account per step, but all accounts will be refunded in a
single transaction.
Before discussing other forms of contracts, we need to describe values, observations and actions.
Values, observations and actions¶
Values include some quantities that change with time, including “the current slot number”, 1 “the current balance of some token in an account”, and any choices that have already been made; we call these volatile values. Values can also be combined using addition, subtraction, negation, multiplication, and division, and can be conditional on an observation. Even though they are supported by Marlowe, the use of multiplication and division may render the process of static analysis intractable.
Observations are Boolean values derived by comparing values, and can be combined using the standard Boolean operators. It is also possible to observe whether any choice has been made (for a particular identified choice).
Observations will have a value at every step of execution. On the other hand, actions happen at particular points during execution. As noted earlier, actions can be
depositing money,
making a choice between various alternatives, including an oracle value (see next section), or
notifying an external value of some kind.
Oracles¶
Oracles are being developed for the Cardano blockchain in general, and will be available for use within Marlowe on Cardano. In the meantime, we have introduced an oracle prototype, which is implemented in the Marlowe Playground.
We model Oracles as choices that are made by a participant with a specific
Oracle role: "kraken"
.
If a role in a contract is "kraken"
, and that role makes a choice
such as "dir-adausd"
then, in the Playground simulation, this choice
will be pre-filled, based on data from Cryptowat.ch, with the current
value of the direct ADA/USD conversion rate. You can find all supported
currency pairs here https://api.cryptowat.ch/markets/kraken
It is also possible to obtain the inverse rates of currency pairs listed
by adding the prefix inv-
instead. For example, "inv-adausd"
would
return the value of the USD/ADA conversion rate.
Note, that we support only whole numbers as choice inputs. How then do
we use current ADA/USD price, which might be $0.098924? We simply multiply the
price by 108, so the price would appear as 9892400. You can use
DivValue
with the resulting value after doing your calculations.
For example, you’d like to buy USDT for 12 ADA, using Oracle price.
Get the price:
When [Choice (ChoiceId "dir-adausdt" (Role "kraken") [Bound 1000000 10000000] ...
Calculate USDT amount
Let (ValueId "amount of USDT in microcents")
(MulValue
(Constant 12)
(ChoiceValue (ChoiceId "dir-adausdt" (Role "kraken"))))
Scale the result down by 106 to get amount in USDT cents.
DivValue (UseValue (ValueId "amount of USDT in microcents")) (Constant 1000000)
If¶
The conditional If obs cont1 cont2
will continue as cont1
or
cont2
, depending on the Boolean value of the observation obs
when this construct is executed.
When¶
This is the most complex constructor for contracts, with the form
When cases timeout cont
. It is a contract that is triggered on
actions, which may or may not happen at any particular slot: what
happens when various actions happen is described by the cases in the
contract.
In the contract When cases timeout cont
, the list cases
contains
a collection of cases. Each case has the form Case ac co
where
ac
is an action and co
a continuation (another contract). When a
particular action, e.g. ac
, happens, the state is updated
accordingly and the contract will continue as the corresponding
continuation co
.
In order to make sure that the contract makes progress eventually, the
contract When cases timeout cont
will continue as cont
once the
timeout
, a slot number, is reached.
Let¶
A let contract Let id val cont
allows a contract to name a value
using an identifier. In this case, the expression val
is evaluated,
and stored with the name id
. The contract then continues as
cont
.
As well as allowing us to use abbreviations, this mechanism also means that we can capture and save volatile values that might be changing with time, e.g. the current price of oil, or the current slot number, at a particular point in the execution of the contract, to be used later on in contract execution.
Assert¶
An assert contract Assert obs cont
does not have any effect on the
state of the contract, it immediately continues as cont
, but it
issues a warning when the observation obs
is false. It can be used
to ensure that a property holds in any given point of the contract,
since static analysis will fail if any execution causes a warning.
- 1
The presentation here is a simplification of the concrete implementation, in which transactions are associated with a slot interval during which it is valid to add them to the blockchain. The reason for this is that in general it is difficult to predict the precise slot in which a transaction will be accepted for inclusion on the blockchain; it is therefore more robust to specify an interval in which the transaction should be accepted. The view presented here is a simplification in that effectively we consider only intervals of length one. So, a Marlowe contract is able to access the upper and lower bounds on the current slot interval, rather than the specific current slot value. Executing a contract can, in some circumstances, lead to an “ambiguous slot interval error”, but we do not cover that any further here.