---
id: TIP-1091
title: Enshrined ZoneFactory
description: Enshrine ZoneFactory as a Tempo precompile and define native ZonePortal account installation.
authors: Tanishk Goyal
status: Draft
related: Tempo Zones, TIP-20, TIP-403
protocolVersion: T10
---

# TIP-1091: Enshrined ZoneFactory

## Abstract

This TIP enshrines `ZoneFactory` as a Tempo precompile. Zone creation becomes a
native protocol operation, and each new zone gets a deterministic `ZonePortal`
account at a reserved vanity address.

The full zone protocol and canonical `ZonePortal` logic remain specified in the
Zones repository. The Solidity files included here cover only the native
factory behavior introduced by this TIP.

## Motivation

Zones should have one canonical registry and predictable portal addresses. A
native factory removes dependence on a separately deployed factory contract and
lets Tempo reserve recognizable portal accounts for zones.

`ZonePortal` is expected to become native later. Until precompiles can perform
the external EVM calls needed by the portal flow, each portal is an EVM account
installed by the protocol. The account contains only minimal proxy/caller
bytecode that points at one central `ZonePortal` logic implementation.

## Reference Artifacts

The reference artifacts for this TIP are:

1. [`tips/verify/src/zones/interfaces/IZone.sol`](verify/src/zones/interfaces/IZone.sol)
2. [`tips/verify/src/zones/tempo/ZoneFactory.sol`](verify/src/zones/tempo/ZoneFactory.sol)

