- ⚙️ Introduction
- 🧩 Project Structure Overview
- 🧱 Step 1 — Understanding the Cabal Configuration
- 💻 Step 2 — Opening the Correct Cabal REPL
- 📘 Step 3 — Loading and Testing a Module
- 🧪 Step 4 — Working with
ParameterizedVesting.hs - 🔍 Step 5 — Fixing Common Build Errors
- 🧠 Step 6 — Validating with QuickCheck or Hspec
- 🧰 Step 7 — Reloading, Debugging, and Exiting
- 📖 Glossary of Terms
Welcome to the professional guide for interactively testing Plutus V2 smart contract modules using cabal repl.
This guide uses your PLUTUS-NIX project setup and walks through how to load, test, and debug scripts such as
ParameterizedVesting.hs, Mint.hs, and your test specs.
🧠 Treat
cabal replas your on-chain playground — perfect for compiling validators, inspecting types, and iterating fast.
Here’s your exact folder structure (from your screenshot):
PLUTUS-NIX/
├── code/
│ ├── Utilities/
│ │ ├── src/
│ │ │ ├── Utilities/
│ │ │ │ ├── Conversions.hs
│ │ │ │ ├── PlutusTx.hs
│ │ │ │ ├── Serialise.hs
│ │ │ │ └── Utilities.hs
│ │ └── Utilities.cabal
│ │
│ └── wspace/
│ ├── assets/
│ ├── lecture/
│ │ ├── CGPlutusUtilsv1.hs
│ │ ├── CGTime.hs
│ │ ├── Demo.hs
│ │ ├── Mint.hs
│ │ ├── ParameterizedVesting.hs
│ │ └── Vesting.hs
│ │
│ ├── tests/
│ │ ├── CGPlutusUtilsSpec.hs
│ │ ├── CGTimeSpec.hs
│ │ ├── DemoSpec.hs
│ │ ├── Main.hs
│ │ ├── MintSpec.hs
│ │ ├── ParameterizedVestingSpec.hs
│ │ ├── Spec.hs
│ │ └── VestingSpec.hs
│ │
│ ├── REPLTutorial.md
│ ├── Tutorials.md
│ ├── Tutorial-*.md (various guides)
│ ├── wspace.cabal
│ ├── cabal.project
│ └── cabal.project.local
│
├── flake.nix
├── default.nix
├── LICENSE
└── README.md
🧩 The Utilities package is a shared dependency library. The wspace package contains your Plutus V2 contracts, specs, and markdown tutorials.
library utilities
hs-source-dirs: src/Utilities
exposed-modules:
Utilities.Conversions
Utilities.PlutusTx
Utilities.Serialise
Utilities.Utilities
build-depends:
, base >=4.14 && <5
, aeson
, plutus-tx
, plutus-ledger-api
, plutus-corelibrary scripts
hs-source-dirs: lecture
exposed-modules:
CGPlutusUtilsv1
CGTime
Demo
Mint
ParameterizedVesting
Vesting
build-depends:
, base
, utilities
, plutus-core ^>=1.54.0.0
, plutus-ledger-api ^>=1.54.0.0
, plutus-tx ^>=1.54.0.0
, plutus-tx-plugin ^>=1.54.0.0✅ Your REPL target for Plutus code is wspace:lib:scripts
✅ Your REPL target for shared utilities is Utilities:lib:utilities
cd ~/PLUTUS-NIX/code/wspace
cabal repl wspace:lib:scriptsExpected output:
Ok, modules loaded: ParameterizedVesting, Vesting, Mint, Demo, CGTime, CGPlutusUtilsv1.
*ParameterizedVesting>
cd ~/PLUTUS-NIX/code/Utilities
cabal repl Utilities:lib:utilitiesExpected output:
Ok, modules loaded: Utilities.Conversions, Utilities.PlutusTx, Utilities.Serialise, Utilities.Utilities.
*Utilities.PlutusTx>
💡 You can switch between these REPL targets without closing GHCi — just quit (
:q) and relaunch the other.
Inside REPL:
:r -- Reload all
:l lecture/ParameterizedVesting.hs
import ParameterizedVesting
:t mkValidatorExample:
mkValidator :: Datum -> Redeemer -> ScriptContext -> BoolExample structure:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
module ParameterizedVesting where
import GHC.Generics (Generic)
import PlutusTx (unstableMakeIsData, makeLift)
import PlutusTx.Prelude
import PlutusLedgerApi.V2
import Data.Aeson (ToJSON, FromJSON)
import Prelude (Show)
data VestingDatum = VestingDatum
{ beneficiary :: PubKeyHash
, releaseTime :: POSIXTime
, amount :: Integer
}
deriving (Show, Generic, ToJSON, FromJSON)
PlutusTx.unstableMakeIsData ''VestingDatum
PlutusTx.makeLift ''VestingDatumTest in REPL:
> :t VestingDatum
VestingDatum :: PubKeyHash -> POSIXTime -> Integer -> VestingDatum✅ Compiles perfectly under Plutus V2 (plutus-ledger-api-1.54.0.0).
| Error Message | Cause | Fix |
|---|---|---|
Cannot open repl for the package |
Wrong directory | Run REPL inside ~/PLUTUS-NIX/code/wspace |
Unknown module: Ledger |
Legacy import | Replace with PlutusLedgerApi.V2.* |
makeIsData not in scope |
Outdated API | Use unstableMakeIsData |
No instance for FromJSON PubKeyHash |
Missing JSON derivations | Add deriving anyclass |
GHC plugin: PlutusTx Plugin failed |
Missing plugin | Add plutus-tx-plugin to dependencies |
Each .Spec.hs file under tests/ can be run via REPL.
Example:
cabal repl wspace:test:vesting-tests
:l tests/ParameterizedVestingSpec.hs
mainimport Test.Hspec
import ParameterizedVesting
main :: IO ()
main = hspec $ describe "ParameterizedVesting" $
it "validates datum creation" $
amount (VestingDatum "pkh" 1234 100) `shouldBe` 100| Command | Description |
|---|---|
:r |
Reload all files |
:l <path> |
Load file manually |
:t <symbol> |
Show type |
:i <symbol> |
Show info |
:browse <Module> |
List exports |
:set -v |
Verbose compilation |
:q |
Quit GHCi |
| Term | Definition |
|---|---|
| Cabal | Build and dependency manager for Haskell |
| REPL | Read–Eval–Print Loop |
| PlutusTx | Compiler for Haskell → Plutus Core |
| Datum/Redeemer | On-chain data inputs |
| unstableMakeIsData | Derives serialization for on-chain types |
| QuickCheck/Hspec | Haskell property testing frameworks |
| POSIXTime | Smart contract timestamp |
| PubKeyHash | Hash identifying wallet public key |
Your PLUTUS-NIX project now has a two-layer REPL workflow:
| Package | REPL Target | Purpose |
|---|---|---|
| Utilities | Utilities:lib:utilities |
Common helpers & encoding utilities |
| wspace | wspace:lib:scripts |
Smart contracts, validators, and specs |
You can now:
✅ Load Plutus and utility modules directly ✅ Debug validators with GHCi ✅ Run all test specs ✅ Iterate on scripts before serialization