Skip to main content

Overview

x402r uses the factory pattern with CREATE2 for gas-efficient, deterministic contract deployments. Factories enable on-demand instance creation with predictable addresses.

Why factories

Addresses are predictable before deployment, enabling:
  • Off-chain address generation
  • Cross-chain address consistency
  • Contract-to-contract communication without registries
Many instances can share immutable configuration:
  • Lower deployment costs
  • Consistent behavior across instances
  • Centralized ownership control
Calling a factory with the same parameters returns the existing contract:
  • Safe to call again
  • No duplicate deployments
  • Built-in deduplication
Singleton conditions deployed once, reused everywhere:
  • PayerCondition, ReceiverCondition deployed once
  • All operators share the same condition instances
  • Minimal storage overhead

Payment Operator Factory

Deploys PaymentOperator instances with deterministic addresses.

Contract Address

All factories use universal CREATE2 addresses (same on every chain). PaymentOperatorFactory: 0xa0d4734842df1690a5B33Cb21828c946e39D55a2

Configuration Structure

Deployment Method

Parameters (in config):
  • feeReceiver - Who receives operator fees (arbiter, service provider, or treasury)
  • authorizePreActionCondition through refundPostActionHook - 10-slot configuration
Note: the factory sets maxFeeBps and protocolFeePct (shared across all operators) Returns: Address of deployed operator (or existing if already deployed)

Address Prediction

Predict the operator address before deployment:
Usage:

Example Deployment

Marketplace Operator

Subscription Operator

If you call deployOperator() with the same configuration twice, the factory returns the existing operator address without deploying a new contract.

Escrow Period Factory

Deploys EscrowPeriod contracts - combined hook and condition for time-based capture logic.

Contract Address

EscrowPeriodFactory: 0xe72D2014ebC48F1d92521e8629574918E8030548

Deployment Method

Parameters:
  • escrowPeriod - Duration in seconds (for example, 7 * 24 * 60 * 60 for 7 days)
  • authorizedCodehash - Runtime codehash of authorized caller (bytes32(0) = operator-only)
Returns: Address of deployed EscrowPeriod contract

How It Works

The factory deploys a single EscrowPeriod contract that:
  • Extends AuthorizationTimeRecorderHook (implements IHook)
  • Implements ICondition
  • Records authorization timestamp when used as hook
  • Checks if escrow period has passed when used as condition
Architecture:
Use the SAME EscrowPeriod address for both AUTHORIZE_POST_ACTION_HOOK and CAPTURE_PRE_ACTION_CONDITION slots on the operator. For freeze functionality, deploy a separate Freeze condition and compose via AndCondition([escrowPeriod, freeze]).

Example Deployment

Common Escrow Periods


Freeze Factory

Deploys Freeze condition contracts that block capture when the payer freezes a payment.

Contract Address

FreezeFactory: 0xeC092cf1215DB44af0Abe87c1157E304FEa5d0Eb

Deployment Method

Parameters:
  • freezeCondition - ICondition that gates freeze calls (for example, PayerCondition)
  • unfreezeCondition - ICondition that gates unfreeze calls (for example, PayerCondition or ArbiterCondition)
  • freezeDuration - How long freeze lasts in seconds (0 = permanent until unfrozen)
  • escrowPeriodContract - Address of EscrowPeriod contract (address(0) = freeze unconstrained by time)
Returns: Address of deployed Freeze condition

Full Freeze Deployment Example

Condition Singletons

Reference the pre-deployed condition singletons (PayerCondition, ReceiverCondition, AlwaysTrueCondition) by their canonical addresses. The full address registry lives on Periphery Overview: Condition Singletons, identical across every supported chain.

Example Deployments

Payer can freeze, arbiter can unfreeze (or it expires after 3 days):

Freeze Duration Guidelines

Freeze duration should balance payer protection with receiver UX. Too long and receivers may avoid the platform. Too short and payers can’t adequately investigate.

Factory Ownership

A multisig wallet owns all factories for security.

Owner Capabilities

Factory owners can:
  • Update factory configuration (if mutable fields exist)
  • Rescue stuck ETH (via rescueETH())
  • Transfer ownership (2-step process)
Factory owners cannot:
  • Change deployed instances
  • Pause or stop operations
  • Access funds in deployed operators

Ownership Transfer


Gas Costs

Approximate gas costs for factory deployments (Base Sepolia):
Use predict*Address() functions before deploying to verify addresses off-chain and avoid unnecessary deployments.

CREATE2 Details

Salt Generation

Each factory uses different salt strategies: PaymentOperatorFactory:
EscrowPeriodFactory:
FreezeFactory:

Cross-Chain Addresses

Because the factory uses CREATE2, the same configuration produces the same operator address on any chain where the factory itself lives at the canonical address. As supported chains expand beyond Base, an operator deployed with identical config will land at the same address on each new chain without the integrator needing per-chain bookkeeping. This enables:
  • Consistent addressing across chains
  • Simplified multi-chain integrations
  • Predictable contract locations

Best Practices

1. Predict Before Deploy

Always verify predicted address before deployment:

2. Reuse Condition Singletons

Don’t deploy new PayerCondition/ReceiverCondition - use existing singletons:

3. Test Configuration First

Deploy on testnet with same configuration before mainnet:

4. Document your config

Keep a record of your deployed configurations:

Next Steps

Conditions

Learn about the pluggable condition system.

Examples

See real-world configuration examples.

Deploy an Operator

Use the SDK’s deployMarketplaceOperator() for simplified deployment.

SDK Overview

Install the SDK packages.