Skip to content

Assets

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.

Ada is always in scope inside tx bodies. You use it as a constructor — apply it to an amount to get an AnyAsset:

Ada(1000000) // 1 ADA expressed in lovelace
Ada(500000) // 0.5 ADA

Ada is the only asset name the language bakes in; everything else is declared.

If your protocol mentions a specific asset class repeatedly, give it a name with an asset declaration:

asset StaticAsset = 0xABCDEF1234."MYTOKEN";

The right-hand side is a policy expression, a dot, and an asset-name expression — both must have type Bytes. Once declared, the name can be used as a constructor like Ada:

StaticAsset(100)

Asset declarations live at the top level, alongside party, policy, and type.

When the policy or asset name is only known at runtime — for example because it comes from a parameter, an env value, or another input’s datum — use the built-in AnyAsset constructor function:

AnyAsset(policy, asset_name, quantity)

It takes a Bytes policy, a Bytes asset name, and an Int amount, and returns an AnyAsset value.

party Minter;
tx mint_from_local(
mint_policy: Bytes,
quantity: Int
) {
locals {
token: AnyAsset(mint_policy, "ABC", quantity),
}
mint {
amount: token,
redeemer: (),
}
input source {
from: Minter,
min_amount: fees,
}
output {
to: Minter,
amount: token - fees,
}
}

AnyAsset values combine with + and -:

Ada(1000000) + StaticAsset(50)
source + token - fees

Conceptually each operand is a bag of (policy, asset_name, amount) entries; addition merges and sums the amounts for matching entries, and subtraction takes the difference. There is no separate “multi-asset” type — bundles are just AnyAsset values that happen to carry more than one entry.

The value attached to an input or reference participates in the same arithmetic: source - Ada(quantity) - fees makes sense because source is an asset value.

An AnyAsset exposes three properties:

ExpressionTypeMeaning
asset.policyBytesThe asset class’s policy id.
asset.asset_nameBytesThe asset name.
asset.amountIntThe quantity.

These are useful when forwarding details into datums:

party Sender;
party Receiver;
type TokenBundle {
token_id: Bytes,
token_name: Bytes,
amount: Int,
}
tx send_token(policy: Bytes, asset_name: Bytes, quantity: Int) {
input source {
from: Sender,
min_amount: AnyAsset(policy, asset_name, quantity),
}
output {
to: Receiver,
amount: AnyAsset(policy, asset_name, quantity),
datum: TokenBundle {
token_id: policy,
token_name: asset_name,
amount: quantity,
},
}
output {
to: Sender,
amount: source - AnyAsset(policy, asset_name, quantity) - fees,
}
}

Asset values appear in a few specific positions:

  • input { min_amount: ... } — a lower bound on the value the resolver must find on the input.
  • output { amount: ... } — the exact value attached to a new output.
  • mint { amount: ... } / burn { amount: ... } — the assets minted or burnt by the transaction.

For the full surface of tx body blocks see Transactions.