You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
description: Deferred redeemer construction for Plutus script index optimization
3
+
description: Deferred redeemer construction for efficient validator input lookup
4
4
---
5
5
6
6
import { Mermaid } from"@/components/mdx/mermaid"
7
7
8
8
## Abstract
9
9
10
-
Plutus validators receive transaction context including sorted input indices. Validators can use these indices for efficient lookups instead of traversing entire input lists. However, input indices depend on the **final sorted order** of all transaction inputs, including those added during coin selection. This creates a circular dependency: redeemer construction needs final indices, but final indices aren't known until after coin selection. The architecture resolves this through **deferred redeemer construction**: store a builder function instead of a concrete redeemer, then invoke it during the build phase once all inputs are known and sorted.
10
+
Plutus validators can receive input indices via redeemers for O(1) array lookup instead of O(n) traversal. On-chain traversal is expensive in execution units, so passing indices allows validators to directly access only the inputs they care about.
11
11
12
-
## Purpose and Scope
12
+
The challenge: Cardano currently enforces canonical sorting of inputs, and indices depend on this final sorted order. When coin selection or other operations add inputs to the transaction, they insert into the sorted order and shift existing indices.
13
13
14
-
**Purpose**: Enable Plutus validators to use input indices efficiently by deferring redeemer construction until final input ordering is determined.
14
+
The solution: **defer redeemer construction** until all inputs are known and sorted.
15
15
16
-
**Scope**:
17
-
- Defines the `RedeemerBuilder` pattern for deferred redeemer construction
18
-
- Supports two variants: `"batch"` (multiple inputs) and `"self"` (single input per UTxO)
19
-
- Provides both index and UTxO data to builder functions
20
-
- Integrates with deferred execution model
21
-
- Resolves indices after coin selection when canonical input order is final
16
+
> **Note**: [CIP-128](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0128) proposes removing the canonical ordering requirement, allowing user-controlled input order. Once implemented, you could place contract inputs at known positions (e.g., indices 0, 1, 2) and append wallet inputs after, making indices predictable without deferred construction.
Cardano transactions sort inputs lexicographically by `(txHash, outputIndex)` before validation. When a Plutus validator executes, it receives inputs in this canonical order. Validators often need to correlate their own input with corresponding outputs. This is the classic "UTxO indexer" pattern for preventing double satisfaction attacks.
20
+
Cardano transactions sort inputs lexicographically by `(txHash, outputIndex)`. A validator receives all transaction inputs in this canonical order. Without indices, finding relevant inputs requires traversing the entire list.
This optimization requires the off-chain code to know the input's index. But the index depends on the final sorted order of **all**inputs, including wallet UTxOs added during coin selection for fees and balancing.
33
+
Consider a transaction with 15 inputs: 5 contract UTxOs and 10 wallet UTxOs for fees. A naive validator would check all 15 inputs to find the 5 it cares about. With indices in the redeemer, it directly accesses `inputs[3]`, `inputs[7]`, `inputs[12]`, etc.
49
34
50
-
### The Circular Dependency
35
+
## The Stake Validator Pattern
36
+
37
+
A common pattern uses a stake validator as the main coordinator. Spend validators are "dumb" and only verify that the stake validator ran. The stake validator receives all relevant input indices and performs the actual business logic.
1. Receives indices of contract inputs in its redeemer
63
+
2. Directly accesses those inputs via index
64
+
3. Validates business logic for all contract inputs in one execution
65
+
66
+
The spend validators:
67
+
1. Simply verify the stake validator ran (withdrawal exists in transaction)
68
+
2. Delegate all logic to the stake validator
69
+
70
+
This pattern reduces total execution cost since the stake validator runs once instead of N spend validators running independently.
71
+
72
+
## The Circular Dependency
73
+
74
+
The problem: input indices depend on the final sorted order of **all** inputs. But coin selection adds wallet UTxOs after the user specifies script inputs. The indices change.
The architecture resolves this by **deferring** redeemer construction: instead of building the redeemer immediately, store a **builder function** that will be invoked after coin selection completes.
85
+
## Deferred Construction
67
86
68
-
### Deferred Redeemer Construction
87
+
Instead of building the redeemer immediately, store a **builder function** that will be invoked after coin selection:
69
88
70
89
<Mermaidchart={`
71
90
sequenceDiagram
@@ -74,233 +93,73 @@ sequenceDiagram
74
93
participant CoinSelection
75
94
participant Resolver
76
95
77
-
User->>Builder: Add script inputs with RedeemerBuilder
78
-
Note over Builder: Store builder function<br/>(don't execute yet)
79
-
96
+
User->>Builder: Specify inputs with redeemer function
Resolver->>Resolver: Invoke redeemer function with indices
88
107
Resolver-->>Builder: Concrete redeemer returned
89
-
108
+
90
109
Builder-->>User: Transaction ready
91
110
`} />
92
111
93
-
When a user provides a `RedeemerBuilder` instead of a concrete redeemer:
94
-
95
-
1. The builder stores a **deferred operation** (the intent without the final redeemer)
96
-
2. During `build()`, coin selection runs and all inputs are collected
97
-
3. Inputs are sorted canonically and indices are computed
98
-
4. The builder function is called with both **index and UTxO data**
99
-
5. The returned redeemer completes the operation
100
-
101
-
## Index-Only vs Full-Context Callbacks
102
-
103
-
Some transaction building approaches only provide indices to the redeemer builder callback. This becomes problematic when validators need UTxO data to construct the redeemer. For example, unique token minting where the asset name derives from the output reference being spent.
112
+
## Three Modes
104
113
105
-
**The Problem**: If the callback only receives an index, the UTxO data must be captured externally and correlated manually. This is error-prone and awkward.
114
+
### Batch Mode
106
115
107
-
**The Solution**: Provide both index **and** UTxO to the callback.
116
+
For validators that need indices of multiple inputs. The function receives all indexed inputs and returns a single redeemer.
B1[Specify inputs to track] --> B2[Function called once after sorting] --> B3["Receives all { index, utxo } pairs"] --> B4[Returns redeemer with indices list]
129
121
`} />
130
122
131
-
The `IndexedInput` structure provides both:
132
-
-**index**: Position in canonical input order (for the validator's O(1) lookup)
133
-
-**utxo**: Full UTxO data (for constructing redeemers that depend on output references)
123
+
Primary use case: stake validator coordinator receiving list of contract input indices.
134
124
135
-
##RedeemerBuilder Variants
125
+
### Self Mode
136
126
137
-
### Batch Variant
138
-
139
-
For operations requiring indices of **multiple specified inputs**. The callback receives an array of `{ index, utxo }` pairs, maintaining the same order as the originally specified inputs.
127
+
For spend validators that need their own index. The function is called once per script UTxO.
SE1[Each script UTxO] --> SE2[Function called per UTxO] --> SE3["Receives own { index, utxo }"] --> SE4[Returns redeemer with own index]
162
132
`} />
163
133
164
-
**Use Cases**:
165
-
- Batch spending: redeemer contains list of `(inputIndex, outputIndex)` pairs
166
-
- Multi-validator coordination: indices reference inputs across multiple scripts
167
-
- Mint policies checking specific spend indices
134
+
Use case: spend validator that looks up its own input by index for validation.
168
135
169
-
### Self Variant
136
+
### Static Mode
170
137
171
-
For operations where **each input needs its own redeemer with its own index**. The callback is invoked once per UTxO, receiving a single `{ index, utxo }` each time.
138
+
For redeemers that don't need indices. The data is used directly.
1.**Collect**: Gather all inputs from transaction state (explicit inputs + coin selection additions)
285
-
2.**Sort**: Apply canonical ordering, lexicographic by `(txHash, outputIndex)`
286
-
3.**Index Map**: Create lookup table mapping each input's identifier to its position
287
-
4.**Resolve Batch**: For `"batch"` variant, lookup all specified input indices, call builder once with array
288
-
5.**Resolve Self**: For `"self"` variant, iterate inputs, call builder per UTxO with its index
147
+
Both Batch and Self modes receive indexed inputs containing:
289
148
290
-
## Error Handling
149
+
-**Index**: Final 0-based position in canonically sorted transaction inputs
150
+
-**UTxO**: The original UTxO data
291
151
292
-
Resolution can fail if specified inputs are missing from the final transaction:
152
+
The UTxO is included because redeemers may need output reference data, not just the index.
293
153
294
-
**Missing Input Error**: A `RedeemerBuilder` references a UTxO that wasn't included in the final input set. This occurs if:
295
-
- The UTxO was never added via `collectFrom`
296
-
- The UTxO was filtered out during processing
297
-
- The UTxO reference is malformed
154
+
## When to Use Each Mode
298
155
299
-
**Resolution After Selection Error**: If coin selection adds inputs that change the transaction balance, requiring another selection round, previously-resolved redeemers may have stale indices. The system detects this and fails with guidance to set a minimum fee.
156
+
| Mode | Use Case |
157
+
|------|----------|
158
+
|**Batch**| Stake validator coordinator with list of contract input indices |
159
+
|**Self**| Spend validator needing its own index |
160
+
|**Static**| Redeemer doesn't depend on indices |
300
161
301
162
## Related Topics
302
163
303
164
-[Deferred Execution](/docs/architecture/deferred-execution) - Program storage and fresh state model
304
165
-[Transaction Flow](/docs/architecture/transaction-flow) - Build phases and state machine
305
-
-[Coin Selection](/docs/architecture/coin-selection) - How wallet UTxOs are added
306
-
-[Script Evaluation](/docs/architecture/script-evaluation) - ExUnits calculation after redeemer resolution
0 commit comments