You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+10Lines changed: 10 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,5 +1,15 @@
1
1
# @opensea/tool-sdk
2
2
3
+
## 0.16.0
4
+
5
+
### Minor Changes
6
+
7
+
- Add `paidPredicateGate`, a combined gate that resolves identity verification (predicate) and x402 payment in a single 402 round trip.
8
+
9
+
Tools using `paidPredicateGate` need only 2 requests (a 402 advertising the real payment amount, then a 200) instead of 3 (predicate 402, then x402 402, then 200). The caller's `X-Payment` signature for the payment amount simultaneously proves identity, via the recovered `from` address, and authorizes the transfer. The onchain predicate is checked before the facilitator settles payment: if access is denied, the gate returns a 403 and no funds move.
10
+
11
+
New exports: `paidPredicateGate` and `PaidPredicateGateConfig`.
Copy file name to clipboardExpand all lines: README.md
+21Lines changed: 21 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -842,6 +842,27 @@ const data = await res.json()
842
842
843
843
Gate your tool using the onchain access predicate system. When `operatorAddress` is configured, `predicateGate` uses a unified 402 challenge flow: it returns `PaymentRequirements` with `maxAmountRequired: "0"`, the caller signs a zero-value `X-Payment`, and the middleware recovers the caller's address via `ecrecover`. Access is then delegated to `IToolRegistry.tryHasAccess` — it works with ERC721OwnerPredicate, ERC1155OwnerPredicate, SubscriptionPredicate, ERC20BalancePredicate, CompositePredicate, or any future predicate automatically.
For tools that require **both** predicate access and x402 payment, use `paidPredicateGate` to resolve identity and payment in a single 402 round trip (2 requests instead of 3):
The gate advertises the real payment amount in the 402 challenge. The caller's `X-Payment` signature proves identity (`from`) AND authorizes payment in one step. The predicate is verified before payment settles — if access is denied, no funds move.
865
+
845
866
See [docs/predicate-gating-guide.md](docs/predicate-gating-guide.md) for the full setup walkthrough.
// ctx.callerAddress — verified wallet (set by predicate gate)
359
+
// ctx.callerAddress — verified wallet (recovered from X-Payment)
362
360
// ctx.gates.predicate.granted === true
363
361
// ctx.gates.x402.paid === true
364
362
return { result: "access granted and payment received" }
365
363
},
366
364
})
367
365
```
368
366
369
-
### Middleware ordering
367
+
### How `paidPredicateGate` works
370
368
371
-
Gates run in array order (see `src/lib/handler/index.ts`). Put `predicateGate`**first**:
369
+
The combined gate issues a single 402 with the **real payment amount** (not zero). The caller's `X-Payment` signature simultaneously:
370
+
- Proves identity (the recovered `from` address)
371
+
- Authorizes the USDC transfer (via the facilitator)
372
372
373
-
1.**Predicate gate** runs first — returns 402 with zero-value `PaymentRequirements`. The client signs a zero-value `X-Payment` and retries. Once verified, the predicate gate establishes `ctx.callerAddress`. Returns `403` if the predicate denies access.
374
-
2.**x402 gate** runs second — returns 402 with real payment `PaymentRequirements`. The client signs a real `X-Payment` and retries. Returns `402` if no payment is provided.
373
+
The gate runs these checks in order:
374
+
1. Verify the EIP-712 signature and recover the caller's address
375
+
2. Check the onchain predicate (`tryHasAccess`) — returns 403 if denied
376
+
3. Verify the payment with the facilitator — returns 402 if invalid
377
+
4. After the handler succeeds: settle the payment via the facilitator
375
378
376
-
The client handles multiple 402 challenges in a retry loop — `paidAuthenticatedFetch` does this automatically.
379
+
This ordering ensures **no funds move** if the predicate denies access.
377
380
378
381
### Client requirements
379
382
380
-
`paidAuthenticatedFetch` handles the multi-402 flow automatically: it sends a bare request, then on each 402 reads `PaymentRequirements`, signs an `X-Payment`, and retries. No separate `Authorization` header is needed.
383
+
`paidAuthenticatedFetch` handles the 402 challenge automatically. Since `paidPredicateGate` issues only one 402, the client signs once and the call completes:
maxAmount: "100000", // safety cap for the x402 payment
393
+
maxAmount: "100000", // safety cap: 0.10 USDC
394
+
allowedRecipients: ["0xYourPayoutAddress"],
391
395
})
392
396
```
397
+
398
+
### Legacy: separate gates (3 requests)
399
+
400
+
For backward compatibility, you can still chain `predicateGate` + x402 gate as separate middleware. The client handles both 402s in a retry loop. However, `paidPredicateGate` is preferred for new tools.
Tools can require both identity verification and x402 payment. The server runs gates sequentially: predicate first (identity via zero-value `X-Payment`), then x402 (real payment via `X-Payment`). The client handles multiple 402 challenges in a retry loop.
172
+
Tools that require both identity verification (predicate) and x402 payment should use `paidPredicateGate` — a single gate that resolves both in **one 402 round trip** instead of two.
The caller's real-value `X-Payment` signature simultaneously proves identity (`from` = caller) AND authorizes payment. The predicate is checked BEFORE the facilitator settles — if the caller doesn't meet the access requirement, a 403 is returned and no funds move.
`paidAuthenticatedFetch` handles multiple 402 challenges automatically: it sends a bare request, then on each 402 it reads the `PaymentRequirements`, signs an `X-Payment`, and retries. For a combined predicate + x402 tool, the first 402 is the predicate's zero-value challenge and the second is the x402 real payment.
248
+
`paidAuthenticatedFetch` handles the 402 challenge automatically. For a `paidPredicateGate` tool, only one 402 is issued (with the real amount), so the client signs once and the call completes.
For backward compatibility, tools can still chain `predicateGate` + `paywall.gate` as separate gates. The client handles both 402 challenges in a retry loop. However, `paidPredicateGate` is preferred for new tools since it eliminates one round trip.
0 commit comments