diff --git a/contracts/architecture.mdx b/contracts/architecture.mdx
index 144955a..374777b 100644
--- a/contracts/architecture.mdx
+++ b/contracts/architecture.mdx
@@ -4,14 +4,63 @@ description: "System architecture, payment flows, and contract relationships"
icon: "diagram-project"
---
-## System Diagrams
-
-For detailed visual architecture diagrams, see the [x402r-contracts repository](https://github.com/BackTrackCo/x402r-contracts#architecture):
+## System Overview
+
+```mermaid
+flowchart TB
+ subgraph Users
+ Payer
+ Receiver
+ DesAddr["Designated Address
(Arbiter / Provider / DAO)"]
+ end
+
+ subgraph Factories
+ POF[PaymentOperatorFactory]
+ EPF[EscrowPeriodFactory]
+ FF[FreezeFactory]
+ end
+
+ subgraph Operator["PaymentOperator (per config)"]
+ Auth[authorize]
+ Charge[charge]
+ Release[release]
+ RefundIE[refundInEscrow]
+ RefundPE[refundPostEscrow]
+ end
+
+ subgraph Plugins["Conditions & Recorders"]
+ Cond["ICondition
(check before action)"]
+ Rec["IRecorder
(record after action)"]
+ EP[EscrowPeriod]
+ Freeze[Freeze]
+ And[AndCondition]
+ Or[OrCondition]
+ end
+
+ Escrow[AuthCaptureEscrow]
+ RR[RefundRequest]
+
+ Payer -->|authorize / freeze| Operator
+ Receiver -->|release / charge| Operator
+ DesAddr -->|refund / release| Operator
+ Payer -->|requestRefund| RR
+
+ POF -->|deploys| Operator
+ EPF -->|deploys| EP
+ FF -->|deploys| Freeze
+
+ Operator -->|checks| Cond
+ Operator -->|calls| Rec
+ Operator -->|locks/releases funds| Escrow
+
+ EP -.->|implements| Cond
+ EP -.->|implements| Rec
+ Freeze -.->|implements| Cond
+ And -.->|composes| Cond
+ Or -.->|composes| Cond
+```
-- [System Overview](https://github.com/BackTrackCo/x402r-contracts#system-overview) - User interactions, operator slots, and escrow state machine
-- [Contract Relationships](https://github.com/BackTrackCo/x402r-contracts#contract-relationships) - How factories, operators, and conditions interact
-- [Condition Combinator Pattern](https://github.com/BackTrackCo/x402r-contracts#condition-combinator-pattern) - Composable authorization logic
-- [Escrow Period & Freeze Flow](https://github.com/BackTrackCo/x402r-contracts#escrow-period--freeze-flow) - Timeline of payment states
+For additional visual diagrams, see the [x402r-contracts repository](https://github.com/BackTrackCo/x402r-contracts#architecture).
## Payment Flow Sequence
@@ -236,23 +285,20 @@ Ownership transfers use Solady's Ownable pattern:
### Core Events
```solidity
-// Payment lifecycle
-event PaymentAuthorized(bytes32 indexed paymentId, address indexed payer, uint256 amount);
-event PaymentReleased(bytes32 indexed paymentId, uint256 amount);
-event PaymentRefunded(bytes32 indexed paymentId, uint256 amount, bool inEscrow);
-
-// Refund requests
-event RefundRequested(bytes32 indexed paymentId, address indexed requester);
-event RefundApproved(bytes32 indexed paymentId, address indexed approver);
-event RefundDenied(bytes32 indexed paymentId, address indexed denier);
-
-// Freeze state
-event PaymentFrozen(bytes32 indexed paymentId, uint40 frozenAt);
-event PaymentUnfrozen(bytes32 indexed paymentId);
-
-// Fee changes
-event FeeUpdateQueued(uint256 newMaxFee, uint256 newProtocolPct, uint256 effectiveTime);
-event FeeUpdateExecuted(uint256 maxFee, uint256 protocolPct);
+// Payment lifecycle (PaymentOperator events)
+event AuthorizationCreated(bytes32 indexed paymentInfoHash, address indexed payer, address indexed receiver, uint256 amount, uint256 timestamp);
+event ChargeExecuted(bytes32 indexed paymentInfoHash, address indexed payer, address indexed receiver, uint256 amount, uint256 timestamp);
+event ReleaseExecuted(AuthCaptureEscrow.PaymentInfo paymentInfo, uint256 amount, uint256 timestamp);
+event RefundInEscrowExecuted(AuthCaptureEscrow.PaymentInfo paymentInfo, address indexed payer, uint256 amount);
+event RefundPostEscrowExecuted(AuthCaptureEscrow.PaymentInfo paymentInfo, address indexed payer, uint256 amount);
+
+// Fee distribution
+event FeesDistributed(address indexed token, uint256 protocolAmount, uint256 arbiterAmount);
+event OperatorDeployed(address indexed operator, address indexed feeRecipient, address indexed releaseCondition);
+
+// Freeze state (Freeze contract events)
+event PaymentFrozen(bytes32 indexed paymentInfoHash, uint40 frozenAt);
+event PaymentUnfrozen(bytes32 indexed paymentInfoHash);
```
These events enable off-chain monitoring and indexing.
diff --git a/contracts/conditions.mdx b/contracts/conditions.mdx
index b31e80f..44d8e07 100644
--- a/contracts/conditions.mdx
+++ b/contracts/conditions.mdx
@@ -97,11 +97,11 @@ Pre-deployed singletons for role-based access control.
Only the payment payer can call the action.
-**Address (Base Sepolia):** `0xDc0D800007ceACFf1299b926Ce22B4d4edCE6Ce7`
+**Address (Base Sepolia):** `0xBAF68176FF94CAdD403EF7FbB776bbca548AC09D`
**Logic:**
```solidity
-function check(PaymentInfo calldata payment, address caller)
+function check(PaymentInfo calldata payment, uint256, address caller)
external pure returns (bool)
{
return caller == payment.payer;
@@ -117,11 +117,11 @@ function check(PaymentInfo calldata payment, address caller)
Only the payment receiver can call the action.
-**Address (Base Sepolia):** `0x138Bf828643350AA3692aedDE8b2254eDF4D07EF`
+**Address (Base Sepolia):** `0x12EDefd4549c53497689067f165c0f101796Eb6D`
**Logic:**
```solidity
-function check(PaymentInfo calldata payment, address caller)
+function check(PaymentInfo calldata payment, uint256, address caller)
external pure returns (bool)
{
return caller == payment.receiver;
@@ -148,7 +148,7 @@ contract StaticAddressCondition is ICondition {
DESIGNATED_ADDRESS = _designatedAddress;
}
- function check(PaymentInfo calldata payment, address caller)
+ function check(PaymentInfo calldata payment, uint256, address caller)
external view returns (bool)
{
return caller == DESIGNATED_ADDRESS;
@@ -179,11 +179,11 @@ const daoCondition = new StaticAddressCondition(daoMultisigAddress);
Anyone can call the action (no restrictions).
-**Address (Base Sepolia):** `0xe2659dc0d716B1226DF6a09A5f47862cd1ff6733`
+**Address (Base Sepolia):** `0x785cC83DEa3d46D5509f3bf7496EAb26D42EE610`
**Logic:**
```solidity
-function check(PaymentInfo calldata payment, address caller)
+function check(PaymentInfo calldata payment, uint256, address caller)
external pure returns (bool)
{
return true;
@@ -205,15 +205,14 @@ Compose multiple conditions with logical operators.
All conditions must pass (`A && B && C`).
**Usage:**
-```solidity
-// Deploy combinator
-AndCondition combo = new AndCondition([
- RECEIVER_CONDITION, // Must be receiver
- ESCROW_PERIOD_CONDITION // Must be after escrow
+```typescript
+// Deploy via factory
+const comboAddress = await andConditionFactory.write.deploy([
+ [RECEIVER_CONDITION, ESCROW_PERIOD_ADDRESS] // Must be receiver AND after escrow
]);
// Use in operator config
-config.releaseCondition = address(combo);
+config.releaseCondition = comboAddress;
```
**Example:** Release requires receiver AND escrow passed
@@ -223,14 +222,13 @@ config.releaseCondition = address(combo);
At least one condition must pass (`A || B`).
**Usage:**
-```solidity
+```typescript
// Receiver OR Arbiter can release
-OrCondition combo = new OrCondition([
- RECEIVER_CONDITION,
- ARBITER_CONDITION
+const comboAddress = await orConditionFactory.write.deploy([
+ [RECEIVER_CONDITION, ARBITER_CONDITION]
]);
-config.releaseCondition = address(combo);
+config.releaseCondition = comboAddress;
```
**Example:** Either receiver or arbiter can release
@@ -240,11 +238,11 @@ config.releaseCondition = address(combo);
Inverts a condition (`!A`).
**Usage:**
-```solidity
+```typescript
// Anyone EXCEPT payer can call
-NotCondition combo = new NotCondition(PAYER_CONDITION);
+const comboAddress = await notConditionFactory.write.deploy([PAYER_CONDITION]);
-config.releaseCondition = address(combo);
+config.releaseCondition = comboAddress;
```
**Example:** Prevent payer from releasing their own payment
@@ -253,19 +251,17 @@ config.releaseCondition = address(combo);
Combine combinators for complex logic:
-```solidity
+```typescript
// (Receiver OR Arbiter) AND EscrowPassed
-OrCondition receiverOrArbiter = new OrCondition([
- RECEIVER_CONDITION,
- ARBITER_CONDITION
+const receiverOrArbiter = await orConditionFactory.write.deploy([
+ [RECEIVER_CONDITION, ARBITER_CONDITION]
]);
-AndCondition releaseCondition = new AndCondition([
- address(receiverOrArbiter),
- ESCROW_PERIOD_CONDITION
+const releaseCondition = await andConditionFactory.write.deploy([
+ [receiverOrArbiter, ESCROW_PERIOD_ADDRESS]
]);
-config.releaseCondition = address(releaseCondition);
+config.releaseCondition = releaseCondition;
```
**Logic Tree:**
@@ -448,13 +444,12 @@ function getPaymentIndex(
Combines multiple recorders into one, calling each in sequence.
**Usage:**
-```solidity
-RecorderCombinator combo = new RecorderCombinator([
- escrowPeriod, // Records authorization time
- paymentIndexRecorder // Records payment index
+```typescript
+const comboAddress = await recorderCombinatorFactory.write.deploy([
+ [escrowPeriodAddress, paymentIndexRecorderAddress] // Records auth time + payment index
]);
-config.authorizeRecorder = address(combo);
+config.authorizeRecorder = comboAddress;
```
### Custom Recorders
@@ -540,16 +535,24 @@ config = {
### Pattern 4: Multi-Party Release (2-of-3)
-```solidity
+```typescript
// Requires any 2 of 3 parties to agree: Payer, Receiver, or Arbiter
// This creates three OR branches, each requiring a different pair to call simultaneously
-const twoOfThree = new OrCondition([
- new AndCondition([PAYER_CONDITION, RECEIVER_CONDITION]), // Payer AND Receiver
- new AndCondition([PAYER_CONDITION, ARBITER_CONDITION]), // Payer AND Arbiter
- new AndCondition([RECEIVER_CONDITION, ARBITER_CONDITION]) // Receiver AND Arbiter
+const payerAndReceiver = await andConditionFactory.write.deploy([
+ [PAYER_CONDITION, RECEIVER_CONDITION]
+]);
+const payerAndArbiter = await andConditionFactory.write.deploy([
+ [PAYER_CONDITION, ARBITER_CONDITION]
+]);
+const receiverAndArbiter = await andConditionFactory.write.deploy([
+ [RECEIVER_CONDITION, ARBITER_CONDITION]
+]);
+
+const twoOfThree = await orConditionFactory.write.deploy([
+ [payerAndReceiver, payerAndArbiter, receiverAndArbiter]
]);
-config.releaseCondition = address(twoOfThree);
+config.releaseCondition = twoOfThree;
```
**Use case:** Multi-sig style releases requiring coordination
@@ -594,14 +597,14 @@ Prefer stateless conditions when possible:
```solidity
// ✅ Stateless: No storage reads
-function check(PaymentInfo calldata payment, address caller)
+function check(PaymentInfo calldata payment, uint256, address caller)
external pure returns (bool)
{
return caller == payment.receiver; // Pure computation
}
// ❌ Stateful: Storage reads
-function check(PaymentInfo calldata payment, address caller)
+function check(PaymentInfo calldata payment, uint256, address caller)
external view returns (bool)
{
return allowList[caller]; // SLOAD costs gas
@@ -642,6 +645,7 @@ contract TimeOfDayCondition is ICondition {
function check(
PaymentInfo calldata payment,
+ uint256,
address caller
) external view returns (bool) {
uint256 hour = (block.timestamp / 3600) % 24;
diff --git a/contracts/core-contracts.mdx b/contracts/core-contracts.mdx
index 3fc5a77..d0216a9 100644
--- a/contracts/core-contracts.mdx
+++ b/contracts/core-contracts.mdx
@@ -299,7 +299,7 @@ Protocol fee changes require 7-day timelock. Operator fees are immutable (set at
- **ReentrancyGuardTransient** - EIP-1153 transient storage for gas-efficient reentrancy protection
- **Ownership** - Solady's Ownable with 2-step transfer
- **Timelock** - 7-day delay on protocol fee changes (operator fees are immutable)
-- **Immutable Core** - Escrow and arbiter cannot be changed
+- **Immutable Core** - Escrow, conditions, and fee configuration cannot be changed
---
@@ -481,8 +481,7 @@ stateDiagram-v2
NonExistent --> InEscrow: authorize()
InEscrow --> Released: release()
InEscrow --> Settled: void() / refundInEscrow()
- Released --> Settled: (settled)
- InEscrow --> Settled: reclaim() / refundPostEscrow()
+ Released --> Settled: reclaim() / refundPostEscrow()
Settled --> [*]
note right of InEscrow
@@ -520,6 +519,10 @@ function authorize(
**Requires:** Payer has approved escrow contract for `amount` of `token`
+
+The base escrow contract uses individual parameters (paymentId, payer, receiver, etc.) while the PaymentOperator wraps them in a `PaymentInfo` struct. The operator translates between the two formats internally.
+
+
#### release()
Releases tokens to receiver. Called by operator.
diff --git a/contracts/examples.mdx b/contracts/examples.mdx
index b173c71..42b29b3 100644
--- a/contracts/examples.mdx
+++ b/contracts/examples.mdx
@@ -4,6 +4,10 @@ description: "Complete configuration examples for common x402r use cases"
icon: "code"
---
+
+The configuration examples below use simplified pseudo-code (e.g., `new StaticAddressCondition(...)`, `new AndCondition(...)`) to illustrate the logical composition of conditions. In practice, deploy conditions via their respective [factory contracts](/contracts/factories) using viem. See the [SDK quickstart](/sdk/client/quickstart) for executable code.
+
+
## Example 1: Standard E-Commerce with 7-Day Escrow
**Use Case:** Online marketplace with buyer protection. 7-day escrow period, payer can freeze for 3 days, receiver or arbiter can release after escrow.
@@ -231,7 +235,7 @@ sequenceDiagram
Note over Buyer,Escrow: Day 10 - Product Arrives
Note over Buyer: Inspects and finds defect
- Buyer->>Operator: freeze(paymentId)
+ Seller->>Operator: freeze(paymentId)
Note over Operator: Payment frozen for 5 days
Note over Buyer,Escrow: Day 14 - Escrow Ends
@@ -491,19 +495,20 @@ sequenceDiagram
Operator->>Escrow: capture partial
Escrow->>Provider: 100 USDC
- Note over User,Escrow: Months 2-5 - Continues
+ Note over User,Escrow: Months 2-11 - Continues
Provider->>Operator: charge(100 USDC)
Operator->>Escrow: capture partial
Escrow->>Provider: 100 USDC
- Note over User,Escrow: Month 6 - Dispute
- User->>Operator: Disputes charge
- Operator->>Escrow: Partial refund
- Escrow->>User: Refund
+ Note over User,Escrow: Month 12 - Subscription Ends
+ Provider->>Operator: release(paymentId)
+ Operator->>Escrow: release remaining
+ Escrow->>Provider: Remaining funds
- Note over User,Escrow: Month 12 - Expiry
- User->>Escrow: reclaim(paymentId)
- Escrow->>User: Remaining funds
+ Note over User,Escrow: Alt: If cancelled early
+ Note over User: Payer reclaims after authorizationExpiry
+ User->>Escrow: void(paymentId)
+ Escrow->>User: Unused funds
```
---
@@ -583,7 +588,8 @@ sequenceDiagram
// Deploy condition for platform address
const platformCondition = await new StaticAddressCondition(PLATFORM_ADDRESS);
-// Time-proportional charge condition (custom)
+// Time-proportional charge condition (custom — not provided out of the box,
+// this is a hypothetical custom condition you would implement yourself)
const timeProportionalCondition = await new TimeProportionalCondition();
const config = {
@@ -746,38 +752,51 @@ Before deploying, verify:
## Testing Your Configuration
```typescript
+import { createTestClient, http, parseUnits, keccak256, toHex } from 'viem';
+import { baseSepolia } from 'viem/chains';
+import { PaymentOperatorABI } from '@x402r/core/abis';
+
// Deploy on Base Sepolia first
-const testOperator = await testnetFactory.deploy(
- testArbiter,
- config,
- 5,
- 25
-);
+const operatorAddress = await factory.write.deployOperator([config]);
-// Test payment flow
-const paymentId = ethers.utils.keccak256(
- ethers.utils.toUtf8Bytes("test-payment-1")
-);
+const testClient = createTestClient({
+ chain: baseSepolia,
+ transport: http(),
+ mode: 'anvil',
+});
// 1. Authorize
-await testOperator.connect(payer).authorize(
- paymentId,
- receiver.address,
- ethers.utils.parseUnits("100", 6), // 100 USDC
- USDC_ADDRESS
-);
+await walletClient.writeContract({
+ address: operatorAddress,
+ abi: PaymentOperatorABI,
+ functionName: 'authorize',
+ args: [paymentInfo, parseUnits('100', 6), tokenCollectorAddress, collectorData],
+});
// 2. Try to release immediately (should fail if escrow configured)
-await expect(
- testOperator.connect(receiver).release(paymentId)
-).to.be.revertedWith("EscrowPeriodNotPassed");
+// Expect revert with ConditionNotMet
+try {
+ await walletClient.writeContract({
+ address: operatorAddress,
+ abi: PaymentOperatorABI,
+ functionName: 'release',
+ args: [paymentInfo, parseUnits('100', 6)],
+ });
+} catch (e) {
+ console.log('Expected revert: escrow period not passed');
+}
-// 3. Fast forward time
-await ethers.provider.send("evm_increaseTime", [7 * 24 * 60 * 60]);
-await ethers.provider.send("evm_mine", []);
+// 3. Fast forward time (requires Anvil/Hardhat test node)
+await testClient.increaseTime({ seconds: 7 * 24 * 60 * 60 });
+await testClient.mine({ blocks: 1 });
// 4. Release after escrow
-await testOperator.connect(receiver).release(paymentId);
+await walletClient.writeContract({
+ address: operatorAddress,
+ abi: PaymentOperatorABI,
+ functionName: 'release',
+ args: [paymentInfo, parseUnits('100', 6)],
+});
// Verify funds transferred correctly
```
diff --git a/contracts/factories.mdx b/contracts/factories.mdx
index 147e153..c31fc3e 100644
--- a/contracts/factories.mdx
+++ b/contracts/factories.mdx
@@ -46,7 +46,9 @@ Deploys PaymentOperator instances with deterministic addresses.
### Contract Address
-**Base Sepolia:** TBD (deploy generic version)
+**Base Sepolia:** `0xe01CEd771A30A23a7A4C9c1db604C74D4Dc4ebe8`
+
+**Base Mainnet:** `0xA50F51254E8B08899EdB76Bd24b4DC6A61ba7dE7`
### Configuration Structure
@@ -119,7 +121,7 @@ assert(deployedAddress === predictedAddress);
import { createWalletClient, http, getContract, zeroAddress } from 'viem';
import { base } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';
-import { PaymentOperatorFactory, StaticAddressCondition } from "@x402r/contracts";
+import { PaymentOperatorABI } from '@x402r/core/abis';
const FACTORY_ADDRESS = '0x...'; // Replace with actual factory address
@@ -132,28 +134,33 @@ const walletClient = createWalletClient({
const factory = getContract({
address: FACTORY_ADDRESS,
- abi: PaymentOperatorFactory.abi,
+ abi: PaymentOperatorABI,
client: walletClient
});
-// Deploy condition for arbiter
-const arbiterCondition = await new StaticAddressCondition(arbiterAddress);
+// Deploy condition for arbiter via factory
+const arbiterConditionHash = await staticAddressConditionFactory.write.deploy([arbiterAddress]);
+const arbiterConditionAddress = /* get from receipt */;
+
+// Deploy release condition: arbiter AND escrow period passed
+const releaseConditionHash = await andConditionFactory.write.deploy([
+ [arbiterConditionAddress, escrowPeriodAddress]
+]);
+const releaseConditionAddress = /* get from receipt */;
// Define configuration
const config = {
feeRecipient: arbiterAddress, // Arbiter earns fees
+ feeCalculator: feeCalculatorAddress,
authorizeCondition: ALWAYS_TRUE_CONDITION,
- authorizeRecorder: escrowRecorder,
+ authorizeRecorder: escrowPeriodAddress,
chargeCondition: zeroAddress, // Default allow
chargeRecorder: zeroAddress, // No recording
- releaseCondition: new AndCondition([
- arbiterCondition.address,
- escrowPeriodCondition
- ]),
- releaseRecorder: escrowRecorder,
- refundInEscrowCondition: arbiterCondition.address,
+ releaseCondition: releaseConditionAddress,
+ releaseRecorder: zeroAddress,
+ refundInEscrowCondition: arbiterConditionAddress,
refundInEscrowRecorder: zeroAddress,
- refundPostEscrowCondition: arbiterCondition.address,
+ refundPostEscrowCondition: arbiterConditionAddress,
refundPostEscrowRecorder: zeroAddress
};
@@ -201,7 +208,9 @@ Deploys `EscrowPeriod` contracts - combined recorder and condition for time-base
### Contract Address
-**Base Sepolia:** TBD
+**Base Sepolia:** `0x206D4DbB6E7b876e4B5EFAAD2a04e7d7813FB6ba`
+
+**Base Mainnet:** `0x2714EA3e839Ac50F52B2e2a5788F614cACeE5316`
### Deployment Method
@@ -346,11 +355,11 @@ const config = {
Use these pre-deployed condition contracts:
-| Condition | Address (Base Sepolia) | Description |
-|-----------|------------------------|-------------|
-| PayerCondition | TBD | Only payer can call |
-| ReceiverCondition | TBD | Only receiver can call |
-| AlwaysTrueCondition | TBD | Anyone can call |
+| Condition | Address (Base Sepolia) | Address (Base Mainnet) | Description |
+|-----------|------------------------|------------------------|-------------|
+| PayerCondition | `0xBAF68176FF94CAdD403EF7FbB776bbca548AC09D` | `0xb33D6502EdBbC47201cd1E53C49d703EC0a660b8` | Only payer can call |
+| ReceiverCondition | `0x12EDefd4549c53497689067f165c0f101796Eb6D` | `0xed02d3E5167BCc9582D851885A89b050AB816a56` | Only receiver can call |
+| AlwaysTrueCondition | `0x785cC83DEa3d46D5509f3bf7496EAb26D42EE610` | `0xc9BbA6A2CF9838e7Dd8c19BC8B3BAC620B9D8178` | Anyone can call |
### Example Deployments
@@ -463,7 +472,7 @@ Approximate gas costs for factory deployments (Base Sepolia):
| Operation | Gas Cost | USD (at 0.1 gwei, $3000 ETH) |
|-----------|----------|------------------------------|
-| Deploy ArbitrationOperator | ~2.5M gas | ~$0.75 |
+| Deploy PaymentOperator | ~2.5M gas | ~$0.75 |
| Deploy EscrowPeriod (condition + recorder) | ~1.8M gas | ~$0.54 |
| Deploy Freeze | ~1.0M gas | ~$0.30 |
| Predict address (view call) | 0 gas | $0.00 |
@@ -558,9 +567,10 @@ This enables:
Always verify predicted address before deployment:
```typescript
-const predicted = await factory.predictOperatorAddress(arbiter, config, 5, 25);
-const deployed = await factory.deploy(arbiter, config, 5, 25);
-assert(predicted === deployed);
+const predicted = await factory.read.computeAddress([config]);
+const hash = await factory.write.deployOperator([config]);
+const receipt = await walletClient.waitForTransactionReceipt({ hash });
+// deployed address matches predicted
```
### 2. Reuse Condition Singletons
@@ -588,11 +598,11 @@ Deploy on testnet with same configuration before mainnet:
```typescript
// Test on Base Sepolia first
-const testOperator = await testnetFactory.deploy(arbiter, config, 5, 25);
+const testHash = await testnetFactory.write.deployOperator([config]);
// ... test thoroughly ...
// Deploy on mainnet with identical config (same address!)
-const mainnetOperator = await mainnetFactory.deploy(arbiter, config, 5, 25);
+const mainnetHash = await mainnetFactory.write.deployOperator([config]);
```
### 4. Document Your Config
diff --git a/contracts/overview.mdx b/contracts/overview.mdx
index e2087bb..f8a5262 100644
--- a/contracts/overview.mdx
+++ b/contracts/overview.mdx
@@ -153,7 +153,7 @@ x402r extends commerce-payments with flexible payment capabilities:
Most contracts are immutable to prevent rug pulls and ensure trustlessness:
- **PaymentOperator** - Cannot pause or upgrade
-- **EscrowPeriodCondition** - Cannot modify escrow period
+- **EscrowPeriod** - Cannot modify escrow period
- **Freeze** - Cannot change freeze rules after deployment
Protocol fee configuration is mutable via `ProtocolFeeConfig` (with 7-day timelock). Operator fees are immutable.
@@ -195,7 +195,7 @@ For factory deployment patterns, see [Factories](/contracts/factories).
View system architecture diagrams and payment flows.
- Learn about ArbitrationOperator, RefundRequest, and other core contracts.
+ Learn about PaymentOperator, RefundRequest, and other core contracts.
Understand factory patterns and CREATE2 deployments.
diff --git a/index.mdx b/index.mdx
index b193d0e..fd83c34 100644
--- a/index.mdx
+++ b/index.mdx
@@ -1,6 +1,7 @@
---
-title: "Introduction"
+title: "What is x402r?"
description: "x402r adds escrow deposits, refund windows, and dispute resolution to HTTP-native payments"
+icon: "house"
---
**x402r** is a refundable payments protocol extension for [x402](https://www.x402.org/). It enables secure, reversible transactions with built-in buyer protection through smart contract escrow on Base.
diff --git a/x402-integration/comparison.mdx b/x402-integration/comparison.mdx
index c85376a..ab89142 100644
--- a/x402-integration/comparison.mdx
+++ b/x402-integration/comparison.mdx
@@ -362,7 +362,7 @@ flowchart TD
Understand operator implementations.
-
+
Build your first payment flow.
diff --git a/x402-integration/escrow-scheme.mdx b/x402-integration/escrow-scheme.mdx
index 0356650..fc64b0a 100644
--- a/x402-integration/escrow-scheme.mdx
+++ b/x402-integration/escrow-scheme.mdx
@@ -336,7 +336,7 @@ See [Comparison](/x402-integration/comparison) for detailed trade-offs.
Learn about escrow and operator contracts.
-
+
Build your first escrow-based payment flow.
diff --git a/x402-integration/overview.mdx b/x402-integration/overview.mdx
index bbd12c8..cdea7a7 100644
--- a/x402-integration/overview.mdx
+++ b/x402-integration/overview.mdx
@@ -218,7 +218,7 @@ Smart contract that controls capture/void logic. Different operators enable diff
Understand the escrow and operator contracts.
-
+
Get started building with x402r.