Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 152 additions & 34 deletions sdk/deploy-operator.mdx
Original file line number Diff line number Diff line change
@@ -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.

<Card title="Run the example on GitHub" icon="github" href="https://github.com/BackTrackCo/x402r-sdk/tree/main/examples/deploy-operator" horizontal>
Clone the deploy-operator example and deploy your own operator in minutes.
</Card>

## 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:

Expand All @@ -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+
Expand Down Expand Up @@ -82,7 +91,7 @@ PRIVATE_KEY=0x... pnpm tsx examples/deploy-operator/deploy-short-escrow.ts
```
</Tip>

## Using the SDK Directly
## Using the SDK directly

If you want to integrate deployment into your own code:

Expand All @@ -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
};
}
```
Expand All @@ -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.
</Tip>

## Preview Addresses (No Deploy)
## Preview addresses (no deploy)

You can preview what addresses will be created without actually deploying:

Expand All @@ -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,
Expand All @@ -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);
```

<Note>
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.
</Note>

### 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:

Expand All @@ -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).
</Warning>

## Next Steps
## Next steps

<CardGroup cols={2}>
<Card title="Example on GitHub" icon="github" href="https://github.com/BackTrackCo/x402r-sdk/tree/main/examples/deploy-operator">
Expand Down
21 changes: 19 additions & 2 deletions sdk/examples.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
Loading