Quick Start

This guide gets you started on setting up the dev environment for a simple Cardano smart contract, with the on-chain validator written in Plutus Tx. There are several other options for writing on-chain validators, such as Aiken and OpShin, and you can refer to their respective documentation for how to use them.

Writing the On-Chain Validator


GHC and Cabal should be installed in order to follow this guide. Plutus Tx currently supports GHC v9.2.x and v9.6.x. Cabal v3.8+ is recommended.

Additionally, some C libraries will need to be installed. Plutus Tx depends on cardano-base, which in turn depends on a few cryptographic C libraries, including libblst, libsecp256k1, and libsodium. Cabal is not designed to manage C dependencies, so you need to either install them yourself, or manage them using another tool (such as Nix).

If you are not using Nix, follow the instructions in cardano-base or cardano-node to install these libraries (or install them in your own way if you like).

If you are using Nix, you can use plutus repository’s dev shell:

nix develop github:input-output-hk/plutus

The dev shell comes with not only the required C libraries, but also GHC and Cabal.

Create a New Cabal Package

First, make a new directory plutus-quickstart:

mkdir plutus-quickstart && cd $_

Then, create a new Cabal package that builds an executable, using default settings:

cabal init --exe --non-interactive

Declare Plutus Tx Dependencies

Next we have to add some packages as dependencies. The Plutus packages aren’t on Hackage (the default repository Cabal looks for packages from), but on the CHaP repository. Therefore, we need to tell Cabal to look for packages from CHaP (in addition to Hackage). To do so, make a cabal.project file, and follow the instructions in the CHaP repository to make Cabal aware of CHaP. Then, add the following to your cabal.project:


Finally, declare the dependencies in the build-depends field in plutus-quickstart.cabal:

  , base
  , base16-bytestring
  , bytestring
  , plutus-core
  , plutus-ledger-api
  , plutus-tx
  , plutus-tx-plugin

At this point, you should be able to build your project: cabal build plutus-quickstart should succeed.

Write the Validator

Now we are ready to write the on-chain validator using Plutus Tx. We showed and explained an auction validator in Simple example, and we’ll reuse the same validator here. Make a file named AuctionValidator.hs in the app directory, and copy the content over from here. Then, Add the following in plutus-quickstart.cabal:


Note that AuctionValidator.hs imports ScriptContext from PlutusLedgerApi.V2, which means that the script created from it will be a PlutusV2 script. PlutusV2 only supports Plutus Core v1.0.0 (currently the highest and default version is v1.1.0), which is why the target-version=1.0.0 flag is needed.

Next, write the main function in Main.hs, which does two things:

  • Instantiate the validator by providing the AuctionParams of a specific auction

  • Serialise the instantiated validator and write it to a file

Here is what Main.hs may look like:

module Main where

import AuctionValidator
import Data.ByteString qualified as B
import Data.ByteString.Base16 qualified as Base16
import Data.ByteString.Short qualified as B
import PlutusLedgerApi.V2 qualified as V2

main :: IO ()
main = B.writeFile "validator.uplc" . Base16.encode $ B.fromShort serialisedScript
    script = auctionValidatorScript params
    serialisedScript = V2.serialiseCompiledCode script
    params =
        { apSeller = error "Replace with seller's wallet address"
        , -- The asset to be auctioned is 10000 lovelaces
          apAsset = V2.singleton V2.adaSymbol V2.adaToken 10000
        , -- The minimum bid is 100 lovelaces
          apMinBid = 100
        , apEndTime = error "Replace with your desired end time"

Replace apSeller with the seller’s PubKeyHash, which can be generated using Cardano CLI, Cardano API or an off-chain library for Cardano. Replace apEndTime with your desired POSIXTime.

Now, build it:

cabal build plutus-quickstart

This should succeed and will write the serialised validator to validator.uplc. Congratulations - you’ve successfully created a Plutus validator script.

Creating and Submitting Transactions using an Off-Chain Framework

Once you have the validator, you can proceed with deploying and interacting with a smart contract that uses this validator. To do so, you’ll need the ability to perform operations like the following:

  • Generating key pairs

  • Querying available UTXOs that satisfy certain criteria and can be used as the input of a transaction

  • Building transactions and calculating transaction fees

  • Signing and submitting transactions

These can be done using low-level Cardano CLI commands or the Cardano API library functions. A better way is to use high-level off-chain libraries and frameworks, such as:

  • Lucid, a JavaScript off-chain library for Cardano

  • Kuber, which provides a Haskell library and a JSON API for working with Cardano transactions

  • cardano-transaction-lib, a PureScript library for building Cardano transactions

These frameworks either consume compiled validators in serialised form (such as the one you just made), or depend on the Plutus Tx library and compile the on-chain code from source. Refer to their respective documentation for more details about how to use them.

A good way to quickly deploy and test a smart contract is to do it on a public testnet, such as Preview. Generate a key pair, go to the faucet for the testnet you are using to request some funds, submit a transaction to lock the funds in your smart contract validator script, and off you go to have all the fun with it. Read Simple example, section Life cycle of the auction smart contract if you need to understand how one can submit transactions to interact with the auction smart contract.

Interfacing between Plutus Tx and Off-Chain Frameworks

At this time, interfacing between Plutus Tx and most off-chain frameworks (especially non-Haskell ones) isn’t very well supported. What this means is that you may run into inconveniences like these:

  • An off-chain framework may expect serialised validators to be in a format different than that produced by Plutus Tx.

  • The redeemer type is defined in Haskell (e.g., AuctionRedeemer in AuctionValidator.hs), but needs to be redefined in another language when using a non-Haksell off-chain framework. For instance, when using Lucid, you’ll need to define an object in JavaScript corresponding to AuctionRedeemer in order to construct your redeemer.

These inconveniences will be addressed once Plutus contract blueprints, as outlined in CIP-0057, are adopted and implemented by us as well as the off-chain frameworks.