Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Parameters

Problem. Understand when a value belongs to a contract template's fields versus an action's params, and how a value gets filled in without prompting.

The Pay action from recipe 1 takes one value from you each time it runs, and bakes another into the covenant address forever. This recipe pins down the difference.

Two kinds of parameters

This trips up everyone at first, so it's worth being precise:

template fieldsaction params
When fixedAt deploy time, once.Per transaction, every time you run the action.
Baked into the script?Yes — they change the covenant's address.No — they only affect this transaction.
Stored inthe instancenowhere; supplied at run time
Referenced asinstance.NAMEparams.NAME
ExamplePUBKEY, LOAN_EXPIRATION_TIMEamount_sat, CURRENT_BLOCK_HEIGHT

A useful test: "if I changed this value, would the on-chain address change?" If yes, it belongs in a template's fields. If it only affects which inputs and outputs this particular transaction picks, it's an action param.

A single-type contract that never needs an instance can skip contract_templates altogether and declare everything as action params — which is what the early recipes in this book do.

Params that fill themselves in: compute

A param that declares a compute block is never prompted for. The simplest form is a bare formula string; the structured forms cover what an expression cannot say:

"params": {
  "BORROWER_PUB_KEY": {
    "type": "pubkey",
    "description": "Borrower's signing key.",
    "compute": { "type": "wallet", "wallet": "key" }
  },
  "PRINCIPAL_INTEREST_AMOUNT": {
    "type": "u64",
    "compute": "instance.PRINCIPAL_AMOUNT * instance.INTEREST_RATE / 10000"
  }
}
compute.typeProduces
(bare string) or exprThe value of a formula
walletA value from the executing wallet — see below
script_hashsha256(scriptPubKey) of an address
tapleafA covenant script hash, by compiling a .simf
simf_fnThe return value of a named function in a .simf
hookNothing here — a hook sets this param later in the run

The wallet variant takes a second key naming which wallet-derived value it wants:

compute.walletResolves to
"key"The wallet's 32-byte x-only BIP340 pubkey
"script_hash"sha256(scriptPubKey) of the wallet's index-0 explicit output
"address"The explicit address matching "script_hash" — the two are a pair

Declaring { "type": "hook" } looks pointless but is not: it is how a param that a hook will set gets an identifier. Without the declaration, a hook writing to params.SOMETHING would be inventing a name nothing checks.

If a param has no compute, the tool prompts you for it interactively (or you supply it via --params, below). In recipe 1, PUBKEY has no compute, so Pay prompts you for it.

Where a manifest's guarantees come from

There is no block for business rules — no place to write "amount must be greater than zero" and have the wallet check it. That is deliberate: a rule a wallet enforces is a rule a different wallet can skip. Guarantees come from three places instead, in descending order of strength:

  • The covenant. Anything that actually protects value belongs in the .simf, where the chain enforces it and no wallet can skip it. If a value matters, make the program check it.
  • allow_change. An action defaults to "none", so a surplus that no declared output absorbs is an error rather than a silent change output moving value somewhere you never wrote down.
  • Parsing. Unknown and misspelled fields are hard errors, and manifest_version is checked before anything else runs, so a file written for a different format revision fails immediately rather than half-working.

Run it

Supplying params non-interactively

Instead of typing params at the prompt, pass a flat JSON file of string→string values and reference it with --params:

{ "amount_sat": "50000", "pubkey": "<64-hex-char x-only pubkey>" }
txw run examples/p2pk/txmanifest.json Pay \
  --network testnet --wallet wallet.json --params pay-params.json

Keys must match the action's param names exactly — pubkey, not PUBKEY.

The CLI also auto-discovers a per-network param file sitting next to the manifest, named <manifest-stem>.<network>.json — so txmanifest.testnet.json beside txmanifest.json is picked up whenever you pass --network testnet. An explicit --params file is loaded after it and wins on any key they share.

Try next

That covers the values going into an action. Next we get precise about what comes out: the destinations a value can go to, and how confidentiality is decided: Outputs & destinations.