Transaction Manifest

The transaction model used with Radix Engine v2 is dramatically different from the transaction model used currently on the Radix Olympia mainnet. This new transaction manifest model will not become the model used for transactions on the Radix mainnet until the Babylon release.

Introduction

A transaction manifest is the Radix way of building transactions. It makes it possible to compose multiple actions to be executed atomically by describing a sequence of component calls and movements of resources between components. In short, full atomic composability becomes possible directly in transactions.

Transaction manifests can also describe the use of badges for authorization to components, payment of transaction fees, and checks on resources amounts to provide guaranteed results for the user.

Transaction manifests are human-readable so that developers or client software (such as a new Radix Wallet currently in development) can understand what they are signing. When it’s time to submit, the transaction manifest is translated into a binary representation and cryptographically signed to create a final transaction that may be efficiently sent to the network and processed by the Radix Engine.

Radix Transaction Layer

Transaction manifests orchestrate the movement of resources between components. This includes accounts, which are also components (that only their owner may withdraw from). This is done through a sequence of instructions using a special instruction set created specifically for this purpose (transaction manifests do not use Scrypto). Radix Engine processes these instructions in order, and if any step fails for any reason, the entire transaction fails and none of the steps are committed to the ledger on the Radix network. This is what is meant by the transaction being "atomic".

Execution of a given transaction can be thought of as happening at its own "layer", above any components that are called during the transaction. This layer has some special features that make transaction manifests quite powerful.

The Worktop

The most common instruction in a transaction manifest is a component call. Each call to a component can include data and buckets of resources, and each component may then return resources.

The transaction layer itself must include a way of managing resources between component calls. For this, we introduce the worktop. Each transaction has a worktop that is a place that resources may be held during the transaction execution. Resources returned from component calls are automatically put onto the worktop. From there, the manifest may specify that resources on the worktop be put into buckets so that those buckets may be passed as arguments to other component calls.

The manifest may also use ASSERT commands to check the contents of the worktop, causing the entire transaction to fail if not enough of the checked resource is present. This is useful to guarantee results of a transaction, even if you may be unsure of what a component may return.

Of course we know that all resources must be in a vault by the end of any transaction, so the transaction manifest creator must ensure that no resources are left hanging around the worktop or in buckets by the end of the manifest’s steps.

The Authorization Zone

Another feature of the transaction layer is the authorization zone. This is somewhat similar to the worktop, but is used specifically for authorization. Rather than holding resources, the authorization zone holds Proofs. A Proof is a special object that proves the possession of a given resource or resources. When a component is called by the transaction manifest, the Proofs in that transaction’s authorization zone are automatically passed to that component method’s authorization rules to be checked to determine if that method may be called or not.

These Proofs will include those that are automatically added to the authorization zone from "virtual badges" that are produced by signatures to the transaction. This, for example, is the primary method used to allow a user to authorize to their own account component to withdraw their tokens to use them elsewhere in the transaction manifest.

For more about Proofs and authorization, please see Access Control.

How to Create a Transaction Manifest?

There are two ways of creating transaction manifests for testing and interacting with your components in the Radix simulator:

Use the Simulator Tools

The resim CLI supports basic method (or function) invocation transactions. Starting from v0.3.0, any transaction resim supports can be outputted as a transaction manifest without committing it to the simulated ledger.

To turn on the feature, add --manifest <path> flag to your resim call-function command (or any other command that would create a transaction).

For example,

resim call-function 0147dfe3a58e8a630305f2f3df82949cd70ce49e2cde097b259f8d Hello new --manifest out.rtm

will produce something like:

CALL_FUNCTION PackageAddress("0147dfe3a58e8a630305f2f3df82949cd70ce49e2cde097b259f8d") "Hello" "new";
CALL_METHOD_WITH_ALL_RESOURCES ComponentAddress("0293c502780e23621475989d707cd8128e4506362e5fed6ac0c00a") "deposit_batch";

You can edit the auto-generated manifest to modify or add any other instructions you like.

Write it from Scratch

Once you’re familiar enough with the transaction manifest syntax, you can write a manifest from scratch.

Web front-end applications will of course need to construct their own transaction manifests to be passed to the Radix Wallet for signature and submission.

Please read the full specifications to learn more.

Example

In the following example, we’ll take 1 XRD from our account, call another component to buy a gumball, and then swap that gumball in a Radiswap component for some other resources (we won’t check on what we got back; we’ll just deposit whatever it is back into our account).

First, we’ll use some placeholders for readability:

# withdraw 1 XRD from account, which goes to the worktop
CALL_METHOD ComponentAddress("<my account address>") "withdraw" Decimal("1") ResourceAddress("<XRD address>");

# take 1 XRD from the worktop and pass it to the gumball machine
TAKE_FROM_WORKTOP_BY_AMOUNT Decimal("1") ResourceAddress("<XRD address>") Bucket("xrd");
CALL_METHOD ComponentAddress("<gumball machine address>") "buy_gumball" Bucket("xrd");

# take all returned gumballs and do a radiswap
TAKE_FROM_WORKTOP ResourceAddress("<GUM address>") Bucket("gumballs");
CALL_METHOD ComponentAddress("<Radiswap address>") "swap" Bucket("gumballs");

# deposit everything into my account
CALL_METHOD_WITH_ALL_RESOURCES ComponentAddress("<my account address>") "deposit_batch";

If you want to see the same transaction with some actual addresses from a local run, here you go:

# withdraw 1 XRD from account, which goes to the worktop
CALL_METHOD ComponentAddress("0293c502780e23621475989d707cd8128e4506362e5fed6ac0c00a") "withdraw" Decimal("1") ResourceAddress("030000000000000000000000000000000000000000000000000004");

# take 1 XRD from the worktop and pass it to the gumball machine
TAKE_FROM_WORKTOP_BY_AMOUNT Decimal("1") ResourceAddress("030000000000000000000000000000000000000000000000000004") Bucket("xrd");
CALL_METHOD ComponentAddress("024486cb926e3608d63429a08ed8a61122d70c7bd71d5db9880782") "buy_gumball" Bucket("xrd");

# take all returned gumballs and do a radiswap
TAKE_FROM_WORKTOP ResourceAddress("03afe53b9981dddb1b4ae44ff09deeae68f5bf42b360d88eaeb077") Bucket("gumballs");
CALL_METHOD ComponentAddress("0293c502780e23621475989d707cd8128e4506362e5fed6ac0c00a") "swap" Bucket("gumballs");

# deposit everything into my account
CALL_METHOD_WITH_ALL_RESOURCES ComponentAddress("0293c502780e23621475989d707cd8128e4506362e5fed6ac0c00a") "deposit_batch";