diff --git a/sdk/deploy-operator.mdx b/sdk/deploy-operator.mdx
index a97990b..5cbc2ce 100644
--- a/sdk/deploy-operator.mdx
+++ b/sdk/deploy-operator.mdx
@@ -1,16 +1,27 @@
---
-title: "Deploy an Operator"
+title: "Deploy an operator"
description: "Deploy a PaymentOperator with escrow, freeze, and dispute resolution in one call"
icon: "rocket"
---
-The `@x402r/core` package includes deployment utilities that handle the full lifecycle of deploying a PaymentOperator and all its supporting contracts.
+The `@x402r/core` package includes deployment presets that handle the full lifecycle of deploying a PaymentOperator and all its supporting contracts.
Clone the deploy-operator example and deploy your own operator in minutes.
-## Overview
+## Presets
+
+The SDK ships two deployment presets. Pick the one that matches your use case:
+
+| Preset | Use case | Freeze | Fees | RefundRequest |
+|--------|----------|--------|------|---------------|
+| `deployMarketplaceOperator` | General marketplace with dispute resolution | Yes (optional) | Yes (optional) | Yes |
+| `deployDeliveryProtectionOperator` | Garbage detection / delivery verification | No | No | No |
+
+All contracts are deployed via factories using CREATE2, so identical configurations produce identical addresses across deployments.
+
+## Marketplace operator
A complete marketplace operator deployment includes:
@@ -21,9 +32,7 @@ A complete marketplace operator deployment includes:
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
+## Deploy your operator
**Prerequisites:**
- Node.js 20+, pnpm 9.15+
@@ -82,7 +91,7 @@ PRIVATE_KEY=0x... pnpm tsx examples/deploy-operator/deploy-short-escrow.ts
```
-## Using the SDK Directly
+## Using the SDK directly
If you want to integrate deployment into your own code:
@@ -107,49 +116,54 @@ const walletClient = createWalletClient({
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)
+ chainId: 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);
+console.log('New deployments:', result.summary.newCount);
+console.log('Existing (reused):', result.summary.existingCount);
```
-## Configuration Options
+## Configuration options
| Option | Type | Description |
|--------|------|-------------|
+| `chainId` | `number` | Target chain ID (e.g., `84532` for Base Sepolia) |
| `feeRecipient` | `Address` | Address that receives operator fees |
| `arbiter` | `Address` | Arbiter address for dispute resolution |
| `escrowPeriodSeconds` | `bigint` | Escrow waiting period (e.g., `604800n` for 7 days) |
| `freezeDurationSeconds` | `bigint` | How long freezes last. Default: `0n` (permanent until unfrozen) |
| `operatorFeeBps` | `bigint` | Fee in basis points. Default: `0n` (no fee). `100n` = 1% |
+| `authorizedCodehash` | `Hex` | Optional codehash restriction. Default: `bytes32(0)` (no restriction) |
-## Deployment Result
+## Deployment result
The `deployMarketplaceOperator` function returns:
```typescript
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
+ operatorAddress: Address; // The PaymentOperator
+ escrowPeriodAddress: Address; // EscrowPeriod recorder/condition
+ freezeAddress: Address | null; // Freeze condition (null if disabled)
+ refundRequestAddress: Address; // RefundRequest contract
+ refundRequestEvidenceAddress: Address; // RefundRequestEvidence contract
+ refundInEscrowConditionAddress: Address; // OR(Receiver, Arbiter)
+ feeCalculatorAddress: Address | null; // null if no fee
+ operatorConfig: OperatorConfig; // Full operator slot configuration
+ deployments: DeployResult[]; // Per-contract deploy details
summary: {
- newDeployments: number; // Newly deployed contracts
- existingContracts: number; // Reused existing contracts
+ newCount: number; // Newly deployed contracts
+ existingCount: number; // Reused existing contracts
+ txHashes: `0x${string}`[]; // All deployment tx hashes
};
}
```
@@ -158,7 +172,7 @@ interface MarketplaceOperatorDeployment {
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)
+## Preview addresses (no deploy)
You can preview what addresses will be created without actually deploying:
@@ -167,8 +181,8 @@ import { previewMarketplaceOperator } from '@x402r/core/deploy';
const preview = await previewMarketplaceOperator(
publicClient,
- 'eip155:84532',
{
+ chainId: 84532,
feeRecipient: '0xYourAddress...',
arbiter: '0xArbiterAddress...',
escrowPeriodSeconds: 604800n,
@@ -179,22 +193,126 @@ console.log('Operator will be at:', preview.operatorAddress);
console.log('EscrowPeriod will be at:', preview.escrowPeriodAddress);
```
-## Operator Slot Configuration
+## Marketplace operator slot configuration
-The deployed operator has the following slot configuration:
+The deployed marketplace operator has the following slot configuration:
| Slot | Contract | Purpose |
|------|----------|---------|
| `AUTHORIZE_CONDITION` | UsdcTvlLimit | Safety limit on authorization |
| `AUTHORIZE_RECORDER` | EscrowPeriod | Records authorization timestamp |
| `CHARGE_CONDITION` | (none) | No restrictions on charge |
-| `RELEASE_CONDITION` | EscrowPeriod | Blocks release during escrow period |
+| `RELEASE_CONDITION` | EscrowPeriod (or AND(EscrowPeriod, Freeze) if freeze enabled) | Blocks release during escrow period |
| `REFUND_IN_ESCROW_CONDITION` | OR(Receiver, Arbiter) | Receiver or arbiter can approve |
+| `REFUND_IN_ESCROW_RECORDER` | RefundRequest | Tracks refund request state |
| `REFUND_POST_ESCROW_CONDITION` | Receiver | Only receiver after escrow |
-| `FEE_CALCULATOR` | StaticFeeCalculator | Fixed percentage fee |
+| `FEE_CALCULATOR` | StaticFeeCalculator | Fixed percentage fee (if configured) |
| `FEE_RECIPIENT` | Your address | Receives fees |
-## Network Support
+---
+
+## Delivery protection operator
+
+The delivery protection preset is a simpler operator designed for garbage detection and content verification use cases. An arbiter verifies that content was delivered correctly and releases funds. If the arbiter does nothing, the escrow window expires and anyone can trigger a refund.
+
+This preset has **no freeze, no fees, and no RefundRequest** contracts — making it cheaper to deploy.
+
+### Condition layout
+
+| Slot | Contract | Purpose |
+|------|----------|---------|
+| `RELEASE_CONDITION` | StaticAddressCondition(arbiter) | Only the arbiter can release |
+| `AUTHORIZE_RECORDER` | EscrowPeriod | Records authorization timestamp |
+| `REFUND_IN_ESCROW_CONDITION` | EscrowPeriod | Anyone can refund after escrow window expires |
+| `REFUND_POST_ESCROW_CONDITION` | Receiver | Receiver can refund post-escrow |
+
+### Deploy a delivery protection operator
+
+```typescript
+import { createPublicClient, createWalletClient, http } from 'viem';
+import { baseSepolia } from 'viem/chains';
+import { privateKeyToAccount } from 'viem/accounts';
+import { deployDeliveryProtectionOperator } 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 deployDeliveryProtectionOperator(
+ walletClient,
+ publicClient,
+ {
+ chainId: 84532, // Base Sepolia
+ arbiter: '0xArbiterAddress...', // Delivery verifier
+ feeRecipient: account.address, // Required (non-zero), future-proofs for fees
+ escrowPeriodSeconds: 604800n, // 7 days
+ }
+);
+
+console.log('Operator:', result.operatorAddress);
+console.log('EscrowPeriod:', result.escrowPeriodAddress);
+console.log('Arbiter condition:', result.arbiterConditionAddress);
+```
+
+
+The `feeRecipient` is required by the factory even though no fee calculator is set. This future-proofs the operator so you can add a fee calculator later without redeploying.
+
+
+### Configuration options
+
+| Option | Type | Description |
+|--------|------|-------------|
+| `chainId` | `number` | Target chain ID (e.g., `84532` for Base Sepolia) |
+| `arbiter` | `Address` | Arbiter address that verifies delivery |
+| `feeRecipient` | `Address` | Fee recipient (required, non-zero) |
+| `escrowPeriodSeconds` | `bigint` | Escrow waiting period (e.g., `604800n` for 7 days) |
+| `authorizedCodehash` | `Hex` | Optional codehash restriction. Default: `bytes32(0)` (no restriction) |
+
+### Deployment result
+
+```typescript
+interface DeliveryProtectionOperatorDeployment {
+ operatorAddress: Address; // The PaymentOperator
+ escrowPeriodAddress: Address; // EscrowPeriod recorder/condition
+ arbiterConditionAddress: Address; // StaticAddressCondition for arbiter
+ operatorConfig: OperatorConfig; // Full operator slot configuration
+ deployments: DeployResult[]; // Per-contract deploy details
+ summary: {
+ newCount: number; // Newly deployed contracts
+ existingCount: number; // Reused existing contracts
+ txHashes: `0x${string}`[]; // All deployment tx hashes
+ };
+}
+```
+
+### Preview addresses (no deploy)
+
+```typescript
+import { previewDeliveryProtectionOperator } from '@x402r/core/deploy';
+
+const preview = await previewDeliveryProtectionOperator(
+ publicClient,
+ {
+ chainId: 84532,
+ arbiter: '0xArbiterAddress...',
+ feeRecipient: '0xYourAddress...',
+ escrowPeriodSeconds: 604800n,
+ }
+);
+
+console.log('Operator will be at:', preview.operatorAddress);
+console.log('Arbiter condition:', preview.arbiterConditionAddress);
+```
+
+## Network support
Deployment is supported on all configured networks:
@@ -216,7 +334,7 @@ Deployment is supported on all configured networks:
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](https://docs.base.org/base-chain/tools/network-faucets).
-## Next Steps
+## Next steps
diff --git a/sdk/examples.mdx b/sdk/examples.mdx
index 44df86f..2d75a04 100644
--- a/sdk/examples.mdx
+++ b/sdk/examples.mdx
@@ -78,8 +78,8 @@ import { deployMarketplaceOperator } from '@x402r/core/deploy';
const result = await deployMarketplaceOperator(
walletClient,
publicClient,
- 'eip155:84532',
{
+ chainId: 84532, // Base Sepolia
feeRecipient: account.address,
arbiter: arbiterAddress,
escrowPeriodSeconds: 604800n, // 7 days
@@ -88,7 +88,24 @@ const result = await deployMarketplaceOperator(
);
```
-See [Deploy an Operator](/sdk/deploy-operator) for the full guide.
+You can also deploy a simpler delivery protection operator for content verification use cases:
+
+```typescript
+import { deployDeliveryProtectionOperator } from '@x402r/core/deploy';
+
+const result = await deployDeliveryProtectionOperator(
+ walletClient,
+ publicClient,
+ {
+ chainId: 84532,
+ arbiter: arbiterAddress,
+ feeRecipient: account.address,
+ escrowPeriodSeconds: 604800n,
+ }
+);
+```
+
+See [Deploy an operator](/sdk/deploy-operator) for the full guide.
## Server Examples