Spending UTxO can be conditional with script-defined logic. In such a case, the spender of UTxO must provide the script with such data that will enable the unlocking of UTxO. In this article, we will look at how to lock UTxO and how to unlock it by executing a validator script.
UTxOs are spent through Transactions
To understand the article, it is necessary to understand the UTxO accounting model, the structure of UTxO, Shelley addresses, and the common way of spending UTxO through key credentials. We described this in the previous article.
In the image below you can see the Shelley address containing the Payment Credentials including two options to spend the associated UTxO. One address can be associated with multiple UTxOs.
A payment credential is part of a Shelley address that identifies who owns the funds in the address and thus who is able to spend UTxOs. A payment credential can be either a key credential (based on a public/verification key) or a script credential (a hash of the script).
The trigger for spending UTxO is a transaction. Among other parameters, the transaction must contain a witness. A witness is a piece of data that proves that a transaction is authorized by the owner of the funds. If the witness is valid, the transaction can consume the input UTxOs. A witness can be either a signature (for key credentials) or a script execution (for script credentials).
In the case of key credentials, the witness is the signature made by the owner's private key. The signature must correspond to the key credentials (public key) that belong to the address holding the UTxOs.
When users want to send funds to someone else, the wallet constructs a regular transaction for them. The wallet selects the appropriate UTxOs and asks the sender to sign the transaction. In the background, a witness is inserted into the transaction for each UTxO to be spent.
If the transaction is valid and will be finalized by the Cardano network (permanently stored in the blockchain), the transaction will consume the input UTxOs from which new UTxOs will be created (exactly as defined by the sender). Spending can be simply imagined as moving UTxOs (funds) from one address to another. Funds may be redistributed during the move, but the total value must be maintained (or less).
At the end of the blockchain, new blocks containing transactions are appended at regular intervals. The Cardano blockchain can be seen as accounting records of UTxO transfers.
We described it in detail in the previous article. In this article, we focus on the case where a witness is a script execution.
Spending UTxO via a script (validator script) is a two-phase process that includes two transactions. The first transaction locks funds and the second transaction unlocks funds by executing the script. We call the first transaction a locking transaction and the second a spending transaction.
The first transaction is the one that creates the UTxO that gets locked on a script address. No script is executed when UTxOs are locked at the script address. Only the destination address (defined in the output UTxO of the locking transaction) is important, which is the script address.
The second transaction tries to spend the UTxOs held by the script address. This triggers the execution of the validator script. The return value of the script (the result of the script execution) will decide whether to unlock the UTxOs.
In the image below you can see the process of locking and unlocking UTxO through a script. The locking transaction has one output UTxO whose destination address shows the script address. This locks UTxO to the script address. The spending transaction consumes UTxO from the script address, which triggers the execution of the script on the Cardano node (red arrow). The picture describes the basic concept. We will explain this in more detail below.
Let's explain the term script address. A Shelley address is a generic term for any address that is supported by the Shelley era of Cardano. A Shelley address can have different types and formats, depending on the credential and network tag. A Shelley address can have a script hash as payment credentials, and this makes it a script address. If you hear the term script addresses, you know that the spending of UTxOs is only possible through a script.
The script address is derived from the hash of the validator script. Developers create a script and make a hash of it. The hash is used as the payment credentials of the Shelley address. When a UTxO gets associated with the script address, it can be unlocked only by the script. The script will be passed several inputs (more on that later) and if its execution ends with a return value of True, the funds can be spent.
Note one important thing. A script address was created, but the script (script content) is not stored in the blockchain. Only the hash of the script was used to create the script address (and that is not enough for execution). Below you will learn how the content of the script is delivered to the Cardano node for validating the spending transaction.
In the image below you can see the address that has a script hash set in the payment credentials. When the script's source code was finished (and tested), the developer used it as the basis for creating a script hash. The script hash is a unique identifier that is derived from the content of the script. A script hash is a form of a short digital fingerprint that ensures that the exact script (the content of the script) that was used to create the hash will be used during validation (i.e. script execution). This ensures that the conditions for spending UTxO cannot be changed by using another script.
The script hash is validated by the Cardano node when it is verified whether the nodes validate the correct script. If you changed a single character in the original script, its hash would be completely different. If the script is delivered through a transaction, the content of the script must produce the same hash that is identical to the hash stored in the payment credentials.
There must always be a match between the hash stored in the payment credentials and the hash of the source code of the script that is to be executed to validate a transaction. If the hashes are different, the validation will fail.
A script address must be created before a transaction sends (associates) funds to it. Otherwise, the locking transaction would not know where to send the funds and how to lock them with the script.
Once the script address is created, funds can be sent to it, as you can see in the image below. The transaction has one input UTxO and two outputs. The sender of the transaction is Alice. Alice authorizes the transaction by providing a single witness for the input UTxO. Alice wants to send 1000 ADA to a script address that belongs to DEX. She only has a UTxO with 2500 ADA in her wallet. The first destination address belongs to her and returns 1500 ADA back to her address. The second destination address is a script address that belongs to the DEX.
In the image below you can see the script address after the network has validated (and finalized) the transaction. UTxO is associated with the script address.
A Datum is an optional parameter that can be used to store some information or state that is relevant for spending a UTxO. A Datum is stored alongside the UTxO at the script address and can be accessed by the validator script during the execution.
A Datum is inserted into the transaction that locks UTxO at the script address, and therefore also inserted into the transaction that unlocks UTxO from the script address.
In the image below you can see the transaction that locks the UTxO on the script address together with the Datum. This locking transaction differs from the previous locking transaction only in that it contains Datum which will be associated with UTxO.
In the image below you can see the script address after the network has validated (and finalized) the transaction. UTxO is associated with the script address and it contains Datum.
The funds on the script address are controlled by the script itself, which defines the conditions for spending the UTxO. The script takes some inputs, such as a witness, a Datum, a Redeemer, and a script context, and returns either True or False, indicating whether the spending is valid or not. We will talk about script inputs in detail later.
We now have UTxO locked on the script address.
Before we start explaining how UTxOs are unlocked, we need to look at how nodes find scripts that are used to validate transactions that try to spend UTxOs from script addresses. The content of the script must be available to the Cardano node so that it can execute it, i.e. validate the spending transaction.
The script content can be inserted into the spending transaction data as a script witness. This means that the transaction that tries to spend the UTxO must include the content of the script as part of the transaction body. This increases the size of the transaction and thus also the fees. In general, it limits the complexity of scripts because it forces developers to make scripts as small as possible (block size is limited and users do not want to pay high fees).
If users were to send multiple such transactions, the content of the same script would be stored in the blockchain multiple times, which is an unnecessary waste of space.
In the image below, you can see a spending transaction that contains the content of the script in the Witness section (in red). Notice how the input UTxO defines through the script hash which script must be used for validation. The Cardano node takes the script content from the transaction body and creates a hash. The hash must match the script hash contained in the payment credentials (red arrows).
Another possibility is that the script can be associated with a UTxO as a reference script. This means that the locking transaction that creates the UTxO must include the script as part of the transaction output. The content of the script is then stored on the blockchain as a reference script and can be accessed by its hash.
Thus, scripts can be stored on the blockchain only once, and then reused by subsequent transactions without having to include the contents of scripts. Instead, subsequent transactions can refer to scripts by their hashes, which is also how they are identified within script addresses. This reduces the size and fees of the transactions and allows for more flexibility and scalability of scripts.
In the image below, you can see a locking transaction that stores the contents of the script to the blockchain at the script address.
First, a script address is created as described above. After creating the script address, it does not contain the contents of the script. Once a script address exists, the contents of the script can be stored on it via a transaction.
In the image below you can see the script address after the network has validated (and finalized) the transaction. The content of the script is stored in the blockchain and can be referenced by other transactions.
There is always only one script address that is associated with the script. Users can then send (and thus lock) other UTxOs to the same script address via locking transactions and try to spend them from the address via spending transactions (unlocking always requires the execution of the script).
The UTxO with the script is always on the script address unless it is spent by satisfying the script.
So, the transaction that spends the UTxO can reference the script by its hash, without having to include it in the transaction data.
In the picture below, you can see a script address with a UTxO that stores the content of the script (red) and also 3 UTxOs (blue) that were sent to the address by other users and that will be spent from the address through the execution of the script. The address will be reused as long as the script is used.
Now you know how to lock UTxOs at the script address and how the Cardano node gets the contents of the scripts. Let's take a closer look at unlocking UTxOs.
UTxO that is locked at the script address can be spent through a spending transaction that triggers the execution of the script. If UTxO is associated with a script address, the script must always be executed and there is no way around it.
It does not matter what destination addresses are defined in the output UTxOs of the spending transaction and how funds are to be distributed via new UTxOs.
Let's see how a Cardano node validates a spending transaction that requires the execution of a script.
The node checks if the spending transaction is phase-1 valid, meaning that it is constructed correctly and can pay its processing fee.
The transaction must pay enough fees to cover the execution cost of the validator script, which depends on the size and complexity of the script. The fees are calculated using the coinsPerUTxOWord protocol parameter, which determines how much ADA is required per byte of script code.
If the transaction passes phase-1 validation, the script can be executed, otherwise, the transaction is rejected.
During phase-2 validation, the script is executed. The transaction will be valid only if it is possible to spend (unlock) each input UTxO. For each input UTxO that is associated with a scrip address, the return value of the script must be True.
The node retrieves the script that is associated with the UTxO that the transaction tries to spend. The script can be either referenced by its hash or inserted into the transaction data. The node also checks if the hash of the retrieved or inserted script matches the hash stored in the payment credentials of the output. If there is no match, the transaction is rejected as invalid.
Node passes the inputs to the script and executes it. The validator script can get the following inputs:
- Datum: This is a piece of data attached to the UTxO that the script is locking. The Datum can be used to store some state or condition that is relevant for spending the UTxO.
- Redeemer: This is a piece of data attached to the spending input. This is typically used to provide input to the script from the spender. It can be used to provide some information or logic that is needed to unlock the funds. A Redeemer is mandatory for any input UTxO that has a script-based address, regardless of the type or content of the script.
- Transaction context: This is a piece of data that represents information about the spending transaction. This is used to make assertions about the way the output is being spent. For example, the logic of the script can require a signature. The signature can be used to prove ownership of some funds or tokens that are required by the script.
The script returns either True or False, indicating whether the spending condition is met or not. If True, then the transaction is accepted and the UTxO is spent. If False, then the transaction is rejected and the UTxO remains unspent.
The picture below shows the unlocking of UTxO through a spending transaction. The input of the transaction is UTxO which is associated with the script address (blue arrow).
The transaction references the script (in this example the script content is not part of the transaction body). The Cardano node gets the script content that is stored in the blockchain (in UTxO) and compares the hash of the script content with the script hash in the payment credentials (red arrows).
Next, it is necessary to obtain inputs for the script and pass them to it (green arrows).
If the Datum is already stored on the blockchain, then it is not necessary to insert it again into the spending transaction. The node can find the Datum value by looking up the Datum hash that is stored in the UTxO. However, if the Datum is not stored on the blockchain, then the spending transaction must provide the Datum value that matches the Datum hash in the UTxO. This is to ensure that the script can access the Datum value when validating the spending transaction.
Furthermore, Redeemer and transaction context are passed as inputs for the script. The transaction context contains all the necessary information about the transaction, so a list of input UTxOs and output UTxOs, the fee, witnesses (signatures), Datums, validation scripts (or references to them), etc.
Inputs are passed to the script and the script logic is executed. The inputs affect the return value. In our example, the return value is True, so UTxO can be spent (black arrows). Funds will be moved to a new destination address.
About the Logic of the DEX Script
In this chapter, we will try to explain some more context regarding smart contracts on Cardano.
In our example, Alice sent 1000 ADA to DEX’s script address, which means that she created a UTxO that is locked by the script address. The script address is derived from the hash of the DEX’s validator script, which contains the logic for exchanging tokens on the DEX platform. The script can also contain logic for canceling the order.
How is it actually possible that Alice does not have to manually create the transaction? She used the exchange's user interface, entered her request (a swap order), and was then prompted to confirm the transaction. DEX (the application) created a transaction for Alice.
In the image below, you can see that in the Cardano ecosystem, a smart contract can be composed of off-chain and on-chain parts of the application. Off-chain logic is responsible for the construction of locking and spending transactions. In the case of DEX, the off-chain logic is processed on a server (in the cloud). In the article, we only dealt with on-chain logic, i.e. script validation, which takes place on the blockchain through the Cardano Virtual Machine.
Alice only interacts with the off-chain part of the DEX that constructs transactions. A locking transaction (the swap order) requires a signature from Alice.
Alice can get her funds back if she decides to cancel her order on DEX. To do this, she must create a transaction that spends the UTxO that is locked by the script address and sends it back to her own address. The cancel (spending) transaction is constructed by the off-chain logic, so Alice just uses DEX’s interface. She must provide a witness that proves her ownership of the UTxO, and a Redeemer that indicates her intention to cancel the order. The validator script will then verify if Alice is indeed the owner of the UTxO.
Validator scripts of various DEXes (or other applications) differ significantly in their functionality and the options they offer users.
To unlock the funds, DEX, Alice, or anyone else who wants to spend the UTxO must provide the correct inputs to the validator script, such as a signature, a Redeemer, or a Datum. The validator script will then check if the inputs satisfy the exchange rules, such as matching the offer price, paying the fees, and respecting the validity interval.
Although basically anyone can spend funds, the DEX is expected to match the orders and perform the swap, i.e. it can best fulfill the conditions of the script. However, one of the conditions of the script can be the signature with the private key of Alice or the DEX, so it is possible to define a group of those who can spend funds.
DEX cannot steal funds on script addresses. DEX can only satisfy the script by providing inputs and thus executing the orders of users. When DEX fulfills an order, the signature from Alice is not required. DEX is able to execute the swap without any further interaction with Alice. In other words, DEX is able to construct a spending transaction that satisfies the conditions of the script. Alice receives tokens at her address as a result of the requested swap of a pair of tokens.
Validator scripts are usually very simple and work with a limited amount of information. They basically just decide whether funds can be spent from the script address. A simple condition may suffice for this. Most of the application logic should be in off-chain parts. The execution of validation scripts consumes the resources of the distributed network, which can be more expensive than using the resources of the server or local computers of users.