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
6 changes: 5 additions & 1 deletion sdk/delivery-arbiter.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ app.post('/verify', async (req, res) => {
await arbiter.payment.release(paymentInfo, amounts.capturableAmount)
res.json({ verdict: 'PASS' })
} else {
// Do nothing. Funds auto-refund after escrow expires.
// Arbiter can refund immediately without waiting for escrow expiry.
const amounts = await arbiter.payment.getAmounts(paymentInfo)
await arbiter.refund.refundInEscrow(paymentInfo, amounts.capturableAmount)
res.json({ verdict: 'FAIL' })
}
})
Expand Down Expand Up @@ -108,6 +110,8 @@ async function evaluate(responseBody: string): Promise<boolean> {

### 5. What Happens on Failure

With delivery protection v2, the arbiter can call `refundInEscrow()` immediately on a FAIL verdict. You do not need to wait for escrow expiry. The receiver (merchant) can also trigger a voluntary refund at any time.

<Warning>
If your service goes down, no payments get evaluated and funds stay in escrow until timeout. The escrow period protects payers, but add uptime monitoring and alerting.
</Warning>
Expand Down
7 changes: 4 additions & 3 deletions sdk/delivery-protection.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ description: "Automated quality verification for every transaction."
icon: "shield-check"
---

In the delivery protection model, the arbiter evaluates every transaction automatically. Only the arbiter can release funds. If the arbiter does not release, funds auto-refund to the payer after escrow expires.
In the delivery protection model, the arbiter evaluates every transaction automatically. The arbiter or a satisfied payer can release funds. If the arbiter issues a FAIL verdict, it can trigger an immediate refund without waiting for escrow expiry. If nobody acts, funds auto-refund to the payer after escrow expires.

This is different from the [marketplace model](/sdk/overview) where the merchant releases funds and the arbiter only gets involved when a payer files a dispute.

| | Marketplace | Delivery Protection |
|---|---|---|
| Who releases funds | Merchant (after escrow) | Arbiter only |
| Who releases funds | Merchant (after escrow) | Arbiter or payer |
| Refund during escrow | Receiver or arbiter | Escrow expiry, receiver, or arbiter |
| Dispute process | Payer files refund request | No disputes needed |
| Arbiter involvement | Only on disputes | Every transaction |
| Contracts needed | Operator + EscrowPeriod + RefundRequest + Evidence + Freeze | Operator + EscrowPeriod + StaticAddressCondition |
| Contracts deployed | ~8 (Operator, EscrowPeriod, RefundRequest, Evidence, Freeze, etc.) | 6 (Operator, EscrowPeriod, SAC, 2x OrCondition, RecorderCombinator) |
| Deploy preset | `deployMarketplaceOperator()` | `deployDeliveryProtectionOperator()` |

Use this when every response needs automated quality checks: AI content verification, garbage detection, schema validation.
Expand Down
71 changes: 61 additions & 10 deletions sdk/deploy-operator.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ Deployment requires gas fees. Ensure your wallet has ETH on the target network.

## Delivery Protection Operator

For automated quality verification (AI garbage detection, schema validation), use the simpler delivery protection preset. No RefundRequest, Evidence, or Freeze contracts. The arbiter is the only address that can release funds.
For automated quality verification (AI garbage detection, schema validation), use the delivery protection preset. No RefundRequest, Evidence, or Freeze contracts. The arbiter or payer can release funds, and the arbiter can issue immediate refunds without waiting for escrow expiry.

```typescript
import { deployDeliveryProtectionOperator } from '@x402r/core'
Expand All @@ -136,23 +136,74 @@ const deployment = await deployDeliveryProtectionOperator(
console.log('Operator:', deployment.operatorAddress)
console.log('EscrowPeriod:', deployment.escrowPeriodAddress)
console.log('ArbiterCondition:', deployment.arbiterConditionAddress)
console.log('ReleaseCondition:', deployment.releaseConditionAddress)
console.log('AuthorizeRecorder:', deployment.authorizeRecorderAddress)
```

| Option | Type | Description |
|--------|------|-------------|
| `chainId` | `number` | Target chain |
| `arbiter` | `Address` | Only address that can call `release()` |
| `arbiter` | `Address` | Arbiter address for release and refund decisions |
| `feeRecipient` | `Address` | Receives protocol fees |
| `escrowPeriodSeconds` | `bigint` | Verification window before auto-refund |
| `authorizedCodehash` | `Hex` | Override the default `recorderCombinatorCodehash`. Optional |
| `paymentIndexRecorderAddress` | `Address` | Override the default PaymentIndexRecorder. Pass `zeroAddress` to skip on-chain payment indexing. Optional |
| `allowArbiterRefund` | `boolean` | Allow arbiter to refund immediately during escrow. Default: `true` |

<Accordion title="Delivery Protection Slot Configuration">
| Slot | Contract | Purpose |
|------|----------|---------|
| `RELEASE_CONDITION` | StaticAddressCondition(arbiter) | Only arbiter can release |
| `AUTHORIZE_RECORDER` | EscrowPeriod | Records authorization time |
| `REFUND_IN_ESCROW_CONDITION` | EscrowPeriod | Anyone can refund after escrow expires |
| `REFUND_POST_ESCROW_CONDITION` | Receiver | Receiver can refund post-escrow |
</Accordion>
<AccordionGroup>
<Accordion title="Deployment Result Type">
```typescript
interface DeliveryProtectionOperatorDeployment {
operatorAddress: Address
escrowPeriodAddress: Address
arbiterConditionAddress: Address
releaseConditionAddress: Address // OrCondition([arbiter, payer])
refundInEscrowConditionAddress: Address // OrCondition([escrowPeriod, receiver, arbiter])
authorizeRecorderAddress: Address // RecorderCombinator([escrowPeriod, paymentIndexRecorder])
paymentIndexRecorderAddress: Address
operatorConfig: OperatorConfig
deployments: DeployResult[]
summary: {
newCount: number
existingCount: number
txHashes: `0x${string}`[]
}
}
```

Deploys 6 contracts by default: EscrowPeriod, StaticAddressCondition(arbiter), OrCondition(release), OrCondition(refund), RecorderCombinator, and the Operator. If you pass `paymentIndexRecorderAddress: zeroAddress`, the RecorderCombinator is skipped (5 contracts).

Redeploying with the same parameters is idempotent (CREATE3). It detects existing contracts and skips them.
</Accordion>

<Accordion title="Preview Addresses (No Deploy)">
Compute addresses without deploying:

```typescript
import { previewDeliveryProtectionOperator } from '@x402r/core'

const preview = await previewDeliveryProtectionOperator(publicClient, {
chainId: 84532,
arbiter: '0xArbiterServiceAddress',
feeRecipient: account.address,
escrowPeriodSeconds: 300n,
})

console.log('Operator will be at:', preview.operatorAddress)
console.log('EscrowPeriod will be at:', preview.escrowPeriodAddress)
console.log('AuthorizeRecorder will be at:', preview.authorizeRecorderAddress)
```
</Accordion>

<Accordion title="Delivery Protection Slot Configuration">
| Slot | Contract | Purpose |
|------|----------|---------|
| `RELEASE_CONDITION` | OrCondition([SAC(arbiter), PayerCondition]) | Arbiter or satisfied payer can release |
| `AUTHORIZE_RECORDER` | RecorderCombinator([EscrowPeriod, PaymentIndexRecorder]) | Records authorization time and indexes payments on-chain |
| `REFUND_IN_ESCROW_CONDITION` | OrCondition([EscrowPeriod, ReceiverCondition, SAC(arbiter)]) | Escrow expiry, receiver voluntary refund, or arbiter immediate refund |
| `REFUND_POST_ESCROW_CONDITION` | ReceiverCondition | Only receiver after escrow |
</Accordion>
</AccordionGroup>

## Next Steps

Expand Down
2 changes: 1 addition & 1 deletion sdk/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Three roles interact with the protocol:

**Marketplace** (`deployMarketplaceOperator`): The merchant releases funds after escrow. If the payer contests, they file a refund request and an arbiter resolves it. Use this for general commerce where most transactions are uncontested.

**Delivery Protection** (`deployDeliveryProtectionOperator`): The arbiter evaluates every transaction automatically and is the only address that can release funds. If the arbiter does not release, funds auto-refund after escrow. Use this for AI content verification, schema validation, or automated quality checks.
**Delivery Protection** (`deployDeliveryProtectionOperator`): The arbiter evaluates every transaction automatically. The arbiter or a satisfied payer can release funds. On a FAIL verdict, the arbiter can trigger an immediate refund. If nobody acts, funds auto-refund after escrow. Use this for AI content verification, schema validation, or automated quality checks.

### Packages

Expand Down
Loading