Opening the offer
📝 Draft. This chapter has not been reviewed yet — content may be incomplete or change.
Phase 2 of the lending walkthrough. The borrower publishes the offer on-chain:
LockCollateralmoves the collateral and all four NFTs into covenant UTXOs, advancing the contract fromnfts_issuedtooffer_open.
After issuing the NFTs the borrower holds four tokens and
an instance file, but the collateral is still loose in their wallet. This phase
commits it — the protocol's first move of value into covenant addresses, and the
first use of the op_return destination.
LockCollateral — publishing the offer
LockCollateral takes the collateral and all four NFTs from the borrower's wallet
and moves them into covenant addresses. The collateral goes into the pre_lock
UTXO; each NFT goes into a prelock_script_auth UTXO (the script_auth.simf
wrapper compiled to PRE_LOCK_COV_HASH):
"LockCollateral": {
"inputs": [
{ "id": "collateral_in", "utxo_source": "wallet", "asset": "instance.COLLATERAL_ASSET_ID",
"amount_sat": { "min_amount": "instance.COLLATERAL_AMOUNT" } },
{ "id": "borrower_nft_in", "utxo_source": "wallet", "asset": "instance.BORROWER_NFT_ASSET_ID", "amount_sat": 1 },
{ "id": "lender_nft_in", "utxo_source": "wallet", "asset": "instance.LENDER_NFT_ASSET_ID", "amount_sat": 1 },
{ "id": "first_params_in", "utxo_source": "wallet", "asset": "instance.FIRST_PARAMETERS_NFT_ASSET_ID", "amount_sat": "instance.FIRST_PARAMETERS_ENCODED" },
{ "id": "second_params_in", "utxo_source": "wallet", "asset": "instance.SECOND_PARAMETERS_NFT_ASSET_ID", "amount_sat": "instance.SECOND_PARAMETERS_ENCODED" },
{ "id": "fee_input", "utxo_source": "wallet", "asset": "lbtc" }
],
"outputs": [
{ "id": "pre_lock_out", "destination": { "utxo_type": "pre_lock" }, "asset": "instance.COLLATERAL_ASSET_ID", "amount_sat": "instance.COLLATERAL_AMOUNT" },
{ "id": "borrower_nft_locked","destination": { "utxo_type": "prelock_script_auth" }, "asset": "instance.BORROWER_NFT_ASSET_ID", "amount_sat": 1 },
{ "id": "lender_nft_locked", "destination": { "utxo_type": "prelock_script_auth" }, "asset": "instance.LENDER_NFT_ASSET_ID", "amount_sat": 1 },
{ "id": "first_params_locked", "destination": { "utxo_type": "prelock_script_auth" }, "asset": "instance.FIRST_PARAMETERS_NFT_ASSET_ID", "amount_sat": "instance.FIRST_PARAMETERS_ENCODED" },
{ "id": "second_params_locked", "destination": { "utxo_type": "prelock_script_auth" }, "asset": "instance.SECOND_PARAMETERS_NFT_ASSET_ID", "amount_sat": "instance.SECOND_PARAMETERS_ENCODED" },
{ "id": "indexer_op_return", "destination": { "type": "op_return" },
"data": "concat(instance.BORROWER_PUB_KEY, instance.PRINCIPAL_ASSET_ID)" },
{ "id": "collateral_change", "destination": "change", "asset": "instance.COLLATERAL_ASSET_ID", "optional": true },
{ "id": "fee_change", "destination": "change", "asset": "lbtc", "optional": true }
]
}
Two things worth pausing on:
- The
OP_RETURNadvertisement.indexer_op_returnwritesconcat(BORROWER_PUB_KEY, PRINCIPAL_ASSET_ID)into an unspendable output. It carries no value — it's a beacon so an indexer (or a prospective lender's wallet) can discover the open offer and the asset it wants by scanning for these markers. Outputs & destinations introduced theop_returndestination; here it's used for discovery rather than burning. - The collateral amount is checked on-chain, and only on-chain. The
pre_lockcovenant re-derivesCOLLATERAL_AMOUNTand rejects a spend that does not match. Nothing checks it before the build, so a mis-sized input costs you a failed dry-run rather than a friendly prompt — the covenant is the law, and it is also the only check.
After this broadcasts, the contract is in offer_open: collateral and NFTs sit in
covenant UTXOs that only the two pre_lock paths can move.
Run it
LockCollateral is run by the borrower, using the instance file written at
construction:
# --- Borrower puts the offer on-chain ---
txw run examples/lending/txmanifest.json LockCollateral \
--instance examples/lending/lending.instance.json --wallet borrower.json
txw sync --wallet borrower.json
With the collateral and NFTs locked behind pre_lock, the offer is live. Next, a
lender accepts it — or the borrower withdraws it:
Accepting or cancelling the offer.