aiken_design_patterns/linked_list

Storing lists directly in datums is generally impractical: as the datum grows, the UTxO can become too expensive or even impossible to spend.

A linked list stores an unbounded collection as many UTxOs. Each list element is one UTxO with:

The list NFT policy is the authentication boundary. A payment credential alone never proves that a UTxO is a list element: anyone can create an output at the list payment credential without invoking the spend script or list policy. A UTxO with no asset under the list policy is outside the list state, even at that credential. UTxOs sharing a credential are independent, and a transaction consumes only explicitly selected inputs; an outside party cannot force its UTxO into a list transition. Such UTxOs never need to be discovered, collected, spent, or cleaned up by the list protocol. These helpers do not authenticate or protect their data or value, and contracts must not rely on them to do so.

Usage Guideline

Contracts using this module should keep every authenticated list element controlled by one spend script/payment credential and one list NFT minting policy:

  1. Define the spend script datum as an applied alias of Element<a, b>:
pub type Datum = linked_list.Element<RootType, NodeType>
  1. Ensure the UTxO produced by init goes to that spend script credential.
  2. Implement list spend scripts so structural spends only use spend_for_adding_or_removing_an_element, and non-structural updates use spend_for_updating_elements_data.
  3. Implement the list minting policy so structural mint/burn branches only succeed through this module’s matching mint helpers for the exact init/insert/remove/fold/deinit shape. This spend/mint split is a deliberate budget compromise: the spend-side gate is not a standalone structural proof.
  4. Pass the complete, unmodified ScriptContext.transaction.inputs list in ledger order to every helper inputs argument. A filtered, reordered, reconstructed, or redeemer-provided list invalidates exact-input claims.
  5. Reserve <list_nft_policy_id, root_key> across every policy branch. The one-time init is the only mint of this asset and creates exactly one at the list payment credential. Continuations preserve it there, deinit is its only burn, and every application-specific same-policy branch must reject it. A correctly wired policy therefore cannot have a root_key token at an external payment credential.
  6. Choose root and node NFT names so their namespaces are disjoint: use a non-empty node_key_prefix, non-empty node keys, and a root_key that does not begin with the node prefix.

These rules preserve the invariant that list NFTs cannot leave the list spend script/payment credential.

The guideline above implies a few important design constraints:

  1. Element UTxOs must not carry reference scripts. This default API rejects them when authenticating outputs. Use linked_list/advanced when reference scripts must be supported.
  2. Continued anchors are checked by full address equality. Newly minted nodes only need to share the anchor payment credential, which allows callers to choose staking parts for new nodes. If a callback does not receive a produced element address directly, the corresponding Output is an argument the caller supplied to the helper and can be captured by the callback.
  3. Every Output passed to a minting helper must be selected from the script context transaction outputs. The helpers authenticate those outputs as linked-list UTxOs, but they intentionally do not prove how the caller selected them. Callers may use redeemer indexes, filtering, list.find, or any other method, as long as the final Output value comes from ScriptContext.transaction.outputs and is not redeemer data or a locally constructed output.
  4. The default mint helpers do not allow extra mint/burn changes under the list NFT policy. Use linked_list/advanced when structural operations also need unrelated same-policy assets outside the reserved root key and node-key namespace.
  5. Inputs without assets under the list policy are not list elements and have no linked-list validity requirements, regardless of their payment credential. They cannot participate in or block a list transition unless the transaction builder explicitly selects them, and the list protocol never needs to collect or clean them up. Off-chain discovery must authenticate the structural list token and canonical element shape rather than treating every UTxO at the payment credential as list state. Namespace-aware advanced and nested scanners explicitly ignore inputs with no asset under the list policy.
  6. Root and node asset-name namespaces must be fixed and disjoint for the deployed list. This is a soft precondition for budget reasons, not an on-chain proof repeated by every branch. Agents wiring a contract must choose a non-empty node prefix, non-empty node keys, and a root key that cannot equal node_key_prefix ++ node_key.

Types

Callback for appending an unordered node.

