Emitting Outputs
While handling a request your application can emit four kinds of outputs. They differ in what they can do and in what can be proven about them on-chain:
| Output | Provable | Purpose |
|---|---|---|
| Voucher | yes | Execute a transaction on-chain (transfer assets, call a contract) |
| Notice | yes | Attest a fact about the application state |
| Report | no | Logs, diagnostics, inspect responses |
| Exception | no | Signal that the request could not be processed |
Vouchers and notices are added to the application's outputs merkle tree, so their existence can later be proven and they can be executed/validated on-chain. Reports are plain data made available off-chain.
Vouchers: act on-chain
A voucher is a one-shot transaction the application authorizes: when the epoch containing it settles, anyone can execute it through the application contract. Use it to transfer assets out, call DeFi protocols, or trigger any on-chain effect:
// transfer 1 ETH from the application contract to a user
const = .({
: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
: 1_000_000_000_000_000_000n, // wei
: "0x", // no calldata — plain value transfer
});To call a contract, put the EVM calldata in payload — for example an ERC-20 transfer(address,uint256):
const = "f39Fd6e51aad88F6F4ce6aB8827279cffFb92266".(64, "0");
const = (10n ** 18n).(16).(64, "0");
.({
: "0x6B175474E89094C44Da98b954EedeAC495271d0F", // token contract
: `0xa9059cbb${}${}`, // transfer(address,uint256) selector + args
});There is also a delegate-call variant that executes the target's code in the application contract's own storage context — an advanced tool for upgrade-style patterns.
Notices: attest facts
A notice carries no on-chain action — it is a verifiable statement that "the application, having processed inputs up to this point, says X". Typical uses: settlement results, state snapshots, anything an off-chain consumer wants to trust without re-executing the machine:
const = { : "0xf39F…2266", : "1000" };
.(.(.()));Reports: observe and debug
Reports are for everything that doesn't need a proof: log lines, error messages, and answers to inspect queries. They cost nothing on-chain and cannot be verified:
.(.("debug: cache warmed up"));Exceptions: give up loudly
An exception tells the outside world the request could not be processed at all — a structural failure rather than a business-rule rejection. Prefer returning false from a handler (or throwing, which run turns into a report + rejection); reach for emitException only when the application cannot continue in a meaningful way.
Output indices and proofs
emitVoucher, emitDelegateCallVoucher and emitNotice return the output's index — its position among all provable outputs ever produced by the application. Persist or report it if an off-chain component needs to locate the output later to fetch its proof and execute/validate it:
const index = .(.("state-root:abc123"));
Byte and number conventions
All payload-like arguments accept three shapes, converted automatically:
.("0xdeadbeef"); // 0x-prefixed hex string
.(.("hello")); // Buffer
.(new ([1, 2, 3])); // Uint8Array- Addresses (
destination,msgSender, …) must be exactly 20 bytes; as strings they are 0x-prefixed hex. - 256-bit values (
value) acceptbigint,number, or 32 bytes. - Outgoing data is always
Buffer(payloads) /bigint(numbers) / 0x-hex strings (addresses).
Hex strings are typed as `0x${string}` (Hex), so values from viem — Hex from encodeFunctionData, Address, bigint from parseEther — pass in directly, and request addresses pass back into viem without casts.
Invalid lengths or malformed hex throw TypeError/RangeError before anything reaches the device.
When libcmt fails
Every method can throw a RollupError carrying the libcmt error code if the underlying call fails — for example emitting an output larger than the machine's output buffer. The negative errno is available on error.errno and the failed call name on error.syscall.