Introduction
The Radix Engine Toolkit (RET) is the library that lets a program outside the network build, sign, and inspect Radix transactions. It is written in Rust and compiled to WebAssembly and native targets, with wrappers published for TypeScript, Python, Swift, C#, and Kotlin. Rust itself needs no wrapper β it uses the scrypto and radix-engine crates directly.
The boundary that matters is that RET is strictly off-ledger. It has no knowledge of ledger state, so it cannot tell you an account balance or whether a transaction committed. Those are Gateway API questions. What RET can do is everything that is pure computation: construct a manifest, compile and hash a transaction, derive an address from a public key, and decode SBOR payloads. Nearly every wallet, exchange integration, and backend signer on Radix is built on it.
What It Does
- Transactions β manifest building, transaction construction, compilation and decompilation, intent hashing, static validation, address extraction, and execution analysis (working out what a manifest will actually move).
- Derivation β virtual account and identity addresses from public keys, virtual signature non-fungible global IDs, and the Olympia-to-Babylon address mapping that migration tooling needs.
- SBOR β decoding Scrypto and Manifest SBOR payloads into readable form, and limited encoding back.
- Events β decoding events emitted by native components into typed models.
Static validation is the part most integrations under-use. It checks a transaction is well-formed β header bounds, signature counts, manifest structure β without a network round trip, which turns a class of submission failures into a local error.
Choosing an Entry Point
The TypeScript wrapper exposes three classes, and picking the wrong one is the usual first mistake:
RadixEngineToolkitβ the developer-facing class. Full functionality, idiomatic TypeScript, no backward-compatibility guarantee. Use this unless you have a reason not to.LTSRadixEngineToolkitβ a deliberately small surface with strong compatibility guarantees, aimed at exchange and custody integrations doing simple fungible transfers. Less capable by design; clients often outgrow it.RawRadixEngineToolkitβ the internal WASM invocation layer. No compatibility guarantees at all. You should not need it.
Note also that the TypeScript wrapper is hand-written while the others are generated with UniFFI, and it is scoped to transaction construction, signing, and derivations. For anything beyond that, reach for one of the other wrappers or the Rust library.
Building a Manifest in TypeScript
npm install @radixdlt/radix-engine-toolkitThe ManifestBuilder mirrors the Rust builder used throughout the Scrypto test suite, and allocates bucket and proof ids for you:
import {
ManifestBuilder,
RadixEngineToolkit,
NetworkId,
address,
bucket,
decimal,
} from "@radixdlt/radix-engine-toolkit";
const manifest = new ManifestBuilder()
.callMethod(senderAccount, "lock_fee", [decimal(5)])
.callMethod(senderAccount, "withdraw", [address(xrd), decimal(10)])
.takeAllFromWorktop(xrd, (builder, bucketId) =>
builder.callMethod(recipientAccount, "try_deposit_or_abort", [bucket(bucketId)])
)
.build();
// build() returns { instructions: { kind: "Parsed", value: [...] }, blobs: [] }.
// Manifest text is a separate, asynchronous conversion:
const text = await RadixEngineToolkit.Instructions.convert(
manifest.instructions,
NetworkId.Mainnet,
"String"
);
console.log(text.value);What build() returns is not manifest text. It is a TransactionManifest, declared as { instructions: Instructions; blobs: Uint8Array[] }, and Instructions is a two-variant union: { kind: "Parsed", value: Instruction[] } or { kind: "String", value: string }. The builder always emits the Parsed variant, and the interface carries no toString() β calling one gets the default [object Object]. Going from the parsed tree to the text described in Transaction Manifest Language is a round trip through the WASM core via RadixEngineToolkit.Instructions.convert(), which returns a Promise and takes a network id, because manifest text carries Bech32m addresses whose human-readable part is network-specific. The same module converts the other way, and also exposes compile(), decompile(), extractAddresses() and staticallyValidate() over the same instructions.
The Parsed form is the one to keep hold of: TransactionBuilder takes the TransactionManifest itself, not text. TransactionBuilder.new() is asynchronous β it has to instantiate the WASM host first β and returns a builder whose steps are separate types, so the order is enforced by the compiler: header() yields the manifest step, manifest() the signature step, and sign()/signAsync() accumulate before notarize() resolves to a NotarizedTransaction ready for the Gateway. That is the flow described in Transaction Lifecycle.
When You Need It
Front-end dApps usually do not call RET directly β the Radix dApp Toolkit builds manifests and hands them to the wallet for signing, which is the right pattern when a human approves each transaction. Reach for RET when there is no wallet in the loop:
- Backend signing β a service that holds keys and submits transactions itself, such as an exchange withdrawal pipeline.
- Programmatic wallets β the official iOS and Android wallets are built on the Swift and Kotlin wrappers.
- Analysis tooling β decompiling a transaction or decoding SBOR to explain what it did, without running a node.
- Autonomous agents β see AI Agents & x402 Payments, where the agent constructs a transaction that a policy component or wallet then authorises.
Next Steps
- Radix dApp Toolkit β the other direction β let the user's wallet do the signing
- Addresses and Entity Types β the format RET encodes, decodes, and derives