The anchor and the new node have already been checked to be terminal, and the continued anchor has already been checked to link to the new node. Since this is unordered, the library does not prove anything about the new key beyond its node namespace membership; enforce application-level uniqueness or indexing here if needed. The produced node address is available from the new_node_output supplied to the helper.

Args: anchor input, anchor Lovelace change, anchor key (None for root), anchor data, new node Lovelace, new node key, new node data.

Alias

AppendValidation = fn(
    Input,
    LovelaceChange,
    Option<NodeKey>,
    Data,
    Lovelace,
    NodeKey,
    NodeData,
  ) ->
    Bool

Datum stored at each list UTxO: either the root or a node, plus its successor link.

Terminology:

  • Root is the first element of the list.
  • Node is any non-root list element.
  • Element is the umbrella term for either a root or a node.

Contracts using this library must define their datums as an applied datum alias:

pub type Datum = linked_list.Element<RootType, NodeType>

Where RootType and NodeType are application-specific custom types.

Constructors

  • Element { data: ElementData<root_data, node_data>, link: Link }

Root or node payload carried by an Element.

Operation callbacks usually provide the element key alongside the raw payload data. None for the key means the data came from a Root; Some(key) means it came from a Node.

Constructors

  • Root { data: root_data }
  • Node { data: node_data }

Reader alias for helpers that can validate either root elements or node elements. These helpers need the list policy, the root asset name, and the node asset-name namespace.

To reduce execution budget, readers receive node_key_prefix_length explicitly next to node_key_prefix; otherwise each operation would need to compute the length on-chain.

The length of RootKey can be at most 32 bytes. The length of any NodeKey can be at most (32 - NodeKeyPrefixLength). “Length” here means bytes after decoding from the chosen encoding, not the number of visible characters.

Alias

ElementEval<result> = fn(PolicyId, RootKey, NodeKeyPrefix, NodeKeyPrefixLength) ->
    result

get_element_info continuation.

Args: Address, Lovelace, element key (None for root, Some(key) for node), data, link.

Alias

ElementInfo<a> = fn(Address, Lovelace, Option<NodeKey>, Data, Link) -> a

Callback for folding the first node into the root.

The library has already authenticated the root, the first node, and the continued root; proved that the root points to the folded node; burned the folded node NFT; and moved the folded node’s old link onto the continued root. The callback decides whether the root data transition and folded node contents are valid for the application.

Args: root input, root Lovelace change, root data, folding node input, folding node Lovelace, folding node key, folding node data, folding node link, continued root data.

Alias

FoldValidation = fn(
    Input,
    LovelaceChange,
    RootData,
    Input,
    Lovelace,
    NodeKey,
    NodeData,
    Link,
    RootData,
  ) ->
    Bool

Raw Data element alias used by low-level authentication/read helpers.

This is not an application datum type. It is the authenticated structural shape that lets the library decode Root versus Node before passing raw payload data to caller callbacks.

Alias

GenericElement = Element<Data, Data>

Raw Data element-data alias used by low-level authentication/read helpers.

Root { data } and Node { data } identify the structural role of an authenticated element, while the inner data remains caller-defined.

Alias

GenericElementData = ElementData<Data, Data>

Successor pointer stored in an element datum.

None means the element is terminal. Some(node_key) stores the successor node key without NodeKeyPrefix; the policy/prefix namespace is supplied by the operation context rather than repeated in each datum link.

Alias

Link = Option<NodeKey>

Lovelace delta for a continued list element.

This is always computed as: continued_output_lovelace - spent_input_lovelace.

A positive value means the operation added ADA to the continued element, and a negative value means it removed ADA from it. The library does not impose an application policy on this delta; operation callbacks receive it so contracts can enforce their own deposit, fee, rent, or conservation rules.

Alias

LovelaceChange = Lovelace

Raw payload stored in a Node datum.

This has the same generic callback contract as RootData: the library authenticates the list element and its structural role, while the caller decides how to decode and validate the application payload.

Alias

NodeData = Data

get_node_element_info continuation.

Args: Address, Lovelace, node key, node data, link.

Alias

NodeElementInfo<a> = fn(Address, Lovelace, NodeKey, NodeData, Link) -> a

Reader alias for helpers that only need the list policy and node namespace.

Use this for node-only operations where the root key is irrelevant. These helpers do not validate the root asset name, so passing only the node prefix and prefix length keeps the caller surface narrower and avoids unused constants in mint/spend branches.

Alias

NodeEval<result> = fn(PolicyId, NodeKeyPrefix, NodeKeyPrefixLength) -> result

Key bytes of a linked-list node, without NodeKeyPrefix.

Node keys should be non-empty and unique under the list policy/prefix. A common approach is to derive the key from an input output reference or some other unique witness. If you allow duplicate keys, list integrity must be preserved by some other invariant.

Ordered operations compare keys as bytearrays. That means integer-like keys should be encoded with a fixed width if numeric ordering matters.

255 < 256, but #"ff" > #"0100" in bytearray comparison. Fixed-width encoding gives the intended order: #"00ff" < #"0100".

Alias

NodeKey = ByteArray

Bytes prefixing every node NFT asset name.

The full node asset name is node_key_prefix ++ node_key, and Cardano asset names can be at most 32 bytes. For example, if the prefix takes 4 bytes, node keys can take at most 28 bytes.

The prefix should be non-empty and must not prefix RootKey; otherwise root/node identity can collide.

Alias

NodeKeyPrefix = ByteArray

Must equal bytearray.length(node_key_prefix).

This is passed separately to avoid recomputing the prefix length on-chain. Define it as a const so the compiler can inline it:

pub const node_key_prefix = "🔗"

pub const node_key_prefix_length =
  bytearray.length(node_key_prefix)

Alias

NodeKeyPrefixLength = Int

Callback for ordered insertion.

The library has already authenticated the anchor input, authenticated the continued anchor output and new node output, checked the anchor continuation, checked the node-key namespace, checked minting, and checked the requested key ordering before this callback runs. The produced node address is not repeated in this callback; callers that need stake-credential or full-address checks can capture the new_node_output they passed to the insertion helper.

Args: anchor input, anchor Lovelace change, anchor key (None for root), anchor data, new node Lovelace, new node key, new node data, new node link. Return True to accept the application-specific part of the insertion; use expect or return False to reject.

Alias

OrderedInsertValidation = fn(
    Input,
    LovelaceChange,
    Option<NodeKey>,
    Data,
    Lovelace,
    NodeKey,
    NodeData,
    Link,
  ) ->
    Bool

Callback for prepending an unordered node.

The anchor has already been authenticated as the root. The continued root links to the new node, and the new node links to the root’s previous first node. The callback is responsible only for application-specific root/new node payload and Lovelace checks. The produced node address is available from the new_node_output supplied to the helper.

Args: root input, root Lovelace change, root data, new node Lovelace, new node key, new node data, new node link.

Alias

PrependValidation = fn(
    Input,
    LovelaceChange,
    RootData,
    Lovelace,
    NodeKey,
    NodeData,
    Link,
  ) ->
    Bool

Callback for removal.

The library has already selected the anchor by anchor_input_outref, authenticated the removed node, proved that the anchor currently points to that node, burned the removed node NFT, and checked that the continued anchor skips over the removed node. The callback validates the application-specific consequences of that removal.

Args: anchor input, anchor Lovelace change, anchor key (None for root), anchor data, removing node input, removing node Lovelace, removing node key, removing node data, removing node link.

Alias

RemoveValidation = fn(
    Input,
    LovelaceChange,
    Option<NodeKey>,
    Data,
    Input,
    Lovelace,
    NodeKey,
    NodeData,
    Link,
  ) ->
    Bool

Raw payload stored in a Root datum.

Operation callbacks receive raw Data rather than an application-specific type so this module can remain generic. Callers should decode with expect inside callbacks and let invalid application data reject the transaction.

Alias

RootData = Data

get_root_element_info continuation.

Args: Address, Lovelace, root data, link.

Alias

RootElementInfo<a> = fn(Address, Lovelace, RootData, Link) -> a

