Plutus HTLC: Atomic Swap Smart Contract
📌 Table of Contents
🔍 Overview
⚙️ Contract Structure
🧩 HTLCParams – Contract Parameters
🎯 HTLCRedeemer – User Actions
🧪 mkValidator – Core Logic
📜 Typed Validator & Wrapping
📫 Script Address
📘 Glossary of Terms
1️⃣ 🔍 Overview
This contract implements a Hashed Timelock Contract (HTLC), which enables secure, trustless, and time-limited swaps of assets between two parties. The contract has two unlock conditions:
✅ Hashlock: Receiver must reveal the preimage of a given hash before the deadline.
⏳ Timelock: If deadline passes, the sender can reclaim the funds.
2️⃣ ⚙️ Contract Structure
The contract consists of:
📦
HTLCParams
: The configuration (sender, receiver, hash, and deadline).🎯
HTLCRedeemer
: The action chosen (Redeem with a secret, or Refund).🧪
mkValidator
: The validator logic.🧵 Typed validator & script address.
3️⃣ 🧩 HTLCParams
— Contract Parameters
HTLCParams
— Contract Parametersdata HTLCParams = HTLCParams
{ sender :: PubKeyHash
, receiver :: PubKeyHash
, hashlock :: BuiltinByteString -- sha2_256 of secret
, deadline :: POSIXTime
}
🔍 Explanation:
sender
: Person initiating the swap (can reclaim funds after deadline).receiver
: Person claiming funds with the secret.hashlock
: A SHA-256 hash of the secret.deadline
: Time after which refund is allowed.
🚀 Make this value once off-chain, and lift it using Template Haskell:
PlutusTx.makeLift ''HTLCParams
4️⃣ 🎯 HTLCRedeemer
— User Actions
HTLCRedeemer
— User Actionsdata HTLCRedeemer = Redeem BuiltinByteString | Refund
✨ Purpose:
Redeem secret
— Receiver provides the secret before the deadline.Refund
— Sender reclaims after the deadline.
Must be serializable:
PlutusTx.unstableMakeIsData ''HTLCRedeemer
5️⃣ 🧪 mkValidator
— Core Validation Logic
mkValidator
— Core Validation LogicmkValidator :: HTLCParams -> () -> HTLCRedeemer -> ScriptContext -> Bool
mkValidator htlc _ redeemer ctx =
case redeemer of
Redeem secret ->
traceIfFalse "Hash mismatch" correctHash &&
traceIfFalse "Not signed by receiver" signedByReceiver &&
traceIfFalse "Too late" withinDeadline
where
correctHash = sha2_256 secret == hashlock htlc
signedByReceiver = txSignedBy info (receiver htlc)
withinDeadline = to (deadline htlc) `contains` txInfoValidRange info
Refund ->
traceIfFalse "Too early" pastDeadline &&
traceIfFalse "Not signed by sender" signedBySender
where
pastDeadline = from (deadline htlc) `contains` txInfoValidRange info
signedBySender = txSignedBy info (sender htlc)
where
info = scriptContextTxInfo ctx
🔍 What this does:
🔑 If Redeem secret
:
Checks
sha2_256(secret) == hashlock
Ensures the receiver signed the tx
Ensures the current time is before the deadline
💸 If Refund
:
Checks time is after the deadline
Ensures the sender signed it
All conditions use traceIfFalse
for on-chain debugging.
6️⃣ 📜 Typed Validator & Wrapping
✂️ Define the validator types:
data HTLC
instance Scripts.ValidatorTypes HTLC where
type instance DatumType HTLC = ()
type instance RedeemerType HTLC = HTLCRedeemer
🧵 Compile the validator:
typedValidator :: HTLCParams -> Scripts.TypedValidator HTLC
typedValidator htlc = Scripts.mkTypedValidator @HTLC
($$(PlutusTx.compile [|| mkValidator ||]) `PlutusTx.applyCode` PlutusTx.liftCode htlc)
$$(PlutusTx.compile [|| wrap ||])
where
wrap = Scripts.wrapValidator @() @HTLCRedeemer
🔁 This connects the raw mkValidator
to Plutus's typed validation system.
7️⃣ 📫 Script Address
This lets you lock funds at the HTLC address on-chain:
validator :: HTLCParams -> Validator
validator = Scripts.validatorScript . typedValidator
scrAddress :: HTLCParams -> Address
scrAddress = scriptAddress . validator
8️⃣ 📘 Glossary of Terms
sha2_256
Hash function used for locking the secret
PubKeyHash
Public key identifier
txSignedBy
Verifies transaction is signed by a given party
scriptContextTxInfo
Retrieves transaction info for validation
contains
Checks if a time range contains a point
traceIfFalse
On-chain debugging that fails with a message
POSIXTime
UTC timestamp used in Plutus timelocks
Validator
A Plutus smart contract function
BuiltinByteString
On-chain byte string
Redeemer
Input that unlocks a validator
TypedValidator
Type-safe validator using Plutus type system
🧪 Sample Off-Chain Parameter Setup
exampleParams = HTLCParams
{ sender = <senderPubKeyHash>
, receiver = <receiverPubKeyHash>
, hashlock = sha2_256 "superSecret123"
, deadline = <somePOSIXTime>
}
You'd pass this into typedValidator
and derive the address to fund it.
✅ Summary
This HTLC Plutus contract allows:
Atomic swaps between chains or users
Time-bound claim and refund
Secure hash-based locking
It's ideal for use cases like:
💱 Cross-chain asset exchanges
🧾 Conditional token access
🔐 Trustless payments with refund guarantees
Last updated