The canonical portal logic is
[`ZonePortal.sol`](https://github.com/tempoxyz/zones/blob/main/specs/ref-impls/src/tempo/ZonePortal.sol)
in the Zones repository. T10 defines and embeds the canonical deployed runtime
bytecode for the portal implementation, verifier, and shared messenger in the
Tempo release that activates the hardfork.

## Specification

Tempo adds a `ZoneFactory` precompile at:

```solidity
address constant ZONE_FACTORY_ADDRESS = 0x5AF2000000000000000000000000000000000000;
```

The precompile exposes `IZoneFactory`. Its registry behavior MUST match the
canonical Zones implementation: monotonically increasing zone IDs, `zones`,
`nextZoneId`, `isZonePortal`, and `ZoneCreated`.

Each zone is created with a nonempty set of at most 8 sequencers and a nonzero
settlement `threshold` no greater than the set size. Sequencer addresses MUST be
nonzero and unique. Their order has no protocol significance and implementations
MUST NOT reject a sequencer set based on address ordering. All members have
the same permissions: any sequencer MAY submit a settlement carrying a valid
threshold certificate or perform another sequencer-authorized portal operation.
There is no primary or distinguished sequencer. The initial configuration nonce
is `0`. The portal admin MAY atomically replace the set and threshold; every
later replacement increments the nonce and invalidates certificates from prior
configurations.

A settlement certificate MUST contain at least `threshold` distinct signatures
from the active set. Its domain-separated commitment MUST bind the Tempo chain,
portal, zone ID, configuration nonce, zone height, and every input that affects
the resulting settlement. Duplicate, malformed, unregistered, or stale-configuration
signatures MUST be rejected. Any active sequencer MAY submit a valid certificate;
the submitter has no additional authority and need not occupy a distinguished
position in the set.

For the initial permissioned launch, the activation hardfork MUST configure a
nonzero factory `owner`. `createZone` MUST reject calls from any other account.
The owner MAY atomically transfer this authority to another account, including
the zero address.
This gate is temporary: the hardfork that enables open zone creation MUST remove
the caller restriction, after which any account may call `createZone` subject to
its normal validation rules.

A successful createZone call MUST consume at least 15,000,000 gas.
`createZone` MUST reject an initial token that does not have an explicit TIP-403
transfer-policy binding.

Zone creation selects independent initial enforcement modes for account access
(`Closed` or `Open`) and callback gateways (`Enforced` or `Open`). The optional
`allowedAccounts` and `zoneGateways` lists prepopulate roles even while their
corresponding mode is open. An empty closed or enforced role set denies all such
access until the admin assigns a role or changes the mode. The shared messenger
MUST NOT be an allowed account, and an address MUST NOT appear in both lists.
Duplicate and zero-address entries are permitted. The factory initializes
allowed accounts with the `Account` role and zone gateways with the
`CallbackGateway` role in the new portal.

Each zone portal is installed at:

```text
ZONE_PORTAL_PREFIX = 0x5AD000000000000000000000
portal = bytes20(ZONE_PORTAL_PREFIX || uint64(zoneId))
```

The suffix is the zone ID encoded big-endian into the low 8 bytes. `zoneId == 0`
is reserved, so `zoneId == 1` maps to
`0x5AD0000000000000000000000000000000000001`.

`isZonePortal(address)` MUST return true when the address has
`ZONE_PORTAL_PREFIX` and encodes a nonzero zone ID smaller than the next zone
ID. Callers MUST use this function rather than reproduce that check.

The portal implementation, verifier, and shared messenger use the following
protocol-managed addresses:

```solidity
address constant ZONE_PORTAL_IMPL_ADDRESS = 0x5AD1000000000000000000000000000000000000;
address constant ZONE_VERIFIER_ADDRESS = 0x5a56000000000000000000000000000000000000;
address constant ZONE_MESSENGER_ADDRESS = 0x5A4d000000000000000000000000000000000000;
```

Each portal account is etched with the standard
[ERC-1167](https://eips.ethereum.org/EIPS/eip-1167) deployed proxy bytecode,
with `ZONE_PORTAL_IMPL_ADDRESS` as the implementation address:

```text
portal_proxy_runtime =
  hex"363d3d373d3d3d363d73"
  || bytes20(ZONE_PORTAL_IMPL_ADDRESS)
  || hex"5af43d82803e903d91602b57fd5bf3"
```

### Initial Factory Configuration

The native factory MUST start with zone ID `1`. Its initial owner is configured
by T10. The owner MAY transfer ownership to any address, including the zero
address. The shared messenger at `ZONE_MESSENGER_ADDRESS` MUST authorize
`ZONE_FACTORY_ADDRESS`.

### Shared Runtime Management

At the T10 boundary, the protocol MUST directly install the hardfork-defined
portal implementation, verifier, and shared messenger runtime bytecode at their
protocol-managed addresses. The ZoneFactory MUST NOT expose any function that
allows its owner or another account to replace the installed runtimes.

When T10 is active at genesis, the genesis allocation MUST contain the same
runtimes at their protocol-managed addresses. The factory marker and initial
configuration MUST be installed in the same genesis state.

Any later runtime replacement MUST be specified by a hardfork and applied
directly at that hardfork boundary. Replacing the portal
implementation upgrades every existing portal, because every portal proxy
delegates to the fixed `ZONE_PORTAL_IMPL_ADDRESS`; replacement implementations
MUST therefore preserve the existing storage layout. The portal implementation
and shared messenger MUST embed `ZONE_FACTORY_ADDRESS` as their factory
authority.

`createZone` MUST NOT deploy the portal with EVM `CREATE` or `CREATE2`, and
MUST NOT rely on nonce-based address prediction. Instead, the native factory:

1. validates the request as the canonical factory would;
2. assigns the next zone ID;
3. computes the portal vanity address;
4. etches `portal_proxy_runtime` at that address;
5. calls the portal's factory-only initializer with the enforcement flags, role
   lists, sequencer set, threshold, and protocol-managed verifier and messenger
   addresses;
6. records the zone ID, portal, initial enforcement flags, admin, sequencer set,
   threshold, verifier, and RPC URL, and emits `ZoneCreated`.

The portal account MUST NOT contain a full copy of `ZonePortal` runtime
bytecode. It MUST route to one protocol-managed portal logic implementation,
while each portal keeps independent observable state.

Only `ZONE_FACTORY_ADDRESS` MAY call a portal initializer, and initialization
MUST succeed at most once. The factory MUST initialize a newly etched portal in
the same `createZone` operation before the portal becomes externally usable.
`ZONE_FACTORY_ADDRESS` MUST remain constant across portal logic deployments and
upgrades. Every `ZonePortal` implementation MUST enforce
`ZONE_FACTORY_ADDRESS` as its initializer authority; that value MUST NOT be
derived from the address that deploys the logic.

The initialized portal MUST use `ZONE_MESSENGER_ADDRESS` as its shared
messenger. Its initial zone `blockHash` MUST be `bytes32(0)`. Withdrawal callback
behavior MUST match the canonical `ZonePortal.sol` reference. It MUST store
account and gateway enforcement as two packed booleans in a dedicated slot where
zero represents open/open, and MUST emit `EnforcementModesUpdated` during
initialization before the initial `RoleUpdated` events.

User withdrawals MUST expose only a nonzero `uint64 fallbackNonce` on Tempo.
The zone outbox assigns this nonce monotonically and stores its mapping to the
private zone fallback recipient; a withdrawal bounce-back carries the nonce back
to the zone, where the inbox resolves and consumes the mapping. This keeps the
recipient out of L1-visible data while preserving deterministic execution.

At activation, T10 MUST atomically install the native factory at
`ZONE_FACTORY_ADDRESS` with the initial configuration above and install the three
shared runtimes as described in Shared Runtime Management.

Once native precompiles can perform the external EVM calls required by the
portal flow, a later hardfork MAY register the `ZONE_PORTAL_PREFIX` address
family for native dispatch. The native implementation MUST operate on the
called portal account's existing storage and preserve the `IZonePortal` ABI,
events, and authorization behavior. The migration MUST NOT change portal
addresses or require per-zone state migration solely because execution becomes
native; the existing proxy runtime MAY remain as observable account code while
native dispatch takes precedence.

## Out Of Scope

This TIP does not enshrine `ZonePortal`, `ZoneMessenger`, `Verifier`, or
`SwapAndDepositRouter`. It also does not specify migration for existing zones or
the final proof system.