Reader alias for helpers that only need the list policy and root asset name.

Use this for root-only operations such as initialization, deinitialization, or root UTxO reads. These helpers do not validate node keys, so they do not need node namespace constants.

Alias

RootEval<result> = fn(PolicyId, RootKey) -> result

Asset name of the NFT held by the root element. It may be empty and can be no longer than 32 bytes.

Choose this outside the node namespace: it must not begin with NodeKeyPrefix. The usual low-budget setup is an empty root key, a non-empty node prefix, and non-empty node keys.

The asset <list_nft_policy_id, root_key> is a policy-wide singleton. A correctly wired policy permits its one-time mint only through init, keeps it in the canonical root at the list payment credential through every continuation, and permits its burn only through deinit. Every other policy branch must reject this asset name. Consequently, a root_key token at an external payment credential is unreachable valid state; helpers which locate the root by this asset rely on that invariant.

Some visible UTF-8 characters occupy more than one byte. For example, the tree emoji (🌳) takes up 4 bytes, so "🌳" is not a 1-byte asset name.

Alias

RootKey = AssetName

Callback for non-structural element continuations.

The updater preserves address, NFT, constructor, and link; this callback controls the Lovelace change and data transition.

Args: Address, Lovelace change, element key (None for root), old data, new data, preserved link. Return True to accept the application-specific update.

Alias

UpdateValidation = fn(
    Address,
    LovelaceChange,
    Option<NodeKey>,
    Data,
    Data,
    Link,
  ) ->
    Bool

Functions

Finalization Functions

run_element_with(
  reader: ElementEval<a>,
  list_nft_policy_id: PolicyId,
  root_key: RootKey,
  node_key_prefix: NodeKeyPrefix,
  node_key_prefix_length: NodeKeyPrefixLength,
) -> a

Finalize an ElementEval by supplying the list policy, root key, and node namespace constants.

In most validators, this becomes a small helper:

use aiken_design_patterns/linked_list

const root_key = #""

const node_key_prefix = "NODE"

const node_key_prefix_length =
  bytearray.length(node_key_prefix)

pub fn finalize_linked_list(
  eval: linked_list.ElementEval<Bool>,
  list_nft_policy_id: PolicyId,
) -> Bool {
  linked_list.run_element_with(
    eval,
    list_nft_policy_id,
    root_key,
    node_key_prefix,
    node_key_prefix_length,
  )
}

Then your minting policy can validate an operation like this:

let linked_list_eval = {
  let
    anchor_input,
    anchor_lovelace_change,
    // etc.
  <-
    linked_list.insert_ascending(...)

  expect anchor_lovelace_change >= 0

  // other validations
}

expect linked_list_eval |> finalize_linked_list(own_policy_id)

If a redeemer branch can only handle a root or only handle a node, use run_root_with or run_node_with with the narrower set of constants.

run_root_with(
  reader: RootEval<a>,
  list_nft_policy_id: PolicyId,
  root_key: RootKey,
) -> a

Finalize a RootEval by supplying the list policy and root key.

Root-only helpers return this reader shape when they do not need the node namespace. Keeping those constants out of the helper avoids requiring every root branch to know the node prefix.

run_node_with(
  reader: NodeEval<a>,
  list_nft_policy_id: PolicyId,
  node_key_prefix: NodeKeyPrefix,
  node_key_prefix_length: NodeKeyPrefixLength,
) -> a

Finalize a NodeEval by supplying the list policy and node namespace constants.

Node-only helpers return this reader shape when they never inspect or validate the root key. The caller is still responsible for supplying the exact prefix length that corresponds to node_key_prefix.

Initialization and De-initialization

init(
  nonce_validated: Bool,
  produced_element_output: Output,
  tx_mint: Value,
  root_validator: fn(Address, Lovelace, Data) -> Bool,
) -> RootEval<Bool>

Initialize a linked-list root UTxO with the root NFT and Root datum. The output must contain exactly ADA plus that one root NFT.

nonce_validated must prove a unique consumed nonce or equivalent one-time authorization. Passing literal True is only appropriate in tests and fixtures.

