Build A Dedicated L2 App

This guide shows the shape of a fast-deployable app built on AuthorizedEventHub.

Use this path when general object records are not enough and the app needs its own command format, hook contract, and replayed state machine. The result is a dedicated L2-style app: users submit commands to a shared base log, the app writes its own event stream, and an app subgraph derives state from that stream.

The current SDK and app templates are still evolving. Keep app interfaces small and explicit.

1. Define The App Domain

An app should keep its state under its own namespace.

Example:

/<app-contract-address>/<app-name>/inbox

The inbox is where the app writes command records. The app subgraph replays those records.

2. Define Commands

Commands are application-specific.

Example from the payment demo:

InitSupply
Transfer

The command payload should be ABI-encoded and stable across versions.

3. Implement The App Contract

The app contract receives actions from AEH:

function onAction(address actor, bytes32 actionType, bytes calldata payload) external;

The hook should:

Business-invalid commands may still be logged. The subgraph decides whether each command changes the derived app state.

4. Register The App In AEH

The AEH administrator registers:

This allows the shared relay to sponsor allowlisted app actions without allowing arbitrary contract calls.

5. Submit Commands From The SDK

Generic call:

await client.submitToApp({
  appId,
  actionType,
  payload,
});

Payment demo helper:

await client.paymentDemoTransfer({
  to,
  amount,
});

The SDK signs and submits through the same relay lifecycle as public-domain mutations.

6. Build The App Subgraph

The app subgraph should:

For example, the payment demo derives account balances from inbox instructions.

7. Handle UI State

Use SDK operation states:

draft -> signing -> submitted -> confirmed -> indexed

confirmed means the command is on-chain. indexed means the app subgraph has replayed it and the derived state is queryable.

If the app allows invalid commands into the log, the frontend should distinguish: