Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion language/assets.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Assets
sidebar:
order: 6
order: 7
---

A UTxO carries value: a bag of `(policy, asset_name, amount)` triples. Tx3 surfaces this with the `AnyAsset` type and a small set of conveniences that make it easy to talk about quantities of specific assets without writing the policy and asset name out at every use.
Expand Down
2 changes: 1 addition & 1 deletion language/cardano.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Cardano-specific Features
sidebar:
label: Cardano
order: 8
order: 9
---

Most of the Tx3 language is chain-agnostic, but some transaction features are specific to one chain. On Cardano those features live under the `cardano::` namespace: stake operations, witnesses for script-using actions, reference-script publishing, treasury donations, and so on.
Expand Down
2 changes: 1 addition & 1 deletion language/comments.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Comments
sidebar:
order: 9
order: 10
---

Tx3 has three kinds of comments: regular line comments, block comments, and doc-comments. The first two are stripped during parsing and have no effect beyond letting you annotate the source. Doc-comments are different — they are preserved and surfaced by downstream tooling (the registry UI, generated bindings, etc.).
Expand Down
4 changes: 2 additions & 2 deletions language/data.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,12 @@ Accessing a property that does not exist on the value's static type is a compile

## Built-in functions

These functions are always in scope.
These functions are always in scope. They use the same call syntax as [user-defined functions](./functions), and share the same namespace.

| Call | Returns | Notes |
| ------------------------- | ---------- | -------------------------------------------------------------------------------------- |
| `min_utxo(output_name)` | `AnyAsset` | Minimum Ada the named output needs to satisfy the chain's min-UTxO rule. |
| `tip_slot()` | `Int` | The chain tip slot at resolution time. May also be written as the bare identifier `tip_slot`. |
| `tip_slot()` | `Int` | The chain tip slot at resolution time. |
| `slot_to_time(slot)` | `Int` | Converts a slot number to a POSIX time. |
| `time_to_slot(time)` | `Int` | Converts a POSIX time to a slot number. |
| `concat(a, b)` | same as `a`| Concatenates two `Bytes` values or two `List<T>` values; both arguments must have the same type. |
Expand Down
90 changes: 90 additions & 0 deletions language/functions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
---
title: Functions
sidebar:
order: 6
---

A function is a named, reusable piece of a [data expression](./data). When the same calculation shows up in several places — or when a sub-expression is complex enough to deserve a name — you can factor it into an `fn` and call it wherever a value is expected.

Functions are a source-level convenience: they are *inlined* by the compiler, so a call leaves no trace in the resolved transaction. They add naming and reuse, not new runtime behaviour. They are pure, non-recursive, and cannot touch transaction state (inputs, outputs, `fees`, and the like).

## Declaring a function

```tx3
fn double(x: Int) -> Int {
x + x
}
```

A function declaration has a name, a parenthesised parameter list, a return type after `->`, and a body in braces. Every parameter is typed, and the return type is explicit — there is no inference.

Identifier rules match the rest of the language: a function name starts with a letter and contains letters, digits, and underscores, and must be unique across the program's top-level declarations (including the built-in functions below).

## Function body

A body is an optional sequence of `let`-bindings followed by a single result expression. The result is the value the function returns; its type must match the declared return type.

```tx3
fn discounted(base: Int, off: Int) -> Int {
let net = base - off;
let doubled = net + net;
doubled
}
```

Each `let` names the value of a data expression and is visible to the bindings that follow it and to the result. A body is itself just a data expression, so the usual operators (`+`, `-`, property access, indexing) and constructors are available — but transaction blocks, control flow, and recursion are not.

Functions can return records (or any other type), and can call other functions:

```tx3
type PoolState {
pair_a: Int,
pair_b: Int,
}

fn make_pool(a: Int, b: Int) -> PoolState {
PoolState {
pair_a: a,
pair_b: b,
}
}

fn adjust_pool(pool: PoolState, delta_a: Int, delta_b: Int) -> PoolState {
PoolState {
pair_a: pool.pair_a + delta_a,
pair_b: pool.pair_b - delta_b,
}
}
```

## Calling a function

Call a function with the same syntax as the [built-in functions](./data#built-in-functions): the name followed by parenthesised arguments. A call is valid anywhere a data expression is — output amounts, datums, locals, and so on.

```tx3
party Sender;
party Receiver;

tx use_double(amount: Int) {
input source {
from: Sender,
min_amount: Ada(amount),
}
output {
to: Receiver,
amount: Ada(double(amount)),
}
}
```

The number of arguments must match the parameters, and each argument's type must match the corresponding parameter's type.

## Functions and the built-ins

The built-in helpers `min_utxo`, `tip_slot`, `slot_to_time`, and `time_to_slot` are functions too — they share the same call syntax and namespace, and you cannot define an `fn` that reuses one of their names. The difference is that the built-ins are evaluated by the resolver (using chain state), whereas user-defined functions are inlined away at compile time. See [Data Expressions](./data#built-in-functions) for their signatures.

## Restrictions

- Functions are top-level only; they cannot be nested.
- Functions must not be recursive, directly or through a cycle of calls — inlining would not terminate.
- A function body cannot reference a transaction's inputs, outputs, references, locals, or `fees`; it only sees its own parameters and `let`-bindings, plus program-level names (parties, policies, assets, types).
2 changes: 2 additions & 0 deletions language/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ The right-hand side of every Tx3 clause is a data expression — a literal, a co

<LinkCard title="Data Expressions" href="language/data" description="Literals, operators, constructors, property access, and built-in functions" />

<LinkCard title="Functions" href="language/functions" description="Named, reusable data expressions — declared with `fn` and inlined at compile time" />

## Transaction templates

The core declaration in a Tx3 file is a `tx`: a parameterised template that the toolchain turns into a concrete, balanced transaction. A `tx` body is a collection of blocks — inputs, outputs, mints, witnesses, validity bounds, signers, metadata, locals, collateral — combined freely.
Expand Down
2 changes: 1 addition & 1 deletion language/txs.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Transactions
sidebar:
order: 7
order: 8
---

A `tx` declares a transaction *template*: a parameterised description of a transaction that the toolchain resolves into a concrete, balanced, ready-to-submit transaction at the moment of use. The body of a `tx` is a collection of blocks — inputs, outputs, mints, witnesses, metadata, and so on — that together describe what the transaction must contain.
Expand Down