This one-time initialization must be the only policy branch allowed to mint <list_nft_policy_id, root_key>. It mints exactly one, and root_validator must pin the produced root to the intended list payment credential. No other policy branch may mint that reserved asset.

root_validator receives the produced address, Lovelace quantity, and raw root data. The address is passed explicitly even though callers could inspect produced_element_output, so callbacks can validate the destination without destructuring the output again.

produced_element_output must be an output selected from ScriptContext.transaction.outputs. This helper authenticates the selected output as a singleton root UTxO, but leaves the selection method to the caller so contracts can use indexes, filtering, or any other shape that fits their redeemer/API.

It is up to callers to ensure this address has a script payment credential controlled by a script that only uses the spend helpers from this library. In other words, the destination script must ensure list NFTs never leave its custody.

deinit(
  inputs: List<Input>,
  tx_mint: Value,
  root_validator: fn(Input, Lovelace, Data) -> Bool,
) -> RootEval<Bool>

Deinitialize an empty list.

root_validator args: spent root input, root Lovelace, raw root data.

inputs must be the complete, unmodified ScriptContext.transaction.inputs list in ledger order. The policy-wide singleton-root invariant guarantees that the root_key asset being burned cannot have originated at another payment credential.

Element Addition and Removal

insert_ascending(
  continued_anchor_element_output: Output,
  new_node_output: Output,
  inputs: List<Input>,
  tx_mint: Value,
  additional_validations: OrderedInsertValidation,
) -> ElementEval<Bool>

Insert a new node into an ascending ordered list.

The anchor element is inferred from inputs by requiring exactly one authentic linked-list element input. Its continued output must preserve the spent anchor address and data, and the new node must use the anchor payment credential.

Ordering rule:

  • root anchor: new < old_first
  • node anchor: anchor < new < old_next

additional_validations receives:

  1. anchor input
  2. anchor Lovelace change
  3. anchor key (None for root, Some(key) for node)
  4. anchor data
  5. new node Lovelace
  6. new node key
  7. new node data
  8. new node link

The default module preserves anchor data. Use linked_list/advanced when insertion should allow caller-validated anchor data changes.

continued_anchor_element_output and new_node_output must both be selected from ScriptContext.transaction.outputs. The caller controls how they are selected, for example by redeemer indexes or by filtering outputs.

inputs must be the complete, unmodified ScriptContext.transaction.inputs list in ledger order.

insert_descending(
  continued_anchor_element_output: Output,
  new_node_output: Output,
  inputs: List<Input>,
  tx_mint: Value,
  additional_validations: OrderedInsertValidation,
) -> ElementEval<Bool>

Like insert_ascending, but the ordering is reversed:

  • root anchor: new > old_first
  • node anchor: anchor > new > old_next

continued_anchor_element_output and new_node_output must both be selected from ScriptContext.transaction.outputs; this helper validates the selected outputs, not the selection method.

inputs must be the complete, unmodified ScriptContext.transaction.inputs list in ledger order.

append_unordered(
  continued_anchor_element_output: Output,
  new_node_output: Output,
  inputs: List<Input>,
  tx_mint: Value,
  additional_validations: AppendValidation,
) -> ElementEval<Bool>

Append a new terminal node to an unordered list.

The single authentic input is the current terminal anchor. The continued anchor links to the new node, and the new node must be terminal too.

There is no key-order comparison for unordered appends; callers are still responsible for maintaining any application-specific uniqueness or indexing invariant in additional_validations.

additional_validations receives:

  1. anchor input
  2. anchor Lovelace change
  3. anchor key (None for root, Some(key) for node)
  4. anchor data
  5. new node Lovelace
  6. new node key
  7. new node data

The default module preserves anchor data. Use linked_list/advanced when appending should allow caller-validated anchor data changes.

continued_anchor_element_output and new_node_output must both be selected from ScriptContext.transaction.outputs. The caller may choose any deterministic selection method that suits the surrounding validator.

inputs must be the complete, unmodified ScriptContext.transaction.inputs list in ledger order.

