Overview
A transaction runs under a resource budget. Two of the four layers that make up that budget are written in MUFL: a source clause on the transaction, and a packet governance update. This page covers the syntax of both.
The limits(...) clause
A transaction may carry a limits(...) clause that tightens its own budget. The clause is part of
the transaction source, so it is included in the signed and hashed body of the transaction — a
changed clause changes the transaction hash.
A clause is a list of name: count entries, one per dimension you want to tighten:
trn my_transaction limits( fuel_ops: 40000, call_depth: 128 ) { ... }
Only the nine dimension names are accepted. The clause is tighten-only: a value in the clause can lower the effective cap for a dimension but can never raise it above what the outer layers already fixed. A malformed clause — an unknown dimension name, or a count that does not resolve — is rejected when the transaction is compiled.
Selecting a tier
Instead of listing dimensions, a clause may name one of the three vetted tiers:
trn my_transaction limits( $limits::standard ) { ... }
An explicit dimension written after a tier overrides that tier’s entry for the named dimension:
trn my_transaction limits( $limits::standard, call_depth: 512 ) { ... }
Packet governance (_set_limits)
A packet may set its own default limits — the packet-governance layer. Unlike the transaction clause,
governance may raise a dimension above the global default as well as tighten it (each value is
still clamped to the dimension’s hard bound, and a value outside
[1, ceiling] is rejected).
Governance defaults are updated with _set_limits, which takes a dictionary of name -> count:
_set_limits( ( "fuel_ops" -> 200000, "call_depth" -> 512 ) ).
_set_limits is a mechanism, not an authorization policy. The engine validates the update — the
argument must be a dictionary, every key must be one of the nine canonical dimension names, and
every value must be in range — but it does not itself require a signature or check who is calling.
Deciding who may change a packet’s limits is the packet’s own responsibility, expressed in the
packet’s code around the call.
The update has these properties:
- Validated shape. A non-dictionary argument leaves the packet’s limits unchanged. An unknown dimension name or an out-of-range value is rejected with an error.
- Atomic. If any entry in the dictionary is invalid, the whole update is rejected and no dimension is changed — a partially-applied update never occurs.
- Read-modify-write. Setting some dimensions leaves the others at their existing values; an update names only the dimensions it intends to change.
How the layers combine
The transaction clause and packet governance are two of the four composition layers described in Resource Limits. Governance sets the packet’s default (up or down, within the hard bound); the transaction clause and the signed envelope then tighten from there. The effective cap for each dimension is the tightest value across all layers that apply.