Skip to main content
The @x402r/core package includes deployment utilities that handle the full lifecycle of deploying a PaymentOperator and all its supporting contracts.

Run the example on GitHub

Clone the deploy-operator example and deploy your own operator in minutes.

Overview

A complete marketplace operator deployment includes:
  1. EscrowPeriod — Records authorization time, enforces waiting period before release
  2. Freeze — Allows payer to freeze payment during escrow, receiver to unfreeze
  3. StaticAddressCondition — Restricts refund approval to the designated arbiter
  4. OrCondition — Allows either the receiver OR the arbiter to approve in-escrow refunds
  5. StaticFeeCalculator — Optional operator fee (basis points)
  6. PaymentOperator — The main contract tying everything together
All contracts are deployed via factories using CREATE2, so identical configurations produce identical addresses across deployments.

Deploy Your Operator

Prerequisites:
  • Node.js 20+, pnpm 9.15+
  • A private key with Base Sepolia ETH (get testnet ETH)
1

Clone and build the SDK

git clone https://github.com/BackTrackCo/x402r-sdk.git
cd x402r-sdk
pnpm install && pnpm build
2

Configure your deployment

Copy the example env file and set your private key:
cd examples/deploy-operator
cp .env.example .env
Edit .env with your values. Only PRIVATE_KEY is required — everything else has sensible defaults:
VariableDefaultDescription
PRIVATE_KEYDeployer wallet (required)
ARBITERdeployer addressDispute resolver
FEE_RECIPIENTdeployer addressReceives operator fees
ESCROW_PERIOD604800 (7 days)Escrow period in seconds
FREEZE_DURATION259200 (3 days)Freeze duration in seconds
FEE_BPS100 (1%)Operator fee in basis points
NETWORK_IDeip155:84532Chain identifier
RPC_URLhttps://sepolia.base.orgRPC endpoint
3

Run the deployment

pnpm start
Or pass env vars inline from the SDK root:
PRIVATE_KEY=0x... pnpm tsx examples/deploy-operator/index.ts
4

Use your operator address

The script outputs all deployed contract addresses and BaseScan links. Use the PaymentOperator address in your payment payloads:
operator: "0xYourOperatorAddress..."
For quick E2E testing with short timers (5-minute escrow, 3-minute freeze), use the short-escrow variant instead:
PRIVATE_KEY=0x... pnpm tsx examples/deploy-operator/deploy-short-escrow.ts

Using the SDK Directly

If you want to integrate deployment into your own code:
import { createPublicClient, createWalletClient, http } from 'viem';
import { baseSepolia } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';
import { deployMarketplaceOperator } from '@x402r/core/deploy';

const publicClient = createPublicClient({
  chain: baseSepolia,
  transport: http(),
});

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const walletClient = createWalletClient({
  account,
  chain: baseSepolia,
  transport: http(),
});

const result = await deployMarketplaceOperator(
  walletClient,
  publicClient,
  'eip155:84532', // Base Sepolia
  {
    feeRecipient: account.address,        // receives operator fees
    arbiter: '0xArbiterAddress...',        // dispute resolver
    escrowPeriodSeconds: 604800n,          // 7 days
    freezeDurationSeconds: 259200n,        // 3 days max freeze
    operatorFeeBps: 100n,                  // 1% fee (optional)
  }
);

console.log('Operator:', result.operatorAddress);
console.log('EscrowPeriod:', result.escrowPeriodAddress);
console.log('Freeze:', result.freezeAddress);
console.log('New deployments:', result.summary.newDeployments);
console.log('Existing (reused):', result.summary.existingContracts);

Configuration Options

OptionTypeDescription
feeRecipientAddressAddress that receives operator fees
arbiterAddressArbiter address for dispute resolution
escrowPeriodSecondsbigintEscrow waiting period (e.g., 604800n for 7 days)
freezeDurationSecondsbigintHow long freezes last. Default: 0n (permanent until unfrozen)
operatorFeeBpsbigintFee in basis points. Default: 0n (no fee). 100n = 1%

Deployment Result

The deployMarketplaceOperator function returns:
interface MarketplaceOperatorDeployment {
  operatorAddress: Address;           // The PaymentOperator
  escrowPeriodAddress: Address;       // EscrowPeriod recorder/condition
  freezeAddress: Address;             // Freeze condition
  arbiterConditionAddress: Address;   // StaticAddressCondition for arbiter
  refundInEscrowCondition: Address;   // OR(Receiver, Arbiter)
  feeCalculatorAddress: Address | null; // null if no fee
  txHashes: Hash[];                   // All deployment tx hashes
  summary: {
    newDeployments: number;           // Newly deployed contracts
    existingContracts: number;        // Reused existing contracts
  };
}
Because all contracts use CREATE2, redeploying with the same parameters is idempotent — it will detect existing contracts and skip them. The summary tells you what was new vs reused.

Preview Addresses (No Deploy)

You can preview what addresses will be created without actually deploying:
import { previewMarketplaceOperator } from '@x402r/core/deploy';

const preview = await previewMarketplaceOperator(
  publicClient,
  'eip155:84532',
  {
    feeRecipient: '0xYourAddress...',
    arbiter: '0xArbiterAddress...',
    escrowPeriodSeconds: 604800n,
  }
);

console.log('Operator will be at:', preview.operatorAddress);
console.log('EscrowPeriod will be at:', preview.escrowPeriodAddress);

Operator Slot Configuration

The deployed operator has the following slot configuration:
SlotContractPurpose
AUTHORIZE_CONDITIONUsdcTvlLimitSafety limit on authorization
AUTHORIZE_RECORDEREscrowPeriodRecords authorization timestamp
CHARGE_CONDITION(none)No restrictions on charge
RELEASE_CONDITIONEscrowPeriodBlocks release during escrow period
REFUND_IN_ESCROW_CONDITIONOR(Receiver, Arbiter)Receiver or arbiter can approve
REFUND_POST_ESCROW_CONDITIONReceiverOnly receiver after escrow
FEE_CALCULATORStaticFeeCalculatorFixed percentage fee
FEE_RECIPIENTYour addressReceives fees

Network Support

Deployment is supported on all configured networks:
NetworkChain IDEIP-155 ID
Base Sepolia84532eip155:84532
Base Mainnet8453eip155:8453
Ethereum1eip155:1
Ethereum Sepolia11155111eip155:11155111
Arbitrum Sepolia421614eip155:421614
Polygon137eip155:137
Arbitrum42161eip155:42161
Optimism10eip155:10
Avalanche43114eip155:43114
Celo42220eip155:42220
Monad143eip155:143
Deployment requires gas fees. Ensure your wallet has ETH on the target network. On Base Sepolia, you can get testnet ETH from Base network faucets.

Next Steps

Example on GitHub

Clone and run the deploy-operator example.

Examples

See working merchant and client examples.

Helpers

Mark payment options as refundable with your operator.

Smart Contracts

Understand the underlying contract architecture.