prepend_unordered(
  continued_root_element_output: Output,
  new_node_output: Output,
  inputs: List<Input>,
  tx_mint: Value,
  additional_validations: PrependValidation,
) -> ElementEval<Bool>

Prepend a new first node to an unordered list.

The single authentic input must be the root. The continued root links to the new node, and the new node links to the root’s old first node.

additional_validations receives:

  1. root input
  2. root Lovelace change
  3. root data
  4. new node Lovelace
  5. new node key
  6. new node data
  7. new node link

The default module preserves root data. Use linked_list/advanced when prepending should allow caller-validated root data changes.

continued_root_element_output and new_node_output must both be selected from ScriptContext.transaction.outputs. The helper authenticates the selected outputs and leaves indexing/filtering strategy to the caller.

inputs must be the complete, unmodified ScriptContext.transaction.inputs list in ledger order.

remove(
  anchor_input_outref: OutputReference,
  continued_anchor_element_output: Output,
  inputs: List<Input>,
  tx_mint: Value,
  additional_validations: RemoveValidation,
) -> ElementEval<Bool>

Remove the node linked by the selected anchor.

The anchor is identified by anchor_input_outref; the anchor and removing node are inferred from the transaction inputs. The removed node must be the node named by the selected anchor’s current link, and the continued anchor’s link becomes the removed node’s old link.

Only UTxOs authenticated by assets under the list policy participate in this transition. Other UTxOs at the anchor payment credential are independent and never need to be selected, spent, or cleaned up for removal.

inputs must be the complete, unmodified ScriptContext.transaction.inputs list in ledger order.

additional_validations receives:

  1. anchor input
  2. anchor Lovelace change
  3. anchor key (None for root, Some(key) for node)
  4. anchor data
  5. removing node input
  6. removing node Lovelace
  7. removing node key
  8. removing node data
  9. removing node link

The default module preserves anchor data. Use linked_list/advanced when removal should allow caller-validated anchor data changes.

Fold Functions

fold_from_root(
  anchor_root_input_outref: OutputReference,
  continued_anchor_root_output: Output,
  inputs: List<Input>,
  tx_mint: Value,
  additional_validations: FoldValidation,
) -> ElementEval<Bool>

Fold the root’s first node into the root.

The root is identified by its output reference. The helper authenticates the root, the linked node, and the continued root; preserves the root address/key; burns the folding node NFT; and enforces the root/node namespace checks.

This traverses inputs and expects exactly two authentic list inputs at the list payment credential: the root input and the folding node input. UTxOs without an asset under the list policy are independent of that state and never need to be selected, spent, or cleaned up for the fold.

inputs must be the complete, unmodified ScriptContext.transaction.inputs list in ledger order.

additional_validations receives:

  1. root input
  2. root Lovelace change
  3. root data
  4. folding node input
  5. folding node Lovelace
  6. folding node key
  7. folding node data
  8. folding node link
  9. continued root data

Spending Script Helpers

spend_for_adding_or_removing_an_element(
  list_nft_policy_id: PolicyId,
  tx_mint: Value,
) -> Bool

Spend-side gate for structural operations.

This function is intentionally not a structural validator by itself. It must only be used in a spend script whose paired list minting policy accepts structural mint/burns exclusively through this module’s matching mint helpers (init, insert_*, remove, fold_from_root, deinit, etc.).

The security invariant is split across scripts:

  • the spend script uses this gate to permit structural list spends only when a list-policy mint/burn is present;
  • the minting policy proves the exact linked-list transition with the corresponding mint helper.

Do not use this as standalone authorization for moving a list UTxO.

A UTxO at the same payment credential without an asset under the list policy is not a list UTxO. This gate deliberately provides no list-state guarantees for it. Such a UTxO is independent, cannot be forced into another party’s transaction, and never needs to be consumed or cleaned up by the list.

Use this for spend branches that allow adding/removing list elements. Use spend_for_updating_elements_data for non-structural continuations where the element is reproduced and no list-policy mint/burn occurs.

