The lending protocol
π Draft. This chapter has not been reviewed yet β content may be incomplete or change.
Problem. Two strangers want to transact a collateralised loan with no escrow agent and no trusted backend. A borrower locks collateral and advertises terms; a lender supplies the principal; the loan later settles by repayment or β if the borrower defaults β by liquidation after a deadline. Every rule is enforced on-chain by Simplicity covenants.
This is the capstone of the cookbook. Everything the recipes introduced one at a time β covenant UTXO types, multiple spending paths, asset issuance & NFTs, formulas & derived params, hooks & tapleaf compute, and the template / instance model β shows up here at once, wired into a single working protocol.
The full example lives in the repository at
examples/lending/:
one txmanifest.json
plus five .simf covenant programs. This chapter walks through it in the order
you'd actually run it.
The deal, in one paragraph
Alice has collateral (say, L-BTC) and wants to borrow L-USDT against it without selling. She locks her collateral into a covenant and publishes her terms β amount, interest, expiry β as on-chain NFTs anyone can read. Bob sees the offer, likes the terms, and accepts by sending Alice the principal; in the same transaction her collateral moves into a second covenant that holds it for the life of the loan. To get her collateral back, Alice repays principal plus interest before the deadline. If she doesn't, Bob can seize the collateral once the deadline passes. At no point does either party have to trust the other or any third party β the covenants only permit the honest transitions.
The cast
Two roles. The protocol has a borrower and a lender. They are
different people with different wallets, so when you run it you'll keep two wallet
files β borrower.json and lender.json β and run each action as the
appropriate party.
Five covenant programs. Each .simf file is a small Simplicity program that
gates one kind of UTXO. They are deliberately tiny and composable:
| Program | Role |
|---|---|
pre_lock.simf | Holds the collateral while the offer is open. PATH::LEFT lets a lender activate the loan; PATH::RIGHT lets the borrower cancel (with a signature). |
lending.simf | Holds the collateral during the active loan. PATH::LEFT is repayment; PATH::RIGHT is liquidation after expiry. |
script_auth.simf | Wraps each NFT so it can only be spent co-spent with the right collateral covenant. The glue that binds the NFTs to the deal. |
asset_auth.simf | Guards the lender's principal vault: release requires burning the Lender NFT. |
p2pk.simf | The borrower's plain Schnorr payout address β the Hello World program, reused for where the principal lands. |
Four NFTs. The protocol mints four single-unit Liquid assets at construction. Two are bearer auth tokens (whoever holds it can act); two encode the loan terms in their amount field so the offer is self-describing on-chain:
| NFT | Carries | Used for |
|---|---|---|
| Borrower NFT | nothing (amount = 1) | proves a transaction is the borrower's; co-spent in setup and repayment |
| Lender NFT | nothing (amount = 1) | the lender's bearer token; needed to liquidate and to drain the vault |
| First Parameters NFT | interest rate, expiry, decimals β bit-packed into its amount | publishes the loan terms; checked by every covenant |
| Second Parameters NFT | collateral & principal base amounts β bit-packed into its amount | publishes the amounts; checked by every covenant |
The lifecycle
The whole protocol is one lending_contract contract template, and its
actions walk an instance through a sequence of states:
| State | Reached by | Meaning |
|---|---|---|
nfts_issued | IssueUtilityNFTs | The four NFTs exist; terms are encoded. |
offer_open | LockCollateral | Collateral and NFTs sit behind pre_lock. |
cancelled | CancelOffer | The borrower withdrew before a lender took it. |
loan_active | SetupLending | A lender funded the offer. |
repaid | RepayLoan | The borrower paid principal plus interest. |
liquidated | LiquidateAfterExpiry | The deadline passed; the lender took the collateral. |
settled | ClaimPrincipalWithInterest | The lender drew the repayment from the vault. |
These state names are ours, not the format's. A manifest has nowhere to declare a state machine β no
stateslist, nofrom/toon an action. The table above is prose, written to make this chapter readable.What actually constrains the order is the chain. Each action's inputs name UTXO types that only the previous action creates, so
SetupLendingcannot run beforeLockCollateralhas produced apre_lockoutput to spend. Read the sequence off the covenants, which enforce it, rather than off a table, which cannot.
Rendered, those transitions are the protocol's flow:
IssueUtilityNFTs
β (borrower mints 4 NFTs + computes covenant hashes)
βΌ
βββββββββββββ
β nfts_issuedβ
βββββββββββββ
β LockCollateral (borrower)
βΌ
βββββββββββββ CancelOffer (borrower, unilateral)
β offer_open β ββββββββββββββββββββββββββββββΊ cancelled
βββββββββββββ
β SetupLending (lender accepts)
βΌ
βββββββββββββ
βloan_active β
βββββββββββββ
β β
RepayLoan β β LiquidateAfterExpiry
(borrower, β β (lender, unilateral,
cooperative) βΌ βΌ after LOAN_EXPIRATION_TIME)
ββββββββββ βββββββββββββ
β repaid β β liquidatedβ
ββββββββββ βββββββββββββ
β
ClaimPrincipalWithInterest (lender drains the vault)
βΌ
settled
Two of those arrows are unilateral escape hatches β CancelOffer and
LiquidateAfterExpiry β that one party can take without the other's cooperation.
That's the whole point of a trustless protocol: the exits don't depend on the
counterparty playing along. RepayLoan is the cooperative happy path both
sides want. Nothing in the manifest labels them as such; what makes an exit
unilateral is that its covenant path needs only one party's key or NFT, which you
can read off the action's witnesses.
How this chapter is organised
The walkthrough follows those states across four pages, each building one phase and pulling in the recipes that introduced its pieces:
- Issuing the NFTs & encoding the terms β the
IssueUtilityNFTsconstructor: minting four NFTs from issuance inputs, bit-packing the loan terms into Parameter NFT amounts, and computing the web of interdependent covenant hashes that every later step relies on. - Opening the offer β
LockCollateralputs the collateral and NFTs on-chain behind thepre_lockcovenant, with anop_returndiscovery beacon. - Accepting or cancelling the offer β the two spending
paths of
pre_lock: the lender'sSetupLending(with therequired_indexdiscipline a covenant demands) versus the borrower'sCancelOffer. - Settling: repay, liquidate, withdraw β the
borrower's
RepayLoanversus the lender'sLiquidateAfterExpiryon thelendingcovenant, then draining the principal vault withClaimPrincipalWithInterest.
Before you run it
The CLI is tx-manifest-wallet, aliased throughout the book to txw. Do the
one-time setup and made a wallet first. Because this protocol has two
roles, create two wallets and fund both from the testnet faucet:
txw create-wallet --out borrower.json
txw create-wallet --out lender.json
# fund each from https://liquidtestnet.com/faucet, then:
txw sync --wallet borrower.json
txw sync --wallet lender.json
Where's the funding address? Run
infoon each wallet and copy the receive address it prints, then paste that into the faucet:txw info --wallet borrower.json # copy the receive address, fund it, repeat for lender.jsonSee Fund and sync for the full walk through.
Get oriented with describe and validate before building anything β describe
prints the contract templates, their fields and actions; validate checks the
manifest is internally consistent:
txw describe examples/lending/txmanifest.json
txw validate examples/lending/txmanifest.json
Then start with Issuing the NFTs.
This is the most involved example in the book. If you haven't worked through Hello World and the Last Will covenant yet, do those first β they introduce the single-key and multi-path patterns this protocol composes at scale.