aiken_design_patterns/linked_list
Storing lists in datums is generally impractical, as its growth can lead to unspendable UTxOs due to limited resources available on-chain.
A linked list is a construct for storing an infinitely large array of elements on-chain, such that each element is represented with a UTxO that points to its immediate successor.
Usage Guideline
Contracts using this module must keep the linked list controlled by one dedicated spend script and one list NFT minting policy:
- Define the spend script datum as an applied alias of
Element<a, b>:
pub type Datum = linked_list.Element<RootType, NodeType>
- Ensure that the UTxO produced by
initgoes to that spend script credential. - Implement list spend scripts so they only succeed through this module’s
two spend helpers:
spend_for_adding_or_removing_an_elementandspend_for_updating_elements_data. - Implement list mint scripts so they only succeed through this module’s provided mint helpers.
- Choose root and node NFT names so their namespaces are disjoint: use a
non-empty
node_key_prefix, non-empty node keys, and aroot_keythat does not begin with the node prefix. This avoids root/node identity collisions without adding repeated runtime checks to every operation.
These rules preserve the invariant that linked list NFTs cannot leave the list spend script/payment credential.
The guideline above implies five important design constraints:
- The elements are required to have no scripts attached to them at mint. The continuations are already provided with too many values. Providing their reference script fields would overwhelm the API even further. So far this doesn’t seem to be a needed feature.
- While continued anchor nodes are validated to go to the same address, for new/removed nodes only the payment parts are validated to match the anchors’. This allows customization of staking parts of new nodes, while preserving the staking parts of existing elements.
- Extra mints under the list NFT policy beyond the NFT needed for the list structure are unsupported (e.g. CIP-68 tokens are not supported). The provided mint helpers enforce exactly the mint/burn operation needed by the list structure.
- List operations assume that the list script/payment credential is dedicated to the linked list. For remove/fold operations, every spent input at that payment credential is expected to be an authentic list element participating in the operation. Non-authentic inputs at the same payment credential are invalid transaction construction; implementations may reject them without trying to ignore or classify them.
- The module assumes root and node asset-name namespaces are disjoint. It validates the root key and node prefix at operation boundaries, but does not add extra comparisons to prove namespace disjointness on-chain.
Types
The terminology used here is as follows:
Rootis the first element of the linked listNodeis any other element in a linked list other than the rootElementis the umbrella term to refer to either the root or nodes
You probably won’t need to interact with this.
Constructors
-
Element { data: ElementData<root_data, node_data>, link: Link }
Similar to Element above, the API is designed so that you
wouldn’t need to work with this datatype directly.
Continuations that provide element data implicitly signal whether it should
be coerced into a root_data or a node_data through the (potential) key.
That is, if no key is provided, it means the element at hand is a root
element.
Constructors
-
Root { data: root_data } -
Node { data: node_data }
Similar to Eval, but with polymorphic return type. This allows
returning custom types with get_element_info.
Alias
ElementEval<a> = fn(PolicyId, RootKey, NodeKeyPrefix, NodeKeyPrefixLength) -> a
Type alias for the continuation function passed to
get_element_info.
Alias
ElementInfo<a> = fn(Lovelace, Option<NodeKey>, Data, Link) -> a
This is the datatype for allowing you to provide environment constants. To
be a bit nerdy, this is essentially a Reader monad.
To prevent excessive overhead, this function requires the length of the key prefix to be provided explicitly, next to the key prefix itself (otherwise the length should have been validated on-chain).
The length of the
RootKeycan be 32 bytes at the maximum. On the other hand, the length of any of theNodeKeys can be at most(32 - NodeKeyPrefixLength). “Length” here refers to the number of bytes after the asset name is decoded from any of the encoding schemes (e.g. UTF-8).
Alias
Eval = fn(PolicyId, RootKey, NodeKeyPrefix, NodeKeyPrefixLength) -> Bool
Alias
Link = Option<NodeKey>
Alias
LovelaceChange = Lovelace
Alias
NodeData = Data
Alias for representing the keys of linked list nodes. This must be at least one byte long, and contracts should implement validations on them depending on their needs. The generic module assumes this invariant rather than re-checking it on every operation.
Requiring keys to be unique is the recommended approach (e.g. making sure it’s hash of an input’s out ref). If you do need to allow duplicate keys, proceed with extreme caution as linked list integrity breaks without some other means of preserving it.
As with the general principle of this API, for ordered operations (e.g.
insert_ascending), proper ordering is already
validated.
Alias
NodeKey = ByteArray
Bytes prefixing all linked list nodes (i.e. all elements except for the
root). It should be non-empty and must not prefix RootKey.
Note that the total number of bytes allowed for NFTs of linked list elements can be 32 bytes at most. Therefore, if for example your prefix occupies 4 bytes, your keys can have 28 bytes at most.
Alias
NodeKeyPrefix = ByteArray
A sort of boilerplate to reduce execution budgets. You can use const to
leverage Aiken compiler’s optimizations:
pub const node_key_prefix = "🔗"
pub const node_key_prefix_length =
bytearray.length(node_key_prefix)
Alias
NodeKeyPrefixLength = Int
Alias
RootData = Data
Similar to Eval, but for (init)[#init] and (deinit)[#deinit],
which don’t validate node key prefixes.
Alias
RootEval = fn(PolicyId, RootKey) -> Bool
An alias for the asset name of the NFT held in the root element. This can be zero bytes or more (32 bytes at most).
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.
Keep in mind that some UTF-8 characters occupy more than 1 byte. For example the tree emoji (🌳) takes up 4 bytes.
Alias
RootKey = AssetName
Functions
Finalization Functions
run_eval_with(
reader: Eval,
list_nft_policy_id: PolicyId,
root_key: RootKey,
node_key_prefix: NodeKeyPrefix,
node_key_prefix_length: NodeKeyPrefixLength,
) -> Bool
This typically ends up as boilerplate by defining a helper like this:
use aiken_design_patterns/linked_list
const node_key_prefix = "NODE"
const node_key_prefix_length =
bytearray.length(node_key_prefix)
pub fn finalize_linked_list(
eval: linked_list.Eval,
list_nft_policy_id: PolicyId,
) -> Bool {
linked_list.run_eval_with(
eval,
list_nft_policy_id,
node_key_prefix,
node_key_prefix_length,
)
}
As an example, with this you’ll be able to turn your Eval results to
Bools as such:
// In your minting contract:
let linked_list_eval = insert_ascending(...)
expect linked_list_eval |> finalize_linked_list(own_policy_id)
Initialization and De-initialization
init(
nonce_validated: Bool,
produced_element_output: Output,
tx_mint: Value,
root_validator: fn(Lovelace, Data) -> Bool,
) -> RootEval
Initialize a linked list, i.e. produce a UTxO which contains some ADA and
the authentication NFT, with a Root element data. The asset name of the
NFT (i.e. RootKey) is something that must be validated by passing the
result of this function to run_root_with.
deinit(
inputs: List<Input>,
tx_mint: Value,
root_validator: fn(Input, Lovelace, Data) -> Bool,
) -> RootEval
Deinitialize an empty list.
Element Addition and Removal
insert_ascending(
continued_anchor_element_output: Output,
new_node_output: Output,
inputs: List<Input>,
tx_mint: Value,
additional_validations: fn(
Input,
LovelaceChange,
Option<NodeKey>,
Data,
Lovelace,
NodeKey,
NodeData,
Link,
) ->
Bool,
) -> Eval
The anchor element is inferred from inputs by requiring exactly one
authentic linked list element input. It is assumed the address from which the
anchor element comes from is the destination address for the new node.
The additional_validations argument provides you with 8 values:
- Input of the anchor element
- Change in Lovelace count from anchor input to continued anchor output
- If the anchor element is a node, its key (i.e. asset name without the
prefix) is provided
- Underlying data of the anchor element – note that this can come from
either a
Root or a Node
- Lovelace count of the new node UTxO
- Key (i.e. asset name without the prefix) of the new node
- Underlying data of the new node – note that this is extracted from a
Node
Link of the new node
insert_descending(
continued_anchor_element_output: Output,
new_node_output: Output,
inputs: List<Input>,
tx_mint: Value,
additional_validations: fn(
Input,
LovelaceChange,
Option<NodeKey>,
Data,
Lovelace,
NodeKey,
NodeData,
Link,
) ->
Bool,
) -> Eval
This is exactly like insert_ascending, with the
exception that the inserted key is expected to be less than the anchor’s,
and greater than anchor’s link (if any).
append_unordered(
continued_anchor_element_output: Output,
new_node_output: Output,
inputs: List<Input>,
tx_mint: Value,
additional_validations: fn(
Input,
LovelaceChange,
Option<NodeKey>,
Data,
Lovelace,
NodeKey,
NodeData,
) ->
Bool,
) -> Eval
Append a new element at the very end of an unordered list. The anchor
element is inferred from inputs by requiring exactly one authentic linked
list element input. There is no comparison validation for the new key with
respect to the anchor element’s, but the anchor must be terminal.
The additional_validations argument provides you with 7 values:
- Input of the anchor element
- Change in Lovelace count from anchor input to continued anchor output
- If the anchor element is a node, its key (i.e. asset name without the
prefix) is provided
- Underlying data of the anchor element – note that this can come from
either a
Root or a Node
- Lovelace count of the new node UTxO
- Key (i.e. asset name without the prefix) of the new node
- Underlying data of the new node – note that this is extracted from a
Node
prepend_unordered(
continued_root_element_output: Output,
new_node_output: Output,
inputs: List<Input>,
tx_mint: Value,
additional_validations: fn(
Input,
LovelaceChange,
RootData,
Lovelace,
NodeKey,
NodeData,
Link,
) ->
Bool,
) -> Eval
Prepend a new element at the very start of an unordered list. The root
element is inferred from inputs by requiring exactly one authentic linked
list element input. There is no validation for the new key to be less than
or greater than the root element’s potential link.
The additional_validations argument provides you with 7 values:
- Input of the root element
- Change in Lovelace count from root input to continued root output
- Underlying data of the root
- Lovelace count of the new node UTxO
- Key (i.e. asset name without the prefix) of the new node
- Underlying data of the new node – note that this is extracted from a
Node
Link of the new node
remove(
anchor_input_outref: OutputReference,
continued_anchor_element_output: Output,
inputs: List<Input>,
tx_mint: Value,
additional_validations: fn(
Input,
LovelaceChange,
Option<NodeKey>,
Data,
Input,
Lovelace,
NodeKey,
NodeData,
Link,
) ->
Bool,
) -> Eval
Remove a node. The anchor element is identified by its output reference, and
the anchor and removing node are inferred from the transaction inputs.
This operation assumes the list script/payment credential is dedicated to
list elements, so the transaction must not include unrelated inputs at the
same payment credential.
The additional_validations argument provides you with 9 values:
- Input of the anchor element
- Change in Lovelace count from anchor input to continued anchor output
- If the anchor element is a node, its key (i.e. asset name without the
prefix) is provided
- Underlying data of the anchor element – note that this can come from
either a
Root or a Node
- Input of the removing node
- Lovelace count of the removing node UTxO
- Key (i.e. asset name without the prefix) of the removing node
- Underlying data of the removing node
Link of the removing node
Fold Functions
fold_from_root(
anchor_root_input_outref: OutputReference,
continued_anchor_root_output: Output,
inputs: List<Input>,
tx_mint: Value,
additional_validations: fn(
Input,
LovelaceChange,
RootData,
Input,
Lovelace,
NodeKey,
NodeData,
Link,
RootData,
) ->
Bool,
) -> Eval
Spend a root element and its link, reproduce the root while expecting the
linked node to be burnt. The root is identified by its output reference.
This traverses the whole inputs list and expects exactly two authentic
list inputs at the list payment credential: the root input and the folding
node input. This relies on the list spending validators preserving the
invariant that list NFTs do not leave their list script/payment credential,
and on the list script/payment credential being dedicated to list elements.
Unrelated inputs at the same payment credential are intentionally rejected.
Additionally, performs an arbitrary validation on keys and underlying data.
This function ensures:
- Authenticity of the two spent list inputs and root’s continued counterpart
- The anchor is root, and that it correctly points to the folding node
- The root is continued at the same address
- The continued anchor data remains a
Root
- The folding node shares the root’s payment credential
- Folding node is burnt
- Root’s key is correct, and node’s key is prefixed as expected
The function you define for additional_validations is provided with 9
values (in order):
- Input of the root element
- Change in Lovelace count from input root to output
- Underlying data of the root
- Input of the folding node
- Lovelace count of the folding node
- Key (i.e. asset name) of the folding node
- Underlying data of the folding node
Link of the folding node
- Underlying data of the reproduced/continued root
Spending Script Helpers
Simply checks if there are any tokens getting minted/burnt in transaction’s
mint field. How you provide list_nft_policy_id is most likely the
“coupling” needed between the spending and minting endpoints.
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: fn(LovelaceChange, Option<NodeKey>, Data, Data, Link) ->
Bool,
) -> Eval
For spending an individual element within a linked list and reproducing it
with updated data, without affecting the structure of the linked list.
The continuation provides you with:
- Change in Lovelace count from input to output (i.e. output minus input).
- A possible node key (i.e. its asset name without the prefix). It’ll be
None if the spent element is root.
- Input underlying data.
- Updated data in the reproduced element.
- Element’s link.
The transaction’s mint is required to ensure no linked list assets are minted
or burnt.
Exposed Helpers
This should generally not be needed, but in cases where another script wants
to validate expenditure from the linked list, this function can help.
After validating the authenticity of the provided element UTxO, it provides
the continuation with:
- Lovelace count of the element
- A possible
NodeKey (i.e. it will be None if the element UTxO is root)
- Underlying data of the element
- Element’s link
Unlike other helpers, this one returns an ElementEval<a>. This is very
similar to Eval. However, the type variable a allows the final
evaluation to result in any type rather than being restricted to Bool.
run_eval_with(
reader: Eval,
list_nft_policy_id: PolicyId,
root_key: RootKey,
node_key_prefix: NodeKeyPrefix,
node_key_prefix_length: NodeKeyPrefixLength,
) -> Bool
This typically ends up as boilerplate by defining a helper like this:
use aiken_design_patterns/linked_list
const node_key_prefix = "NODE"
const node_key_prefix_length =
bytearray.length(node_key_prefix)
pub fn finalize_linked_list(
eval: linked_list.Eval,
list_nft_policy_id: PolicyId,
) -> Bool {
linked_list.run_eval_with(
eval,
list_nft_policy_id,
node_key_prefix,
node_key_prefix_length,
)
}
As an example, with this you’ll be able to turn your Eval results to
Bools as such:
// In your minting contract:
let linked_list_eval = insert_ascending(...)
expect linked_list_eval |> finalize_linked_list(own_policy_id)
init(
nonce_validated: Bool,
produced_element_output: Output,
tx_mint: Value,
root_validator: fn(Lovelace, Data) -> Bool,
) -> RootEval
Initialize a linked list, i.e. produce a UTxO which contains some ADA and
the authentication NFT, with a Root element data. The asset name of the
NFT (i.e. RootKey) is something that must be validated by passing the
result of this function to run_root_with.
deinit(
inputs: List<Input>,
tx_mint: Value,
root_validator: fn(Input, Lovelace, Data) -> Bool,
) -> RootEval
Deinitialize an empty list.
Element Addition and Removal
insert_ascending(
continued_anchor_element_output: Output,
new_node_output: Output,
inputs: List<Input>,
tx_mint: Value,
additional_validations: fn(
Input,
LovelaceChange,
Option<NodeKey>,
Data,
Lovelace,
NodeKey,
NodeData,
Link,
) ->
Bool,
) -> Eval
The anchor element is inferred from inputs by requiring exactly one
authentic linked list element input. It is assumed the address from which the
anchor element comes from is the destination address for the new node.
The additional_validations argument provides you with 8 values:
- Input of the anchor element
- Change in Lovelace count from anchor input to continued anchor output
- If the anchor element is a node, its key (i.e. asset name without the
prefix) is provided
- Underlying data of the anchor element – note that this can come from
either a
Root or a Node
- Lovelace count of the new node UTxO
- Key (i.e. asset name without the prefix) of the new node
- Underlying data of the new node – note that this is extracted from a
Node
Link of the new node
insert_descending(
continued_anchor_element_output: Output,
new_node_output: Output,
inputs: List<Input>,
tx_mint: Value,
additional_validations: fn(
Input,
LovelaceChange,
Option<NodeKey>,
Data,
Lovelace,
NodeKey,
NodeData,
Link,
) ->
Bool,
) -> Eval
This is exactly like insert_ascending, with the
exception that the inserted key is expected to be less than the anchor’s,
and greater than anchor’s link (if any).
append_unordered(
continued_anchor_element_output: Output,
new_node_output: Output,
inputs: List<Input>,
tx_mint: Value,
additional_validations: fn(
Input,
LovelaceChange,
Option<NodeKey>,
Data,
Lovelace,
NodeKey,
NodeData,
) ->
Bool,
) -> Eval
Append a new element at the very end of an unordered list. The anchor
element is inferred from inputs by requiring exactly one authentic linked
list element input. There is no comparison validation for the new key with
respect to the anchor element’s, but the anchor must be terminal.
The additional_validations argument provides you with 7 values:
- Input of the anchor element
- Change in Lovelace count from anchor input to continued anchor output
- If the anchor element is a node, its key (i.e. asset name without the
prefix) is provided
- Underlying data of the anchor element – note that this can come from
either a
Root or a Node
- Lovelace count of the new node UTxO
- Key (i.e. asset name without the prefix) of the new node
- Underlying data of the new node – note that this is extracted from a
Node
prepend_unordered(
continued_root_element_output: Output,
new_node_output: Output,
inputs: List<Input>,
tx_mint: Value,
additional_validations: fn(
Input,
LovelaceChange,
RootData,
Lovelace,
NodeKey,
NodeData,
Link,
) ->
Bool,
) -> Eval
Prepend a new element at the very start of an unordered list. The root
element is inferred from inputs by requiring exactly one authentic linked
list element input. There is no validation for the new key to be less than
or greater than the root element’s potential link.
The additional_validations argument provides you with 7 values:
- Input of the root element
- Change in Lovelace count from root input to continued root output
- Underlying data of the root
- Lovelace count of the new node UTxO
- Key (i.e. asset name without the prefix) of the new node
- Underlying data of the new node – note that this is extracted from a
Node
Link of the new node
remove(
anchor_input_outref: OutputReference,
continued_anchor_element_output: Output,
inputs: List<Input>,
tx_mint: Value,
additional_validations: fn(
Input,
LovelaceChange,
Option<NodeKey>,
Data,
Input,
Lovelace,
NodeKey,
NodeData,
Link,
) ->
Bool,
) -> Eval
Remove a node. The anchor element is identified by its output reference, and
the anchor and removing node are inferred from the transaction inputs.
This operation assumes the list script/payment credential is dedicated to
list elements, so the transaction must not include unrelated inputs at the
same payment credential.
The additional_validations argument provides you with 9 values:
- Input of the anchor element
- Change in Lovelace count from anchor input to continued anchor output
- If the anchor element is a node, its key (i.e. asset name without the
prefix) is provided
- Underlying data of the anchor element – note that this can come from
either a
Root or a Node
- Input of the removing node
- Lovelace count of the removing node UTxO
- Key (i.e. asset name without the prefix) of the removing node
- Underlying data of the removing node
Link of the removing node
Fold Functions
fold_from_root(
anchor_root_input_outref: OutputReference,
continued_anchor_root_output: Output,
inputs: List<Input>,
tx_mint: Value,
additional_validations: fn(
Input,
LovelaceChange,
RootData,
Input,
Lovelace,
NodeKey,
NodeData,
Link,
RootData,
) ->
Bool,
) -> Eval
Spend a root element and its link, reproduce the root while expecting the
linked node to be burnt. The root is identified by its output reference.
This traverses the whole inputs list and expects exactly two authentic
list inputs at the list payment credential: the root input and the folding
node input. This relies on the list spending validators preserving the
invariant that list NFTs do not leave their list script/payment credential,
and on the list script/payment credential being dedicated to list elements.
Unrelated inputs at the same payment credential are intentionally rejected.
Additionally, performs an arbitrary validation on keys and underlying data.
This function ensures:
- Authenticity of the two spent list inputs and root’s continued counterpart
- The anchor is root, and that it correctly points to the folding node
- The root is continued at the same address
- The continued anchor data remains a
Root
- The folding node shares the root’s payment credential
- Folding node is burnt
- Root’s key is correct, and node’s key is prefixed as expected
The function you define for additional_validations is provided with 9
values (in order):
- Input of the root element
- Change in Lovelace count from input root to output
- Underlying data of the root
- Input of the folding node
- Lovelace count of the folding node
- Key (i.e. asset name) of the folding node
- Underlying data of the folding node
Link of the folding node
- Underlying data of the reproduced/continued root
Spending Script Helpers
Simply checks if there are any tokens getting minted/burnt in transaction’s
mint field. How you provide list_nft_policy_id is most likely the
“coupling” needed between the spending and minting endpoints.
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: fn(LovelaceChange, Option<NodeKey>, Data, Data, Link) ->
Bool,
) -> Eval
For spending an individual element within a linked list and reproducing it
with updated data, without affecting the structure of the linked list.
The continuation provides you with:
- Change in Lovelace count from input to output (i.e. output minus input).
- A possible node key (i.e. its asset name without the prefix). It’ll be
None if the spent element is root.
- Input underlying data.
- Updated data in the reproduced element.
- Element’s link.
The transaction’s mint is required to ensure no linked list assets are minted
or burnt.
Exposed Helpers
This should generally not be needed, but in cases where another script wants
to validate expenditure from the linked list, this function can help.
After validating the authenticity of the provided element UTxO, it provides
the continuation with:
- Lovelace count of the element
- A possible
NodeKey (i.e. it will be None if the element UTxO is root)
- Underlying data of the element
- Element’s link
Unlike other helpers, this one returns an ElementEval<a>. This is very
similar to Eval. However, the type variable a allows the final
evaluation to result in any type rather than being restricted to Bool.
insert_ascending(
continued_anchor_element_output: Output,
new_node_output: Output,
inputs: List<Input>,
tx_mint: Value,
additional_validations: fn(
Input,
LovelaceChange,
Option<NodeKey>,
Data,
Lovelace,
NodeKey,
NodeData,
Link,
) ->
Bool,
) -> Eval
The anchor element is inferred from inputs by requiring exactly one
authentic linked list element input. It is assumed the address from which the
anchor element comes from is the destination address for the new node.
The additional_validations argument provides you with 8 values:
- Input of the anchor element
- Change in Lovelace count from anchor input to continued anchor output
- If the anchor element is a node, its key (i.e. asset name without the prefix) is provided
- Underlying data of the anchor element – note that this can come from
either a
Rootor aNode - Lovelace count of the new node UTxO
- Key (i.e. asset name without the prefix) of the new node
- Underlying data of the new node – note that this is extracted from a
Node Linkof the new node
insert_descending(
continued_anchor_element_output: Output,
new_node_output: Output,
inputs: List<Input>,
tx_mint: Value,
additional_validations: fn(
Input,
LovelaceChange,
Option<NodeKey>,
Data,
Lovelace,
NodeKey,
NodeData,
Link,
) ->
Bool,
) -> Eval
This is exactly like insert_ascending, with the
exception that the inserted key is expected to be less than the anchor’s,
and greater than anchor’s link (if any).
append_unordered(
continued_anchor_element_output: Output,
new_node_output: Output,
inputs: List<Input>,
tx_mint: Value,
additional_validations: fn(
Input,
LovelaceChange,
Option<NodeKey>,
Data,
Lovelace,
NodeKey,
NodeData,
) ->
Bool,
) -> Eval
Append a new element at the very end of an unordered list. The anchor
element is inferred from inputs by requiring exactly one authentic linked
list element input. There is no comparison validation for the new key with
respect to the anchor element’s, but the anchor must be terminal.
The additional_validations argument provides you with 7 values:
- Input of the anchor element
- Change in Lovelace count from anchor input to continued anchor output
- If the anchor element is a node, its key (i.e. asset name without the prefix) is provided
- Underlying data of the anchor element – note that this can come from
either a
Rootor aNode - Lovelace count of the new node UTxO
- Key (i.e. asset name without the prefix) of the new node
- Underlying data of the new node – note that this is extracted from a
Node
prepend_unordered(
continued_root_element_output: Output,
new_node_output: Output,
inputs: List<Input>,
tx_mint: Value,
additional_validations: fn(
Input,
LovelaceChange,
RootData,
Lovelace,
NodeKey,
NodeData,
Link,
) ->
Bool,
) -> Eval
Prepend a new element at the very start of an unordered list. The root
element is inferred from inputs by requiring exactly one authentic linked
list element input. There is no validation for the new key to be less than
or greater than the root element’s potential link.
The additional_validations argument provides you with 7 values:
- Input of the root element
- Change in Lovelace count from root input to continued root output
- Underlying data of the root
- Lovelace count of the new node UTxO
- Key (i.e. asset name without the prefix) of the new node
- Underlying data of the new node – note that this is extracted from a
Node Linkof the new node
remove(
anchor_input_outref: OutputReference,
continued_anchor_element_output: Output,
inputs: List<Input>,
tx_mint: Value,
additional_validations: fn(
Input,
LovelaceChange,
Option<NodeKey>,
Data,
Input,
Lovelace,
NodeKey,
NodeData,
Link,
) ->
Bool,
) -> Eval
Remove a node. The anchor element is identified by its output reference, and the anchor and removing node are inferred from the transaction inputs. This operation assumes the list script/payment credential is dedicated to list elements, so the transaction must not include unrelated inputs at the same payment credential.
The additional_validations argument provides you with 9 values:
- Input of the anchor element
- Change in Lovelace count from anchor input to continued anchor output
- If the anchor element is a node, its key (i.e. asset name without the prefix) is provided
- Underlying data of the anchor element – note that this can come from
either a
Rootor aNode - Input of the removing node
- Lovelace count of the removing node UTxO
- Key (i.e. asset name without the prefix) of the removing node
- Underlying data of the removing node
Linkof the removing node
fold_from_root(
anchor_root_input_outref: OutputReference,
continued_anchor_root_output: Output,
inputs: List<Input>,
tx_mint: Value,
additional_validations: fn(
Input,
LovelaceChange,
RootData,
Input,
Lovelace,
NodeKey,
NodeData,
Link,
RootData,
) ->
Bool,
) -> Eval
Spend a root element and its link, reproduce the root while expecting the
linked node to be burnt. The root is identified by its output reference.
This traverses the whole inputs list and expects exactly two authentic
list inputs at the list payment credential: the root input and the folding
node input. This relies on the list spending validators preserving the
invariant that list NFTs do not leave their list script/payment credential,
and on the list script/payment credential being dedicated to list elements.
Unrelated inputs at the same payment credential are intentionally rejected.
Additionally, performs an arbitrary validation on keys and underlying data.
This function ensures:
- Authenticity of the two spent list inputs and root’s continued counterpart
- The anchor is root, and that it correctly points to the folding node
- The root is continued at the same address
- The continued anchor data remains a
Root - The folding node shares the root’s payment credential
- Folding node is burnt
- Root’s key is correct, and node’s key is prefixed as expected
The function you define for additional_validations is provided with 9
values (in order):
- Input of the root element
- Change in Lovelace count from input root to output
- Underlying data of the root
- Input of the folding node
- Lovelace count of the folding node
- Key (i.e. asset name) of the folding node
- Underlying data of the folding node
Linkof the folding node- Underlying data of the reproduced/continued root
Spending Script Helpers
Simply checks if there are any tokens getting minted/burnt in transaction’s
mint field. How you provide list_nft_policy_id is most likely the
“coupling” needed between the spending and minting endpoints.
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: fn(LovelaceChange, Option<NodeKey>, Data, Data, Link) ->
Bool,
) -> Eval
For spending an individual element within a linked list and reproducing it
with updated data, without affecting the structure of the linked list.
The continuation provides you with:
- Change in Lovelace count from input to output (i.e. output minus input).
- A possible node key (i.e. its asset name without the prefix). It’ll be
None if the spent element is root.
- Input underlying data.
- Updated data in the reproduced element.
- Element’s link.
The transaction’s mint is required to ensure no linked list assets are minted
or burnt.
Exposed Helpers
This should generally not be needed, but in cases where another script wants
to validate expenditure from the linked list, this function can help.
After validating the authenticity of the provided element UTxO, it provides
the continuation with:
- Lovelace count of the element
- A possible
NodeKey (i.e. it will be None if the element UTxO is root)
- Underlying data of the element
- Element’s link
Unlike other helpers, this one returns an ElementEval<a>. This is very
similar to Eval. However, the type variable a allows the final
evaluation to result in any type rather than being restricted to Bool.
Simply checks if there are any tokens getting minted/burnt in transaction’s
mint field. How you provide list_nft_policy_id is most likely the
“coupling” needed between the spending and minting endpoints.
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: fn(LovelaceChange, Option<NodeKey>, Data, Data, Link) ->
Bool,
) -> Eval
For spending an individual element within a linked list and reproducing it with updated data, without affecting the structure of the linked list.
The continuation provides you with:
- Change in Lovelace count from input to output (i.e. output minus input).
- A possible node key (i.e. its asset name without the prefix). It’ll be
Noneif the spent element is root. - Input underlying data.
- Updated data in the reproduced element.
- Element’s link.
The transaction’s mint is required to ensure no linked list assets are minted or burnt.
This should generally not be needed, but in cases where another script wants to validate expenditure from the linked list, this function can help.
After validating the authenticity of the provided element UTxO, it provides the continuation with:
- Lovelace count of the element
- A possible
NodeKey(i.e. it will beNoneif the element UTxO is root) - Underlying data of the element
- Element’s link
Unlike other helpers, this one returns an ElementEval<a>. This is very
similar to Eval. However, the type variable a allows the final
evaluation to result in any type rather than being restricted to Bool.