Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
82ba36f
feat: settlement pending auto-recovery (#3214)
CarsonRoscoe Aug 25, 2026
bde6fe5
Update docs/core-concepts/facilitator.md (#3259)
mintlify[bot] Aug 25, 2026
f8a3682
Auth-capture spec update: v1.1 (#3197)
phdargen Aug 25, 2026
bb46ffc
feat(ts/go): upfront paymentflow for exact mechanism (#3240)
phdargen Aug 25, 2026
cdfa491
Document upfront payment flow for exact scheme (#3267)
mintlify[bot] Aug 25, 2026
01b0a68
feat(Ts/Go): avoid fee-payer signing svm exact /verify + Go smart wal…
phdargen Aug 26, 2026
b1a88ef
Update docs/schemes/exact.mdx (#3272)
mintlify[bot] Aug 26, 2026
ab1b418
feat(evm): add Upto-only deployment entrypoint (#3199)
huaweigu Aug 26, 2026
b32b564
fix: upto proxy canonical address (#3276)
CarsonRoscoe Aug 26, 2026
acaa904
fix(ts/go): refactor svm upto rpc config (#3274)
phdargen Aug 26, 2026
8468e3a
Fix SVM upto facilitator RPC configuration examples (#3277)
mintlify[bot] Aug 26, 2026
44f6b17
feat(ts/go): auth-capture client v1.1 (#3283)
phdargen Aug 27, 2026
f257584
chore: version typescript packages  (#3287)
phdargen Aug 27, 2026
d22ae96
chore: version python package  (#3288)
phdargen Aug 27, 2026
8ac521c
chore(go): release (#3289)
phdargen Aug 27, 2026
b703a0e
Update docs/sdk-features.md (#3286)
mintlify[bot] Aug 27, 2026
e398a9e
disable spend controls in stellar integration test (#3290)
marcelosalloum Aug 28, 2026
dd25875
fix(e2e): scope EVM/SVM client signer derivation to selected families…
Eras256 Aug 31, 2026
cd43052
fix(go): use maxTimeoutSeconds for EIP-3009 validBefore (#3282)
Tehsapper Aug 31, 2026
6838428
feat(python): make facilitator gas limit configurable (#3233)
nniiovoo Aug 31, 2026
240492e
docs(specs): correct v2 §8 discovery fields to match the wire format …
onlyarche Aug 31, 2026
675bb5d
fix(go): prevent settlement override percent overflow (#2962)
wnjoon Aug 31, 2026
18ecba0
e2e: add script to check wallet balances (#3297)
phdargen Aug 31, 2026
87a19a7
Fix e2e warnings (#3308)
phdargen Aug 31, 2026
d2bc9ba
fix: improve facilitator evm latency (#3312)
CarsonRoscoe Aug 31, 2026
e187dda
fix(py): add server-only extension_responses sidechannel (#3306)
phdargen Aug 31, 2026
a3c6004
validate builder-code app attribution on (#3313)
phdargen Aug 31, 2026
a140d2b
fix #3019 (#3309)
phdargen Aug 31, 2026
138d415
Document builder-code `a` field validation and v1 behavior (#3315)
mintlify[bot] Aug 31, 2026
68e529b
Update withX402 docs for keyed route patterns (#3316)
mintlify[bot] Aug 31, 2026
1bc2ae8
fix(core): server-only extensionResponses sidechannel (#3278)
Bartok9 Aug 31, 2026
94f9951
fix(e2e): svm smart wallet config (#3319)
phdargen Aug 31, 2026
bbcb974
fix(core): throw when no scheme server is registered (#3051)
VedantAnand17 Sep 1, 2026
71dd629
docs(ts/mcp): update README for current API and payment shapes (#3089)
Xeift Sep 1, 2026
0344bdf
fix(e2e): HTTP-only swig-setup RPC with devnet fallback (#3327)
phdargen Sep 1, 2026
fed6a04
fix: replace crypto dependency with @noble/hashes in @x402/svm (#3335)
CarsonRoscoe Sep 2, 2026
6c1a4b2
fix(go): validate builder-code app attribution (#3302)
wnjoon Sep 2, 2026
b0febf2
docs: list FTP Canton Facilitator (#3243)
nicky2pc Sep 2, 2026
eee9386
Merge upstream x402-foundation/main into Cardano fork
Kammerlo Sep 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
22 changes: 21 additions & 1 deletion .github/workflows/e2e_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ jobs:
{
echo "SERVER_EVM_ADDRESS=${{ secrets.SERVER_EVM_ADDRESS }}"
echo "SERVER_SVM_ADDRESS=${{ secrets.SERVER_SVM_ADDRESS }}"
echo "SERVER_SVM_RECEIVER_AUTHORIZER_PRIVATE_KEY=${{ secrets.SERVER_SVM_RECEIVER_AUTHORIZER_PRIVATE_KEY }}"
echo "SERVER_AVM_ADDRESS=${{ secrets.SERVER_AVM_ADDRESS }}"
echo "SERVER_APTOS_ADDRESS=${{ secrets.SERVER_APTOS_ADDRESS }}"
echo "SERVER_CCD_ADDRESS=${{ secrets.SERVER_CCD_ADDRESS }}"
Expand Down Expand Up @@ -94,7 +95,7 @@ jobs:
echo "FACILITATOR_CARDANO_MNEMONIC=${{ secrets.FACILITATOR_CARDANO_MNEMONIC }}"
echo "BLOCKFROST_PROJECT_ID=${{ secrets.BLOCKFROST_PROJECT_ID }}"
echo "SERVER_EVM_RECEIVER_AUTHORIZER_PRIVATE_KEY=${{ secrets.SERVER_EVM_RECEIVER_AUTHORIZER_PRIVATE_KEY }}"
echo "CLIENT_EVM_VOUCHER_SIGNER_PRIVATE_KEY=${{ secrets.CLIENT_EVM_VOUCHER_SIGNER_PRIVATE_KEY }}"
echo "CLIENT_EVM_BATCH_SETTLEMENT_VOUCHER_SIGNER_PRIVATE_KEY=${{ secrets.CLIENT_EVM_BATCH_SETTLEMENT_VOUCHER_SIGNER_PRIVATE_KEY }}"
echo "TVM_PROVIDER=${{ secrets.TVM_PROVIDER }}"
echo "TVM_TONAPI_API_KEY=${{ secrets.TVM_TONAPI_API_KEY }}"
echo "TVM_TONCENTER_API_KEY=${{ secrets.TVM_TONCENTER_API_KEY }}"
Expand Down Expand Up @@ -158,10 +159,29 @@ jobs:
pnpm install --frozen-lockfile
pnpm build

- name: Print wallet status
if: steps.families.outputs.configured == 'true'
run: pnpm wallet:status

- name: Setup E2E components
if: steps.families.outputs.configured == 'true'
run: ./setup.sh

# Create/fund the Swig smart wallet when SVM is in the run and persist
# SWIG_ACCOUNT_ADDRESS to e2e/.env for the test step (and later steps).
- name: Setup Swig smart wallet
if: steps.families.outputs.configured == 'true' && contains(steps.families.outputs.families, 'svm')
run: |
set -a
# shellcheck disable=SC1091
source .env
set +a
# Only forward a custom RPC; empty SVM_TESTNET_RPC_URL falls back to devnet in swig-setup.
if [[ -n "${SVM_TESTNET_RPC_URL:-}" ]]; then
export SVM_RPC_URL="$SVM_TESTNET_RPC_URL"
fi
pnpm swig:setup

- name: Run minimized e2e tests (testnet)
if: steps.families.outputs.configured == 'true'
run: |
Expand Down
26 changes: 20 additions & 6 deletions contracts/evm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Both contracts:
| Contract | Address |
|----------|---------|
| x402ExactPermit2Proxy | `0x402085c248EeA27D92E8b30b2C58ed07f9E20001` |
| x402UptoPermit2Proxy | `0x402015c795ecb48A360bDC6e35a2EaEb313a0002` |
| x402UptoPermit2Proxy | `0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002` |

**Batch settlement (CREATE2 vanity `0x4020…`)**

Expand All @@ -41,7 +41,7 @@ Both contracts:

| Chain | Exact | Upto |
|-------|-------|------|
| Base Mainnet | [Deployed](https://basescan.org/address/0x402085c248EeA27D92E8b30b2C58ed07f9E20001) | |
| Base Mainnet | [Deployed](https://basescan.org/address/0x402085c248EeA27D92E8b30b2C58ed07f9E20001) | [Deployed](https://basescan.org/address/0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002) |
| Base Sepolia | [Deployed](https://sepolia.basescan.org/address/0x402085c248EeA27D92E8b30b2C58ed07f9E20001) | [Legacy\*](https://sepolia.basescan.org/address/0x402039b3d6E6BEC5A02c2C9fd937ac17A6940002) |

**Batch settlement deployments**
Expand All @@ -61,7 +61,7 @@ Both contracts:
| Monad Mainnet | [Deployed](https://monadscan.com/address/0x4020074e9dF2ce1deE5A9C1b5c3f541D02a10003) | [Deployed](https://monadscan.com/address/0x4020806089470a89826cB9fB1f4059150b550004) | [Deployed](https://monadscan.com/address/0x4020425FAf3B746C082C2f942b4E5159887B0005) |

> \*Older testnet deployments may use prior vanity salts; the canonical **Upto** address for
> CREATE2 deployments from this tree is `0x402015c7…313a0002` (see `forge script script/ComputeAddress.s.sol`).
> CREATE2 deployments from this tree is `0x4020A4f3…C240002` (see `forge script script/ComputeAddress.s.sol`).

## Prerequisites

Expand Down Expand Up @@ -106,7 +106,7 @@ and `keccak256(initCode)`—not on who sends the transaction.
```
You should see:
- Exact → `0x402085c248EeA27D92E8b30b2C58ed07f9E20001`
- Upto → `0x402015c795ecb48A360bDC6e35a2EaEb313a0002`
- Upto → `0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002`

3. **Check prerequisites on the target chain**
- [Permit2](https://github.com/Uniswap/permit2) must be deployed at `0x000000000022D473030F116dDEE9F6B43aC78BA3`
Expand All @@ -115,15 +115,29 @@ and `keccak256(initCode)`—not on who sends the transaction.

4. **Deploy**
```bash
export PRIVATE_KEY="your_private_key"
cast wallet import x402-deployment-signer --interactive

forge script script/Deploy.s.sol \
--rpc-url <RPC_URL> \
--account x402-deployment-signer \
--sender <DEPLOYMENT_SIGNER_ADDRESS> \
--broadcast \
--verify
```

The script automatically:
To deploy only the Upto proxy, select its deployment entry point:

```bash
forge script script/Deploy.s.sol \
--sig "runUpto()" \
--rpc-url <RPC_URL> \
--account x402-deployment-signer \
--sender <DEPLOYMENT_SIGNER_ADDRESS> \
--broadcast \
--verify
```

The default `run()` entry point automatically:
- Loads the pre-built initCode for Exact and compiler-derived initCode for Upto
- Skips any contract already deployed at the expected address
- Verifies `PERMIT2()` returns the correct address after deployment
Expand Down
4 changes: 2 additions & 2 deletions contracts/evm/script/ComputeAddress.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ contract ComputeAddress is Script {
bytes32 constant DEFAULT_EXACT_SALT = 0x0000000000000000000000000000000000000000000000003000000007263b0e;

/// @notice Default salt for x402UptoPermit2Proxy
/// @dev Vanity mined for address 0x402015c795ecb48a360bdc6e35a2eaeb313a0002
bytes32 constant DEFAULT_UPTO_SALT = 0x0000000000000000000000000000000000000000000000000800000007e2e4de;
/// @dev Vanity mined for address 0x4020a4f3b7b90cca423b9fabcc0ce57c6c240002
bytes32 constant DEFAULT_UPTO_SALT = 0x000000000000000000000000000000000000000000000000b000000001db633d;

/// @notice Expected initCodeHash for x402ExactPermit2Proxy (pre-built, includes CBOR metadata)
bytes32 constant EXACT_INIT_CODE_HASH = 0xe774d1d5a07218946ab54efe010b300481478b86861bb17d69c98a57f68a604c;
Expand Down
19 changes: 17 additions & 2 deletions contracts/evm/script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ contract DeployX402Proxies is Script {
bytes32 constant EXACT_SALT = 0x0000000000000000000000000000000000000000000000003000000007263b0e;

/// @notice Salt for x402UptoPermit2Proxy deterministic deployment
/// @dev Vanity mined for address 0x402015c795ecb48a360bdc6e35a2eaeb313a0002
bytes32 constant UPTO_SALT = 0x0000000000000000000000000000000000000000000000000800000007e2e4de;
/// @dev Vanity mined for address 0x4020a4f3b7b90cca423b9fabcc0ce57c6c240002
bytes32 constant UPTO_SALT = 0x000000000000000000000000000000000000000000000000b000000001db633d;

/// @notice Expected initCodeHash for x402ExactPermit2Proxy (pre-built, includes CBOR metadata)
bytes32 constant EXACT_INIT_CODE_HASH = 0xe774d1d5a07218946ab54efe010b300481478b86861bb17d69c98a57f68a604c;
Expand Down Expand Up @@ -81,6 +81,21 @@ contract DeployX402Proxies is Script {
console2.log("");
}

/// @notice Deploys only x402UptoPermit2Proxy.
function runUpto() public {
address permit2 = vm.envOr("PERMIT2_ADDRESS", CANONICAL_PERMIT2);

if (block.chainid != 31_337 && block.chainid != 1337) {
require(permit2.code.length > 0, "Permit2 not found on this network");
console2.log("Permit2 verified");

require(CREATE2_DEPLOYER.code.length > 0, "CREATE2 deployer not found on this network");
console2.log("CREATE2 deployer verified");
}

_deployUpto(permit2);
}

function _deployExact(
address permit2
) internal {
Expand Down
6 changes: 2 additions & 4 deletions contracts/evm/src/x402BasePermit2Proxy.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {ReentrancyGuardTransient} from "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";

import {ISignatureTransfer} from "./interfaces/ISignatureTransfer.sol";
Expand All @@ -22,11 +22,9 @@ import {ISignatureTransfer} from "./interfaces/ISignatureTransfer.sol";
* every chain keeps the initCode identical, preserving a uniform CREATE2
* address for these proxies across all chains.
*
* Uses {ReentrancyGuardTransient} (EIP-1153); deploy only on chains with transient storage support.
*
* @author x402 Protocol
*/
abstract contract x402BasePermit2Proxy is ReentrancyGuardTransient {
abstract contract x402BasePermit2Proxy is ReentrancyGuard {
/// @notice The Permit2 contract address (set once at construction, immutable)
ISignatureTransfer public immutable PERMIT2;

Expand Down
89 changes: 89 additions & 0 deletions contracts/evm/test/Deploy.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Test} from "forge-std/Test.sol";
import {DeployX402Proxies} from "../script/Deploy.s.sol";
import {x402ExactPermit2Proxy} from "../src/x402ExactPermit2Proxy.sol";
import {x402UptoPermit2Proxy} from "../src/x402UptoPermit2Proxy.sol";

/// @title DeployX402ProxiesTest
/// @notice Regression tests for the CREATE2 deployment path (non-local chain IDs), where
/// Deploy.s.sol must reproduce the documented canonical addresses exactly. A prior
/// change silently regressed UPTO_SALT to a stale value that no longer matched the
/// live, SDK-referenced x402UptoPermit2Proxy deployment; these tests pin both
/// canonical addresses so a future salt/initCode regression fails loudly here
/// instead of silently deploying an orphaned contract on a new chain.
contract DeployX402ProxiesTest is Test {
address constant CANONICAL_PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
address constant CREATE2_DEPLOYER = 0x4e59b44847b379578588920cA78FbF26c0B4956C;

address constant EXPECTED_EXACT_ADDRESS = 0x402085c248EeA27D92E8b30b2C58ed07f9E20001;
address constant EXPECTED_UPTO_ADDRESS = 0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002;

/// @dev Bytecode of Arachnid's deterministic CREATE2 deployer, same on every EVM chain.
bytes constant CREATE2_DEPLOYER_CODE =
hex"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3";

function _simulateRealChain() internal {
vm.chainId(8453); // any non-anvil chain ID forces the CREATE2 (non-local) deployment path
vm.etch(CREATE2_DEPLOYER, CREATE2_DEPLOYER_CODE);
vm.etch(CANONICAL_PERMIT2, hex"60006000fd"); // just needs non-empty code for the prereq check
}

function test_runDeploysToCanonicalAddresses() public {
_simulateRealChain();

DeployX402Proxies deployer = new DeployX402Proxies();
deployer.run();

assertGt(EXPECTED_EXACT_ADDRESS.code.length, 0, "no code at canonical Exact address");
assertGt(EXPECTED_UPTO_ADDRESS.code.length, 0, "no code at canonical Upto address");

assertEq(
address(x402ExactPermit2Proxy(EXPECTED_EXACT_ADDRESS).PERMIT2()),
CANONICAL_PERMIT2,
"Exact PERMIT2 mismatch"
);
assertEq(
address(x402UptoPermit2Proxy(EXPECTED_UPTO_ADDRESS).PERMIT2()), CANONICAL_PERMIT2, "Upto PERMIT2 mismatch"
);
}

function test_runIsIdempotent() public {
_simulateRealChain();

DeployX402Proxies deployer = new DeployX402Proxies();
deployer.run();
deployer.run(); // must detect existing deployments and skip, not attempt to redeploy

assertGt(EXPECTED_EXACT_ADDRESS.code.length, 0);
assertGt(EXPECTED_UPTO_ADDRESS.code.length, 0);
}

function test_runUptoDeploysOnlyUpto() public {
DeployX402Proxies deployer = new DeployX402Proxies();
uint64 nonceBefore = vm.getNonce(DEFAULT_SENDER);
address expectedAddress = vm.computeCreateAddress(DEFAULT_SENDER, nonceBefore);

deployer.runUpto();

assertEq(vm.getNonce(DEFAULT_SENDER), nonceBefore + 1);
assertGt(expectedAddress.code.length, 0);
x402UptoPermit2Proxy referenceProxy = new x402UptoPermit2Proxy(CANONICAL_PERMIT2);
assertEq(expectedAddress.codehash, address(referenceProxy).codehash);
assertEq(address(x402UptoPermit2Proxy(expectedAddress).PERMIT2()), CANONICAL_PERMIT2);
}

function test_runUptoDeploysToCanonicalAddress() public {
_simulateRealChain();

DeployX402Proxies deployer = new DeployX402Proxies();
deployer.runUpto();

assertGt(EXPECTED_UPTO_ADDRESS.code.length, 0, "no code at canonical Upto address");
assertEq(EXPECTED_EXACT_ADDRESS.code.length, 0, "Exact should not be deployed");
assertEq(
address(x402UptoPermit2Proxy(EXPECTED_UPTO_ADDRESS).PERMIT2()), CANONICAL_PERMIT2, "Upto PERMIT2 mismatch"
);
}
}
2 changes: 1 addition & 1 deletion contracts/evm/vanity-miner/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const EXACT_INIT_CODE_HASH: [u8; 32] =
hex_literal::hex!("e774d1d5a07218946ab54efe010b300481478b86861bb17d69c98a57f68a604c");
// x402UptoPermit2Proxy (deterministic build, no CBOR metadata)
const UPTO_INIT_CODE_HASH: [u8; 32] =
hex_literal::hex!("01575bfc9cacbf6463db62ee8867594b1657139c8493a712ef6bcefa848a20b7");
hex_literal::hex!("74f7a29cbc3c55f87cdef7f7c551643189e8bb62eed9de67753aebc402b83797");
// x402BatchSettlement — keccak256(type(x402BatchSettlement).creationCode) after `forge build`
const BATCH_INIT_CODE_HASH: [u8; 32] =
hex_literal::hex!("ed07c0a1aeb6bd4b8e28932959d66e9f2c1a2f6ebb1fd3dc5f3fbcf8135850f6");
Expand Down
12 changes: 9 additions & 3 deletions docs/core-concepts/facilitator.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,17 @@ Multiple facilitators are live in production, supporting various networks includ
11. `Facilitator server` returns a `Payment Execution Response` to the resource server.
12. `Resource server` returns a response to the `Client` with a `PAYMENT-RESPONSE` header containing the `Settlement Response` as Base64-encoded JSON. On success, this is a `200 OK` with the requested resource. On failure, this is a `402 Payment Required` with error details.

### Settlement Pending (EVM)
### Settlement pending and auto-recovery

When an EVM settlement transaction is broadcast but its confirmation cannot be established — for example, due to an RPC error or a timeout while waiting for the receipt — the facilitator returns `settlement_pending` instead of a terminal failure. This is a **non-terminal** error: the transaction may still confirm on chain. The `SettlementResponse` carries the broadcast transaction hash in `transaction` and the network in `network`, so callers can reconcile on chain before deciding whether to retry.
When a settlement transaction is broadcast but its confirmation cannot be established — for example, due to an RPC error or a timeout while waiting for the receipt — the facilitator returns `settlement_pending` instead of a terminal failure. This is a **non-terminal** error: the transaction may still confirm on chain. The `SettlementResponse` carries the broadcast transaction hash in `transaction` and the network in `network`, so callers can reconcile on chain before deciding whether to retry.

This applies to the `exact`, `upto`, and `batch-settlement` EVM schemes (v2 only).
This applies to the `exact`, `upto`, and `batch-settlement` schemes on both EVM and SVM (v2 only).

#### Automatic retry and reconciliation

The resource server automatically retries settlement exactly once when it receives a `settlement_pending` response. On the retry, the facilitator mechanism checks a `PendingSettlementStore` for the broadcast transaction hash keyed to the payment payload. When a match is found, the mechanism reconciles against the already-broadcast transaction instead of verifying and broadcasting a second one. This prevents double-spend and avoids redundant on-chain submissions.

The `PendingSettlementStore` is an interface, not a concrete type, so multi-instance facilitators (running several replicas with no session affinity) can supply a shared, network-backed implementation such as Redis instead of the default in-memory store. The in-memory default only works when the retry lands on the same process.

**Facilitators deployed behind a platform request deadline** (serverless functions, gateway timeouts) should bound the receipt wait below that deadline. If the process is killed mid-wait, the caller receives a 5xx with no transaction hash instead of `settlement_pending` with a hash to reconcile against.

Expand Down
1 change: 1 addition & 0 deletions docs/dev-tools/facilitators.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ If you are evaluating a mainnet EVM route, decide on your production facilitator
| [Corbits](https://corbits.dev) | Production-grade multi-network, multi-token facilitator supporting EVM and Solana |
| [Dexter](https://dexter.cash/facilitator) | Free public x402 facilitator across Solana and EVM chains with no fees and no account required |
| [Fireblocks Facilitator](https://developers.fireblocks.com/docs/x402-facilitator-overview) | Open-source and hosted facilitator with Fireblocks vault settlement; private keys never leave Fireblocks |
| [FTP Canton Facilitator](https://www.ftptech.xyz/x402) | Production x402 facilitator for Canton Network mainnet, supporting Canton Coin and CIP-56 tokens |
| [HPP Facilitator](https://docs.hpp.io/x402/facilitator) | Gasless, public x402 facilitator for HPP Mainnet and Sepolia |
| [Meridian](https://mrdn.finance) | Multi-chain facilitator with developer-first features |
| [Mogami Facilitator](https://facilitator.mogami.tech) | Free, developer-focused facilitator for Base with optional self-hosted Docker deployment |
Expand Down
2 changes: 1 addition & 1 deletion docs/extensions/bazaar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ The Bazaar extension supports parameterized routes (e.g., `/users/[userId]`, `/w

This means `/users/123`, `/users/456`, and `/users/789` all map to the same catalog entry: `/users/:userId`.

**Note:** Wildcard segments (`*`) are automatically converted to named parameters (`:var1`, `:var2`, etc.) for catalog normalization.
**Note:** Wildcard segments (`*`) within a path (e.g. `/api/*/data`) are automatically converted to named parameters (`:var1`, `:var2`, etc.) for catalog normalization. However, bare wildcard patterns (e.g. `"*"` with no literal path segments) do **not** produce a `routeTemplate` — use a keyed path pattern in `withX402` (e.g. `{ "/api/weather": config }`) so the resource is indexed with the correct URL.

#### Service Metadata

Expand Down
Loading
Loading