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.