spend_for_updating_elements_data(
  element_input_index: Int,
  continued_element_output_index: Int,
  element_input_outref: OutputReference,
  inputs: List<Input>,
  outputs: List<Output>,
  tx_mint: Value,
  additional_validations: UpdateValidation,
) -> ElementEval<Bool>

Continue an individual element without changing the linked-list structure.

No list-policy mint/burn is allowed; address, NFT, constructor, and link are preserved before UpdateValidation decides the Lovelace and data change.

additional_validations receives:

  1. preserved element address
  2. Lovelace change
  3. element key (None for root, Some(key) for node)
  4. old data
  5. new data
  6. preserved link

inputs and outputs must be the complete, unmodified ScriptContext.transaction.inputs and ScriptContext.transaction.outputs lists in ledger order. The indexes are interpreted against those lists.

Exposed Helpers

get_element_info(
  element_utxo: Output,
  info_validations: ElementInfo<a>,
) -> ElementEval<a>

Authenticate a root or node UTxO and pass its info to info_validations.

The UTxO must be an inline-datum singleton list element under the supplied policy. The returned key is None for the root and Some(node_key) for a node, where node_key excludes NodeKeyPrefix. Root keys and node-prefix membership are checked before the callback runs.

get_root_element_info(
  element_utxo: Output,
  info_validations: RootElementInfo<a>,
) -> RootEval<a>

Authenticate a root UTxO and pass its info to info_validations.

The UTxO must carry the configured root NFT and a Root datum. The callback receives address, Lovelace, raw root payload data, and the root link. Use this when a script only accepts roots and should reject node UTxOs immediately.

get_node_element_info(
  element_utxo: Output,
  info_validations: NodeElementInfo<a>,
) -> NodeEval<a>

Authenticate a node UTxO and pass its info to info_validations.

The UTxO must carry a list NFT whose asset name starts with node_key_prefix and must contain a Node datum. The callback receives address, the node key with the prefix stripped, raw node payload data, and the node link.

Low-level Helpers

validate_singular_authentic_input(
  inputs: List<Input>,
  nft_policy_id: PolicyId,
  return: fn(Input, Lovelace, AssetName, GenericElementData, Link) -> Bool,
) -> Bool

Validate exactly one authentic list input while scanning all transaction inputs.

Inputs with no asset under nft_policy_id are not list candidates and are assigned no structural meaning, irrespective of payment credential. After one candidate is found, this optimized scanner rejects a second singleton list-shaped input under the same policy; it assigns no meaning to inputs without an asset under that policy.

The narrow candidate check relies on authentic elements containing exactly ADA plus one list NFT and on ledger values listing ADA first. Other value shapes are not canonical base-list elements and are ignored.

inputs must be the complete, unmodified ScriptContext.transaction.inputs list in ledger order. The exact-one claim is only as complete as the supplied list.

Args: callback receives input, Lovelace, asset name, data, link.

Another internal helper, you probably don’t need this.

validate_dual_authentic_inputs(
  anchor_input_outref: OutputReference,
  inputs: List<Input>,
  nft_policy_id: PolicyId,
  with: fn(
    Input,
    Lovelace,
    AssetName,
    GenericElementData,
    Link,
    Input,
    Lovelace,
    AssetName,
    GenericElementData,
    Link,
  ) ->
    Bool,
) -> Bool

Validate exactly two authentic list inputs while scanning all transaction inputs.

This compact fold is specialized for the exact two-list-input shape at one payment credential; it is not a general-purpose transaction-input classifier. This specialization has no bearing on list membership, which is authenticated only by assets under nft_policy_id.

anchor_input_outref selects which found input is passed to the callback as the anchor. Validation fails if neither authentic input has that reference.

inputs must be the complete, unmodified ScriptContext.transaction.inputs list in ledger order. The exact-two claim is only as complete as the supplied list.

Args: callback receives anchor input, anchor Lovelace, anchor asset name, anchor data, anchor link, removing input, removing Lovelace, removing asset name, removing data, removing link.

Another internal helper, you probably don’t need this.

Search Document